--- /dev/null
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 5be5e92..818313e 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -176,6 +176,7 @@ IF(WITH_DEFAULT_FEATURE_SET)
+ ENDIF()
+
+ # Add macros
++INCLUDE(wsrep)
+ INCLUDE(character_sets)
+ INCLUDE(cpu_info)
+ INCLUDE(zlib)
+@@ -324,6 +325,12 @@ OPTION(OPTIMIZER_TRACE "Support tracing of Optimizer" ON)
+ OPTION(INNODB_COMPILER_HINTS "Compile InnoDB with compiler hints" ON)
+ MARK_AS_ADVANCED(INNODB_COMPILER_HINTS)
+
++OPTION(WITH_INNODB_DISALLOW_WRITES "InnoDB freeze writes patch from Google" ${WITH_WSREP})
++IF (WITH_INNODB_DISALLOW_WRITES)
++ MESSAGE(STATUS "INNODB_DISALLOW_WRITES")
++ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWITH_INNODB_DISALLOW_WRITES")
++ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_INNODB_DISALLOW_WRITES")
++ENDIF()
+ OPTION(INNODB_PAGE_ATOMIC_REF_COUNT "Use atomics for the page reference count"
+ ON)
+ MARK_AS_ADVANCED(INNODB_PAGE_ATOMIC_REF_COUNT)
+@@ -444,6 +451,9 @@ ADD_SUBDIRECTORY(regex)
+ ADD_SUBDIRECTORY(mysys)
+ ADD_SUBDIRECTORY(mysys_ssl)
+ ADD_SUBDIRECTORY(libmysql)
++IF(WITH_WSREP)
++ADD_SUBDIRECTORY(wsrep)
++ENDIF()
+
+ IF(WITH_UNIT_TESTS)
+ ADD_SUBDIRECTORY(unittest)
+@@ -539,7 +549,7 @@ ADD_SUBDIRECTORY(packaging/solaris)
+ # (see http://public.kitware.com/Bug/view.php?id=11452)
+ SET(CPACK_MONOLITHIC_INSTALL 1 CACHE INTERNAL "")
+
+-IF(UNIX)
++IF(UNIX AND NOT WITH_WSREP)
+ INSTALL(FILES Docs/mysql.info DESTINATION ${INSTALL_INFODIR} OPTIONAL COMPONENT Info)
+ ENDIF()
+ #
+@@ -554,7 +564,7 @@ IF(NOT INSTALL_LAYOUT MATCHES "RPM")
+ INSTALL(FILES README DESTINATION ${INSTALL_DOCREADMEDIR} COMPONENT Readme)
+ INSTALL(FILES ${CMAKE_BINARY_DIR}/Docs/INFO_SRC ${CMAKE_BINARY_DIR}/Docs/INFO_BIN DESTINATION ${INSTALL_DOCDIR})
+ IF(UNIX)
+- INSTALL(FILES Docs/INSTALL-BINARY DESTINATION ${INSTALL_DOCREADMEDIR} COMPONENT Readme)
++ INSTALL(FILES Docs/INSTALL-BINARY Docs/README-wsrep DESTINATION ${INSTALL_DOCREADMEDIR} COMPONENT Readme)
+ ENDIF()
+ # MYSQL_DOCS_LOCATON is used in "make dist", points to the documentation directory
+ SET(MYSQL_DOCS_LOCATION "" CACHE PATH "Location from where documentation is copied")
+@@ -562,6 +572,7 @@ IF(NOT INSTALL_LAYOUT MATCHES "RPM")
+ INSTALL(DIRECTORY Docs/ DESTINATION ${INSTALL_DOCDIR}
+ COMPONENT Documentation
+ PATTERN "INSTALL-BINARY" EXCLUDE
++ PATTERN "README-wsrep" EXCLUDE
+ PATTERN "Makefile.*" EXCLUDE
+ PATTERN "glibc*" EXCLUDE
+ PATTERN "linuxthreads.txt" EXCLUDE
+diff --git a/Docs/ChangeLog b/Docs/ChangeLog
+index 9988db3..e1775d5 100644
+--- a/Docs/ChangeLog
++++ b/Docs/ChangeLog
+@@ -1 +1,2 @@
+-This is a first release, this file is supposed to be empty
++Placeholder
++
+diff --git a/Docs/INFO_SRC b/Docs/INFO_SRC
+deleted file mode 100644
+index fbc1a6c..0000000
+--- a/Docs/INFO_SRC
++++ /dev/null
+@@ -1,7 +0,0 @@
+-commit: 19ff9770da1307a8b44be40beaa456c4d1149c2a
+-date: 2015-01-19 14:26:20 +0100
+-build-date: 2015-01-19 14:38:00 +0100
+-short: 19ff977
+-branch: mysql-5.6.23-release
+-
+-MySQL source 5.6.23
+diff --git a/WSREP_REVISION b/WSREP_REVISION
+new file mode 100644
+index 0000000..72dd598
+--- /dev/null
++++ b/WSREP_REVISION
+@@ -0,0 +1 @@
++6b378be
+diff --git a/client/mysqltest.cc b/client/mysqltest.cc
+index 2def9bd..1421309 100644
+--- a/client/mysqltest.cc
++++ b/client/mysqltest.cc
+@@ -496,7 +496,7 @@ struct st_match_err
+
+ struct st_expected_errors
+ {
+- struct st_match_err err[10];
++ struct st_match_err err[20];
+ uint count;
+ };
+ static struct st_expected_errors saved_expected_errors;
+diff --git a/cmake/configure.pl b/cmake/configure.pl
+index 22c1329..0a6a3cf 100644
+--- a/cmake/configure.pl
++++ b/cmake/configure.pl
+@@ -223,6 +223,16 @@ foreach my $option (@ARGV)
+ $cmakeargs = $cmakeargs." \"-DWITH_COMMENT=".substr($option,13)."\"";
+ next;
+ }
++ if ($option =~ /layout=/)
++ {
++ $cmakeargs = $cmakeargs." -DINSTALL_LAYOUT=".substr($option,7);
++ next;
++ }
++ if ($option =~ /with-unix-socket-path=/)
++ {
++ $cmakeargs = $cmakeargs." -DMYSQL_UNIX_ADDR=".substr($option,22);
++ next;
++ }
+ if ($option =~ /mysql-maintainer-mode/)
+ {
+ $cmakeargs = $cmakeargs." -DMYSQL_MAINTAINER_MODE=" .
+diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake
+index 4adda0b..5cd47af 100644
+--- a/cmake/install_layout.cmake
++++ b/cmake/install_layout.cmake
+@@ -146,7 +146,10 @@ SET(INSTALL_BINDIR_RPM "bin")
+ SET(INSTALL_SBINDIR_RPM "sbin")
+ SET(INSTALL_SCRIPTDIR_RPM "bin")
+ #
+-IF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
++# Deciding via system processor may give wrong answer in
++# virtual environments that see host CPU directly.
++# IF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
++IF(64BIT)
+ SET(INSTALL_LIBDIR_RPM "lib64")
+ SET(INSTALL_PLUGINDIR_RPM "lib64/mysql/plugin")
+ ELSE()
+diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake
+index 83bd6bd..4f66008 100644
+--- a/cmake/install_macros.cmake
++++ b/cmake/install_macros.cmake
+@@ -13,8 +13,34 @@
+ # along with this program; if not, write to the Free Software
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
++if(APPLE)
++ LIST(APPEND CMAKE_CXX_LINK_EXECUTABLE "dsymutil <TARGET>")
++ LIST(APPEND CMAKE_C_LINK_EXECUTABLE "dsymutil <TARGET>")
++ LIST(APPEND CMAKE_CXX_CREATE_SHARED_LIBRARY "dsymutil <TARGET>")
++ LIST(APPEND CMAKE_C_CREATE_SHARED_LIBRARY "dsymutil <TARGET>")
++ LIST(APPEND CMAKE_CXX_CREATE_SHARED_MODULE "dsymutil <TARGET>")
++ LIST(APPEND CMAKE_C_CREATE_SHARED_MODULE "dsymutil <TARGET>")
++ENDIF()
++
+ GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
+ INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake)
++MACRO (INSTALL_DSYM_DIRECTORIES targets)
++ IF(APPLE)
++ FOREACH(target ${targets})
++ GET_TARGET_PROPERTY(location ${target} LOCATION)
++ GET_TARGET_PROPERTY(type ${target} TYPE)
++ # It's a dirty hack, but cmake too stupid and mysql cmake files too buggy */
++ STRING(REGEX REPLACE "/liblibmysql.dylib$" "/libmysqlclient.${SHARED_LIB_MAJOR_VERSION}.dylib" location ${location})
++ IF(DEBUG_EXTNAME)
++ STRING(REGEX REPLACE "/mysqld$" "/mysqld-debug" location ${location})
++ ENDIF()
++ IF(type MATCHES "EXECUTABLE" OR type MATCHES "MODULE" OR type MATCHES "SHARED_LIBRARY")
++ INSTALL(DIRECTORY "${location}.dSYM" DESTINATION ${INSTALL_LOCATION} COMPONENT Debuginfo)
++ ENDIF()
++ ENDFOREACH()
++ ENDIF()
++ENDMACRO()
++
+ MACRO (INSTALL_DEBUG_SYMBOLS targets)
+ IF(MSVC)
+ FOREACH(target ${targets})
+@@ -241,6 +267,7 @@ FUNCTION(MYSQL_INSTALL_TARGETS)
+ INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION} ${COMP})
+ SET(INSTALL_LOCATION ${ARG_DESTINATION} )
+ INSTALL_DEBUG_SYMBOLS("${TARGETS}")
++ INSTALL_DSYM_DIRECTORIES("${TARGETS}")
+ SET(INSTALL_LOCATION)
+ ENDFUNCTION()
+
+diff --git a/cmake/os/FreeBSD.cmake b/cmake/os/FreeBSD.cmake
+index e095929..bd72a58 100644
+--- a/cmake/os/FreeBSD.cmake
++++ b/cmake/os/FreeBSD.cmake
+@@ -22,3 +22,5 @@
+
+ # The below was used for really old versions of FreeBSD, roughly: before 5.1.9
+ # ADD_DEFINITIONS(-DHAVE_BROKEN_REALPATH)
++
++SET(HAVE_SYS_TIMEB_H CACHE INTERNAL "")
+diff --git a/cmake/os/WindowsCache.cmake b/cmake/os/WindowsCache.cmake
+index a1764ad..891c4e4 100644
+--- a/cmake/os/WindowsCache.cmake
++++ b/cmake/os/WindowsCache.cmake
+@@ -74,6 +74,7 @@ SET(HAVE_FSYNC CACHE INTERNAL "")
+ SET(HAVE_FTIME 1 CACHE INTERNAL "")
+ SET(HAVE_FTRUNCATE CACHE INTERNAL "")
+ SET(HAVE_GETADDRINFO 1 CACHE INTERNAL "")
++SET(HAVE_GETIFADDRS CACHE INTERNAL "")
+ SET(HAVE_GETCWD 1 CACHE INTERNAL "")
+ SET(HAVE_GETHOSTBYADDR_R CACHE INTERNAL "")
+ SET(HAVE_GETHRTIME CACHE INTERNAL "")
+diff --git a/cmake/package_name.cmake b/cmake/package_name.cmake
+index 4cb5c95..882259c 100644
+--- a/cmake/package_name.cmake
++++ b/cmake/package_name.cmake
+@@ -27,6 +27,8 @@ IF(NOT VERSION)
+ SET(DEFAULT_MACHINE ${CMAKE_SYSTEM_PROCESSOR})
+ IF(SIZEOF_VOIDP EQUAL 8)
+ SET(64BIT 1)
++ ELSE()
++ SET(64BIT 0)
+ ENDIF()
+
+ IF(CMAKE_SYSTEM_NAME MATCHES "Windows")
+@@ -127,7 +129,14 @@ IF(NOT VERSION)
+ STRING(REGEX REPLACE "^.*-ndb-" "" NDBVERSION "${VERSION}")
+ SET(package_name "mysql-cluster${PRODUCT_TAG}-${NDBVERSION}-${SYSTEM_NAME_AND_PROCESSOR}")
+ ELSE()
+- SET(package_name "mysql${PRODUCT_TAG}-${VERSION}-${SYSTEM_NAME_AND_PROCESSOR}")
++ IF(WITH_WSREP)
++ IF(NOT WSREP_VERSION)
++ MESSAGE(FATAL_ERROR "Variable WSREP_VERSION must be set")
++ ENDIF()
++ SET(package_name "mysql-wsrep${PRODUCT_TAG}-${VERSION}-${WSREP_VERSION}-${SYSTEM_NAME_AND_PROCESSOR}")
++ ELSE()
++ SET(package_name "mysql${PRODUCT_TAG}-${VERSION}-${SYSTEM_NAME_AND_PROCESSOR}")
++ ENDIF()
+ ENDIF()
+
+ MESSAGE(STATUS "Packaging as: ${package_name}")
+diff --git a/cmake/wsrep.cmake b/cmake/wsrep.cmake
+new file mode 100644
+index 0000000..2c7d799
+--- /dev/null
++++ b/cmake/wsrep.cmake
+@@ -0,0 +1,52 @@
++# Copyright (c) 2011, Codership Oy <info@codership.com>.
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++
++# We need to generate a proper spec file even without --with-wsrep flag,
++# so WSREP_VERSION is produced regardless
++
++# Set the patch version
++SET(WSREP_PATCH_VERSION "10")
++
++# Obtain patch revision number
++SET(WSREP_REVISION $ENV{WSREP_REV})
++IF(NOT WSREP_REVISION)
++ SET(WSREP_REVISION "XXXX" CACHE STRING "WSREP revision")
++ENDIF()
++
++
++# Obtain wsrep API version
++EXECUTE_PROCESS(
++ COMMAND sh -c "grep WSREP_INTERFACE_VERSION ${MySQL_SOURCE_DIR}/wsrep/wsrep_api.h | cut -d '\"' -f 2"
++ OUTPUT_VARIABLE WSREP_API_VERSION
++ RESULT_VARIABLE RESULT
++)
++#FILE(WRITE "wsrep_config" "Debug: WSREP_API_VERSION result: ${RESULT}\n")
++STRING(REGEX REPLACE "(\r?\n)+$" "" WSREP_API_VERSION "${WSREP_API_VERSION}")
++
++SET(WSREP_VERSION "${WSREP_API_VERSION}.${WSREP_PATCH_VERSION}"
++ CACHE STRING "WSREP version")
++
++OPTION(WITH_WSREP "WSREP replication API (to use, e.g. Galera Replication library)" OFF)
++IF (WITH_WSREP)
++ SET(WSREP_C_FLAGS "-DWITH_WSREP -DWSREP_PROC_INFO -DMYSQL_MAX_VARIABLE_VALUE_LEN=2048")
++ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WSREP_C_FLAGS}")
++ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WSREP_C_FLAGS}")
++ SET(COMPILATION_COMMENT "${COMPILATION_COMMENT}, wsrep_${WSREP_VERSION}")
++ SET(WITH_EMBEDDED_SERVER OFF)
++ SET(WITH_INNODB_DISALLOW_WRITES ON)
++ SET(WITH_INNODB_MEMCACHED ON)
++ENDIF()
++
++#
+diff --git a/config.h.cmake b/config.h.cmake
+index 987be27..a216b18 100644
+--- a/config.h.cmake
++++ b/config.h.cmake
+@@ -155,6 +155,7 @@
+ #cmakedefine HAVE_FSYNC 1
+ #cmakedefine HAVE_FTIME 1
+ #cmakedefine HAVE_GETADDRINFO 1
++#cmakedefine HAVE_GETIFADDRS 1
+ #cmakedefine HAVE_GETCWD 1
+ #cmakedefine HAVE_GETHOSTBYADDR_R 1
+ #cmakedefine HAVE_GETHRTIME 1
+diff --git a/configure.cmake b/configure.cmake
+index e1c1793..a4d4809 100644
+--- a/configure.cmake
++++ b/configure.cmake
+@@ -433,6 +433,7 @@ CHECK_FUNCTION_EXISTS (getpassphrase HAVE_GETPASSPHRASE)
+ CHECK_FUNCTION_EXISTS (getpwnam HAVE_GETPWNAM)
+ CHECK_FUNCTION_EXISTS (getpwuid HAVE_GETPWUID)
+ CHECK_FUNCTION_EXISTS (getrlimit HAVE_GETRLIMIT)
++CHECK_FUNCTION_EXISTS (getifaddrs HAVE_GETIFADDRS)
+ CHECK_FUNCTION_EXISTS (getrusage HAVE_GETRUSAGE)
+ CHECK_FUNCTION_EXISTS (getwd HAVE_GETWD)
+ CHECK_FUNCTION_EXISTS (gmtime_r HAVE_GMTIME_R)
+diff --git a/include/my_md5.h b/include/my_md5.h
+index 452676e..4f89105 100644
+--- a/include/my_md5.h
++++ b/include/my_md5.h
+@@ -43,7 +43,11 @@ static inline void array_to_hex(char *to, const unsigned char *str, uint len)
+ *to++= _dig_vec_lower[((uchar) *str) & 0x0F];
+ }
+ }
+-
++#ifdef WITH_WSREP
++void *wsrep_md5_init();
++void wsrep_md5_update(void *ctx, char* buf, int len);
++ void wsrep_compute_md5_hash(char *digest, void *ctx);
++#endif
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/include/thr_lock.h b/include/thr_lock.h
+index c5638ec..6b0741f 100644
+--- a/include/thr_lock.h
++++ b/include/thr_lock.h
+@@ -20,6 +20,15 @@
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
++#ifdef WITH_WSREP
++#include <my_sys.h>
++ typedef my_bool (* wsrep_thd_is_brute_force_fun)(void *, my_bool);
++ typedef int (* wsrep_abort_thd_fun)(void *, void *, my_bool);
++ typedef int (* wsrep_on_fun)(void *);
++ void wsrep_thr_lock_init(
++ wsrep_thd_is_brute_force_fun bf_fun, wsrep_abort_thd_fun abort_fun,
++ my_bool debug, my_bool convert_LOCK_to_trx, wsrep_on_fun on_fun);
++#endif
+
+ #include <my_pthread.h>
+ #include <my_list.h>
+@@ -89,6 +98,10 @@ typedef struct st_thr_lock_info
+ {
+ pthread_t thread;
+ my_thread_id thread_id;
++#ifdef WITH_WSREP
++ void *mysql_thd; // THD pointer
++ my_bool in_lock_tables; // true, if inside locking session
++#endif
+ } THR_LOCK_INFO;
+
+
+diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental
+index 2519428..1cfa4c5 100644
+--- a/mysql-test/collections/default.experimental
++++ b/mysql-test/collections/default.experimental
+@@ -21,9 +21,3 @@ rpl.rpl_gtid_logs_without_rotate_or_stop_event @windows # Bug#16207800 2013-0
+ perfschema.socket_summary_by_instance_func # bug#16274580
+ innodb.innodb_bug14676111 # bug#17026780 2013-07-08 anitha Originally made experimental due to bug#16371942 which is now fixed. Now fails with mismatch in CLUST_INDEX_SIZE
+
+-# This file is for marking internal tests as experimental.
+-# Use the same way as the "normal" default.experimental
+-# The contents of this file will be appended to it in PB2 but not for
+-# normal developer builds.
+-# Internal tests should *not* be listed in the public default.experimental!
+-
+diff --git a/mysql-test/collections/default.release b/mysql-test/collections/default.release
+deleted file mode 100644
+index 9ac7af5..0000000
+--- a/mysql-test/collections/default.release
++++ /dev/null
+@@ -1,16 +0,0 @@
+-# This file contains the old default.release, the plan is to replace that
+-# with something like the below (remove space after #):
+-# include default.daily
+-# include default.weekly
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=debug --vardir=var-debug --skip-rpl --report-features --debug-server
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=normal --vardir=var-normal --report-features --unit-tests
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=ps --vardir=var-ps --ps-protocol
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=funcs1+ps --vardir=var-funcs_1_ps --suite=funcs_1 --ps-protocol
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=funcs2 --vardir=var-funcs2 --suite=funcs_2
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=partitions --vardir=var-parts --suite=parts
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=stress --vardir=var-stress --suite=stress
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=jp --vardir=var-jp --suite=jp
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=embedded --vardir=var-embedded --embedded-server --skip-rpl
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=nist --vardir=var-nist --suite=nist
+-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=nist+ps --vardir=var-nist_ps --suite=nist --ps-protocol
+-perl mysql-test-run.pl --timer --force --comment=memcached --vardir=var-memcached --experimental=collections/default.experimental --parallel=auto --retry=0 --suite=memcached
+diff --git a/mysql-test/collections/default.release.done b/mysql-test/collections/default.release.done
+deleted file mode 100644
+index 3d1016b..0000000
+--- a/mysql-test/collections/default.release.done
++++ /dev/null
+@@ -1 +0,0 @@
+-/export/home/pb2/build/sb_0-14135359-1421674846.01/mysql-5.6.23-release-export-7480611_gpl/mysql-test/collections/default.release.in
+diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test
+index 8a36566..c332c01 100644
+--- a/mysql-test/extra/binlog_tests/binlog.test
++++ b/mysql-test/extra/binlog_tests/binlog.test
+@@ -337,7 +337,8 @@ dfLtTBcBAAAAIgAAAPkAAAAAABcAAAAAAAcAAf/+AQAAAA==
+
+ SELECT * FROM t1;
+ --echo # Their values should be ON
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
++SHOW SESSION VARIABLES LIKE "unique_checks";
+
+ --echo
+ SET @@SESSION.foreign_key_checks= OFF;
+@@ -352,7 +353,8 @@ dfLtTBcBAAAAIgAAAM0BAAAAABcAAAAAAAEAAf/+AgAAAA==
+
+ SELECT * FROM t1;
+ --echo # Their values should be OFF
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
++SHOW SESSION VARIABLES LIKE "unique_checks";
+
+ --echo # INSERT INTO t1 VALUES(2)
+ --echo # foreign_key_checks=1 and unique_checks=1
+@@ -366,7 +368,8 @@ dfLtTBcBAAAAIgAAAM0BAAAAABcAAAAAAAEAAf/+AgAAAA==
+
+ SELECT * FROM t1;
+ --echo # Their values should be OFF
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
++SHOW SESSION VARIABLES LIKE "unique_checks";
+
+ DROP TABLE t1;
+
+diff --git a/mysql-test/include/check-testcase.test b/mysql-test/include/check-testcase.test
+index fe5c756..6ef3aec 100644
+--- a/mysql-test/include/check-testcase.test
++++ b/mysql-test/include/check-testcase.test
+@@ -6,6 +6,7 @@
+ # tescase. In that way, it is possible to check that a testcase does
+ # not have any unwanted side affects.
+
++--source include/wait_until_connected_again.inc
+ --disable_query_log
+
+ # We want to ensure all slave configuration is restored. But SHOW
+diff --git a/mysql-test/include/default_mysqld.cnf b/mysql-test/include/default_mysqld.cnf
+index cecd4a6..1c98245 100644
+--- a/mysql-test/include/default_mysqld.cnf
++++ b/mysql-test/include/default_mysqld.cnf
+@@ -34,7 +34,7 @@ loose-innodb_lru_scan_depth= 100
+ loose-innodb_write_io_threads= 2
+ loose-innodb_read_io_threads= 2
+ loose-innodb_log_buffer_size= 1M
+-loose-innodb_log_file_size= 5M
++innodb_log_file_size= 5M
+ loose-innodb_additional_mem_pool_size= 1M
+ loose-innodb_log_files_in_group= 2
+
+diff --git a/mysql-test/include/galera_cluster.inc b/mysql-test/include/galera_cluster.inc
+new file mode 100644
+index 0000000..bc65222
+--- /dev/null
++++ b/mysql-test/include/galera_cluster.inc
+@@ -0,0 +1,10 @@
++# galera_cluster.inc
++# ==================
++#
++# Description
++# -----------
++# Configure galera cluster with 2 nodes.
++#
++
++--let $galera_cluster_size = 2
++--source include/galera_init.inc
+diff --git a/mysql-test/include/galera_connect.inc b/mysql-test/include/galera_connect.inc
+new file mode 100644
+index 0000000..bfd9b18
+--- /dev/null
++++ b/mysql-test/include/galera_connect.inc
+@@ -0,0 +1,45 @@
++# galera_connect.inc
++# ==================
++#
++# Description
++# -----------
++# Open a connection to the specified server number ($galera_server_number).
++# The connection itself would be identified by $galera_connection_name.
++#
++# Parameters
++# ----------
++# $galera_connection_name
++# Name of the resulting connection.
++#
++# $galera_server_number
++# Sequence number of the node in the galera cluster.
++#
++# $galera_debug
++# Print debug information.
++#
++
++if (!$galera_connection_name)
++{
++ --die ERROR IN TEST: $galera_connection_name must be set before sourcing include/galera_connect.inc
++}
++
++if (!$galera_server_number)
++{
++ --die ERROR IN TEST: $galera_server_number must be set before sourcing include/galera_connect.inc
++}
++
++--let $_galera_port= \$NODE_MYPORT_$galera_server_number
++if (!$_galera_port)
++{
++ --echo Bug in test case: '\$NODE_MYPORT_$galera_server_number' not initialized. Check the test's .cfg file.
++ --die Not all NODE_MYPORT_* environment variables are setup correctly.
++}
++
++if ($galera_debug)
++{
++ --echo connect($galera_connection_name,127.0.0.1,root,,test,$_galera_port,)
++}
++
++# Open a connection
++--connect($galera_connection_name,127.0.0.1,root,,test,$_galera_port,)
++
+diff --git a/mysql-test/include/galera_diff.inc b/mysql-test/include/galera_diff.inc
+new file mode 100644
+index 0000000..6043b58
+--- /dev/null
++++ b/mysql-test/include/galera_diff.inc
+@@ -0,0 +1,100 @@
++# galera_diff.inc
++# ===============
++#
++# Description
++# -----------
++# Compare the output of the given statement on all the nodes of the cluster.
++#
++# Parameters
++# ----------
++# $galera_diff_statement
++# Statement for which the output would be compared.
++#
++# $galera_diff_database
++# Database against which the above statement would be executed.
++# (Default : test)
++#
++# $galera_diff_servers
++# Comma separated list of servers to executed the diff statement on. If not
++# set, a list of servers will be generated based on $galera_cluster_size.
++#
++# $galerra_debug
++# Print debug information.
++#
++
++if (!$galera_diff_statement)
++{
++ --die ERROR IN TEST: $galera_diff_statement must be set before sourcing include/galera_diff.inc
++}
++
++--let $_galera_diff_database = $galera_diff_database
++if (!$_galera_diff_database)
++{
++ --let $_galera_diff_database = test
++}
++
++--let $_galera_diff_servers= $galera_diff_servers
++if (!$_galera_diff_servers)
++{
++ --let $_i= $galera_cluster_size
++ --let $_galera_diff_servers=
++ while ($_i)
++ {
++ --let $_galera_diff_servers= $_i,$_galera_diff_servers
++ --dec $_i
++ }
++}
++if ($galera_debug)
++{
++ --echo \$galera_diff_servers= '$_galera_diff_servers'
++}
++
++if (!$galera_debug)
++{
++ --disable_query_log
++}
++
++# Generate file containing $galera_diff_statement. We don't pass the
++# statement on the command line, because it would be subject to shell
++# substitutions.
++--let $write_to_file= GENERATE
++--let $write_var= $galera_diff_statement
++--source include/write_var_to_file.inc
++--let $_galera_diff_statement_file= $write_to_file
++
++if (!$galera_debug)
++{
++ --enable_query_log
++}
++
++# Compare all servers.
++--let $_galera_diff_first= 1
++while ($_galera_diff_servers)
++{
++ # Set $_galera_diff_server_i to the first number in the list
++ --let $_galera_diff_server_i= `SELECT SUBSTRING_INDEX('$_galera_diff_servers', ',', 1)`
++ # Remove $_galera_diff_server_i from the list
++ --let $_galera_diff_servers= `SELECT SUBSTRING('$_galera_diff_servers', LENGTH('$_galera_diff_server_i') + 2)`
++
++ # Execute statement
++ --let $_galera_diff_file= $MYSQLTEST_VARDIR/tmp/_galera_diff_server-$_galera_diff_server_i.tmp
++ --exec $MYSQL --defaults-group-suffix=.$_galera_diff_server_i $_galera_diff_database < $_galera_diff_statement_file > $_galera_diff_file
++
++ # Compare
++ if (!$_galera_diff_first)
++ {
++ if ($galera_debug)
++ {
++ --echo diffing $_galera_diff_file and $_galera_diff_prev_file
++ }
++ --diff_files $_galera_diff_file $_galera_diff_prev_file
++ --remove_file $_galera_diff_prev_file
++ }
++ --let $_galera_diff_prev_file= $_galera_diff_file
++ --let $_galera_diff_first= 0
++}
++
++# Cleanup
++--remove_file $_galera_diff_prev_file
++--remove_file $_galera_diff_statement_file
++
+diff --git a/mysql-test/include/galera_end.inc b/mysql-test/include/galera_end.inc
+new file mode 100644
+index 0000000..0fb5479
+--- /dev/null
++++ b/mysql-test/include/galera_end.inc
+@@ -0,0 +1,25 @@
++# galera_end.inc
++# ==============
++#
++# Description
++# -----------
++# Closes the connections opened via include/galera_init.inc
++#
++# Parameters
++# ----------
++# $galera_cluster_size
++# Number of nodes in the cluster.
++#
++
++--let $_galera_node= $galera_cluster_size
++
++while ($_galera_node)
++{
++ if ($galera_debug)
++ {
++ --echo Disconnecting node_$_galera_node
++ }
++ --disconnect node_$_galera_node
++ --dec $_galera_node
++}
++
+diff --git a/mysql-test/include/galera_init.inc b/mysql-test/include/galera_init.inc
+new file mode 100644
+index 0000000..7c79d6f
+--- /dev/null
++++ b/mysql-test/include/galera_init.inc
+@@ -0,0 +1,27 @@
++# galera_init.inc
++# ===============
++#
++# Description
++# -----------
++# Set up a Galera cluster with $wsrep_cluster_size nodes.
++#
++# Parameters
++# ----------
++# $galera_cluster_size
++# Number of nodes in the cluster.
++#
++
++--source include/have_wsrep_provider.inc
++--source include/have_wsrep_enabled.inc
++
++--let $_galera_node= $galera_cluster_size
++
++while ($_galera_node)
++{
++ --let $galera_connection_name= node_$_galera_node
++ --let $galera_server_number= $_galera_node
++ --source include/galera_connect.inc
++
++ --dec $_galera_node
++}
++
+diff --git a/mysql-test/include/galera_resume.inc b/mysql-test/include/galera_resume.inc
+new file mode 100644
+index 0000000..232cb46
+--- /dev/null
++++ b/mysql-test/include/galera_resume.inc
+@@ -0,0 +1,9 @@
++--echo Resuming node ...
++--perl
++ my $pid_filename = $ENV{'_SUSPEND_NODE_PIDFILE'};
++ my $mysqld_pid = `cat $pid_filename`;
++ chomp($mysqld_pid);
++ system("kill -18 $mysqld_pid");
++ exit(0);
++EOF
++
+diff --git a/mysql-test/include/galera_suspend.inc b/mysql-test/include/galera_suspend.inc
+new file mode 100644
+index 0000000..3495ad2
+--- /dev/null
++++ b/mysql-test/include/galera_suspend.inc
+@@ -0,0 +1,14 @@
++#
++# This macro suspends the current node
++#
++
++--let _SUSPEND_NODE_PIDFILE = `SELECT @@pid_file`
++--echo Suspending node ...
++
++--perl
++ my $pid_filename = $ENV{'_SUSPEND_NODE_PIDFILE'};
++ my $mysqld_pid = `cat $pid_filename`;
++ chomp($mysqld_pid);
++ system("kill -19 $mysqld_pid");
++ exit(0);
++EOF
+diff --git a/mysql-test/include/galera_wait_ready.inc b/mysql-test/include/galera_wait_ready.inc
+new file mode 100644
+index 0000000..5652538
+--- /dev/null
++++ b/mysql-test/include/galera_wait_ready.inc
+@@ -0,0 +1,3 @@
++let $wait_timeout = 10;
++let $wait_condition = SELECT 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready' AND VARIABLE_VALUE = 'ON';
++--source include/wait_condition.inc
+diff --git a/mysql-test/include/have_wsrep.inc b/mysql-test/include/have_wsrep.inc
+new file mode 100644
+index 0000000..52220ed
+--- /dev/null
++++ b/mysql-test/include/have_wsrep.inc
+@@ -0,0 +1,8 @@
++# To be used in a test which requires server to be compiled with wsrep support
++# (-DWITH_WSREP=ON) and wsrep plugin is ACTIVE.
++
++if (`SELECT COUNT(*)=0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'wsrep' AND PLUGIN_STATUS='ACTIVE'`)
++{
++ --skip Test requires wsrep plugin.
++}
++
+diff --git a/mysql-test/include/have_wsrep_enabled.inc b/mysql-test/include/have_wsrep_enabled.inc
+new file mode 100644
+index 0000000..1b5948f
+--- /dev/null
++++ b/mysql-test/include/have_wsrep_enabled.inc
+@@ -0,0 +1,10 @@
++
++# To be used in a test which requires wsrep plugin to be ACTIVE and enabled
++# (i.e. wsrep_on=ON). It includes have_wsrep.inc.
++
++--source include/have_wsrep.inc
++
++--require r/have_wsrep.require
++--disable_query_log
++SHOW VARIABLES LIKE 'wsrep_on';
++--enable_query_log
+diff --git a/mysql-test/include/have_wsrep_provider.inc b/mysql-test/include/have_wsrep_provider.inc
+new file mode 100644
+index 0000000..bb5cbc0
+--- /dev/null
++++ b/mysql-test/include/have_wsrep_provider.inc
+@@ -0,0 +1,6 @@
++if (`SELECT COUNT(*)=0 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE
++ VARIABLE_NAME LIKE 'wsrep_provider' AND VARIABLE_VALUE NOT LIKE 'none'`)
++{
++ --skip Test requires wsrep provider library (libgalera_smm.so). Did you set $WSREP_PROVIDER?
++}
++
+diff --git a/mysql-test/include/kill_galera.inc b/mysql-test/include/kill_galera.inc
+new file mode 100644
+index 0000000..d7f665d
+--- /dev/null
++++ b/mysql-test/include/kill_galera.inc
+@@ -0,0 +1,20 @@
++--echo Killing server ...
++
++# Write file to make mysql-test-run.pl expect the crash, but don't start it
++--let $_server_id= `SELECT @@server_id`
++--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect
++--exec echo "wait" > $_expect_file_name
++
++# Kill the connected server
++--disable_reconnect
++--let KILL_NODE_PIDFILE = `SELECT @@pid_file`
++
++--perl
++ my $pid_filename = $ENV{'KILL_NODE_PIDFILE'};
++ my $mysqld_pid = `cat $pid_filename`;
++ chomp($mysqld_pid);
++ system("kill -9 $mysqld_pid");
++ exit(0);
++EOF
++
++--source include/wait_until_disconnected.inc
+diff --git a/mysql-test/include/mtr_check.sql b/mysql-test/include/mtr_check.sql
+index 3bd2583..560dd96 100644
+--- a/mysql-test/include/mtr_check.sql
++++ b/mysql-test/include/mtr_check.sql
+@@ -60,15 +60,23 @@ BEGIN
+
+ -- Dump all global variables except those that may change.
+ -- timestamp changes if time passes. server_uuid changes if server restarts.
++ -- wsrep_start_position can change on mysqldump SST
++ -- auto_increment_offset can change on cluster reconfigurations
+ SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
+ WHERE variable_name NOT IN ('timestamp', 'server_uuid',
+ 'innodb_file_format_max',
+- 'gtid_executed', 'gtid_purged')
++ 'gtid_executed', 'gtid_purged',
++ 'wsrep_start_position',
++ 'auto_increment_offset',
++ 'auto_increment_increment',
++ 'wsrep_data_home_dir')
+ ORDER BY VARIABLE_NAME;
+
+ -- Dump all databases, there should be none
+ -- except those that was created during bootstrap
+- SELECT * FROM INFORMATION_SCHEMA.SCHEMATA;
++ -- and the mtr_wsrep_notify schema which is populated by the std_data/wsrep_notify.sh script
++ -- and the suite/galera/t/galera_var_notify_cmd.test
++ SELECT * FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME != 'mtr_wsrep_notify';
+
+ -- The test database should not contain any tables
+ SELECT table_name AS tables_in_test FROM INFORMATION_SCHEMA.TABLES
+diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql
+index 9de3058..3b7f559 100644
+--- a/mysql-test/include/mtr_warnings.sql
++++ b/mysql-test/include/mtr_warnings.sql
+@@ -246,6 +246,40 @@ INSERT INTO global_suppressions VALUES
+ ("Changed limits: table_cache: *"),
+ ("Could not increase number of max_open_files to more than *"),
+
++ /*
++ Galera suppressions
++ */
++ ("WSREP:*down context*"),
++ ("WSREP: Failed to send state UUID:*"),
++ ("WSREP: wsrep_sst_receive_address is set to '127.0.0.1"),
++ ("WSREP: option --wsrep-casual-reads is deprecated"),
++ ("WSREP: --wsrep-casual-reads=ON takes precedence over --wsrep-sync-wait=0"),
++ ("WSREP: Could not open saved state file for reading: "),
++ ("WSREP: access file\\(.*gvwstate\\.dat\\) failed\\(No such file or directory\\)"),
++ ("WSREP: Gap in state sequence\\. Need state transfer\\."),
++ ("WSREP: Failed to prepare for incremental state transfer: Local state UUID \\(00000000-0000-0000-0000-000000000000\\) does not match group state UUID"),
++ ("WSREP: No existing UUID has been found, so we assume that this is the first time that this server has been started\\. Generating a new UUID: "),
++ ("WSREP: last inactive check more than"),
++ ("WSREP: binlog cache not empty \\(0 bytes\\) at connection close"),
++ ("WSREP: SQL statement was ineffective"),
++ ("WSREP: Refusing exit for the last slave thread"),
++ ("WSREP: Quorum: No node with complete state"),
++ ("WSREP: Failed to report last committed"),
++ ("Slave SQL: Error 'Duplicate entry"),
++ ("Query apply warning:"),
++ ("WSREP: Ignoring error for TO isolated action:"),
++ ("WSREP: Initial position was provided by configuration or SST, avoiding override"),
++ ("Warning: Using a password on the command line interface can be insecure"),
++ ("InnoDB: Error: Table \"mysql\"\\.\"innodb_table_stats\" not found"),
++ ("but it is impossible to select State Transfer donor: Resource temporarily unavailable"),
++ ("WSREP: Could not find peer"),
++ ("WSREP: discarding established \\(time wait\\)"),
++ ("sending install message failed: Resource temporarily unavailable"),
++ ("WSREP: Ignoring possible split-brain \\(allowed by configuration\\) from view"),
++ ("WSREP: no nodes coming from prim view, prim not possible"),
++ ("WSREP: Failed to prepare for incremental state transfer: Local state seqno is undefined:"),
++ ("WSREP: gcs_caused\\(\\) returned -107 \\(Transport endpoint is not connected\\)"),
++
+ ("THE_LAST_SUPPRESSION")||
+
+
+@@ -292,7 +326,17 @@ BEGIN
+ END IF;
+
+ -- Cleanup for next test
+- TRUNCATE test_suppressions;
++ IF @@wsrep_on = 1 THEN
++ -- The TRUNCATE should not be replicated under Galera
++ -- as it causes the custom suppressions on the other
++ -- nodes to be deleted as well
++ SET wsrep_on = 0;
++ TRUNCATE test_suppressions;
++ SET wsrep_on = 1;
++ ELSE
++ TRUNCATE test_suppressions;
++ END IF;
++
+ DROP TABLE error_log;
+
+ END||
+diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc
+index e156cfd..8b3b387 100644
+--- a/mysql-test/include/mysqld--help.inc
++++ b/mysql-test/include/mysqld--help.inc
+@@ -18,7 +18,7 @@ perl;
+ # their paths may vary:
+ @skipvars=qw/basedir open-files-limit general-log-file plugin-dir
+ pid-file slow-query-log-file
+- datadir slave-load-tmpdir tmpdir socket table-definition-cache table-open-cache/;
++ datadir slave-load-tmpdir tmpdir socket table-definition-cache table-open-cache wsrep-node-name/;
+
+ # Plugins which may or may not be there:
+ @plugins=qw/innodb ndb ndbinfo archive blackhole federated partition ndbcluster debug temp-pool ssl des-key-file
+diff --git a/mysql-test/include/not_wsrep.inc b/mysql-test/include/not_wsrep.inc
+new file mode 100644
+index 0000000..3314b5c
+--- /dev/null
++++ b/mysql-test/include/not_wsrep.inc
+@@ -0,0 +1,7 @@
++# To be used in a test which should be skipped if server is compiled with wsrep
++# support (-DWITH_WSREP=ON) and wsrep plugin is ACTIVE.
++
++-- require r/not_wsrep.require
++disable_query_log;
++SELECT VERSION() LIKE '%wsrep%' AS 'HAVE_WSREP';
++enable_query_log;
+diff --git a/mysql-test/include/start_mysqld.inc b/mysql-test/include/start_mysqld.inc
+index 983c566..4ee3d17 100644
+--- a/mysql-test/include/start_mysqld.inc
++++ b/mysql-test/include/start_mysqld.inc
+@@ -1,7 +1,16 @@
+ # Include this script only after using shutdown_mysqld.inc
+ # where $_expect_file_name was initialized.
+ # Write file to make mysql-test-run.pl start up the server again
+---exec echo "restart" > $_expect_file_name
++
++if ($galera_wsrep_start_position != '') {
++ --echo Using --wsrep-start-position when starting mysqld ...
++ --exec echo "restart:$start_mysqld_params --wsrep-start-position=$galera_wsrep_start_position" > $_expect_file_name
++ --let $galera_wsrep_start_position = 0
++}
++
++if ($galera_wsrep_start_position == '') {
++ --exec echo "restart:$start_mysqld_params" > $_expect_file_name
++}
+
+ # Turn on reconnect
+ --enable_reconnect
+@@ -11,4 +20,3 @@
+
+ # Turn off reconnect again
+ --disable_reconnect
+-
+diff --git a/mysql-test/include/wait_until_connected_again.inc b/mysql-test/include/wait_until_connected_again.inc
+index c7bb774..005d518 100644
+--- a/mysql-test/include/wait_until_connected_again.inc
++++ b/mysql-test/include/wait_until_connected_again.inc
+@@ -1,23 +1,34 @@
+ #
+ # Include this script to wait until the connection to the
+ # server has been restored or timeout occurs
++
++#
++# We require two consequtive connection successes in order to
++# work around a race condition on Galera startup where the server
++# can temporarily accept queries before starting to reject them again
++#
++
+ --disable_result_log
+ --disable_query_log
+ let $counter= 500;
+ let $mysql_errno= 9999;
+-while ($mysql_errno)
+-{
+- # Strangely enough, the server might return "Too many connections"
+- # while being shutdown, thus 1040 is an "allowed" error
+- # See BUG#36228
+- --error 0,1040,1053,2002,2003,2006,2013
+- show status;
++let $successes= 2;
++
++while ($successes) {
++ while ($mysql_errno) {
++ # Strangely enough, the server might return "Too many connections"
++ # while being shutdown, thus 1040 is an "allowed" error
++ # See BUG#36228
++ --error 0,1040,1047,1053,1205,2002,2003,2006,2013,1205
++ show status;
+
+- dec $counter;
+- if (!$counter)
+- {
+- --die Server failed to restart
++ --dec $counter
++ if (!$counter) {
++ --die Server failed to restart
++ }
++ --sleep 0.1
+ }
++ --dec $successes
+ --sleep 0.1
+ }
+ --enable_query_log
+diff --git a/mysql-test/include/wait_until_disconnected.inc b/mysql-test/include/wait_until_disconnected.inc
+index 8a989be..56889d1 100644
+--- a/mysql-test/include/wait_until_disconnected.inc
++++ b/mysql-test/include/wait_until_disconnected.inc
+@@ -7,7 +7,7 @@ let $counter= 500;
+ let $mysql_errno= 0;
+ while (!$mysql_errno)
+ {
+- --error 0,1040,1053,2002,2003,2006,2013
++ --error 0,1040,1047,1053,2002,2003,2006,2013
+ show status;
+
+ dec $counter;
+diff --git a/mysql-test/include/write_var_to_file.inc b/mysql-test/include/write_var_to_file.inc
+index 8bb9e72..6ee04c7 100644
+--- a/mysql-test/include/write_var_to_file.inc
++++ b/mysql-test/include/write_var_to_file.inc
+@@ -54,7 +54,7 @@ if (`SELECT LENGTH(@@secure_file_priv) > 0`)
+ --copy_file $_wvtf_tmp_file $write_to_file
+ --remove_file $_wvtf_tmp_file
+ }
+-if (`SELECT LENGTH(@@secure_file_priv) = 0`)
++if (`SELECT LENGTH(@@secure_file_priv) = 0 OR LENGTH(@@secure_file_priv) IS NULL`)
+ {
+ --eval SELECT '$write_var' INTO DUMPFILE '$write_to_file'
+ }
+diff --git a/mysql-test/lib/My/Config.pm b/mysql-test/lib/My/Config.pm
+index 535df07..642ee0a 100644
+--- a/mysql-test/lib/My/Config.pm
++++ b/mysql-test/lib/My/Config.pm
+@@ -178,6 +178,36 @@ sub if_exist {
+ return $option->value();
+ }
+
++package My::Config::Group::ENV;
++our @ISA=qw(My::Config::Group);
++
++use strict;
++use warnings;
++use Carp;
++
++sub new {
++ my ($class, $group_name)= @_;
++ bless My::Config::Group->new($group_name), $class;
++}
++
++#
++# Return value for an option in the group, fail if it does not exist
++#
++sub value {
++ my ($self, $option_name)= @_;
++ my $option= $self->option($option_name);
++
++ if (! defined($option) and defined $ENV{$option_name}) {
++ my $value= $ENV{$option_name};
++ $option= My::Config::Option->new($option_name, $value);
++ }
++
++ croak "No option named '$option_name' in group '$self->{name}'"
++ if ! defined($option);
++
++ return $option->value();
++}
++
+
+ package My::Config;
+
+@@ -197,7 +227,9 @@ sub new {
+ my ($class, $path)= @_;
+ my $group_name= undef;
+
+- my $self= bless { groups => [] }, $class;
++ my $self= bless { groups => [
++ My::Config::Group::ENV->new('ENV')
++ ] }, $class;
+ my $F= IO::File->new($path, "<")
+ or croak "Could not open '$path': $!";
+
+diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm
+index bf2a8fa..fca7f9d 100644
+--- a/mysql-test/lib/My/ConfigFactory.pm
++++ b/mysql-test/lib/My/ConfigFactory.pm
+@@ -1,5 +1,5 @@
+ # -*- cperl -*-
+-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
++# Copyright (c) 2007, 2011, Oracle and/or its affiliates
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU Library General Public
+@@ -31,12 +31,22 @@ use File::Basename;
+ #
+ # Rules to run first of all
+ #
++
++sub add_opt_values {
++ my ($self, $config)= @_;
++
++ # add auto-options
++ $config->insert('OPT', 'port' => sub { fix_port($self, $config) });
++ $config->insert('mysqld', "loose-skip-plugin-$_" => undef) for (@::optional_plugins);
++}
++
+ my @pre_rules=
+ (
++ \&add_opt_values,
+ );
+
+
+-my @share_locations= ("share/mysql", "sql/share", "share");
++my @share_locations= ("share/mariadb", "share/mysql", "sql/share", "share");
+
+
+ sub get_basedir {
+@@ -89,16 +99,12 @@ sub fix_pidfile {
+
+ sub fix_port {
+ my ($self, $config, $group_name, $group)= @_;
+- my $hostname= $group->value('#host');
+- return $self->{HOSTS}->{$hostname}++;
++ return $self->{PORT}++;
+ }
+
+ sub fix_host {
+ my ($self)= @_;
+- # Get next host from HOSTS array
+- my @hosts= keys(%{$self->{HOSTS}});;
+- my $host_no= $self->{NEXT_HOST}++ % @hosts;
+- return $hosts[$host_no];
++ 'localhost'
+ }
+
+ sub is_unique {
+@@ -169,13 +175,6 @@ sub fix_log_slow_queries {
+ return "$dir/mysqld-slow.log";
+ }
+
+-sub fix_secure_file_priv {
+- my ($self)= @_;
+- my $vardir= $self->{ARGS}->{vardir};
+- # By default, prevent the started mysqld to access files outside of vardir
+- return $vardir;
+-}
+-
+ sub fix_std_data {
+ my ($self, $config, $group_name, $group)= @_;
+ my $testdir= $self->get_testdir($group);
+@@ -239,12 +238,18 @@ my @mysqld_rules=
+ { 'pid-file' => \&fix_pidfile },
+ { '#host' => \&fix_host },
+ { 'port' => \&fix_port },
++ # galera base_port and port used during SST
++ { '#galera_port' => \&fix_port },
++ # Galera uses base_port + 1 for IST, so we do not use it for things such as SST
++ { '#ist_port' => \&fix_port },
++ { '#sst_port' => \&fix_port },
+ { 'socket' => \&fix_socket },
+ { '#log-error' => \&fix_log_error },
+- { 'general_log' => 1 },
+- { 'general_log_file' => \&fix_log },
+- { 'slow_query_log' => 1 },
+- { 'slow_query_log_file' => \&fix_log_slow_queries },
++ { 'general-log' => 1 },
++ { 'plugin-dir' => sub { $::plugindir } },
++ { 'general-log-file' => \&fix_log },
++ { 'slow-query-log' => 1 },
++ { 'slow-query-log-file' => \&fix_log_slow_queries },
+ { '#user' => sub { return shift->{ARGS}->{user} || ""; } },
+ { '#password' => sub { return shift->{ARGS}->{password} || ""; } },
+ { 'server-id' => \&fix_server_id, },
+@@ -265,7 +270,7 @@ if (IS_WINDOWS)
+ sub fix_ndb_mgmd_port {
+ my ($self, $config, $group_name, $group)= @_;
+ my $hostname= $group->value('HostName');
+- return $self->{HOSTS}->{$hostname}++;
++ return $self->{PORT}++;
+ }
+
+
+@@ -307,8 +312,6 @@ my @ndbd_rules=
+ { 'BackupDataDir' => \&fix_cluster_backup_dir },
+ );
+
+-
+-#
+ # Rules to run for each memcached in the config
+ # - will be run in order listed here
+ #
+@@ -396,7 +399,7 @@ sub post_check_client_group {
+
+ if (! defined $option){
+ #print $config;
+- croak "Could not get value for '$name_from'";
++ croak "Could not get value for '$name_from' for test $self->{testname}";
+ }
+ $config->insert($client_group_name, $name_to, $option->value())
+ }
+@@ -419,7 +422,7 @@ sub post_check_client_group {
+ sub post_check_client_groups {
+ my ($self, $config)= @_;
+
+- my $first_mysqld= $config->first_like('mysqld.');
++ my $first_mysqld= $config->first_like('mysqld\.');
+
+ return unless $first_mysqld;
+
+@@ -455,7 +458,7 @@ sub post_check_embedded_group {
+ my $first_mysqld= $config->first_like('mysqld.') or
+ croak "Can't run with embedded, config has no mysqld";
+
+- my @no_copy =
++ my %no_copy = map { $_ => 1 }
+ (
+ '#log-error', # Embedded server writes stderr to mysqltest's log file
+ 'slave-net-timeout', # Embedded server are not build with replication
+@@ -464,7 +467,7 @@ sub post_check_embedded_group {
+
+ foreach my $option ( $mysqld->options(), $first_mysqld->options() ) {
+ # Don't copy options whose name is in "no_copy" list
+- next if grep ( $option->name() eq $_, @no_copy);
++ next if $no_copy{$option->name()};
+
+ $config->insert('embedded', $option->name(), $option->value())
+ }
+@@ -474,20 +477,24 @@ sub post_check_embedded_group {
+
+ sub resolve_at_variable {
+ my ($self, $config, $group, $option)= @_;
++ local $_ = $option->value();
++ my ($res, $after);
+
+- # Split the options value on last .
+- my @parts= split(/\./, $option->value());
+- my $option_name= pop(@parts);
+- my $group_name= join('.', @parts);
+-
+- $group_name =~ s/^\@//; # Remove at
++ while (m/(.*?)\@((?:\w+\.)+)(#?[-\w]+)/g) {
++ my ($before, $group_name, $option_name)= ($1, $2, $3);
++ $after = $';
++ chop($group_name);
+
+ my $from_group= $config->group($group_name)
+ or croak "There is no group named '$group_name' that ",
+- "can be used to resolve '$option_name'";
++ "can be used to resolve '$option_name' for test '$self->{testname}'";
+
+- my $from= $from_group->value($option_name);
+- $config->insert($group->name(), $option->name(), $from)
++ my $value= $from_group->value($option_name);
++ $res .= $before.$value;
++ }
++ $res .= $after;
++
++ $config->insert($group->name(), $option->name(), $res)
+ }
+
+
+@@ -499,7 +506,7 @@ sub post_fix_resolve_at_variables {
+ next unless defined $option->value();
+
+ $self->resolve_at_variable($config, $group, $option)
+- if ($option->value() =~ /^\@/);
++ if ($option->value() =~ /\@/);
+ }
+ }
+ }
+@@ -641,37 +648,21 @@ sub new_config {
+ croak "you must pass '$required'" unless defined $args->{$required};
+ }
+
+- # Fill in hosts/port hash
+- my $hosts= {};
+- my $baseport= $args->{baseport};
+- $args->{hosts}= [ 'localhost' ] unless exists($args->{hosts});
+- foreach my $host ( @{$args->{hosts}} ) {
+- $hosts->{$host}= $baseport;
+- }
+-
+ # Open the config template
+ my $config= My::Config->new($args->{'template_path'});
+- my $extra_template_path= $args->{'extra_template_path'};
+- if ($extra_template_path){
+- $config->append(My::Config->new($extra_template_path));
+- }
+ my $self= bless {
+ CONFIG => $config,
+ ARGS => $args,
+- HOSTS => $hosts,
+- NEXT_HOST => 0,
++ PORT => $args->{baseport},
+ SERVER_ID => 1,
++ testname => $args->{testname},
+ }, $class;
+
+-
+- {
+- # Run pre rules
+- foreach my $rule ( @pre_rules ) {
+- &$rule($self, $config);
+- }
++ # Run pre rules
++ foreach my $rule ( @pre_rules ) {
++ &$rule($self, $config);
+ }
+
+-
+ $self->run_section_rules($config,
+ 'cluster_config\.\w*$',
+ @cluster_config_rules);
+@@ -689,9 +680,9 @@ sub new_config {
+ @mysqld_rules);
+
+ $self->run_section_rules($config,
+- 'memcached.',
+- @memcached_rules);
+-
++ 'memcached.',
++ @memcached_rules);
++
+ # [mysqlbinlog] need additional settings
+ $self->run_rules_for_group($config,
+ $config->insert('mysqlbinlog'),
+diff --git a/mysql-test/lib/My/ConfigFactory.pm.memcached b/mysql-test/lib/My/ConfigFactory.pm.memcached
+new file mode 100644
+index 0000000..0b0e468
+--- /dev/null
++++ b/mysql-test/lib/My/ConfigFactory.pm.memcached
+@@ -0,0 +1,718 @@
++# -*- cperl -*-
++# Copyright (c) 2007, 2011, Oracle and/or its affiliates
++#
++# This program is free software; you can redistribute it and/or
++# modify it under the terms of the GNU Library General Public
++# License as published by the Free Software Foundation; version 2
++# of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# Library General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++
++package My::ConfigFactory;
++
++use strict;
++use warnings;
++use Carp;
++
++use My::Config;
++use My::Find;
++use My::Platform;
++
++use File::Basename;
++
++
++#
++# Rules to run first of all
++#
++
++sub add_opt_values {
++ my ($self, $config)= @_;
++
++ # add auto-options
++ $config->insert('OPT', 'port' => sub { fix_port($self, $config) });
++ $config->insert('mysqld', "loose-skip-plugin-$_" => undef) for (@::optional_plugins);
++}
++
++my @pre_rules=
++(
++ \&add_opt_values,
++);
++
++
++my @share_locations= ("share/mariadb", "share/mysql", "sql/share", "share");
++
++
++sub get_basedir {
++ my ($self, $group)= @_;
++ my $basedir= $group->if_exist('basedir') ||
++ $self->{ARGS}->{basedir};
++ return $basedir;
++}
++
++sub get_testdir {
++ my ($self, $group)= @_;
++ my $testdir= $group->if_exist('testdir') ||
++ $self->{ARGS}->{testdir};
++ return $testdir;
++}
++
++# Retrive build directory (which is different from basedir in out-of-source build)
++sub get_bindir {
++ if (defined $ENV{MTR_BINDIR})
++ {
++ return $ENV{MTR_BINDIR};
++ }
++ my ($self, $group)= @_;
++ return $self->get_basedir($group);
++}
++
++sub fix_charset_dir {
++ my ($self, $config, $group_name, $group)= @_;
++ return my_find_dir($self->get_basedir($group),
++ \@share_locations, "charsets");
++}
++
++sub fix_language {
++ my ($self, $config, $group_name, $group)= @_;
++ return my_find_dir($self->get_bindir($group),
++ \@share_locations);
++}
++
++sub fix_datadir {
++ my ($self, $config, $group_name)= @_;
++ my $vardir= $self->{ARGS}->{vardir};
++ return "$vardir/$group_name/data";
++}
++
++sub fix_pidfile {
++ my ($self, $config, $group_name, $group)= @_;
++ my $vardir= $self->{ARGS}->{vardir};
++ return "$vardir/run/$group_name.pid";
++}
++
++sub fix_port {
++ my ($self, $config, $group_name, $group)= @_;
++ return $self->{PORT}++;
++}
++
++sub fix_host {
++ my ($self)= @_;
++ 'localhost'
++}
++
++sub is_unique {
++ my ($config, $name, $value)= @_;
++
++ foreach my $group ( $config->groups() ) {
++ if ($group->option($name)) {
++ if ($group->value($name) eq $value){
++ return 0;
++ }
++ }
++ }
++ return 1;
++}
++
++sub fix_server_id {
++ my ($self, $config, $group_name, $group)= @_;
++#define in the order that mysqlds are listed in my.cnf
++
++ my $server_id= $group->if_exist('server-id');
++ if (defined $server_id){
++ if (!is_unique($config, 'server-id', $server_id)) {
++ croak "The server-id($server_id) for '$group_name' is not unique";
++ }
++ return $server_id;
++ }
++
++ do {
++ $server_id= $self->{SERVER_ID}++;
++ } while(!is_unique($config, 'server-id', $server_id));
++
++ #print "$group_name: server_id: $server_id\n";
++ return $server_id;
++}
++
++sub fix_socket {
++ my ($self, $config, $group_name, $group)= @_;
++ # Put socket file in tmpdir
++ my $dir= $self->{ARGS}->{tmpdir};
++ return "$dir/$group_name.sock";
++}
++
++sub fix_tmpdir {
++ my ($self, $config, $group_name, $group)= @_;
++ my $dir= $self->{ARGS}->{tmpdir};
++ return "$dir/$group_name";
++}
++
++sub fix_log_error {
++ my ($self, $config, $group_name, $group)= @_;
++ my $dir= $self->{ARGS}->{vardir};
++ if ( $::opt_valgrind and $::opt_debug ) {
++ return "$dir/log/$group_name.trace";
++ } else {
++ return "$dir/log/$group_name.err";
++ }
++}
++
++sub fix_log {
++ my ($self, $config, $group_name, $group)= @_;
++ my $dir= dirname($group->value('datadir'));
++ return "$dir/mysqld.log";
++}
++
++sub fix_log_slow_queries {
++ my ($self, $config, $group_name, $group)= @_;
++ my $dir= dirname($group->value('datadir'));
++ return "$dir/mysqld-slow.log";
++}
++
++sub fix_std_data {
++ my ($self, $config, $group_name, $group)= @_;
++ my $testdir= $self->get_testdir($group);
++ return "$testdir/std_data";
++}
++
++sub ssl_supported {
++ my ($self)= @_;
++ return $self->{ARGS}->{ssl};
++}
++
++sub fix_skip_ssl {
++ return if !ssl_supported(@_);
++ # Add skip-ssl if ssl is supported to avoid
++ # that mysqltest connects with SSL by default
++ return 1;
++}
++
++sub fix_ssl_ca {
++ return if !ssl_supported(@_);
++ my $std_data= fix_std_data(@_);
++ return "$std_data/cacert.pem"
++}
++
++sub fix_ssl_server_cert {
++ return if !ssl_supported(@_);
++ my $std_data= fix_std_data(@_);
++ return "$std_data/server-cert.pem"
++}
++
++sub fix_ssl_client_cert {
++ return if !ssl_supported(@_);
++ my $std_data= fix_std_data(@_);
++ return "$std_data/client-cert.pem"
++}
++
++sub fix_ssl_server_key {
++ return if !ssl_supported(@_);
++ my $std_data= fix_std_data(@_);
++ return "$std_data/server-key.pem"
++}
++
++sub fix_ssl_client_key {
++ return if !ssl_supported(@_);
++ my $std_data= fix_std_data(@_);
++ return "$std_data/client-key.pem"
++}
++
++
++#
++# Rules to run for each mysqld in the config
++# - will be run in order listed here
++#
++my @mysqld_rules=
++ (
++ { 'basedir' => sub { return shift->{ARGS}->{basedir}; } },
++ { 'tmpdir' => \&fix_tmpdir },
++ { 'character-sets-dir' => \&fix_charset_dir },
++ { 'lc-messages-dir' => \&fix_language },
++ { 'datadir' => \&fix_datadir },
++ { 'pid-file' => \&fix_pidfile },
++ { '#host' => \&fix_host },
++ { 'port' => \&fix_port },
++ # galera base_port and port used during SST
++ { '#galera_port' => \&fix_port },
++ { '#sst_port' => \&fix_port },
++ { '#memcached_port' => \&fix_port },
++ { 'socket' => \&fix_socket },
++ { '#log-error' => \&fix_log_error },
++ { 'general-log' => 1 },
++ { 'plugin-dir' => sub { $::plugindir } },
++ { 'general-log-file' => \&fix_log },
++ { 'slow-query-log' => 1 },
++ { 'slow-query-log-file' => \&fix_log_slow_queries },
++ { '#user' => sub { return shift->{ARGS}->{user} || ""; } },
++ { '#password' => sub { return shift->{ARGS}->{password} || ""; } },
++ { 'server-id' => \&fix_server_id, },
++ # By default, prevent the started mysqld to access files outside of vardir
++ { 'secure-file-priv' => sub { return shift->{ARGS}->{vardir}; } },
++ { 'ssl-ca' => \&fix_ssl_ca },
++ { 'ssl-cert' => \&fix_ssl_server_cert },
++ { 'ssl-key' => \&fix_ssl_server_key },
++ );
++
++if (IS_WINDOWS)
++{
++ # For simplicity, we use the same names for shared memory and
++ # named pipes.
++ push(@mysqld_rules, {'shared-memory-base-name' => \&fix_socket});
++}
++
++sub fix_ndb_mgmd_port {
++ my ($self, $config, $group_name, $group)= @_;
++ my $hostname= $group->value('HostName');
++ return $self->{PORT}++;
++}
++
++
++sub fix_cluster_dir {
++ my ($self, $config, $group_name, $group)= @_;
++ my $vardir= $self->{ARGS}->{vardir};
++ my (undef, $process_type, $idx, $suffix)= split(/\./, $group_name);
++ return "$vardir/mysql_cluster.$suffix/$process_type.$idx";
++}
++
++
++sub fix_cluster_backup_dir {
++ my ($self, $config, $group_name, $group)= @_;
++ my $vardir= $self->{ARGS}->{vardir};
++ my (undef, $process_type, $idx, $suffix)= split(/\./, $group_name);
++ return "$vardir/mysql_cluster.$suffix/";
++}
++
++
++#
++# Rules to run for each ndb_mgmd in the config
++# - will be run in order listed here
++#
++my @ndb_mgmd_rules=
++(
++ { 'PortNumber' => \&fix_ndb_mgmd_port },
++ { 'DataDir' => \&fix_cluster_dir },
++);
++
++
++#
++# Rules to run for each ndbd in the config
++# - will be run in order listed here
++#
++my @ndbd_rules=
++(
++ { 'HostName' => \&fix_host },
++ { 'DataDir' => \&fix_cluster_dir },
++ { 'BackupDataDir' => \&fix_cluster_backup_dir },
++);
++
++# Rules to run for each memcached in the config
++# - will be run in order listed here
++#
++my @memcached_rules=
++(
++ { '#host' => \&fix_host },
++ { 'port' => \&fix_port },
++);
++
++#
++# Rules to run for each cluster_config section
++# - will be run in order listed here
++#
++my @cluster_config_rules=
++(
++ { 'ndb_mgmd' => \&fix_host },
++ { 'ndbd' => \&fix_host },
++ { 'mysqld' => \&fix_host },
++ { 'ndbapi' => \&fix_host },
++);
++
++
++#
++# Rules to run for [client] section
++# - will be run in order listed here
++#
++my @client_rules=
++(
++);
++
++
++#
++# Rules to run for [mysqltest] section
++# - will be run in order listed here
++#
++my @mysqltest_rules=
++(
++ { 'ssl-ca' => \&fix_ssl_ca },
++ { 'ssl-cert' => \&fix_ssl_client_cert },
++ { 'ssl-key' => \&fix_ssl_client_key },
++ { 'skip-ssl' => \&fix_skip_ssl },
++);
++
++
++#
++# Rules to run for [mysqlbinlog] section
++# - will be run in order listed here
++#
++my @mysqlbinlog_rules=
++(
++ { 'character-sets-dir' => \&fix_charset_dir },
++);
++
++
++#
++# Rules to run for [mysql_upgrade] section
++# - will be run in order listed here
++#
++my @mysql_upgrade_rules=
++(
++ { 'tmpdir' => sub { return shift->{ARGS}->{tmpdir}; } },
++);
++
++
++#
++# Generate a [client.<suffix>] group to be
++# used for connecting to [mysqld.<suffix>]
++#
++sub post_check_client_group {
++ my ($self, $config, $client_group_name, $mysqld_group_name)= @_;
++
++
++ # Settings needed for client, copied from its "mysqld"
++ my %client_needs=
++ (
++ port => 'port',
++ socket => 'socket',
++ host => '#host',
++ user => '#user',
++ password => '#password',
++ );
++ my $group_to_copy_from= $config->group($mysqld_group_name);
++ while (my ($name_to, $name_from)= each( %client_needs )) {
++ my $option= $group_to_copy_from->option($name_from);
++
++ if (! defined $option){
++ #print $config;
++ croak "Could not get value for '$name_from' for test $self->{testname}";
++ }
++ $config->insert($client_group_name, $name_to, $option->value())
++ }
++
++ if (IS_WINDOWS)
++ {
++ if (! $self->{ARGS}->{embedded})
++ {
++ # Shared memory base may or may not be defined (e.g not defined in embedded)
++ my $shm = $group_to_copy_from->option("shared-memory-base-name");
++ if (defined $shm)
++ {
++ $config->insert($client_group_name,"shared-memory-base-name", $shm->value());
++ }
++ }
++ }
++}
++
++
++sub post_check_client_groups {
++ my ($self, $config)= @_;
++
++ my $first_mysqld= $config->first_like('mysqld\.');
++
++ return unless $first_mysqld;
++
++ # Always generate [client] pointing to the first
++ # [mysqld.<suffix>]
++ $self->post_check_client_group($config,
++ 'client',
++ $first_mysqld->name());
++
++ # Then generate [client.<suffix>] for each [mysqld.<suffix>]
++ foreach my $mysqld ( $config->like('mysqld.') ) {
++ $self->post_check_client_group($config,
++ 'client'.$mysqld->after('mysqld'),
++ $mysqld->name())
++ }
++
++}
++
++
++#
++# Generate [embedded] by copying the values
++# needed from the default [mysqld] section
++# and from first [mysqld.<suffix>]
++#
++sub post_check_embedded_group {
++ my ($self, $config)= @_;
++
++ return unless $self->{ARGS}->{embedded};
++
++ my $mysqld= $config->group('mysqld') or
++ croak "Can't run with embedded, config has no default mysqld section";
++
++ my $first_mysqld= $config->first_like('mysqld.') or
++ croak "Can't run with embedded, config has no mysqld";
++
++ my %no_copy = map { $_ => 1 }
++ (
++ '#log-error', # Embedded server writes stderr to mysqltest's log file
++ 'slave-net-timeout', # Embedded server are not build with replication
++ 'shared-memory-base-name', # No shared memory for embedded
++ );
++
++ foreach my $option ( $mysqld->options(), $first_mysqld->options() ) {
++ # Don't copy options whose name is in "no_copy" list
++ next if $no_copy{$option->name()};
++
++ $config->insert('embedded', $option->name(), $option->value())
++ }
++
++}
++
++
++sub resolve_at_variable {
++ my ($self, $config, $group, $option)= @_;
++ local $_ = $option->value();
++ my ($res, $after);
++
++ while (m/(.*?)\@((?:\w+\.)+)(#?[-\w]+)/g) {
++ my ($before, $group_name, $option_name)= ($1, $2, $3);
++ $after = $';
++ chop($group_name);
++
++ my $from_group= $config->group($group_name)
++ or croak "There is no group named '$group_name' that ",
++ "can be used to resolve '$option_name' for test '$self->{testname}'";
++
++ my $value= $from_group->value($option_name);
++ $res .= $before.$value;
++ }
++ $res .= $after;
++
++ $config->insert($group->name(), $option->name(), $res)
++}
++
++
++sub post_fix_resolve_at_variables {
++ my ($self, $config)= @_;
++
++ foreach my $group ( $config->groups() ) {
++ foreach my $option ( $group->options()) {
++ next unless defined $option->value();
++
++ $self->resolve_at_variable($config, $group, $option)
++ if ($option->value() =~ /\@/);
++ }
++ }
++}
++
++sub post_fix_mysql_cluster_section {
++ my ($self, $config)= @_;
++
++ # Add a [mysl_cluster.<suffix>] section for each
++ # defined [cluster_config.<suffix>] section
++ foreach my $group ( $config->like('cluster_config\.\w*$') )
++ {
++ my @urls;
++ # Generate ndb_connectstring for this cluster
++ foreach my $ndb_mgmd ( $config->like('cluster_config.ndb_mgmd.')) {
++ if ($ndb_mgmd->suffix() eq $group->suffix()) {
++ my $host= $ndb_mgmd->value('HostName');
++ my $port= $ndb_mgmd->value('PortNumber');
++ push(@urls, "$host:$port");
++ }
++ }
++ croak "Could not generate valid ndb_connectstring for '$group'"
++ unless @urls > 0;
++ my $ndb_connectstring= join(";", @urls);
++
++ # Add ndb_connectstring to [mysql_cluster.<suffix>]
++ $config->insert('mysql_cluster'.$group->suffix(),
++ 'ndb_connectstring', $ndb_connectstring);
++
++ # Add ndb_connectstring to each mysqld connected to this
++ # cluster
++ foreach my $mysqld ( $config->like('cluster_config.mysqld.')) {
++ if ($mysqld->suffix() eq $group->suffix()) {
++ my $after= $mysqld->after('cluster_config.mysqld');
++ $config->insert("mysqld$after",
++ 'ndb_connectstring', $ndb_connectstring);
++ }
++ }
++ }
++}
++
++#
++# Rules to run last of all
++#
++my @post_rules=
++(
++ \&post_check_client_groups,
++ \&post_fix_mysql_cluster_section,
++ \&post_fix_resolve_at_variables,
++ \&post_check_embedded_group,
++);
++
++
++sub run_rules_for_group {
++ my ($self, $config, $group, @rules)= @_;
++ foreach my $hash ( @rules ) {
++ while (my ($option, $rule)= each( %{$hash} )) {
++ # Only run this rule if the value is not already defined
++ if (!$config->exists($group->name(), $option)) {
++ my $value;
++ if (ref $rule eq "CODE") {
++ # Call the rule function
++ $value= &$rule($self, $config, $group->name(),
++ $config->group($group->name()));
++ } else {
++ $value= $rule;
++ }
++ if (defined $value) {
++ $config->insert($group->name(), $option, $value, 1);
++ }
++ }
++ }
++ }
++}
++
++
++sub run_section_rules {
++ my ($self, $config, $name, @rules)= @_;
++
++ foreach my $group ( $config->like($name) ) {
++ $self->run_rules_for_group($config, $group, @rules);
++ }
++}
++
++
++sub run_generate_sections_from_cluster_config {
++ my ($self, $config)= @_;
++
++ my @options= ('ndb_mgmd', 'ndbd',
++ 'mysqld', 'ndbapi');
++
++ foreach my $group ( $config->like('cluster_config\.\w*$') ) {
++
++ # Keep track of current index per process type
++ my %idxes;
++ map { $idxes{$_}= 1; } @options;
++
++ foreach my $option_name ( @options ) {
++ my $value= $group->value($option_name);
++ my @hosts= split(/,/, $value, -1); # -1 => return also empty strings
++
++ # Add at least one host
++ push(@hosts, undef) unless scalar(@hosts);
++
++ # Assign hosts unless already fixed
++ @hosts= map { $self->fix_host() unless $_; } @hosts;
++
++ # Write the hosts value back
++ $group->insert($option_name, join(",", @hosts));
++
++ # Generate sections for each host
++ foreach my $host ( @hosts ){
++ my $idx= $idxes{$option_name}++;
++
++ my $suffix= $group->suffix();
++ # Generate a section for ndb_mgmd to read
++ $config->insert("cluster_config.$option_name.$idx$suffix",
++ "HostName", $host);
++
++ if ($option_name eq 'mysqld'){
++ my $datadir=
++ $self->fix_cluster_dir($config,
++ "cluster_config.mysqld.$idx$suffix",
++ $group);
++ $config->insert("mysqld.$idx$suffix",
++ 'datadir', "$datadir/data");
++ }
++ }
++ }
++ }
++}
++
++
++sub new_config {
++ my ($class, $args)= @_;
++
++ my @required_args= ('basedir', 'baseport', 'vardir', 'template_path');
++
++ foreach my $required ( @required_args ) {
++ croak "you must pass '$required'" unless defined $args->{$required};
++ }
++
++ # Open the config template
++ my $config= My::Config->new($args->{'template_path'});
++ my $self= bless {
++ CONFIG => $config,
++ ARGS => $args,
++ PORT => $args->{baseport},
++ SERVER_ID => 1,
++ testname => $args->{testname},
++ }, $class;
++
++ # Run pre rules
++ foreach my $rule ( @pre_rules ) {
++ &$rule($self, $config);
++ }
++
++ $self->run_section_rules($config,
++ 'cluster_config\.\w*$',
++ @cluster_config_rules);
++ $self->run_generate_sections_from_cluster_config($config);
++
++ $self->run_section_rules($config,
++ 'cluster_config.ndb_mgmd.',
++ @ndb_mgmd_rules);
++ $self->run_section_rules($config,
++ 'cluster_config.ndbd',
++ @ndbd_rules);
++
++ $self->run_section_rules($config,
++ 'mysqld.',
++ @mysqld_rules);
++
++ $self->run_section_rules($config,
++ 'memcached.',
++ @memcached_rules);
++
++ # [mysqlbinlog] need additional settings
++ $self->run_rules_for_group($config,
++ $config->insert('mysqlbinlog'),
++ @mysqlbinlog_rules);
++
++ # [mysql_upgrade] need additional settings
++ $self->run_rules_for_group($config,
++ $config->insert('mysql_upgrade'),
++ @mysql_upgrade_rules);
++
++ # Additional rules required for [client]
++ $self->run_rules_for_group($config,
++ $config->insert('client'),
++ @client_rules);
++
++
++ # Additional rules required for [mysqltest]
++ $self->run_rules_for_group($config,
++ $config->insert('mysqltest'),
++ @mysqltest_rules);
++
++ {
++ # Run post rules
++ foreach my $rule ( @post_rules ) {
++ &$rule($self, $config);
++ }
++ }
++
++ return $config;
++}
++
++
++1;
++
+diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm
+index eed3100..d3a36e8 100644
+--- a/mysql-test/lib/mtr_cases.pm
++++ b/mysql-test/lib/mtr_cases.pm
+@@ -509,6 +509,9 @@ sub collect_one_suite($)
+ my @new_cases;
+ foreach my $comb (@combinations)
+ {
++ # ENV is used in My::Config::ENV to store the environment so is not a true combination
++ next if ( $comb->{'name'} eq 'ENV' );
++
+ foreach my $test (@cases)
+ {
+
+diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
+index 82b5d1c..da9876a 100755
+--- a/mysql-test/mysql-test-run.pl
++++ b/mysql-test/mysql-test-run.pl
+@@ -360,6 +360,8 @@ sub main {
+ gcov_prepare($basedir);
+ }
+
++ check_wsrep_support();
++
+ if (!$opt_suites) {
+ $opt_suites= $DEFAULT_SUITES;
+ }
+@@ -2895,7 +2897,6 @@ sub check_ndbcluster_support ($) {
+ # which is the default case
+ return;
+ }
+-
+ if ($opt_skip_ndbcluster)
+ {
+ # Compiled with ndbcluster but ndbcluster skipped
+@@ -3311,6 +3312,49 @@ sub ndbcluster_start ($) {
+ }
+
+
++sub have_wsrep() {
++ my $wsrep_on= $mysqld_variables{'wsrep-on'};
++ return defined $wsrep_on
++}
++
++
++sub check_wsrep_support() {
++ if (have_wsrep())
++ {
++ mtr_report(" - binaries built with wsrep patch");
++
++ # ADD scripts to $PATH to that wsrep_sst_* can be found
++ my ($path) = grep { -f "$_/wsrep_sst_rsync"; } "$::bindir/scripts", $::path_client_bindir;
++ mtr_error("No SST scripts") unless $path;
++ $ENV{PATH}="$path:$ENV{PATH}";
++
++ # Check whether WSREP_PROVIDER environment variable is set.
++ if (defined $ENV{'WSREP_PROVIDER'}) {
++ if ((mtr_file_exists($ENV{'WSREP_PROVIDER'}) eq "") &&
++ ($ENV{'WSREP_PROVIDER'} ne "none")) {
++ mtr_error("WSREP_PROVIDER env set to an invalid path");
++ }
++ # WSREP_PROVIDER is valid; set to a valid path or "none").
++ mtr_verbose("WSREP_PROVIDER env set to $ENV{'WSREP_PROVIDER'}");
++ } else {
++ # WSREP_PROVIDER env not defined. Lets try to locate the wsrep provider
++ # library.
++ my $file_wsrep_provider=
++ mtr_file_exists("/usr/lib/galera/libgalera_smm.so",
++ "/usr/lib64/galera/libgalera_smm.so");
++
++ if ($file_wsrep_provider ne "") {
++ # wsrep provider library found !
++ mtr_verbose("wsrep provider library found : $file_wsrep_provider");
++ $ENV{'WSREP_PROVIDER'}= $file_wsrep_provider;
++ } else {
++ mtr_verbose("Could not find wsrep provider library, setting it to 'none'");
++ $ENV{'WSREP_PROVIDER'}= "none";
++ }
++ }
++ }
++}
++
+ sub create_config_file_for_extern {
+ my %opts=
+ (
+@@ -3737,6 +3781,24 @@ sub run_query {
+ }
+
+
++sub sleep_until_returns_true($$$) {
++ my ($tinfo, $mysqld, $query)= @_;
++
++ my $timeout = $opt_start_timeout;
++ my $sleeptime= 100; # Milliseconds
++ my $loops= ($timeout * 1000) / $sleeptime;
++
++ for ( my $loop= 1; $loop <= $loops; $loop++ ) {
++ my $query_result = run_query($tinfo, $mysqld, $query);
++ if (run_query($tinfo, $mysqld, $query) == 1) {
++ return 0;
++ }
++ }
++
++ return 1;
++}
++
++
+ sub do_before_run_mysqltest($)
+ {
+ my $tinfo= shift;
+@@ -5785,6 +5847,13 @@ sub start_servers($) {
+ }
+ return 1;
+ }
++
++ if (have_wsrep()) {
++ if(sleep_until_returns_true($tinfo, $mysqld, 'SELECT @@wsrep_ready')) {
++ $tinfo->{logfile}= "WSREP did not transition to state READY";
++ return 1;
++ }
++ }
+ }
+
+ # Start memcached(s) for each cluster
+diff --git a/mysql-test/r/galera_sst_mode.result b/mysql-test/r/galera_sst_mode.result
+new file mode 100644
+index 0000000..ea25b32
+--- /dev/null
++++ b/mysql-test/r/galera_sst_mode.result
+@@ -0,0 +1,24 @@
++#
++# Test for mysqldump's galera-sst-mode option
++#
++#
++# MDEV-6490: mysqldump unknown option --galera-sst-mode
++#
++CREATE DATABASE bug6490;
++USE bug6490;
++CREATE TABLE t1(c1 INT);
++INSERT INTO t1 values (1);
++INSERT INTO t1 values (2);
++# Save the current gtid_binlog_state.
++# Take a dump of bug6490 database
++DROP TABLE t1;
++# Load the dump
++RESET MASTER;
++SELECT * from t1;
++c1
++1
++2
++# Compare the two gtid_binlog_state's
++# Cleanup
++DROP DATABASE bug6490;
++# End of test
+diff --git a/mysql-test/r/have_wsrep.require b/mysql-test/r/have_wsrep.require
+new file mode 100644
+index 0000000..af32ac7
+--- /dev/null
++++ b/mysql-test/r/have_wsrep.require
+@@ -0,0 +1,2 @@
++Variable_name Value
++wsrep_on ON
+diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
+index fce2f40..149babc 100644
+--- a/mysql-test/r/information_schema.result
++++ b/mysql-test/r/information_schema.result
+@@ -1664,9 +1664,7 @@ drop table if exists t1;drop table if exists t1;
+ drop table if exists t1;drop table if exists t1;
+ drop table if exists t1;drop table if exists t1;
+ drop table if exists t1;drop table if exists t1;
+-drop table if exists t1;drop table if exists
+-Warnings:
+-Warning 1265 Data truncated for column 'VARIABLE_VALUE' at row 1
++drop table if exists t1;drop table if exists t1;
+ set global init_connect="";
+ create table t0 select * from information_schema.global_status where VARIABLE_NAME='COM_SELECT';
+ SELECT 1;
+diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result
+index 18b21ff..d31ef0f 100644
+--- a/mysql-test/r/mysqld--help-notwin.result
++++ b/mysql-test/r/mysqld--help-notwin.result
+@@ -1010,6 +1010,94 @@ The following options may be given as the first argument:
+ -V, --version Output version information and exit.
+ --wait-timeout=# The number of seconds the server waits for activity on a
+ connection before closing it
++ --wsrep-OSU-method[=name]
++ Method for Online Schema Upgrade
++ --wsrep-auto-increment-control
++ To automatically control the assignment of autoincrement
++ variables
++ (Defaults to on; use --skip-wsrep-auto-increment-control to disable.)
++ --wsrep-causal-reads
++ (DEPRECATED) setting this variable is equivalent to
++ setting wsrep_sync_wait READ flag
++ --wsrep-certify-nonPK
++ Certify tables with no primary key
++ (Defaults to on; use --skip-wsrep-certify-nonPK to disable.)
++ --wsrep-cluster-address=name
++ Address to initially connect to cluster
++ --wsrep-cluster-name=name
++ Name for the cluster
++ --wsrep-convert-LOCK-to-trx
++ To convert locking sessions into transactions
++ --wsrep-data-home-dir=name
++ home directory for wsrep provider
++ --wsrep-dbug-option=name
++ DBUG options to provider library
++ --wsrep-debug To enable debug level logging
++ --wsrep-desync To desynchronize the node from the cluster
++ --wsrep-drupal-282555-workaround
++ To use a workaround forbad autoincrement value
++ --wsrep-forced-binlog-format=name
++ binlog format to take effect over user's choice
++ --wsrep-load-data-splitting
++ To commit LOAD DATA transaction after every 10K rows
++ inserted
++ (Defaults to on; use --skip-wsrep-load-data-splitting to disable.)
++ --wsrep-log-conflicts
++ To log multi-master conflicts
++ --wsrep-max-ws-rows=#
++ Max number of rows in write set
++ --wsrep-max-ws-size=#
++ Max write set size (bytes)
++ --wsrep-mysql-replication-bundle=#
++ mysql replication group commit
++ --wsrep-node-address=name
++ Node address
++ --wsrep-node-incoming-address=name
++ Client connection address
++ --wsrep-node-name=name
++ Node name
++ --wsrep-notify-cmd=name
++ --wsrep-on To enable wsrep replication
++ (Defaults to on; use --skip-wsrep-on to disable.)
++ --wsrep-preordered To enable preordered write set processing
++ --wsrep-provider=name
++ Path to replication provider library
++ --wsrep-provider-options=name
++ provider specific options
++ --wsrep-recover Recover database state after crash and exit
++ --wsrep-replicate-myisam
++ To enable myisam replication
++ --wsrep-restart-slave
++ Should MySQL slave be restarted automatically, when node
++ joins back to cluster
++ --wsrep-retry-autocommit=#
++ Max number of times to retry a failed autocommit
++ statement
++ --wsrep-slave-FK-checks
++ Should slave thread do foreign key constraint checks
++ (Defaults to on; use --skip-wsrep-slave-FK-checks to disable.)
++ --wsrep-slave-UK-checks
++ Should slave thread do secondary index uniqueness chesks
++ --wsrep-slave-threads=#
++ Number of slave appliers to launch
++ --wsrep-sst-auth=name
++ Authentication for SST connection
++ --wsrep-sst-donor=name
++ preferred donor node for the SST
++ --wsrep-sst-donor-rejects-queries
++ Reject client queries when donating state snapshot
++ transfer
++ --wsrep-sst-method=name
++ State snapshot transfer method
++ --wsrep-sst-receive-address=name
++ Address where node is waiting for SST contact
++ --wsrep-start-position=name
++ global transaction position to start from
++ --wsrep-sync-wait[=#]
++ Ensure "synchronous" read view before executing an
++ operation of the type specified by bitmask: 1 -
++ READ(includes SELECT, SHOW and BEGIN/START TRANSACTION);
++ 2 - UPDATE and DELETE; 4 - INSERT and REPLACE
+
+ Variables (--variable-name=value)
+ abort-slave-event-count 0
+@@ -1299,6 +1387,45 @@ updatable-views-with-limit YES
+ validate-user-plugins TRUE
+ verbose TRUE
+ wait-timeout 28800
++wsrep-OSU-method TOI
++wsrep-auto-increment-control TRUE
++wsrep-causal-reads FALSE
++wsrep-certify-nonPK TRUE
++wsrep-cluster-address
++wsrep-cluster-name my_wsrep_cluster
++wsrep-convert-LOCK-to-trx FALSE
++wsrep-data-home-dir
++wsrep-dbug-option
++wsrep-debug FALSE
++wsrep-desync FALSE
++wsrep-drupal-282555-workaround FALSE
++wsrep-forced-binlog-format NONE
++wsrep-load-data-splitting TRUE
++wsrep-log-conflicts FALSE
++wsrep-max-ws-rows 131072
++wsrep-max-ws-size 1073741824
++wsrep-mysql-replication-bundle 0
++wsrep-node-address
++wsrep-node-incoming-address AUTO
++wsrep-notify-cmd
++wsrep-on FALSE
++wsrep-preordered FALSE
++wsrep-provider none
++wsrep-provider-options
++wsrep-recover FALSE
++wsrep-replicate-myisam FALSE
++wsrep-restart-slave FALSE
++wsrep-retry-autocommit 1
++wsrep-slave-FK-checks TRUE
++wsrep-slave-UK-checks FALSE
++wsrep-slave-threads 1
++wsrep-sst-auth (No default value)
++wsrep-sst-donor
++wsrep-sst-donor-rejects-queries FALSE
++wsrep-sst-method rsync
++wsrep-sst-receive-address AUTO
++wsrep-start-position 00000000-0000-0000-0000-000000000000:-1
++wsrep-sync-wait 0
+
+ To see what values a running MySQL server is using, type
+ 'mysqladmin variables' instead of 'mysqld --verbose --help'.
+diff --git a/mysql-test/r/not_wsrep.require b/mysql-test/r/not_wsrep.require
+new file mode 100644
+index 0000000..7c8e74a
+--- /dev/null
++++ b/mysql-test/r/not_wsrep.require
+@@ -0,0 +1,2 @@
++HAVE_WSREP
++0
+diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
+index 92d3147..8bc7b0e 100644
+--- a/mysql-test/r/show_check.result
++++ b/mysql-test/r/show_check.result
+@@ -101,19 +101,19 @@ drop table t1;
+ show variables like "wait_timeout%";
+ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
+ def information_schema VARIABLES VARIABLES VARIABLE_NAME Variable_name 253 64 12 N 1 0 8
+-def information_schema VARIABLES VARIABLES VARIABLE_VALUE Value 253 1024 5 Y 0 0 8
++def information_schema VARIABLES VARIABLES VARIABLE_VALUE Value 253 2048 5 Y 0 0 8
+ Variable_name Value
+ wait_timeout 28800
+ show variables like "WAIT_timeout%";
+ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
+ def information_schema VARIABLES VARIABLES VARIABLE_NAME Variable_name 253 64 12 N 1 0 8
+-def information_schema VARIABLES VARIABLES VARIABLE_VALUE Value 253 1024 5 Y 0 0 8
++def information_schema VARIABLES VARIABLES VARIABLE_VALUE Value 253 2048 5 Y 0 0 8
+ Variable_name Value
+ wait_timeout 28800
+ show variables like "this_doesn't_exists%";
+ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
+ def information_schema VARIABLES VARIABLES VARIABLE_NAME Variable_name 253 64 0 N 1 0 8
+-def information_schema VARIABLES VARIABLES VARIABLE_VALUE Value 253 1024 0 Y 0 0 8
++def information_schema VARIABLES VARIABLES VARIABLE_VALUE Value 253 2048 0 Y 0 0 8
+ Variable_name Value
+ show table status from test like "this_doesn't_exists%";
+ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
+diff --git a/mysql-test/std_data/wsrep_notify.sh b/mysql-test/std_data/wsrep_notify.sh
+new file mode 100644
+index 0000000..7036f60
+--- /dev/null
++++ b/mysql-test/std_data/wsrep_notify.sh
+@@ -0,0 +1,99 @@
++#!/bin/sh -eu
++
++# This is a simple example of wsrep notification script (wsrep_notify_cmd).
++# It will create 'wsrep' schema and two tables in it: 'membeship' and 'status'
++# and fill them on every membership or node status change.
++#
++# Edit parameters below to specify the address and login to server.
++
++USER=root
++HOST=127.0.0.1
++PORT=$NODE_MYPORT_1
++
++SCHEMA="mtr_wsrep_notify"
++MEMB_TABLE="$SCHEMA.membership"
++STATUS_TABLE="$SCHEMA.status"
++
++BEGIN="
++SET wsrep_on=0;
++CREATE SCHEMA IF NOT EXISTS $SCHEMA;
++CREATE TABLE IF NOT EXISTS $MEMB_TABLE (
++ idx INT,
++ uuid CHAR(40), /* node UUID */
++ name VARCHAR(32), /* node name */
++ addr VARCHAR(256) /* node address */
++) ENGINE=MEMORY;
++CREATE TABLE IF NOT EXISTS $STATUS_TABLE (
++ size INT, /* component size */
++ idx INT, /* this node index */
++ status CHAR(16), /* this node status */
++ uuid CHAR(40), /* cluster UUID */
++ prim BOOLEAN /* if component is primary */
++) ENGINE=MEMORY;
++BEGIN;
++"
++END="COMMIT;"
++
++configuration_change()
++{
++ echo "$BEGIN;"
++
++ local idx=0
++
++ for NODE in $(echo $MEMBERS | sed s/,/\ /g)
++ do
++ echo "INSERT INTO $MEMB_TABLE VALUES ( $idx, "
++ # Don't forget to properly quote string values
++ echo "'$NODE'" | sed s/\\//\',\'/g
++ echo ");"
++ idx=$(( $idx + 1 ))
++ done
++
++ echo "INSERT INTO $STATUS_TABLE VALUES($idx, $INDEX, '$STATUS', '$CLUSTER_UUID', $PRIMARY);"
++
++ echo "$END"
++}
++
++status_update()
++{
++ echo "SET wsrep_on=0; BEGIN; UPDATE $STATUS_TABLE SET status='$STATUS'; COMMIT;"
++}
++
++COM=status_update # not a configuration change by default
++
++while [ $# -gt 0 ]
++do
++ case $1 in
++ --status)
++ STATUS=$2
++ shift
++ ;;
++ --uuid)
++ CLUSTER_UUID=$2
++ shift
++ ;;
++ --primary)
++ [ "$2" = "yes" ] && PRIMARY="1" || PRIMARY="0"
++ COM=configuration_change
++ shift
++ ;;
++ --index)
++ INDEX=$2
++ shift
++ ;;
++ --members)
++ MEMBERS=$2
++ shift
++ ;;
++ esac
++ shift
++done
++
++# Undefined means node is shutting down
++if [ "$STATUS" != "Undefined" ]
++then
++ $COM | mysql -B -u$USER -h$HOST -P$PORT
++fi
++
++exit 0
++#
+diff --git a/mysql-test/suite/binlog/r/binlog_row_binlog.result b/mysql-test/suite/binlog/r/binlog_row_binlog.result
+index f60f80c..142a522 100644
+--- a/mysql-test/suite/binlog/r/binlog_row_binlog.result
++++ b/mysql-test/suite/binlog/r/binlog_row_binlog.result
+@@ -788,9 +788,11 @@ SELECT * FROM t1;
+ c1
+ 1
+ # Their values should be ON
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
+ Variable_name Value
+ foreign_key_checks ON
++SHOW SESSION VARIABLES LIKE "unique_checks";
++Variable_name Value
+ unique_checks ON
+
+ SET @@SESSION.foreign_key_checks= OFF;
+@@ -806,9 +808,11 @@ c1
+ 1
+ 2
+ # Their values should be OFF
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
+ Variable_name Value
+ foreign_key_checks OFF
++SHOW SESSION VARIABLES LIKE "unique_checks";
++Variable_name Value
+ unique_checks OFF
+ # INSERT INTO t1 VALUES(2)
+ # foreign_key_checks=1 and unique_checks=1
+@@ -824,8 +828,10 @@ c1
+ 1
+ 2
+ # Their values should be OFF
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
+ Variable_name Value
+ foreign_key_checks OFF
++SHOW SESSION VARIABLES LIKE "unique_checks";
++Variable_name Value
+ unique_checks OFF
+ DROP TABLE t1;
+diff --git a/mysql-test/suite/binlog/r/binlog_simplified_binlog_gtid_recovery.result b/mysql-test/suite/binlog/r/binlog_simplified_binlog_gtid_recovery.result
+new file mode 100644
+index 0000000..40b50b8
+--- /dev/null
++++ b/mysql-test/suite/binlog/r/binlog_simplified_binlog_gtid_recovery.result
+@@ -0,0 +1,47 @@
++include/rpl_init.inc [topology=none]
++include/rpl_default_connections.inc
++CREATE TABLE t1 (
++c1 INT NOT NULL PRIMARY KEY
++);
++# Generate master-bin.000002
++FLUSH LOGS;
++INSERT INTO t1 VALUES (1);
++# Generate master-bin.000003
++FLUSH LOGS;
++INSERT INTO t1 VALUES (2);
++# Generate master-bin.000004
++FLUSH LOGS;
++INSERT INTO t1 VALUES (3);
++# Move master-bin.000002 to master-bin.000002.bkp and
++# master-bin.000003 to master-bin.000003.bkp
++#
++# Only master-bin.000001 and master-bin.000004 remain, others are moved
++# , restart the server with enabled simplified-binlog-gtid-recovery
++# and gtid_mode on. If the server scans more than one binary log in
++# every iteration, it will cause read error on the 2nd and 3rd files.
++#
++include/rpl_restart_server.inc [server_number=1 parameters: --simplified-binlog-gtid-recovery=on --gtid-mode=on --enforce-gtid-consistency --log-slave-updates]
++#
++# Verify that GLOBAL.GTID_EXECUTED and GLOBAL.GTID_PURGED are empty
++# after server restarts.
++#
++include/assert.inc [GLOBAL.GTID_EXECUTED must be empty.]
++include/assert.inc [GLOBAL.GTID_PURGED must be empty.]
++DROP TABLE t1;
++# Move master-bin.000004 to master-bin.000004.bkp
++#
++# Only master-bin.000001 and master-bin.000005 remain, others are moved
++# , restart the server with enabled simplified-binlog-gtid-recovery
++# and gtid_mode on again. If the server scans more than one binary
++# log in every iteration, it will cause read error on the 2nd and
++# 4th files.
++#
++include/rpl_restart_server.inc [server_number=1 parameters: --simplified-binlog-gtid-recovery=on --gtid-mode=on --enforce-gtid-consistency --log-slave-updates]
++#
++# Verify that GLOBAL.GTID_EXECUTED contains committed gtid MASTER_UUID:1
++# and GLOBAL.GTID_PURGED is empty after server restarts again.
++#
++include/assert.inc [committed gtid MASTER_UUID:1]
++include/assert.inc [GLOBAL.GTID_PURGED is empty]
++# Move binary logs back.
++include/rpl_end.inc
+diff --git a/mysql-test/suite/binlog/r/binlog_stm_binlog.result b/mysql-test/suite/binlog/r/binlog_stm_binlog.result
+index e0cc9ca..8842ace 100644
+--- a/mysql-test/suite/binlog/r/binlog_stm_binlog.result
++++ b/mysql-test/suite/binlog/r/binlog_stm_binlog.result
+@@ -557,9 +557,11 @@ SELECT * FROM t1;
+ c1
+ 1
+ # Their values should be ON
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
+ Variable_name Value
+ foreign_key_checks ON
++SHOW SESSION VARIABLES LIKE "unique_checks";
++Variable_name Value
+ unique_checks ON
+
+ SET @@SESSION.foreign_key_checks= OFF;
+@@ -575,9 +577,11 @@ c1
+ 1
+ 2
+ # Their values should be OFF
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
+ Variable_name Value
+ foreign_key_checks OFF
++SHOW SESSION VARIABLES LIKE "unique_checks";
++Variable_name Value
+ unique_checks OFF
+ # INSERT INTO t1 VALUES(2)
+ # foreign_key_checks=1 and unique_checks=1
+@@ -593,8 +597,10 @@ c1
+ 1
+ 2
+ # Their values should be OFF
+-SHOW SESSION VARIABLES LIKE "%_checks";
++SHOW SESSION VARIABLES LIKE "foreign_key_checks";
+ Variable_name Value
+ foreign_key_checks OFF
++SHOW SESSION VARIABLES LIKE "unique_checks";
++Variable_name Value
+ unique_checks OFF
+ DROP TABLE t1;
+diff --git a/mysql-test/suite/binlog/t/binlog_simplified_binlog_gtid_recovery.test b/mysql-test/suite/binlog/t/binlog_simplified_binlog_gtid_recovery.test
+new file mode 100644
+index 0000000..5337e7b
+--- /dev/null
++++ b/mysql-test/suite/binlog/t/binlog_simplified_binlog_gtid_recovery.test
+@@ -0,0 +1,120 @@
++# ==== Purpose ====
++#
++# BUG#16741603: MYSQLD SCANS ALL BINARY LOGS ON CRASH RECOVERY
++#
++# Verify that the server does not scan more than one binary log
++# in every iteration when initializing GTID sets on server start
++# if simplified-binlog-gtid-recovery is enabled.
++#
++#
++# ==== Implementation ====
++#
++# 1) Start server and generate four binary logs with gtid_mode off.
++# 2) Move master-bin.000002 to master-bin.000002.bkp and
++# master-bin.000003 to master-bin.000003.bkp
++# 3) Only master-bin.000001 and master-bin.000004 remain, others are moved
++# , restart the server with enabled simplified-binlog-gtid-recovery
++# and gtid_mode on. If the server scans more than one binary log in
++# every iteration, it will cause read error on the 2nd and 3rd files.
++# 4) Verify that GLOBAL.GTID_EXECUTED and GLOBAL.GTID_PURGED are empty
++# after server restarts.
++# 5) Move master-bin.000004 to master-bin.000004.bkp
++# 6) Only master-bin.000001 and master-bin.000005 remain, others are moved
++# , restart the server with enabled simplified-binlog-gtid-recovery
++# and gtid_mode on again. If the server scans more than one binary
++# log in every iteration, it will cause read error on the 2nd and
++# 4th files.
++# 7) Verify that GLOBAL.GTID_EXECUTED contains committed gtid MASTER_UUID:1
++# and GLOBAL.GTID_PURGED is empty after server restarts again.
++# 8) Move binary logs back.
++#
++
++--source include/not_gtid_enabled.inc
++
++# Invoke rpl_init.inc in order to set up the connections needed by
++# rpl_restart_server.inc
++--let $rpl_server_count= 1
++--let $rpl_topology= none
++--source include/rpl_init.inc
++--source include/rpl_default_connections.inc
++
++--let $MYSQLD_DATADIR= `select @@datadir`
++--let $master_uuid= `SELECT @@GLOBAL.SERVER_UUID`
++CREATE TABLE t1 (
++ c1 INT NOT NULL PRIMARY KEY
++);
++
++--echo # Generate master-bin.000002
++FLUSH LOGS;
++INSERT INTO t1 VALUES (1);
++--let $binlog_file2= query_get_value(SHOW MASTER STATUS, File, 1)
++
++--echo # Generate master-bin.000003
++FLUSH LOGS;
++INSERT INTO t1 VALUES (2);
++--let $binlog_file3= query_get_value(SHOW MASTER STATUS, File, 1)
++
++--echo # Generate master-bin.000004
++FLUSH LOGS;
++INSERT INTO t1 VALUES (3);
++--let $binlog_file4= query_get_value(SHOW MASTER STATUS, File, 1)
++
++--echo # Move master-bin.000002 to master-bin.000002.bkp and
++--echo # master-bin.000003 to master-bin.000003.bkp
++--move_file $MYSQLD_DATADIR/$binlog_file2 $MYSQLD_DATADIR/$binlog_file2.bkp
++--move_file $MYSQLD_DATADIR/$binlog_file3 $MYSQLD_DATADIR/$binlog_file3.bkp
++
++--echo #
++--echo # Only master-bin.000001 and master-bin.000004 remain, others are moved
++--echo # , restart the server with enabled simplified-binlog-gtid-recovery
++--echo # and gtid_mode on. If the server scans more than one binary log in
++--echo # every iteration, it will cause read error on the 2nd and 3rd files.
++--echo #
++--let $rpl_server_number= 1
++--let $rpl_server_parameters= --simplified-binlog-gtid-recovery=on --gtid-mode=on --enforce-gtid-consistency --log-slave-updates
++--source include/rpl_restart_server.inc
++
++--echo #
++--echo # Verify that GLOBAL.GTID_EXECUTED and GLOBAL.GTID_PURGED are empty
++--echo # after server restarts.
++--echo #
++--let $assert_text= GLOBAL.GTID_EXECUTED must be empty.
++--let $assert_cond= "[SELECT @@GLOBAL.GTID_EXECUTED]" = ""
++--source include/assert.inc
++--let $assert_text= GLOBAL.GTID_PURGED must be empty.
++--let $assert_cond= "[SELECT @@GLOBAL.GTID_PURGED]" = ""
++--source include/assert.inc
++
++DROP TABLE t1;
++--echo # Move master-bin.000004 to master-bin.000004.bkp
++--move_file $MYSQLD_DATADIR/$binlog_file4 $MYSQLD_DATADIR/$binlog_file4.bkp
++
++--echo #
++--echo # Only master-bin.000001 and master-bin.000005 remain, others are moved
++--echo # , restart the server with enabled simplified-binlog-gtid-recovery
++--echo # and gtid_mode on again. If the server scans more than one binary
++--echo # log in every iteration, it will cause read error on the 2nd and
++--echo # 4th files.
++--echo #
++--let $rpl_server_number= 1
++--let $rpl_server_parameters= --simplified-binlog-gtid-recovery=on --gtid-mode=on --enforce-gtid-consistency --log-slave-updates
++--source include/rpl_restart_server.inc
++
++--echo #
++--echo # Verify that GLOBAL.GTID_EXECUTED contains committed gtid MASTER_UUID:1
++--echo # and GLOBAL.GTID_PURGED is empty after server restarts again.
++--echo #
++--let $assert_text= committed gtid MASTER_UUID:1
++--let $assert_cond= "[SELECT @@GLOBAL.GTID_EXECUTED]" = "$master_uuid:1"
++--source include/assert.inc
++--let $assert_text= GLOBAL.GTID_PURGED is empty
++--let $assert_cond= "[SELECT @@GLOBAL.GTID_PURGED]" = ""
++--source include/assert.inc
++
++--echo # Move binary logs back.
++--move_file $MYSQLD_DATADIR/$binlog_file2.bkp $MYSQLD_DATADIR/$binlog_file2
++--move_file $MYSQLD_DATADIR/$binlog_file3.bkp $MYSQLD_DATADIR/$binlog_file3
++--move_file $MYSQLD_DATADIR/$binlog_file4.bkp $MYSQLD_DATADIR/$binlog_file4
++
++--source include/rpl_end.inc
++
+diff --git a/mysql-test/suite/funcs_1/r/is_columns_is.result b/mysql-test/suite/funcs_1/r/is_columns_is.result
+index f0ab3a7..ec485d4 100644
+--- a/mysql-test/suite/funcs_1/r/is_columns_is.result
++++ b/mysql-test/suite/funcs_1/r/is_columns_is.result
+@@ -111,9 +111,9 @@ def information_schema FILES UPDATE_COUNT 13 NULL YES bigint NULL NULL 19 0 NULL
+ def information_schema FILES UPDATE_TIME 34 NULL YES datetime NULL NULL NULL NULL 0 NULL NULL datetime select
+ def information_schema FILES VERSION 25 NULL YES bigint NULL NULL 20 0 NULL NULL NULL bigint(21) unsigned select
+ def information_schema GLOBAL_STATUS VARIABLE_NAME 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+-def information_schema GLOBAL_STATUS VARIABLE_VALUE 2 NULL YES varchar 1024 3072 NULL NULL NULL utf8 utf8_general_ci varchar(1024) select
++def information_schema GLOBAL_STATUS VARIABLE_VALUE 2 NULL YES varchar 2048 6144 NULL NULL NULL utf8 utf8_general_ci varchar(2048) select
+ def information_schema GLOBAL_VARIABLES VARIABLE_NAME 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+-def information_schema GLOBAL_VARIABLES VARIABLE_VALUE 2 NULL YES varchar 1024 3072 NULL NULL NULL utf8 utf8_general_ci varchar(1024) select
++def information_schema GLOBAL_VARIABLES VARIABLE_VALUE 2 NULL YES varchar 2048 6144 NULL NULL NULL utf8 utf8_general_ci varchar(2048) select
+ def information_schema KEY_COLUMN_USAGE COLUMN_NAME 7 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+ def information_schema KEY_COLUMN_USAGE CONSTRAINT_CATALOG 1 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
+ def information_schema KEY_COLUMN_USAGE CONSTRAINT_NAME 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+@@ -243,9 +243,9 @@ def information_schema SCHEMA_PRIVILEGES PRIVILEGE_TYPE 4 NO varchar 64 192 NUL
+ def information_schema SCHEMA_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
+ def information_schema SCHEMA_PRIVILEGES TABLE_SCHEMA 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+ def information_schema SESSION_STATUS VARIABLE_NAME 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+-def information_schema SESSION_STATUS VARIABLE_VALUE 2 NULL YES varchar 1024 3072 NULL NULL NULL utf8 utf8_general_ci varchar(1024) select
++def information_schema SESSION_STATUS VARIABLE_VALUE 2 NULL YES varchar 2048 6144 NULL NULL NULL utf8 utf8_general_ci varchar(2048) select
+ def information_schema SESSION_VARIABLES VARIABLE_NAME 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+-def information_schema SESSION_VARIABLES VARIABLE_VALUE 2 NULL YES varchar 1024 3072 NULL NULL NULL utf8 utf8_general_ci varchar(1024) select
++def information_schema SESSION_VARIABLES VARIABLE_VALUE 2 NULL YES varchar 2048 6144 NULL NULL NULL utf8 utf8_general_ci varchar(2048) select
+ def information_schema STATISTICS CARDINALITY 10 NULL YES bigint NULL NULL 19 0 NULL NULL NULL bigint(21) select
+ def information_schema STATISTICS COLLATION 9 NULL YES varchar 1 3 NULL NULL NULL utf8 utf8_general_ci varchar(1) select
+ def information_schema STATISTICS COLUMN_NAME 8 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+@@ -507,9 +507,9 @@ NULL information_schema FILES CHECKSUM bigint NULL NULL NULL NULL bigint(21) uns
+ 3.0000 information_schema FILES STATUS varchar 20 60 utf8 utf8_general_ci varchar(20)
+ 3.0000 information_schema FILES EXTRA varchar 255 765 utf8 utf8_general_ci varchar(255)
+ 3.0000 information_schema GLOBAL_STATUS VARIABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
+-3.0000 information_schema GLOBAL_STATUS VARIABLE_VALUE varchar 1024 3072 utf8 utf8_general_ci varchar(1024)
++3.0000 information_schema GLOBAL_STATUS VARIABLE_VALUE varchar 2048 6144 utf8 utf8_general_ci varchar(2048)
+ 3.0000 information_schema GLOBAL_VARIABLES VARIABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
+-3.0000 information_schema GLOBAL_VARIABLES VARIABLE_VALUE varchar 1024 3072 utf8 utf8_general_ci varchar(1024)
++3.0000 information_schema GLOBAL_VARIABLES VARIABLE_VALUE varchar 2048 6144 utf8 utf8_general_ci varchar(2048)
+ 3.0000 information_schema KEY_COLUMN_USAGE CONSTRAINT_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
+ 3.0000 information_schema KEY_COLUMN_USAGE CONSTRAINT_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
+ 3.0000 information_schema KEY_COLUMN_USAGE CONSTRAINT_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
+@@ -639,9 +639,9 @@ NULL information_schema ROUTINES LAST_ALTERED datetime NULL NULL NULL NULL datet
+ 3.0000 information_schema SCHEMA_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
+ 3.0000 information_schema SCHEMA_PRIVILEGES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
+ 3.0000 information_schema SESSION_STATUS VARIABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
+-3.0000 information_schema SESSION_STATUS VARIABLE_VALUE varchar 1024 3072 utf8 utf8_general_ci varchar(1024)
++3.0000 information_schema SESSION_STATUS VARIABLE_VALUE varchar 2048 6144 utf8 utf8_general_ci varchar(2048)
+ 3.0000 information_schema SESSION_VARIABLES VARIABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
+-3.0000 information_schema SESSION_VARIABLES VARIABLE_VALUE varchar 1024 3072 utf8 utf8_general_ci varchar(1024)
++3.0000 information_schema SESSION_VARIABLES VARIABLE_VALUE varchar 2048 6144 utf8 utf8_general_ci varchar(2048)
+ 3.0000 information_schema STATISTICS TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
+ 3.0000 information_schema STATISTICS TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
+ 3.0000 information_schema STATISTICS TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
+diff --git a/mysql-test/suite/galera/galera_2nodes.cnf b/mysql-test/suite/galera/galera_2nodes.cnf
+new file mode 100644
+index 0000000..8adae4c
+--- /dev/null
++++ b/mysql-test/suite/galera/galera_2nodes.cnf
+@@ -0,0 +1,55 @@
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_provider_options='base_port=@mysqld.1.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.1.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++innodb_flush_log_at_trx_commit=2
++
++[mysqld.2]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.2.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++innodb_flush_log_at_trx_commit=2
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_GALERAPORT_1= @mysqld.1.#galera_port
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++
++NODE_SSTPORT_1= @mysqld.1.#sst_port
++NODE_SSTPORT_2= @mysqld.2.#sst_port
+diff --git a/mysql-test/suite/galera/galera_2nodes_as_master.cnf b/mysql-test/suite/galera/galera_2nodes_as_master.cnf
+new file mode 100644
+index 0000000..614ece9
+--- /dev/null
++++ b/mysql-test/suite/galera/galera_2nodes_as_master.cnf
+@@ -0,0 +1,71 @@
++#
++# This .cnf file creates a setup with a 2-node Galera cluster and one stand-alone MySQL server, to be used as a slave
++#
++
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++server-id=1
++binlog-format=row
++log-bin=mysqld-bin
++log_slave_updates
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_provider_options='base_port=@mysqld.1.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.1.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++innodb_flush_log_at_trx_commit=2
++
++[mysqld.2]
++server-id=2
++binlog-format=row
++log-bin=mysqld-bin
++log_slave_updates
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.2.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++innodb_flush_log_at_trx_commit=2
++
++[mysqld.3]
++server-id=3
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_MYPORT_3= @mysqld.3.port
++NODE_MYSOCK_3= @mysqld.3.socket
++
++NODE_GALERAPORT_1= @mysqld.1.#galera_port
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++
++NODE_SSTPORT_1= @mysqld.1.#sst_port
++NODE_SSTPORT_2= @mysqld.2.#sst_port
+diff --git a/mysql-test/suite/galera/galera_2nodes_as_slave.cnf b/mysql-test/suite/galera/galera_2nodes_as_slave.cnf
+new file mode 100644
+index 0000000..5d14109
+--- /dev/null
++++ b/mysql-test/suite/galera/galera_2nodes_as_slave.cnf
+@@ -0,0 +1,68 @@
++#
++# This .cnf file creates a setup with 1 standard MySQL server, followed by a 2-node Galera cluster
++#
++
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++log-bin=mysqld-bin
++binlog-format=row
++server-id=1
++
++[mysqld.2]
++binlog-format=row
++server-id=2
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_provider_options='base_port=@mysqld.2.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++innodb_flush_log_at_trx_commit=2
++
++[mysqld.3]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.2.#galera_port'
++wsrep_provider_options='base_port=@mysqld.3.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.3.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.3.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++innodb_flush_log_at_trx_commit=2
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_MYPORT_3= @mysqld.2.port
++NODE_MYSOCK_3= @mysqld.2.socket
++
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++NODE_GALERAPORT_3= @mysqld.3.#galera_port
++
++NODE_SSTPORT_2= @mysqld.2.#sst_port
++NODE_SSTPORT_3= @mysqld.3.#sst_port
+diff --git a/mysql-test/suite/galera/galera_4nodes.cnf b/mysql-test/suite/galera/galera_4nodes.cnf
+new file mode 100644
+index 0000000..068c734
+--- /dev/null
++++ b/mysql-test/suite/galera/galera_4nodes.cnf
+@@ -0,0 +1,97 @@
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_provider_options='base_port=@mysqld.1.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.1.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[mysqld.2]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.2.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[mysqld.3]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.3.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.3.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.3.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[mysqld.4]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.4.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.4.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.4.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_MYPORT_3= @mysqld.3.port
++NODE_MYSOCK_3= @mysqld.3.socket
++
++NODE_MYPORT_4= @mysqld.4.port
++NODE_MYSOCK_4= @mysqld.4.socket
++
++NODE_GALERAPORT_1= @mysqld.1.#galera_port
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++NODE_GALERAPORT_3= @mysqld.3.#galera_port
++NODE_GALERAPORT_4= @mysqld.4.#galera_port
++
++NODE_SSTPORT_1= @mysqld.1.#sst_port
++NODE_SSTPORT_2= @mysqld.2.#sst_port
++NODE_SSTPORT_3= @mysqld.3.#sst_port
++NODE_SSTPORT_4= @mysqld.4.#sst_port
+diff --git a/mysql-test/suite/galera/include/galera_have_debug_sync.inc b/mysql-test/suite/galera/include/galera_have_debug_sync.inc
+new file mode 100644
+index 0000000..7c01560
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_have_debug_sync.inc
+@@ -0,0 +1,9 @@
++--disable_query_log
++
++--let $galera_have_debug_sync = `SELECT 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_debug_sync_waiters'`
++
++--if (!$galera_have_debug_sync) {
++ --skip "Test requires Galera debug library with debug_sync functionality"
++}
++
++--enable_query_log
+diff --git a/mysql-test/suite/galera/include/galera_load_provider.inc b/mysql-test/suite/galera/include/galera_load_provider.inc
+new file mode 100644
+index 0000000..0ecf43e
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_load_provider.inc
+@@ -0,0 +1,10 @@
++--echo Loading wsrep provider ...
++
++--disable_query_log
++--eval SET GLOBAL wsrep_provider = '$wsrep_provider_orig';
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++
++--enable_reconnect
++--source include/wait_until_connected_again.inc
++--source include/galera_wait_ready.inc
+diff --git a/mysql-test/suite/galera/include/galera_reset_cluster_address.inc b/mysql-test/suite/galera/include/galera_reset_cluster_address.inc
+new file mode 100644
+index 0000000..02937ec
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_reset_cluster_address.inc
+@@ -0,0 +1,12 @@
++--echo Resetting wsrep_cluster_address
++
++--let $wsrep_cluster_size_orig = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'`
++
++SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++
++--source include/wait_until_connected_again.inc
++
++# Wait for wsrep_cluster_size to go back to its original value
++
++--let $wait_condition = SELECT VARIABLE_VALUE = $wsrep_cluster_size_orig FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
+diff --git a/mysql-test/suite/galera/include/galera_sst_restore.inc b/mysql-test/suite/galera/include/galera_sst_restore.inc
+new file mode 100644
+index 0000000..a08b148
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_sst_restore.inc
+@@ -0,0 +1,29 @@
++#
++# Restore the various options used for SST to their original values
++# so that MTR's end-of-test checks are happy.
++#
++
++--connection node_1
++CALL mtr.add_suppression("Slave SQL: Error 'The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement' on query");
++
++--disable_query_log
++--eval SET GLOBAL wsrep_sst_auth = '$wsrep_sst_auth_orig';
++--enable_query_log
++
++--error 0,ER_CANNOT_USER
++DROP USER sst;
++
++--connection node_2
++CALL mtr.add_suppression("Slave SQL: Error 'The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement' on query");
++CALL mtr.add_suppression("InnoDB: Error: Table \"mysql\"\\.\"innodb_index_stats\" not found");
++CALL mtr.add_suppression("InnoDB: New log files created");
++CALL mtr.add_suppression("InnoDB: Creating foreign key constraint system tables");
++CALL mtr.add_suppression("Can't open and lock time zone table");
++CALL mtr.add_suppression("Can't open and lock privilege tables");
++CALL mtr.add_suppression("Info table is not ready to be used");
++CALL mtr.add_suppression("Native table .* has the wrong structure");
++
++--disable_query_log
++--eval SET GLOBAL wsrep_sst_method = '$wsrep_sst_method_orig';
++--eval SET GLOBAL wsrep_sst_receive_address = '$wsrep_sst_receive_address_orig';
++--enable_query_log
+diff --git a/mysql-test/suite/galera/include/galera_sst_set_mysqldump.inc b/mysql-test/suite/galera/include/galera_sst_set_mysqldump.inc
+new file mode 100644
+index 0000000..405c16c
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_sst_set_mysqldump.inc
+@@ -0,0 +1,22 @@
++#
++# Set all the variables required for the SST to be performed via mysqldump
++#
++
++--echo Setting SST method to mysqldump ...
++
++--connection node_1
++# We need a user with a password to perform SST, otherwise we hit LP #1378253
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++
++--let $wsrep_sst_auth_orig = `SELECT @@wsrep_sst_auth`
++SET GLOBAL wsrep_sst_auth = 'sst:sst';
++
++--connection node_2
++--let $wsrep_sst_method_orig = `SELECT @@wsrep_sst_method`
++--let $wsrep_sst_receive_address_orig = `SELECT @@wsrep_sst_receive_address`
++
++--disable_query_log
++# Set wsrep_sst_receive_address to the SQL port
++--eval SET GLOBAL wsrep_sst_receive_address = '127.0.0.2:$NODE_MYPORT_2';
++--enable_query_log
++SET GLOBAL wsrep_sst_method = 'mysqldump';
+diff --git a/mysql-test/suite/galera/include/galera_st_clean_slave.inc b/mysql-test/suite/galera/include/galera_st_clean_slave.inc
+new file mode 100644
+index 0000000..3a49f4f
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_st_clean_slave.inc
+@@ -0,0 +1,115 @@
++--echo Performing State Transfer on a server that starts from a clean var directory
++--echo This is accomplished by shutting down node #2 and removing its var directory before restarting it
++
++--connection node_1
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++
++--echo Shutting down server ...
++--source include/shutdown_mysqld.inc
++
++--connection node_1
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++--echo Cleaning var directory ...
++--remove_files_wildcard $MYSQLTEST_VARDIR/mysqld.2/data/mtr
++--remove_files_wildcard $MYSQLTEST_VARDIR/mysqld.2/data/performance_schema
++--remove_files_wildcard $MYSQLTEST_VARDIR/mysqld.2/data/test
++--remove_files_wildcard $MYSQLTEST_VARDIR/mysqld.2/data/mysql
++--remove_files_wildcard $MYSQLTEST_VARDIR/mysqld.2/data
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++
++--connect node_1a_galera_st_clean_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++
++--connection node_2
++--echo Starting server ...
++--source include/start_mysqld.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++
++--connection node_1
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++
++--connection node_1a_galera_st_clean_slave
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_1
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/include/galera_st_disconnect_slave.inc b/mysql-test/suite/galera/include/galera_st_disconnect_slave.inc
+new file mode 100644
+index 0000000..c886974
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_st_disconnect_slave.inc
+@@ -0,0 +1,105 @@
++--echo Performing State Transfer on a server that has been temporarily disconnected
++
++--connection node_1
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++
++--source suite/galera/include/galera_unload_provider.inc
++
++--connection node_1
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++
++--connect node_1a_galera_st_disconnect_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++
++--connection node_2
++--source suite/galera/include/galera_load_provider.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++
++--connection node_1
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++
++--connection node_1a_galera_st_disconnect_slave
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_1
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/include/galera_st_kill_slave.inc b/mysql-test/suite/galera/include/galera_st_kill_slave.inc
+new file mode 100644
+index 0000000..534d7b6
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_st_kill_slave.inc
+@@ -0,0 +1,110 @@
++--echo Performing State Transfer on a server that has been killed and restarted
++
++--connection node_1
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++
++--source include/kill_galera.inc
++
++--connection node_1
++--source include/wait_until_connected_again.inc
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++
++--connect node_1a_galera_st_kill_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++
++--connection node_2
++--let $galera_wsrep_recover_server_id=2
++--source suite/galera/include/galera_wsrep_recover.inc
++
++--echo Starting server ...
++--source include/start_mysqld.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++
++--connection node_1
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++
++--connection node_1a_galera_st_kill_slave
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_1
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/include/galera_st_kill_slave_ddl.inc b/mysql-test/suite/galera/include/galera_st_kill_slave_ddl.inc
+new file mode 100644
+index 0000000..8b99b1e
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_st_kill_slave_ddl.inc
+@@ -0,0 +1,123 @@
++--echo Performing State Transfer on a server that has been killed and restarted
++--echo while a DDL was in progress on it
++
++--connection node_1
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++
++--connection node_2
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++
++# Suspend the applier as it applies the ALTER TABLE
++--let $debug_orig = `SELECT @@debug`
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++
++--connection node_1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++
++--connection node_2
++SET wsrep_sync_wait = 0;
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'debug sync point: now'
++--source include/wait_condition.inc
++
++--source include/kill_galera.inc
++
++--connection node_1
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++
++--connect node_1a_galera_st_kill_slave_ddl, 127.0.0.1, root, , test, $NODE_MYPORT_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++
++--connection node_2
++--let $galera_wsrep_recover_server_id=2
++--source suite/galera/include/galera_wsrep_recover.inc
++
++--connection node_2
++--echo Starting server ...
++--source include/start_mysqld.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++
++--connection node_1
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++
++--connection node_1a_galera_st_kill_slave_ddl
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_1
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/include/galera_st_shutdown_slave.inc b/mysql-test/suite/galera/include/galera_st_shutdown_slave.inc
+new file mode 100644
+index 0000000..6c09b0c
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_st_shutdown_slave.inc
+@@ -0,0 +1,107 @@
++--echo Performing State Transfer on a server that has been shut down cleanly and restarted
++
++--connection node_1
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++
++--echo Shutting down server ...
++--source include/shutdown_mysqld.inc
++
++--connection node_1
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++
++--connect node_1a_galera_st_shutdown_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++
++--connection node_2
++--echo Starting server ...
++--source include/start_mysqld.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++
++--connection node_1
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++
++--connection node_1a_galera_st_shutdown_slave
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_1
++SELECT COUNT(*) = 35 FROM t1;
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/include/galera_unload_provider.inc b/mysql-test/suite/galera/include/galera_unload_provider.inc
+new file mode 100644
+index 0000000..edc7eb3
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_unload_provider.inc
+@@ -0,0 +1,7 @@
++--echo Unloading wsrep provider ...
++
++--let $wsrep_cluster_address_orig = `SELECT @@wsrep_cluster_address`
++--let $wsrep_provider_orig = `SELECT @@wsrep_provider`
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++
++SET GLOBAL wsrep_provider = 'none';
+diff --git a/mysql-test/suite/galera/include/galera_wsrep_recover.inc b/mysql-test/suite/galera/include/galera_wsrep_recover.inc
+new file mode 100644
+index 0000000..3126955
+--- /dev/null
++++ b/mysql-test/suite/galera/include/galera_wsrep_recover.inc
+@@ -0,0 +1,23 @@
++--echo Performing --wsrep-recover ...
++--exec $MYSQLD --defaults-group-suffix=.$galera_wsrep_recover_server_id --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover > $MYSQL_TMP_DIR/galera_wsrep_recover.log 2>&1
++
++--perl
++ use strict;
++ my $wsrep_start_position_str = "grep 'WSREP: Recovered position:' $ENV{MYSQL_TMP_DIR}/galera_wsrep_recover.log | sed 's/.*WSREP\:\ Recovered\ position://' | sed 's/^[ \t]*//'";
++ my $wsrep_start_position = `grep 'WSREP: Recovered position:' $ENV{MYSQL_TMP_DIR}/galera_wsrep_recover.log | sed 's/.*WSREP\:\ Recovered\ position://' | sed 's/^[ \t]*//'`;
++ chomp($wsrep_start_position);
++
++ die if $wsrep_start_position eq '';
++
++ open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/galera_wsrep_start_position.inc") or die;
++ print FILE "--let \$galera_wsrep_start_position = $wsrep_start_position\n";
++ close FILE;
++EOF
++
++--source $MYSQL_TMP_DIR/galera_wsrep_start_position.inc
++
++if ($galera_wsrep_start_position == '') {
++ --die "Could not obtain wsrep_start_position."
++}
++
++--remove_file $MYSQL_TMP_DIR/galera_wsrep_start_position.inc
+diff --git a/mysql-test/suite/galera/my.cnf b/mysql-test/suite/galera/my.cnf
+new file mode 100644
+index 0000000..ca163a5
+--- /dev/null
++++ b/mysql-test/suite/galera/my.cnf
+@@ -0,0 +1 @@
++!include galera_2nodes.cnf
+diff --git a/mysql-test/suite/galera/r/galera_account_management.result b/mysql-test/suite/galera/r/galera_account_management.result
+new file mode 100644
+index 0000000..9b3ae9b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_account_management.result
+@@ -0,0 +1,40 @@
++CREATE USER user1, user2 IDENTIFIED BY 'password';
++SELECT COUNT(*) = 2 FROM mysql.user WHERE user IN ('user1', 'user2');
++COUNT(*) = 2
++1
++RENAME USER user2 TO user3;
++SELECT COUNT(*) = 0 FROM mysql.user WHERE user = 'user2';
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 1 FROM mysql.user WHERE user = 'user3';
++COUNT(*) = 1
++1
++SET PASSWORD FOR user3 = PASSWORD('foo');
++SELECT password != '' FROM mysql.user WHERE user = 'user3';
++password != ''
++1
++DROP USER user1, user3;
++SELECT COUNT(*) = 0 FROM mysql.user WHERE user IN ('user1', 'user2');
++COUNT(*) = 0
++1
++GRANT ALL ON *.* TO user4 IDENTIFIED BY 'password';
++SELECT COUNT(*) = 1 FROM mysql.user WHERE user = 'user4';
++COUNT(*) = 1
++1
++SELECT Select_priv = 'Y' FROM mysql.user WHERE user = 'user4';
++Select_priv = 'Y'
++1
++CREATE USER user5;
++GRANT PROXY ON user4 TO user5;
++SELECT COUNT(*) = 1 FROM mysql.proxies_priv WHERE user = 'user5';
++COUNT(*) = 1
++1
++REVOKE ALL PRIVILEGES ON *.* FROM user4;
++SELECT Select_priv = 'N' FROM mysql.user WHERE user = 'user4';
++Select_priv = 'N'
++1
++REVOKE PROXY ON user4 FROM user5;
++SELECT COUNT(*) = 0 FROM mysql.proxies_priv WHERE user = 'user5';
++COUNT(*) = 0
++1
++DROP USER user4, user5;
+diff --git a/mysql-test/suite/galera/r/galera_alter_engine_innodb.result b/mysql-test/suite/galera/r/galera_alter_engine_innodb.result
+new file mode 100644
+index 0000000..2b30ac5
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_alter_engine_innodb.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++ALTER TABLE t1 ENGINE=InnoDB;
++SELECT ENGINE = 'InnoDB' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++ENGINE = 'InnoDB'
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_alter_engine_myisam.result b/mysql-test/suite/galera/r/galera_alter_engine_myisam.result
+new file mode 100644
+index 0000000..280cb58
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_alter_engine_myisam.result
+@@ -0,0 +1,11 @@
++SET GLOBAL wsrep_replicate_myisam = TRUE;
++CREATE TABLE t1 (f1 INTEGER) ENGINE=MyISAM;
++INSERT INTO t1 VALUES (1);
++ALTER TABLE t1 ENGINE=InnoDB;
++SELECT ENGINE = 'InnoDB' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++ENGINE = 'InnoDB'
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_alter_table_force.result b/mysql-test/suite/galera/r/galera_alter_table_force.result
+new file mode 100644
+index 0000000..401ab46
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_alter_table_force.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++ALTER TABLE t1 FORCE;
++SELECT ENGINE = 'InnoDB' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++ENGINE = 'InnoDB'
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_as_master.result b/mysql-test/suite/galera/r/galera_as_master.result
+new file mode 100644
+index 0000000..cca5535
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_as_master.result
+@@ -0,0 +1,9 @@
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++INSERT INTO t1 VALUES(2);
++DROP TABLE t1;
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/r/galera_as_master_gtid.result b/mysql-test/suite/galera/r/galera_as_master_gtid.result
+new file mode 100644
+index 0000000..8dfe462
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_as_master_gtid.result
+@@ -0,0 +1,59 @@
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++uuids_do_not_match
++1
++SHOW BINLOG EVENTS IN 'mysqld-bin.000002' FROM 120;
++Log_name Pos Event_type Server_id End_log_pos Info
++mysqld-bin.000002 120 Previous_gtids 1 151
++mysqld-bin.000002 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
++mysqld-bin.000002 199 Query 1 327 use `test`; CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB
++mysqld-bin.000002 327 Gtid 1 375 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
++mysqld-bin.000002 375 Query 1 452 BEGIN
++mysqld-bin.000002 452 Table_map 1 497 table_id: # (test.t1)
++mysqld-bin.000002 497 Write_rows 1 537 table_id: # flags: STMT_END_F
++mysqld-bin.000002 537 Xid 1 568 COMMIT /* xid=# */
++INSERT INTO t1 VALUES(2);
++uuids_do_not_match
++1
++uuids_match
++1
++SHOW BINLOG EVENTS IN 'mysqld-bin.000003' FROM 120;
++Log_name Pos Event_type Server_id End_log_pos Info
++mysqld-bin.000003 120 Previous_gtids 2 151
++mysqld-bin.000003 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
++mysqld-bin.000003 199 Query 1 327 use `test`; CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB
++mysqld-bin.000003 327 Gtid 1 375 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
++mysqld-bin.000003 375 Query 1 443 BEGIN
++mysqld-bin.000003 443 Table_map 1 488 table_id: # (test.t1)
++mysqld-bin.000003 488 Write_rows 1 528 table_id: # flags: STMT_END_F
++mysqld-bin.000003 528 Xid 1 559 COMMIT /* xid=# */
++mysqld-bin.000003 559 Gtid 2 607 SET @@SESSION.GTID_NEXT= '<effective_uuid>:3'
++mysqld-bin.000003 607 Query 2 684 BEGIN
++mysqld-bin.000003 684 Table_map 2 729 table_id: # (test.t1)
++mysqld-bin.000003 729 Write_rows 2 769 table_id: # flags: STMT_END_F
++mysqld-bin.000003 769 Xid 2 800 COMMIT /* xid=# */
++uuids_do_not_match
++1
++uuids_match
++1
++SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
++Log_name Pos Event_type Server_id End_log_pos Info
++mysqld-bin.000001 120 Previous_gtids 3 151
++mysqld-bin.000001 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
++mysqld-bin.000001 199 Query 1 327 use `test`; CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB
++mysqld-bin.000001 327 Gtid 1 375 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
++mysqld-bin.000001 375 Query 1 443 BEGIN
++mysqld-bin.000001 443 Table_map 1 488 table_id: # (test.t1)
++mysqld-bin.000001 488 Write_rows 1 528 table_id: # flags: STMT_END_F
++mysqld-bin.000001 528 Xid 1 559 COMMIT /* xid=# */
++mysqld-bin.000001 559 Gtid 2 607 SET @@SESSION.GTID_NEXT= '<effective_uuid>:3'
++mysqld-bin.000001 607 Query 2 675 BEGIN
++mysqld-bin.000001 675 Table_map 2 720 table_id: # (test.t1)
++mysqld-bin.000001 720 Write_rows 2 760 table_id: # flags: STMT_END_F
++mysqld-bin.000001 760 Xid 2 791 COMMIT /* xid=# */
++DROP TABLE t1;
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/r/galera_as_master_gtid_change_master.result b/mysql-test/suite/galera/r/galera_as_master_gtid_change_master.result
+new file mode 100644
+index 0000000..80fbccf
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_as_master_gtid_change_master.result
+@@ -0,0 +1,15 @@
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++INSERT INTO t1 VALUES(2);
++STOP SLAVE;
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++INSERT INTO t1 VALUES(3);
++INSERT INTO t1 VALUES(4);
++DROP TABLE t1;
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/r/galera_as_slave.result b/mysql-test/suite/galera/r/galera_as_slave.result
+new file mode 100644
+index 0000000..f03c813
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_as_slave.result
+@@ -0,0 +1,16 @@
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++INSERT INTO t1 VALUES (3);
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++DROP TABLE t1;
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/r/galera_as_slave_gtid.result b/mysql-test/suite/galera/r/galera_as_slave_gtid.result
+new file mode 100644
+index 0000000..f7ee666
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_as_slave_gtid.result
+@@ -0,0 +1,18 @@
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++SELECT LENGTH(@@global.gtid_executed) > 1;
++LENGTH(@@global.gtid_executed) > 1
++1
++gtid_executed_equal
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++gtid_executed_equal
++1
++DROP TABLE t1;
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/r/galera_bf_abort.result b/mysql-test/suite/galera/r/galera_bf_abort.result
+new file mode 100644
+index 0000000..c55f1a4
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_bf_abort.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (2);
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++wsrep_local_aborts_increment
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_bf_abort_for_update.result b/mysql-test/suite/galera/r/galera_bf_abort_for_update.result
+new file mode 100644
+index 0000000..3978a3d
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_bf_abort_for_update.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (1);
++SELECT * FROM t1 FOR UPDATE;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++wsrep_local_aborts_increment
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_bf_abort_ftwrl.result b/mysql-test/suite/galera/r/galera_bf_abort_ftwrl.result
+new file mode 100644
+index 0000000..e381917
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_bf_abort_ftwrl.result
+@@ -0,0 +1,8 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++FLUSH TABLES WITH READ LOCK;;
++INSERT INTO t1 VALUES (1);
++UNLOCK TABLES;
++wsrep_local_aborts_increment
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_bf_abort_get_lock.result b/mysql-test/suite/galera/r/galera_bf_abort_get_lock.result
+new file mode 100644
+index 0000000..2e44a77
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_bf_abort_get_lock.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SELECT GET_LOCK("foo", 1000);
++GET_LOCK("foo", 1000)
++1
++SET AUTOCOMMIT=OFF;
++INSERT INTO t1 VALUES (1);
++SELECT GET_LOCK("foo", 1000);;
++INSERT INTO t1 VALUES (1);
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++wsrep_local_aborts_increment
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_bf_abort_lock_table.result b/mysql-test/suite/galera/r/galera_bf_abort_lock_table.result
+new file mode 100644
+index 0000000..e657e72
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_bf_abort_lock_table.result
+@@ -0,0 +1,8 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++LOCK TABLE t1 WRITE;
++INSERT INTO t1 VALUES (1);;
++INSERT INTO t1 VALUES (2);
++wsrep_local_aborts_increment
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_bf_abort_sleep.result b/mysql-test/suite/galera/r/galera_bf_abort_sleep.result
+new file mode 100644
+index 0000000..8e85a5f
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_bf_abort_sleep.result
+@@ -0,0 +1,9 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++INSERT INTO t1 VALUES (1);
++SELECT SLEEP(1000);;
++INSERT INTO t1 VALUES (1);
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++wsrep_local_aborts_increment
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_binlog_cache_size.result b/mysql-test/suite/galera/r/galera_binlog_cache_size.result
+new file mode 100644
+index 0000000..9726cf2
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_binlog_cache_size.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (f1 VARCHAR(767)) ENGINE=InnoDB;
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++SET GLOBAL binlog_cache_size=4096;
++SET GLOBAL max_binlog_cache_size=4096;
++SET AUTOCOMMIT=ON;
++START TRANSACTION;
++INSERT INTO t1 SELECT REPEAT('a', 767) FROM ten;
++INSERT INTO t1 SELECT REPEAT('a', 767) FROM ten;
++ERROR HY000: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_binlog_checksum.result b/mysql-test/suite/galera/r/galera_binlog_checksum.result
+new file mode 100644
+index 0000000..a6ab623
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_binlog_checksum.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_binlog_event_max_size_max.result b/mysql-test/suite/galera/r/galera_binlog_event_max_size_max.result
+new file mode 100644
+index 0000000..4156c0c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_binlog_event_max_size_max.result
+@@ -0,0 +1,9 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 VARCHAR(1000));
++INSERT INTO t1 SELECT REPEAT('x', 1000) FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++SELECT COUNT(*) = 10000 FROM t1;
++COUNT(*) = 10000
++1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_binlog_event_max_size_min.result b/mysql-test/suite/galera/r/galera_binlog_event_max_size_min.result
+new file mode 100644
+index 0000000..984a943
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_binlog_event_max_size_min.result
+@@ -0,0 +1,6 @@
++CREATE TABLE t1 (f1 VARCHAR(1000));
++INSERT INTO t1 VALUES (REPEAT('x', 1000));
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = REPEAT('x', 1000);
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_binlog_row_image.result b/mysql-test/suite/galera/r/galera_binlog_row_image.result
+new file mode 100644
+index 0000000..a1f0fb4
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_binlog_row_image.result
+@@ -0,0 +1,79 @@
++SET SESSION binlog_row_image=minimal;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER NOT NULL UNIQUE) ENGINE=InnoDB;
++CREATE TABLE t3 (f1 VARCHAR(1)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t3 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t3 WHERE f1 = 1;
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++UPDATE t2 SET f1 = 2 WHERE f1 = 1;
++UPDATE t3 SET f1 = 2 WHERE f1 = 1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 2;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t3 WHERE f1 = 2;
++COUNT(*) = 1
++1
++DELETE FROM t1;
++DELETE FROM t2;
++DELETE FROM t3;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t3;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
++SET SESSION binlog_row_image=noblob;
++CREATE TABLE t1 (f1 BLOB, f2 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t1 VALUES ('abc', 1);
++INSERT INTO t2 VALUES ('abc');
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'abc';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 'abc';
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 'xyz';
++UPDATE t2 SET f1 = 'xyz';
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'xyz';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 'xyz';
++COUNT(*) = 1
++1
++UPDATE t1 SET f2 = 2 WHERE f2 = 1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 2;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'xyz';
++COUNT(*) = 1
++1
++DELETE FROM t1;
++DELETE FROM t2;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_binlog_rows_query_log_events.result b/mysql-test/suite/galera/r/galera_binlog_rows_query_log_events.result
+new file mode 100644
+index 0000000..80ae3d0
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_binlog_rows_query_log_events.result
+@@ -0,0 +1,12 @@
++SET GLOBAL binlog_rows_query_log_events=TRUE;
++CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++COUNT(*) = 1
++1
++SET GLOBAL binlog_rows_query_log_events = 0;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_create_function.result b/mysql-test/suite/galera/r/galera_create_function.result
+new file mode 100644
+index 0000000..4d19faf
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_create_function.result
+@@ -0,0 +1,55 @@
++CREATE USER 'user1';
++CREATE
++DEFINER = 'user1'
++FUNCTION f1 (param INTEGER)
++RETURNS VARCHAR(200)
++COMMENT 'f1_comment'
++LANGUAGE SQL
++NOT DETERMINISTIC
++MODIFIES SQL DATA
++SQL SECURITY DEFINER
++RETURN 'abc';
++GRANT EXECUTE ON FUNCTION f1 TO user1;
++CREATE
++DEFINER = CURRENT_USER
++FUNCTION f2 (param VARCHAR(100))
++RETURNS INTEGER
++DETERMINISTIC
++NO SQL
++SQL SECURITY INVOKER
++RETURN 123;
++SHOW CREATE FUNCTION f1;
++Function sql_mode Create Function character_set_client collation_connection Database Collation
++f1 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`%` FUNCTION `f1`(param INTEGER) RETURNS varchar(200) CHARSET latin1
++ MODIFIES SQL DATA
++ COMMENT 'f1_comment'
++RETURN 'abc' latin1 latin1_swedish_ci latin1_swedish_ci
++SHOW CREATE FUNCTION f1;
++Function sql_mode Create Function character_set_client collation_connection Database Collation
++f1 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`%` FUNCTION `f1`(param INTEGER) RETURNS varchar(200) CHARSET latin1
++ MODIFIES SQL DATA
++ COMMENT 'f1_comment'
++RETURN 'abc' latin1 latin1_swedish_ci latin1_swedish_ci
++SHOW CREATE FUNCTION f2;
++Function sql_mode Create Function character_set_client collation_connection Database Collation
++f2 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f2`(param VARCHAR(100)) RETURNS int(11)
++ NO SQL
++ DETERMINISTIC
++ SQL SECURITY INVOKER
++RETURN 123 latin1 latin1_swedish_ci latin1_swedish_ci
++SHOW CREATE FUNCTION f2;
++Function sql_mode Create Function character_set_client collation_connection Database Collation
++f2 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f2`(param VARCHAR(100)) RETURNS int(11)
++ NO SQL
++ DETERMINISTIC
++ SQL SECURITY INVOKER
++RETURN 123 latin1 latin1_swedish_ci latin1_swedish_ci
++SELECT f1(1) = 'abc';
++f1(1) = 'abc'
++1
++SELECT f2('abc') = 123;
++f2('abc') = 123
++1
++DROP FUNCTION f1;
++DROP FUNCTION f2;
++DROP USER 'user1';
+diff --git a/mysql-test/suite/galera/r/galera_create_procedure.result b/mysql-test/suite/galera/r/galera_create_procedure.result
+new file mode 100644
+index 0000000..997bbba
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_create_procedure.result
+@@ -0,0 +1,53 @@
++CREATE USER 'user1';
++CREATE TABLE t1 (f1 INTEGER);
++CREATE
++DEFINER = 'user1'
++PROCEDURE p1 (IN param1 INTEGER, OUT param2 INTEGER, INOUT param3 INTEGER)
++COMMENT 'p1_comment'
++LANGUAGE SQL
++NOT DETERMINISTIC
++MODIFIES SQL DATA
++SQL SECURITY DEFINER
++INSERT INTO t1 VALUES (1);
++GRANT EXECUTE ON PROCEDURE p1 TO user1;
++CREATE
++DEFINER = CURRENT_USER
++PROCEDURE p2 (param VARCHAR(100))
++DETERMINISTIC
++NO SQL
++SQL SECURITY INVOKER BEGIN END ;
++SHOW CREATE PROCEDURE p1;
++Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
++p1 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`%` PROCEDURE `p1`(IN param1 INTEGER, OUT param2 INTEGER, INOUT param3 INTEGER)
++ MODIFIES SQL DATA
++ COMMENT 'p1_comment'
++INSERT INTO t1 VALUES (1) latin1 latin1_swedish_ci latin1_swedish_ci
++SELECT 1 FROM DUAL;
++1
++1
++SHOW CREATE PROCEDURE p1;
++Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
++p1 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`%` PROCEDURE `p1`(IN param1 INTEGER, OUT param2 INTEGER, INOUT param3 INTEGER)
++ MODIFIES SQL DATA
++ COMMENT 'p1_comment'
++INSERT INTO t1 VALUES (1) latin1 latin1_swedish_ci latin1_swedish_ci
++SHOW CREATE PROCEDURE p2;
++Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
++p2 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `p2`(param VARCHAR(100))
++ NO SQL
++ DETERMINISTIC
++ SQL SECURITY INVOKER
++BEGIN END latin1 latin1_swedish_ci latin1_swedish_ci
++SHOW CREATE PROCEDURE p2;
++Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
++p2 NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `p2`(param VARCHAR(100))
++ NO SQL
++ DETERMINISTIC
++ SQL SECURITY INVOKER
++BEGIN END latin1 latin1_swedish_ci latin1_swedish_ci
++CALL p1(@a, @b, @c);
++CALL p2('abc');
++DROP PROCEDURE p1;
++DROP PROCEDURE p2;
++DROP USER 'user1';
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_create_table_like.result b/mysql-test/suite/galera/r/galera_create_table_like.result
+new file mode 100644
+index 0000000..b335101
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_create_table_like.result
+@@ -0,0 +1,47 @@
++CREATE SCHEMA schema1;
++CREATE SCHEMA schema2;
++USE schema1;
++CREATE TABLE real_table (f1 INTEGER) ENGINE=InnoDB;
++CREATE TEMPORARY TABLE temp_table (f1 INTEGER) ENGINE=InnoDB;
++CREATE TABLE myisam_table (f1 INTEGER) ENGINE=MyISAM;
++USE schema2;
++CREATE TABLE real_table1 LIKE schema1.real_table;
++CREATE TABLE real_table2 LIKE schema1.temp_table;
++CREATE TABLE real_table3 LIKE schema1.myisam_table;
++CREATE TEMPORARY TABLE temp_table1 LIKE schema1.real_table;
++CREATE TEMPORARY TABLE temp_table2 LIKE schema1.temp_table;
++CREATE TEMPORARY TABLE temp_table3 LIKE schema1.myisam_table;
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table' AND TABLE_SCHEMA = 'schema1';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'myisam_table' AND TABLE_SCHEMA = 'schema1';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table' AND TABLE_SCHEMA = 'schema1';
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table1' AND TABLE_SCHEMA = 'schema2';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table2' AND TABLE_SCHEMA = 'schema2';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table3' AND TABLE_SCHEMA = 'schema2';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table1' AND TABLE_SCHEMA = 'schema2';
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table2' AND TABLE_SCHEMA = 'schema2';
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table3' AND TABLE_SCHEMA = 'schema2';
++COUNT(*) = 0
++1
++DROP TABLE schema1.real_table;
++DROP TABLE schema1.myisam_table;
++DROP TABLE schema2.real_table1;
++DROP TABLE schema2.real_table2;
++DROP TABLE schema2.real_table3;
++DROP SCHEMA schema1;
++DROP SCHEMA schema2;
+diff --git a/mysql-test/suite/galera/r/galera_create_trigger.result b/mysql-test/suite/galera/r/galera_create_trigger.result
+new file mode 100644
+index 0000000..7e65608
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_create_trigger.result
+@@ -0,0 +1,42 @@
++CREATE TABLE definer_root (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE TABLE definer_user (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE TABLE definer_current_user (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE TABLE definer_default (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE USER 'user1';
++CREATE DEFINER=root@localhost TRIGGER definer_root BEFORE INSERT ON definer_root FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++CREATE DEFINER=user1 TRIGGER definer_user BEFORE INSERT ON definer_user FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++CREATE DEFINER=current_user TRIGGER definer_current_user BEFORE INSERT ON definer_current_user FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++CREATE TRIGGER definer_default BEFORE INSERT ON definer_default FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++INSERT INTO definer_root (f1) VALUES (1);
++SELECT DEFINER = 'root@localhost' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_root';
++DEFINER = 'root@localhost'
++1
++SELECT trigger_user = 'root@localhost' FROM definer_root;
++trigger_user = 'root@localhost'
++1
++INSERT INTO definer_user (f1) VALUES (1);
++SELECT DEFINER = 'user1@%' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_user';
++DEFINER = 'user1@%'
++1
++SELECT trigger_user = 'user1@%' FROM definer_user;
++trigger_user = 'user1@%'
++1
++INSERT INTO definer_current_user (f1) VALUES (1);
++SELECT DEFINER = 'root@localhost' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_current_user';
++DEFINER = 'root@localhost'
++1
++SELECT trigger_user = 'root@localhost' FROM definer_current_user;
++trigger_user = 'root@localhost'
++1
++INSERT INTO definer_default (f1) VALUES (1);
++SELECT DEFINER = 'root@localhost' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_default';
++DEFINER = 'root@localhost'
++1
++SELECT trigger_user = 'root@localhost' FROM definer_default;
++trigger_user = 'root@localhost'
++1
++DROP TABLE definer_current_user;
++DROP TABLE definer_user;
++DROP TABLE definer_root;
++DROP TABLE definer_default;
++DROP USER 'user1';
+diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result
+new file mode 100644
+index 0000000..8bd2221
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_defaults.result
+@@ -0,0 +1,117 @@
++SELECT COUNT(*) = 40 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%';
++COUNT(*) = 40
++1
++SELECT VARIABLE_NAME, VARIABLE_VALUE
++FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
++WHERE VARIABLE_NAME LIKE 'wsrep_%'
++AND VARIABLE_NAME NOT IN (
++'WSREP_PROVIDER_OPTIONS',
++'WSREP_SST_RECEIVE_ADDRESS',
++'WSREP_NODE_ADDRESS',
++'WSREP_NODE_NAME',
++'WSREP_PROVIDER',
++'WSREP_DATA_HOME_DIR',
++'WSREP_NODE_INCOMING_ADDRESS',
++'WSREP_START_POSITION'
++)
++ORDER BY VARIABLE_NAME;
++VARIABLE_NAME VARIABLE_VALUE
++WSREP_AUTO_INCREMENT_CONTROL ON
++WSREP_CAUSAL_READS ON
++WSREP_CERTIFY_NONPK ON
++WSREP_CLUSTER_ADDRESS gcomm://
++WSREP_CLUSTER_NAME my_wsrep_cluster
++WSREP_CONVERT_LOCK_TO_TRX OFF
++WSREP_DBUG_OPTION
++WSREP_DEBUG OFF
++WSREP_DESYNC OFF
++WSREP_DRUPAL_282555_WORKAROUND OFF
++WSREP_FORCED_BINLOG_FORMAT NONE
++WSREP_LOAD_DATA_SPLITTING ON
++WSREP_LOG_CONFLICTS OFF
++WSREP_MAX_WS_ROWS 131072
++WSREP_MAX_WS_SIZE 1073741824
++WSREP_MYSQL_REPLICATION_BUNDLE 0
++WSREP_NOTIFY_CMD
++WSREP_ON ON
++WSREP_OSU_METHOD TOI
++WSREP_PREORDERED OFF
++WSREP_RECOVER OFF
++WSREP_REPLICATE_MYISAM OFF
++WSREP_RESTART_SLAVE OFF
++WSREP_RETRY_AUTOCOMMIT 1
++WSREP_SLAVE_FK_CHECKS ON
++WSREP_SLAVE_THREADS 1
++WSREP_SLAVE_UK_CHECKS OFF
++WSREP_SST_AUTH
++WSREP_SST_DONOR
++WSREP_SST_DONOR_REJECTS_QUERIES OFF
++WSREP_SST_METHOD rsync
++WSREP_SYNC_WAIT 7
++<BASE_DIR>; <BASE_HOST>; <BASE_PORT>; cert.log_conflicts = no; debug = no; evs.auto_evict = 0; evs.causal_keepalive_period = PT1S; evs.debug_log_mask = 0x1; evs.delay_margin = PT1S; evs.delayed_keep_period = PT30S; evs.inactive_check_period = PT0.5S; evs.inactive_timeout = PT15S; evs.info_log_mask = 0; evs.install_timeout = PT7.5S; evs.join_retrans_period = PT1S; evs.keepalive_period = PT1S; evs.max_install_timeouts = 3; evs.send_window = 4; evs.stats_report_period = PT1M; evs.suspect_timeout = PT5S; evs.use_aggregate = true; evs.user_send_window = 2; evs.version = 0; evs.view_forget_timeout = P1D; <GCACHE_DIR>; gcache.keep_pages_size = 0; gcache.mem_size = 0; <GCACHE_NAME>; gcache.page_size = 128M; gcache.size = 128M; gcs.fc_debug = 0; gcs.fc_factor = 1.0; gcs.fc_limit = 16; gcs.fc_master_slave = no; gcs.max_packet_size = 64500; gcs.max_throttle = 0.25; gcs.recv_q_hard_limit = 9223372036854775807; gcs.recv_q_soft_limit = 0.25; gcs.sync_donor = no; <GMCAST_LISTEN_ADDR>; gmcast.mcast_addr = ; gmcast.mcast_ttl = 1; gmcast.peer_timeout = PT3S; gmcast.segment = 0; gmcast.time_wait = PT5S; gmcast.version = 0; <IST_RECV_ADDR>; pc.announce_timeout = PT3S; pc.checksum = false; pc.ignore_quorum = false; pc.ignore_sb = false; pc.linger = PT20S; pc.npvo = false; pc.recovery = true; pc.version = 0; pc.wait_prim = true; pc.wait_prim_timeout = P30S; pc.weight = 1; protonet.backend = asio; protonet.version = 0; repl.causal_read_timeout = PT30S; repl.commit_order = 3; repl.key_format = FLAT8; repl.max_ws_size = 2147483647; repl.proto_max = 7; socket.checksum = 2;
++SELECT COUNT(*) FROM INFORMATION_SCHEMA.GLOBAL_STATUS
++WHERE VARIABLE_NAME LIKE 'wsrep_%'
++AND VARIABLE_NAME != 'wsrep_debug_sync_waiters';
++COUNT(*)
++56
++SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.GLOBAL_STATUS
++WHERE VARIABLE_NAME LIKE 'wsrep_%'
++AND VARIABLE_NAME != 'wsrep_debug_sync_waiters'
++ORDER BY VARIABLE_NAME;
++VARIABLE_NAME
++WSREP_APPLY_OOOE
++WSREP_APPLY_OOOL
++WSREP_APPLY_WINDOW
++WSREP_CAUSAL_READS
++WSREP_CERT_DEPS_DISTANCE
++WSREP_CERT_INDEX_SIZE
++WSREP_CERT_INTERVAL
++WSREP_CLUSTER_CONF_ID
++WSREP_CLUSTER_SIZE
++WSREP_CLUSTER_STATE_UUID
++WSREP_CLUSTER_STATUS
++WSREP_COMMIT_OOOE
++WSREP_COMMIT_OOOL
++WSREP_COMMIT_WINDOW
++WSREP_CONNECTED
++WSREP_EVS_DELAYED
++WSREP_EVS_EVICT_LIST
++WSREP_EVS_REPL_LATENCY
++WSREP_EVS_STATE
++WSREP_FLOW_CONTROL_PAUSED
++WSREP_FLOW_CONTROL_PAUSED_NS
++WSREP_FLOW_CONTROL_RECV
++WSREP_FLOW_CONTROL_SENT
++WSREP_GCOMM_UUID
++WSREP_INCOMING_ADDRESSES
++WSREP_LAST_COMMITTED
++WSREP_LOCAL_BF_ABORTS
++WSREP_LOCAL_CACHED_DOWNTO
++WSREP_LOCAL_CERT_FAILURES
++WSREP_LOCAL_COMMITS
++WSREP_LOCAL_INDEX
++WSREP_LOCAL_RECV_QUEUE
++WSREP_LOCAL_RECV_QUEUE_AVG
++WSREP_LOCAL_RECV_QUEUE_MAX
++WSREP_LOCAL_RECV_QUEUE_MIN
++WSREP_LOCAL_REPLAYS
++WSREP_LOCAL_SEND_QUEUE
++WSREP_LOCAL_SEND_QUEUE_AVG
++WSREP_LOCAL_SEND_QUEUE_MAX
++WSREP_LOCAL_SEND_QUEUE_MIN
++WSREP_LOCAL_STATE
++WSREP_LOCAL_STATE_COMMENT
++WSREP_LOCAL_STATE_UUID
++WSREP_PROTOCOL_VERSION
++WSREP_PROVIDER_NAME
++WSREP_PROVIDER_VENDOR
++WSREP_PROVIDER_VERSION
++WSREP_READY
++WSREP_RECEIVED
++WSREP_RECEIVED_BYTES
++WSREP_REPLICATED
++WSREP_REPLICATED_BYTES
++WSREP_REPL_DATA_BYTES
++WSREP_REPL_KEYS
++WSREP_REPL_KEYS_BYTES
++WSREP_REPL_OTHER_BYTES
+diff --git a/mysql-test/suite/galera/r/galera_delete_limit.result b/mysql-test/suite/galera/r/galera_delete_limit.result
+new file mode 100644
+index 0000000..72bee18
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_delete_limit.result
+@@ -0,0 +1,19 @@
++CREATE TABLE ten (f1 INTEGER) Engine=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 SELECT f1 FROM ten ORDER BY RAND();
++DELETE FROM t1 ORDER BY RAND() LIMIT 5;
++sum_matches
++1
++max_matches
++1
++DROP TABLE t1;
++CREATE TABLE t2 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t2 SELECT f1 FROM ten ORDER BY RAND();
++DELETE FROM t2 ORDER BY RAND() LIMIT 5;
++sum_matches
++1
++max_matches
++1
++DROP TABLE t2;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_enum.result b/mysql-test/suite/galera/r/galera_enum.result
+new file mode 100644
+index 0000000..e853c5c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_enum.result
+@@ -0,0 +1,37 @@
++CREATE TABLE t1 (f1 ENUM('', 'one', 'two'), KEY (f1)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES ('');
++INSERT INTO t1 VALUES ('one'), ('two');
++INSERT INTO t1 VALUES (0), (1), (2);
++Warnings:
++Warning 1265 Data truncated for column 'f1' at row 1
++SELECT COUNT(*) = 6 FROM t1;
++COUNT(*) = 6
++1
++SELECT COUNT(*) = 2 FROM t1 where f1 = '';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 2 FROM t1 where f1 = 'one';
++COUNT(*) = 2
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 ENUM('', 'one', 'two', 'three', 'four') PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (''), ('one'), ('two');
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = '';
++COUNT(*) = 1
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'three' where f1 = '';
++SET AUTOCOMMIt=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'four' where f1 = '';
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'three';
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_events.result b/mysql-test/suite/galera/r/galera_events.result
+new file mode 100644
+index 0000000..09d8406
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_events.result
+@@ -0,0 +1,18 @@
++CREATE EVENT event1 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO SELECT 1;
++SELECT DEFINER= 'root@localhost', ORIGINATOR = 1, STATUS = 'SLAVESIDE_DISABLED', EVENT_TYPE = 'ONE TIME', ON_COMPLETION = 'NOT PRESERVE' FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event1';
++DEFINER= 'root@localhost' ORIGINATOR = 1 STATUS = 'SLAVESIDE_DISABLED' EVENT_TYPE = 'ONE TIME' ON_COMPLETION = 'NOT PRESERVE'
++1 1 1 1 1
++ALTER EVENT event1 DISABLE;
++SELECT DEFINER= 'root@localhost', ORIGINATOR = 1, STATUS = 'SLAVESIDE_DISABLED', EVENT_TYPE = 'ONE TIME', ON_COMPLETION = 'NOT PRESERVE' FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event1';
++DEFINER= 'root@localhost' ORIGINATOR = 1 STATUS = 'SLAVESIDE_DISABLED' EVENT_TYPE = 'ONE TIME' ON_COMPLETION = 'NOT PRESERVE'
++1 1 1 1 1
++SET GLOBAL event_scheduler = ON;
++CREATE EVENT event2 ON SCHEDULE AT CURRENT_TIMESTAMP ON COMPLETION NOT PRESERVE DO SELECT 1;
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event2';
++COUNT(*) = 0
++1
++DROP EVENT event1;
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event1';
++COUNT(*) = 0
++1
++SET GLOBAL event_scheduler = OFF;;
+diff --git a/mysql-test/suite/galera/r/galera_fk_cascade_delete.result b/mysql-test/suite/galera/r/galera_fk_cascade_delete.result
+new file mode 100644
+index 0000000..89f4301
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_cascade_delete.result
+@@ -0,0 +1,30 @@
++CREATE TABLE grandparent (
++id INT NOT NULL PRIMARY KEY
++) ENGINE=InnoDB;
++CREATE TABLE parent (
++id INT NOT NULL PRIMARY KEY,
++grandparent_id INT,
++FOREIGN KEY (grandparent_id)
++REFERENCES grandparent(id)
++ON DELETE CASCADE
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT NOT NULL PRIMARY KEY,
++parent_id INT,
++FOREIGN KEY (parent_id)
++REFERENCES parent(id)
++ON DELETE CASCADE
++) ENGINE=InnoDB;
++INSERT INTO grandparent VALUES (1),(2);
++INSERT INTO parent VALUES (1,1), (2,2);
++INSERT INTO child VALUES (1,1), (2,2);
++DELETE FROM grandparent WHERE id = 1;
++SELECT COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1;
++COUNT(*) = 0
++1
++DROP TABLE child;
++DROP TABLE parent;
++DROP TABLE grandparent;
+diff --git a/mysql-test/suite/galera/r/galera_fk_cascade_update.result b/mysql-test/suite/galera/r/galera_fk_cascade_update.result
+new file mode 100644
+index 0000000..2ab2ad3
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_cascade_update.result
+@@ -0,0 +1,30 @@
++CREATE TABLE grandparent (
++id INT NOT NULL PRIMARY KEY
++) ENGINE=InnoDB;
++CREATE TABLE parent (
++id INT NOT NULL PRIMARY KEY,
++grandparent_id INT,
++FOREIGN KEY (grandparent_id)
++REFERENCES grandparent(id)
++ON UPDATE CASCADE
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT NOT NULL PRIMARY KEY,
++grandparent_id INT,
++FOREIGN KEY (grandparent_id)
++REFERENCES parent(grandparent_id)
++ON UPDATE CASCADE
++) ENGINE=InnoDB;
++INSERT INTO grandparent VALUES (1),(2);
++INSERT INTO parent VALUES (1,1), (2,2);
++INSERT INTO child VALUES (1,1), (2,2);
++UPDATE grandparent SET id = 3 WHERE id = 1;
++SELECT COUNT(*) = 1 FROM parent WHERE grandparent_id = 3;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM child WHERE grandparent_id = 3;
++COUNT(*) = 1
++1
++DROP TABLE child;
++DROP TABLE parent;
++DROP TABLE grandparent;
+diff --git a/mysql-test/suite/galera/r/galera_fk_conflict.result b/mysql-test/suite/galera/r/galera_fk_conflict.result
+new file mode 100644
+index 0000000..ae6c482
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_conflict.result
+@@ -0,0 +1,23 @@
++CREATE TABLE parent (
++id INT PRIMARY KEY,
++KEY (id)
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT PRIMARY KEY,
++parent_id INT,
++FOREIGN KEY (parent_id)
++REFERENCES parent(id)
++) ENGINE=InnoDB;
++INSERT INTO parent VALUES (1), (2);
++INSERT INTO child VALUES (1,1);
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++DELETE FROM parent WHERE id = 2;
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++INSERT INTO child VALUES (2, 2);
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/r/galera_fk_mismatch.result b/mysql-test/suite/galera/r/galera_fk_mismatch.result
+new file mode 100644
+index 0000000..07cdb1b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_mismatch.result
+@@ -0,0 +1,25 @@
++CREATE TABLE parent (
++id1 INT,
++id2 INT,
++PRIMARY KEY (id1, id2) /* Multipart PK */
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT PRIMARY KEY,
++parent_id1 INT,
++FOREIGN KEY (parent_id1)
++REFERENCES parent(id1) /* FK is subset of PK above */
++ON UPDATE CASCADE
++ON DELETE CASCADE
++) ENGINE=InnoDB;
++INSERT INTO parent VALUES (1, 2);
++INSERT INTO child VALUES (1, 1);
++UPDATE parent SET id1 = 3 WHERE id1 = 1;
++SELECT COUNT(*) = 1 FROM child WHERE parent_id1 = 3;
++COUNT(*) = 1
++1
++DELETE FROM parent WHERE id1 = 3;
++SELECT COUNT(*) = 0 FROM child WHERE parent_id1 = 3;
++COUNT(*) = 0
++1
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/r/galera_fk_multicolumn.result b/mysql-test/suite/galera/r/galera_fk_multicolumn.result
+new file mode 100644
+index 0000000..a86b87a
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_multicolumn.result
+@@ -0,0 +1,35 @@
++CREATE TABLE t0 (
++f1 INT PRIMARY KEY,
++f2 INT UNIQUE
++);
++CREATE TABLE t1 (
++f1 INT PRIMARY KEY,
++FOREIGN KEY (f1)
++REFERENCES t0(f1)
++ON UPDATE CASCADE
++);
++CREATE TABLE t2 (
++f2 INT PRIMARY KEY,
++FOREIGN KEY (f2)
++REFERENCES t0(f2)
++ON UPDATE CASCADE
++);
++INSERT INTO t0 VALUES (0, 0);
++INSERT INTO t1 VALUES (0);
++INSERT INTO t2 VALUES (0);
++UPDATE t0 SET f1 = 1, f2 = 2;
++SELECT f1 = 1 FROM t1 WHERE f1 = 1;
++f1 = 1
++1
++SELECT f2 = 2 FROM t2 WHERE f2 = 2;
++f2 = 2
++1
++SELECT f1 = 1 FROM t1;
++f1 = 1
++1
++SELECT f2 = 2 FROM t2;
++f2 = 2
++1
++DROP TABLE t2;
++DROP TABLE t1;
++DROP TABLE t0;
+diff --git a/mysql-test/suite/galera/r/galera_fk_multitable.result b/mysql-test/suite/galera/r/galera_fk_multitable.result
+new file mode 100644
+index 0000000..e77128d
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_multitable.result
+@@ -0,0 +1,22 @@
++CREATE TABLE t0 (
++f0 INT PRIMARY KEY
++);
++CREATE TABLE t1 (
++f1 INT PRIMARY KEY,
++f0 INTEGER,
++FOREIGN KEY (f0)
++REFERENCES t0(f0)
++ON DELETE CASCADE
++);
++INSERT INTO t0 VALUES (0), (1);
++INSERT INTO t1 VALUES (0, 0);
++INSERT INTO t1 VALUES (1, 0);
++DELETE t0.*, t1.* FROM t0, t1 WHERE t0.f0 = 0 AND t1.f1 = 0;
++SELECT COUNT(*) = 1 FROM t0;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++DROP TABLE t0;
+diff --git a/mysql-test/suite/galera/r/galera_fk_no_pk.result b/mysql-test/suite/galera/r/galera_fk_no_pk.result
+new file mode 100644
+index 0000000..e4f9286
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_no_pk.result
+@@ -0,0 +1,28 @@
++CREATE TABLE parent (
++id INT,
++KEY (id)
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT,
++parent_id INT,
++FOREIGN KEY (parent_id)
++REFERENCES parent(id)
++ON UPDATE CASCADE
++ON DELETE CASCADE
++) ENGINE=InnoDB;
++INSERT INTO parent VALUES (1), (1), (2), (2);
++INSERT INTO child VALUES (1,1), (2,2), (1,1), (2,2);
++DELETE FROM parent WHERE id = 1;
++SELECT COUNT(*) = 0 FROM child WHERE id = 1;
++COUNT(*) = 0
++1
++UPDATE parent SET id = 3 WHERE id = 2;
++SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1;
++COUNT(*) = 0
++1
++SELECT parent_id = 3 FROM child WHERE id = 2;
++parent_id = 3
++1
++1
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/r/galera_fk_selfreferential.result b/mysql-test/suite/galera/r/galera_fk_selfreferential.result
+new file mode 100644
+index 0000000..25c3704
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_selfreferential.result
+@@ -0,0 +1,13 @@
++CREATE TABLE t1 (
++f1 INT NOT NULL PRIMARY KEY,
++f2 INT,
++FOREIGN KEY (f2)
++REFERENCES t1(f1)
++ON DELETE CASCADE
++) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1, 1), (2, 1);
++DELETE FROM t1 WHERE f1 = 1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_fk_setnull.result b/mysql-test/suite/galera/r/galera_fk_setnull.result
+new file mode 100644
+index 0000000..f7fb9d0
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fk_setnull.result
+@@ -0,0 +1,30 @@
++CREATE TABLE parent (
++id INT NOT NULL,
++PRIMARY KEY (id)
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT,
++parent_id INT,
++FOREIGN KEY (parent_id)
++REFERENCES parent(id)
++ON UPDATE SET NULL
++ON DELETE SET NULL
++) ENGINE=InnoDB;
++INSERT INTO parent VALUES (1),(2);
++INSERT INTO child VALUES (1,1),(2,2);
++DELETE FROM parent WHERE id = 1;
++SELECT parent_id IS NULL FROM child WHERE id = 1;
++parent_id IS NULL
++1
++SELECT parent_id IS NULL FROM child WHERE id = 1;
++parent_id IS NULL
++1
++UPDATE parent SET id = 3 WHERE id = 2;
++SELECT parent_id IS NULL FROM child WHERE id = 2;
++parent_id IS NULL
++1
++SELECT parent_id IS NULL FROM child WHERE id = 2;
++parent_id IS NULL
++1
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/r/galera_ftwrl.result b/mysql-test/suite/galera/r/galera_ftwrl.result
+new file mode 100644
+index 0000000..c216b52
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ftwrl.result
+@@ -0,0 +1,16 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++SET GLOBAL wsrep_provider_options = "repl.causal_read_timeout=PT1S";
++FLUSH TABLES WITH READ LOCK;
++INSERT INTO t1 VALUES (1);
++SHOW TABLES;
++ERROR HY000: Lock wait timeout exceeded; try restarting transaction
++SELECT * FROM t1;
++ERROR HY000: Lock wait timeout exceeded; try restarting transaction
++UNLOCK TABLES;
++SHOW TABLES;
++Tables_in_test
++t1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_fulltext.result b/mysql-test/suite/galera/r/galera_fulltext.result
+new file mode 100644
+index 0000000..7257769
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_fulltext.result
+@@ -0,0 +1,26 @@
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INT PRIMARY KEY AUTO_INCREMENT, f2 VARCHAR(100), FULLTEXT (f2)) ENGINE=InnoDB;
++SELECT COUNT(*) = 13 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE name LIKE 'test/%';
++COUNT(*) = 13
++1
++INSERT INTO t1 (f2) SELECT 'foobarbaz' FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++SELECT COUNT(f2) = 10000 FROM t1 WHERE MATCH(f2) AGAINST ('foobarbaz');
++COUNT(f2) = 10000
++1
++UPDATE t1 SET f2 = 'abcdefjhk';
++SELECT COUNT(f2) = 10000 FROM t1 WHERE MATCH(f2) AGAINST ('abcdefjhk');
++COUNT(f2) = 10000
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 VARCHAR(100), FULLTEXT (f1)) ENGINE=InnoDB;
++INSERT INTO t1 (f1) SELECT 'foobarbaz' FROM ten AS a1, ten AS a2, ten AS a3;
++SELECT COUNT(f1) = 1000 FROM t1 WHERE MATCH(f1) AGAINST ('foobarbaz');
++COUNT(f1) = 1000
++1
++UPDATE t1 SET f1 = 'abcdefjhk';
++SELECT COUNT(f1) = 1000 FROM t1 WHERE MATCH(f1) AGAINST ('abcdefjhk');
++COUNT(f1) = 1000
++1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_gcs_fc_limit.result b/mysql-test/suite/galera/r/galera_gcs_fc_limit.result
+new file mode 100644
+index 0000000..99c710f
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_gcs_fc_limit.result
+@@ -0,0 +1,17 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET GLOBAL wsrep_provider_options = 'gcs.fc_limit=1';
++FLUSH TABLES WITH READ LOCK;
++INSERT INTO t1 VALUES (2);
++INSERT INTO t1 VALUES (3);
++INSERT INTO t1 VALUES (4);
++INSERT INTO t1 VALUES (5);
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'query end' AND INFO = 'INSERT INTO t1 VALUES (5)';
++COUNT(*) = 1
++1
++UNLOCK TABLES;
++INSERT INTO t1 VALUES (6);
++SELECT COUNT(*) = 6 FROM t1;
++COUNT(*) = 6
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_gcs_max_packet_size.result b/mysql-test/suite/galera/r/galera_gcs_max_packet_size.result
+new file mode 100644
+index 0000000..606cb54
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_gcs_max_packet_size.result
+@@ -0,0 +1,15 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
++CREATE TABLE t1 (f1 INT PRIMARY KEY AUTO_INCREMENT, f2 INTEGER) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 VARCHAR(512) UNIQUE) ENGINE=InnoDB;
++INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++INSERT INTO t2 VALUES (REPEAT('x', 512));
++SELECT COUNT(*) = 10000 FROM t1;
++COUNT(*) = 10000
++1
++SELECT LENGTH(f1) = 512 FROM t2 WHERE f1 = REPEAT('x', 512);
++LENGTH(f1) = 512
++1
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_gtid.result b/mysql-test/suite/galera/r/galera_gtid.result
+new file mode 100644
+index 0000000..50d561d
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_gtid.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (f1 INT PRIMARY KEY);
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 2;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++COUNT(*) = 1
++1
++gtid_executed_equal
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_insert_ignore.result b/mysql-test/suite/galera/r/galera_insert_ignore.result
+new file mode 100644
+index 0000000..2d6e38e
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_insert_ignore.result
+@@ -0,0 +1,48 @@
++SET GLOBAL wsrep_sync_wait = 7;
++SET GLOBAL wsrep_sync_wait = 7;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++INSERT IGNORE INTO t1 VALUES (1), (2);
++SELECT * FROM t1;
++f1
++1
++2
++SELECT * FROM t1;
++f1
++1
++2
++CREATE TABLE t2 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (0), (2), (3);
++INSERT IGNORE INTO t1 SELECT f1 FROM t2;
++SELECT * FROM t1;
++f1
++0
++1
++2
++3
++SELECT * FROM t1;
++f1
++0
++1
++2
++3
++CREATE TABLE t3 (f1 INTEGER UNIQUE) Engine=InnoDB;
++INSERT INTO t3 VALUES (NULL);
++INSERT IGNORE INTO t3 VALUES (1), (NULL), (2);
++SELECT * FROM t3;
++f1
++NULL
++NULL
++1
++2
++SELECT * FROM t3;
++f1
++NULL
++NULL
++1
++2
++SET GLOBAL wsrep_sync_wait = (SELECT @@wsrep_sync_wait);
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
++SET GLOBAL wsrep_sync_wait = (SELECT @@wsrep_sync_wait);
+diff --git a/mysql-test/suite/galera/r/galera_insert_multi.result b/mysql-test/suite/galera/r/galera_insert_multi.result
+new file mode 100644
+index 0000000..3371778
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_insert_multi.result
+@@ -0,0 +1,58 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1),(2);
++INSERT INTO t1 VALUES (3),(4);
++SELECT COUNT(*) = 4 FROM t1;
++COUNT(*) = 4
++1
++SELECT COUNT(*) = 4 FROM t1;
++COUNT(*) = 4
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER, KEY (f1)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1),(1);
++INSERT INTO t1 VALUES (2),(2);
++SELECT COUNT(*) = 4 FROM t1;
++COUNT(*) = 4
++1
++SELECT COUNT(*) = 4 FROM t1;
++COUNT(*) = 4
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (1);
++ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1), (2);
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (2), (1);
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++ROLLBACK;
++INSERT INTO t1 VALUES (1), (2);
++ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1), (2);
++START TRANSACTION;
++INSERT INTO t1 VALUES (2), (1);
++ROLLBACK;
++COMMIT;
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_ist_innodb_flush_logs.result b/mysql-test/suite/galera/r/galera_ist_innodb_flush_logs.result
+new file mode 100644
+index 0000000..5421b23
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ist_innodb_flush_logs.result
+@@ -0,0 +1,184 @@
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/r/galera_ist_mysqldump.result b/mysql-test/suite/galera/r/galera_ist_mysqldump.result
+new file mode 100644
+index 0000000..4b5a8fe
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ist_mysqldump.result
+@@ -0,0 +1,284 @@
++Setting SST method to mysqldump ...
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++SET GLOBAL wsrep_sst_auth = 'sst:sst';
++SET GLOBAL wsrep_sst_method = 'mysqldump';
++Performing State Transfer on a server that has been shut down cleanly and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++CALL mtr.add_suppression("Slave SQL: Error 'The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement' on query");
++DROP USER sst;
++CALL mtr.add_suppression("Slave SQL: Error 'The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement' on query");
++CALL mtr.add_suppression("InnoDB: Error: Table \"mysql\"\\.\"innodb_index_stats\" not found");
++CALL mtr.add_suppression("InnoDB: New log files created");
++CALL mtr.add_suppression("InnoDB: Creating foreign key constraint system tables");
++CALL mtr.add_suppression("Can't open and lock time zone table");
++CALL mtr.add_suppression("Can't open and lock privilege tables");
++CALL mtr.add_suppression("Info table is not ready to be used");
++CALL mtr.add_suppression("Native table .* has the wrong structure");
+diff --git a/mysql-test/suite/galera/r/galera_ist_restart_joiner.result b/mysql-test/suite/galera/r/galera_ist_restart_joiner.result
+new file mode 100644
+index 0000000..f7a1386
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ist_restart_joiner.result
+@@ -0,0 +1,43 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
++INSERT INTO t1 VALUES (1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a'),(6, 'a');
++Unloading wsrep provider ...
++SET GLOBAL wsrep_provider = 'none';
++UPDATE t1 SET f2 = 'b' WHERE f1 > 1;
++UPDATE t1 SET f2 = 'c' WHERE f1 > 2;
++SET GLOBAL wsrep_provider_options = 'dbug=d,recv_IST_after_apply_trx';
++SET SESSION wsrep_sync_wait = 0;
++Loading wsrep_provider ...
++SHOW STATUS LIKE 'wsrep_debug_sync_waiters';
++Variable_name Value
++wsrep_debug_sync_waiters recv_IST_after_apply_trx
++UPDATE t1 SET f2 = 'd' WHERE f1 > 3;
++CREATE TABLE t2 (f1 INTEGER);
++UPDATE t1 SET f2 = 'e' WHERE f1 > 4;
++CREATE TABLE t3 (f1 INTEGER);
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++UPDATE t1 SET f2 = 'f' WHERE f1 > 5;
++SELECT * FROM t1;
++f1 f2
++1 a
++2 b
++3 c
++4 d
++5 e
++6 f
++SELECT * FROM t1;
++f1 f2
++1 a
++2 b
++3 c
++4 d
++5 e
++6 f
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t3;
++COUNT(*) = 0
++1
++DROP TABLE t1, t2, t3;
+diff --git a/mysql-test/suite/galera/r/galera_ist_rsync.result b/mysql-test/suite/galera/r/galera_ist_rsync.result
+new file mode 100644
+index 0000000..175e744
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ist_rsync.result
+@@ -0,0 +1,357 @@
++Performing State Transfer on a server that has been temporarily disconnected
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Unloading wsrep provider ...
++SET GLOBAL wsrep_provider = 'none';
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Loading wsrep provider ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been shut down cleanly and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/r/galera_ist_xtrabackup-v2.result b/mysql-test/suite/galera/r/galera_ist_xtrabackup-v2.result
+new file mode 100644
+index 0000000..175e744
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ist_xtrabackup-v2.result
+@@ -0,0 +1,357 @@
++Performing State Transfer on a server that has been temporarily disconnected
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Unloading wsrep provider ...
++SET GLOBAL wsrep_provider = 'none';
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Loading wsrep provider ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been shut down cleanly and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/r/galera_kill_ddl.result b/mysql-test/suite/galera/r/galera_kill_ddl.result
+new file mode 100644
+index 0000000..8dd3649
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_kill_ddl.result
+@@ -0,0 +1,11 @@
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++Killing server ...
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='t1';
++COUNT(*) = 2
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_kill_largechanges.result b/mysql-test/suite/galera/r/galera_kill_largechanges.result
+new file mode 100644
+index 0000000..a37056a
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_kill_largechanges.result
+@@ -0,0 +1,14 @@
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
++CREATE TABLE t1 (f1 VARCHAR(128)) ENGINE=InnoDB;
++Killing server ...
++INSERT INTO t1 SELECT REPEAT('a', 128) FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5, ten AS a6;
++SELECT COUNT(*) = 1000000 FROM t1;
++COUNT(*) = 1000000
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_kill_nochanges.result b/mysql-test/suite/galera/r/galera_kill_nochanges.result
+new file mode 100644
+index 0000000..accace9
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_kill_nochanges.result
+@@ -0,0 +1,9 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_kill_smallchanges.result b/mysql-test/suite/galera/r/galera_kill_smallchanges.result
+new file mode 100644
+index 0000000..8409740
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_kill_smallchanges.result
+@@ -0,0 +1,11 @@
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++Killing server ...
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_lock_table.result b/mysql-test/suite/galera/r/galera_lock_table.result
+new file mode 100644
+index 0000000..16e9037
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_lock_table.result
+@@ -0,0 +1,21 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (id INT PRIMARY KEY) ENGINE=InnoDB;
++LOCK TABLE t1 READ;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++SET SESSION wsrep_sync_wait=0;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++UNLOCK TABLES;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2;
++COUNT(*) = 1
++1
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_log_bin.result b/mysql-test/suite/galera/r/galera_log_bin.result
+new file mode 100644
+index 0000000..9a8513d
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_log_bin.result
+@@ -0,0 +1,53 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++CREATE TABLE t2 (id INT) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (1);
++INSERT INTO t2 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 2 FROM t2;
++COUNT(*) = 2
++1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++FLUSH LOGS;
++SHOW BINLOG EVENTS IN '0.000002' FROM 120;
++Log_name Pos Event_type Server_id End_log_pos Info
++0.000002 120 Query 1 244 use `test`; CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB
++0.000002 244 Query 1 321 BEGIN
++0.000002 321 Table_map 1 366 table_id: 103 (test.t1)
++0.000002 366 Write_rows 1 406 table_id: 103 flags: STMT_END_F
++0.000002 406 Xid 1 437 COMMIT /* xid=2 */
++0.000002 437 Query 1 549 use `test`; CREATE TABLE t2 (id INT) ENGINE=InnoDB
++0.000002 549 Query 1 626 BEGIN
++0.000002 626 Table_map 1 671 table_id: 104 (test.t2)
++0.000002 671 Write_rows 1 711 table_id: 104 flags: STMT_END_F
++0.000002 711 Xid 1 742 COMMIT /* xid=4 */
++0.000002 742 Query 1 819 BEGIN
++0.000002 819 Table_map 1 864 table_id: 104 (test.t2)
++0.000002 864 Write_rows 1 904 table_id: 104 flags: STMT_END_F
++0.000002 904 Xid 1 935 COMMIT /* xid=5 */
++0.000002 935 Query 1 1045 use `test`; ALTER TABLE t1 ADD COLUMN f2 INTEGER
++0.000002 1045 Rotate 1 1084 0.000003;pos=4
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SHOW BINLOG EVENTS IN '0.000001' FROM 120;
++Log_name Pos Event_type Server_id End_log_pos Info
++0.000001 120 Query 1 244 use `test`; CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB
++0.000001 244 Query 1 312 BEGIN
++0.000001 312 Table_map 1 357 table_id: 81 (test.t1)
++0.000001 357 Write_rows 1 397 table_id: 81 flags: STMT_END_F
++0.000001 397 Xid 1 428 COMMIT /* xid=2 */
++0.000001 428 Query 1 540 use `test`; CREATE TABLE t2 (id INT) ENGINE=InnoDB
++0.000001 540 Query 1 608 BEGIN
++0.000001 608 Table_map 1 653 table_id: 82 (test.t2)
++0.000001 653 Write_rows 1 693 table_id: 82 flags: STMT_END_F
++0.000001 693 Xid 1 724 COMMIT /* xid=4 */
++0.000001 724 Query 1 792 BEGIN
++0.000001 792 Table_map 1 837 table_id: 82 (test.t2)
++0.000001 837 Write_rows 1 877 table_id: 82 flags: STMT_END_F
++0.000001 877 Xid 1 908 COMMIT /* xid=5 */
++0.000001 908 Query 1 1018 use `test`; ALTER TABLE t1 ADD COLUMN f2 INTEGER
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_log_output_csv.result b/mysql-test/suite/galera/r/galera_log_output_csv.result
+new file mode 100644
+index 0000000..07a7846
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_log_output_csv.result
+@@ -0,0 +1,21 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) > 0 FROM mysql.general_log;
++COUNT(*) > 0
++1
++SELECT 1 = 1 FROM t1;
++1 = 1
++1
++SELECT COUNT(*) = 1 FROM mysql.slow_log WHERE sql_text = 'SELECT 1 = 1 FROM t1';
++COUNT(*) = 1
++1
++SELECT COUNT(*) > 0 FROM mysql.general_log WHERE argument = 'CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB';
++COUNT(*) > 0
++1
++SELECT 2 = 2 FROM t1;
++2 = 2
++1
++SELECT COUNT(*) = 1 FROM mysql.slow_log WHERE sql_text = 'SELECT 2 = 2 FROM t1';
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_many_columns.result b/mysql-test/suite/galera/r/galera_many_columns.result
+new file mode 100644
+index 0000000..6fa574e
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_many_columns.result
+@@ -0,0 +1,32 @@
++INSERT INTO t1 (f1) VALUES (DEFAULT);
++SELECT f1 = 'ABC', f1017 = 'ABC' FROM t1;
++f1 = 'ABC' f1017 = 'ABC'
++1 1
++UPDATE t1 SET f1 = 'XYZ', f1017 = 'XYZ' ;
++SELECT f1 = 'XYZ', f1017 = 'XYZ' FROM t1 WHERE f1 = 'XYZ' AND f1017 = 'XYZ';
++f1 = 'XYZ' f1017 = 'XYZ'
++1 1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'KLM' WHERE f1 = 'XYZ' AND f1017 = 'XYZ';
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'CDE' WHERE f1 = 'XYZ' AND f1017 = 'XYZ';
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++ROLLBACK;
++ROLLBACK;
++START TRANSACTION;
++INSERT INTO t1 (f1, f1017) VALUES ('BCE','BCE');
++INSERT INTO t1 (f1, f1017) VALUES ('CED','CED');
++INSERT INTO t1 (f1, f1017) VALUES ('EDF','EDF');
++INSERT INTO t1 (f1, f1017) VALUES ('FED','FED');
++ROLLBACK;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_many_indexes.result b/mysql-test/suite/galera/r/galera_many_indexes.result
+new file mode 100644
+index 0000000..ab6eec5
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_many_indexes.result
+@@ -0,0 +1,123 @@
++CREATE TABLE t1 (f1 VARCHAR(767) PRIMARY KEY) ENGINE=InnoDB;
++CREATE UNIQUE INDEX i63 ON t1(f1);
++CREATE UNIQUE INDEX i62 ON t1(f1);
++CREATE UNIQUE INDEX i61 ON t1(f1);
++CREATE UNIQUE INDEX i60 ON t1(f1);
++CREATE UNIQUE INDEX i59 ON t1(f1);
++CREATE UNIQUE INDEX i58 ON t1(f1);
++CREATE UNIQUE INDEX i57 ON t1(f1);
++CREATE UNIQUE INDEX i56 ON t1(f1);
++CREATE UNIQUE INDEX i55 ON t1(f1);
++CREATE UNIQUE INDEX i54 ON t1(f1);
++CREATE UNIQUE INDEX i53 ON t1(f1);
++CREATE UNIQUE INDEX i52 ON t1(f1);
++CREATE UNIQUE INDEX i51 ON t1(f1);
++CREATE UNIQUE INDEX i50 ON t1(f1);
++CREATE UNIQUE INDEX i49 ON t1(f1);
++CREATE UNIQUE INDEX i48 ON t1(f1);
++CREATE UNIQUE INDEX i47 ON t1(f1);
++CREATE UNIQUE INDEX i46 ON t1(f1);
++CREATE UNIQUE INDEX i45 ON t1(f1);
++CREATE UNIQUE INDEX i44 ON t1(f1);
++CREATE UNIQUE INDEX i43 ON t1(f1);
++CREATE UNIQUE INDEX i42 ON t1(f1);
++CREATE UNIQUE INDEX i41 ON t1(f1);
++CREATE UNIQUE INDEX i40 ON t1(f1);
++CREATE UNIQUE INDEX i39 ON t1(f1);
++CREATE UNIQUE INDEX i38 ON t1(f1);
++CREATE UNIQUE INDEX i37 ON t1(f1);
++CREATE UNIQUE INDEX i36 ON t1(f1);
++CREATE UNIQUE INDEX i35 ON t1(f1);
++CREATE UNIQUE INDEX i34 ON t1(f1);
++CREATE UNIQUE INDEX i33 ON t1(f1);
++CREATE UNIQUE INDEX i32 ON t1(f1);
++CREATE UNIQUE INDEX i31 ON t1(f1);
++CREATE UNIQUE INDEX i30 ON t1(f1);
++CREATE UNIQUE INDEX i29 ON t1(f1);
++CREATE UNIQUE INDEX i28 ON t1(f1);
++CREATE UNIQUE INDEX i27 ON t1(f1);
++CREATE UNIQUE INDEX i26 ON t1(f1);
++CREATE UNIQUE INDEX i25 ON t1(f1);
++CREATE UNIQUE INDEX i24 ON t1(f1);
++CREATE UNIQUE INDEX i23 ON t1(f1);
++CREATE UNIQUE INDEX i22 ON t1(f1);
++CREATE UNIQUE INDEX i21 ON t1(f1);
++CREATE UNIQUE INDEX i20 ON t1(f1);
++CREATE UNIQUE INDEX i19 ON t1(f1);
++CREATE UNIQUE INDEX i18 ON t1(f1);
++CREATE UNIQUE INDEX i17 ON t1(f1);
++CREATE UNIQUE INDEX i16 ON t1(f1);
++CREATE UNIQUE INDEX i15 ON t1(f1);
++CREATE UNIQUE INDEX i14 ON t1(f1);
++CREATE UNIQUE INDEX i13 ON t1(f1);
++CREATE UNIQUE INDEX i12 ON t1(f1);
++CREATE UNIQUE INDEX i11 ON t1(f1);
++CREATE UNIQUE INDEX i10 ON t1(f1);
++CREATE UNIQUE INDEX i9 ON t1(f1);
++CREATE UNIQUE INDEX i8 ON t1(f1);
++CREATE UNIQUE INDEX i7 ON t1(f1);
++CREATE UNIQUE INDEX i6 ON t1(f1);
++CREATE UNIQUE INDEX i5 ON t1(f1);
++CREATE UNIQUE INDEX i4 ON t1(f1);
++CREATE UNIQUE INDEX i3 ON t1(f1);
++CREATE UNIQUE INDEX i2 ON t1(f1);
++CREATE UNIQUE INDEX i1 ON t1(f1);
++INSERT INTO t1 VALUES (REPEAT('a', 767));
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT LENGTH(f1) = 767 FROM t1;
++LENGTH(f1) = 767
++1
++EXPLAIN SELECT COUNT(*) = 1 FROM t1 FORCE KEY (PRIMARY) WHERE f1 = REPEAT('a', 767);
++id select_type table type possible_keys key key_len ref rows Extra
++1 SIMPLE t1 const PRIMARY PRIMARY 769 const 1 Using index
++SELECT COUNT(*) = 1 FROM t1 FORCE KEY (PRIMARY) WHERE f1 = REPEAT('a', 767);
++COUNT(*) = 1
++1
++EXPLAIN SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i1) WHERE f1 = REPEAT('a', 767);
++id select_type table type possible_keys key key_len ref rows Extra
++1 SIMPLE t1 const i1 i1 769 const 1 Using index
++SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i1) WHERE f1 = REPEAT('a', 767);
++COUNT(*) = 1
++1
++EXPLAIN SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i63) WHERE f1 = REPEAT('a', 767);
++id select_type table type possible_keys key key_len ref rows Extra
++1 SIMPLE t1 const i63 i63 769 const 1 Using index
++SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i63) WHERE f1 = REPEAT('a', 767);
++COUNT(*) = 1
++1
++INSERT INTO t1 VALUES (REPEAT('b', 767));
++ANALYZE TABLE t1;
++Table Op Msg_type Msg_text
++test.t1 analyze status OK
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++ANALYZE TABLE t1;
++Table Op Msg_type Msg_text
++test.t1 analyze status OK
++DELETE FROM t1 WHERE f1 = REPEAT('b', 767);
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++INSERT INTO t1 (f1) VALUES (REPEAT('c', 767));
++ROLLBACK;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++START TRANSACTION;
++SET AUTOCOMMIT=OFF;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++START TRANSACTION;
++START TRANSACTION;
++UPDATE t1 SET f1 = REPEAT('e', 767);
++UPDATE t1 SET f1 = REPEAT('f', 767);
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_many_rows.result b/mysql-test/suite/galera/r/galera_many_rows.result
+new file mode 100644
+index 0000000..6ec0add
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_many_rows.result
+@@ -0,0 +1,41 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) Engine=InnoDB;
++INSERT INTO t1 (f2) SELECT a1.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++SELECT COUNT(*) = 100000 FROM t1;
++COUNT(*) = 100000
++1
++INSERT INTO t1 (f2) SELECT a1.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++SELECT COUNT(*) = 200000 FROM t1;
++COUNT(*) = 200000
++1
++UPDATE t1 SET f2 = 1;
++SELECT COUNT(*) = 200000 FROM t1 WHERE f2 = 1;
++COUNT(*) = 200000
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f2) SELECT a1.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++ROLLBACK;
++SELECT COUNT(*) = 200000 FROM t1;
++COUNT(*) = 200000
++1
++SELECT COUNT(*) = 200000 FROM t1;
++COUNT(*) = 200000
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 2;
++ROLLBACK;
++START TRANSACTION;
++SELECT COUNT(*) = 200000 FROM t1;
++COUNT(*) = 200000
++1
++UPDATE t1 SET f2 = 3;
++START TRANSACTION;
++UPDATE t1 SET f2 = 4;
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_many_tables_nopk.result b/mysql-test/suite/galera/r/galera_many_tables_nopk.result
+new file mode 100644
+index 0000000..7a4f364
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_many_tables_nopk.result
+@@ -0,0 +1,17 @@
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++COMMIT;
++CREATE TABLE sum_table (f1 INTEGER);
++SELECT SUM(f1) = 1000 FROM sum_table;
++SUM(f1) = 1000
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1000 SET f1 = 3;
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP SCHEMA test;
++CREATE SCHEMA test;
+diff --git a/mysql-test/suite/galera/r/galera_many_tables_pk.result b/mysql-test/suite/galera/r/galera_many_tables_pk.result
+new file mode 100644
+index 0000000..6b6899d
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_many_tables_pk.result
+@@ -0,0 +1,20 @@
++SELECT COUNT(*) = 1000 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME LIKE 't%';
++COUNT(*) = 1000
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++COMMIT;
++CREATE TABLE sum_table (f1 INTEGER);
++SELECT SUM(f1) = 1000 FROM sum_table;
++SUM(f1) = 1000
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1000 SET f1 = 3;
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP SCHEMA test;
++CREATE SCHEMA test;
+diff --git a/mysql-test/suite/galera/r/galera_migrate.result b/mysql-test/suite/galera/r/galera_migrate.result
+new file mode 100644
+index 0000000..7c92d66
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_migrate.result
+@@ -0,0 +1,79 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++INSERT INTO t1 VALUES (2);
++START SLAVE USER='root';
++Warnings:
++Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
++INSERT INTO t1 VALUES (3);
++INSERT INTO t1 VALUES (4);
++SET GLOBAL wsrep_cluster_address='gcomm://';
++INSERT INTO t1 VALUES (5);
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 1
++1
++INSERT INTO t1 VALUES (6);
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++SET GLOBAL wsrep_sst_auth = 'sst:sst';
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++STOP SLAVE;
++RESET SLAVE ALL;
++STOP SLAVE;
++RESET SLAVE ALL;
++INSERT INTO t1 VALUES (7);
++INSERT INTO t1 VALUES (8);
++SELECT COUNT(*) = 8 FROM t1;
++COUNT(*) = 8
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++SELECT COUNT(*) = 8 FROM t1;
++COUNT(*) = 8
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++DROP TABLE t1;
++DROP TABLE t1;
++SET GLOBAL wsrep_provider = 'none';
++SET GLOBAL wsrep_sst_auth = '';
++SET GLOBAL wsrep_provider_options = '';
++DROP TABLE t1;
++DROP USER sst;
++SET GLOBAL wsrep_provider = 'none';
++SET GLOBAL wsrep_sst_method = 'rsync';
++SET GLOBAL wsrep_provider_options = '';
++SET GLOBAL wsrep_sst_receive_address = 'AUTO';
++DROP TABLE t1;
++DROP USER sst;
++CALL mtr.add_suppression("InnoDB: Error: Table \"mysql\"\\.\"innodb_index_stats\" not found");
+diff --git a/mysql-test/suite/galera/r/galera_multi_database.result b/mysql-test/suite/galera/r/galera_multi_database.result
+new file mode 100644
+index 0000000..a04eb48
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_multi_database.result
+@@ -0,0 +1,28 @@
++CREATE DATABASE d1;
++CREATE TABLE d1.t1(f1 INTEGER) ENGINE=InnoDB;
++CREATE DATABASE d2;
++CREATE TABLE d2.t1(f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO d1.t1 VALUES (1);
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO d2.t1 VALUES (1);
++COMMIT;
++COMMIT;
++SELECT COUNT(*) = 1 FROM d1.t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM d2.t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM d1.t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM d2.t1;
++COUNT(*) = 1
++1
++DROP TABLE d1.t1;
++DROP TABLE d2.t1;
++DROP DATABASE d1;
++DROP DATABASE d2;
+diff --git a/mysql-test/suite/galera/r/galera_myisam_autocommit.result b/mysql-test/suite/galera/r/galera_myisam_autocommit.result
+new file mode 100644
+index 0000000..3f8d93b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_myisam_autocommit.result
+@@ -0,0 +1,24 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=MyISAM;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (2), (3);
++INSERT INTO t1 SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL;
++CREATE TABLE t2 (f1 INTEGER PRIMARY KEY) ENGINE=MyISAM;
++INSERT INTO t2 VALUES (1);
++INSERT INTO t2 VALUES (2), (3);
++INSERT INTO t2 SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL;
++INSERT INTO t2 VALUES (6), (1);
++ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
++UPDATE t1 SET f1 = 9;
++UPDATE t2 SET f1 = 9 WHERE f1 = 1;
++DELETE FROM t1 WHERE f1 = 9;
++DELETE FROM t2 WHERE f1 = 9;
++TRUNCATE TABLE t1;
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_myisam_transactions.result b/mysql-test/suite/galera/r/galera_myisam_transactions.result
+new file mode 100644
+index 0000000..284f92b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_myisam_transactions.result
+@@ -0,0 +1,34 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER) ENGINE=MyISAM;
++CREATE TABLE t3 (f1 INTEGER) ENGINE=MyISAM;
++CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t3 VALUES (NEW.f1);
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++COMMIT;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++ROLLBACK;
++Warnings:
++Warning 1196 Some non-transactional changed tables couldn't be rolled back
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++DROP TABLE t1, t2, t3;
+diff --git a/mysql-test/suite/galera/r/galera_nopk_bit.result b/mysql-test/suite/galera/r/galera_nopk_bit.result
+new file mode 100644
+index 0000000..5723dac
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_nopk_bit.result
+@@ -0,0 +1,27 @@
++CREATE TABLE t1 (f1 BIT) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL),(0),(b'1');
++SELECT f1 IS NULL, f1 = b'1' FROM t1;
++f1 IS NULL f1 = b'1'
++1 NULL
++0 0
++0 1
++DELETE FROM t1 WHERE f1 = b'1';
++UPDATE t1 SET f1 = b'1' WHERE f1 IS NULL;
++UPDATE t1 SET f1 = 1 WHERE f1 = b'0';
++SELECT f1 IS NULL, f1 = b'1' FROM t1;
++f1 IS NULL f1 = b'1'
++0 1
++0 1
++CREATE TABLE t2 (f1 BIT) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (NULL);
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 0 WHERE f1 IS NULL;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 1 WHERE f1 IS NULL;
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_nopk_blob.result b/mysql-test/suite/galera/r/galera_nopk_blob.result
+new file mode 100644
+index 0000000..7491b71
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_nopk_blob.result
+@@ -0,0 +1,27 @@
++CREATE TABLE t1 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL),('abc');
++SELECT f1 FROM t1;
++f1
++NULL
++abc
++DELETE FROM t1 WHERE f1 IS NULL;
++UPDATE t1 SET f1 = 'xyz' WHERE f1 = 'abc';
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT f1 = 'abc' FROM t1;
++f1 = 'abc'
++0
++CREATE TABLE t2 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (NULL);
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'abc' WHERE f1 IS NULL;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'xyz' WHERE f1 IS NULL;
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_nopk_large_varchar.result b/mysql-test/suite/galera/r/galera_nopk_large_varchar.result
+new file mode 100644
+index 0000000..abca81e
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_nopk_large_varchar.result
+@@ -0,0 +1,30 @@
++CREATE TABLE t1 (f1 VARCHAR(8000)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL),(CONCAT(REPEAT('x', 7999), 'a'));
++SELECT LENGTH(f1) FROM t1;
++LENGTH(f1)
++NULL
++8000
++DELETE FROM t1 WHERE f1 IS NULL;
++UPDATE t1 SET f1 = CONCAT(REPEAT('x', 7999), 'b') WHERE f1 = CONCAT(REPEAT('x', 7999), 'a');
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT LENGTH(f1) = 8000 FROM t1;
++LENGTH(f1) = 8000
++1
++SELECT f1 = CONCAT(REPEAT('x', 7999), 'b') FROM t1;
++f1 = CONCAT(REPEAT('x', 7999), 'b')
++1
++CREATE TABLE t2 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (CONCAT(REPEAT('x', 7999), 'a'));
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'abc' WHERE f1 = CONCAT(REPEAT('x', 7999), 'a');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'xyz' WHERE f1 = CONCAT(REPEAT('x', 7999), 'a');
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_nopk_unicode.result b/mysql-test/suite/galera/r/galera_nopk_unicode.result
+new file mode 100644
+index 0000000..68d049a
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_nopk_unicode.result
+@@ -0,0 +1,24 @@
++CREATE TABLE t1 (
++f1 VARCHAR(255),
++KEY (f1)
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++INSERT INTO t1 VALUES ('текст');
++SELECT f1 = 'текст' FROM t1;
++f1 = 'текст'
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст2';
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст3';
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SELECT f1 = 'текст2' FROM t1;
++f1 = 'текст2'
++1
++SELECT f1 = 'текст2' FROM t1 WHERE f1 = 'текст2';
++f1 = 'текст2'
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_parallel_apply_lock_table.result b/mysql-test/suite/galera/r/galera_parallel_apply_lock_table.result
+new file mode 100644
+index 0000000..85ba022
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_parallel_apply_lock_table.result
+@@ -0,0 +1,33 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (id INT PRIMARY KEY) ENGINE=InnoDB;
++SET GLOBAL wsrep_slave_threads = 2;
++LOCK TABLE t1 READ;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++SET SESSION wsrep_sync_wait=0;
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE '%applied write set%';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE '%Waiting for table level lock%';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++UNLOCK TABLES;
++SET SESSION wsrep_sync_wait = 7;;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE '%committed%';
++COUNT(*) = 2
++1
++SET GLOBAL wsrep_slave_threads = 1;;
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_parallel_autoinc_largetrx.result b/mysql-test/suite/galera/r/galera_parallel_autoinc_largetrx.result
+new file mode 100644
+index 0000000..1f163f4
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_parallel_autoinc_largetrx.result
+@@ -0,0 +1,18 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) Engine=InnoDB;
++SET GLOBAL wsrep_slave_threads = 4;
++INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;;
++INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;;
++INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;;
++SELECT COUNT(*) = 30000 FROM t1;
++COUNT(*) = 30000
++1
++SELECT COUNT(DISTINCT f1) = 30000 FROM t1;
++COUNT(DISTINCT f1) = 30000
++1
++SELECT COUNT(*) = 5 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++COUNT(*) = 5
++1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_parallel_autoinc_manytrx.result b/mysql-test/suite/galera/r/galera_parallel_autoinc_manytrx.result
+new file mode 100644
+index 0000000..05ce328
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_parallel_autoinc_manytrx.result
+@@ -0,0 +1,15 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) Engine=InnoDB;
++SET GLOBAL wsrep_slave_threads = 4;
++SELECT COUNT(*) = 20000 FROM t1;
++COUNT(*) = 20000
++1
++SELECT COUNT(DISTINCT f1) = 20000 FROM t1;
++COUNT(DISTINCT f1) = 20000
++1
++SELECT COUNT(*) = 4 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE 'committed%';
++COUNT(*) = 4
++1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_parallel_simple.result b/mysql-test/suite/galera/r/galera_parallel_simple.result
+new file mode 100644
+index 0000000..294a94b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_parallel_simple.result
+@@ -0,0 +1,27 @@
++CREATE TABLE t1 (id INT) ENGINE=InnoDB;
++CREATE TABLE t2 (id INT) ENGINE=InnoDB;
++SET GLOBAL wsrep_slave_threads = 2;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++SELECT COUNT(*) = 10 FROM t1;
++COUNT(*) = 10
++0
++SELECT COUNT(*) = 10 FROM t2;
++COUNT(*) = 10
++0
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE 'committed%';
++COUNT(*) = 2
++1
++SET GLOBAL wsrep_slave_threads = 1;;
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_pc_ignore_sb.result b/mysql-test/suite/galera/r/galera_pc_ignore_sb.result
+new file mode 100644
+index 0000000..5fcccfe
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_pc_ignore_sb.result
+@@ -0,0 +1,12 @@
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++Killing server ...
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++DROP TABLE t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 1
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SET GLOBAL wsrep_cluster_address = '';
+diff --git a/mysql-test/suite/galera/r/galera_pk_bigint_signed.result b/mysql-test/suite/galera/r/galera_pk_bigint_signed.result
+new file mode 100644
+index 0000000..a307599
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_pk_bigint_signed.result
+@@ -0,0 +1,26 @@
++CREATE TABLE t1 (f1 BIGINT SIGNED PRIMARY KEY, f2 VARCHAR(5)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES
++(-9223372036854775808, 'min'),
++(9223372036854775807, 'max')
++;
++SELECT * FROM t1;
++f1 f2
++-9223372036854775808 min
++9223372036854775807 max
++UPDATE t1 SET f2 = CONCAT(f2, '_');
++SELECT * FROM t1;
++f1 f2
++-9223372036854775808 min_
++9223372036854775807 max_
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'foo' WHERE f1 = -9223372036854775808;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'bar' WHERE f1 = -9223372036854775808;
++COMMIT;
++SET AUTOCOMMIT=ON;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SET AUTOCOMMIT=ON;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_pk_bigint_unsigned.result b/mysql-test/suite/galera/r/galera_pk_bigint_unsigned.result
+new file mode 100644
+index 0000000..441926e
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_pk_bigint_unsigned.result
+@@ -0,0 +1,23 @@
++CREATE TABLE t1 (f1 BIGINT UNSIGNED PRIMARY KEY, f2 VARCHAR(5)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES
++(18446744073709551615, 'max')
++;
++SELECT f1 = 18446744073709551615 FROM t1;
++f1 = 18446744073709551615
++1
++UPDATE t1 SET f2 = CONCAT(f2, '_');
++SELECT f1 = 18446744073709551615 FROM t1;
++f1 = 18446744073709551615
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'foo' WHERE f1 = 18446744073709551615;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'bar' WHERE f1 = 18446744073709551615;
++COMMIT;
++SET AUTOCOMMIT=ON;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SET AUTOCOMMIT=ON;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_query_cache.result b/mysql-test/suite/galera/r/galera_query_cache.result
+new file mode 100644
+index 0000000..502d8a5
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_query_cache.result
+@@ -0,0 +1,57 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++RESET QUERY CACHE;
++FLUSH STATUS;
++SELECT COUNT(*) FROM t1;
++COUNT(*)
++1
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++VARIABLE_VALUE = 1
++1
++SELECT COUNT(*) FROM t1;
++COUNT(*)
++1
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++VARIABLE_VALUE = 1
++1
++INSERT INTO t1 VALUES (2);
++FLUSH STATUS;
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++VARIABLE_VALUE = 0
++1
++SELECT COUNT(*) FROM t1;
++COUNT(*)
++2
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++VARIABLE_VALUE = 1
++1
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++VARIABLE_VALUE = 0
++1
++SELECT COUNT(*) FROM t1;
++COUNT(*)
++2
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++VARIABLE_VALUE = 1
++1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++FLUSH STATUS;
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++VARIABLE_VALUE = 0
++1
++SELECT COUNT(*) FROM t1;
++COUNT(*)
++2
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++VARIABLE_VALUE = 1
++1
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++VARIABLE_VALUE = 0
++1
++SELECT COUNT(*) FROM t1;
++COUNT(*)
++2
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++VARIABLE_VALUE = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_read_only.result b/mysql-test/suite/galera/r/galera_read_only.result
+new file mode 100644
+index 0000000..d2af386
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_read_only.result
+@@ -0,0 +1,8 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++SET GLOBAL read_only=TRUE;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SET GLOBAL read_only=FALSE;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_repl_key_format_flat16.result b/mysql-test/suite/galera/r/galera_repl_key_format_flat16.result
+new file mode 100644
+index 0000000..4acf014
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_repl_key_format_flat16.result
+@@ -0,0 +1,18 @@
++SET GLOBAL wsrep_provider_options = 'repl.key_format=FLAT16';
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (123);
++CREATE TABLE t2 (f1 VARCHAR(256)) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (REPEAT('a', 256));
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 234;
++UPDATE t2 SET f1 = REPEAT('b', 256);
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 234;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = REPEAT('b', 256);
++COUNT(*) = 1
++1
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_repl_max_ws_size.result b/mysql-test/suite/galera/r/galera_repl_max_ws_size.result
+new file mode 100644
+index 0000000..6e1054c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_repl_max_ws_size.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 VARCHAR(512)) ENGINE=InnoDB;
++SET GLOBAL wsrep_provider_options = 'repl.max_ws_size=512';
++INSERT INTO t1 VALUES (REPEAT('a', 512));
++ERROR HY000: Got error 5 during COMMIT
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++CALL mtr.add_suppression("WSREP: Maximum writeset size exceeded by");
++CALL mtr.add_suppression("WSREP: transaction size exceeded");
+diff --git a/mysql-test/suite/galera/r/galera_restart_nochanges.result b/mysql-test/suite/galera/r/galera_restart_nochanges.result
+new file mode 100644
+index 0000000..accace9
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_restart_nochanges.result
+@@ -0,0 +1,9 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_rsu_add_pk.result b/mysql-test/suite/galera/r/galera_rsu_add_pk.result
+new file mode 100644
+index 0000000..3d8677d
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_rsu_add_pk.result
+@@ -0,0 +1,26 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t1 (f1) SELECT 000000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++INSERT INTO t1 (f1) SELECT 100000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;;
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET GLOBAL wsrep_OSU_method = "TOI";
++INSERT INTO t1 (f1) SELECT 200000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++SELECT COUNT(*) = 300000 FROM t1;
++COUNT(*) = 300000
++1
++SELECT MAX(f1) = 299999 FROM t1;
++MAX(f1) = 299999
++1
++SELECT COUNT(*) = 300000 FROM t1;
++COUNT(*) = 300000
++1
++SELECT MAX(f1) = 299999 FROM t1;
++MAX(f1) = 299999
++1
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET GLOBAL wsrep_OSU_method = "TOI";
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_rsu_drop_pk.result b/mysql-test/suite/galera/r/galera_rsu_drop_pk.result
+new file mode 100644
+index 0000000..43b4b57
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_rsu_drop_pk.result
+@@ -0,0 +1,42 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 (f1) SELECT 000000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++INSERT INTO t1 (f1) SELECT 100000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;;
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 DROP PRIMARY KEY;
++SET GLOBAL wsrep_OSU_method = "TOI";
++INSERT INTO t1 (f1) SELECT 200000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++SELECT COUNT(*) = 300000 FROM t1;
++COUNT(*) = 300000
++1
++SELECT MAX(f1) = 299999 FROM t1;
++MAX(f1) = 299999
++1
++SELECT COUNT(*) = 300000 FROM t1;
++COUNT(*) = 300000
++1
++SELECT MAX(f1) = 299999 FROM t1;
++MAX(f1) = 299999
++1
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 DROP PRIMARY KEY;
++SET GLOBAL wsrep_OSU_method = "TOI";
++INSERT INTO t1 (f1) VALUES (1);
++INSERT INTO t1 (f1) VALUES (10);
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 1;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 10;
++COUNT(*) = 2
++1
++INSERT INTO t1 (f1) VALUES (100);
++INSERT INTO t1 (f1) VALUES (1000);
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 100;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 1000;
++COUNT(*) = 2
++1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_rsu_error.result b/mysql-test/suite/galera/r/galera_rsu_error.result
+new file mode 100644
+index 0000000..1f9ea36
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_rsu_error.result
+@@ -0,0 +1,21 @@
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t1 VALUES (1), (1);
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
++SET GLOBAL wsrep_OSU_method = "TOI";
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = 't1';
++COUNT(*) = 0
++1
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = 't1';
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(3) = 4 FROM t1;
++COUNT(3) = 4
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_rsu_simple.result b/mysql-test/suite/galera/r/galera_rsu_simple.result
+new file mode 100644
+index 0000000..169c4a5
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_rsu_simple.result
+@@ -0,0 +1,22 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET GLOBAL wsrep_OSU_method = "TOI";
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 1
++1
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++INSERT INTO t1 (f1) VALUES (2);
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_sbr.result b/mysql-test/suite/galera/r/galera_sbr.result
+new file mode 100644
+index 0000000..66ca8cf
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_sbr.result
+@@ -0,0 +1,11 @@
++SET GLOBAL binlog_format = 'STATEMENT';
++SET SESSION binlog_format = 'STATEMENT';
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET SESSION binlog_format = 'MIXED';
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
++SET GLOBAL binlog_format = 'ROW';
+diff --git a/mysql-test/suite/galera/r/galera_sbr_binlog.result b/mysql-test/suite/galera/r/galera_sbr_binlog.result
+new file mode 100644
+index 0000000..66ca8cf
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_sbr_binlog.result
+@@ -0,0 +1,11 @@
++SET GLOBAL binlog_format = 'STATEMENT';
++SET SESSION binlog_format = 'STATEMENT';
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET SESSION binlog_format = 'MIXED';
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
++SET GLOBAL binlog_format = 'ROW';
+diff --git a/mysql-test/suite/galera/r/galera_split_brain.result b/mysql-test/suite/galera/r/galera_split_brain.result
+new file mode 100644
+index 0000000..6156150
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_split_brain.result
+@@ -0,0 +1,5 @@
++call mtr.add_suppression("WSREP: TO isolation failed for: ");
++Killing server ...
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SET GLOBAL wsrep_cluster_address = '';
+diff --git a/mysql-test/suite/galera/r/galera_sql_log_bin_zero.result b/mysql-test/suite/galera/r/galera_sql_log_bin_zero.result
+new file mode 100644
+index 0000000..89ab8d5
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_sql_log_bin_zero.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++SET SESSION sql_log_bin = 0;
++INSERT INTO t1 VALUES (1);
++SET SESSION sql_log_bin = 1;
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t1 WHERE f1 = 1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_ssl.result b/mysql-test/suite/galera/r/galera_ssl.result
+new file mode 100644
+index 0000000..569c3c6
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_ssl.result
+@@ -0,0 +1,18 @@
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_sst_mysqldump.result b/mysql-test/suite/galera/r/galera_sst_mysqldump.result
+new file mode 100644
+index 0000000..1be2002
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_sst_mysqldump.result
+@@ -0,0 +1,459 @@
++Setting SST method to mysqldump ...
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++SET GLOBAL wsrep_sst_auth = 'sst:sst';
++SET GLOBAL wsrep_sst_method = 'mysqldump';
++Performing State Transfer on a server that has been temporarily disconnected
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Unloading wsrep provider ...
++SET GLOBAL wsrep_provider = 'none';
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Loading wsrep provider ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been shut down cleanly and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that starts from a clean var directory
++This is accomplished by shutting down node #2 and removing its var directory before restarting it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++Cleaning var directory ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++CALL mtr.add_suppression("Slave SQL: Error 'The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement' on query");
++DROP USER sst;
++CALL mtr.add_suppression("Slave SQL: Error 'The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement' on query");
++CALL mtr.add_suppression("InnoDB: Error: Table \"mysql\"\\.\"innodb_index_stats\" not found");
++CALL mtr.add_suppression("InnoDB: New log files created");
++CALL mtr.add_suppression("InnoDB: Creating foreign key constraint system tables");
++CALL mtr.add_suppression("Can't open and lock time zone table");
++CALL mtr.add_suppression("Can't open and lock privilege tables");
++CALL mtr.add_suppression("Info table is not ready to be used");
++CALL mtr.add_suppression("Native table .* has the wrong structure");
+diff --git a/mysql-test/suite/galera/r/galera_sst_rsync.result b/mysql-test/suite/galera/r/galera_sst_rsync.result
+new file mode 100644
+index 0000000..750d73b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_sst_rsync.result
+@@ -0,0 +1,358 @@
++Performing State Transfer on a server that has been shut down cleanly and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that starts from a clean var directory
++This is accomplished by shutting down node #2 and removing its var directory before restarting it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++Cleaning var directory ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/r/galera_sst_xtrabackup-v2.result b/mysql-test/suite/galera/r/galera_sst_xtrabackup-v2.result
+new file mode 100644
+index 0000000..750d73b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_sst_xtrabackup-v2.result
+@@ -0,0 +1,358 @@
++Performing State Transfer on a server that has been shut down cleanly and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that starts from a clean var directory
++This is accomplished by shutting down node #2 and removing its var directory before restarting it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Shutting down server ...
++Cleaning var directory ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Starting server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++INSERT INTO t1 VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++INSERT INTO t1 VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++INSERT INTO t1 VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
++Performing State Transfer on a server that has been killed and restarted
++while a DDL was in progress on it
++CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++INSERT INTO t1 VALUES ('node1_committed_before');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++INSERT INTO t1 VALUES ('node2_committed_before');
++COMMIT;
++SET GLOBAL debug = 'd,sync.alter_opened_table';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET wsrep_sync_wait = 0;
++Killing server ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++INSERT INTO t1 (f1) VALUES ('node1_committed_during');
++COMMIT;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++Performing --wsrep-recover ...
++Starting server ...
++Using --wsrep-start-position when starting mysqld ...
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++INSERT INTO t1 (f1) VALUES ('node2_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after');
++COMMIT;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++INSERT INTO t1 (f1) VALUES ('node1_committed_after');
++COMMIT;
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after');
++ROLLBACK;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++COMMIT;
++SET AUTOCOMMIT=ON;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 35 FROM t1;
++COUNT(*) = 35
++1
++SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++COMMIT;
++SET AUTOCOMMIT=ON;
+diff --git a/mysql-test/suite/galera/r/galera_status_cluster.result b/mysql-test/suite/galera/r/galera_status_cluster.result
+new file mode 100644
+index 0000000..d7cf671
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_status_cluster.result
+@@ -0,0 +1,12 @@
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
+diff --git a/mysql-test/suite/galera/r/galera_status_local_state.result b/mysql-test/suite/galera/r/galera_status_local_state.result
+new file mode 100644
+index 0000000..65713f1
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_status_local_state.result
+@@ -0,0 +1,14 @@
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SET GLOBAL wsrep_desync = 1;
++SELECT VARIABLE_VALUE = 'Donor/Desynced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Donor/Desynced'
++1
++SET GLOBAL wsrep_desync = 0;
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
+diff --git a/mysql-test/suite/galera/r/galera_suspend_slave.result b/mysql-test/suite/galera/r/galera_suspend_slave.result
+new file mode 100644
+index 0000000..0290481
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_suspend_slave.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++Suspending node_2 ...
++INSERT INTO t1 VALUES (1);
++Got one of the listed errors
++Resuming node_2 ...
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result b/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
+new file mode 100644
+index 0000000..f914153
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
+@@ -0,0 +1,42 @@
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++ALTER TABLE t1 AUTO_INCREMENT = 1000;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++SELECT MIN(f1) >= 1000, COUNT(*) = 20, COUNT(DISTINCT f1) = 20 FROM t1 WHERE f1 >= 1000;
++MIN(f1) >= 1000 COUNT(*) = 20 COUNT(DISTINCT f1) = 20
++1 1 1
++SELECT MIN(f1) >= 1000, COUNT(*) = 20, COUNT(DISTINCT f1) = 20 FROM t1 WHERE f1 >= 1000;
++MIN(f1) >= 1000 COUNT(*) = 20 COUNT(DISTINCT f1) = 20
++1 1 1
++ALTER TABLE t1 AUTO_INCREMENT = 5;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++SELECT MIN(f1) >= 1000, COUNT(*) = 40, COUNT(DISTINCT f1) = 40 FROM t1 WHERE f1 >= 1000;
++MIN(f1) >= 1000 COUNT(*) = 40 COUNT(DISTINCT f1) = 40
++1 1 1
++SELECT MIN(f1) >= 1000, COUNT(*) = 40, COUNT(DISTINCT f1) = 40 FROM t1 WHERE f1 >= 1000;
++MIN(f1) >= 1000 COUNT(*) = 40 COUNT(DISTINCT f1) = 40
++1 1 1
++DROP TABLE t1;
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
++ALTER TABLE t1 AUTO_INCREMENT=100;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++SELECT MIN(f1) = 100, MAX(f1) = 119, COUNT(f1) = 20, COUNT(DISTINCT f1) = 20 FROM t1;
++MIN(f1) = 100 MAX(f1) = 119 COUNT(f1) = 20 COUNT(DISTINCT f1) = 20
++1 1 1 1
++SELECT MIN(f1) = 100, MAX(f1) = 119, COUNT(f1) = 20, COUNT(DISTINCT f1) = 20 FROM t1;
++MIN(f1) = 100 MAX(f1) = 119 COUNT(f1) = 20 COUNT(DISTINCT f1) = 20
++1 1 1 1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_toi_ddl_error.result b/mysql-test/suite/galera/r/galera_toi_ddl_error.result
+new file mode 100644
+index 0000000..656e20b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_ddl_error.result
+@@ -0,0 +1,19 @@
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 (f1) SELECT (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++INSERT INTO t1 (f1) SELECT MAX(f1) FROM t1;
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++ERROR 23000: Duplicate entry '111110' for key 'PRIMARY'
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `f1` int(11) DEFAULT NULL
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `f1` int(11) DEFAULT NULL
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_toi_ddl_fk_insert.result b/mysql-test/suite/galera/r/galera_toi_ddl_fk_insert.result
+new file mode 100644
+index 0000000..81781fb
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_ddl_fk_insert.result
+@@ -0,0 +1,31 @@
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE parent (
++id INT PRIMARY KEY AUTO_INCREMENT,
++f2 INTEGER,
++KEY (id)
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT PRIMARY KEY AUTO_INCREMENT,
++parent_id INT
++) ENGINE=InnoDB;
++INSERT INTO parent VALUES (DEFAULT, 0);
++INSERT INTO child (parent_id) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;;
++INSERT INTO parent (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;;
++INSERT INTO parent (f2) SELECT 2 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;;
++ALTER TABLE child ADD FOREIGN KEY (parent_id) REFERENCES parent(id);;
++SELECT COUNT(*) = 20001 FROM parent;
++COUNT(*) = 20001
++1
++SELECT COUNT(*) = 10000 FROM child;
++COUNT(*) = 10000
++1
++SELECT COUNT(*) = 20001 FROM parent;
++COUNT(*) = 20001
++1
++SELECT COUNT(*) = 10000 FROM child;
++COUNT(*) = 10000
++1
++DROP TABLE child;
++DROP TABLE parent;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_toi_ddl_locking.result b/mysql-test/suite/galera/r/galera_toi_ddl_locking.result
+new file mode 100644
+index 0000000..d961f04
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_ddl_locking.result
+@@ -0,0 +1,39 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER) ENGINE=InnoDB;
++SET DEBUG_SYNC = 'alter_table_before_open_tables WAIT_FOR continue';
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;;
++SET SESSION wsrep_sync_wait = 0;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++INSERT INTO t1 VALUES (1);
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t2 VALUES (1);
++COMMIT;;
++SET SESSION wsrep_sync_wait = 0;
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE INFO = 'Commit';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++SET DEBUG_SYNC= 'now SIGNAL continue';
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 1 FROM t2;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 1 FROM t2;
++COUNT(*) = 1
++1
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_toi_ddl_nonconflicting.result b/mysql-test/suite/galera/r/galera_toi_ddl_nonconflicting.result
+new file mode 100644
+index 0000000..41e693c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_ddl_nonconflicting.result
+@@ -0,0 +1,23 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY AUTO_INCREMENT, f2 INTEGER);
++ALTER TABLE t1 ADD COLUMN f3 INTEGER; INSERT INTO t1 (f1, f2) VALUES (DEFAULT, 123);;
++CREATE UNIQUE INDEX i1 ON t1(f2);;
++INSERT INTO t1 (f1, f2) VALUES (DEFAULT, 234);
++SELECT COUNT(*) = 3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 3
++1
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++COUNT(*) = 3
++1
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_toi_ddl_sequential.result b/mysql-test/suite/galera/r/galera_toi_ddl_sequential.result
+new file mode 100644
+index 0000000..9dfa433
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_ddl_sequential.result
+@@ -0,0 +1,35 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++INSERT INTO t1 VALUES (2, 3);
++ALTER TABLE t1 DROP COLUMN f2;
++INSERT INTO t1 VALUES (4);
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `f1` int(11) NOT NULL,
++ PRIMARY KEY (`f1`)
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++SELECT * FROM t1 ORDER BY f1;
++f1
++1
++2
++4
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `f1` int(11) NOT NULL,
++ PRIMARY KEY (`f1`)
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++SELECT * FROM t1 ORDER BY f1;
++f1
++1
++2
++4
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_toi_ftwrl.result b/mysql-test/suite/galera/r/galera_toi_ftwrl.result
+new file mode 100644
+index 0000000..594717c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_ftwrl.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++FLUSH TABLES WITH READ LOCK;
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++UNLOCK TABLES;
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `id` int(11) NOT NULL,
++ `f2` int(11) DEFAULT NULL,
++ PRIMARY KEY (`id`)
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_toi_lock_exclusive.result b/mysql-test/suite/galera/r/galera_toi_lock_exclusive.result
+new file mode 100644
+index 0000000..eac50e8
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_lock_exclusive.result
+@@ -0,0 +1,17 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (2);
++ALTER TABLE t1 ADD COLUMN f2 INTEGER, LOCK=EXCLUSIVE;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++INSERT INTO t1 VALUES (2, 2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++INSERT INTO t1 VALUES (3, 3);
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_toi_lock_shared.result b/mysql-test/suite/galera/r/galera_toi_lock_shared.result
+new file mode 100644
+index 0000000..36c3886
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_toi_lock_shared.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++ALTER TABLE t1 ADD COLUMN f2 INTEGER, LOCK=SHARED;
++INSERT INTO t1 VALUES (2, 2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++INSERT INTO t1 VALUES (3, 3);
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_transaction_read_only.result b/mysql-test/suite/galera/r/galera_transaction_read_only.result
+new file mode 100644
+index 0000000..3cd1076
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_transaction_read_only.result
+@@ -0,0 +1,21 @@
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++COMMIT;
++wsrep_last_committed_diff
++1
++START TRANSACTION READ ONLY;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++COMMIT;
++wsrep_last_committed_diff
++1
++START TRANSACTION;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++COMMIT;
++wsrep_last_committed_diff
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_transaction_replay.result b/mysql-test/suite/galera/r/galera_transaction_replay.result
+new file mode 100644
+index 0000000..23ed87f
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_transaction_replay.result
+@@ -0,0 +1,30 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
++INSERT INTO t1 VALUES (1, 'a');
++INSERT INTO t1 VALUES (2, 'a');
++SET AUTOCOMMIT=ON;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'b' WHERE f1 = 1;
++SELECT * FROM t1 WHERE f1 = 2 FOR UPDATE;
++f1 f2
++2 a
++SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_enter_sync';
++COMMIT;;
++SET SESSION wsrep_sync_wait = 0;
++UPDATE t1 SET f2 = 'c' WHERE f1 = 2;
++SET GLOBAL wsrep_provider_options = 'dbug=';
++SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_enter_sync';
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'b';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
++COUNT(*) = 1
++1
++wsrep_local_replays
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'b';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_truncate.result b/mysql-test/suite/galera/r/galera_truncate.result
+new file mode 100644
+index 0000000..eeeb672
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_truncate.result
+@@ -0,0 +1,29 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 VALUES (1);
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++CREATE TABLE t2 (f1 VARCHAR(255)) Engine=InnoDB;
++INSERT INTO t2 VALUES ('abc');
++TRUNCATE TABLE t2;
++SELECT COUNT(*) = 0 FROM t2;
++COUNT(*) = 0
++1
++CREATE TABLE t3 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t3 VALUES (DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT);
++CREATE TABLE t4 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY) Engine=InnoDB AUTO_INCREMENT=1234;
++INSERT INTO t4 VALUES (DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT);
++TRUNCATE TABLE t3;
++TRUNCATE TABLE t4;
++SELECT AUTO_INCREMENT = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME IN ('t3', 't4');
++AUTO_INCREMENT = 1
++1
++1
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
++DROP TABLE t4;
+diff --git a/mysql-test/suite/galera/r/galera_truncate_temporary.result b/mysql-test/suite/galera/r/galera_truncate_temporary.result
+new file mode 100644
+index 0000000..0bdc4e3
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_truncate_temporary.result
+@@ -0,0 +1,63 @@
++CREATE TEMPORARY TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 VALUES (1);
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT * FROM t1;
++ERROR 42S02: Table 'test.t1' doesn't exist
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++CREATE TEMPORARY TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (2);
++SELECT f1 = 2 FROM t1;
++f1 = 2
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT f1 = 1 FROM t1;
++f1 = 1
++1
++DROP TABLE t1;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT f1 = 1 FROM t1;
++f1 = 1
++1
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++CREATE TEMPORARY TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (2);
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT f1 = 2 FROM t1;
++f1 = 2
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_unicode_identifiers.result b/mysql-test/suite/galera/r/galera_unicode_identifiers.result
+new file mode 100644
+index 0000000..212c6c6
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_unicode_identifiers.result
+@@ -0,0 +1,46 @@
++SET GLOBAL wsrep_sync_wait = 7;
++SET GLOBAL wsrep_sync_wait = 7;
++CREATE DATABASE `database with space`;
++USE `database with space`;
++CREATE TABLE `table with space` (
++`column with space` INTEGER AUTO_INCREMENT PRIMARY KEY,
++`second column with space` INTEGER,
++UNIQUE `index name with space` (`second column with space`)
++);
++INSERT INTO `table with space` VALUES (DEFAULT, 1);
++CREATE DATABASE `база`;
++USE `база`;
++CREATE TABLE `таблица` (
++`първа_колона` INTEGER PRIMARY KEY,
++`втора_колона` INTEGER,
++UNIQUE `индекс` (`втора_колона`)
++);
++INSERT INTO `таблица` VALUES (1, 1);
++CREATE DATABASE `втора база`;
++USE `втора база`;
++CREATE TABLE `втора таблица` (
++`първа колона` INTEGER,
++`втора колона` INTEGER,
++KEY `първи индекс` (`първа колона`)
++);
++INSERT INTO `втора таблица` VALUES (1, 1);
++USE `database with space`;
++SELECT `second column with space` FROM `table with space`;
++second column with space
++1
++USE `база`;
++SELECT * FROM `таблица`;
++първа_колона втора_колона
++1 1
++USE `втора база`;
++SELECT `втора колона` FROM `втора таблица`;
++втора колона
++1
++SET GLOBAL wsrep_sync_wait = (SELECT @@wsrep_sync_wait);
++DROP TABLE `database with space`.`table with space`;
++DROP TABLE `база`.`таблица`;
++DROP TABLE `втора база`.`втора таблица`;
++DROP DATABASE `database with space`;
++DROP DATABASE `база`;
++DROP DATABASE `втора база`;
++SET GLOBAL wsrep_sync_wait = (SELECT @@wsrep_sync_wait);
+diff --git a/mysql-test/suite/galera/r/galera_unicode_pk.result b/mysql-test/suite/galera/r/galera_unicode_pk.result
+new file mode 100644
+index 0000000..d59615b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_unicode_pk.result
+@@ -0,0 +1,31 @@
++CREATE TABLE t1 (
++f1 VARCHAR(255) PRIMARY KEY
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++INSERT INTO t1 VALUES ('текст');
++SELECT f1 = 'текст' FROM t1;
++f1 = 'текст'
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст2';
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст3';
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SELECT f1 = 'текст2' FROM t1;
++f1 = 'текст2'
++1
++SELECT f1 = 'текст2' FROM t1 WHERE f1 = 'текст2';
++f1 = 'текст2'
++1
++START TRANSACTION;
++INSERT INTO t1 VALUES ('текст4');
++START TRANSACTION;
++INSERT INTO t1 VALUES ('текст4');
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++COMMIT;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_update_limit.result b/mysql-test/suite/galera/r/galera_update_limit.result
+new file mode 100644
+index 0000000..c26eb1c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_update_limit.result
+@@ -0,0 +1,17 @@
++CREATE TABLE ten (f1 INTEGER) Engine=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 SELECT f1 FROM ten ORDER BY RAND();
++UPDATE IGNORE t1 SET f1 = FLOOR(1 + (RAND() * 10)) ORDER BY RAND() LIMIT 5;
++sum_matches
++1
++max_matches
++1
++DROP TABLE t1;
++CREATE TABLE t2 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t2 SELECT f1 FROM ten ORDER BY RAND();
++UPDATE IGNORE t2 SET f1 = FLOOR(1 + (RAND() * 10)) ORDER BY RAND() LIMIT 5;
++sum_matches
++1
++DROP TABLE t2;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_v1_row_events.result b/mysql-test/suite/galera/r/galera_v1_row_events.result
+new file mode 100644
+index 0000000..a6ab623
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_v1_row_events.result
+@@ -0,0 +1,10 @@
++CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_auto_inc_control_off.result b/mysql-test/suite/galera/r/galera_var_auto_inc_control_off.result
+new file mode 100644
+index 0000000..92b69fb
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_auto_inc_control_off.result
+@@ -0,0 +1,61 @@
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++SELECT @@auto_increment_increment = 1;
++@@auto_increment_increment = 1
++1
++SELECT @@auto_increment_offset = 1;
++@@auto_increment_offset = 1
++1
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, node VARCHAR(10)) ENGINE=InnoDB;
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `f1` int(11) NOT NULL AUTO_INCREMENT,
++ `node` varchar(10) DEFAULT NULL,
++ PRIMARY KEY (`f1`)
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++SHOW CREATE TABLE t1;
++Table Create Table
++t1 CREATE TABLE `t1` (
++ `f1` int(11) NOT NULL AUTO_INCREMENT,
++ `node` varchar(10) DEFAULT NULL,
++ PRIMARY KEY (`f1`)
++) ENGINE=InnoDB DEFAULT CHARSET=latin1
++SELECT @@auto_increment_increment = 1;
++@@auto_increment_increment = 1
++1
++SELECT @@auto_increment_offset = 1;
++@@auto_increment_offset = 1
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (node) VALUES ('node1');
++SELECT f1 FROM t1;
++f1
++1
++SELECT @@auto_increment_increment = 1;
++@@auto_increment_increment = 1
++1
++SELECT @@auto_increment_offset = 1;
++@@auto_increment_offset = 1
++1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (node) VALUES ('node2');
++SELECT f1 FROM t1;
++f1
++1
++COMMIT;
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++SELECT * FROM t1;
++f1 node
++1 node1
++SELECT * FROM t1;
++f1 node
++1 node1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_auto_inc_control_on.result b/mysql-test/suite/galera/r/galera_var_auto_inc_control_on.result
+new file mode 100644
+index 0000000..8859209
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_auto_inc_control_on.result
+@@ -0,0 +1,30 @@
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, node VARCHAR(10)) ENGINE=InnoDB;
++SELECT @@auto_increment_increment = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size');
++@@auto_increment_increment = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size')
++1
++SELECT @@auto_increment_offset = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index') + 1;
++@@auto_increment_offset = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index') + 1
++1
++INSERT INTO t1 VALUES (DEFAULT, 'node1');;
++INSERT INTO t1 VALUES (DEFAULT, 'node2');;
++SELECT @@auto_increment_increment = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size');
++@@auto_increment_increment = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size')
++1
++SELECT @@auto_increment_offset = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index') + 1;
++@@auto_increment_offset = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index') + 1
++1
++INSERT INTO t1 VALUES (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2');;
++INSERT INTO t1 VALUES (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1');;
++SELECT COUNT(*) = 22 FROM t1;
++COUNT(*) = 22
++1
++SELECT COUNT(DISTINCT f1) = 22 FROM t1;
++COUNT(DISTINCT f1) = 22
++1
++SELECT COUNT(*) = 22 FROM t1;
++COUNT(*) = 22
++1
++SELECT COUNT(DISTINCT f1) = 22 FROM t1;
++COUNT(DISTINCT f1) = 22
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_certify_nonPK_off.result b/mysql-test/suite/galera/r/galera_var_certify_nonPK_off.result
+new file mode 100644
+index 0000000..35dabb7
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_certify_nonPK_off.result
+@@ -0,0 +1,21 @@
++SET GLOBAL wsrep_certify_nonPK = OFF;
++SET GLOBAL wsrep_certify_nonPK = OFF;
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB /* Table has no primary key */;
++CREATE TABLE t2 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1), (2);
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++INSERT INTO t2 VALUES (1), (2);
++UPDATE t2 SET f1 = 3 WHERE f1 = 1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 2 FROM t2;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 3;
++COUNT(*) = 1
++1
++SET GLOBAL wsrep_certify_nonPK = 1;
++SET GLOBAL wsrep_certify_nonPK = 1;
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_var_cluster_address.result b/mysql-test/suite/galera/r/galera_var_cluster_address.result
+new file mode 100644
+index 0000000..cd52007
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_cluster_address.result
+@@ -0,0 +1,63 @@
++SET GLOBAL wsrep_cluster_address = 'foo://';
++SHOW STATUS;
++ERROR HY000: Lock wait timeout exceeded; try restarting transaction
++SET SESSION wsrep_sync_wait=0;
++SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS;
++ERROR 08S01: Unknown command
++SHOW STATUS LIKE 'wsrep_ready';
++Variable_name Value
++wsrep_ready OFF
++SHOW STATUS LIKE 'wsrep_cluster_status';
++Variable_name Value
++wsrep_cluster_status non-Primary
++SHOW STATUS LIKE 'wsrep_local_state';
++Variable_name Value
++wsrep_local_state 0
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++Variable_name Value
++wsrep_local_state_comment Initialized
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 1
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++SET GLOBAL wsrep_cluster_address = 'gcomm://192.0.2.1';
++SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS;
++ERROR 08S01: Unknown command
++SHOW STATUS LIKE 'wsrep_ready';
++Variable_name Value
++wsrep_ready OFF
++SHOW STATUS LIKE 'wsrep_cluster_status';
++Variable_name Value
++wsrep_cluster_status non-Primary
++SHOW STATUS LIKE 'wsrep_local_state';
++Variable_name Value
++wsrep_local_state 0
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++Variable_name Value
++wsrep_local_state_comment Initialized
++SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++CALL mtr.add_suppression("Backend not supported: foo");
++CALL mtr.add_suppression("Failed to initialize backend using 'foo");
++CALL mtr.add_suppression("Failed to open channel 'my_wsrep_cluster' at 'foo");
++CALL mtr.add_suppression("gcs connect failed: Socket type not supported");
++CALL mtr.add_suppression("wsrep::connect\\(\\) failed: 7");
++CALL mtr.add_suppression("gcs_caused\\(\\) returned -103 \\(Software caused connection abort\\)");
++CALL mtr.add_suppression("failed to open gcomm backend connection: 110: failed to reach primary view: 110");
++CALL mtr.add_suppression("Failed to open backend connection: -110 \\(Connection timed out\\)");
++CALL mtr.add_suppression("Failed to open channel 'my_wsrep_cluster' at 'gcomm://192\\.0\\.2\\.1': -110 \\(Connection timed out\\)");
++CALL mtr.add_suppression("gcs connect failed: Connection timed out");
+diff --git a/mysql-test/suite/galera/r/galera_var_desync_on.result b/mysql-test/suite/galera/r/galera_var_desync_on.result
+new file mode 100644
+index 0000000..0b5f346
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_desync_on.result
+@@ -0,0 +1,29 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET GLOBAL wsrep_provider_options = 'gcs.fc_limit=1';
++SET GLOBAL wsrep_desync = TRUE;
++FLUSH TABLES WITH READ LOCK;
++INSERT INTO t1 VALUES (2);
++INSERT INTO t1 VALUES (3);
++INSERT INTO t1 VALUES (4);
++INSERT INTO t1 VALUES (5);
++INSERT INTO t1 VALUES (6);
++INSERT INTO t1 VALUES (7);
++INSERT INTO t1 VALUES (8);
++INSERT INTO t1 VALUES (9);
++INSERT INTO t1 VALUES (10);
++SET SESSION wsrep_sync_wait = 0;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SET GLOBAL wsrep_desync = FALSE;
++UNLOCK TABLES;
++SET SESSION wsrep_sync_wait = 1;
++SELECT COUNT(*) = 10 FROM t1;
++COUNT(*) = 10
++1
++INSERT INTO t1 VALUES (11);
++SELECT COUNT(*) = 11 FROM t1;
++COUNT(*) = 11
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_fkchecks.result b/mysql-test/suite/galera/r/galera_var_fkchecks.result
+new file mode 100644
+index 0000000..342212a
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_fkchecks.result
+@@ -0,0 +1,26 @@
++CREATE TABLE parent (
++id INT PRIMARY KEY,
++KEY (id)
++) ENGINE=InnoDB;
++CREATE TABLE child (
++id INT PRIMARY KEY,
++parent_id INT,
++FOREIGN KEY (parent_id)
++REFERENCES parent(id)
++) ENGINE=InnoDB;
++INSERT INTO parent VALUES (1);
++INSERT INTO child VALUES (1,1);
++SET SESSION foreign_key_checks = 0;
++INSERT INTO child VALUES (2,2);
++SELECT COUNT(*) = 1 FROM child WHERE id = 2;
++COUNT(*) = 1
++1
++INSERT INTO child VALUES (3,3);
++ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))
++SET SESSION foreign_key_checks = 0;
++DELETE FROM parent;
++SELECT COUNT(*) = 0 FROM parent;
++COUNT(*) = 0
++1
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/r/galera_var_innodb_disallow_writes.result b/mysql-test/suite/galera/r/galera_var_innodb_disallow_writes.result
+new file mode 100644
+index 0000000..912e45a
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_innodb_disallow_writes.result
+@@ -0,0 +1,12 @@
++SET SESSION wsrep_sync_wait = 0;
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++SET GLOBAL innodb_disallow_writes=ON;
++INSERT INTO t1 VALUES (1);;
++SET GLOBAL innodb_disallow_writes=OFF;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_load_data_splitting.result b/mysql-test/suite/galera/r/galera_var_load_data_splitting.result
+new file mode 100644
+index 0000000..db145fd
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_load_data_splitting.result
+@@ -0,0 +1,9 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET GLOBAL wsrep_load_data_splitting = TRUE;
++SELECT COUNT(*) = 95000 FROM t1;
++COUNT(*) = 95000
++1
++wsrep_last_committed_diff
++1
++SET GLOBAL wsrep_load_data_splitting = 1;;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_max_ws_size.result b/mysql-test/suite/galera/r/galera_var_max_ws_size.result
+new file mode 100644
+index 0000000..6db5c24
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_max_ws_size.result
+@@ -0,0 +1,12 @@
++call mtr.add_suppression('WSREP: transaction size limit.*');
++call mtr.add_suppression('WSREP: rbr write fail.*');
++call mtr.add_suppression('WSREP: Maximum writeset size exceeded by.*');
++call mtr.add_suppression('WSREP: transaction size exceeded.*');
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 VARCHAR(1024)) Engine=InnoDB;
++SET GLOBAL wsrep_max_ws_size = 1024;
++INSERT INTO t1 VALUES (DEFAULT, REPEAT('X', 1024));
++ERROR HY000: Got error 5 during COMMIT
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_mysql_replication_bundle.result b/mysql-test/suite/galera/r/galera_var_mysql_replication_bundle.result
+new file mode 100644
+index 0000000..f2a951c
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_mysql_replication_bundle.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=InnoDB;
++SET GLOBAL wsrep_mysql_replication_bundle = 2;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++0
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SET GLOBAL wsrep_mysql_replication_bundle = 0;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_notify_cmd.result b/mysql-test/suite/galera/r/galera_var_notify_cmd.result
+new file mode 100644
+index 0000000..e9e4605
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_notify_cmd.result
+@@ -0,0 +1,10 @@
++SELECT COUNT(DISTINCT uuid) = 2 FROM mtr_wsrep_notify.membership;
++COUNT(DISTINCT uuid) = 2
++1
++SELECT MAX(size) = 2 FROM mtr_wsrep_notify.status;
++MAX(size) = 2
++1
++SELECT COUNT(DISTINCT idx) = 2 FROM mtr_wsrep_notify.status;
++COUNT(DISTINCT idx) = 2
++1
++DROP SCHEMA mtr_wsrep_notify;
+diff --git a/mysql-test/suite/galera/r/galera_var_replicate_myisam_off.result b/mysql-test/suite/galera/r/galera_var_replicate_myisam_off.result
+new file mode 100644
+index 0000000..c8b7907
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_replicate_myisam_off.result
+@@ -0,0 +1,8 @@
++SET GLOBAL wsrep_replicate_myisam = FALSE;
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=MyISAM;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++SET GLOBAL wsrep_replicate_myisam = 0;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_var_replicate_myisam_on.result b/mysql-test/suite/galera/r/galera_var_replicate_myisam_on.result
+new file mode 100644
+index 0000000..73a0576
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_replicate_myisam_on.result
+@@ -0,0 +1,78 @@
++SET GLOBAL wsrep_replicate_myisam = TRUE;
++SET GLOBAL wsrep_replicate_myisam = TRUE;
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=MyISAM;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (2), (3);
++INSERT INTO t1 SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL;
++SELECT COUNT(*) = 5 FROM t1;
++COUNT(*) = 5
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 VARCHAR(100)) ENGINE=MyISAM;
++INSERT INTO t1 VALUES (1, 'abc'),(2,'abc'), (3, 'xxx');
++REPLACE INTO t1 VALUES (1, 'klm'), (2,'xyz');
++REPLACE INTO t1 SELECT 3, 'yyy' FROM DUAL;
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 1 AND f2 = 'klm';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2 AND f2 = 'xyz';
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 3 AND f2 = 'yyy';
++COUNT(*) = 1
++1
++UPDATE t1 SET f2 = 'zzz' WHERE f2 = 'yyy';
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'zzz';
++COUNT(*) = 1
++1
++DELETE FROM t1 WHERE f2 = 'zzz';
++SELECT COUNT(*) = 0 FROM t1 WHERE f2 = 'zzz';
++COUNT(*) = 0
++1
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++DROP TABLE t1;
++CREATE TABLE t1 (f1 INTEGER) ENGINE=MyISAM;
++CREATE TABLE t2 (f1 INTEGER) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++COMMIT;
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = 1 FROM t2;
++COUNT(*) = 1
++1
++START TRANSACTION;
++INSERT INTO t1 VALUES (2);
++INSERT INTO t2 VALUES (2);
++ROLLBACK;
++Warnings:
++Warning 1196 Some non-transactional changed tables couldn't be rolled back
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 1 FROM t2;
++COUNT(*) = 1
++1
++DROP TABLE t1;
++DROP TABLE t2;
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=MyISAM;
++CREATE TABLE t2 (f2 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
++COMMIT;
++DROP TABLE t1;
++DROP TABLE t2;
++SET GLOBAL wsrep_replicate_myisam = 0;
++SET GLOBAL wsrep_replicate_myisam = 0;
+diff --git a/mysql-test/suite/galera/r/galera_var_slave_threads.result b/mysql-test/suite/galera/r/galera_var_slave_threads.result
+new file mode 100644
+index 0000000..6b84f03
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_slave_threads.result
+@@ -0,0 +1,106 @@
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=InnoDB;
++CREATE TABLE t2 (f1 INT AUTO_INCREMENT PRIMARY KEY) Engine=InnoDB;
++SET GLOBAL wsrep_slave_threads = 0;
++Warnings:
++Warning 1292 Truncated incorrect wsrep_slave_threads value: '0'
++SHOW WARNINGS;
++Level Code Message
++Warning 1292 Truncated incorrect wsrep_slave_threads value: '0'
++SELECT @@wsrep_slave_threads = 1;
++@@wsrep_slave_threads = 1
++1
++SET GLOBAL wsrep_slave_threads = 1;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%wsrep aborter%';
++COUNT(*) = 1
++1
++SET GLOBAL wsrep_slave_threads = 64;
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SELECT COUNT(*) = @@wsrep_slave_threads + 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++COUNT(*) = @@wsrep_slave_threads + 1
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%wsrep aborter%';
++COUNT(*) = 1
++1
++SET GLOBAL wsrep_slave_threads = 1;
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++INSERT INTO t2 VALUES (DEFAULT);
++SELECT COUNT(*) = 64 FROM t2;
++COUNT(*) = 64
++1
++SELECT COUNT(*) = @@wsrep_slave_threads + 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++COUNT(*) = @@wsrep_slave_threads + 1
++1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%wsrep aborter%';
++COUNT(*) = 1
++1
++SET GLOBAL wsrep_slave_threads = 1;
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/galera_var_sync_wait.result b/mysql-test/suite/galera/r/galera_var_sync_wait.result
+new file mode 100644
+index 0000000..f6136a4
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_sync_wait.result
+@@ -0,0 +1,21 @@
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=InnoDB;
++SET GLOBAL wsrep_sync_wait = 1;
++SHOW TABLES LIKE '%t1';
++Tables_in_test (%t1)
++t1
++SELECT COUNT(*) = 0 FROM t1;
++COUNT(*) = 0
++1
++CREATE TABLE t2 (f1 INT PRIMARY KEY) Engine=InnoDB;
++SET GLOBAL wsrep_sync_wait = 4;
++INSERT INTO t2 VALUES (1);
++CREATE TABLE t3 (f1 INT PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t3 VALUES (1);
++SET GLOBAL wsrep_sync_wait = 2;
++UPDATE t3 SET f1 = 2;
++affected rows: 1
++info: Rows matched: 1 Changed: 1 Warnings: 0
++SET GLOBAL wsrep_sync_wait = 7;
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
+diff --git a/mysql-test/suite/galera/r/galera_var_wsrep_on_off.result b/mysql-test/suite/galera/r/galera_var_wsrep_on_off.result
+new file mode 100644
+index 0000000..8b1c4eb
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_var_wsrep_on_off.result
+@@ -0,0 +1,19 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET SESSION wsrep_on = FALSE;
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++SET GLOBAL wsrep_on = TRUE;
++INSERT INTO t1 VALUES (3);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SELECT COUNT(*) = 0 FROM t1 WHERE f1 = 2;
++COUNT(*) = 0
++1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 3;
++COUNT(*) = 1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_wan.result b/mysql-test/suite/galera/r/galera_wan.result
+new file mode 100644
+index 0000000..6be32b2
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_wan.result
+@@ -0,0 +1,14 @@
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 4
++1
++CREATE TABLE t1 (f1 INTEGER);
++INSERT INTO t1 VALUES (1);
++CALL mtr.add_suppression("There are no nodes in the same segment that will ever be able to become donors, yet there is a suitable donor outside");
++SELECT VARIABLE_VALUE LIKE '%gmcast.segment = 3%' FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME = 'wsrep_provider_options';
++VARIABLE_VALUE LIKE '%gmcast.segment = 3%'
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
++CALL mtr.add_suppression("There are no nodes in the same segment that will ever be able to become donors, yet there is a suitable donor outside");
+diff --git a/mysql-test/suite/galera/r/galera_wsrep_desync_wsrep_on.result b/mysql-test/suite/galera/r/galera_wsrep_desync_wsrep_on.result
+new file mode 100644
+index 0000000..06fc27a
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_wsrep_desync_wsrep_on.result
+@@ -0,0 +1,33 @@
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t1 (f1) SELECT 000000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++SET GLOBAL wsrep_desync = TRUE;
++SET SESSION wsrep_on = FALSE;
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET SESSION wsrep_on = TRUE;
++SET GLOBAL wsrep_desync = FALSE;
++INSERT INTO t1 (f1) SELECT 100000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++SELECT COUNT(*) = 200000 FROM t1;
++COUNT(*) = 200000
++1
++SELECT MAX(f1) = 199999 FROM t1;
++MAX(f1) = 199999
++1
++SELECT COUNT(*) = 200000 FROM t1;
++COUNT(*) = 200000
++1
++SELECT MAX(f1) = 199999 FROM t1;
++MAX(f1) = 199999
++1
++SET GLOBAL wsrep_desync = TRUE;
++SET SESSION wsrep_on = FALSE;
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET SESSION wsrep_on = TRUE;
++SET GLOBAL wsrep_desync = FALSE;
++INSERT INTO t1 (f1) VALUES (1);
++ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
++INSERT INTO t1 (f1) VALUES (100);
++ERROR 23000: Duplicate entry '100' for key 'PRIMARY'
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/r/galera_wsrep_new_cluster.result b/mysql-test/suite/galera/r/galera_wsrep_new_cluster.result
+new file mode 100644
+index 0000000..e3f2fa4
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_wsrep_new_cluster.result
+@@ -0,0 +1,36 @@
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index';
++VARIABLE_VALUE = 0
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index';
++VARIABLE_VALUE = 0
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
+diff --git a/mysql-test/suite/galera/r/galera_wsrep_provider_unset_set.result b/mysql-test/suite/galera/r/galera_wsrep_provider_unset_set.result
+new file mode 100644
+index 0000000..681e460
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_wsrep_provider_unset_set.result
+@@ -0,0 +1,13 @@
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET GLOBAL wsrep_provider='none';
++INSERT INTO t1 VALUES (2);
++INSERT INTO t1 VALUES (3);
++INSERT INTO t1 VALUES (4);
++SELECT COUNT(*) = 4 FROM t1;
++COUNT(*) = 4
++1
++SELECT COUNT(*) = 3 FROM t1;
++COUNT(*) = 3
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/galera_zero_length_column.result b/mysql-test/suite/galera/r/galera_zero_length_column.result
+new file mode 100644
+index 0000000..2e6119b
+--- /dev/null
++++ b/mysql-test/suite/galera/r/galera_zero_length_column.result
+@@ -0,0 +1,38 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY , f2 VARCHAR(0)) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 VARCHAR(0)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1, NULL);
++INSERT INTO t1 VALUES (2, '');
++INSERT INTO t2 VALUES (NULL);
++INSERT INTO t2 VALUES ('');
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SELECT f2 IS NULL FROM t1 WHERE f1 = 1;
++f2 IS NULL
++1
++SELECT f2 = '' FROM t1 WHERE f1 = 2;
++f2 = ''
++1
++SELECT COUNT(*) = 2 FROM t2;
++COUNT(*) = 2
++1
++SELECT f1 IS NULL FROM t2 WHERE f1 IS NULL;
++f1 IS NULL
++1
++SELECT f1 = '' FROM t2 WHERE f1 IS NOT NULL;
++f1 = ''
++1
++UPDATE t1 SET f2 = '' WHERE f1 = 1;
++UPDATE t1 SET f2 = NULL WHERE f1 = 2;
++UPDATE t2 SET f1 = '' WHERE f1 IS NULL;
++SELECT f2 = '' FROM t1 WHERE f1 = 1;
++f2 = ''
++1
++SELECT f2 IS NULL FROM t1 WHERE f1 = 2;
++f2 IS NULL
++1
++SELECT COUNT(*) = 2 FROM t2 WHERE f1 = '';
++COUNT(*) = 2
++1
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/r/lp1276424.result b/mysql-test/suite/galera/r/lp1276424.result
+new file mode 100644
+index 0000000..5f09ec9
+--- /dev/null
++++ b/mysql-test/suite/galera/r/lp1276424.result
+@@ -0,0 +1,11 @@
++CREATE TABLE t1 (f1 INT DEFAULT NULL, UNIQUE KEY i1 (f1)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL);
++INSERT INTO t1 VALUES (NULL);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++SELECT f1 IS NULL FROM t1;
++f1 IS NULL
++1
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/r/lp1347768.result b/mysql-test/suite/galera/r/lp1347768.result
+new file mode 100644
+index 0000000..c085059
+--- /dev/null
++++ b/mysql-test/suite/galera/r/lp1347768.result
+@@ -0,0 +1,17 @@
++CREATE TABLE `r8kmb_redirect_links` (
++`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
++`old_url` varchar(255) DEFAULT NULL,
++`new_url` varchar(255) NOT NULL,
++`referer` varchar(150) NOT NULL,
++`comment` varchar(255) NOT NULL,
++`published` tinyint(4) NOT NULL,
++`created_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
++`modified_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
++PRIMARY KEY (`id`),
++UNIQUE KEY `idx_link_old` (`old_url`),
++KEY `idx_link_modifed` (`modified_date`)
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++INSERT INTO r8kmb_redirect_links VALUES (550,'http://mysite.com/images/download/ßуñûічýøù_ôþóþòір_þфõÑ.doc','','','',0,'2013-07-15 14:29:42','0000-00-00 00:00:00');
++Warnings:
++Warning 1265 Data truncated for column 'old_url' at row 1
++DROP TABLE r8kmb_redirect_links;
+diff --git a/mysql-test/suite/galera/r/lp959512.result b/mysql-test/suite/galera/r/lp959512.result
+new file mode 100644
+index 0000000..55adfa3
+--- /dev/null
++++ b/mysql-test/suite/galera/r/lp959512.result
+@@ -0,0 +1,24 @@
++DROP TABLE IF EXISTS variable;
++Warnings:
++Note 1051 Unknown table 'test.variable'
++DROP TABLE IF EXISTS foo;
++Warnings:
++Note 1051 Unknown table 'test.foo'
++CREATE TABLE variable (
++name varchar(128) NOT NULL DEFAULT '' COMMENT 'The name of the variable.',
++value longblob NOT NULL COMMENT 'The value of the variable.',
++PRIMARY KEY (name)
++) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Named variable/value pairs created by Drupal core or any...';
++CREATE TABLE foo (a int);
++INSERT INTO variable (name, value) VALUES ('menu_expanded', 'a:0:{}');
++START TRANSACTION;
++SELECT 1 AS expression FROM variable variable
++WHERE ( (name = 'menu_expanded') ) FOR UPDATE;
++expression
++1
++UPDATE variable SET value='a:0:{}' WHERE ( (name = 'menu_expanded') );
++COMMIT;
++INSERT INTO foo VALUES (1);
++UPDATE foo SET a = 2 WHERE a = 1;
++DROP TABLE foo;
++DROP TABLE variable;
+diff --git a/mysql-test/suite/galera/t/disabled.def b/mysql-test/suite/galera/t/disabled.def
+new file mode 100644
+index 0000000..a1495c6
+--- /dev/null
++++ b/mysql-test/suite/galera/t/disabled.def
+@@ -0,0 +1,4 @@
++galera_wsrep_provider_unset_set : lp1379204 'Unsupported protocol downgrade: incremental data collection disabled. Expect abort.'
++galera_kill_nochanges : mysql-wsrep#24 Galera server does not restart properly if killed
++galera_bf_abort_for_update : mysql-wsrep#26 SELECT FOR UPDATE sometimes allowed to proceed in the face of a concurrent update
++galera_toi_ddl_fk_insert : qa#39 galera_toi_ddl_fk_insert fails sporadically
+diff --git a/mysql-test/suite/galera/t/galera_account_management.test b/mysql-test/suite/galera/t/galera_account_management.test
+new file mode 100644
+index 0000000..357319a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_account_management.test
+@@ -0,0 +1,101 @@
++#
++# Test the account management statements - GRANT, REVOKE, etc.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# CREATE USER
++#
++--connection node_1
++CREATE USER user1, user2 IDENTIFIED BY 'password';
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM mysql.user WHERE user IN ('user1', 'user2');
++
++#
++# ALTER USER
++#
++
++# LP bug 1376269
++#
++#--connection node_1
++#ALTER USER user1 PASSWORD EXPIRE;
++#SELECT password_expired = 'Y' FROM mysql.user WHERE user = 'user1';
++#
++#--connection node_2
++#SELECT password_expired = 'Y' FROM mysql.user WHERE user = 'user1';
++
++#
++# RENAME USER
++#
++
++--connection node_1
++RENAME USER user2 TO user3;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM mysql.user WHERE user = 'user2';
++SELECT COUNT(*) = 1 FROM mysql.user WHERE user = 'user3';
++
++#
++# SET PASSWORD
++#
++
++--connection node_1
++SET PASSWORD FOR user3 = PASSWORD('foo');
++
++--connection node_1
++SELECT password != '' FROM mysql.user WHERE user = 'user3';
++
++#
++# DROP USER
++#
++--connection node_1
++DROP USER user1, user3;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM mysql.user WHERE user IN ('user1', 'user2');
++
++#
++# GRANT
++#
++
++--connection node_1
++GRANT ALL ON *.* TO user4 IDENTIFIED BY 'password';
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM mysql.user WHERE user = 'user4';
++SELECT Select_priv = 'Y' FROM mysql.user WHERE user = 'user4';
++
++#
++# GRANT PROXY ON
++#
++--connection node_1
++CREATE USER user5;
++GRANT PROXY ON user4 TO user5;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM mysql.proxies_priv WHERE user = 'user5';
++
++#
++# REVOKE
++#
++
++--connection node_1
++REVOKE ALL PRIVILEGES ON *.* FROM user4;
++
++--connection node_2
++SELECT Select_priv = 'N' FROM mysql.user WHERE user = 'user4';
++
++#
++# REVOKE PROXY
++#
++
++--connection node_1
++REVOKE PROXY ON user4 FROM user5;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM mysql.proxies_priv WHERE user = 'user5';
++
++DROP USER user4, user5;
+diff --git a/mysql-test/suite/galera/t/galera_alter_engine_innodb.test b/mysql-test/suite/galera/t/galera_alter_engine_innodb.test
+new file mode 100644
+index 0000000..bc914a3
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_alter_engine_innodb.test
+@@ -0,0 +1,17 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test ALTER ENGINE from InnoDB to InnoDB
++#
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++ALTER TABLE t1 ENGINE=InnoDB;
++
++--connection node_2
++SELECT ENGINE = 'InnoDB' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_alter_engine_myisam.test b/mysql-test/suite/galera/t/galera_alter_engine_myisam.test
+new file mode 100644
+index 0000000..6d41d27
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_alter_engine_myisam.test
+@@ -0,0 +1,25 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test ALTER ENGINE from MyISAM to InnoDB under wsrep_replicate_myisam
++#
++
++--let $wsrep_replicate_myisam_orig = `SELECT @@wsrep_replicate_myisam`
++SET GLOBAL wsrep_replicate_myisam = TRUE;
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=MyISAM;
++INSERT INTO t1 VALUES (1);
++
++ALTER TABLE t1 ENGINE=InnoDB;
++
++--connection node_2
++SELECT ENGINE = 'InnoDB' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_1
++--disable_query_log
++--eval SET GLOBAL wsrep_replicate_myisam = $wsrep_replicate_myisam_orig
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_alter_table_force.test b/mysql-test/suite/galera/t/galera_alter_table_force.test
+new file mode 100644
+index 0000000..1fcc9d4
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_alter_table_force.test
+@@ -0,0 +1,17 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test ALTER TABLE FORCE, a 5.6.3 feature that simply rebuilds the table
++#
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++ALTER TABLE t1 FORCE;
++
++--connection node_2
++SELECT ENGINE = 'InnoDB' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_as_master.cnf b/mysql-test/suite/galera/t/galera_as_master.cnf
+new file mode 100644
+index 0000000..52fd309
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_master.cnf
+@@ -0,0 +1 @@
++!include ../galera_2nodes_as_master.cnf
+diff --git a/mysql-test/suite/galera/t/galera_as_master.test b/mysql-test/suite/galera/t/galera_as_master.test
+new file mode 100644
+index 0000000..3367a20
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_master.test
+@@ -0,0 +1,39 @@
++#
++# Test Galera as a master to a MySQL slave
++#
++# The galera/galera_2node_master.cnf describes the setup of the nodes
++#
++
++--source include/have_innodb.inc
++--source include/have_log_bin.inc
++--source include/galera_cluster.inc
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++
++--connection node_2
++INSERT INTO t1 VALUES(2);
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 2 FROM t1;
++--source include/wait_condition.inc
++
++--connection node_1
++DROP TABLE t1;
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/t/galera_as_master_gtid.cnf b/mysql-test/suite/galera/t/galera_as_master_gtid.cnf
+new file mode 100644
+index 0000000..1951755
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_master_gtid.cnf
+@@ -0,0 +1,8 @@
++!include ../galera_2nodes_as_master.cnf
++
++[mysqld]
++gtid-mode=ON
++log-bin=mysqld-bin
++log-slave-updates
++enforce-gtid-consistency
++binlog-format=ROW
+diff --git a/mysql-test/suite/galera/t/galera_as_master_gtid.test b/mysql-test/suite/galera/t/galera_as_master_gtid.test
+new file mode 100644
+index 0000000..9db104b
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_master_gtid.test
+@@ -0,0 +1,70 @@
++#
++# Test Galera as a master to a MySQL slave with GTID
++#
++# The galera/galera_2node_master.cnf describes the setup of the nodes
++#
++# We check that all transactions originating from within Galera use a UUID that is
++# different from the server_uuid of either node
++#
++#
++
++--source include/have_innodb.inc
++--source include/have_log_bin.inc
++--source include/galera_cluster.inc
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++
++--let $effective_uuid = `SELECT LEFT(@@global.gtid_executed, 36)`
++--disable_query_log
++--eval SELECT '$effective_uuid' != @@global.server_uuid AS uuids_do_not_match;
++--enable_query_log
++
++--replace_result $effective_uuid <effective_uuid>
++--replace_regex /table_id: [0-9]+/table_id: #/ /xid=[0-9]+/xid=#/
++SHOW BINLOG EVENTS IN 'mysqld-bin.000002' FROM 120;
++
++--connection node_2
++INSERT INTO t1 VALUES(2);
++
++--disable_query_log
++--eval SELECT '$effective_uuid' != @@global.server_uuid AS uuids_do_not_match;
++--eval SELECT '$effective_uuid' = LEFT(@@global.gtid_executed, 36) AS uuids_match;
++--enable_query_log
++
++--replace_result $effective_uuid <effective_uuid>
++--replace_regex /table_id: [0-9]+/table_id: #/ /xid=[0-9]+/xid=#/
++SHOW BINLOG EVENTS IN 'mysqld-bin.000003' FROM 120;
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 2 FROM t1;
++--source include/wait_condition.inc
++
++--disable_query_log
++--eval SELECT '$effective_uuid' != @@global.server_uuid AS uuids_do_not_match;
++--eval SELECT '$effective_uuid' = LEFT(@@global.gtid_executed, 36) AS uuids_match;
++--enable_query_log
++
++--replace_result $effective_uuid <effective_uuid>
++--replace_regex /table_id: [0-9]+/table_id: #/ /xid=[0-9]+/xid=#/
++SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
++
++--connection node_1
++DROP TABLE t1;
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/t/galera_as_master_gtid_change_master.cnf b/mysql-test/suite/galera/t/galera_as_master_gtid_change_master.cnf
+new file mode 100644
+index 0000000..1951755
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_master_gtid_change_master.cnf
+@@ -0,0 +1,8 @@
++!include ../galera_2nodes_as_master.cnf
++
++[mysqld]
++gtid-mode=ON
++log-bin=mysqld-bin
++log-slave-updates
++enforce-gtid-consistency
++binlog-format=ROW
+diff --git a/mysql-test/suite/galera/t/galera_as_master_gtid_change_master.test b/mysql-test/suite/galera/t/galera_as_master_gtid_change_master.test
+new file mode 100644
+index 0000000..23606d7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_master_gtid_change_master.test
+@@ -0,0 +1,54 @@
++#
++# Test that a MySQL slave can use CHANGE MASTER MASTER_AUTO_POSITION to begin replicating
++# from another Galera node
++#
++# The galera/galera_2node_master.cnf describes the setup of the nodes
++#
++#
++
++--source include/have_innodb.inc
++--source include/have_log_bin.inc
++--source include/galera_cluster.inc
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++
++--connection node_2
++INSERT INTO t1 VALUES(2);
++
++--connection node_3
++STOP SLAVE;
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_2, MASTER_AUTO_POSITION=1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++INSERT INTO t1 VALUES(3);
++
++--connection node_2
++INSERT INTO t1 VALUES(4);
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 4 FROM t1;
++--source include/wait_condition.inc
++
++--connection node_1
++DROP TABLE t1;
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/t/galera_as_slave.cnf b/mysql-test/suite/galera/t/galera_as_slave.cnf
+new file mode 100644
+index 0000000..9449ec9
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_slave.cnf
+@@ -0,0 +1 @@
++!include ../galera_2nodes_as_slave.cnf
+diff --git a/mysql-test/suite/galera/t/galera_as_slave.test b/mysql-test/suite/galera/t/galera_as_slave.test
+new file mode 100644
+index 0000000..0f899fd
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_slave.test
+@@ -0,0 +1,48 @@
++#
++# Test Galera as a slave to a MySQL master
++#
++# The galera/galera_2node_slave.cnf describes the setup of the nodes
++#
++
++--source include/have_innodb.inc
++--source include/have_log_bin.inc
++
++# As node #1 is not a Galera node, we connect to node #2 in order to run include/galera_cluster.inc
++--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
++--source include/galera_cluster.inc
++
++--connection node_2
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++
++--connection node_2
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 1 FROM t1;
++--source include/wait_condition.inc
++
++INSERT INTO t1 VALUES (2);
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++SELECT COUNT(*) = 2 FROM t1;
++INSERT INTO t1 VALUES (3);
++
++--connection node_2
++SELECT COUNT(*) = 3 FROM t1;
++
++--connection node_1
++DROP TABLE t1;
++
++--connection node_2
++--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/t/galera_as_slave_gtid.cnf b/mysql-test/suite/galera/t/galera_as_slave_gtid.cnf
+new file mode 100644
+index 0000000..92f6a16
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_slave_gtid.cnf
+@@ -0,0 +1,8 @@
++!include ../galera_2nodes_as_slave.cnf
++
++[mysqld]
++gtid-mode=ON
++log-bin=mysqld-bin
++log-slave-updates
++enforce-gtid-consistency
++binlog-format=ROW
+diff --git a/mysql-test/suite/galera/t/galera_as_slave_gtid.test b/mysql-test/suite/galera/t/galera_as_slave_gtid.test
+new file mode 100644
+index 0000000..02fe5f7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_as_slave_gtid.test
+@@ -0,0 +1,68 @@
++#
++# Test Galera as a slave to a MySQL master using GTIDs
++#
++# suite/galera/galera_2nodes_as_slave.cnf describes the setup of the nodes
++# suite/galera/t/galera_as_slave_gtid.cnf has the GTID options
++#
++# In addition to performing DDL and DML, we check that the gtid of the master is preserved inside the cluster
++#
++
++--source include/have_innodb.inc
++--source include/have_log_bin.inc
++
++# As node #1 is not a Galera node, we connect to node #2 in order to run include/galera_cluster.inc
++--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
++--source include/galera_cluster.inc
++
++--connection node_2
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES(1);
++
++SELECT LENGTH(@@global.gtid_executed) > 1;
++--let $gtid_executed_node1 = `SELECT @@global.gtid_executed;`
++
++--connection node_2
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 1 FROM t1;
++--source include/wait_condition.inc
++
++--disable_query_log
++--eval SELECT '$gtid_executed_node1' = @@global.gtid_executed AS gtid_executed_equal;
++--enable_query_log
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++SELECT COUNT(*) = 1 FROM t1;
++
++--disable_query_log
++--eval SELECT '$gtid_executed_node1' = @@global.gtid_executed AS gtid_executed_equal;
++--enable_query_log
++
++--connection node_1
++DROP TABLE t1;
++
++#
++# Unfortunately without the sleep below the following statement fails with "query returned no rows", which
++# is difficult to understand given that it is an aggregate query. A "query execution was interrupted"
++# warning is also reported by MTR, which is also weird.
++#
++
++--sleep 1
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--connection node_2
++--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++STOP SLAVE;
++RESET SLAVE ALL;
+diff --git a/mysql-test/suite/galera/t/galera_bf_abort.test b/mysql-test/suite/galera/t/galera_bf_abort.test
+new file mode 100644
+index 0000000..69825ea
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_bf_abort.test
+@@ -0,0 +1,29 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test a local transaction being aborted by a slave one
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++--let $wsrep_local_bf_aborts_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++INSERT INTO t1 VALUES (2);
++
++--let $wsrep_local_bf_aborts_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++
++--disable_query_log
++--eval SELECT $wsrep_local_bf_aborts_after - $wsrep_local_bf_aborts_before = 1 AS wsrep_local_aborts_increment;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_bf_abort_for_update.test b/mysql-test/suite/galera/t/galera_bf_abort_for_update.test
+new file mode 100644
+index 0000000..24c2977
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_bf_abort_for_update.test
+@@ -0,0 +1,29 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test a local transaction being aborted by a slave one while it is running a SELECT FOR UPDATE
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++--let $wsrep_local_bf_aborts_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++SELECT * FROM t1 FOR UPDATE;
++
++--let $wsrep_local_bf_aborts_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++
++--disable_query_log
++--eval SELECT $wsrep_local_bf_aborts_after - $wsrep_local_bf_aborts_before = 1 AS wsrep_local_aborts_increment;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_bf_abort_ftwrl.test b/mysql-test/suite/galera/t/galera_bf_abort_ftwrl.test
+new file mode 100644
+index 0000000..44398e7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_bf_abort_ftwrl.test
+@@ -0,0 +1,30 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# A local transaction running FLUSH TABLES WITH READ LOCK will not be aborted by a slave transaction
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++--let $wsrep_local_bf_aborts_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++--send FLUSH TABLES WITH READ LOCK;
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--reap
++
++UNLOCK TABLES;
++
++--let $wsrep_local_bf_aborts_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++
++# No aborts should be registered on the counter
++--disable_query_log
++--eval SELECT $wsrep_local_bf_aborts_after - $wsrep_local_bf_aborts_before = 0 AS wsrep_local_aborts_increment;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_bf_abort_get_lock.test b/mysql-test/suite/galera/t/galera_bf_abort_get_lock.test
+new file mode 100644
+index 0000000..72fc1c5
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_bf_abort_get_lock.test
+@@ -0,0 +1,36 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test a local transaction being aborted by a slave one while it is running a GET_LOCK()
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++SELECT GET_LOCK("foo", 1000);
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++--let $wsrep_local_bf_aborts_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++INSERT INTO t1 VALUES (1);
++--send SELECT GET_LOCK("foo", 1000);
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++--reap
++
++--let $wsrep_local_bf_aborts_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++
++# Check that wsrep_local_bf_aborts has been incremented by exactly 1
++--disable_query_log
++--eval SELECT $wsrep_local_bf_aborts_after - $wsrep_local_bf_aborts_before = 1 AS wsrep_local_aborts_increment;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_bf_abort_lock_table.test b/mysql-test/suite/galera/t/galera_bf_abort_lock_table.test
+new file mode 100644
+index 0000000..7884271
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_bf_abort_lock_table.test
+@@ -0,0 +1,33 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test that a local LOCK TABLE will be broken by an incoming remote transaction against that table
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++--let $wsrep_local_bf_aborts_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++LOCK TABLE t1 WRITE;
++
++# Issue a concurrent INSERT against the lock table that will block
++--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
++--send INSERT INTO t1 VALUES (1);
++
++--connection node_1
++INSERT INTO t1 VALUES (2);
++
++# The concurent insert is allowed to complete because the LOCK TABLE is now broken
++--connection node_2a
++--error 0
++--reap
++
++--let $wsrep_local_bf_aborts_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++
++--disable_query_log
++--eval SELECT $wsrep_local_bf_aborts_after - $wsrep_local_bf_aborts_before = 1 AS wsrep_local_aborts_increment;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_bf_abort_sleep.test b/mysql-test/suite/galera/t/galera_bf_abort_sleep.test
+new file mode 100644
+index 0000000..8d135dc
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_bf_abort_sleep.test
+@@ -0,0 +1,30 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test a local transaction being aborted by a slave one while it is running a SLEEP()
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++--let $wsrep_local_bf_aborts_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++INSERT INTO t1 VALUES (1);
++--send SELECT SLEEP(1000);
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++--reap
++
++--let $wsrep_local_bf_aborts_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
++
++# Check that wsrep_local_bf_aborts has been incremented by exactly 1
++--disable_query_log
++--eval SELECT $wsrep_local_bf_aborts_after - $wsrep_local_bf_aborts_before = 1 AS wsrep_local_aborts_increment;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_binlog_cache_size.test b/mysql-test/suite/galera/t/galera_binlog_cache_size.test
+new file mode 100644
+index 0000000..6ce9072
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_cache_size.test
+@@ -0,0 +1,35 @@
++#
++# Test that Galera, like the stock MySQL, returns an error on transactions
++# larger than max_binlog_cache_size
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 VARCHAR(767)) ENGINE=InnoDB;
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++--let $max_binlog_cache_size_orig = `SELECT @@max_binlog_cache_size`
++--let $binlog_cache_size_orig = `SELECT @@binlog_cache_size`
++
++SET GLOBAL binlog_cache_size=4096;
++SET GLOBAL max_binlog_cache_size=4096;
++
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++--connection node_1a
++SET AUTOCOMMIT=ON;
++START TRANSACTION;
++INSERT INTO t1 SELECT REPEAT('a', 767) FROM ten;
++--error ER_TRANS_CACHE_FULL
++INSERT INTO t1 SELECT REPEAT('a', 767) FROM ten;
++
++--disable_query_log
++--eval SET GLOBAL max_binlog_cache_size = $max_binlog_cache_size_orig
++--eval SET GLOBAL binlog_cache_size = $binlog_cache_size_orig
++--enable_query_log
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_binlog_checksum-master.opt b/mysql-test/suite/galera/t/galera_binlog_checksum-master.opt
+new file mode 100644
+index 0000000..c8e53f0
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_checksum-master.opt
+@@ -0,0 +1 @@
++--binlog-checksum=CRC32 --master-verify-checksum=1 --slave-sql-verify-checksum=1
+diff --git a/mysql-test/suite/galera/t/galera_binlog_checksum.test b/mysql-test/suite/galera/t/galera_binlog_checksum.test
+new file mode 100644
+index 0000000..4866930
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_checksum.test
+@@ -0,0 +1,22 @@
++#
++# Test that Galera works with binary log checksums.
++# The galera_binlog_checksum-master.opt file is used to enable checksums.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_binlog_event_max_size_max-master.opt b/mysql-test/suite/galera/t/galera_binlog_event_max_size_max-master.opt
+new file mode 100644
+index 0000000..a36d213
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_event_max_size_max-master.opt
+@@ -0,0 +1 @@
++--binlog-row-event-max-size=4294967295
+diff --git a/mysql-test/suite/galera/t/galera_binlog_event_max_size_max.test b/mysql-test/suite/galera/t/galera_binlog_event_max_size_max.test
+new file mode 100644
+index 0000000..600432c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_event_max_size_max.test
+@@ -0,0 +1,20 @@
++#
++# Test that replication works event with the maximum value of binlog-row-event-max-size - 4294967295 (on 32-bit platforms)
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 VARCHAR(1000));
++
++# Insert 10K records, 1K bytes each
++INSERT INTO t1 SELECT REPEAT('x', 1000) FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--connection node_2
++SELECT COUNT(*) = 10000 FROM t1;
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_binlog_event_max_size_min-master.opt b/mysql-test/suite/galera/t/galera_binlog_event_max_size_min-master.opt
+new file mode 100644
+index 0000000..2217475
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_event_max_size_min-master.opt
+@@ -0,0 +1 @@
++--binlog-row-event-max-size=256
+diff --git a/mysql-test/suite/galera/t/galera_binlog_event_max_size_min.test b/mysql-test/suite/galera/t/galera_binlog_event_max_size_min.test
+new file mode 100644
+index 0000000..00b5533
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_event_max_size_min.test
+@@ -0,0 +1,15 @@
++#
++# Test that replication works event with the minimum value of binlog-row-event-max-size - 256
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 VARCHAR(1000));
++INSERT INTO t1 VALUES (REPEAT('x', 1000));
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = REPEAT('x', 1000);
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_binlog_row_image.test b/mysql-test/suite/galera/t/galera_binlog_row_image.test
+new file mode 100644
+index 0000000..70262ec
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_row_image.test
+@@ -0,0 +1,100 @@
++#
++# Test the operation on the different values of the binlog_row_image option
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# binlog_row_image = minimal
++#
++
++--connection node_1
++SET SESSION binlog_row_image=minimal;
++
++# Create a table with a PK, with a unique key and with no key
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER NOT NULL UNIQUE) ENGINE=InnoDB;
++CREATE TABLE t3 (f1 VARCHAR(1)) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t3 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 1;
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 1;
++SELECT COUNT(*) = 1 FROM t3 WHERE f1 = 1;
++
++--connection node_1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++UPDATE t2 SET f1 = 2 WHERE f1 = 1;
++UPDATE t3 SET f1 = 2 WHERE f1 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 2;
++SELECT COUNT(*) = 1 FROM t3 WHERE f1 = 2;
++
++--connection node_1
++DELETE FROM t1;
++DELETE FROM t2;
++DELETE FROM t3;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++SELECT COUNT(*) = 0 FROM t3;
++
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
++
++#
++# binlog_row_image = noblob
++#
++
++# A table with only a blob, and a table with a PK and a blob
++
++--connection node_1
++SET SESSION binlog_row_image=noblob;
++
++CREATE TABLE t1 (f1 BLOB, f2 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 BLOB) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES ('abc', 1);
++INSERT INTO t2 VALUES ('abc');
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'abc';
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 'abc';
++
++--connection node_1
++UPDATE t1 SET f1 = 'xyz';
++UPDATE t2 SET f1 = 'xyz';
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'xyz';
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 'xyz';
++
++--connection node_1
++UPDATE t1 SET f2 = 2 WHERE f2 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 2;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'xyz';
++
++--connection node_1
++DELETE FROM t1;
++DELETE FROM t2;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++
++DROP TABLE t1;
++DROP TABLE t2;
++
++
++
++
+diff --git a/mysql-test/suite/galera/t/galera_binlog_rows_query_log_events.test b/mysql-test/suite/galera/t/galera_binlog_rows_query_log_events.test
+new file mode 100644
+index 0000000..95bc85c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_binlog_rows_query_log_events.test
+@@ -0,0 +1,28 @@
++#
++# Test that Galera continues to run even with binlog-rows-query-log-events=TRUE
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $binlog_rows_query_log_events_orig = `SELECT @@binlog_rows_query_log_events`
++
++SET GLOBAL binlog_rows_query_log_events=TRUE;
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++
++--connection node_1
++--eval SET GLOBAL binlog_rows_query_log_events = $binlog_rows_query_log_events_orig
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_create_function.test b/mysql-test/suite/galera/t/galera_create_function.test
+new file mode 100644
+index 0000000..fd4903a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_create_function.test
+@@ -0,0 +1,57 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test CREATE FUNCTION
++#
++
++--connection node_1
++CREATE USER 'user1';
++
++CREATE
++DEFINER = 'user1'
++FUNCTION f1 (param INTEGER)
++RETURNS VARCHAR(200)
++COMMENT 'f1_comment'
++LANGUAGE SQL
++NOT DETERMINISTIC
++MODIFIES SQL DATA
++SQL SECURITY DEFINER
++RETURN 'abc';
++GRANT EXECUTE ON FUNCTION f1 TO user1;
++
++CREATE
++DEFINER = CURRENT_USER
++FUNCTION f2 (param VARCHAR(100))
++RETURNS INTEGER
++DETERMINISTIC
++NO SQL
++SQL SECURITY INVOKER
++RETURN 123;
++
++--connection node_1
++SHOW CREATE FUNCTION f1;
++
++--connection node_2
++SHOW CREATE FUNCTION f1;
++
++--connection node_1
++SHOW CREATE FUNCTION f2;
++
++--connection node_2
++SHOW CREATE FUNCTION f2;
++
++SELECT f1(1) = 'abc';
++SELECT f2('abc') = 123;
++
++--connection node_1
++DROP FUNCTION f1;
++DROP FUNCTION f2;
++
++DROP USER 'user1';
++
++
++
++
++
++
+diff --git a/mysql-test/suite/galera/t/galera_create_procedure.test b/mysql-test/suite/galera/t/galera_create_procedure.test
+new file mode 100644
+index 0000000..30bc85f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_create_procedure.test
+@@ -0,0 +1,52 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test CREATE PROCEDURE
++#
++
++--connection node_1
++CREATE USER 'user1';
++CREATE TABLE t1 (f1 INTEGER);
++
++CREATE
++DEFINER = 'user1'
++PROCEDURE p1 (IN param1 INTEGER, OUT param2 INTEGER, INOUT param3 INTEGER)
++COMMENT 'p1_comment'
++LANGUAGE SQL
++NOT DETERMINISTIC
++MODIFIES SQL DATA
++SQL SECURITY DEFINER
++INSERT INTO t1 VALUES (1);
++GRANT EXECUTE ON PROCEDURE p1 TO user1;
++
++CREATE
++DEFINER = CURRENT_USER
++PROCEDURE p2 (param VARCHAR(100))
++DETERMINISTIC
++NO SQL
++SQL SECURITY INVOKER BEGIN END ;
++
++--connection node_1
++SHOW CREATE PROCEDURE p1;
++
++--connection node_2
++# Perform causal wait
++SELECT 1 FROM DUAL;
++SHOW CREATE PROCEDURE p1;
++
++--connection node_1
++SHOW CREATE PROCEDURE p2;
++
++--connection node_2
++SHOW CREATE PROCEDURE p2;
++
++CALL p1(@a, @b, @c);
++CALL p2('abc');
++
++--connection node_1
++DROP PROCEDURE p1;
++DROP PROCEDURE p2;
++
++DROP USER 'user1';
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_create_table_like.test b/mysql-test/suite/galera/t/galera_create_table_like.test
+new file mode 100644
+index 0000000..0e0e8b0
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_create_table_like.test
+@@ -0,0 +1,50 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test the various forms of CREATE TABLE LIKE ... , since Galera has special handling
++# for them, especially when one of the tables is a temporary one.
++#
++
++CREATE SCHEMA schema1;
++CREATE SCHEMA schema2;
++
++USE schema1;
++CREATE TABLE real_table (f1 INTEGER) ENGINE=InnoDB;
++CREATE TEMPORARY TABLE temp_table (f1 INTEGER) ENGINE=InnoDB;
++CREATE TABLE myisam_table (f1 INTEGER) ENGINE=MyISAM;
++
++USE schema2;
++CREATE TABLE real_table1 LIKE schema1.real_table;
++CREATE TABLE real_table2 LIKE schema1.temp_table;
++CREATE TABLE real_table3 LIKE schema1.myisam_table;
++
++CREATE TEMPORARY TABLE temp_table1 LIKE schema1.real_table;
++CREATE TEMPORARY TABLE temp_table2 LIKE schema1.temp_table;
++CREATE TEMPORARY TABLE temp_table3 LIKE schema1.myisam_table;
++
++--connection node_2
++# Only the non-temporary tables are replicated, regardless of the type of table they are based on
++
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table' AND TABLE_SCHEMA = 'schema1';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'myisam_table' AND TABLE_SCHEMA = 'schema1';
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table' AND TABLE_SCHEMA = 'schema1';
++
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table1' AND TABLE_SCHEMA = 'schema2';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table2' AND TABLE_SCHEMA = 'schema2';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'real_table3' AND TABLE_SCHEMA = 'schema2';
++
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table1' AND TABLE_SCHEMA = 'schema2';
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table2' AND TABLE_SCHEMA = 'schema2';
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_table3' AND TABLE_SCHEMA = 'schema2';
++
++--connection node_1
++DROP TABLE schema1.real_table;
++DROP TABLE schema1.myisam_table;
++
++DROP TABLE schema2.real_table1;
++DROP TABLE schema2.real_table2;
++DROP TABLE schema2.real_table3;
++
++DROP SCHEMA schema1;
++DROP SCHEMA schema2;
+diff --git a/mysql-test/suite/galera/t/galera_create_trigger.test b/mysql-test/suite/galera/t/galera_create_trigger.test
+new file mode 100644
+index 0000000..74dc616
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_create_trigger.test
+@@ -0,0 +1,48 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test CREATE TRIGGER, especially with different DEFINER
++#
++
++CREATE TABLE definer_root (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE TABLE definer_user (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE TABLE definer_current_user (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++CREATE TABLE definer_default (f1 INTEGER, trigger_user VARCHAR(100)) ENGINE=InnoDB;
++
++CREATE USER 'user1';
++CREATE DEFINER=root@localhost TRIGGER definer_root BEFORE INSERT ON definer_root FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++CREATE DEFINER=user1 TRIGGER definer_user BEFORE INSERT ON definer_user FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++CREATE DEFINER=current_user TRIGGER definer_current_user BEFORE INSERT ON definer_current_user FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++CREATE TRIGGER definer_default BEFORE INSERT ON definer_default FOR EACH ROW SET NEW.trigger_user = CURRENT_USER();
++
++--connection node_2
++INSERT INTO definer_root (f1) VALUES (1);
++SELECT DEFINER = 'root@localhost' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_root';
++SELECT trigger_user = 'root@localhost' FROM definer_root;
++
++INSERT INTO definer_user (f1) VALUES (1);
++SELECT DEFINER = 'user1@%' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_user';
++SELECT trigger_user = 'user1@%' FROM definer_user;
++
++INSERT INTO definer_current_user (f1) VALUES (1);
++SELECT DEFINER = 'root@localhost' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_current_user';
++SELECT trigger_user = 'root@localhost' FROM definer_current_user;
++
++INSERT INTO definer_default (f1) VALUES (1);
++SELECT DEFINER = 'root@localhost' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'definer_default';
++SELECT trigger_user = 'root@localhost' FROM definer_default;
++
++--connection node_1
++DROP TABLE definer_current_user;
++DROP TABLE definer_user;
++DROP TABLE definer_root;
++DROP TABLE definer_default;
++
++DROP USER 'user1';
++
++
++
++
++
++
+diff --git a/mysql-test/suite/galera/t/galera_defaults.test b/mysql-test/suite/galera/t/galera_defaults.test
+new file mode 100644
+index 0000000..25d85b7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_defaults.test
+@@ -0,0 +1,63 @@
++#
++# The purpose of this test is to preserve the current state of the following:
++# * SHOW VARIABLES LIKE 'wsrep%'
++# * wsrep_provider_options
++# * The names of the Galera status variables
++#
++# This way, if there is any change, inadvertent or not, the test will fail and the
++# developer and QA will be alerted.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++# Global Variables
++
++SELECT COUNT(*) = 40 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%';
++
++SELECT VARIABLE_NAME, VARIABLE_VALUE
++FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
++WHERE VARIABLE_NAME LIKE 'wsrep_%'
++AND VARIABLE_NAME NOT IN (
++ 'WSREP_PROVIDER_OPTIONS',
++ 'WSREP_SST_RECEIVE_ADDRESS',
++ 'WSREP_NODE_ADDRESS',
++ 'WSREP_NODE_NAME',
++ 'WSREP_PROVIDER',
++ 'WSREP_DATA_HOME_DIR',
++ 'WSREP_NODE_INCOMING_ADDRESS',
++ 'WSREP_START_POSITION'
++)
++ORDER BY VARIABLE_NAME;
++
++# wsrep_provider_options
++#
++# We replace the ones that vary from run to run with placeholders
++
++--let _WSREP_PROVIDER_OPTIONS = `SELECT @@wsrep_provider_options`
++--perl
++ use strict;
++ my $wsrep_provider_options = $ENV{'_WSREP_PROVIDER_OPTIONS'};
++ $wsrep_provider_options =~ s/base_dir = .*?;/<BASE_DIR>;/sgio;
++ $wsrep_provider_options =~ s/base_host = .*?;/<BASE_HOST>;/sgio;
++ $wsrep_provider_options =~ s/base_port = .*?;/<BASE_PORT>;/sgio;
++ $wsrep_provider_options =~ s/gcache\.dir = .*?;/<GCACHE_DIR>;/sgio;
++ $wsrep_provider_options =~ s/gcache\.name = .*?;/<GCACHE_NAME>;/sgio;
++ $wsrep_provider_options =~ s/gmcast\.listen_addr = .*?;/<GMCAST_LISTEN_ADDR>;/sgio;
++ $wsrep_provider_options =~ s/ist\.recv_addr = .*?;/<IST_RECV_ADDR>;/sgio;
++ $wsrep_provider_options =~ s/evs\.evict = .*?;/<EVS_EVICT>;/sgio;
++ $wsrep_provider_options =~ s/signal = .*?;\s*//sgio;
++ $wsrep_provider_options =~ s/dbug = .*?;\s*//sgio;
++ print $wsrep_provider_options."\n";
++EOF
++
++# Global Status
++
++SELECT COUNT(*) FROM INFORMATION_SCHEMA.GLOBAL_STATUS
++WHERE VARIABLE_NAME LIKE 'wsrep_%'
++AND VARIABLE_NAME != 'wsrep_debug_sync_waiters';
++
++SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.GLOBAL_STATUS
++WHERE VARIABLE_NAME LIKE 'wsrep_%'
++AND VARIABLE_NAME != 'wsrep_debug_sync_waiters'
++ORDER BY VARIABLE_NAME;
+diff --git a/mysql-test/suite/galera/t/galera_delete_limit.test b/mysql-test/suite/galera/t/galera_delete_limit.test
+new file mode 100644
+index 0000000..4cbadbd
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_delete_limit.test
+@@ -0,0 +1,52 @@
++#
++# DELETE LIMIT should not cause any issues with row-based Galera replication
++# regardless of the order in which the rows were deleted
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# With a PK
++#
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER) Engine=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 SELECT f1 FROM ten ORDER BY RAND();
++
++--connection node_2
++DELETE FROM t1 ORDER BY RAND() LIMIT 5;
++--let $sum_remaining = `SELECT SUM(f1) FROM t1`
++--let $max_remaining = `SELECT MAX(f1) FROM t1`
++
++--connection node_1
++--disable_query_log
++--eval SELECT (SELECT SUM(f1) FROM t1) = $sum_remaining AS sum_matches;
++--eval SELECT f1 = $max_remaining AS max_matches FROM t1 WHERE f1 = $max_remaining;
++--enable_query_log
++
++DROP TABLE t1;
++
++#
++# Without a PK
++#
++
++CREATE TABLE t2 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t2 SELECT f1 FROM ten ORDER BY RAND();
++
++--connection node_2
++DELETE FROM t2 ORDER BY RAND() LIMIT 5;
++--let $sum_remaining = `SELECT SUM(f1) FROM t2`
++--let $max_remaining = `SELECT MAX(f1) FROM t2`
++
++--connection node_1
++--disable_query_log
++--eval SELECT (SELECT SUM(f1) FROM t2) = $sum_remaining AS sum_matches;
++--eval SELECT f1 = $max_remaining AS max_matches FROM t2 WHERE f1 = $max_remaining;
++--enable_query_log
++
++DROP TABLE t2;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_enum.test b/mysql-test/suite/galera/t/galera_enum.test
+new file mode 100644
+index 0000000..ff53324
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_enum.test
+@@ -0,0 +1,62 @@
++#
++# Test the ENUM column type, as it is frequently an unwanted child
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# ENUM as key
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 ENUM('', 'one', 'two'), KEY (f1)) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES ('');
++INSERT INTO t1 VALUES ('one'), ('two');
++INSERT INTO t1 VALUES (0), (1), (2);
++
++--connection node_2
++SELECT COUNT(*) = 6 FROM t1;
++SELECT COUNT(*) = 2 FROM t1 where f1 = '';
++SELECT COUNT(*) = 2 FROM t1 where f1 = 'one';
++
++DROP TABLE t1;
++
++#
++# ENUM as PK
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 ENUM('', 'one', 'two', 'three', 'four') PRIMARY KEY) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (''), ('one'), ('two');
++
++--connection node_2
++SELECT COUNT(*) = 3 FROM t1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = '';
++
++# Conflict
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'three' where f1 = '';
++
++--connection node_2
++SET AUTOCOMMIt=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'four' where f1 = '';
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++--connection node_1
++
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 'three';
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_events.test b/mysql-test/suite/galera/t/galera_events.test
+new file mode 100644
+index 0000000..ae9940f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_events.test
+@@ -0,0 +1,53 @@
++#
++# Test that the replication of MySQL events conforms to the behavior of stock MySQL replication as described here
++# http://dev.mysql.com/doc/refman/5.6/en/replication-features-invoked.html
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $event_scheduler_orig = `SELECT @@event_scheduler;`
++
++#
++# Events arrive on slave as SLAVESIDE_DISABLED
++#
++
++--connection node_1
++CREATE EVENT event1 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO SELECT 1;
++
++--connection node_2
++SELECT DEFINER= 'root@localhost', ORIGINATOR = 1, STATUS = 'SLAVESIDE_DISABLED', EVENT_TYPE = 'ONE TIME', ON_COMPLETION = 'NOT PRESERVE' FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event1';
++
++--connection node_1
++ALTER EVENT event1 DISABLE;
++
++--connection node_2
++# The definition on node 2 should still say SLAVESIDE_DISABLED
++SELECT DEFINER= 'root@localhost', ORIGINATOR = 1, STATUS = 'SLAVESIDE_DISABLED', EVENT_TYPE = 'ONE TIME', ON_COMPLETION = 'NOT PRESERVE' FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event1';
++
++#
++# Expired event should be dropped from the slave
++#
++
++--connection node_2
++SET GLOBAL event_scheduler = ON;
++CREATE EVENT event2 ON SCHEDULE AT CURRENT_TIMESTAMP ON COMPLETION NOT PRESERVE DO SELECT 1;
++--sleep 1
++
++--connection node_1
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event2';
++
++#
++# DROP EVENT causes event to be dropped everywhere
++#
++
++--connection node_1
++DROP EVENT event1;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME = 'event1';
++
++# Cleanup
++
++--connection node_2
++--eval SET GLOBAL event_scheduler = $event_scheduler_orig;
+diff --git a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
+new file mode 100644
+index 0000000..9b79b4c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
+@@ -0,0 +1,41 @@
++#
++# Test Foreign Key Cascading DELETEs
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE grandparent (
++ id INT NOT NULL PRIMARY KEY
++) ENGINE=InnoDB;
++
++CREATE TABLE parent (
++ id INT NOT NULL PRIMARY KEY,
++ grandparent_id INT,
++ FOREIGN KEY (grandparent_id)
++ REFERENCES grandparent(id)
++ ON DELETE CASCADE
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT NOT NULL PRIMARY KEY,
++ parent_id INT,
++ FOREIGN KEY (parent_id)
++ REFERENCES parent(id)
++ ON DELETE CASCADE
++) ENGINE=InnoDB;
++
++INSERT INTO grandparent VALUES (1),(2);
++INSERT INTO parent VALUES (1,1), (2,2);
++INSERT INTO child VALUES (1,1), (2,2);
++
++--connection node_2
++DELETE FROM grandparent WHERE id = 1;
++
++--connection node_1
++SELECT COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
++SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1;
++
++DROP TABLE child;
++DROP TABLE parent;
++DROP TABLE grandparent;
+diff --git a/mysql-test/suite/galera/t/galera_fk_cascade_update.test b/mysql-test/suite/galera/t/galera_fk_cascade_update.test
+new file mode 100644
+index 0000000..e736803
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_cascade_update.test
+@@ -0,0 +1,41 @@
++#
++# Test Foreign Key Cascading UPDATEs
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE grandparent (
++ id INT NOT NULL PRIMARY KEY
++) ENGINE=InnoDB;
++
++CREATE TABLE parent (
++ id INT NOT NULL PRIMARY KEY,
++ grandparent_id INT,
++ FOREIGN KEY (grandparent_id)
++ REFERENCES grandparent(id)
++ ON UPDATE CASCADE
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT NOT NULL PRIMARY KEY,
++ grandparent_id INT,
++ FOREIGN KEY (grandparent_id)
++ REFERENCES parent(grandparent_id)
++ ON UPDATE CASCADE
++) ENGINE=InnoDB;
++
++INSERT INTO grandparent VALUES (1),(2);
++INSERT INTO parent VALUES (1,1), (2,2);
++INSERT INTO child VALUES (1,1), (2,2);
++
++--connection node_2
++UPDATE grandparent SET id = 3 WHERE id = 1;
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM parent WHERE grandparent_id = 3;
++SELECT COUNT(*) = 1 FROM child WHERE grandparent_id = 3;
++
++DROP TABLE child;
++DROP TABLE parent;
++DROP TABLE grandparent;
+diff --git a/mysql-test/suite/galera/t/galera_fk_conflict.test b/mysql-test/suite/galera/t/galera_fk_conflict.test
+new file mode 100644
+index 0000000..cb6f95e
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_conflict.test
+@@ -0,0 +1,41 @@
++#
++# Test two transactions on separate nodes which conflict on a FK
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE parent (
++ id INT PRIMARY KEY,
++ KEY (id)
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT PRIMARY KEY,
++ parent_id INT,
++ FOREIGN KEY (parent_id)
++ REFERENCES parent(id)
++) ENGINE=InnoDB;
++
++INSERT INTO parent VALUES (1), (2);
++INSERT INTO child VALUES (1,1);
++
++--connection node_1
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++DELETE FROM parent WHERE id = 2;
++
++--connection node_2
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++INSERT INTO child VALUES (2, 2);
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/t/galera_fk_mismatch.test b/mysql-test/suite/galera/t/galera_fk_mismatch.test
+new file mode 100644
+index 0000000..bded413
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_mismatch.test
+@@ -0,0 +1,38 @@
++#
++# Test the operation where the definition of the FK is different from the one of the underlying key
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE parent (
++ id1 INT,
++ id2 INT,
++ PRIMARY KEY (id1, id2) /* Multipart PK */
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT PRIMARY KEY,
++ parent_id1 INT,
++ FOREIGN KEY (parent_id1)
++ REFERENCES parent(id1) /* FK is subset of PK above */
++ ON UPDATE CASCADE
++ ON DELETE CASCADE
++) ENGINE=InnoDB;
++
++INSERT INTO parent VALUES (1, 2);
++INSERT INTO child VALUES (1, 1);
++
++--connection node_2
++UPDATE parent SET id1 = 3 WHERE id1 = 1;
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM child WHERE parent_id1 = 3;
++
++DELETE FROM parent WHERE id1 = 3;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM child WHERE parent_id1 = 3;
++
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/t/galera_fk_multicolumn.test b/mysql-test/suite/galera/t/galera_fk_multicolumn.test
+new file mode 100644
+index 0000000..ad42f65
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_multicolumn.test
+@@ -0,0 +1,42 @@
++#
++# Test UPDATE on multiple columns with multiple FKs
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t0 (
++ f1 INT PRIMARY KEY,
++ f2 INT UNIQUE
++);
++
++CREATE TABLE t1 (
++ f1 INT PRIMARY KEY,
++ FOREIGN KEY (f1)
++ REFERENCES t0(f1)
++ ON UPDATE CASCADE
++);
++
++CREATE TABLE t2 (
++ f2 INT PRIMARY KEY,
++ FOREIGN KEY (f2)
++ REFERENCES t0(f2)
++ ON UPDATE CASCADE
++);
++
++INSERT INTO t0 VALUES (0, 0);
++INSERT INTO t1 VALUES (0);
++INSERT INTO t2 VALUES (0);
++
++--connection node_2
++UPDATE t0 SET f1 = 1, f2 = 2;
++
++--connection node_1
++SELECT f1 = 1 FROM t1 WHERE f1 = 1;
++SELECT f2 = 2 FROM t2 WHERE f2 = 2;
++SELECT f1 = 1 FROM t1;
++SELECT f2 = 2 FROM t2;
++
++DROP TABLE t2;
++DROP TABLE t1;
++DROP TABLE t0;
+diff --git a/mysql-test/suite/galera/t/galera_fk_multitable.test b/mysql-test/suite/galera/t/galera_fk_multitable.test
+new file mode 100644
+index 0000000..6adfb81
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_multitable.test
+@@ -0,0 +1,32 @@
++#
++# Test multi-table DELETE in the presence of FKs
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t0 (
++ f0 INT PRIMARY KEY
++);
++
++CREATE TABLE t1 (
++ f1 INT PRIMARY KEY,
++ f0 INTEGER,
++ FOREIGN KEY (f0)
++ REFERENCES t0(f0)
++ ON DELETE CASCADE
++);
++
++INSERT INTO t0 VALUES (0), (1);
++INSERT INTO t1 VALUES (0, 0);
++INSERT INTO t1 VALUES (1, 0);
++
++--connection node_2
++DELETE t0.*, t1.* FROM t0, t1 WHERE t0.f0 = 0 AND t1.f1 = 0;
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM t0;
++SELECT COUNT(*) = 0 FROM t1;
++
++DROP TABLE t1;
++DROP TABLE t0;
+diff --git a/mysql-test/suite/galera/t/galera_fk_no_pk.test b/mysql-test/suite/galera/t/galera_fk_no_pk.test
+new file mode 100644
+index 0000000..d1f9c26
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_no_pk.test
+@@ -0,0 +1,37 @@
++#
++# Test foreign keys if no PK is present
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE parent (
++ id INT,
++ KEY (id)
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT,
++ parent_id INT,
++ FOREIGN KEY (parent_id)
++ REFERENCES parent(id)
++ ON UPDATE CASCADE
++ ON DELETE CASCADE
++) ENGINE=InnoDB;
++
++INSERT INTO parent VALUES (1), (1), (2), (2);
++INSERT INTO child VALUES (1,1), (2,2), (1,1), (2,2);
++
++--connection node_2
++DELETE FROM parent WHERE id = 1;
++SELECT COUNT(*) = 0 FROM child WHERE id = 1;
++
++--connection node_1
++UPDATE parent SET id = 3 WHERE id = 2;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1;
++SELECT parent_id = 3 FROM child WHERE id = 2;
++
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/t/galera_fk_selfreferential.test b/mysql-test/suite/galera/t/galera_fk_selfreferential.test
+new file mode 100644
+index 0000000..e2c1900
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_selfreferential.test
+@@ -0,0 +1,24 @@
++#
++# Test self-referential foreign keys
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (
++ f1 INT NOT NULL PRIMARY KEY,
++ f2 INT,
++ FOREIGN KEY (f2)
++ REFERENCES t1(f1)
++ ON DELETE CASCADE
++) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (1, 1), (2, 1);
++
++--connection node_2
++DELETE FROM t1 WHERE f1 = 1;
++
++--connection node_1
++SELECT COUNT(*) = 0 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_fk_setnull.test b/mysql-test/suite/galera/t/galera_fk_setnull.test
+new file mode 100644
+index 0000000..46ba82d
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fk_setnull.test
+@@ -0,0 +1,36 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE parent (
++ id INT NOT NULL,
++ PRIMARY KEY (id)
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT,
++ parent_id INT,
++ FOREIGN KEY (parent_id)
++ REFERENCES parent(id)
++ ON UPDATE SET NULL
++ ON DELETE SET NULL
++) ENGINE=InnoDB;
++
++INSERT INTO parent VALUES (1),(2);
++INSERT INTO child VALUES (1,1),(2,2);
++
++--connection node_2
++DELETE FROM parent WHERE id = 1;
++SELECT parent_id IS NULL FROM child WHERE id = 1;
++
++--connection node_1
++SELECT parent_id IS NULL FROM child WHERE id = 1;
++
++UPDATE parent SET id = 3 WHERE id = 2;
++SELECT parent_id IS NULL FROM child WHERE id = 2;
++
++--connection node_2
++SELECT parent_id IS NULL FROM child WHERE id = 2;
++
++--connection node_1
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/t/galera_ftwrl.test b/mysql-test/suite/galera/t/galera_ftwrl.test
+new file mode 100644
+index 0000000..db9bd13
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ftwrl.test
+@@ -0,0 +1,39 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# At this time, issing a FLUSH TABLES WITH READ LOCK causes SELECT and SHOW to hang if
++# casuality can not be ensured because another node issued a statement in the meantime
++# which could not be applied because FTWRL blocks the applier as well
++#
++# See LP bug 1271177
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options;`
++SET GLOBAL wsrep_provider_options = "repl.causal_read_timeout=PT1S";
++FLUSH TABLES WITH READ LOCK;
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--error ER_LOCK_WAIT_TIMEOUT
++SHOW TABLES;
++
++--error ER_LOCK_WAIT_TIMEOUT
++SELECT * FROM t1;
++
++UNLOCK TABLES;
++
++SHOW TABLES;
++SELECT COUNT(*) = 1 FROM t1;
++
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = "$wsrep_provider_options_orig";
++--enable_query_log
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_fulltext.test b/mysql-test/suite/galera/t/galera_fulltext.test
+new file mode 100644
+index 0000000..aa93a33
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_fulltext.test
+@@ -0,0 +1,62 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# InnoDB FULLTEXT indexes
++#
++
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++#
++# Fulltext index creation causes the creation of multiple system tables
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INT PRIMARY KEY AUTO_INCREMENT, f2 VARCHAR(100), FULLTEXT (f2)) ENGINE=InnoDB;
++
++--connection node_2
++SELECT COUNT(*) = 13 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE name LIKE 'test/%';
++
++#
++# Fulltext insertion causes a flurry of updates on those system tables
++#
++
++--connection node_1
++# Insert 10K rows
++INSERT INTO t1 (f2) SELECT 'foobarbaz' FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--connection node_2
++SELECT COUNT(f2) = 10000 FROM t1 WHERE MATCH(f2) AGAINST ('foobarbaz');
++
++UPDATE t1 SET f2 = 'abcdefjhk';
++
++--connection node_1
++SELECT COUNT(f2) = 10000 FROM t1 WHERE MATCH(f2) AGAINST ('abcdefjhk');
++
++--connection node_2
++
++DROP TABLE t1;
++
++#
++# Same on a table with no PK
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 VARCHAR(100), FULLTEXT (f1)) ENGINE=InnoDB;
++
++--connection node_2
++# We insert only 1K rows here, because updates without a PK are very slow
++INSERT INTO t1 (f1) SELECT 'foobarbaz' FROM ten AS a1, ten AS a2, ten AS a3;
++
++--connection node_1
++SELECT COUNT(f1) = 1000 FROM t1 WHERE MATCH(f1) AGAINST ('foobarbaz');
++
++UPDATE t1 SET f1 = 'abcdefjhk';
++
++--connection node_2
++SELECT COUNT(f1) = 1000 FROM t1 WHERE MATCH(f1) AGAINST ('abcdefjhk');
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_gcs_fc_limit.test b/mysql-test/suite/galera/t/galera_gcs_fc_limit.test
+new file mode 100644
+index 0000000..e15da0e
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_gcs_fc_limit.test
+@@ -0,0 +1,52 @@
++#
++# Test that under gcs.fc_limit=1 on the slave, transactions on the master can not commit.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'gcs.fc_limit=1';
++
++# Block the slave applier thread
++FLUSH TABLES WITH READ LOCK;
++
++--connection node_1
++
++INSERT INTO t1 VALUES (2);
++INSERT INTO t1 VALUES (3);
++INSERT INTO t1 VALUES (4);
++
++# This query will hang because flow control will kick in
++--send
++INSERT INTO t1 VALUES (5);
++--sleep 1
++
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++--connection node_1a
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'query end' AND INFO = 'INSERT INTO t1 VALUES (5)';
++
++--connection node_2
++# Unblock the slave applier thread
++UNLOCK TABLES;
++
++--connection node_1
++--reap
++
++INSERT INTO t1 VALUES (6);
++
++--connection node_2
++# Replication catches up and continues normally
++SELECT COUNT(*) = 6 FROM t1;
++
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_gcs_max_packet_size.cnf b/mysql-test/suite/galera/t/galera_gcs_max_packet_size.cnf
+new file mode 100644
+index 0000000..aae3fee
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_gcs_max_packet_size.cnf
+@@ -0,0 +1,5 @@
++!include ../galera_2nodes.cnf
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcs.max_packet_size=64'
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;gcs.max_packet_size=64'
+diff --git a/mysql-test/suite/galera/t/galera_gcs_max_packet_size.test b/mysql-test/suite/galera/t/galera_gcs_max_packet_size.test
+new file mode 100644
+index 0000000..cafd8ac
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_gcs_max_packet_size.test
+@@ -0,0 +1,25 @@
++#
++# Test fragmentation by setting gcs.max_packet_size to a low value
++# The actual setting is performed in galera_gcs_max_packet_size.cnf
++# as gcs.max_packet_size is not a dynamic variable
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY AUTO_INCREMENT, f2 INTEGER) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 VARCHAR(512) UNIQUE) ENGINE=InnoDB;
++
++INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++INSERT INTO t2 VALUES (REPEAT('x', 512));
++
++--connection node_2
++SELECT COUNT(*) = 10000 FROM t1;
++SELECT LENGTH(f1) = 512 FROM t2 WHERE f1 = REPEAT('x', 512);
++
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_gtid-master.opt b/mysql-test/suite/galera/t/galera_gtid-master.opt
+new file mode 100644
+index 0000000..48e46d7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_gtid-master.opt
+@@ -0,0 +1 @@
++--gtid-mode=ON --log-bin --log-slave-updates --enforce-gtid-consistency
+diff --git a/mysql-test/suite/galera/t/galera_gtid.test b/mysql-test/suite/galera/t/galera_gtid.test
+new file mode 100644
+index 0000000..97e7d1f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_gtid.test
+@@ -0,0 +1,27 @@
++#
++# Test basic Galera operation under --gtid-mode=ON
++#
++
++--source include/have_log_bin.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY);
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++UPDATE t1 SET f1 = 2;
++
++--let $gtid_executed_node2 = `SELECT @@global.gtid_executed;`
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++
++--disable_query_log
++--eval SELECT '$gtid_executed_node2' = @@global.gtid_executed AS gtid_executed_equal;
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_insert_ignore.test b/mysql-test/suite/galera/t/galera_insert_ignore.test
+new file mode 100644
+index 0000000..4b4b0a6
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_insert_ignore.test
+@@ -0,0 +1,60 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_sync_wait_orig = (SELECT @@wsrep_sync_wait)
++SET GLOBAL wsrep_sync_wait = 7;
++
++--connection node_2
++SET GLOBAL wsrep_sync_wait = 7;
++
++
++#
++# INSERT IGNORE with PRIMARY KEY
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++INSERT IGNORE INTO t1 VALUES (1), (2);
++SELECT * FROM t1;
++
++--connection node_2
++SELECT * FROM t1;
++
++#
++# INSERT IGNORE ... SELECT
++#
++
++--connection node_2
++CREATE TABLE t2 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (0), (2), (3);
++INSERT IGNORE INTO t1 SELECT f1 FROM t2;
++
++SELECT * FROM t1;
++--connection node_1
++SELECT * FROM t1;
++
++#
++# INSERT IGNORE with UNIQUE + NULLs
++#
++
++--connection node_2
++CREATE TABLE t3 (f1 INTEGER UNIQUE) Engine=InnoDB;
++INSERT INTO t3 VALUES (NULL);
++
++--connection node_1
++INSERT IGNORE INTO t3 VALUES (1), (NULL), (2);
++SELECT * FROM t3;
++
++--connection node_2
++SELECT * FROM t3;
++
++--eval SET GLOBAL wsrep_sync_wait = $wsrep_sync_wait_orig
++
++--connection node_1
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
++--eval SET GLOBAL wsrep_sync_wait = $wsrep_sync_wait_orig
++
+diff --git a/mysql-test/suite/galera/t/galera_insert_multi.test b/mysql-test/suite/galera/t/galera_insert_multi.test
+new file mode 100644
+index 0000000..d62283a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_insert_multi.test
+@@ -0,0 +1,122 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Multi-row INSERT with a PK
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1),(2);
++
++--connection node_2
++INSERT INTO t1 VALUES (3),(4);
++
++--connection node_1
++SELECT COUNT(*) = 4 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 4 FROM t1;
++
++DROP TABLE t1;
++
++#
++# Multi-row INSERT without a PK
++#
++
++--connection node_2
++CREATE TABLE t1 (f1 INTEGER, KEY (f1)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1),(1);
++
++--connection node_1
++INSERT INTO t1 VALUES (2),(2);
++
++--connection node_2
++SELECT COUNT(*) = 4 FROM t1;
++
++--connection node_1
++SELECT COUNT(*) = 4 FROM t1;
++
++DROP TABLE t1;
++
++#
++# Error in the middle of a multi-row INSERT
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--error ER_DUP_ENTRY
++INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (1);
++
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++
++DROP TABLE t1;
++
++#
++# Deadlock
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1), (2);
++
++--connection node_2
++SET AUTOCOMMIT = OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (2), (1);
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++# Workaround for mysql-wsrep#39 Transaction receives deadlock error twice in row
++--error 0,ER_LOCK_DEADLOCK
++ROLLBACK;
++
++--error ER_DUP_ENTRY
++INSERT INTO t1 VALUES (1), (2);
++
++DROP TABLE t1;
++
++#
++# Rollback
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++START TRANSACTION;
++INSERT INTO t1 VALUES (1), (2);
++
++--connection node_2
++START TRANSACTION;
++INSERT INTO t1 VALUES (2), (1);
++
++--connection node_1
++ROLLBACK;
++
++--connection node_2
++COMMIT;
++SELECT COUNT(*) = 2 FROM t1;
++
++--connection node_1
++SELECT COUNT(*) = 2 FROM t1;
++
++DROP TABLE t1;
++
++
++
++
++
++
++
+diff --git a/mysql-test/suite/galera/t/galera_ist_innodb_flush_logs.cnf b/mysql-test/suite/galera/t/galera_ist_innodb_flush_logs.cnf
+new file mode 100644
+index 0000000..85245ff
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_innodb_flush_logs.cnf
+@@ -0,0 +1,12 @@
++!include ../galera_2nodes.cnf
++
++[mysqld]
++wsrep_sst_method=xtrabackup-v2
++innodb_flush_log_at_trx_commit=0
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_ist_innodb_flush_logs.test b/mysql-test/suite/galera/t/galera_ist_innodb_flush_logs.test
+new file mode 100644
+index 0000000..0783870
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_innodb_flush_logs.test
+@@ -0,0 +1,12 @@
++#
++# This test performs server kill and IST while innodb_flush_logs_on_trx_commit = 0
++# This confirms that IST can properly catch up even in the face of relaxed single-node durability
++#
++#
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
+diff --git a/mysql-test/suite/galera/t/galera_ist_mysqldump.cnf b/mysql-test/suite/galera/t/galera_ist_mysqldump.cnf
+new file mode 100644
+index 0000000..db6b7d5
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_mysqldump.cnf
+@@ -0,0 +1,11 @@
++!include ../galera_2nodes.cnf
++
++# We do not set mysqldump-related SST options here because doing so on startup
++# causes the first MTR connection to be forefully dropped by Galera, which in turn confuses MTR
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_ist_mysqldump.test b/mysql-test/suite/galera/t/galera_ist_mysqldump.test
+new file mode 100644
+index 0000000..a9ff8c4
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_mysqldump.test
+@@ -0,0 +1,17 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_sst_set_mysqldump.inc
++
++# mysql-wsrep#33 - nnoDB: Failing assertion: xid_seqno > trx_sys_cur_xid_seqno in trx_sys_update_wsrep_checkpoint with mysqldump IST
++# --source suite/galera/include/galera_st_disconnect_slave.inc
++
++# We set the required mysqldump SST options here so that they are used every time the server is restarted during the test
++--let $start_mysqld_params = --wsrep_sst_auth=sst:sst --wsrep_sst_method=mysqldump --wsrep-sst-receive-address=127.0.0.1:$NODE_MYPORT_2 --skip-grant-tables
++
++--source suite/galera/include/galera_st_shutdown_slave.inc
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
++
++--source suite/galera/include/galera_sst_restore.inc
+diff --git a/mysql-test/suite/galera/t/galera_ist_restart_joiner.cnf b/mysql-test/suite/galera/t/galera_ist_restart_joiner.cnf
+new file mode 100644
+index 0000000..10958aa
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_restart_joiner.cnf
+@@ -0,0 +1,4 @@
++!include ../galera_2nodes.cnf
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true'
+diff --git a/mysql-test/suite/galera/t/galera_ist_restart_joiner.test b/mysql-test/suite/galera/t/galera_ist_restart_joiner.test
+new file mode 100644
+index 0000000..69446f0
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_restart_joiner.test
+@@ -0,0 +1,106 @@
++#
++# Test that a joiner performing IST can be killed and restarted with no adverse consequences.
++# This is achieved by using the recv_IST_after_apply_trx Galera dbug sync point to block IST after
++# one transaction has been applied. When IST blocks, we kill and restart the joiner
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/have_debug_sync.inc
++--source suite/galera/include/galera_have_debug_sync.inc
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
++INSERT INTO t1 VALUES (1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a'),(6, 'a');
++
++# Disconnect node #2
++--connection node_2
++--source suite/galera/include/galera_unload_provider.inc
++
++--connection node_1
++UPDATE t1 SET f2 = 'b' WHERE f1 > 1;
++
++# Wait until node #1 has left
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++UPDATE t1 SET f2 = 'c' WHERE f1 > 2;
++
++--connection node_2
++# Make sure IST will block ...
++SET GLOBAL wsrep_provider_options = 'dbug=d,recv_IST_after_apply_trx';
++SET SESSION wsrep_sync_wait = 0;
++
++
++# Write file to make mysql-test-run.pl expect the crash, but don't start it
++--let $_server_id= `SELECT @@server_id`
++--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect
++--exec echo "wait" > $_expect_file_name
++
++--let KILL_NODE_PIDFILE = `SELECT @@pid_file`
++
++# ... and restart provider to force IST
++--echo Loading wsrep_provider ...
++--disable_query_log
++--eval SET GLOBAL wsrep_provider = '$wsrep_provider_orig';
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++
++# We can not use a wait_condition on SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS as such queries are blocked during IST
++# so we perform a simple sleep and SHOW instead
++
++--sleep 5
++SHOW STATUS LIKE 'wsrep_debug_sync_waiters';
++
++--connection node_1
++# Perform DML and DDL while IST is in progress
++--connection node_1
++UPDATE t1 SET f2 = 'd' WHERE f1 > 3;
++CREATE TABLE t2 (f1 INTEGER);
++
++# Kill node #2 while IST is in progress
++--connection node_2
++
++# Kill the connected server
++--disable_reconnect
++
++--perl
++ my $pid_filename = $ENV{'KILL_NODE_PIDFILE'};
++ my $mysqld_pid = `cat $pid_filename`;
++ chomp($mysqld_pid);
++ system("kill -9 $mysqld_pid");
++ exit(0);
++EOF
++
++--source include/wait_until_disconnected.inc
++
++--connection node_1
++--source include/wait_until_connected_again.inc
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++# Perform DML and DDL while node #2 is down
++UPDATE t1 SET f2 = 'e' WHERE f1 > 4;
++CREATE TABLE t3 (f1 INTEGER);
++
++--connection node_2
++
++--let $galera_wsrep_recover_server_id=2
++--source suite/galera/include/galera_wsrep_recover.inc
++
++--echo Starting server ...
++--source include/start_mysqld.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++--connection node_1
++UPDATE t1 SET f2 = 'f' WHERE f1 > 5;
++SELECT * FROM t1;
++
++--connection node_2
++SELECT * FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++SELECT COUNT(*) = 0 FROM t3;
++
++--connection node_1
++DROP TABLE t1, t2, t3;
+diff --git a/mysql-test/suite/galera/t/galera_ist_rsync.cnf b/mysql-test/suite/galera/t/galera_ist_rsync.cnf
+new file mode 100644
+index 0000000..bbe0f60
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_rsync.cnf
+@@ -0,0 +1,11 @@
++!include ../galera_2nodes.cnf
++
++[mysqld]
++wsrep_sst_method=rsync
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_ist_rsync.test b/mysql-test/suite/galera/t/galera_ist_rsync.test
+new file mode 100644
+index 0000000..41d1a0c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_rsync.test
+@@ -0,0 +1,8 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_st_disconnect_slave.inc
++--source suite/galera/include/galera_st_shutdown_slave.inc
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
+diff --git a/mysql-test/suite/galera/t/galera_ist_xtrabackup-v2.cnf b/mysql-test/suite/galera/t/galera_ist_xtrabackup-v2.cnf
+new file mode 100644
+index 0000000..21e5974
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_xtrabackup-v2.cnf
+@@ -0,0 +1,11 @@
++!include ../galera_2nodes.cnf
++
++[mysqld]
++wsrep_sst_method=xtrabackup-v2
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_ist_xtrabackup-v2.test b/mysql-test/suite/galera/t/galera_ist_xtrabackup-v2.test
+new file mode 100644
+index 0000000..8b399e7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ist_xtrabackup-v2.test
+@@ -0,0 +1,9 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_st_disconnect_slave.inc
++--source suite/galera/include/galera_st_shutdown_slave.inc
++
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
+diff --git a/mysql-test/suite/galera/t/galera_kill_ddl.test b/mysql-test/suite/galera/t/galera_kill_ddl.test
+new file mode 100644
+index 0000000..3c2bce5
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_kill_ddl.test
+@@ -0,0 +1,39 @@
++#
++# This test kill -9-s a slave while small updates have been performed on the master.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++
++# Enable the master to continue running during the split-brain situation that
++# occurs when the slave is killed
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++
++--connection node_2
++--source include/kill_galera.inc
++
++--connection node_1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++
++--connection node_2
++--source include/start_mysqld.inc
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='t1';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_1
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_kill_largechanges.test b/mysql-test/suite/galera/t/galera_kill_largechanges.test
+new file mode 100644
+index 0000000..e9a32ce
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_kill_largechanges.test
+@@ -0,0 +1,43 @@
++#
++# This test kill -9-s a slave while a large update has been performed on the master. SST is performed.
++#
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++# Enable the master to continue running during the split-brain situation that
++# occurs when the slave is killed
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
++CREATE TABLE t1 (f1 VARCHAR(128)) ENGINE=InnoDB;
++
++--connection node_2
++--source include/kill_galera.inc
++
++--connection node_1
++# We create a 128Mb (or so) transaction that is larger than gcache. The size of the gcache is not adjustable dynamically
++INSERT INTO t1 SELECT REPEAT('a', 128) FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5, ten AS a6;
++
++--connection node_2
++--source include/start_mysqld.inc
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++
++SELECT COUNT(*) = 1000000 FROM t1;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_1
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_kill_nochanges.test b/mysql-test/suite/galera/t/galera_kill_nochanges.test
+new file mode 100644
+index 0000000..1903df4
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_kill_nochanges.test
+@@ -0,0 +1,24 @@
++#
++# This test kill -9-s a slave while no updates have been performed on the master.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--source include/kill_galera.inc
++--source include/start_mysqld.inc
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++
++SELECT COUNT(*) = 1 FROM t1;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_kill_smallchanges.test b/mysql-test/suite/galera/t/galera_kill_smallchanges.test
+new file mode 100644
+index 0000000..d998032
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_kill_smallchanges.test
+@@ -0,0 +1,39 @@
++#
++# This test kill -9-s a slave while small updates have been performed on the master.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++
++# Enable the master to continue running during the split-brain situation that
++# occurs when the slave is killed
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++
++--connection node_2
++--source include/kill_galera.inc
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--source include/start_mysqld.inc
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++
++SELECT COUNT(*) = 1 FROM t1;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_1
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_lock_table.test b/mysql-test/suite/galera/t/galera_lock_table.test
+new file mode 100644
+index 0000000..bd58184
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_lock_table.test
+@@ -0,0 +1,43 @@
++#
++# Test that a LOCK TABLE on the slave will cause the applier thread to block, so no subsequent updates
++# are replicated on the slave until UNLOCK TABLE is issued.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_sync_wait_orig = `SELECT @@wsrep_sync_wait`
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++LOCK TABLE t1 READ;
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++# We use a separate connection here so that we can SELECT from both tables
++# without running into "table t2 was not locked" error.
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++SET SESSION wsrep_sync_wait=0;
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++
++--connection node_2
++UNLOCK TABLES;
++
++--disable_query_log
++--eval SET SESSION wsrep_sync_wait=$wsrep_sync_wait_orig;
++--enable_query_log
++
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 1 FROM t2;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_log_bin-master.opt b/mysql-test/suite/galera/t/galera_log_bin-master.opt
+new file mode 100644
+index 0000000..8a755e9
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_log_bin-master.opt
+@@ -0,0 +1 @@
++--log-bin --log-slave-updates
+diff --git a/mysql-test/suite/galera/t/galera_log_bin.test b/mysql-test/suite/galera/t/galera_log_bin.test
+new file mode 100644
+index 0000000..14f04e9
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_log_bin.test
+@@ -0,0 +1,36 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test Galera with --log-bin --log-slave-updates .
++# This way the actual MySQL binary log is used,
++# rather than Galera's own implementation
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++CREATE TABLE t2 (id INT) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 2 FROM t2;
++
++--connection node_1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++
++FLUSH LOGS;
++# Use pos 120 in order to skip the header that contains the MySQL version number.
++# Otherwise, version number changes will cause the test to break
++SHOW BINLOG EVENTS IN '0.000002' FROM 120;
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++
++SHOW BINLOG EVENTS IN '0.000001' FROM 120;
++
++DROP TABLE t1;
++DROP TABLE t2;
++
+diff --git a/mysql-test/suite/galera/t/galera_log_output_csv-master.opt b/mysql-test/suite/galera/t/galera_log_output_csv-master.opt
+new file mode 100644
+index 0000000..2f71b14
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_log_output_csv-master.opt
+@@ -0,0 +1 @@
++--log-output=TABLE --log-queries-not-using-indexes --general-log --slow-query-log
+diff --git a/mysql-test/suite/galera/t/galera_log_output_csv.test b/mysql-test/suite/galera/t/galera_log_output_csv.test
+new file mode 100644
+index 0000000..0000939
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_log_output_csv.test
+@@ -0,0 +1,27 @@
++#
++# Test that --log-output=FILE works with Galera.
++# The relevant options are set using a -master.opt file
++# wsrep_replicate_myisam is not used as it crashes in MTR with mysql-wsrep#14
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++SELECT COUNT(*) > 0 FROM mysql.general_log;
++
++SELECT 1 = 1 FROM t1;
++SELECT COUNT(*) = 1 FROM mysql.slow_log WHERE sql_text = 'SELECT 1 = 1 FROM t1';
++
++--connection node_2
++
++# CREATE TABLE from master is also present in the slave query log, but is logged twice, mysql-wsrep#44
++SELECT COUNT(*) > 0 FROM mysql.general_log WHERE argument = 'CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB';
++
++SELECT 2 = 2 FROM t1;
++SELECT COUNT(*) = 1 FROM mysql.slow_log WHERE sql_text = 'SELECT 2 = 2 FROM t1';
++
++--connection node_1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_many_columns.test b/mysql-test/suite/galera/t/galera_many_columns.test
+new file mode 100644
+index 0000000..1961eae
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_many_columns.test
+@@ -0,0 +1,62 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--disable_query_log
++SET @create_var1 = "";
++--let $count = 1017
++while ($count)
++{
++ --eval SET @create_var1 = CONCAT(@create_var1, "f", $count, " VARCHAR(3) DEFAULT 'ABC', ")
++ --dec $count
++}
++
++--let $create_var = `SELECT @create_var1`
++--eval CREATE TABLE t1 ($create_var PRIMARY KEY (f1, f1017)) ENGINE=InnoDB;
++--enable_query_log
++
++INSERT INTO t1 (f1) VALUES (DEFAULT);
++
++--connection node_2
++SELECT f1 = 'ABC', f1017 = 'ABC' FROM t1;
++UPDATE t1 SET f1 = 'XYZ', f1017 = 'XYZ' ;
++
++--connection node_1
++SELECT f1 = 'XYZ', f1017 = 'XYZ' FROM t1 WHERE f1 = 'XYZ' AND f1017 = 'XYZ';
++
++
++# Deadlock
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'KLM' WHERE f1 = 'XYZ' AND f1017 = 'XYZ';
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'CDE' WHERE f1 = 'XYZ' AND f1017 = 'XYZ';
++COMMIT;
++
++--connection node_1
++--error ER_LOCK_DEADLOCK
++COMMIT;
++ROLLBACK;
++
++--connection node_2
++ROLLBACK;
++
++# Rollback
++
++--connection node_1
++START TRANSACTION;
++INSERT INTO t1 (f1, f1017) VALUES ('BCE','BCE');
++INSERT INTO t1 (f1, f1017) VALUES ('CED','CED');
++INSERT INTO t1 (f1, f1017) VALUES ('EDF','EDF');
++INSERT INTO t1 (f1, f1017) VALUES ('FED','FED');
++ROLLBACK;
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_many_indexes.test b/mysql-test/suite/galera/t/galera_many_indexes.test
+new file mode 100644
+index 0000000..e01d0b2
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_many_indexes.test
+@@ -0,0 +1,74 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 VARCHAR(767) PRIMARY KEY) ENGINE=InnoDB;
++
++# MySQL complains about multiple identical indexes on the same column
++--disable_warnings
++
++--let $count = 63
++while ($count)
++{
++ --disable_query_log
++ --eval SET @ddl_var1 = CONCAT("CREATE UNIQUE INDEX i", $count, " ON t1(f1)")
++ --let $ddl_var = `SELECT @ddl_var1`
++ --enable_query_log
++ --eval $ddl_var
++ --dec $count
++}
++--enable_warnings
++
++INSERT INTO t1 VALUES (REPEAT('a', 767));
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT LENGTH(f1) = 767 FROM t1;
++
++EXPLAIN SELECT COUNT(*) = 1 FROM t1 FORCE KEY (PRIMARY) WHERE f1 = REPEAT('a', 767);
++SELECT COUNT(*) = 1 FROM t1 FORCE KEY (PRIMARY) WHERE f1 = REPEAT('a', 767);
++
++EXPLAIN SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i1) WHERE f1 = REPEAT('a', 767);
++SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i1) WHERE f1 = REPEAT('a', 767);
++
++EXPLAIN SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i63) WHERE f1 = REPEAT('a', 767);
++SELECT COUNT(*) = 1 FROM t1 FORCE KEY (i63) WHERE f1 = REPEAT('a', 767);
++
++INSERT INTO t1 VALUES (REPEAT('b', 767));
++ANALYZE TABLE t1;
++
++--connection node_1
++SELECT COUNT(*) = 2 FROM t1;
++ANALYZE TABLE t1;
++DELETE FROM t1 WHERE f1 = REPEAT('b', 767);
++
++# Rollback
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++SELECT COUNT(*) = 1 FROM t1;
++INSERT INTO t1 (f1) VALUES (REPEAT('c', 767));
++ROLLBACK;
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_2
++START TRANSACTION;
++SET AUTOCOMMIT=OFF;
++SELECT COUNT(*) = 1 FROM t1;
++
++# Deadlock
++--connection node_1
++START TRANSACTION;
++--connection node_2
++START TRANSACTION;
++
++--connection node_1
++UPDATE t1 SET f1 = REPEAT('e', 767);
++--connection node_2
++UPDATE t1 SET f1 = REPEAT('f', 767);
++
++--connection node_1
++COMMIT;
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_many_rows.test b/mysql-test/suite/galera/t/galera_many_rows.test
+new file mode 100644
+index 0000000..0f4c4c0
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_many_rows.test
+@@ -0,0 +1,55 @@
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) Engine=InnoDB;
++INSERT INTO t1 (f2) SELECT a1.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++--connection node_2
++SELECT COUNT(*) = 100000 FROM t1;
++INSERT INTO t1 (f2) SELECT a1.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++--connection node_1
++SELECT COUNT(*) = 200000 FROM t1;
++UPDATE t1 SET f2 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 200000 FROM t1 WHERE f2 = 1;
++
++# Rollback
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (f2) SELECT a1.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++ROLLBACK;
++SELECT COUNT(*) = 200000 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 200000 FROM t1;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 2;
++ROLLBACK;
++
++--connection node_1
++START TRANSACTION;
++SELECT COUNT(*) = 200000 FROM t1;
++UPDATE t1 SET f2 = 3;
++
++--connection node_2
++START TRANSACTION;
++UPDATE t1 SET f2 = 4;
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_many_tables_nopk.test b/mysql-test/suite/galera/t/galera_many_tables_nopk.test
+new file mode 100644
+index 0000000..2496d14
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_many_tables_nopk.test
+@@ -0,0 +1,103 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# This test forces 1K tables without a PK to participate in a single transaction
++#
++
++#
++# First, create 1K tables
++#
++
++--connection node_1
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("CREATE TABLE t", $count, " (f1 INTEGER) ENGINE=InnoDB")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("INSERT INTO t", $count, " VALUES (1234)")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++#
++# Second, perform 1K updates
++#
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("UPDATE t", $count, " SET f1 = 1")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++COMMIT;
++
++# Third, confirm that all the inserts have arrived on the second node
++#
++
++--connection node_2
++CREATE TABLE sum_table (f1 INTEGER);
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("INSERT INTO sum_table SELECT COUNT(*) FROM t", $count)`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++SELECT SUM(f1) = 1000 FROM sum_table;
++
++#
++# Fourth, create a deadlock
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("UPDATE t", $count, " SET f1 = 2")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1000 SET f1 = 3;
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP SCHEMA test;
++CREATE SCHEMA test;
+diff --git a/mysql-test/suite/galera/t/galera_many_tables_pk.test b/mysql-test/suite/galera/t/galera_many_tables_pk.test
+new file mode 100644
+index 0000000..886cb7c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_many_tables_pk.test
+@@ -0,0 +1,98 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# This test forces 1K tables with a PK to participate in a single transaction
++#
++
++#
++# First, create 1K tables and make sure the DDLs are all propagated
++#
++
++--connection node_1
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("CREATE TABLE t", $count, " (f1 INTEGER AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++--connection node_2
++SELECT COUNT(*) = 1000 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME LIKE 't%';
++
++#
++# Second, create a transaction that uses all those tables
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("INSERT INTO t", $count, " VALUES (DEFAULT)")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++COMMIT;
++
++#
++# Third, confirm that all the inserts have arrived on the second node
++#
++
++--connection node_2
++CREATE TABLE sum_table (f1 INTEGER);
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("INSERT INTO sum_table SELECT COUNT(*) FROM t", $count)`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++SELECT SUM(f1) = 1000 FROM sum_table;
++
++#
++# Fourth, create a deadlock
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ --let $ddl_var = `SELECT CONCAT("UPDATE t", $count, " SET f1 = 2")`
++ --eval $ddl_var
++ --enable_query_log
++ --dec $count
++}
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1000 SET f1 = 3;
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP SCHEMA test;
++CREATE SCHEMA test;
+diff --git a/mysql-test/suite/galera/t/galera_migrate.cnf b/mysql-test/suite/galera/t/galera_migrate.cnf
+new file mode 100644
+index 0000000..dfaf4a6
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_migrate.cnf
+@@ -0,0 +1,47 @@
++#
++# This .cnf file starts 4 servers without enabling Galera.
++# The galera_migrate.test will set wsrep_provider and the other settings as needed.
++#
++
++!include include/default_mysqld.cnf
++
++[mysqld]
++binlog-format=row
++innodb_autoinc_lock_mode=2
++innodb_flush_log_at_trx_commit=2
++log-bin=mysqld-bin
++
++wsrep_node_address=127.0.0.1
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++[mysqld.1]
++
++[mysqld.2]
++
++[mysqld.3]
++
++[mysqld.4]
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_MYPORT_3= @mysqld.3.port
++NODE_MYSOCK_3= @mysqld.3.socket
++
++NODE_MYPORT_4= @mysqld.4.port
++NODE_MYSOCK_4= @mysqld.4.socket
++
++NODE_GALERAPORT_1= @mysqld.1.#galera_port
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++NODE_GALERAPORT_3= @mysqld.3.#galera_port
++NODE_GALERAPORT_4= @mysqld.4.#galera_port
++
++NODE_SSTPORT_1= @mysqld.1.#sst_port
++NODE_SSTPORT_2= @mysqld.2.#sst_port
++NODE_SSTPORT_3= @mysqld.3.#sst_port
++NODE_SSTPORT_4= @mysqld.4.#sst_port
+diff --git a/mysql-test/suite/galera/t/galera_migrate.test b/mysql-test/suite/galera/t/galera_migrate.test
+new file mode 100644
+index 0000000..5c34fd6
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_migrate.test
+@@ -0,0 +1,204 @@
++#
++# Execute a migration from MySQL replication to Galera replication.
++# The test starts with 4 stand-alone servers defined by galera_migrate.cnf and then
++# performs the following steps:
++#
++# 1. Begin with a single MySQL server
++# 2. Establish traditional MySQL master-slave replication
++# 3. Attach a new sever to serve as a MySQL replication slave
++# 4. Enable Galera on the new slave and create a single-node Galera cluster
++# 5. Attach a second Galera node
++# 6. Turn off the traditional replication parts of the system
++# 7. Continue replicating within Galera only
++#
++
++--source include/big_test.inc
++--source include/have_innodb.inc
++--source include/have_log_bin.inc
++
++#
++# Step #1 Begin with a single server
++#
++
++--connect node_1, 127.0.0.1, root, , test, $NODE_MYPORT_1
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++#
++# Step #2. Establish traditional MySQL replication
++#
++
++--connect node_2, 127.0.0.1, root, , test, $NODE_MYPORT_2
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT = $NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 2 FROM t1;
++--source include/wait_condition.inc
++
++#
++# Step #3. Attach a second slave, later to be converted to Galera
++#
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++--disable_query_log
++--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT = $NODE_MYPORT_1;
++--enable_query_log
++START SLAVE USER='root';
++
++--connection node_1
++INSERT INTO t1 VALUES (3);
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 3 FROM t1;
++--source include/wait_condition.inc
++
++#
++# Step #4. Convert this MySQL slave into a Galera node
++#
++
++--connection node_1
++INSERT INTO t1 VALUES (4);
++
++--connection node_3
++--disable_query_log
++--eval SET GLOBAL wsrep_provider='$WSREP_PROVIDER'
++--eval SET GLOBAL wsrep_provider_options='base_port=$NODE_GALERAPORT_3'
++--enable_query_log
++SET GLOBAL wsrep_cluster_address='gcomm://';
++
++--connection node_1
++INSERT INTO t1 VALUES (5);
++
++--connection node_3
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++--let $wait_condition = SELECT COUNT(*) = 5 FROM t1;
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++--source include/wait_condition.inc
++
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_1
++INSERT INTO t1 VALUES (6);
++
++#
++# Step #5. Attach a second Galera node using mysqldump SST
++#
++
++--connection node_3
++# We need a user with a password for mysqldump SST
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++SET GLOBAL wsrep_sst_auth = 'sst:sst';
++
++--connect node_4, 127.0.0.1, root, , test, $NODE_MYPORT_4
++GRANT ALL PRIVILEGES ON *.* TO 'sst' IDENTIFIED BY 'sst';
++
++--disable_query_log
++--eval SET GLOBAL wsrep_sst_method = 'mysqldump';
++--eval SET GLOBAL wsrep_provider='$WSREP_PROVIDER'
++--eval SET GLOBAL wsrep_provider_options='base_port=$NODE_GALERAPORT_4'
++--eval SET GLOBAL wsrep_sst_receive_address = '127.0.0.2:$NODE_MYPORT_4';
++--eval SET GLOBAL wsrep_cluster_address='gcomm://127.0.0.1:$NODE_GALERAPORT_3'
++--enable_query_log
++
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT COUNT(*) = 6 FROM t1;
++--source include/wait_condition.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++--source include/wait_condition.inc
++
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++
++#
++# Step #6. Turn off traditional replication
++#
++
++--connection node_2
++STOP SLAVE;
++RESET SLAVE ALL;
++
++--connection node_3
++STOP SLAVE;
++RESET SLAVE ALL;
++
++#
++# Step #7. Continue replicating within Galera only
++#
++
++# We need fresh connections due to galera#191
++
++--connect node_3a, 127.0.0.1, root, , test, $NODE_MYPORT_3
++INSERT INTO t1 VALUES (7);
++
++--connect node_4a, 127.0.0.1, root, , test, $NODE_MYPORT_4
++INSERT INTO t1 VALUES (8);
++
++--connection node_4a
++SELECT COUNT(*) = 8 FROM t1;
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++--source include/wait_condition.inc
++
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_3a
++SELECT COUNT(*) = 8 FROM t1;
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++--source include/wait_condition.inc
++
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++#
++# Teardown
++#
++
++--connection node_1
++DROP TABLE t1;
++
++--connection node_2
++DROP TABLE t1;
++
++--connection node_3
++SET GLOBAL wsrep_provider = 'none';
++SET GLOBAL wsrep_sst_auth = '';
++SET GLOBAL wsrep_provider_options = '';
++DROP TABLE t1;
++DROP USER sst;
++
++--connection node_4
++SET GLOBAL wsrep_provider = 'none';
++SET GLOBAL wsrep_sst_method = 'rsync';
++SET GLOBAL wsrep_provider_options = '';
++SET GLOBAL wsrep_sst_receive_address = 'AUTO';
++DROP TABLE t1;
++DROP USER sst;
++
++CALL mtr.add_suppression("InnoDB: Error: Table \"mysql\"\\.\"innodb_index_stats\" not found");
+diff --git a/mysql-test/suite/galera/t/galera_multi_database.test b/mysql-test/suite/galera/t/galera_multi_database.test
+new file mode 100644
+index 0000000..6e06aaa
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_multi_database.test
+@@ -0,0 +1,43 @@
++#
++# Test that identical updates can be delivered to two separate
++# databases without this causing a certification conflict
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE DATABASE d1;
++CREATE TABLE d1.t1(f1 INTEGER) ENGINE=InnoDB;
++
++CREATE DATABASE d2;
++CREATE TABLE d2.t1(f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO d1.t1 VALUES (1);
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO d2.t1 VALUES (1);
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++COMMIT;
++
++SELECT COUNT(*) = 1 FROM d1.t1;
++SELECT COUNT(*) = 1 FROM d2.t1;
++
++--connection node_1
++
++SELECT COUNT(*) = 1 FROM d1.t1;
++SELECT COUNT(*) = 1 FROM d2.t1;
++
++DROP TABLE d1.t1;
++DROP TABLE d2.t1;
++
++DROP DATABASE d1;
++DROP DATABASE d2;
+diff --git a/mysql-test/suite/galera/t/galera_myisam_autocommit.test b/mysql-test/suite/galera/t/galera_myisam_autocommit.test
+new file mode 100644
+index 0000000..b01b5dc
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_myisam_autocommit.test
+@@ -0,0 +1,45 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# This tests simple autocommit replication of MyISAM tables. No updates arrive on the slave.
++#
++
++# Without a PK
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=MyISAM;
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (2), (3);
++INSERT INTO t1 SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL;
++
++CREATE TABLE t2 (f1 INTEGER PRIMARY KEY) ENGINE=MyISAM;
++INSERT INTO t2 VALUES (1);
++INSERT INTO t2 VALUES (2), (3);
++INSERT INTO t2 SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL;
++
++# Error
++--error ER_DUP_ENTRY
++INSERT INTO t2 VALUES (6), (1);
++
++# UPDATE
++
++UPDATE t1 SET f1 = 9;
++UPDATE t2 SET f1 = 9 WHERE f1 = 1;
++
++# DELETE
++
++DELETE FROM t1 WHERE f1 = 9;
++DELETE FROM t2 WHERE f1 = 9;
++
++# TRUNCATE
++
++TRUNCATE TABLE t1;
++TRUNCATE TABLE t1;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_myisam_transactions.test b/mysql-test/suite/galera/t/galera_myisam_transactions.test
+new file mode 100644
+index 0000000..00e0bf3
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_myisam_transactions.test
+@@ -0,0 +1,36 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# This tests MyISAM tables in transactions. No MyISAM updates arrive on the slave, but InnoDB ones do.
++#
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER) ENGINE=MyISAM;
++CREATE TABLE t3 (f1 INTEGER) ENGINE=MyISAM;
++
++CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t3 VALUES (NEW.f1);
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++COMMIT;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++SELECT COUNT(*) = 0 FROM t2;
++
++--connection node_1
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++ROLLBACK;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++SELECT COUNT(*) = 0 FROM t2;
++
++DROP TABLE t1, t2, t3;
+diff --git a/mysql-test/suite/galera/t/galera_nopk_bit.test b/mysql-test/suite/galera/t/galera_nopk_bit.test
+new file mode 100644
+index 0000000..4292a6d
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_nopk_bit.test
+@@ -0,0 +1,46 @@
++#
++# This checks that even tables with a single BIT column are replicated properly without a PK
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 BIT) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL),(0),(b'1');
++
++--connection node_2
++SELECT f1 IS NULL, f1 = b'1' FROM t1;
++
++DELETE FROM t1 WHERE f1 = b'1';
++UPDATE t1 SET f1 = b'1' WHERE f1 IS NULL;
++UPDATE t1 SET f1 = 1 WHERE f1 = b'0';
++
++--connection node_1
++SELECT f1 IS NULL, f1 = b'1' FROM t1;
++
++#
++# Provoke a conflict
++#
++
++--connection node_1
++CREATE TABLE t2 (f1 BIT) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (NULL);
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 0 WHERE f1 IS NULL;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 1 WHERE f1 IS NULL;
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_nopk_blob.test b/mysql-test/suite/galera/t/galera_nopk_blob.test
+new file mode 100644
+index 0000000..08e3b99
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_nopk_blob.test
+@@ -0,0 +1,46 @@
++#
++# This checks that even tables with a single BLOB column and no FK are replicated properly
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL),('abc');
++
++--connection node_2
++SELECT f1 FROM t1;
++
++DELETE FROM t1 WHERE f1 IS NULL;
++UPDATE t1 SET f1 = 'xyz' WHERE f1 = 'abc';
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM t1;
++SELECT f1 = 'abc' FROM t1;
++
++#
++# Provoke a conflict
++#
++
++--connection node_1
++CREATE TABLE t2 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (NULL);
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'abc' WHERE f1 IS NULL;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'xyz' WHERE f1 IS NULL;
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_nopk_large_varchar.test b/mysql-test/suite/galera/t/galera_nopk_large_varchar.test
+new file mode 100644
+index 0000000..bb9bcd5
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_nopk_large_varchar.test
+@@ -0,0 +1,50 @@
++#
++# This checks that even tables with a single long VARCHARcolumn and no FK are replicated properly
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++# From the Innodb manual: "The maximum row length, except for variable-length columns (VARBINARY, VARCHAR, BLOB and TEXT),
++# is slightly less than half of a database page. That is, the maximum row length is about 8000 bytes"
++
++CREATE TABLE t1 (f1 VARCHAR(8000)) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (NULL),(CONCAT(REPEAT('x', 7999), 'a'));
++
++--connection node_2
++SELECT LENGTH(f1) FROM t1;
++
++DELETE FROM t1 WHERE f1 IS NULL;
++UPDATE t1 SET f1 = CONCAT(REPEAT('x', 7999), 'b') WHERE f1 = CONCAT(REPEAT('x', 7999), 'a');
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM t1;
++SELECT LENGTH(f1) = 8000 FROM t1;
++SELECT f1 = CONCAT(REPEAT('x', 7999), 'b') FROM t1;
++
++#
++# Provoke a conflict
++#
++
++--connection node_1
++CREATE TABLE t2 (f1 BLOB) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (CONCAT(REPEAT('x', 7999), 'a'));
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'abc' WHERE f1 = CONCAT(REPEAT('x', 7999), 'a');
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t2 SET f1 = 'xyz' WHERE f1 = CONCAT(REPEAT('x', 7999), 'a');
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_nopk_unicode.test b/mysql-test/suite/galera/t/galera_nopk_unicode.test
+new file mode 100644
+index 0000000..e036e14
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_nopk_unicode.test
+@@ -0,0 +1,43 @@
++#
++# Test non-ascii data in table without a PK
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (
++ f1 VARCHAR(255),
++ KEY (f1)
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++
++INSERT INTO t1 VALUES ('текст');
++
++--connection node_2
++SELECT f1 = 'текст' FROM t1;
++
++#
++# Provoke a conflict
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст2';
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст3';
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++SELECT f1 = 'текст2' FROM t1;
++SELECT f1 = 'текст2' FROM t1 WHERE f1 = 'текст2';
++
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_parallel_apply_lock_table.test b/mysql-test/suite/galera/t/galera_parallel_apply_lock_table.test
+new file mode 100644
+index 0000000..7c49a57
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_parallel_apply_lock_table.test
+@@ -0,0 +1,51 @@
++#
++# Test that a LOCK TABLE on the slave will cause all applier threads to block,
++# Even though the two INSERTS are independent transactions, the fact that t1 is locked
++# prevents the applier thread from committing the insert against t2, as commits are done
++# in order.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++
++--let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
++--let $wsrep_sync_wait_orig = `SELECT @@wsrep_sync_wait`
++
++SET GLOBAL wsrep_slave_threads = 2;
++LOCK TABLE t1 READ;
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++# We use a separate connection here so that we can SELECT from both tables
++# without running into "table t2 was not locked" error.
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++--sleep 1
++SET SESSION wsrep_sync_wait=0;
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE '%applied write set%';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE '%Waiting for table level lock%';
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 0 FROM t2;
++
++--connection node_2
++UNLOCK TABLES;
++
++--connection node_2a
++--eval SET SESSION wsrep_sync_wait = $wsrep_sync_wait_orig;
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 1 FROM t2;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE '%committed%';
++
++--eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig;
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_parallel_autoinc_largetrx.test b/mysql-test/suite/galera/t/galera_parallel_autoinc_largetrx.test
+new file mode 100644
+index 0000000..a192044
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_parallel_autoinc_largetrx.test
+@@ -0,0 +1,50 @@
++##
++## This test tests parallel application of multiple auto-increment insert transactions
++##
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++# Create a second connection to node1 so that we can run transactions concurrently
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) Engine=InnoDB;
++--connection node_2
++--let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
++SET GLOBAL wsrep_slave_threads = 4;
++
++--connection node_1
++--send INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--connection node_1a
++--send INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--connection node_2
++--send INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--connection node_1
++--reap
++
++--connection node_1a
++--reap
++
++--connection node_2
++--reap
++SELECT COUNT(*) = 30000 FROM t1;
++SELECT COUNT(DISTINCT f1) = 30000 FROM t1;
++SELECT COUNT(*) = 5 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++
++--disable_query_log
++--eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig;
++--enable_query_log
++
++--connection default
++DROP TABLE t1;
++DROP TABLE ten;
++
+diff --git a/mysql-test/suite/galera/t/galera_parallel_autoinc_manytrx.test b/mysql-test/suite/galera/t/galera_parallel_autoinc_manytrx.test
+new file mode 100644
+index 0000000..cf984f9
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_parallel_autoinc_manytrx.test
+@@ -0,0 +1,53 @@
++##
++## Tests the parallel application of many small-ish auto-increment insert transactions
++##
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++# Create a second connection to node1 so that we can run transactions concurrently
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) Engine=InnoDB;
++--connection node_2
++--let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
++SET GLOBAL wsrep_slave_threads = 4;
++
++--connection node_1
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1;
++ --enable_query_log
++ --dec $count
++}
++
++--connection node_2
++--let $count = 1000
++while ($count)
++{
++ --disable_query_log
++ INSERT INTO t1 (f2) SELECT 1 FROM ten AS a1;
++ --enable_query_log
++ --dec $count
++}
++
++SELECT COUNT(*) = 20000 FROM t1;
++SELECT COUNT(DISTINCT f1) = 20000 FROM t1;
++SELECT COUNT(*) = 4 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE 'committed%';
++
++--disable_query_log
++--eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig;
++--enable_query_log
++
++--connection default
++DROP TABLE t1;
++DROP TABLE ten;
++
+diff --git a/mysql-test/suite/galera/t/galera_parallel_simple.test b/mysql-test/suite/galera/t/galera_parallel_simple.test
+new file mode 100644
+index 0000000..b1dc14d
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_parallel_simple.test
+@@ -0,0 +1,45 @@
++#
++# Test that SHOW PROCESSLIST reports that two slave threads have been involved in applying
++# two independent transactions
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
++
++CREATE TABLE t1 (id INT) ENGINE=InnoDB;
++CREATE TABLE t2 (id INT) ENGINE=InnoDB;
++
++--connection node_2
++SET GLOBAL wsrep_slave_threads = 2;
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++--connection node_2
++
++SELECT COUNT(*) = 10 FROM t1;
++SELECT COUNT(*) = 10 FROM t2;
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE 'committed%';
++
++--eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_pc_ignore_sb.test b/mysql-test/suite/galera/t/galera_pc_ignore_sb.test
+new file mode 100644
+index 0000000..4be7331
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_pc_ignore_sb.test
+@@ -0,0 +1,35 @@
++#
++# Test pc.ignore_sb=true wsrep_provider option . Killing one node should leave the other running.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++--let $wsrep_cluster_address_orig = `SELECT @@wsrep_cluster_address`
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++
++SET GLOBAL wsrep_provider_options = 'pc.ignore_sb=true';
++
++--connection node_2
++--source include/kill_galera.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++DROP TABLE t1;
++
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++
++# Reset the master and restart the slave so that post-test checks can run
++
++SET GLOBAL wsrep_cluster_address = '';
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++--connection node_2
++--source include/start_mysqld.inc
++--source include/wait_until_connected_again.inc
+diff --git a/mysql-test/suite/galera/t/galera_pk_bigint_signed.test b/mysql-test/suite/galera/t/galera_pk_bigint_signed.test
+new file mode 100644
+index 0000000..12a8a8f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_pk_bigint_signed.test
+@@ -0,0 +1,46 @@
++#
++# PK that is a BIGINT SIGNED
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 BIGINT SIGNED PRIMARY KEY, f2 VARCHAR(5)) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES
++ (-9223372036854775808, 'min'),
++ (9223372036854775807, 'max')
++;
++
++--connection node_2
++SELECT * FROM t1;
++
++UPDATE t1 SET f2 = CONCAT(f2, '_');
++
++--connection node_1
++SELECT * FROM t1;
++
++#
++# Deadlock
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'foo' WHERE f1 = -9223372036854775808;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'bar' WHERE f1 = -9223372036854775808;
++
++--connection node_1
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_pk_bigint_unsigned.test b/mysql-test/suite/galera/t/galera_pk_bigint_unsigned.test
+new file mode 100644
+index 0000000..2bb02d5
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_pk_bigint_unsigned.test
+@@ -0,0 +1,45 @@
++#
++# PK that is a BIGINT UNSIGNED
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 BIGINT UNSIGNED PRIMARY KEY, f2 VARCHAR(5)) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES
++ (18446744073709551615, 'max')
++;
++
++--connection node_2
++SELECT f1 = 18446744073709551615 FROM t1;
++
++UPDATE t1 SET f2 = CONCAT(f2, '_');
++
++--connection node_1
++SELECT f1 = 18446744073709551615 FROM t1;
++
++#
++# Deadlock
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'foo' WHERE f1 = 18446744073709551615;
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f2 = 'bar' WHERE f1 = 18446744073709551615;
++
++--connection node_1
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++SET AUTOCOMMIT=ON;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_query_cache-master.opt b/mysql-test/suite/galera/t/galera_query_cache-master.opt
+new file mode 100644
+index 0000000..18f8004
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_query_cache-master.opt
+@@ -0,0 +1,2 @@
++--query_cache_type=1
++
+diff --git a/mysql-test/suite/galera/t/galera_query_cache.test b/mysql-test/suite/galera/t/galera_query_cache.test
+new file mode 100644
+index 0000000..900faba
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_query_cache.test
+@@ -0,0 +1,67 @@
++--source include/have_query_cache.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Ensure that the query cache behaves properly with respect to Galera
++#
++# * in the absence of updates, the query cache does serve cached results
++# * any cache-invalidating query on the remote node also causes the local cache to be invalidated
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++RESET QUERY CACHE;
++FLUSH STATUS;
++
++#
++# 1. Cache works
++#
++
++SELECT COUNT(*) FROM t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++
++SELECT COUNT(*) FROM t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++
++#
++# 2. Cache is invalidated by DML on remote node
++#
++
++--connection node_1
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++FLUSH STATUS;
++
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++SELECT COUNT(*) FROM t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++SELECT COUNT(*) FROM t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++
++#
++# 3. Cache is invalidated by DDL on remote node
++#
++
++--connection node_1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++
++--connection node_2
++FLUSH STATUS;
++
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++SELECT COUNT(*) FROM t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Qcache_queries_in_cache';
++
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++SELECT COUNT(*) FROM t1;
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'Qcache_hits';
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_read_only.test b/mysql-test/suite/galera/t/galera_read_only.test
+new file mode 100644
+index 0000000..828f35d
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_read_only.test
+@@ -0,0 +1,23 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Ensure that the read_only option does not apply to Galera appliers and that replication
++# continues, the way MySQL replication would.
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++SET GLOBAL read_only=TRUE;
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++SET GLOBAL read_only=FALSE;
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_repl_key_format_flat16.test b/mysql-test/suite/galera/t/galera_repl_key_format_flat16.test
+new file mode 100644
+index 0000000..8749c20
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_repl_key_format_flat16.test
+@@ -0,0 +1,34 @@
++#
++# Test repl.key_format = FLAT16 . Since it is very difficult to cause a collision on a 16-byte hash,
++# we simply verify that the option is settable and that replication works.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'repl.key_format=FLAT16';
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (123);
++
++CREATE TABLE t2 (f1 VARCHAR(256)) ENGINE=InnoDB;
++INSERT INTO t2 VALUES (REPEAT('a', 256));
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++UPDATE t1 SET f1 = 234;
++UPDATE t2 SET f1 = REPEAT('b', 256);
++
++--connection node_1
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 234;
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = REPEAT('b', 256);
++
++
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_repl_max_ws_size.test b/mysql-test/suite/galera/t/galera_repl_max_ws_size.test
+new file mode 100644
+index 0000000..255e292
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_repl_max_ws_size.test
+@@ -0,0 +1,27 @@
++#
++# Test repl.max_ws_size . A transaction larger than this size can not commit.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++
++CREATE TABLE t1 (f1 VARCHAR(512)) ENGINE=InnoDB;
++
++SET GLOBAL wsrep_provider_options = 'repl.max_ws_size=512';
++
++--error ER_ERROR_DURING_COMMIT
++INSERT INTO t1 VALUES (REPEAT('a', 512));
++
++SELECT COUNT(*) = 0 FROM t1;
++
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++
++DROP TABLE t1;
++
++CALL mtr.add_suppression("WSREP: Maximum writeset size exceeded by");
++CALL mtr.add_suppression("WSREP: transaction size exceeded");
+diff --git a/mysql-test/suite/galera/t/galera_restart_nochanges.test b/mysql-test/suite/galera/t/galera_restart_nochanges.test
+new file mode 100644
+index 0000000..8eb7617
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_restart_nochanges.test
+@@ -0,0 +1,23 @@
++#
++# This test restarts a slave while no updates have been performed on the master. No SST is performed.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--source include/restart_mysqld.inc
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++
++SELECT COUNT(*) = 1 FROM t1;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_rsu_add_pk.test b/mysql-test/suite/galera/t/galera_rsu_add_pk.test
+new file mode 100644
+index 0000000..1a5501a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_rsu_add_pk.test
+@@ -0,0 +1,44 @@
++#
++# ALTER TABLE ... ADD PRIMARY KEY under Rolling Schema Upgrade
++#
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
++
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++
++# Insert some values before the ALTER
++INSERT INTO t1 (f1) SELECT 000000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++# Insert more values while the ALTER is running
++--send INSERT INTO t1 (f1) SELECT 100000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++--connection node_2
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET GLOBAL wsrep_OSU_method = "TOI";
++
++# Insert values after the ALTER
++INSERT INTO t1 (f1) SELECT 200000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++
++SELECT COUNT(*) = 300000 FROM t1;
++SELECT MAX(f1) = 299999 FROM t1;
++
++--connection node_1
++--reap
++SELECT COUNT(*) = 300000 FROM t1;
++SELECT MAX(f1) = 299999 FROM t1;
++
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET GLOBAL wsrep_OSU_method = "TOI";
++
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_rsu_drop_pk.test b/mysql-test/suite/galera/t/galera_rsu_drop_pk.test
+new file mode 100644
+index 0000000..c3ccf89
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_rsu_drop_pk.test
+@@ -0,0 +1,58 @@
++#
++# ALTER TABLE ... DROP PRIMARY KEY under Rolling Schema Upgrade
++#
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++
++# Insert some values before the ALTER
++INSERT INTO t1 (f1) SELECT 000000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++# Insert more values while the ALTER is running
++--send INSERT INTO t1 (f1) SELECT 100000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++--connection node_2
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 DROP PRIMARY KEY;
++SET GLOBAL wsrep_OSU_method = "TOI";
++
++# Insert even more data after the ALTER has completed
++INSERT INTO t1 (f1) SELECT 200000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++SELECT COUNT(*) = 300000 FROM t1;
++SELECT MAX(f1) = 299999 FROM t1;
++
++--connection node_1
++--reap
++SELECT COUNT(*) = 300000 FROM t1;
++SELECT MAX(f1) = 299999 FROM t1;
++
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 DROP PRIMARY KEY;
++SET GLOBAL wsrep_OSU_method = "TOI";
++
++# Insert some previously-conflicting values after the ALTER has been applied on all nodes.
++--connection node_2
++INSERT INTO t1 (f1) VALUES (1);
++INSERT INTO t1 (f1) VALUES (10);
++
++--connection node_1
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 1;
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 10;
++
++INSERT INTO t1 (f1) VALUES (100);
++INSERT INTO t1 (f1) VALUES (1000);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 100;
++SELECT COUNT(*) = 2 FROM t1 WHERE f1 = 1000;
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_rsu_error.test b/mysql-test/suite/galera/t/galera_rsu_error.test
+new file mode 100644
+index 0000000..b762d2b
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_rsu_error.test
+@@ -0,0 +1,31 @@
++#
++# Test DDL errors under Rolling Schema Upgrade
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t1 VALUES (1), (1);
++
++--connection node_2
++SET GLOBAL wsrep_OSU_method = "RSU";
++--error ER_DUP_ENTRY
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SET GLOBAL wsrep_OSU_method = "TOI";
++
++# The ALTER has no effect
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = 't1';
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_1
++SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 3 FROM t1;
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(3) = 4 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_rsu_simple.test b/mysql-test/suite/galera/t/galera_rsu_simple.test
+new file mode 100644
+index 0000000..ea0eec1
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_rsu_simple.test
+@@ -0,0 +1,35 @@
++#
++# Test Rolling Schema Upgrade
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++
++--connection node_2
++SET GLOBAL wsrep_OSU_method = "RSU";
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++SET GLOBAL wsrep_OSU_method = "TOI";
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++
++--connection node_1
++# The ALTER above is not visible on node_1
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++# The INSERT above is now visible on node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++INSERT INTO t1 (f1) VALUES (2);
++
++--connection node_1
++# The ALTER has not replicated
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++
++# However the INSERT above has
++SELECT COUNT(*) = 2 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_sbr.test b/mysql-test/suite/galera/t/galera_sbr.test
+new file mode 100644
+index 0000000..b598759
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sbr.test
+@@ -0,0 +1,27 @@
++#
++# Test behavior if the user attempts to use statement-based replication
++#
++# SBR is not currently supported but we expect that no crashes or binlog-related assertions will be triggered.
++#
++
++--source include/have_innodb.inc
++--source include/galera_cluster.inc
++
++--connection node_1
++SET GLOBAL binlog_format = 'STATEMENT';
++SET SESSION binlog_format = 'STATEMENT';
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++SET SESSION binlog_format = 'MIXED';
++
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++
++DROP TABLE t1;
++
++--connection node_1
++SET GLOBAL binlog_format = 'ROW';
+diff --git a/mysql-test/suite/galera/t/galera_sbr_binlog-master.opt b/mysql-test/suite/galera/t/galera_sbr_binlog-master.opt
+new file mode 100644
+index 0000000..beae84b
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sbr_binlog-master.opt
+@@ -0,0 +1 @@
++--log-bin
+diff --git a/mysql-test/suite/galera/t/galera_sbr_binlog.test b/mysql-test/suite/galera/t/galera_sbr_binlog.test
+new file mode 100644
+index 0000000..23e490a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sbr_binlog.test
+@@ -0,0 +1,28 @@
++#
++# Test behavior if the user attempts to use statement-based replication
++#
++# SBR is not currently supported but we expect that no crashes or binlog-related assertions will be triggered.
++#
++
++--source include/have_log_bin.inc
++--source include/have_innodb.inc
++--source include/galera_cluster.inc
++
++--connection node_1
++SET GLOBAL binlog_format = 'STATEMENT';
++SET SESSION binlog_format = 'STATEMENT';
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++SET SESSION binlog_format = 'MIXED';
++
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++
++DROP TABLE t1;
++
++--connection node_1
++SET GLOBAL binlog_format = 'ROW';
+diff --git a/mysql-test/suite/galera/t/galera_split_brain.test b/mysql-test/suite/galera/t/galera_split_brain.test
+new file mode 100644
+index 0000000..4e53e96
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_split_brain.test
+@@ -0,0 +1,38 @@
++#
++# Confirm that with two nodes, killing one causes the other to stop accepting connections
++# The pc.ignore_sb=true wsrep_provider option is tested in the galera_kill_* tests.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++call mtr.add_suppression("WSREP: TO isolation failed for: ");
++
++--connection node_1
++--let $wsrep_cluster_address_orig = `SELECT @@wsrep_cluster_address`
++
++--connection node_2
++--source include/kill_galera.inc
++
++--connection node_1
++--error ER_LOCK_DEADLOCK
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++
++# Reset the master and restart the slave so that post-test checks can run
++
++SET GLOBAL wsrep_cluster_address = '';
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++
++--source include/start_mysqld.inc
++--sleep 5
++--source include/wait_until_connected_again.inc
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++--sleep 5
++
++--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
++--source include/wait_until_connected_again.inc
+diff --git a/mysql-test/suite/galera/t/galera_sql_log_bin_zero.test b/mysql-test/suite/galera/t/galera_sql_log_bin_zero.test
+new file mode 100644
+index 0000000..b6965fa
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sql_log_bin_zero.test
+@@ -0,0 +1,25 @@
++#
++# Test SET SESSION sql_log_bin = 0 . We expect that unlogged updates will not be replicated
++# to the slave and that there will be no assertions in the process.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++
++SET SESSION sql_log_bin = 0;
++
++INSERT INTO t1 VALUES (1);
++
++SET SESSION sql_log_bin = 1;
++
++INSERT INTO t1 VALUES (2);
++
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 0 FROM t1 WHERE f1 = 1;
++
++--connection node_1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_ssl.cnf b/mysql-test/suite/galera/t/galera_ssl.cnf
+new file mode 100644
+index 0000000..59ea286
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ssl.cnf
+@@ -0,0 +1,51 @@
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;socket.ssl=yes;socket.ssl_cert=@ENV.MYSQL_TEST_DIR/std_data/galera-cert.pem;socket.ssl_key=@ENV.MYSQL_TEST_DIR/std_data/galera-key.pem'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.1.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[mysqld.2]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;socket.ssl=yes;socket.ssl_cert=@ENV.MYSQL_TEST_DIR/std_data/galera-cert.pem;socket.ssl_key=@ENV.MYSQL_TEST_DIR/std_data/galera-key.pem'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_GALERAPORT_1= @mysqld.1.#galera_port
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++
++NODE_SSTPORT_1= @mysqld.1.#sst_port
++NODE_SSTPORT_2= @mysqld.2.#sst_port
+diff --git a/mysql-test/suite/galera/t/galera_ssl.test b/mysql-test/suite/galera/t/galera_ssl.test
+new file mode 100644
+index 0000000..8dc94dc
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_ssl.test
+@@ -0,0 +1,24 @@
++#
++# Test node connections over SSL. The accompanying galera_ssl.cnf has a customized
++# wsrep_provider_options setting that enables SSL.
++#
++# At this time, the actual operation of SSL is not visible only in the error log and not in SHOW STATUS.
++# So this test can only check that the cluster has formed and is replicating.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_sst_mysqldump.cnf b/mysql-test/suite/galera/t/galera_sst_mysqldump.cnf
+new file mode 100644
+index 0000000..574ae28
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sst_mysqldump.cnf
+@@ -0,0 +1,11 @@
++!include ../galera_2nodes.cnf
++
++# We do not set mysqldump-related SST options here because doing so on startup
++# causes the first MTR connection to be forefully dropped by Galera, which in turn confuses MTR
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcache.size=1;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;gcache.size=1;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_sst_mysqldump.test b/mysql-test/suite/galera/t/galera_sst_mysqldump.test
+new file mode 100644
+index 0000000..0b71715
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sst_mysqldump.test
+@@ -0,0 +1,18 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_sst_set_mysqldump.inc
++
++--source suite/galera/include/galera_st_disconnect_slave.inc
++
++# We set the required mysqldump SST options here so that they are used every time the server is restarted during the test
++--let $start_mysqld_params = --wsrep_sst_auth=sst:sst --wsrep_sst_method=mysqldump --wsrep-sst-receive-address=127.0.0.1:$NODE_MYPORT_2 --skip-grant-tables
++
++--source suite/galera/include/galera_st_shutdown_slave.inc
++--source suite/galera/include/galera_st_clean_slave.inc
++
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
++
++--source suite/galera/include/galera_sst_restore.inc
+diff --git a/mysql-test/suite/galera/t/galera_sst_rsync.cnf b/mysql-test/suite/galera/t/galera_sst_rsync.cnf
+new file mode 100644
+index 0000000..93981d9
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sst_rsync.cnf
+@@ -0,0 +1,11 @@
++!include ../galera_2nodes.cnf
++
++[mysqld]
++wsrep_sst_method=rsync
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcache.size=1;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;gcache.size=1;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_sst_rsync.test b/mysql-test/suite/galera/t/galera_sst_rsync.test
+new file mode 100644
+index 0000000..c682379
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sst_rsync.test
+@@ -0,0 +1,9 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_st_shutdown_slave.inc
++--source suite/galera/include/galera_st_clean_slave.inc
++
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
+diff --git a/mysql-test/suite/galera/t/galera_sst_xtrabackup-v2.cnf b/mysql-test/suite/galera/t/galera_sst_xtrabackup-v2.cnf
+new file mode 100644
+index 0000000..47cb3e0
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sst_xtrabackup-v2.cnf
+@@ -0,0 +1,13 @@
++!include ../galera_2nodes.cnf
++
++[mysqld]
++wsrep_sst_method=xtrabackup-v2
++wsrep_sst_auth="root:"
++wsrep_debug=ON
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcache.size=1;pc.ignore_sb=true'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;gcache.size=1;pc.ignore_sb=true'
++
+diff --git a/mysql-test/suite/galera/t/galera_sst_xtrabackup-v2.test b/mysql-test/suite/galera/t/galera_sst_xtrabackup-v2.test
+new file mode 100644
+index 0000000..c682379
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_sst_xtrabackup-v2.test
+@@ -0,0 +1,9 @@
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--source suite/galera/include/galera_st_shutdown_slave.inc
++--source suite/galera/include/galera_st_clean_slave.inc
++
++--source suite/galera/include/galera_st_kill_slave.inc
++--source suite/galera/include/galera_st_kill_slave_ddl.inc
+diff --git a/mysql-test/suite/galera/t/galera_status_cluster.test b/mysql-test/suite/galera/t/galera_status_cluster.test
+new file mode 100644
+index 0000000..3299613
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_status_cluster.test
+@@ -0,0 +1,18 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# A simple test for the wsrep_cluster_* status variables
++#
++
++--connection node_1
++
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++
++--connection node_2
++
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++
++
+diff --git a/mysql-test/suite/galera/t/galera_status_local_state.test b/mysql-test/suite/galera/t/galera_status_local_state.test
+new file mode 100644
+index 0000000..09cdb25
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_status_local_state.test
+@@ -0,0 +1,28 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Test wsrep_local_state . We can not reliably produce all possible statuses in MTR, but
++# we can at least test for the ones we can.
++#
++
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++SET GLOBAL wsrep_desync = 1;
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++--source include/wait_condition.inc
++
++SELECT VARIABLE_VALUE = 'Donor/Desynced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++SET GLOBAL wsrep_desync = 0;
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++--source include/wait_condition.inc
++
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++
++
++
+diff --git a/mysql-test/suite/galera/t/galera_suspend_slave.test b/mysql-test/suite/galera/t/galera_suspend_slave.test
+new file mode 100644
+index 0000000..6330711
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_suspend_slave.test
+@@ -0,0 +1,51 @@
++##
++## This test tests that transactions on the master will fail if the slave
++## is made completely unresponsive by suspending the process. Resuming the
++## process should allow replication to continue to run.
++##
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++--let NODE_2_PIDFILE = `SELECT @@pid_file`
++--disconnect node_2
++
++--connection node_1
++--echo Suspending node_2 ...
++--perl
++ my $pid_filename = $ENV{'NODE_2_PIDFILE'};
++ my $mysqld_pid = `cat $pid_filename`;
++ chomp($mysqld_pid);
++ system("kill -19 $mysqld_pid");
++ exit(0);
++EOF
++
++--error ER_UNKNOWN_COM_ERROR,ER_LOCK_WAIT_TIMEOUT
++INSERT INTO t1 VALUES (1);
++
++--echo Resuming node_2 ...
++--perl
++ my $pid_filename = $ENV{'NODE_2_PIDFILE'};
++ my $mysqld_pid = `cat $pid_filename`;
++ chomp($mysqld_pid);
++ system("kill -18 $mysqld_pid");
++ exit(0);
++EOF
++
++--sleep 10
++--source include/galera_wait_ready.inc
++INSERT INTO t1 VALUES (1);
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++
++--source include/galera_wait_ready.inc
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test b/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
+new file mode 100644
+index 0000000..641d210
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
+@@ -0,0 +1,120 @@
++
++#
++# Test the operation of ALTER TABLE ... AUTO_INCREMENT
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
++
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++--connection node_2
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++ALTER TABLE t1 AUTO_INCREMENT = 1000;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++--connection node_1
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++SELECT MIN(f1) >= 1000, COUNT(*) = 20, COUNT(DISTINCT f1) = 20 FROM t1 WHERE f1 >= 1000;
++
++--connection node_2
++SELECT MIN(f1) >= 1000, COUNT(*) = 20, COUNT(DISTINCT f1) = 20 FROM t1 WHERE f1 >= 1000;
++
++#
++# AUTO_INCREMENT set to a value lower than the current one.
++# The ALTER does nothing, the sequence continues from the current maximum.
++#
++
++--connection node_1
++ALTER TABLE t1 AUTO_INCREMENT = 5;
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++--connection node_2
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++SELECT MIN(f1) >= 1000, COUNT(*) = 40, COUNT(DISTINCT f1) = 40 FROM t1 WHERE f1 >= 1000;
++
++--connection node_1
++SELECT MIN(f1) >= 1000, COUNT(*) = 40, COUNT(DISTINCT f1) = 40 FROM t1 WHERE f1 >= 1000;
++
++DROP TABLE t1;
++
++#
++# Under wsrep_auto_increment_control = OFF
++#
++
++--connection node_1
++--let $auto_increment_control_orig = `SELECT @@wsrep_auto_increment_control`
++--let $auto_increment_increment_node1 = `SELECT @@auto_increment_increment`
++--let $auto_increment_offset_node1 = `SELECT @@auto_increment_offset`
++
++# Restore stock MySQL defaults
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++
++#Open a fresh connection to node_1 so that the variables above take effect
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++
++--connection node_2
++--let $auto_increment_increment_node2 = `SELECT @@auto_increment_increment`
++--let $auto_increment_offset_node2 = `SELECT @@auto_increment_offset`
++
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++
++#Open a fresh connection to node_2
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++
++--connection node_1a
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
++
++--connection node_2a
++
++ALTER TABLE t1 AUTO_INCREMENT=100;
++
++--connection node_1a
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++--connection node_2a
++INSERT INTO t1 (f2) SELECT 1 FROM ten;
++
++SELECT MIN(f1) = 100, MAX(f1) = 119, COUNT(f1) = 20, COUNT(DISTINCT f1) = 20 FROM t1;
++
++--connection node_1a
++SELECT MIN(f1) = 100, MAX(f1) = 119, COUNT(f1) = 20, COUNT(DISTINCT f1) = 20 FROM t1;
++
++DROP TABLE t1;
++
++#
++# Restore all variables as they were
++#
++
++--disable_query_log
++
++--connection node_1
++--eval SET GLOBAL wsrep_auto_increment_control = $auto_increment_control_orig
++--eval SET GLOBAL auto_increment_increment = $auto_increment_increment_node1
++--eval SET GLOBAL auto_increment_offset = $auto_increment_offset_node1
++
++--connection node_2
++--eval SET GLOBAL wsrep_auto_increment_control = $auto_increment_control_orig
++--eval SET GLOBAL auto_increment_increment = $auto_increment_increment_node2
++--eval SET GLOBAL auto_increment_offset = $auto_increment_offset_node2
++
++--enable_query_log
++
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_toi_ddl_error.test b/mysql-test/suite/galera/t/galera_toi_ddl_error.test
+new file mode 100644
+index 0000000..c586d97
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_ddl_error.test
+@@ -0,0 +1,29 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/big_test.inc
++
++#
++# Test the operation of DDLs that fail partway through
++#
++
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++
++# Insert 100K rows
++INSERT INTO t1 (f1) SELECT (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++# Insert one duplicate value
++INSERT INTO t1 (f1) SELECT MAX(f1) FROM t1;
++
++--connection node_2
++--error ER_DUP_ENTRY
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++SHOW CREATE TABLE t1;
++
++--connection node_1
++SHOW CREATE TABLE t1;
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_toi_ddl_fk_insert.test b/mysql-test/suite/galera/t/galera_toi_ddl_fk_insert.test
+new file mode 100644
+index 0000000..1f44693
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_ddl_fk_insert.test
+@@ -0,0 +1,70 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/big_test.inc
++
++#
++# This test creates a new FK constraint while concurrent INSERTS are running
++#
++
++CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE parent (
++ id INT PRIMARY KEY AUTO_INCREMENT,
++ f2 INTEGER,
++ KEY (id)
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT PRIMARY KEY AUTO_INCREMENT,
++ parent_id INT
++) ENGINE=InnoDB;
++
++INSERT INTO parent VALUES (DEFAULT, 0);
++
++--connection node_2
++--send INSERT INTO child (parent_id) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++--connection node_1a
++--send INSERT INTO parent (f2) SELECT 1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++--send INSERT INTO parent (f2) SELECT 2 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;
++
++--let $galera_connection_name = node_1b
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++--connection node_1b
++--sleep 2
++--send ALTER TABLE child ADD FOREIGN KEY (parent_id) REFERENCES parent(id);
++
++--connection node_1a
++--reap
++
++--connection node_1b
++--reap
++
++--connection node_2
++--reap
++
++--connection node_2a
++--reap
++
++--connection node_1
++SELECT COUNT(*) = 20001 FROM parent;
++SELECT COUNT(*) = 10000 FROM child;
++
++--connection node_2
++SELECT COUNT(*) = 20001 FROM parent;
++SELECT COUNT(*) = 10000 FROM child;
++
++DROP TABLE child;
++DROP TABLE parent;
++
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_toi_ddl_locking.test b/mysql-test/suite/galera/t/galera_toi_ddl_locking.test
+new file mode 100644
+index 0000000..24f918a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_ddl_locking.test
+@@ -0,0 +1,70 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/have_debug.inc
++--source include/have_debug_sync.inc
++
++#
++# Test that DDL indeed causes all nodes to block so even unrelated updates
++# are not allowed to proceed. We block the DDL using DBUG_SYNC
++#
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER) ENGINE=InnoDB;
++
++--connection node_1
++SET DEBUG_SYNC = 'alter_table_before_open_tables WAIT_FOR continue';
++--send ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++
++--let $galera_connection_name = node_1b
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++
++--connection node_1a
++SET SESSION wsrep_sync_wait = 0;
++
++# Allowed
++SELECT COUNT(*) = 0 FROM t1;
++
++# Allowed
++SELECT COUNT(*) = 0 FROM t2;
++
++# Not allowed
++--error ER_LOCK_DEADLOCK
++INSERT INTO t1 VALUES (1);
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++
++# Allowed
++INSERT INTO t2 VALUES (1);
++
++# Hangs
++--send COMMIT;
++--sleep 1
++
++--connection node_1b
++SET SESSION wsrep_sync_wait = 0;
++
++# The Commit issued above is still not done
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE INFO = 'Commit';
++SELECT COUNT(*) = 0 FROM t2;
++SET DEBUG_SYNC= 'now SIGNAL continue';
++
++--connection node_1a
++--reap
++
++--connection node_1
++--reap
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 1 FROM t2;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 1 FROM t2;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_toi_ddl_nonconflicting.test b/mysql-test/suite/galera/t/galera_toi_ddl_nonconflicting.test
+new file mode 100644
+index 0000000..821f7a6
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_ddl_nonconflicting.test
+@@ -0,0 +1,30 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# In this test, we simultaneously send two non-conflicting ALTER TABLE statements
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY AUTO_INCREMENT, f2 INTEGER);
++
++--connection node_2
++--send ALTER TABLE t1 ADD COLUMN f3 INTEGER; INSERT INTO t1 (f1, f2) VALUES (DEFAULT, 123);
++
++--connection node_1
++--send CREATE UNIQUE INDEX i1 ON t1(f2);
++
++--connection node_2
++--reap
++INSERT INTO t1 (f1, f2) VALUES (DEFAULT, 234);
++
++SELECT COUNT(*) = 3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 2 FROM t1;
++
++--connection node_1
++--reap
++SELECT COUNT(*) = 3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
++SELECT COUNT(*) = 2 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test b/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test
+new file mode 100644
+index 0000000..51eae70
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test
+@@ -0,0 +1,29 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# In this test, we send two ALTER TABLE statements that would only work if executed in the right order
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++INSERT INTO t1 VALUES (2, 3);
++
++--connection node_1
++ALTER TABLE t1 DROP COLUMN f2;
++INSERT INTO t1 VALUES (4);
++
++--connection node_2
++SHOW CREATE TABLE t1;
++SELECT COUNT(*) = 3 FROM t1;
++SELECT * FROM t1 ORDER BY f1;
++
++--connection node_1
++SHOW CREATE TABLE t1;
++SELECT COUNT(*) = 3 FROM t1;
++SELECT * FROM t1 ORDER BY f1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_toi_ftwrl.test b/mysql-test/suite/galera/t/galera_toi_ftwrl.test
+new file mode 100644
+index 0000000..4d0edef
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_ftwrl.test
+@@ -0,0 +1,22 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# At this time, issing a FLUSH TABLES WITH READ LOCK on one node does not prevent DDLs from other nodes
++# from proceeding. The locked node will apply the DDL after it has been unlocked
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++FLUSH TABLES WITH READ LOCK;
++
++--connection node_1
++ALTER TABLE t1 ADD COLUMN f2 INTEGER;
++
++--connection node_2
++UNLOCK TABLES;
++SHOW CREATE TABLE t1;
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_toi_lock_exclusive.test b/mysql-test/suite/galera/t/galera_toi_lock_exclusive.test
+new file mode 100644
+index 0000000..3c66286
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_lock_exclusive.test
+@@ -0,0 +1,38 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Ensure that ALTER LOCK=EXCLUSIVE works under TOI. It is difficult to check that concurrent operations
++# are truly not possible, but at least we expect no hangs or deadlocks
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++# Start a transaction that is concurrent to the DDL. This is not strictly necessary for this test
++# but does put more locks into play.
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (2);
++
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++--connection node_2a
++ALTER TABLE t1 ADD COLUMN f2 INTEGER, LOCK=EXCLUSIVE;
++
++# In Galera, a concurrent transaction aborts in the face of ALTER
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++--connection node_1
++INSERT INTO t1 VALUES (2, 2);
++SELECT COUNT(*) = 2 FROM t1;
++
++--connection node_2
++INSERT INTO t1 VALUES (3, 3);
++SELECT COUNT(*) = 3 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_toi_lock_shared.test b/mysql-test/suite/galera/t/galera_toi_lock_shared.test
+new file mode 100644
+index 0000000..6857a0e
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_toi_lock_shared.test
+@@ -0,0 +1,23 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Ensure that ALTER LOCK=SHARED works under TOI. It is difficult to check that concurrent operations
++# will be possible, but at least we expect no hangs or deadlocks
++#
++
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++ALTER TABLE t1 ADD COLUMN f2 INTEGER, LOCK=SHARED;
++
++--connection node_1
++INSERT INTO t1 VALUES (2, 2);
++SELECT COUNT(*) = 2 FROM t1;
++
++--connection node_2
++INSERT INTO t1 VALUES (3, 3);
++SELECT COUNT(*) = 3 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_transaction_read_only.test b/mysql-test/suite/galera/t/galera_transaction_read_only.test
+new file mode 100644
+index 0000000..386d73f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_transaction_read_only.test
+@@ -0,0 +1,58 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Ensure that transactions that do not write anything do not cause the wsrep_last_committed counter to advance
++#
++
++# Empty transaction
++
++--connection node_1
++CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
++
++--connection node_2
++--let $wsrep_last_committed_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++
++--connection node_1
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++COMMIT;
++
++--connection node_2
++--let $wsrep_last_committed_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++--disable_query_log
++--eval SELECT $wsrep_last_committed_after = $wsrep_last_committed_before AS wsrep_last_committed_diff;
++--enable_query_log
++
++# START TRANSACTION READ ONLY
++
++--connection node_2
++--let $wsrep_last_committed_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++
++--connection node_1
++START TRANSACTION READ ONLY;
++SELECT COUNT(*) = 0 FROM t1;
++COMMIT;
++
++--connection node_2
++--let $wsrep_last_committed_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++--disable_query_log
++--eval SELECT $wsrep_last_committed_after = $wsrep_last_committed_before AS wsrep_last_committed_diff;
++--enable_query_log
++
++# Ordinary transaction with only SELECTs
++
++--connection node_1
++START TRANSACTION;
++SELECT COUNT(*) = 0 FROM t1;
++COMMIT;
++
++--connection node_2
++--let $wsrep_last_committed_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++--disable_query_log
++--eval SELECT $wsrep_last_committed_after = $wsrep_last_committed_before AS wsrep_last_committed_diff;
++--enable_query_log
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_transaction_replay.test b/mysql-test/suite/galera/t/galera_transaction_replay.test
+new file mode 100644
+index 0000000..d2c74ab
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_transaction_replay.test
+@@ -0,0 +1,63 @@
++#
++# This test tests the operation of transaction replay. If a potentially conflicting remote transaction arrives at
++# just the right time during the commit of a local transaction, the local transaction will be aborted and replayed.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/have_debug_sync.inc
++--source suite/galera/include/galera_have_debug_sync.inc
++
++--let $wsrep_local_replays_old = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays'`
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
++INSERT INTO t1 VALUES (1, 'a');
++INSERT INTO t1 VALUES (2, 'a');
++
++--connection node_1
++SET AUTOCOMMIT=ON;
++START TRANSACTION;
++
++UPDATE t1 SET f2 = 'b' WHERE f1 = 1;
++SELECT * FROM t1 WHERE f1 = 2 FOR UPDATE;
++
++# Block the commit
++--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
++SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_enter_sync';
++
++--connection node_1
++--send COMMIT;
++
++# Wait until commit is blocked
++--connection node_1a
++SET SESSION wsrep_sync_wait = 0;
++--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_debug_sync_waiters' AND VARIABLE_VALUE = 'apply_monitor_enter_sync'
++--source include/wait_condition.inc
++
++# Issue a conflicting update on node #2
++--connection node_2
++UPDATE t1 SET f2 = 'c' WHERE f1 = 2;
++
++# Unblock the commit
++--connection node_1a
++SET GLOBAL wsrep_provider_options = 'dbug=';
++SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_enter_sync';
++
++# Commit succeeds
++--connection node_1
++--reap
++
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'b';
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
++
++# wsrep_local_replays has increased by 1
++--let $wsrep_local_replays_new = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays'`
++--disable_query_log
++--eval SELECT $wsrep_local_replays_new - $wsrep_local_replays_old = 1 AS wsrep_local_replays;
++--enable_query_log
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'b';
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_truncate.test b/mysql-test/suite/galera/t/galera_truncate.test
+new file mode 100644
+index 0000000..79f9bad
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_truncate.test
+@@ -0,0 +1,57 @@
++#
++# Test TRUNCATE
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Simple case
++#
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_1
++SELECT COUNT(*) = 0 FROM t1;
++
++#
++# Table with no PK
++#
++
++--connection node_2
++CREATE TABLE t2 (f1 VARCHAR(255)) Engine=InnoDB;
++INSERT INTO t2 VALUES ('abc');
++
++--connection node_1
++TRUNCATE TABLE t2;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t2;
++
++#
++# Table with AUTO_INCREMENT. The AUTO_INCREMENT counter must be reset on all nodes
++#
++
++--connection node_1
++CREATE TABLE t3 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t3 VALUES (DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT);
++
++CREATE TABLE t4 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY) Engine=InnoDB AUTO_INCREMENT=1234;
++INSERT INTO t4 VALUES (DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT),(DEFAULT);
++
++TRUNCATE TABLE t3;
++TRUNCATE TABLE t4;
++
++--connection node_2
++SELECT AUTO_INCREMENT = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME IN ('t3', 't4');
++
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
++DROP TABLE t4;
+diff --git a/mysql-test/suite/galera/t/galera_truncate_temporary.test b/mysql-test/suite/galera/t/galera_truncate_temporary.test
+new file mode 100644
+index 0000000..3ad94eb
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_truncate_temporary.test
+@@ -0,0 +1,82 @@
++#
++# Test TRUNCATE on TEMPORARY tables. It should not be replicated
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TEMPORARY TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_2
++--error ER_NO_SUCH_TABLE
++SELECT * FROM t1;
++
++--connection node_1
++DROP TABLE t1;
++
++#
++# Test the case where a TEMPORARY table is masking an existing one
++#
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++CREATE TEMPORARY TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (2);
++
++SELECT f1 = 2 FROM t1;
++SELECT COUNT(*) = 1 FROM t1;
++
++TRUNCATE TABLE t1;
++
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT f1 = 1 FROM t1;
++
++--connection node_1
++
++DROP TABLE t1;
++SELECT COUNT(*) = 1 FROM t1;
++SELECT f1 = 1 FROM t1;
++
++TRUNCATE TABLE t1;
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_1
++DROP TABLE t1;
++
++#
++# Test the case where one node has a TEMPORARY table but the TRUNCATE arrives from another node
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++CREATE TEMPORARY TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++TRUNCATE TABLE t1;
++
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_1
++SELECT f1 = 2 FROM t1;
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
++
++SELECT COUNT(*) = 0 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_udf-master.opt b/mysql-test/suite/galera/t/galera_udf-master.opt
+new file mode 100644
+index 0000000..14dfe3e
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_udf-master.opt
+@@ -0,0 +1,2 @@
++$UDF_EXAMPLE_LIB_OPT
++--query_cache_type=1
+diff --git a/mysql-test/suite/galera/t/galera_unicode_identifiers.test b/mysql-test/suite/galera/t/galera_unicode_identifiers.test
+new file mode 100644
+index 0000000..2f255e9
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_unicode_identifiers.test
+@@ -0,0 +1,72 @@
++#
++# Test non-ascii table, column and index names
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_sync_wait_orig = (SELECT @@wsrep_sync_wait)
++SET GLOBAL wsrep_sync_wait = 7;
++
++--connection node_2
++SET GLOBAL wsrep_sync_wait = 7;
++
++--connection node_1
++
++# Spaces in identifiers
++
++CREATE DATABASE `database with space`;
++USE `database with space`;
++CREATE TABLE `table with space` (
++ `column with space` INTEGER AUTO_INCREMENT PRIMARY KEY,
++ `second column with space` INTEGER,
++ UNIQUE `index name with space` (`second column with space`)
++);
++INSERT INTO `table with space` VALUES (DEFAULT, 1);
++
++# Unicode identifiers
++
++CREATE DATABASE `база`;
++USE `база`;
++CREATE TABLE `таблица` (
++ `първа_колона` INTEGER PRIMARY KEY,
++ `втора_колона` INTEGER,
++ UNIQUE `индекс` (`втора_колона`)
++);
++
++INSERT INTO `таблица` VALUES (1, 1);
++
++# Without a PK
++
++CREATE DATABASE `втора база`;
++USE `втора база`;
++CREATE TABLE `втора таблица` (
++ `първа колона` INTEGER,
++ `втора колона` INTEGER,
++ KEY `първи индекс` (`първа колона`)
++);
++
++INSERT INTO `втора таблица` VALUES (1, 1);
++
++--connection node_2
++USE `database with space`;
++SELECT `second column with space` FROM `table with space`;
++
++USE `база`;
++SELECT * FROM `таблица`;
++
++USE `втора база`;
++SELECT `втора колона` FROM `втора таблица`;
++
++--eval SET GLOBAL wsrep_sync_wait = $wsrep_sync_wait_orig
++
++--connection node_1
++DROP TABLE `database with space`.`table with space`;
++DROP TABLE `база`.`таблица`;
++DROP TABLE `втора база`.`втора таблица`;
++
++DROP DATABASE `database with space`;
++DROP DATABASE `база`;
++DROP DATABASE `втора база`;
++--eval SET GLOBAL wsrep_sync_wait = $wsrep_sync_wait_orig
++
+diff --git a/mysql-test/suite/galera/t/galera_unicode_pk.test b/mysql-test/suite/galera/t/galera_unicode_pk.test
+new file mode 100644
+index 0000000..0d571f5
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_unicode_pk.test
+@@ -0,0 +1,64 @@
++#
++# Test non-ascii data in table where the PK is unicode
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (
++ f1 VARCHAR(255) PRIMARY KEY
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++
++INSERT INTO t1 VALUES ('текст');
++
++--connection node_2
++SELECT f1 = 'текст' FROM t1;
++
++#
++# Provoke a conflict
++#
++
++--connection node_1
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст2';
++
++--connection node_2
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++UPDATE t1 SET f1 = 'текст3';
++
++--connection node_1
++COMMIT;
++
++--connection node_2
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++SELECT f1 = 'текст2' FROM t1;
++SELECT f1 = 'текст2' FROM t1 WHERE f1 = 'текст2';
++
++#
++# Provoke a duplicate key error
++#
++
++--connection node_2
++START TRANSACTION;
++INSERT INTO t1 VALUES ('текст4');
++
++--connection node_1
++START TRANSACTION;
++INSERT INTO t1 VALUES ('текст4');
++
++--connection node_2
++COMMIT;
++
++--connection node_1
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++# Work around for mysql-wsrep#29 'Spurious deadlock error on a DROP TABLE'
++--error 0,ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_update_limit.test b/mysql-test/suite/galera/t/galera_update_limit.test
+new file mode 100644
+index 0000000..baacf2a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_update_limit.test
+@@ -0,0 +1,55 @@
++#
++# UPDATE LIMIT should not cause any issues with row-based Galera replication
++# regardless of the order in which the rows were updated
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# With a PK
++#
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER) Engine=InnoDB;
++INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t1 SELECT f1 FROM ten ORDER BY RAND();
++
++--connection node_2
++UPDATE IGNORE t1 SET f1 = FLOOR(1 + (RAND() * 10)) ORDER BY RAND() LIMIT 5;
++
++# Check that the sum of all elements and the max element are identical across nodes
++# as this will indicate that the same UPDATE was applied to both nodes
++
++--let $sum_rows = `SELECT SUM(f1) FROM t1`
++--let $max_row = `SELECT MAX(f1) FROM t1`
++
++--connection node_1
++--disable_query_log
++--eval SELECT (SELECT SUM(f1) FROM t1) = $sum_rows AS sum_matches;
++--eval SELECT f1 = $max_row AS max_matches FROM t1 WHERE f1 = $max_row;
++--enable_query_log
++
++DROP TABLE t1;
++
++#
++# Without a PK
++#
++
++CREATE TABLE t2 (f1 INTEGER) Engine=InnoDB;
++INSERT INTO t2 SELECT f1 FROM ten ORDER BY RAND();
++
++--connection node_2
++UPDATE IGNORE t2 SET f1 = FLOOR(1 + (RAND() * 10)) ORDER BY RAND() LIMIT 5;
++
++--let $sum_rows = `SELECT SUM(f1) FROM t2`
++
++--connection node_1
++--disable_query_log
++--eval SELECT (SELECT SUM(f1) FROM t2) = $sum_rows AS sum_matches;
++--enable_query_log
++
++DROP TABLE t2;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_v1_row_events-master.opt b/mysql-test/suite/galera/t/galera_v1_row_events-master.opt
+new file mode 100644
+index 0000000..dc82542
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_v1_row_events-master.opt
+@@ -0,0 +1 @@
++--log-bin-use-v1-row-events=1
+diff --git a/mysql-test/suite/galera/t/galera_v1_row_events.test b/mysql-test/suite/galera/t/galera_v1_row_events.test
+new file mode 100644
+index 0000000..0c0a044
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_v1_row_events.test
+@@ -0,0 +1,21 @@
++#
++# Test that Galera continues to run even with --log-bin-use-v1-row-events=1
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_1
++UPDATE t1 SET f1 = 2 WHERE f1 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_auto_inc_control_off.test b/mysql-test/suite/galera/t/galera_var_auto_inc_control_off.test
+new file mode 100644
+index 0000000..c0bbe5a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_auto_inc_control_off.test
+@@ -0,0 +1,105 @@
++#
++# Test wsrep_auto_increment_control = OFF
++# We issue two concurrent INSERTs and one will fail with a deadlock error
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $auto_increment_control_orig = `SELECT @@wsrep_auto_increment_control`
++
++#
++# Preserve existing variable values
++#
++
++--connection node_1
++--let $auto_increment_increment_node1 = `SELECT @@auto_increment_increment`
++--let $auto_increment_offset_node1 = `SELECT @@auto_increment_offset`
++
++# Restore stock MySQL defaults
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++
++#Open a fresh connection to node_1 so that the variables above take effect
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++
++--connection node_2
++--let $auto_increment_increment_node2 = `SELECT @@auto_increment_increment`
++--let $auto_increment_offset_node2 = `SELECT @@auto_increment_offset`
++
++SET GLOBAL wsrep_auto_increment_control = OFF;
++SET GLOBAL auto_increment_increment = 1;
++SET GLOBAL auto_increment_offset = 1;
++
++#Open a fresh connection to node_2
++--let $galera_connection_name = node_2a
++--let $galera_server_number = 2
++--source include/galera_connect.inc
++
++--connection node_1a
++SELECT @@auto_increment_increment = 1;
++SELECT @@auto_increment_offset = 1;
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, node VARCHAR(10)) ENGINE=InnoDB;
++
++#
++# We expect that SHOW CREATE TABLE on both nodes will return identical values
++#
++
++SHOW CREATE TABLE t1;
++
++--connection node_2a
++
++SHOW CREATE TABLE t1;
++
++--connection node_1a
++SELECT @@auto_increment_increment = 1;
++SELECT @@auto_increment_offset = 1;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (node) VALUES ('node1');
++SELECT f1 FROM t1;
++
++--connection node_2a
++SELECT @@auto_increment_increment = 1;
++SELECT @@auto_increment_offset = 1;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 (node) VALUES ('node2');
++SELECT f1 FROM t1;
++
++--connection node_1a
++COMMIT;
++
++--connection node_2a
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++--connection node_1a
++SELECT * FROM t1;
++
++--connection node_2a
++SELECT * FROM t1;
++
++#
++# Restore all variables as they were
++#
++
++--disable_query_log
++
++--connection node_1
++--eval SET GLOBAL wsrep_auto_increment_control = $auto_increment_control_orig
++--eval SET GLOBAL auto_increment_increment = $auto_increment_increment_node1
++--eval SET GLOBAL auto_increment_offset = $auto_increment_offset_node1
++
++--connection node_2
++--eval SET GLOBAL wsrep_auto_increment_control = $auto_increment_control_orig
++--eval SET GLOBAL auto_increment_increment = $auto_increment_increment_node2
++--eval SET GLOBAL auto_increment_offset = $auto_increment_offset_node2
++
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_auto_inc_control_on.test b/mysql-test/suite/galera/t/galera_var_auto_inc_control_on.test
+new file mode 100644
+index 0000000..59f2615
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_auto_inc_control_on.test
+@@ -0,0 +1,53 @@
++#
++# Test the operation of wsrep_auto_increment_control = ON
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, node VARCHAR(10)) ENGINE=InnoDB;
++
++# auto_increment_increment is equal to the number of nodes
++# auto_increment_offset is equal to the ID of the node
++
++SELECT @@auto_increment_increment = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size');
++SELECT @@auto_increment_offset = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index') + 1;
++
++# Expect no conflicts
++--send INSERT INTO t1 VALUES (DEFAULT, 'node1');
++
++--connection node_2
++--send INSERT INTO t1 VALUES (DEFAULT, 'node2');
++
++--connection node_1
++--reap
++
++--connection node_2
++--reap
++
++SELECT @@auto_increment_increment = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size');
++SELECT @@auto_increment_offset = (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index') + 1;
++
++# Expect no conflicts
++--send INSERT INTO t1 VALUES (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2'), (DEFAULT, 'node2');
++
++--connection node_1
++--send INSERT INTO t1 VALUES (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1'), (DEFAULT, 'node1');
++
++--connection node_2
++--reap
++
++--connection node_1
++--reap
++
++--connection node_2
++SELECT COUNT(*) = 22 FROM t1;
++SELECT COUNT(DISTINCT f1) = 22 FROM t1;
++
++--connection node_1
++SELECT COUNT(*) = 22 FROM t1;
++SELECT COUNT(DISTINCT f1) = 22 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_certify_nonPK_off.test b/mysql-test/suite/galera/t/galera_var_certify_nonPK_off.test
+new file mode 100644
+index 0000000..f7967da
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_certify_nonPK_off.test
+@@ -0,0 +1,39 @@
++#
++# Test wsrep_certify_nonPK = OFF
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_certify_nonPK_orig = `SELECT @@wsrep_certify_nonPK`
++SET GLOBAL wsrep_certify_nonPK = OFF;
++
++--connection node_2
++SET GLOBAL wsrep_certify_nonPK = OFF;
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB /* Table has no primary key */;
++CREATE TABLE t2 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++# All DML without a PK is rejected with an error
++--error ER_LOCK_DEADLOCK
++INSERT INTO t1 VALUES (1), (2);
++
++# DML with a PK is allowed to proceed
++INSERT INTO t2 VALUES (1), (2);
++UPDATE t2 SET f1 = 3 WHERE f1 = 1;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++SELECT COUNT(*) = 2 FROM t2;
++SELECT COUNT(*) = 1 FROM t2 WHERE f1 = 3;
++
++--connection node_1
++--eval SET GLOBAL wsrep_certify_nonPK = $wsrep_certify_nonPK_orig
++
++--connection node_2
++--eval SET GLOBAL wsrep_certify_nonPK = $wsrep_certify_nonPK_orig
++
++DROP TABLE t1;
++DROP TABLE t2;
++
+diff --git a/mysql-test/suite/galera/t/galera_var_cluster_address.test b/mysql-test/suite/galera/t/galera_var_cluster_address.test
+new file mode 100644
+index 0000000..609c62c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_cluster_address.test
+@@ -0,0 +1,105 @@
++#
++# Check the handling of @@wsrep_cluster_address
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++#
++# Set to invalid value
++#
++
++--connection node_1
++--let $wsrep_cluster_address_node1 = `SELECT @@wsrep_cluster_address`
++SET GLOBAL wsrep_cluster_address = 'foo://';
++
++# With wsrep_sync_wait, this returns an error
++--error ER_LOCK_WAIT_TIMEOUT
++SHOW STATUS;
++
++SET SESSION wsrep_sync_wait=0;
++
++--error ER_UNKNOWN_COM_ERROR
++SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS;
++
++# Must return 'OFF'
++SHOW STATUS LIKE 'wsrep_ready';
++
++# Must return 'Non-primary'
++SHOW STATUS LIKE 'wsrep_cluster_status';
++
++# Must return 0 = 'Initialized'
++SHOW STATUS LIKE 'wsrep_local_state';
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++
++--connection node_2
++--sleep 1
++# Node #2 thinks that it is now part of a single-node primary cluster
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++
++#
++# Reset everything as it was
++#
++
++--connection node_1
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_node1';
++--enable_query_log
++
++--connection node_2
++SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++
++--source include/wait_until_connected_again.inc
++
++--connection node_1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++#
++# Set to invalid host
++#
++
++--connection node_1
++SET GLOBAL wsrep_cluster_address = 'gcomm://192.0.2.1';
++
++--error ER_UNKNOWN_COM_ERROR
++SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS;
++
++# Must return 'OFF'
++SHOW STATUS LIKE 'wsrep_ready';
++
++# Must return 'Non-primary'
++SHOW STATUS LIKE 'wsrep_cluster_status';
++
++# Must return 0 = 'Initialized'
++SHOW STATUS LIKE 'wsrep_local_state';
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++
++#
++# Reset everything as it was
++#
++
++--connection node_1
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_node1';
++--enable_query_log
++
++--connection node_2
++SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++--sleep 1
++
++--connection node_1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++CALL mtr.add_suppression("Backend not supported: foo");
++CALL mtr.add_suppression("Failed to initialize backend using 'foo");
++CALL mtr.add_suppression("Failed to open channel 'my_wsrep_cluster' at 'foo");
++CALL mtr.add_suppression("gcs connect failed: Socket type not supported");
++CALL mtr.add_suppression("wsrep::connect\\(\\) failed: 7");
++CALL mtr.add_suppression("gcs_caused\\(\\) returned -103 \\(Software caused connection abort\\)");
++CALL mtr.add_suppression("failed to open gcomm backend connection: 110: failed to reach primary view: 110");
++CALL mtr.add_suppression("Failed to open backend connection: -110 \\(Connection timed out\\)");
++CALL mtr.add_suppression("Failed to open channel 'my_wsrep_cluster' at 'gcomm://192\\.0\\.2\\.1': -110 \\(Connection timed out\\)");
++CALL mtr.add_suppression("gcs connect failed: Connection timed out");
+diff --git a/mysql-test/suite/galera/t/galera_var_desync_on.test b/mysql-test/suite/galera/t/galera_var_desync_on.test
+new file mode 100644
+index 0000000..fb0fb9f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_desync_on.test
+@@ -0,0 +1,58 @@
++#
++# Test wsrep_desync = ON . Node should temporarily not participate in flow control
++# so even if fc_limit has been reached, the master should be able to continue to
++# commit transactions.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--let $wsrep_provider_options_orig = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'gcs.fc_limit=1';
++SET GLOBAL wsrep_desync = TRUE;
++
++# Block the slave applier thread
++FLUSH TABLES WITH READ LOCK;
++
++--connection node_1
++
++# Without wsrep_desync = TRUE it would not be possible to perform 10 inserts on the master with gcs.fc_limit=1
++INSERT INTO t1 VALUES (2);
++INSERT INTO t1 VALUES (3);
++INSERT INTO t1 VALUES (4);
++INSERT INTO t1 VALUES (5);
++INSERT INTO t1 VALUES (6);
++INSERT INTO t1 VALUES (7);
++INSERT INTO t1 VALUES (8);
++INSERT INTO t1 VALUES (9);
++INSERT INTO t1 VALUES (10);
++--sleep 1
++
++--connection node_2
++SET SESSION wsrep_sync_wait = 0;
++# No updates have arrived after the FLUSH TABLES
++SELECT COUNT(*) = 1 FROM t1;
++
++# Resync the slave
++SET GLOBAL wsrep_desync = FALSE;
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_orig';
++--enable_query_log
++UNLOCK TABLES;
++
++SET SESSION wsrep_sync_wait = 1;
++# The slave is now fully caught up
++SELECT COUNT(*) = 10 FROM t1;
++
++--connection node_1
++INSERT INTO t1 VALUES (11);
++
++--connection node_2
++# Replication continues normally
++SELECT COUNT(*) = 11 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_fkchecks.test b/mysql-test/suite/galera/t/galera_var_fkchecks.test
+new file mode 100644
+index 0000000..c771b50
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_fkchecks.test
+@@ -0,0 +1,40 @@
++#
++# Test the operation on the foreign_key_checks variable
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE parent (
++ id INT PRIMARY KEY,
++ KEY (id)
++) ENGINE=InnoDB;
++
++CREATE TABLE child (
++ id INT PRIMARY KEY,
++ parent_id INT,
++ FOREIGN KEY (parent_id)
++ REFERENCES parent(id)
++) ENGINE=InnoDB;
++
++INSERT INTO parent VALUES (1);
++INSERT INTO child VALUES (1,1);
++
++SET SESSION foreign_key_checks = 0;
++
++INSERT INTO child VALUES (2,2);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM child WHERE id = 2;
++
++--error ER_NO_REFERENCED_ROW_2
++INSERT INTO child VALUES (3,3);
++
++SET SESSION foreign_key_checks = 0;
++DELETE FROM parent;
++
++--connection node_1
++SELECT COUNT(*) = 0 FROM parent;
++
++DROP TABLE child;
++DROP TABLE parent;
+diff --git a/mysql-test/suite/galera/t/galera_var_innodb_disallow_writes.test b/mysql-test/suite/galera/t/galera_var_innodb_disallow_writes.test
+new file mode 100644
+index 0000000..c08483b
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_innodb_disallow_writes.test
+@@ -0,0 +1,33 @@
++#
++# This test checks that innodb_disallow_writes works as expected
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++# Open a separate connection to be used to run SHOW PROCESSLIST
++--let $galera_connection_name = node_1a
++--let $galera_server_number = 1
++--source include/galera_connect.inc
++--connection node_1a
++SET SESSION wsrep_sync_wait = 0;
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++SET GLOBAL innodb_disallow_writes=ON;
++--send INSERT INTO t1 VALUES (1);
++
++--connection node_1a
++let $wait_condition = SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE INFO = 'INSERT INTO t1 VALUES (1)' AND State = 'query end';
++--source include/wait_condition.inc
++
++SET GLOBAL innodb_disallow_writes=OFF;
++
++--connection node_1
++--reap
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_load_data_splitting.test b/mysql-test/suite/galera/t/galera_var_load_data_splitting.test
+new file mode 100644
+index 0000000..0783dc8
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_load_data_splitting.test
+@@ -0,0 +1,38 @@
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_load_data_splitting_orig = `SELECT @@wsrep_load_data_splitting`
++
++# Create a file for LOAD DATA with 95K entries
++--perl
++open(FILE, ">", "$ENV{'MYSQLTEST_VARDIR'}/tmp/galera_var_load_data_splitting.csv") or die;
++foreach my $i (1..95000) {
++ print FILE "$i\n";
++}
++EOF
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++# Record wsrep_last_committed as it was before LOAD DATA
++--connection node_2
++--let $wsrep_last_committed_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++
++SET GLOBAL wsrep_load_data_splitting = TRUE;
++--disable_query_log
++--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/galera_var_load_data_splitting.csv' INTO TABLE t1;
++--enable_query_log
++
++--connection node_2
++--let $wsrep_last_committed_after = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
++
++SELECT COUNT(*) = 95000 FROM t1;
++
++# LOAD-ing 95K rows causes 10 commits to be registered
++--disable_query_log
++--eval SELECT $wsrep_last_committed_after = $wsrep_last_committed_before + 10 AS wsrep_last_committed_diff;
++--enable_query_log
++
++--connection node_1
++--eval SET GLOBAL wsrep_load_data_splitting = $wsrep_load_data_splitting_orig;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_max_ws_size.test b/mysql-test/suite/galera/t/galera_var_max_ws_size.test
+new file mode 100644
+index 0000000..bd98bab
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_max_ws_size.test
+@@ -0,0 +1,26 @@
++#
++# This test sets wsrep_max_ws_size to a very low value and checks that the transaction is rejected
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++call mtr.add_suppression('WSREP: transaction size limit.*');
++call mtr.add_suppression('WSREP: rbr write fail.*');
++call mtr.add_suppression('WSREP: Maximum writeset size exceeded by.*');
++call mtr.add_suppression('WSREP: transaction size exceeded.*');
++
++CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 VARCHAR(1024)) Engine=InnoDB;
++
++--let $wsrep_max_ws_size_orig = `SELECT @@wsrep_max_ws_size`
++SET GLOBAL wsrep_max_ws_size = 1024;
++
++--error ER_ERROR_DURING_COMMIT
++INSERT INTO t1 VALUES (DEFAULT, REPEAT('X', 1024));
++SELECT COUNT(*) = 0 FROM t1;
++
++--disable_query_log
++--eval SET GLOBAL wsrep_max_ws_size = $wsrep_max_ws_size_orig
++--enable_query_log
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_mysql_replication_bundle.test b/mysql-test/suite/galera/t/galera_var_mysql_replication_bundle.test
+new file mode 100644
+index 0000000..642d939
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_mysql_replication_bundle.test
+@@ -0,0 +1,30 @@
++#
++# Simple test for the operation on the wsrep-mysql-replication-bundle
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_mysql_replication_bundle_orig = `SELECT @@wsrep_mysql_replication_bundle`
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=InnoDB;
++
++SET GLOBAL wsrep_mysql_replication_bundle = 2;
++
++--connection node_1
++# This statement will not be replicated immediately
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_1
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++
++--connection node_1
++--eval SET GLOBAL wsrep_mysql_replication_bundle = $wsrep_mysql_replication_bundle_orig
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_notify_cmd-master.opt b/mysql-test/suite/galera/t/galera_var_notify_cmd-master.opt
+new file mode 100644
+index 0000000..70dfc98
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_notify_cmd-master.opt
+@@ -0,0 +1 @@
++--wsrep_notify_cmd=$MYSQL_TEST_DIR/std_data/wsrep_notify.sh --wsrep-sync-wait=0
+diff --git a/mysql-test/suite/galera/t/galera_var_notify_cmd.test b/mysql-test/suite/galera/t/galera_var_notify_cmd.test
+new file mode 100644
+index 0000000..4fea69f
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_notify_cmd.test
+@@ -0,0 +1,14 @@
++#
++# Test wsrep_notify_cmd. We use a version of the support-files/wsrep_notify.sh script that writes
++# notifications into a table.
++#
++
++--source include/have_innodb.inc
++--source include/galera_cluster.inc
++
++--connection node_1
++SELECT COUNT(DISTINCT uuid) = 2 FROM mtr_wsrep_notify.membership;
++SELECT MAX(size) = 2 FROM mtr_wsrep_notify.status;
++SELECT COUNT(DISTINCT idx) = 2 FROM mtr_wsrep_notify.status;
++
++DROP SCHEMA mtr_wsrep_notify;
+diff --git a/mysql-test/suite/galera/t/galera_var_replicate_myisam_off.test b/mysql-test/suite/galera/t/galera_var_replicate_myisam_off.test
+new file mode 100644
+index 0000000..a981128
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_replicate_myisam_off.test
+@@ -0,0 +1,21 @@
++#
++# Simple test for wsrep-replicate-myisam = FALSE
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_replicate_myisam_orig = `SELECT @@wsrep_replicate_myisam`
++
++SET GLOBAL wsrep_replicate_myisam = FALSE;
++
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=MyISAM;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_1
++--eval SET GLOBAL wsrep_replicate_myisam = $wsrep_replicate_myisam_orig
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_var_replicate_myisam_on.test b/mysql-test/suite/galera/t/galera_var_replicate_myisam_on.test
+new file mode 100644
+index 0000000..a09ba2e
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_replicate_myisam_on.test
+@@ -0,0 +1,138 @@
++#
++# Simple test for wsrep-replicate-myisam = ON
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_replicate_myisam_orig = `SELECT @@wsrep_replicate_myisam`
++
++--connection node_1
++SET GLOBAL wsrep_replicate_myisam = TRUE;
++--connection node_2
++SET GLOBAL wsrep_replicate_myisam = TRUE;
++
++#
++# Simple INSERT
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=MyISAM;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t1 VALUES (2), (3);
++INSERT INTO t1 SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL;
++
++--connection node_2
++SELECT COUNT(*) = 5 FROM t1;
++
++DROP TABLE t1;
++
++#
++# REPLACE
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 VARCHAR(100)) ENGINE=MyISAM;
++INSERT INTO t1 VALUES (1, 'abc'),(2,'abc'), (3, 'xxx');
++REPLACE INTO t1 VALUES (1, 'klm'), (2,'xyz');
++REPLACE INTO t1 SELECT 3, 'yyy' FROM DUAL;
++
++--connection node_2
++SELECT COUNT(*) = 3 FROM t1;
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 1 AND f2 = 'klm';
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 2 AND f2 = 'xyz';
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 3 AND f2 = 'yyy';
++
++#
++# UPDATE
++#
++
++--connection node_1
++UPDATE t1 SET f2 = 'zzz' WHERE f2 = 'yyy';
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'zzz';
++
++#
++# DELETE
++#
++
++--connection node_1
++DELETE FROM t1 WHERE f2 = 'zzz';
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1 WHERE f2 = 'zzz';
++
++#
++# TRUNCATE
++#
++
++--connection node_1
++TRUNCATE TABLE t1;
++
++--connection node_2
++SELECT COUNT(*) = 0 FROM t1;
++DROP TABLE t1;
++
++#
++# Transaction
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=MyISAM;
++CREATE TABLE t2 (f1 INTEGER) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++COMMIT;
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++SELECT COUNT(*) = 1 FROM t2;
++
++#
++# Transaction rollback
++#
++
++--connection node_1
++START TRANSACTION;
++INSERT INTO t1 VALUES (2);
++INSERT INTO t2 VALUES (2);
++ROLLBACK;
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++SELECT COUNT(*) = 1 FROM t2;
++
++DROP TABLE t1;
++DROP TABLE t2;
++
++#
++# Transaction conflict
++#
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=MyISAM;
++CREATE TABLE t2 (f2 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++--connection node_2
++# The MyISAM update is replicated immediately, so a duplicate key error happens even before the COMMIT
++--error ER_DUP_ENTRY
++INSERT INTO t1 VALUES (1);
++
++--connection node_1
++COMMIT;
++
++DROP TABLE t1;
++DROP TABLE t2;
++
++--connection node_1
++--eval SET GLOBAL wsrep_replicate_myisam = $wsrep_replicate_myisam_orig
++
++--connection node_2
++--eval SET GLOBAL wsrep_replicate_myisam = $wsrep_replicate_myisam_orig
+diff --git a/mysql-test/suite/galera/t/galera_var_slave_threads.test b/mysql-test/suite/galera/t/galera_var_slave_threads.test
+new file mode 100644
+index 0000000..a83924c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_slave_threads.test
+@@ -0,0 +1,70 @@
++#
++# This tests the very basic operations around wsrep-slave-threads
++# More complex scenarios will be tested separately in the context of
++# parallel replication
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
++
++--connection node_1
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=InnoDB;
++CREATE TABLE t2 (f1 INT AUTO_INCREMENT PRIMARY KEY) Engine=InnoDB;
++
++--connection node_2
++
++# Setting wsrep_slave_threads to zero triggers a warning
++SET GLOBAL wsrep_slave_threads = 0;
++SHOW WARNINGS;
++SELECT @@wsrep_slave_threads = 1;
++
++SET GLOBAL wsrep_slave_threads = 1;
++# There is a separate wsrep_aborter thread at all times
++SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%wsrep aborter%';
++
++#
++# Increase the number of slave threads. The change takes effect immediately
++#
++
++SET GLOBAL wsrep_slave_threads = 64;
++--sleep 0.5
++
++--connection node_1
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++SELECT COUNT(*) = @@wsrep_slave_threads + 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%wsrep aborter%';
++
++#
++# Reduce the number of slave threads. The change is not immediate -- a thread will only exit after a replication event
++#
++
++SET GLOBAL wsrep_slave_threads = 1;
++
++--connection node_1
++
++# Generate 64 replication events
++--let $count = 64
++while ($count)
++{
++ INSERT INTO t2 VALUES (DEFAULT);
++ --dec $count
++}
++
++--connection node_2
++SELECT COUNT(*) = 64 FROM t2;
++
++SELECT COUNT(*) = @@wsrep_slave_threads + 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
++SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%wsrep aborter%';
++
++
++--eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/galera_var_sync_wait.test b/mysql-test/suite/galera/t/galera_var_sync_wait.test
+new file mode 100644
+index 0000000..935c271
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_sync_wait.test
+@@ -0,0 +1,43 @@
++#
++# Simple test for the various levels of wsrep-sync-wait
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_sync_wait_orig = `SELECT @@wsrep_sync_wait`
++
++--connection node_1
++CREATE TABLE t1 (f1 INT PRIMARY KEY) Engine=InnoDB;
++
++--connection node_2
++SET GLOBAL wsrep_sync_wait = 1;
++# Those statements should see the table
++SHOW TABLES LIKE '%t1';
++SELECT COUNT(*) = 0 FROM t1;
++
++--connection node_1
++CREATE TABLE t2 (f1 INT PRIMARY KEY) Engine=InnoDB;
++
++--connection node_2
++SET GLOBAL wsrep_sync_wait = 4;
++# This insert should see the table and succeed
++INSERT INTO t2 VALUES (1);
++
++--connection node_1
++CREATE TABLE t3 (f1 INT PRIMARY KEY) Engine=InnoDB;
++INSERT INTO t3 VALUES (1);
++
++--connection node_2
++SET GLOBAL wsrep_sync_wait = 2;
++# This statement should see and update 1 row
++--enable_info
++UPDATE t3 SET f1 = 2;
++--disable_info
++
++--connection node_2
++--eval SET GLOBAL wsrep_sync_wait = $wsrep_sync_wait_orig
++
++DROP TABLE t1;
++DROP TABLE t2;
++DROP TABLE t3;
+diff --git a/mysql-test/suite/galera/t/galera_var_wsrep_on_off.test b/mysql-test/suite/galera/t/galera_var_wsrep_on_off.test
+new file mode 100644
+index 0000000..783b787
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_var_wsrep_on_off.test
+@@ -0,0 +1,32 @@
++#
++# Test wsrep_on = OFF. Some events will not be replicated
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET SESSION wsrep_on = FALSE;
++
++# This statement will not be replicated
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++--connection node_1
++SET GLOBAL wsrep_on = TRUE;
++INSERT INTO t1 VALUES (3);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++
++# Middle insert is not replicated
++SELECT COUNT(*) = 0 FROM t1 WHERE f1 = 2;
++
++# Final insert is replicated
++SELECT COUNT(*) = 1 FROM t1 WHERE f1 = 3;
++
++DROP TABLE t1;
++
+diff --git a/mysql-test/suite/galera/t/galera_wan.cnf b/mysql-test/suite/galera/t/galera_wan.cnf
+new file mode 100644
+index 0000000..37ac58a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_wan.cnf
+@@ -0,0 +1,14 @@
++!include ../galera_4nodes.cnf
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;gmcast.segment=1'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;gmcast.segment=1'
++
++[mysqld.3]
++wsrep_provider_options='base_port=@mysqld.3.#galera_port;gmcast.segment=2'
++
++[mysqld.4]
++wsrep_provider_options='base_port=@mysqld.4.#galera_port;gmcast.segment=3'
++
+diff --git a/mysql-test/suite/galera/t/galera_wan.test b/mysql-test/suite/galera/t/galera_wan.test
+new file mode 100644
+index 0000000..a8fd351
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_wan.test
+@@ -0,0 +1,30 @@
++#
++# Test WAN replication and the gmcast.segment functionality.
++# The galera_wan.cnf file partitions 4 Galera nodes into 3 WAN segments
++#
++# We can not test any of the actual WAN optimizations from inside MTR and no
++# status variables are provided. So we only check that simple replication works.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER);
++
++--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
++--connection node_3
++INSERT INTO t1 VALUES (1);
++CALL mtr.add_suppression("There are no nodes in the same segment that will ever be able to become donors, yet there is a suitable donor outside");
++
++--connect node_4, 127.0.0.1, root, , test, $NODE_MYPORT_4
++--connection node_4
++SELECT VARIABLE_VALUE LIKE '%gmcast.segment = 3%' FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME = 'wsrep_provider_options';
++
++SELECT COUNT(*) = 1 FROM t1;
++
++DROP TABLE t1;
++
++CALL mtr.add_suppression("There are no nodes in the same segment that will ever be able to become donors, yet there is a suitable donor outside");
+diff --git a/mysql-test/suite/galera/t/galera_wsrep_desync_wsrep_on.test b/mysql-test/suite/galera/t/galera_wsrep_desync_wsrep_on.test
+new file mode 100644
+index 0000000..3c7988a
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_wsrep_desync_wsrep_on.test
+@@ -0,0 +1,57 @@
++#
++# Test the wsrep_desync + wsrep_on method for schema upgrades discussed at
++# http://www.slideshare.net/Severalnines/schema-upgrades-codershippresodec2013 , slide 30
++#
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE ten (f1 INTEGER);
++INSERT INTO ten VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
++
++CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
++
++# Insert some values before the ALTER
++INSERT INTO t1 (f1) SELECT 000000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++--connection node_2
++SET GLOBAL wsrep_desync = TRUE;
++SET SESSION wsrep_on = FALSE;
++
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++
++SET SESSION wsrep_on = TRUE;
++SET GLOBAL wsrep_desync = FALSE;
++
++# Insert even more data after the ALTER has completed
++INSERT INTO t1 (f1) SELECT 100000 + (10000 * a1.f1) + (1000 * a2.f1) + (100 * a3.f1) + (10 * a4.f1) + a5.f1 FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4, ten AS a5;
++
++SELECT COUNT(*) = 200000 FROM t1;
++SELECT MAX(f1) = 199999 FROM t1;
++
++--connection node_1
++SELECT COUNT(*) = 200000 FROM t1;
++SELECT MAX(f1) = 199999 FROM t1;
++
++SET GLOBAL wsrep_desync = TRUE;
++SET SESSION wsrep_on = FALSE;
++
++ALTER TABLE t1 ADD PRIMARY KEY (f1);
++
++SET SESSION wsrep_on = TRUE;
++SET GLOBAL wsrep_desync = FALSE;
++
++# Insert some conflicting values after the ALTER has been applied on all nodes.
++
++--connection node_2
++--error ER_DUP_ENTRY
++INSERT INTO t1 (f1) VALUES (1);
++
++--connection node_1
++--error ER_DUP_ENTRY
++INSERT INTO t1 (f1) VALUES (100);
++
++DROP TABLE t1;
++DROP TABLE ten;
+diff --git a/mysql-test/suite/galera/t/galera_wsrep_new_cluster-master.opt b/mysql-test/suite/galera/t/galera_wsrep_new_cluster-master.opt
+new file mode 100644
+index 0000000..c31150c
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_wsrep_new_cluster-master.opt
+@@ -0,0 +1 @@
++--wsrep-new-cluster
+diff --git a/mysql-test/suite/galera/t/galera_wsrep_new_cluster.test b/mysql-test/suite/galera/t/galera_wsrep_new_cluster.test
+new file mode 100644
+index 0000000..6ba8ce7
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_wsrep_new_cluster.test
+@@ -0,0 +1,24 @@
++#
++# Test the --wsrep-new-cluster option by putting it in the galera_wsrep_new_cluster-master.opt file
++#
++# In MTR, running two nodes, the result is two separate clusters of size 1
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++--connection node_2
++
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_index';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
+diff --git a/mysql-test/suite/galera/t/galera_wsrep_provider_unset_set.test b/mysql-test/suite/galera/t/galera_wsrep_provider_unset_set.test
+new file mode 100644
+index 0000000..fe4c358
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_wsrep_provider_unset_set.test
+@@ -0,0 +1,41 @@
++#
++# Test that wsrep_provider can be unset and then set back to its original value
++# and replication will continue except for any updates made while the value was 'none'
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++--let $wsrep_provider_orig = `SELECT @@wsrep_provider`
++--let $wsrep_cluster_address_orig = `SELECT @@wsrep_cluster_address`
++
++SET GLOBAL wsrep_provider='none';
++INSERT INTO t1 VALUES (2);
++
++--connection node_1
++INSERT INTO t1 VALUES (3);
++
++--connection node_2
++--disable_query_log
++--eval SET GLOBAL wsrep_provider = '$wsrep_provider_orig';
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++
++--source include/wait_until_connected_again.inc
++--source include/galera_wait_ready.inc
++
++INSERT INTO t1 VALUES (4);
++
++# Node #2 has all the inserts
++SELECT COUNT(*) = 4 FROM t1;
++
++--connection node_1
++# Node #1 is missing the insert made while Node #2 was not replicated
++SELECT COUNT(*) = 3 FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/galera_zero_length_column.test b/mysql-test/suite/galera/t/galera_zero_length_column.test
+new file mode 100644
+index 0000000..6ae81a8
+--- /dev/null
++++ b/mysql-test/suite/galera/t/galera_zero_length_column.test
+@@ -0,0 +1,41 @@
++#
++# Test columns with size zero. This is known to have tripped other storage engines.
++# Keys are not allowed on such columns
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY , f2 VARCHAR(0)) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 VARCHAR(0)) ENGINE=InnoDB;
++
++
++INSERT INTO t1 VALUES (1, NULL);
++INSERT INTO t1 VALUES (2, '');
++
++INSERT INTO t2 VALUES (NULL);
++INSERT INTO t2 VALUES ('');
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++SELECT f2 IS NULL FROM t1 WHERE f1 = 1;
++SELECT f2 = '' FROM t1 WHERE f1 = 2;
++
++SELECT COUNT(*) = 2 FROM t2;
++SELECT f1 IS NULL FROM t2 WHERE f1 IS NULL;
++SELECT f1 = '' FROM t2 WHERE f1 IS NOT NULL;
++
++UPDATE t1 SET f2 = '' WHERE f1 = 1;
++UPDATE t1 SET f2 = NULL WHERE f1 = 2;
++
++UPDATE t2 SET f1 = '' WHERE f1 IS NULL;
++
++--connection node_1
++SELECT f2 = '' FROM t1 WHERE f1 = 1;
++SELECT f2 IS NULL FROM t1 WHERE f1 = 2;
++
++SELECT COUNT(*) = 2 FROM t2 WHERE f1 = '';
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera/t/lp1276424.test b/mysql-test/suite/galera/t/lp1276424.test
+new file mode 100644
+index 0000000..a37e950
+--- /dev/null
++++ b/mysql-test/suite/galera/t/lp1276424.test
+@@ -0,0 +1,17 @@
++#
++# LP:1276424 Deadlock with insertion of NULL unique ke
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE t1 (f1 INT DEFAULT NULL, UNIQUE KEY i1 (f1)) ENGINE=InnoDB;
++
++INSERT INTO t1 VALUES (NULL);
++INSERT INTO t1 VALUES (NULL);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++SELECT f1 IS NULL FROM t1;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera/t/lp1347768.test b/mysql-test/suite/galera/t/lp1347768.test
+new file mode 100644
+index 0000000..96d4286
+--- /dev/null
++++ b/mysql-test/suite/galera/t/lp1347768.test
+@@ -0,0 +1,24 @@
++#
++# LP:1347768 Assertion failure in file ha_innodb.cc line 6759
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++CREATE TABLE `r8kmb_redirect_links` (
++ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
++ `old_url` varchar(255) DEFAULT NULL,
++ `new_url` varchar(255) NOT NULL,
++ `referer` varchar(150) NOT NULL,
++ `comment` varchar(255) NOT NULL,
++ `published` tinyint(4) NOT NULL,
++ `created_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
++ `modified_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
++ PRIMARY KEY (`id`),
++ UNIQUE KEY `idx_link_old` (`old_url`),
++ KEY `idx_link_modifed` (`modified_date`)
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++
++INSERT INTO r8kmb_redirect_links VALUES (550,'http://mysite.com/images/download/ßуñûічýøù_ôþóþòір_þфõÑ.doc','','','',0,'2013-07-15 14:29:42','0000-00-00 00:00:00');
++
++DROP TABLE r8kmb_redirect_links;
+diff --git a/mysql-test/suite/galera/t/lp959512.test b/mysql-test/suite/galera/t/lp959512.test
+new file mode 100644
+index 0000000..bcc0db2
+--- /dev/null
++++ b/mysql-test/suite/galera/t/lp959512.test
+@@ -0,0 +1,26 @@
++#
++# LP#959512 IO cache not reset at trx cleanup if write set was empty Edit
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++DROP TABLE IF EXISTS variable;
++DROP TABLE IF EXISTS foo;
++CREATE TABLE variable (
++ name varchar(128) NOT NULL DEFAULT '' COMMENT 'The name of the variable.',
++ value longblob NOT NULL COMMENT 'The value of the variable.',
++ PRIMARY KEY (name)
++ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Named variable/value pairs created by Drupal core or any...';
++CREATE TABLE foo (a int);
++INSERT INTO variable (name, value) VALUES ('menu_expanded', 'a:0:{}');
++START TRANSACTION;
++SELECT 1 AS expression FROM variable variable
++ WHERE ( (name = 'menu_expanded') ) FOR UPDATE;
++UPDATE variable SET value='a:0:{}' WHERE ( (name = 'menu_expanded') );
++COMMIT;
++INSERT INTO foo VALUES (1);
++UPDATE foo SET a = 2 WHERE a = 1;
++
++DROP TABLE foo;
++DROP TABLE variable;
+diff --git a/mysql-test/suite/galera_3nodes/galera_3nodes.cnf b/mysql-test/suite/galera_3nodes/galera_3nodes.cnf
+new file mode 100644
+index 0000000..e837635
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/galera_3nodes.cnf
+@@ -0,0 +1,75 @@
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_provider_options='base_port=@mysqld.1.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.1.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[mysqld.2]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.2.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[mysqld.3]
++binlog-format=row
++
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
++wsrep_provider_options='base_port=@mysqld.3.#galera_port'
++
++# enforce read-committed characteristics across the cluster
++wsrep_causal_reads=ON
++wsrep_sync_wait = 7
++
++wsrep_node_address=127.0.0.1
++wsrep_sst_receive_address=127.0.0.2:@mysqld.3.#sst_port
++wsrep_node_incoming_address=127.0.0.1:@mysqld.3.port
++
++# Required for Galera
++innodb_autoinc_lock_mode=2
++
++[ENV]
++NODE_MYPORT_1= @mysqld.1.port
++NODE_MYSOCK_1= @mysqld.1.socket
++
++NODE_MYPORT_2= @mysqld.2.port
++NODE_MYSOCK_2= @mysqld.2.socket
++
++NODE_MYPORT_3= @mysqld.3.port
++NODE_MYSOCK_3= @mysqld.3.socket
++
++NODE_GALERAPORT_1= @mysqld.1.#galera_port
++NODE_GALERAPORT_2= @mysqld.2.#galera_port
++NODE_GALERAPORT_3= @mysqld.3.#galera_port
++
++NODE_SSTPORT_1= @mysqld.1.#sst_port
++NODE_SSTPORT_2= @mysqld.2.#sst_port
++NODE_SSTPORT_3= @mysqld.3.#sst_port
++
+diff --git a/mysql-test/suite/galera_3nodes/my.cnf b/mysql-test/suite/galera_3nodes/my.cnf
+new file mode 100644
+index 0000000..bb25b95
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/my.cnf
+@@ -0,0 +1 @@
++!include galera_3nodes.cnf
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_certification_ccc.result b/mysql-test/suite/galera_3nodes/r/galera_certification_ccc.result
+new file mode 100644
+index 0000000..96a2bec
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_certification_ccc.result
+@@ -0,0 +1,17 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 3
++1
++SET GLOBAL wsrep_cluster_address = '';
++INSERT INTO t1 VALUES (2);
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++COMMIT;
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_certification_double_failure.result b/mysql-test/suite/galera_3nodes/r/galera_certification_double_failure.result
+new file mode 100644
+index 0000000..9dc735d
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_certification_double_failure.result
+@@ -0,0 +1,12 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++COMMIT;
++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_evs_suspect_timeout.result b/mysql-test/suite/galera_3nodes/r/galera_evs_suspect_timeout.result
+new file mode 100644
+index 0000000..1464222
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_evs_suspect_timeout.result
+@@ -0,0 +1,19 @@
++SET GLOBAL wsrep_provider_options = 'evs.inactive_timeout=PT100M; evs.suspect_timeout=PT1S';
++SET GLOBAL wsrep_provider_options = 'evs.inactive_timeout=PT100M; evs.suspect_timeout=PT1S';
++Suspending node ...
++SET SESSION wsrep_sync_wait = 0;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++CREATE TABLE t1 (f1 INTEGER);
++INSERT INTO t1 VALUES (1);
++SET SESSION wsrep_sync_wait = 0;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 2
++1
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++DROP TABLE t1;
++Resuming node ...
++CALL mtr.add_suppression("WSREP: gcs_caused() returned -1 \\(Operation not permitted\\)");
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_garbd.result b/mysql-test/suite/galera_3nodes/r/galera_garbd.result
+new file mode 100644
+index 0000000..616c9d3
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_garbd.result
+@@ -0,0 +1,14 @@
++Killing node #3 to free ports for garbd ...
++Starting garbd ...
++CREATE TABLE t1 (f1 INTEGER);
++INSERT INTO t1 VALUES (1);
++SELECT COUNT(*) = 1 FROM t1;
++COUNT(*) = 1
++1
++Killing garbd ...
++INSERT INTO t1 VALUES (2);
++SELECT COUNT(*) = 2 FROM t1;
++COUNT(*) = 2
++1
++DROP TABLE t1;
++Restarting node #3 to satisfy MTR's end-of-test checks
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_ist_gcache_rollover.result b/mysql-test/suite/galera_3nodes/r/galera_ist_gcache_rollover.result
+new file mode 100644
+index 0000000..1ecea5d
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_ist_gcache_rollover.result
+@@ -0,0 +1,46 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY);
++INSERT INTO t1 VALUES (01), (02), (03), (04), (05);
++Unloading wsrep provider ...
++SET GLOBAL wsrep_provider = 'none';
++Unloading wsrep provider ...
++SET GLOBAL wsrep_provider = 'none';
++INSERT INTO t1 VALUES (11), (12), (13), (14), (15);
++INSERT INTO t1 VALUES (21), (22), (23), (24), (25);
++SET GLOBAL wsrep_provider_options = 'dbug=d,ist_sender_send_after_get_buffers';
++INSERT INTO t1 VALUES (31), (32), (33), (34), (35);
++SHOW STATUS LIKE 'wsrep_debug_sync_waiters';
++Variable_name Value
++wsrep_debug_sync_waiters ist_sender_send_after_get_buffers ist_sender_send_after_get_buffers
++INSERT INTO t1 VALUES (41), (42), (43), (44), (45);
++CREATE TABLE t2 (f1 LONGTEXT);
++INSERT INTO t2 VALUES (REPEAT('x', 512 * 1024));
++INSERT INTO t2 VALUES (REPEAT('x', 512 * 1024));
++INSERT INTO t2 VALUES (REPEAT('x', 512 * 1024));
++SET GLOBAL wsrep_provider_options = 'signal=ist_sender_send_after_get_buffers';
++SET GLOBAL wsrep_provider_options = 'dbug=';
++INSERT INTO t1 VALUES (51), (52), (53), (54), (55);
++SELECT COUNT(*) = 30 FROM t1;
++COUNT(*) = 30
++1
++SELECT COUNT(*) = 3 FROM t2;
++COUNT(*) = 3
++1
++SELECT LENGTH(f1) = 512 * 1024 FROM t2;
++LENGTH(f1) = 512 * 1024
++1
++1
++1
++CALL mtr.add_suppression("WSREP: Unsupported protocol downgrade: incremental data collection disabled");
++SELECT COUNT(*) = 30 FROM t1;
++COUNT(*) = 30
++1
++SELECT COUNT(*) = 3 FROM t2;
++COUNT(*) = 3
++1
++SELECT LENGTH(f1) = 512 * 1024 FROM t2;
++LENGTH(f1) = 512 * 1024
++1
++1
++1
++CALL mtr.add_suppression("WSREP: Unsupported protocol downgrade: incremental data collection disabled");
++DROP TABLE t1, t2;
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result b/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result
+new file mode 100644
+index 0000000..9a6d40a
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result
+@@ -0,0 +1,13 @@
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++SET GLOBAL wsrep_slave_threads = 2;
++UPDATE t1 SET f1 = f1 + 10;;
++UPDATE t1 SET f1 = f1 + 100;;
++SELECT f1 = 111 FROM t1;
++f1 = 111
++1
++SELECT COUNT(*) IN (1, 2) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%committed%';
++COUNT(*) IN (1, 2)
++1
++SET GLOBAL wsrep_slave_threads = 1;;
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera_3nodes/r/galera_pc_weight.result b/mysql-test/suite/galera_3nodes/r/galera_pc_weight.result
+new file mode 100644
+index 0000000..ca05143
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/r/galera_pc_weight.result
+@@ -0,0 +1,118 @@
++SET GLOBAL wsrep_provider_options = 'pc.weight=3';
++Suspending node ...
++SET SESSION wsrep_sync_wait=0;
++SHOW STATUS LIKE 'wsrep_cluster_size';
++Variable_name Value
++wsrep_cluster_size 2
++SHOW STATUS LIKE 'wsrep_cluster_status';
++Variable_name Value
++wsrep_cluster_status non-Primary
++SHOW STATUS LIKE 'wsrep_connected';
++Variable_name Value
++wsrep_connected ON
++SHOW STATUS LIKE 'wsrep_ready';
++Variable_name Value
++wsrep_ready OFF
++SHOW STATUS LIKE 'wsrep_local_state';
++Variable_name Value
++wsrep_local_state 0
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++Variable_name Value
++wsrep_local_state_comment Initialized
++SET SESSION wsrep_sync_wait=0;
++SHOW STATUS LIKE 'wsrep_cluster_size';
++Variable_name Value
++wsrep_cluster_size 2
++SHOW STATUS LIKE 'wsrep_cluster_status';
++Variable_name Value
++wsrep_cluster_status non-Primary
++SHOW STATUS LIKE 'wsrep_connected';
++Variable_name Value
++wsrep_connected ON
++SHOW STATUS LIKE 'wsrep_ready';
++Variable_name Value
++wsrep_ready OFF
++SHOW STATUS LIKE 'wsrep_local_state';
++Variable_name Value
++wsrep_local_state 0
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++Variable_name Value
++wsrep_local_state_comment Initialized
++Resuming node ...
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 1
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SET SESSION wsrep_sync_wait=0;
++SET SESSION wsrep_sync_wait=0;
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 3
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 3
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++VARIABLE_VALUE = 3
++1
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++VARIABLE_VALUE = 'Primary'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++VARIABLE_VALUE = 'ON'
++1
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++VARIABLE_VALUE = 4
++1
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++VARIABLE_VALUE = 'Synced'
++1
++CALL mtr.add_suppression('WSREP: gcs_caused\\(\\) returned -1');
++CALL mtr.add_suppression('WSREP: user message in state LEAVING');
++CALL mtr.add_suppression('sending install message failed: Transport endpoint is not connected');
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_certification_ccc.test b/mysql-test/suite/galera_3nodes/t/galera_certification_ccc.test
+new file mode 100644
+index 0000000..da4a609
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_certification_ccc.test
+@@ -0,0 +1,41 @@
++#
++# Test that a cluster configuration change during a transaction does not cause a failure
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++
++--connection node_3
++--let $wsrep_cluster_address_orig = `SELECT @@wsrep_cluster_address`
++SET GLOBAL wsrep_cluster_address = '';
++--sleep 5
++
++--connection node_1
++INSERT INTO t1 VALUES (2);
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++COMMIT;
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++
++--connection node_3
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++--sleep 5
++--source include/galera_wait_ready.inc
++
++--connection node_1
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_certification_double_failure.test b/mysql-test/suite/galera_3nodes/t/galera_certification_double_failure.test
+new file mode 100644
+index 0000000..a2ad076
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_certification_double_failure.test
+@@ -0,0 +1,33 @@
++#
++# This test creates a transaction whose certification will fail on two separate nodes
++# for two different reasons.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++
++--connection node_1
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++CREATE TABLE t2 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++
++SET AUTOCOMMIT=OFF;
++START TRANSACTION;
++INSERT INTO t1 VALUES (1);
++INSERT INTO t2 VALUES (1);
++
++--connection node_2
++INSERT INTO t1 VALUES (1);
++
++--connection node_3
++INSERT INTO t2 VALUES (1);
++
++--connection node_1
++--error ER_LOCK_DEADLOCK
++COMMIT;
++
++DROP TABLE t1;
++DROP TABLE t2;
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_evs_suspect_timeout.test b/mysql-test/suite/galera_3nodes/t/galera_evs_suspect_timeout.test
+new file mode 100644
+index 0000000..a87f19a
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_evs_suspect_timeout.test
+@@ -0,0 +1,67 @@
++#
++# Test the operation of evs.suspect_timeout.
++#
++# We set evs.inactive_timeout to a very high value so that evs.suspect_timeout can kick in instead.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--connection node_1
++--let $wsrep_provider_options_node1 = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'evs.inactive_timeout=PT100M; evs.suspect_timeout=PT1S';
++
++--connection node_2
++--source include/wait_until_connected_again.inc
++--let $wsrep_provider_options_node2 = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'evs.inactive_timeout=PT100M; evs.suspect_timeout=PT1S';
++
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++--connection node_3
++--source include/wait_until_connected_again.inc
++--let $wsrep_cluster_address_node3 = `SELECT @@wsrep_cluster_address`
++
++# Suspend node #3
++
++--source include/galera_suspend.inc
++--sleep 5
++
++# Confirm that the other nodes have booted it out
++
++--connection node_1
++--source include/wait_until_connected_again.inc
++SET SESSION wsrep_sync_wait = 0;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_node1';
++--enable_query_log
++
++--source include/wait_until_connected_again.inc
++CREATE TABLE t1 (f1 INTEGER);
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SET SESSION wsrep_sync_wait = 0;
++SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++--disable_query_log
++--eval SET GLOBAL wsrep_provider_options = '$wsrep_provider_options_node2';
++--enable_query_log
++
++--source include/wait_until_connected_again.inc
++SELECT COUNT(*) = 1 FROM t1;
++DROP TABLE t1;
++
++# Reconnect node #3 so that MTR's end-of-test checks can run
++
++--connection node_3
++--source include/galera_resume.inc
++--source include/wait_until_connected_again.inc
++
++CALL mtr.add_suppression("WSREP: gcs_caused() returned -1 \\(Operation not permitted\\)");
++
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_node3';
++--enable_query_log
++--source include/wait_until_connected_again.inc
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_garbd.test b/mysql-test/suite/galera_3nodes/t/galera_garbd.test
+new file mode 100644
+index 0000000..3f58783
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_garbd.test
+@@ -0,0 +1,50 @@
++#
++# A very basic test for the galera arbitrator. We shut down node #3 and use its port allocation to start garbd.
++# As MTR does not allow multiple servers to be down at the same time, we are limited as to what we can test.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/big_test.inc
++
++--echo Killing node #3 to free ports for garbd ...
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++--source include/shutdown_mysqld.inc
++
++--connection node_1
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++--echo Starting garbd ...
++--exec `dirname $WSREP_PROVIDER`/garb/garbd --address "gcomm://127.0.0.1:$NODE_GALERAPORT_1" --group my_wsrep_cluster --options 'base_port=$NODE_GALERAPORT_3' > $MYSQL_TMP_DIR/garbd.log 2>&1 &
++
++--sleep 5
++
++--let $wait_condition = SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++CREATE TABLE t1 (f1 INTEGER);
++INSERT INTO t1 VALUES (1);
++
++--connection node_2
++SELECT COUNT(*) = 1 FROM t1;
++
++--echo Killing garbd ...
++--exec pkill --oldest --full garbd.*$NODE_GALERAPORT_3
++
++--connection node_1
++--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++INSERT INTO t1 VALUES (2);
++
++--connection node_2
++SELECT COUNT(*) = 2 FROM t1;
++
++DROP TABLE t1;
++
++--echo Restarting node #3 to satisfy MTR's end-of-test checks
++--connection node_3
++--source include/start_mysqld.inc
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_ist_gcache_rollover.cnf b/mysql-test/suite/galera_3nodes/t/galera_ist_gcache_rollover.cnf
+new file mode 100644
+index 0000000..8211752
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_ist_gcache_rollover.cnf
+@@ -0,0 +1,11 @@
++!include ../galera_3nodes.cnf
++
++[mysqld.1]
++wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true;gcache.size=1M'
++
++[mysqld.2]
++wsrep_provider_options='base_port=@mysqld.2.#galera_port;pc.ignore_sb=true;gcache.size=1M'
++
++[mysqld.3]
++wsrep_provider_options='base_port=@mysqld.3.#galera_port;pc.ignore_sb=true;gcache.size=1M'
++
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_ist_gcache_rollover.test b/mysql-test/suite/galera_3nodes/t/galera_ist_gcache_rollover.test
+new file mode 100644
+index 0000000..0668c24
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_ist_gcache_rollover.test
+@@ -0,0 +1,98 @@
++#
++# Test that if the gcache rolls over while IST is in progress, IST will still complete.
++# This is achieved by using the ist_sender_send_after_get_buffers Galera dbug sync point to block the donor after
++# the first gcache buffer has been locked for IST.
++#
++# After IST blocks, we roll over the gcache and resume IST to confirm that it completes successfully.
++#
++# Two nodes perform IST at the same time in order to make the test more stressfull
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++--source include/have_debug_sync.inc
++--source suite/galera/include/galera_have_debug_sync.inc
++
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY);
++INSERT INTO t1 VALUES (01), (02), (03), (04), (05);
++
++# Disconnect nodes #2 and #3
++--connection node_2
++--source suite/galera/include/galera_unload_provider.inc
++
++--connection node_3
++--source suite/galera/include/galera_unload_provider.inc
++
++--connection node_1
++--source include/wait_until_connected_again.inc
++INSERT INTO t1 VALUES (11), (12), (13), (14), (15);
++
++# Wait until nodes #2 and #3 have left
++--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
++--source include/wait_condition.inc
++
++INSERT INTO t1 VALUES (21), (22), (23), (24), (25);
++
++# Make sure IST will block ...
++SET GLOBAL wsrep_provider_options = 'dbug=d,ist_sender_send_after_get_buffers';
++
++# ... and restart providers to force IST
++--connection node_2
++--disable_query_log
++--eval SET GLOBAL wsrep_provider = '$wsrep_provider_orig';
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++
++--connection node_1
++INSERT INTO t1 VALUES (31), (32), (33), (34), (35);
++
++--connection node_3
++--disable_query_log
++--eval SET GLOBAL wsrep_provider = '$wsrep_provider_orig';
++--eval SET GLOBAL wsrep_cluster_address = '$wsrep_cluster_address_orig';
++--enable_query_log
++
++--connection node_1
++--sleep 2
++SHOW STATUS LIKE 'wsrep_debug_sync_waiters';
++
++INSERT INTO t1 VALUES (41), (42), (43), (44), (45);
++
++# Roll over gcache by writing a lot of information to it
++
++CREATE TABLE t2 (f1 LONGTEXT);
++INSERT INTO t2 VALUES (REPEAT('x', 512 * 1024));
++INSERT INTO t2 VALUES (REPEAT('x', 512 * 1024));
++INSERT INTO t2 VALUES (REPEAT('x', 512 * 1024));
++
++# Unlock IST and wait for it to complete
++SET GLOBAL wsrep_provider_options = 'signal=ist_sender_send_after_get_buffers';
++SET GLOBAL wsrep_provider_options = 'dbug=';
++
++INSERT INTO t1 VALUES (51), (52), (53), (54), (55);
++
++--connection node_2
++--source include/wait_until_connected_again.inc
++
++--connection node_3
++--source include/wait_until_connected_again.inc
++
++# Final checks
++--connection node_2
++SELECT COUNT(*) = 30 FROM t1;
++SELECT COUNT(*) = 3 FROM t2;
++SELECT LENGTH(f1) = 512 * 1024 FROM t2;
++CALL mtr.add_suppression("WSREP: Unsupported protocol downgrade: incremental data collection disabled");
++
++# Final checks
++--connection node_3
++SELECT COUNT(*) = 30 FROM t1;
++SELECT COUNT(*) = 3 FROM t2;
++SELECT LENGTH(f1) = 512 * 1024 FROM t2;
++CALL mtr.add_suppression("WSREP: Unsupported protocol downgrade: incremental data collection disabled");
++
++DROP TABLE t1, t2;
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test b/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test
+new file mode 100644
+index 0000000..659df2b
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test
+@@ -0,0 +1,38 @@
++#
++# This test performs two dependent updates on two nodes and checks the results on the third where
++# parallel apply is enabled.
++#
++
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++
++CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
++INSERT INTO t1 VALUES (1);
++
++--connection node_3
++--let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
++SET GLOBAL wsrep_slave_threads = 2;
++
++--connection node_1
++--send UPDATE t1 SET f1 = f1 + 10;
++
++--connection node_2
++--send UPDATE t1 SET f1 = f1 + 100;
++
++--connection node_1
++--reap
++
++--connection node_2
++--reap
++
++--connection node_3
++SELECT f1 = 111 FROM t1;
++SELECT COUNT(*) IN (1, 2) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%committed%';
++
++--eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig;
++
++DROP TABLE t1;
+diff --git a/mysql-test/suite/galera_3nodes/t/galera_pc_weight.test b/mysql-test/suite/galera_3nodes/t/galera_pc_weight.test
+new file mode 100644
+index 0000000..8956bae
+--- /dev/null
++++ b/mysql-test/suite/galera_3nodes/t/galera_pc_weight.test
+@@ -0,0 +1,110 @@
++#
++# Test the pc.weight wsrep provider option. We set Node #1 to have a high weight and then
++# suspend it. This will cause Nodes #2 and #3 to transition to non-primary component.
++#
++
++--source include/big_test.inc
++--source include/galera_cluster.inc
++--source include/have_innodb.inc
++
++--let $wsrep_provider_options_node1 = `SELECT @@wsrep_provider_options`
++SET GLOBAL wsrep_provider_options = 'pc.weight=3';
++
++--source include/wait_until_connected_again.inc
++--source include/galera_suspend.inc
++--sleep 10
++
++--connection node_2
++# Do not wait for causality as we are no longer in the primary component
++SET SESSION wsrep_sync_wait=0;
++--source include/wait_until_connected_again.inc
++
++# We can not use SELECT queries here, as only SHOW is allowed to run.
++# For nodes #2 and #3, we expect a non-primary component of size 2
++
++SHOW STATUS LIKE 'wsrep_cluster_size';
++SHOW STATUS LIKE 'wsrep_cluster_status';
++SHOW STATUS LIKE 'wsrep_connected';
++SHOW STATUS LIKE 'wsrep_ready';
++SHOW STATUS LIKE 'wsrep_local_state';
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++
++--let $galera_connection_name = node_3
++--let $galera_server_number = 3
++--source include/galera_connect.inc
++--connection node_3
++SET SESSION wsrep_sync_wait=0;
++--source include/wait_until_connected_again.inc
++
++SHOW STATUS LIKE 'wsrep_cluster_size';
++SHOW STATUS LIKE 'wsrep_cluster_status';
++SHOW STATUS LIKE 'wsrep_connected';
++SHOW STATUS LIKE 'wsrep_ready';
++SHOW STATUS LIKE 'wsrep_local_state';
++SHOW STATUS LIKE 'wsrep_local_state_comment';
++
++--connection node_1
++--source include/galera_resume.inc
++--sleep 5
++--source include/wait_until_connected_again.inc
++
++# For Node #1, we expect a primary component of size 1
++
++SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++# Restore the cluster by resetting wsrep_cluster_address on nodes #1 and #2
++
++--connection node_2
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++--enable_query_log
++
++SET SESSION wsrep_sync_wait=0;
++--source include/wait_until_connected_again.inc
++
++--connection node_3
++--disable_query_log
++--eval SET GLOBAL wsrep_cluster_address = @@wsrep_cluster_address;
++--enable_query_log
++
++SET SESSION wsrep_sync_wait=0;
++--source include/wait_until_connected_again.inc
++
++# On all nodes, we now expect a Primary component of size 3, Synced and ready
++
++--connection node_1
++--source include/wait_until_connected_again.inc
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++--connection node_2
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++--connection node_3
++SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
++SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_connected';
++SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready';
++SELECT VARIABLE_VALUE = 4 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state';
++SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment';
++
++--connection node_1
++CALL mtr.add_suppression('WSREP: gcs_caused\\(\\) returned -1');
++
++--connection node_3
++CALL mtr.add_suppression('WSREP: user message in state LEAVING');
++CALL mtr.add_suppression('sending install message failed: Transport endpoint is not connected');
+diff --git a/mysql-test/suite/innodb/r/innodb-autoinc.result b/mysql-test/suite/innodb/r/innodb-autoinc.result
+index 0e0f49b..21acf06 100644
+--- a/mysql-test/suite/innodb/r/innodb-autoinc.result
++++ b/mysql-test/suite/innodb/r/innodb-autoinc.result
+@@ -197,7 +197,7 @@ c1 c2
+ 5 9
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 100
+ auto_increment_offset 10
+@@ -228,9 +228,10 @@ c1
+ 410
+ 1000
+ DROP TABLE t1;
++SET GLOBAL wsrep_auto_increment_control=OFF;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -269,7 +270,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -282,7 +283,7 @@ SELECT * FROM t1;
+ c1
+ -1
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 100
+ auto_increment_offset 10
+@@ -315,7 +316,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -330,7 +331,7 @@ SELECT * FROM t1;
+ c1
+ 1
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 100
+ auto_increment_offset 10
+@@ -370,7 +371,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -385,7 +386,7 @@ SELECT * FROM t1;
+ c1
+ 1
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 100
+ auto_increment_offset 10
+@@ -419,7 +420,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -434,7 +435,7 @@ c1
+ 1
+ 9223372036854775794
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 2
+ auto_increment_offset 10
+@@ -452,7 +453,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -467,7 +468,7 @@ c1
+ 1
+ 18446744073709551603
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 2
+ auto_increment_offset 10
+@@ -480,7 +481,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -495,7 +496,7 @@ c1
+ 1
+ 18446744073709551603
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=5, @@SESSION.AUTO_INCREMENT_OFFSET=7;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 5
+ auto_increment_offset 7
+@@ -508,7 +509,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -527,7 +528,7 @@ c1
+ -9223372036854775806
+ 1
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=3, @@SESSION.AUTO_INCREMENT_OFFSET=3;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 3
+ auto_increment_offset 3
+@@ -544,7 +545,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -562,7 +563,7 @@ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1152921504606846976, @@SESSION.AUTO_INCRE
+ Warnings:
+ Warning 1292 Truncated incorrect auto_increment_increment value: '1152921504606846976'
+ Warning 1292 Truncated incorrect auto_increment_offset value: '1152921504606846976'
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 65535
+ auto_increment_offset 65535
+@@ -575,7 +576,7 @@ c1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -862,7 +863,7 @@ Got one of the listed errors
+ DROP TABLE t1;
+ DROP TABLE t2;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+@@ -1252,7 +1253,7 @@ t1 CREATE TABLE `t1` (
+ ) ENGINE=InnoDB AUTO_INCREMENT=18446744073709551615 DEFAULT CHARSET=latin1
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=256;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 256
+@@ -1270,7 +1271,7 @@ c1 c2
+ 1 NULL
+ DROP TABLE t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ Variable_name Value
+ auto_increment_increment 1
+ auto_increment_offset 1
+diff --git a/mysql-test/suite/innodb/t/galera.skip b/mysql-test/suite/innodb/t/galera.skip
+new file mode 100644
+index 0000000..54e07b4
+--- /dev/null
++++ b/mysql-test/suite/innodb/t/galera.skip
+@@ -0,0 +1,43 @@
++innodb : deadlock, failure in UPDATE IGNORE, lp1372296
++innodb_ctype_ldml : Test contains statements unsafe to replicate in statement-based replication
++innodb-autoinc : deadlock, failure in REPLACE, lp1372296
++innodb_mysql : deadlock due to DDL
++innodb_buffer_pool_load : Test contains statements unsafe to replicate in statement-based replication
++innodb-autoinc-56228 : deadlock, lp1372301
++innodb_lock_wait_timeout_1 : Test contains statements unsafe to replicate in statement-based replication
++innodb-consistent : Test contains statements unsafe to replicate in statement-based replication
++innodb-semi-consistent : Test contains statements unsafe to replicate in statement-based replication
++innodb-index : DDL concurrent with transaction
++innodb-lock : deadlock on INSERT IGNORE, lp1372296
++innodb-status-output : Test performs server restart
++innodb-wl5522 : Test contains statements unsafe to replicate in statement-based replication
++innodb-wl6445 : Test performs server restart
++innodb_bug40360 : Test contains statements unsafe to replicate in statement-based replication
++innodb_bug40565 : Galera git bug #137 - Invalid deadlock on UPDATE to NULL without a PK
++innodb_bug42419 : Test contains statements unsafe to replicate in statement-based replication
++innodb_bug49164 : Test contains statements unsafe to replicate in statement-based replication
++innodb_bug45357 : impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging
++innodb_bug52663 : Test contains statements unsafe to replicate in statement-based replication
++innodb_bug59733 : Test contains statements unsafe to replicate in statement-based replication
++innodb_gis : Test contains statements unsafe to replicate in statement-based replication
++innodb_prefix_index_restart_server : crash, lp1372288
++innodb_stats_external_pages : Test contains statements unsafe to replicate in statement-based replication
++innodb-2byte-collation : Unsafe statement written to the binary log
++innodb-change-buffer-recovery : Test contains statements unsafe to replicate in statement-based replication
++innodb_bug53756 : Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT
++innodb-alter-autoinc : AUTO_INCREMENT differences
++innodb_bug13635833 : Test contains statements unsafe to replicate in statement-based replication
++innodb_bug13867871 : mysql-wsrep#3 - innodb_bug13867871 test fails with wsrep loaded
++innodb_bug14006907 : DDL lock wait timeout
++innodb_bug38231 : Deadlock on UNLOCK TABLE
++innodb_bug-13628249 : mysql-wsrep#12 InnoDB: Failing assertion: !srv_read_only_mode with server restart
++innodb-wl6445-1 : mysql-wsrep#12 InnoDB: Failing assertion: !srv_read_only_mode with server restart
++innodb-wl6445-2 : mysql-wsrep#12 InnoDB: Failing assertion: !srv_read_only_mode with server restart
++innodb_bug12400341 : Test does not account for applier threads when performing SHOW PROCESSLIST
++innodb-blob : 'Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging'
++innodb_corrupt_bit : 'Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.'
++innodb-index-online : ALTER succeeds as it is given a higher priority
++innodb-table-online : ALTER succeeds as it is given a higher priority
++innodb-index-online-purge : ALTER succeeds as it is given a higher priority
++innodb-wl5522-debug : Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.
++innodb_stats_table_flag_auto_recalc : Performs multiple restarts in a row which causes '1047: Unknown command' errors
+diff --git a/mysql-test/suite/innodb/t/innodb-autoinc.test b/mysql-test/suite/innodb/t/innodb-autoinc.test
+index 3d9f591..7cc8065 100644
+--- a/mysql-test/suite/innodb/t/innodb-autoinc.test
++++ b/mysql-test/suite/innodb/t/innodb-autoinc.test
+@@ -156,7 +156,7 @@ DROP TABLE t1;
+ #
+ # Test changes to AUTOINC next value calculation
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES (NULL),(5),(NULL);
+@@ -171,9 +171,11 @@ DROP TABLE t1;
+ # Test with SIGNED INT column, by inserting a 0 for the first column value
+ # 0 is treated in the same was NULL.
+ # Reset the AUTOINC session variables
++SET GLOBAL wsrep_auto_increment_control=OFF;
++
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES(0);
+@@ -193,13 +195,13 @@ DROP TABLE t1;
+ # Reset the AUTOINC session variables
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES(-1);
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ INSERT INTO t1 VALUES (-2), (NULL),(2),(NULL);
+ INSERT INTO t1 VALUES (250),(NULL);
+ SELECT * FROM t1;
+@@ -214,13 +216,13 @@ DROP TABLE t1;
+ # Reset the AUTOINC session variables
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES(-1);
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ INSERT INTO t1 VALUES (-2);
+ INSERT INTO t1 VALUES (NULL);
+ INSERT INTO t1 VALUES (2);
+@@ -240,13 +242,13 @@ DROP TABLE t1;
+ # Reset the AUTOINC session variables
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES(-1);
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ INSERT INTO t1 VALUES (-2),(NULL),(2),(NULL);
+ INSERT INTO t1 VALUES (250),(NULL);
+ SELECT * FROM t1;
+@@ -262,7 +264,7 @@ DROP TABLE t1;
+ # Check for overflow handling when increment is > 1
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 BIGINT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ # TODO: Fix the autoinc init code
+@@ -271,7 +273,7 @@ INSERT INTO t1 VALUES(NULL);
+ INSERT INTO t1 VALUES (9223372036854775794); #-- 2^63 - 14
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ # This should just fit
+ INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
+ SELECT * FROM t1;
+@@ -281,7 +283,7 @@ DROP TABLE t1;
+ # Check for overflow handling when increment and offser are > 1
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ # TODO: Fix the autoinc init code
+@@ -290,7 +292,7 @@ INSERT INTO t1 VALUES(NULL);
+ INSERT INTO t1 VALUES (18446744073709551603); #-- 2^64 - 13
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ --error ER_AUTOINC_READ_FAILED
+ INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
+ SELECT * FROM t1;
+@@ -300,7 +302,7 @@ DROP TABLE t1;
+ # Check for overflow handling when increment and offset are odd numbers
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ # TODO: Fix the autoinc init code
+@@ -309,7 +311,7 @@ INSERT INTO t1 VALUES(NULL);
+ INSERT INTO t1 VALUES (18446744073709551603); #-- 2^64 - 13
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=5, @@SESSION.AUTO_INCREMENT_OFFSET=7;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ --error ER_AUTOINC_READ_FAILED
+ INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
+ SELECT * FROM t1;
+@@ -319,7 +321,7 @@ DROP TABLE t1;
+ # and check for large -ve numbers
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 BIGINT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ # TODO: Fix the autoinc init code
+@@ -330,7 +332,7 @@ INSERT INTO t1 VALUES(-9223372036854775807); #-- -2^63 + 1
+ INSERT INTO t1 VALUES(-9223372036854775808); #-- -2^63
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=3, @@SESSION.AUTO_INCREMENT_OFFSET=3;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+@@ -339,7 +341,7 @@ DROP TABLE t1;
+ # large numbers 2^60
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB;
+ # TODO: Fix the autoinc init code
+@@ -348,7 +350,7 @@ INSERT INTO t1 VALUES(NULL);
+ INSERT INTO t1 VALUES (18446744073709551610); #-- 2^64 - 2
+ SELECT * FROM t1;
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1152921504606846976, @@SESSION.AUTO_INCREMENT_OFFSET=1152921504606846976;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ --error ER_WARN_DATA_OUT_OF_RANGE
+ INSERT INTO t1 VALUES (NULL),(NULL);
+ SELECT * FROM t1;
+@@ -359,7 +361,7 @@ DROP TABLE t1;
+ #
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+ SET @@INSERT_ID=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ CREATE TABLE t1 (c1 DOUBLE NOT NULL AUTO_INCREMENT, c2 INT, PRIMARY KEY (c1)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES(NULL, 1);
+ INSERT INTO t1 VALUES(NULL, 2);
+@@ -445,7 +447,7 @@ DROP TABLE t2;
+ # If the user has specified negative values for an AUTOINC column then
+ # InnoDB should ignore those values when setting the table's max value.
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ # TINYINT
+ CREATE TABLE t1 (c1 TINYINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES (1, NULL);
+@@ -641,7 +643,7 @@ DROP TABLE t1;
+
+ # Check if we handle offset > column max value properly
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=256;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ # TINYINT
+ CREATE TABLE t1 (c1 TINYINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES (1, NULL);
+@@ -653,7 +655,7 @@ DROP TABLE t1;
+ # of the column. IMO, this should not be allowed and the assertion that fails
+ # is actually an invariant.
+ SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
+-SHOW VARIABLES LIKE "%auto_inc%";
++SHOW VARIABLES LIKE "auto_inc%";
+ # TINYINT
+ CREATE TABLE t1 (c1 INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB;
+ INSERT INTO t1 VALUES (2147483648, 'a');
+diff --git a/mysql-test/suite/opt_trace/t/disabled.def b/mysql-test/suite/opt_trace/t/disabled.def
+new file mode 100644
+index 0000000..3ed40b8
+--- /dev/null
++++ b/mysql-test/suite/opt_trace/t/disabled.def
+@@ -0,0 +1,2 @@
++general_no_prot_all : Galera mysql-wsrep#5 2014-10-28 pstoev opt_trace.general_no_prot_none and opt_trace.general_no_prot_all fail in mysql-wsrep
++general_no_prot_none : Galera mysql-wsrep#5 2014-10-28 pstoev opt_trace.general_no_prot_none and opt_trace.general_no_prot_all fail in mysql-wsrep
+diff --git a/mysql-test/suite/parts/r/partition_exch_qa_10.result b/mysql-test/suite/parts/r/partition_exch_qa_10.result
+index 77b91f1..7193a6c 100644
+--- a/mysql-test/suite/parts/r/partition_exch_qa_10.result
++++ b/mysql-test/suite/parts/r/partition_exch_qa_10.result
+@@ -23,7 +23,7 @@ a b
+ DROP PROCEDURE test_p1;
+ SET @save_autocommit= @@autocommit;
+ SET @@autocommit= OFF;
+-SHOW VARIABLES LIKE '%autocommit%';
++SHOW VARIABLES LIKE 'autocommit%';
+ Variable_name Value
+ autocommit OFF
+ CREATE TRIGGER test_trg_1 BEFORE UPDATE ON tp FOR EACH ROW
+diff --git a/mysql-test/suite/parts/t/partition_exch_qa_10.test b/mysql-test/suite/parts/t/partition_exch_qa_10.test
+index 4f56960..a87d658 100644
+--- a/mysql-test/suite/parts/t/partition_exch_qa_10.test
++++ b/mysql-test/suite/parts/t/partition_exch_qa_10.test
+@@ -37,7 +37,7 @@ DROP PROCEDURE test_p1;
+
+ SET @save_autocommit= @@autocommit;
+ SET @@autocommit= OFF;
+-SHOW VARIABLES LIKE '%autocommit%';
++SHOW VARIABLES LIKE 'autocommit%';
+ DELIMITER |;
+ --error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+ CREATE TRIGGER test_trg_1 BEFORE UPDATE ON tp FOR EACH ROW
+diff --git a/mysql-test/suite/perfschema/r/dml_setup_instruments.result b/mysql-test/suite/perfschema/r/dml_setup_instruments.result
+index b3de51e..5152d9f 100644
+--- a/mysql-test/suite/perfschema/r/dml_setup_instruments.result
++++ b/mysql-test/suite/perfschema/r/dml_setup_instruments.result
+@@ -34,6 +34,7 @@ where name like 'Wait/Synch/Cond/sql/%'
+ and name not in (
+ 'wait/synch/cond/sql/COND_handler_count',
+ 'wait/synch/cond/sql/DEBUG_SYNC::cond')
++and name not like 'wait/synch/cond/sql/COND_wsrep_%'
+ order by name limit 10;
+ NAME ENABLED TIMED
+ wait/synch/cond/sql/COND_flush_thread_cache YES YES
+diff --git a/mysql-test/suite/perfschema/r/rpl_statements.result b/mysql-test/suite/perfschema/r/rpl_statements.result
+index dd8d349..96016bd 100644
+--- a/mysql-test/suite/perfschema/r/rpl_statements.result
++++ b/mysql-test/suite/perfschema/r/rpl_statements.result
+@@ -14,7 +14,7 @@ Note #### Storing MySQL user name or password information in the master info rep
+
+ *** Create test tables
+
+-show variables like '%binlog_format%';
++show variables like 'binlog_format%';
+ Variable_name Value
+ binlog_format MIXED
+ drop table if exists test.marker;
+@@ -58,7 +58,7 @@ Expect 1
+ *** MASTER ***
+ **************
+
+-show variables like '%binlog_format%';
++show variables like 'binlog_format%';
+ Variable_name Value
+ binlog_format MIXED
+ *** Clear statement events
+diff --git a/mysql-test/suite/perfschema/t/disabled.def b/mysql-test/suite/perfschema/t/disabled.def
+index 888298b..1a6d6b5 100644
+--- a/mysql-test/suite/perfschema/t/disabled.def
++++ b/mysql-test/suite/perfschema/t/disabled.def
+@@ -9,3 +9,6 @@
+ # Do not use any TAB characters for whitespace.
+ #
+ ##############################################################################
++
++perfschema.sizing_growth : Galera LP#1370988 2014-10-28 pstoev Performance schema heuristics need to be updated
++ortho_iter : Galera mysql-wsrep#34 2014-12-19 perfschema.ortho_iter MTR test fails sporadically
+diff --git a/mysql-test/suite/perfschema/t/dml_setup_instruments.test b/mysql-test/suite/perfschema/t/dml_setup_instruments.test
+index 8a4f11b..b679b7a 100644
+--- a/mysql-test/suite/perfschema/t/dml_setup_instruments.test
++++ b/mysql-test/suite/perfschema/t/dml_setup_instruments.test
+@@ -36,7 +36,8 @@ select * from performance_schema.setup_instruments
+ and name not in (
+ 'wait/synch/cond/sql/COND_handler_count',
+ 'wait/synch/cond/sql/DEBUG_SYNC::cond')
+- order by name limit 10;
++ and name not like 'wait/synch/cond/sql/COND_wsrep_%'
++order by name limit 10;
+
+ --disable_result_log
+ select * from performance_schema.setup_instruments
+diff --git a/mysql-test/suite/perfschema/t/rpl_statements.test b/mysql-test/suite/perfschema/t/rpl_statements.test
+index 8e71961..ac4ece5 100644
+--- a/mysql-test/suite/perfschema/t/rpl_statements.test
++++ b/mysql-test/suite/perfschema/t/rpl_statements.test
+@@ -63,7 +63,7 @@ connection master;
+ --echo *** Create test tables
+ --echo
+
+-show variables like '%binlog_format%';
++show variables like 'binlog_format%';
+
+ --disable_warnings
+ drop table if exists test.marker;
+@@ -128,7 +128,7 @@ connection master;
+ --echo *** MASTER ***
+ --echo **************
+ --echo
+-show variables like '%binlog_format%';
++show variables like 'binlog_format%';
+
+ --echo *** Clear statement events
+ --source ../include/rpl_statements_truncate.inc
+diff --git a/mysql-test/suite/rpl/r/rpl_auto_increment.result b/mysql-test/suite/rpl/r/rpl_auto_increment.result
+index 0d858cf..283d244 100644
+--- a/mysql-test/suite/rpl/r/rpl_auto_increment.result
++++ b/mysql-test/suite/rpl/r/rpl_auto_increment.result
+@@ -42,6 +42,7 @@ show variables like "%auto_inc%";
+ Variable_name Value
+ auto_increment_increment 100
+ auto_increment_offset 10
++wsrep_auto_increment_control ON
+ create table t1 (a int not null auto_increment, primary key (a)) engine=myisam;
+ insert into t1 values (NULL),(5),(NULL);
+ insert into t1 values (250),(NULL);
+diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def
+index a82c31c..fd07c96 100644
+--- a/mysql-test/suite/rpl/t/disabled.def
++++ b/mysql-test/suite/rpl/t/disabled.def
+@@ -13,3 +13,4 @@
+ rpl_row_create_table : Bug#11759274 2010-02-27 andrei failed different way than earlier with bug#45576
+ rpl_delayed_slave : Bug#11764654 2010-11-09 andrei rpl_delayed_slave fails sporadically in pb
+ rpl_row_binlog_max_cache_size : BUG#14126780 May 29 2012 Vasil Dimov timeout if est number of rows is 3 instead of 4
++rpl_test_framework : Too many servers started, so fails under Galera-enabled MTR
+diff --git a/mysql-test/suite/sys_vars/r/all_vars.result b/mysql-test/suite/sys_vars/r/all_vars.result
+index e042384..4edc039 100644
+--- a/mysql-test/suite/sys_vars/r/all_vars.result
++++ b/mysql-test/suite/sys_vars/r/all_vars.result
+@@ -1,8 +1,8 @@
+ create table t1 (test_name text);
+ create table t2 (variable_name text);
+ load data infile "MYSQLTEST_VARDIR/tmp/sys_vars.all_vars.txt" into table t1;
+-insert into t2 select variable_name from information_schema.global_variables;
+-insert into t2 select variable_name from information_schema.session_variables;
++insert into t2 select variable_name from information_schema.global_variables where variable_name not like 'wsrep_%' and variable_name not like 'innodb_disallow_writes';
++insert into t2 select variable_name from information_schema.session_variables where variable_name not like 'wsrep_%' and variable_name not like 'innodb_disallow_writes';
+ update t2 set variable_name= replace(variable_name, "PERFORMANCE_SCHEMA_", "PFS_");
+ update t2 set variable_name= replace(variable_name, "_HISTORY_LONG_", "_HL_");
+ update t2 set variable_name= replace(variable_name, "_HISTORY_", "_H_");
+diff --git a/mysql-test/suite/sys_vars/t/all_vars.test b/mysql-test/suite/sys_vars/t/all_vars.test
+index c272528..90f977e 100644
+--- a/mysql-test/suite/sys_vars/t/all_vars.test
++++ b/mysql-test/suite/sys_vars/t/all_vars.test
+@@ -49,8 +49,8 @@ create table t2 (variable_name text);
+ --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+ eval load data infile "$MYSQLTEST_VARDIR/tmp/sys_vars.all_vars.txt" into table t1;
+
+-insert into t2 select variable_name from information_schema.global_variables;
+-insert into t2 select variable_name from information_schema.session_variables;
++insert into t2 select variable_name from information_schema.global_variables where variable_name not like 'wsrep_%' and variable_name not like 'innodb_disallow_writes';
++insert into t2 select variable_name from information_schema.session_variables where variable_name not like 'wsrep_%' and variable_name not like 'innodb_disallow_writes';
+
+ # Performance schema variables are too long for files named
+ # 'mysql-test/suite/sys_vars/t/' ...
+diff --git a/mysql-test/suite/wsrep/my.cnf b/mysql-test/suite/wsrep/my.cnf
+new file mode 100644
+index 0000000..81e22a5
+--- /dev/null
++++ b/mysql-test/suite/wsrep/my.cnf
+@@ -0,0 +1,12 @@
++# Use default setting for mysqld processes
++!include include/default_mysqld.cnf
++
++[mysqld.1]
++wsrep_provider=@ENV.WSREP_PROVIDER
++wsrep_cluster_address='gcomm://'
++wsrep_node_address=127.0.0.1
++binlog-format=row
++
++[ENV]
++GALERA_BASE_PORT=@mysqld.1.#galera_port
++
+diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def
+index dd8a6f4..ae0eb82 100644
+--- a/mysql-test/t/disabled.def
++++ b/mysql-test/t/disabled.def
+@@ -18,3 +18,4 @@ ds_mrr-big @solaris : Bug#14168107 2012-04-03 Hemant disabled new test adde
+ mysql_embedded_client_test : Bug#13964673 2012-04-16 amitbha since most of the test cases are failing
+ mysql_client_test_embedded : Bug#16084066 2013-01-08 Disabled since this test is failing
+ file_contents : Fails without bzr revision id
++bootstrap : Galera mysql-wsrep#19 2014-11-21 pstoev Crash in LOGGER::error_log_print when printing a WSREP message
+diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test
+index 2b92d9e..8c0c56f 100644
+--- a/mysql-test/t/mysqltest.test
++++ b/mysql-test/t/mysqltest.test
+@@ -708,7 +708,7 @@ remove_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql;
+
+ # Too many errorcodes specified
+ --error 1
+---exec echo "--error 1,2,3,4,5,6,7,8,9,10,11" | $MYSQL_TEST 2>&1
++--exec echo "--error 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21" | $MYSQL_TEST 2>&1
+
+
+ # ----------------------------------------------------------------------------
+diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
+index 9ef8632..9098cd8 100644
+--- a/mysys/thr_lock.c
++++ b/mysys/thr_lock.c
+@@ -82,7 +82,24 @@ one TL_WRITE_DELAYED lock at the same time as multiple read locks.
+ my_bool thr_lock_inited=0;
+ ulong locks_immediate = 0L, locks_waited = 0L;
+ enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
+-
++#ifdef WITH_WSREP
++static wsrep_thd_is_brute_force_fun wsrep_thd_is_brute_force= NULL;
++static wsrep_abort_thd_fun wsrep_abort_thd= NULL;
++static my_bool wsrep_debug;
++static my_bool wsrep_convert_LOCK_to_trx;
++static wsrep_on_fun wsrep_on = NULL;
++
++void wsrep_thr_lock_init(
++ wsrep_thd_is_brute_force_fun bf_fun, wsrep_abort_thd_fun abort_fun,
++ my_bool debug, my_bool convert_LOCK_to_trx, wsrep_on_fun on_fun
++) {
++ wsrep_thd_is_brute_force = bf_fun;
++ wsrep_abort_thd = abort_fun;
++ wsrep_debug = debug;
++ wsrep_convert_LOCK_to_trx= convert_LOCK_to_trx;
++ wsrep_on = on_fun;
++}
++#endif
+ /* The following constants are only for debug output */
+ #define MAX_THREADS 100
+ #define MAX_LOCKS 100
+@@ -536,6 +553,108 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
+ DBUG_RETURN(result);
+ }
+
++#ifdef WITH_WSREP
++/*
++ * If brute force applier would need to wait for a thr lock,
++ * it needs to make sure that it will get the lock without (too much)
++ * delay.
++ * We identify here the owners of blocking locks and ask them to
++ * abort. We then put our lock request in the first place in the
++ * wait queue. When lock holders abort (one by one) the lock release
++ * algorithm should grant the lock to us. We rely on this and proceed
++ * to wait_for_locks().
++ * wsrep_break_locks() should be called in all the cases, where lock
++ * wait would happen.
++ *
++ * TODO: current implementation might not cover all possible lock wait
++ * situations. This needs an review still.
++ * TODO: lock release, might favor some other lock (instead our bf).
++ * This needs an condition to check for bf locks first.
++ * TODO: we still have a debug fprintf, this should be removed
++ */
++static inline my_bool
++wsrep_break_lock(
++ THR_LOCK_DATA *data, struct st_lock_list *lock_queue1,
++ struct st_lock_list *lock_queue2, struct st_lock_list *wait_queue)
++{
++ if (wsrep_on(data->owner->mysql_thd) &&
++ wsrep_thd_is_brute_force &&
++ wsrep_thd_is_brute_force(data->owner->mysql_thd, TRUE))
++ {
++ THR_LOCK_DATA *holder;
++
++ /* if locking session conversion to transaction has been enabled,
++ we know that this conflicting lock must be read lock and furthermore,
++ lock holder is read-only. It is safe to wait for him.
++ */
++#ifdef TODO
++ if (wsrep_convert_LOCK_to_trx &&
++ (THD*)(data->owner->mysql_thd)->in_lock_tables)
++ {
++ if (wsrep_debug)
++ fprintf(stderr,"WSREP wsrep_break_lock read lock untouched\n");
++ return FALSE;
++ }
++#endif
++ if (wsrep_debug)
++ fprintf(stderr,"WSREP wsrep_break_lock aborting locks\n");
++
++ /* aborting lock holder(s) here */
++ for (holder=(lock_queue1) ? lock_queue1->data : NULL;
++ holder;
++ holder=holder->next)
++ {
++ if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd, TRUE))
++ {
++ wsrep_abort_thd(data->owner->mysql_thd,
++ holder->owner->mysql_thd, FALSE);
++ }
++ else
++ {
++ if (wsrep_debug)
++ fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n");
++ return FALSE;
++ }
++ }
++ for (holder=(lock_queue2) ? lock_queue2->data : NULL;
++ holder;
++ holder=holder->next)
++ {
++ if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd, TRUE))
++ {
++ wsrep_abort_thd(data->owner->mysql_thd,
++ holder->owner->mysql_thd, FALSE);
++ }
++ else
++ {
++ if (wsrep_debug)
++ fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n");
++ return FALSE;
++ }
++ }
++
++ /* Add our lock to the head of the wait queue */
++ if (*(wait_queue->last)==wait_queue->data)
++ {
++ wait_queue->last=&data->next;
++ assert(wait_queue->data==0);
++ }
++ else
++ {
++ assert(wait_queue->data!=0);
++ wait_queue->data->prev=&data->next;
++ }
++ data->next=wait_queue->data;
++ data->prev=&wait_queue->data;
++ wait_queue->data=data;
++ data->cond=get_cond();
++
++ statistic_increment(locks_immediate,&THR_LOCK_lock);
++ return TRUE;
++ }
++ return FALSE;
++}
++#endif
+
+ enum enum_thr_lock_result
+ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+@@ -544,6 +663,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ THR_LOCK *lock=data->lock;
+ enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
+ struct st_lock_list *wait_queue;
++#ifdef WITH_WSREP
++ my_bool wsrep_lock_inserted= FALSE;
++#endif
+ MYSQL_TABLE_WAIT_VARIABLES(locker, state) /* no ';' */
+ DBUG_ENTER("thr_lock");
+
+@@ -613,6 +735,13 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ }
+ if (lock->write.data->type == TL_WRITE_ONLY)
+ {
++#ifdef WITH_WSREP
++ if (wsrep_break_lock(data, &lock->write, NULL, &lock->read_wait))
++ {
++ wsrep_lock_inserted= TRUE;
++ goto wsrep_read_wait;
++ }
++#endif
+ /* We are not allowed to get a READ lock in this case */
+ data->type=TL_UNLOCK;
+ result= THR_LOCK_ABORTED; /* Can't wait for this one */
+@@ -640,6 +769,13 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ lock but a high priority write waiting in the write_wait queue.
+ In the latter case we should yield the lock to the writer.
+ */
++#ifdef WITH_WSREP
++ if (wsrep_break_lock(data, &lock->write, NULL, &lock->read_wait))
++ {
++ wsrep_lock_inserted= TRUE;
++ }
++ wsrep_read_wait:
++#endif
+ wait_queue= &lock->read_wait;
+ }
+ else /* Request for WRITE lock */
+@@ -648,12 +784,25 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ {
+ if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY)
+ {
++#ifdef WITH_WSREP
++ if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait))
++ {
++ wsrep_lock_inserted=TRUE;
++ goto wsrep_write_wait;
++ }
++#endif
+ data->type=TL_UNLOCK;
+ result= THR_LOCK_ABORTED; /* Can't wait for this one */
+ goto end;
+ }
+ if (lock->write.data || lock->read.data)
+ {
++#ifdef WITH_WSREP
++ if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait))
++ {
++ goto end;
++ }
++#endif
+ /* Add delayed write lock to write_wait queue, and return at once */
+ (*lock->write_wait.last)=data;
+ data->prev=lock->write_wait.last;
+@@ -678,6 +827,13 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ /* Allow lock owner to bypass TL_WRITE_ONLY. */
+ if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
+ {
++#ifdef WITH_WSREP
++ if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait))
++ {
++ wsrep_lock_inserted=TRUE;
++ goto wsrep_write_wait;
++ }
++#endif
+ /* We are not allowed to get a lock in this case */
+ data->type=TL_UNLOCK;
+ result= THR_LOCK_ABORTED; /* Can't wait for this one */
+@@ -781,9 +937,20 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d",
+ lock->read.data->owner->thread_id, data->type));
+ }
++#ifdef WITH_WSREP
++ if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait))
++ {
++ wsrep_lock_inserted= TRUE;
++ }
++ wsrep_write_wait:
++#endif
+ wait_queue= &lock->write_wait;
+ }
+ /* Can't get lock yet; Wait for it */
++#ifdef WITH_WSREP
++ if (wsrep_on(data->owner->mysql_thd) && wsrep_lock_inserted)
++ DBUG_RETURN(wait_for_lock(wait_queue, data, 1, lock_wait_timeout));
++#endif
+ result= wait_for_lock(wait_queue, data, 0, lock_wait_timeout);
+ MYSQL_END_TABLE_LOCK_WAIT(locker);
+ DBUG_RETURN(result);
+diff --git a/mysys_ssl/my_default.cc b/mysys_ssl/my_default.cc
+index 6712b79..ff8f44c 100644
+--- a/mysys_ssl/my_default.cc
++++ b/mysys_ssl/my_default.cc
+@@ -110,6 +110,12 @@ static char my_login_file[FN_REFLEN];
+
+ static my_bool defaults_already_read= FALSE;
+
++#ifdef WITH_WSREP
++/* The only purpose of this global array is to hold full name of my.cnf
++ * which seems to be otherwise unavailable */
++char wsrep_defaults_file[FN_REFLEN + 10]={0,};
++char wsrep_defaults_group_suffix[FN_EXTLEN]={0,};
++#endif /* WITH_WREP */
+ /* Set to TRUE, if --no-defaults is found. */
+ static my_bool found_no_defaults= FALSE;
+
+@@ -534,6 +540,13 @@ int get_defaults_options(int argc, char **argv,
+ if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix="))
+ {
+ *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1;
++
++#ifdef WITH_WSREP
++ /* make sure we do this only once - for top-level file */
++ if ('\0' == wsrep_defaults_group_suffix[0])
++ strncpy(wsrep_defaults_group_suffix, *group_suffix, sizeof(wsrep_defaults_group_suffix) - 1);
++#endif /* WITH_WSREP */
++
+ argc--;
+ default_option_count ++;
+ continue;
+@@ -893,6 +906,11 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
+ if ( !(fp = mysql_file_fopen(key_file_cnf, name, O_RDONLY, MYF(0))))
+ return 1; /* Ignore wrong files */
+ }
++#ifdef WITH_WSREP
++ /* make sure we do this only once - for top-level file */
++ if ('\0' == wsrep_defaults_file[0])
++ strncpy(wsrep_defaults_file, name, sizeof(wsrep_defaults_file) - 1);
++#endif /* WITH_WSREP */
+
+ while (mysql_file_getline(buff, sizeof(buff) - 1, fp))
+ {
+diff --git a/mysys_ssl/my_md5.cc b/mysys_ssl/my_md5.cc
+index 4c14366..1ef0b42 100644
+--- a/mysys_ssl/my_md5.cc
++++ b/mysys_ssl/my_md5.cc
+@@ -66,3 +66,34 @@ void compute_md5_hash(char *digest, const char *buf, int len)
+ my_md5_hash((unsigned char*)digest, (unsigned const char*)buf, len);
+ #endif /* HAVE_YASSL */
+ }
++#ifdef WITH_WSREP
++void *wsrep_md5_init()
++{
++#if defined(HAVE_YASSL)
++ TaoCrypt::MD5 *hasher= new TaoCrypt::MD5;
++ return (void*)hasher;
++#elif defined(HAVE_OPENSSL)
++ MD5_CTX *ctx = new MD5_CTX();
++ MD5_Init (ctx);
++ return (void *)ctx;
++#endif /* HAVE_YASSL */
++}
++void wsrep_md5_update(void *ctx, char* buf, int len)
++{
++#if defined(HAVE_YASSL)
++ ((TaoCrypt::MD5 *)ctx)->Update((TaoCrypt::byte *) buf, len);
++#elif defined(HAVE_OPENSSL)
++ MD5_Update((MD5_CTX*)(ctx), buf, len);
++#endif /* HAVE_YASSL */
++}
++void wsrep_compute_md5_hash(char *digest, void *ctx)
++{
++#if defined(HAVE_YASSL)
++ ((TaoCrypt::MD5*)ctx)->Final((TaoCrypt::byte *) digest);
++ delete (TaoCrypt::MD5*)ctx;
++#elif defined(HAVE_OPENSSL)
++ MD5_Final ((unsigned char*)digest, (MD5_CTX*)ctx);
++ delete (MD5_CTX*)ctx;
++#endif /* HAVE_YASSL */
++}
++#endif
+diff --git a/plugin/innodb_memcached/daemon_memcached/CMakeLists.txt b/plugin/innodb_memcached/daemon_memcached/CMakeLists.txt
+index 11a1276..e53980b 100644
+--- a/plugin/innodb_memcached/daemon_memcached/CMakeLists.txt
++++ b/plugin/innodb_memcached/daemon_memcached/CMakeLists.txt
+@@ -78,7 +78,7 @@ SET(MEMCACHED_SOURCES
+ MYSQL_ADD_PLUGIN(libmemcached ${MEMCACHED_SOURCES}
+ MODULE_ONLY MODULE_OUTPUT_NAME "libmemcached")
+
+-TARGET_LINK_LIBRARIES(libmemcached ${LIBEVENT_LIBRARY})
++TARGET_LINK_LIBRARIES(libmemcached ${LIBEVENT_LIBRARY} ${LIBEVENT_LIBRARIES})
+ TARGET_LINK_LIBRARIES(libmemcached memcached_utilities)
+
+ IF(ENABLE_MEMCACHED_SASL)
+diff --git a/plugin/innodb_memcached/innodb_memcache/src/innodb_api.c b/plugin/innodb_memcached/innodb_memcache/src/innodb_api.c
+index 14e2fa7..e062343 100644
+--- a/plugin/innodb_memcached/innodb_memcache/src/innodb_api.c
++++ b/plugin/innodb_memcached/innodb_memcache/src/innodb_api.c
+@@ -666,7 +666,6 @@ innodb_api_copy_mci(
+
+ } else {
+ mci_item->value_str = malloc(data_len);
+-
+ if (!mci_item->value_str) {
+ return(false);
+ }
+diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
+index c043f07..a69d938 100644
+--- a/scripts/CMakeLists.txt
++++ b/scripts/CMakeLists.txt
+@@ -320,6 +320,15 @@ IF(WIN32)
+ INSTALL_SCRIPT(${CMAKE_CURRENT_BINARY_DIR}/${file}.pl COMPONENT Server_Scripts)
+ ENDFOREACH()
+ ELSE()
++ IF(WITH_WSREP)
++ SET(WSREP_BINARIES
++ wsrep_sst_common
++ wsrep_sst_mysqldump
++ wsrep_sst_rsync
++ wsrep_sst_xtrabackup
++ wsrep_sst_xtrabackup-v2
++ )
++ ENDIF()
+ # Configure this one, for testing, but do not install it.
+ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysql_config.pl.in
+ ${CMAKE_CURRENT_BINARY_DIR}/mysql_config.pl ESCAPE_QUOTES @ONLY)
+@@ -339,6 +348,7 @@ ELSE()
+ mysqldumpslow
+ mysqld_multi
+ mysqld_safe
++ ${WSREP_BINARIES}
+ )
+ FOREACH(file ${BIN_SCRIPTS})
+ IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh)
+diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh
+index 3b588ef..4520503 100644
+--- a/scripts/mysqld_safe.sh
++++ b/scripts/mysqld_safe.sh
+@@ -141,7 +141,7 @@ log_notice () {
+ }
+
+ eval_log_error () {
+- cmd="$1"
++ local cmd="$1"
+ case $logging in
+ file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;;
+ syslog)
+@@ -169,6 +169,78 @@ shell_quote_string() {
+ echo "$1" | sed -e 's,\([^a-zA-Z0-9/_.=-]\),\\\1,g'
+ }
+
++wsrep_pick_url() {
++ [ $# -eq 0 ] && return 0
++
++ log_error "WSREP: 'wsrep_urls' is DEPRECATED! Use wsrep_cluster_address to specify multiple addresses instead."
++
++ if ! which nc >/dev/null; then
++ log_error "ERROR: nc tool not found in PATH! Make sure you have it installed."
++ return 1
++ fi
++
++ local url
++ # Assuming URL in the form scheme://host:port
++ # If host and port are not NULL, the liveness of URL is assumed to be tested
++ # If port part is absent, the url is returned literally and unconditionally
++ # If every URL has port but none is reachable, nothing is returned
++ for url in `echo $@ | sed s/,/\ /g` 0; do
++ local host=`echo $url | cut -d \: -f 2 | sed s/^\\\/\\\///`
++ local port=`echo $url | cut -d \: -f 3`
++ [ -z "$port" ] && break
++ nc -z "$host" $port >/dev/null && break
++ done
++
++ if [ "$url" == "0" ]; then
++ log_error "ERROR: none of the URLs in '$@' is reachable."
++ return 1
++ fi
++
++ echo $url
++}
++
++# Run mysqld with --wsrep-recover and parse recovered position from log.
++# Position will be stored in wsrep_start_position_opt global.
++wsrep_start_position_opt=""
++wsrep_recover_position() {
++ local mysqld_cmd="$@"
++ local euid=$(id -u)
++ local ret=0
++
++ local wr_logfile=$(mktemp $DATADIR/wsrep_recovery.XXXXXX)
++
++ [ "$euid" = "0" ] && chown $user $wr_logfile
++ chmod 600 $wr_logfile
++
++ local wr_pidfile="$DATADIR/"`@HOSTNAME@`"-recover.pid"
++
++ local wr_options="--log_error='$wr_logfile' --pid-file='$wr_pidfile'"
++
++ log_notice "WSREP: Running position recovery with $wr_options"
++
++ eval_log_error "$mysqld_cmd --wsrep_recover $wr_options"
++
++ local rp="$(grep 'WSREP: Recovered position:' $wr_logfile)"
++ if [ -z "$rp" ]; then
++ local skipped="$(grep WSREP $wr_logfile | grep 'skipping position recovery')"
++ if [ -z "$skipped" ]; then
++ log_error "WSREP: Failed to recover position: " `cat $wr_logfile`;
++ ret=1
++ else
++ log_notice "WSREP: Position recovery skipped"
++ fi
++ else
++ local start_pos="$(echo $rp | sed 's/.*WSREP\:\ Recovered\ position://' \
++ | sed 's/^[ \t]*//')"
++ log_notice "WSREP: Recovered position $start_pos"
++ wsrep_start_position_opt="--wsrep_start_position=$start_pos"
++ fi
++
++ rm $wr_logfile
++
++ return $ret
++}
++
+ parse_arguments() {
+ # We only need to pass arguments through to the server if we don't
+ # handle them here. So, we collect unrecognized options (passed on
+@@ -224,7 +296,13 @@ parse_arguments() {
+ --skip-syslog) want_syslog=0 ;;
+ --syslog-tag=*) syslog_tag="$val" ;;
+ --timezone=*) TZ="$val"; export TZ; ;;
+-
++ --wsrep[-_]urls=*) wsrep_urls="$val"; ;;
++ --wsrep[-_]provider=*)
++ if test -n "$val" && test "$val" != "none"
++ then
++ wsrep_restart=1
++ fi
++ ;;
+ --help) usage ;;
+
+ *)
+@@ -770,7 +848,8 @@ do
+ done
+ cmd="$cmd $args"
+ # Avoid 'nohup: ignoring input' warning
+-test -n "$NOHUP_NICENESS" && cmd="$cmd < /dev/null"
++nohup_redir=""
++test -n "$NOHUP_NICENESS" && nohup_redir=" < /dev/null"
+
+ log_notice "Starting $MYSQLD daemon with databases from $DATADIR"
+
+@@ -781,13 +860,28 @@ max_fast_restarts=5
+ # flag whether a usable sleep command exists
+ have_sleep=1
+
++# maximum number of wsrep restarts
++max_wsrep_restarts=0
++
+ while true
+ do
+ rm -f $safe_mysql_unix_port "$pid_file" # Some extra safety
+
+ start_time=`date +%M%S`
+
+- eval_log_error "$cmd"
++ # this sets wsrep_start_position_opt
++ wsrep_recover_position "$cmd"
++
++ [ $? -ne 0 ] && exit 1 #
++
++ [ -n "$wsrep_urls" ] && url=`wsrep_pick_url $wsrep_urls` # check connect address
++
++ if [ -z "$url" ]
++ then
++ eval_log_error "$cmd $wsrep_start_position_opt $nohup_redir"
++ else
++ eval_log_error "$cmd $wsrep_start_position_opt --wsrep_cluster_address=$url $nohup_redir"
++ fi
+
+ if [ $want_syslog -eq 0 -a ! -f "$err_log" ]; then
+ touch "$err_log" # hypothetical: log was renamed but not
+@@ -857,6 +951,20 @@ do
+ I=`expr $I + 1`
+ done
+ fi
++
++ if [ -n "$wsrep_restart" ]
++ then
++ if [ $wsrep_restart -le $max_wsrep_restarts ]
++ then
++ wsrep_restart=`expr $wsrep_restart + 1`
++ log_notice "WSREP: sleeping 15 seconds before restart"
++ sleep 15
++ else
++ log_notice "WSREP: not restarting wsrep node automatically"
++ break
++ fi
++ fi
++
+ log_notice "mysqld restarted"
+ done
+
+diff --git a/scripts/wsrep_sst_common.sh b/scripts/wsrep_sst_common.sh
+new file mode 100644
+index 0000000..46f4076
+--- /dev/null
++++ b/scripts/wsrep_sst_common.sh
+@@ -0,0 +1,187 @@
++# Copyright (C) 2012-2014 Codership Oy
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; see the file COPYING. If not, write to the
++# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston
++# MA 02110-1301 USA.
++
++# This is a common command line parser to be sourced by other SST scripts
++
++set -u
++
++WSREP_SST_OPT_BYPASS=0
++WSREP_SST_OPT_BINLOG=""
++WSREP_SST_OPT_DATA=""
++WSREP_SST_OPT_AUTH=""
++
++while [ $# -gt 0 ]; do
++case "$1" in
++ '--address')
++ readonly WSREP_SST_OPT_ADDR="$2"
++ shift
++ ;;
++ '--auth')
++ WSREP_SST_OPT_AUTH="$2"
++ shift
++ ;;
++ '--bypass')
++ WSREP_SST_OPT_BYPASS=1
++ ;;
++ '--datadir')
++ readonly WSREP_SST_OPT_DATA="$2"
++ shift
++ ;;
++ '--defaults-file')
++ readonly WSREP_SST_OPT_CONF="$2"
++ shift
++ ;;
++ '--defaults-group-suffix')
++ readonly WSREP_SST_OPT_CONF_SUFFIX="$2"
++ shift
++ ;;
++ '--host')
++ readonly WSREP_SST_OPT_HOST="$2"
++ shift
++ ;;
++ '--local-port')
++ readonly WSREP_SST_OPT_LPORT="$2"
++ shift
++ ;;
++ '--parent')
++ readonly WSREP_SST_OPT_PARENT="$2"
++ shift
++ ;;
++ '--password')
++ WSREP_SST_OPT_PSWD="$2"
++ shift
++ ;;
++ '--port')
++ readonly WSREP_SST_OPT_PORT="$2"
++ shift
++ ;;
++ '--role')
++ readonly WSREP_SST_OPT_ROLE="$2"
++ shift
++ ;;
++ '--socket')
++ readonly WSREP_SST_OPT_SOCKET="$2"
++ shift
++ ;;
++ '--user')
++ WSREP_SST_OPT_USER="$2"
++ shift
++ ;;
++ '--gtid')
++ readonly WSREP_SST_OPT_GTID="$2"
++ shift
++ ;;
++ '--binlog')
++ WSREP_SST_OPT_BINLOG="$2"
++ shift
++ ;;
++ *) # must be command
++ # usage
++ # exit 1
++ ;;
++esac
++shift
++done
++readonly WSREP_SST_OPT_BYPASS
++readonly WSREP_SST_OPT_BINLOG
++
++# try to use my_print_defaults, mysql and mysqldump that come with the sources
++# (for MTR suite)
++SCRIPTS_DIR="$(cd $(dirname "$0"); pwd -P)"
++EXTRA_DIR="$SCRIPTS_DIR/../extra"
++CLIENT_DIR="$SCRIPTS_DIR/../client"
++
++if [ -x "$CLIENT_DIR/mysql" ]; then
++ MYSQL_CLIENT="$CLIENT_DIR/mysql"
++else
++ MYSQL_CLIENT=$(which mysql)
++fi
++
++if [ -x "$CLIENT_DIR/mysqldump" ]; then
++ MYSQLDUMP="$CLIENT_DIR/mysqldump"
++else
++ MYSQLDUMP=$(which mysqldump)
++fi
++
++if [ -x "$SCRIPTS_DIR/my_print_defaults" ]; then
++ MY_PRINT_DEFAULTS="$SCRIPTS_DIR/my_print_defaults"
++elif [ -x "$EXTRA_DIR/my_print_defaults" ]; then
++ MY_PRINT_DEFAULTS="$EXTRA_DIR/my_print_defaults"
++else
++ MY_PRINT_DEFAULTS=$(which my_print_defaults)
++fi
++
++# For Bug:1200727
++if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF sst | grep -q "wsrep_sst_auth";then
++ if [ -z "$WSREP_SST_OPT_AUTH" -o "$WSREP_SST_OPT_AUTH" = "(null)" ];then
++ WSREP_SST_OPT_AUTH=$(my_print_defaults -c $WSREP_SST_OPT_CONF sst | grep -- "--wsrep_sst_auth" | cut -d= -f2)
++ fi
++fi
++
++if [ -n "${WSREP_SST_OPT_DATA:-}" ]
++then
++ SST_PROGRESS_FILE="$WSREP_SST_OPT_DATA/sst_in_progress"
++else
++ SST_PROGRESS_FILE=""
++fi
++
++
++wsrep_log()
++{
++ # echo everything to stderr so that it gets into common error log
++ # deliberately made to look different from the rest of the log
++ local readonly tst="$(date +%Y%m%d\ %H:%M:%S.%N | cut -b -21)"
++ echo "WSREP_SST: $* ($tst)" >&2
++}
++
++wsrep_log_error()
++{
++ wsrep_log "[ERROR] $*"
++}
++
++wsrep_log_info()
++{
++ wsrep_log "[INFO] $*"
++}
++
++wsrep_cleanup_progress_file()
++{
++ [ -n "$SST_PROGRESS_FILE" ] && rm -f "$SST_PROGRESS_FILE" 2>/dev/null
++}
++
++wsrep_check_program()
++{
++ local prog=$1
++
++ if ! which $prog >/dev/null
++ then
++ echo "'$prog' not found in PATH"
++ return 2 # no such file or directory
++ fi
++}
++
++wsrep_check_programs()
++{
++ local ret=0
++
++ while [ $# -gt 0 ]
++ do
++ wsrep_check_program $1 || ret=$?
++ shift
++ done
++
++ return $ret
++}
+diff --git a/scripts/wsrep_sst_mysqldump.sh b/scripts/wsrep_sst_mysqldump.sh
+new file mode 100644
+index 0000000..89eb755
+--- /dev/null
++++ b/scripts/wsrep_sst_mysqldump.sh
+@@ -0,0 +1,136 @@
++#!/bin/bash -e
++# Copyright (C) 2009 Codership Oy
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; see the file COPYING. If not, write to the
++# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston
++# MA 02110-1301 USA.
++
++# This is a reference script for mysqldump-based state snapshot tansfer
++
++. $(dirname $0)/wsrep_sst_common
++
++EINVAL=22
++
++local_ip()
++{
++ PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
++
++ [ "$1" = "127.0.0.1" ] && return 0
++ [ "$1" = "localhost" ] && return 0
++ [ "$1" = "$(hostname -s)" ] && return 0
++ [ "$1" = "$(hostname -f)" ] && return 0
++ [ "$1" = "$(hostname -d)" ] && return 0
++
++ # Now if ip program is not found in the path, we can't return 0 since
++ # it would block any address. Thankfully grep should fail in this case
++ ip route get "$1" | grep local >/dev/null && return 0
++
++ return 1
++}
++
++if test -z "$WSREP_SST_OPT_USER"; then wsrep_log_error "USER cannot be nil"; exit $EINVAL; fi
++if test -z "$WSREP_SST_OPT_HOST"; then wsrep_log_error "HOST cannot be nil"; exit $EINVAL; fi
++if test -z "$WSREP_SST_OPT_PORT"; then wsrep_log_error "PORT cannot be nil"; exit $EINVAL; fi
++if test -z "$WSREP_SST_OPT_LPORT"; then wsrep_log_error "LPORT cannot be nil"; exit $EINVAL; fi
++if test -z "$WSREP_SST_OPT_SOCKET";then wsrep_log_error "SOCKET cannot be nil";exit $EINVAL; fi
++if test -z "$WSREP_SST_OPT_GTID"; then wsrep_log_error "GTID cannot be nil"; exit $EINVAL; fi
++
++if local_ip $WSREP_SST_OPT_HOST && \
++ [ "$WSREP_SST_OPT_PORT" = "$WSREP_SST_OPT_LPORT" ]
++then
++ wsrep_log_error \
++ "destination address '$WSREP_SST_OPT_HOST:$WSREP_SST_OPT_PORT' matches source address."
++ exit $EINVAL
++fi
++
++# Check client version
++CLIENT_MINOR=$(mysql --version | cut -d ' ' -f 6 | cut -d '.' -f 2)
++if [ $CLIENT_MINOR -lt "6" ]
++then
++ $MYSQL_CLIENT --version >&2
++ wsrep_log_error "this operation requires MySQL client version 5.6.x"
++ exit $EINVAL
++fi
++
++# For Bug:1293798
++if [ -z "$WSREP_SST_OPT_PSWD" -a -n "$WSREP_SST_OPT_AUTH" ]; then
++ WSREP_SST_OPT_USER=$(echo $WSREP_SST_OPT_AUTH | cut -d: -f1)
++ WSREP_SST_OPT_PSWD=$(echo $WSREP_SST_OPT_AUTH | cut -d: -f2)
++fi
++AUTH="-u$WSREP_SST_OPT_USER"
++if test -n "$WSREP_SST_OPT_PSWD"; then AUTH="$AUTH -p$WSREP_SST_OPT_PSWD"; fi
++
++STOP_WSREP="SET wsrep_on=OFF;"
++
++# NOTE: we don't use --routines here because we're dumping mysql.proc table
++MYSQLDUMP="$MYSQLDUMP $AUTH -S$WSREP_SST_OPT_SOCKET \
++--add-drop-database --add-drop-table --skip-add-locks --create-options \
++--disable-keys --extended-insert --skip-lock-tables --quick --set-charset \
++--skip-comments --flush-privileges --all-databases --events"
++
++# mysqldump cannot restore CSV tables, fix this issue
++CSV_TABLES_FIX="
++set sql_mode='';
++
++USE mysql;
++
++SET @str = IF (@@have_csv = 'YES', 'CREATE TABLE IF NOT EXISTS general_log (event_time TIMESTAMP NOT NULL, user_host MEDIUMTEXT NOT NULL, thread_id INTEGER NOT NULL, server_id INTEGER UNSIGNED NOT NULL, command_type VARCHAR(64) NOT NULL,argument MEDIUMTEXT NOT NULL) engine=CSV CHARACTER SET utf8 comment=\"General log\"', 'SET @dummy = 0');
++
++PREPARE stmt FROM @str;
++EXECUTE stmt;
++DROP PREPARE stmt;
++
++SET @str = IF (@@have_csv = 'YES', 'CREATE TABLE IF NOT EXISTS slow_log (start_time TIMESTAMP NOT NULL, user_host MEDIUMTEXT NOT NULL, query_time TIME NOT NULL, lock_time TIME NOT NULL, rows_sent INTEGER NOT NULL, rows_examined INTEGER NOT NULL, db VARCHAR(512) NOT NULL, last_insert_id INTEGER NOT NULL, insert_id INTEGER NOT NULL, server_id INTEGER UNSIGNED NOT NULL, sql_text MEDIUMTEXT NOT NULL) engine=CSV CHARACTER SET utf8 comment=\"Slow log\"', 'SET @dummy = 0');
++
++PREPARE stmt FROM @str;
++EXECUTE stmt;
++DROP PREPARE stmt;"
++
++SET_START_POSITION="SET GLOBAL wsrep_start_position='$WSREP_SST_OPT_GTID';"
++
++MYSQL="$MYSQL_CLIENT $AUTH -h$WSREP_SST_OPT_HOST -P$WSREP_SST_OPT_PORT "\
++"--disable-reconnect --connect_timeout=10"
++
++# need to disable logging when loading the dump
++# reason is that dump contains ALTER TABLE for log tables, and
++# this causes an error if logging is enabled
++GENERAL_LOG_OPT=`$MYSQL --skip-column-names -e"$STOP_WSREP SELECT @@GENERAL_LOG"`
++SLOW_LOG_OPT=`$MYSQL --skip-column-names -e"$STOP_WSREP SELECT @@SLOW_QUERY_LOG"`
++$MYSQL -e"$STOP_WSREP SET GLOBAL GENERAL_LOG=OFF"
++$MYSQL -e"$STOP_WSREP SET GLOBAL SLOW_QUERY_LOG=OFF"
++
++# commands to restore log settings
++RESTORE_GENERAL_LOG="SET GLOBAL GENERAL_LOG=$GENERAL_LOG_OPT;"
++RESTORE_SLOW_QUERY_LOG="SET GLOBAL SLOW_QUERY_LOG=$SLOW_LOG_OPT;"
++
++# reset master for 5.6 to clear GTID_EXECUTED
++RESET_MASTER="RESET MASTER;"
++
++
++if [ $WSREP_SST_OPT_BYPASS -eq 0 ]
++then
++ # commented out from dump command for 5.6: && echo $CSV_TABLES_FIX \
++ # error is ignored because joiner binlog might be disabled.
++ # and if joiner binlog is disabled, 'RESET MASTER' returns error
++ # ERROR 1186 (HY000) at line 2: Binlog closed, cannot RESET MASTER
++ (echo $STOP_WSREP && echo $RESET_MASTER) | $MYSQL || true
++ (echo $STOP_WSREP && $MYSQLDUMP \
++ && echo $RESTORE_GENERAL_LOG && echo $RESTORE_SLOW_QUERY_LOG \
++ && echo $SET_START_POSITION \
++ || echo "SST failed to complete;") | $MYSQL
++else
++ wsrep_log_info "Bypassing state dump."
++ echo $SET_START_POSITION | $MYSQL
++fi
++
++#
+diff --git a/scripts/wsrep_sst_rsync.sh b/scripts/wsrep_sst_rsync.sh
+new file mode 100644
+index 0000000..8998f73
+--- /dev/null
++++ b/scripts/wsrep_sst_rsync.sh
+@@ -0,0 +1,329 @@
++#!/bin/bash -ue
++
++# Copyright (C) 2010-2014 Codership Oy
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; see the file COPYING. If not, write to the
++# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston
++# MA 02110-1301 USA.
++
++# This is a reference script for rsync-based state snapshot tansfer
++
++RSYNC_PID=
++RSYNC_CONF=
++OS=$(uname)
++[ "$OS" == "Darwin" ] && export -n LD_LIBRARY_PATH
++
++# Setting the path for lsof on CentOS
++export PATH="/usr/sbin:/sbin:$PATH"
++
++. $(dirname $0)/wsrep_sst_common
++
++wsrep_check_programs rsync
++
++cleanup_joiner()
++{
++ wsrep_log_info "Joiner cleanup."
++ local PID=$(cat "$RSYNC_PID" 2>/dev/null || echo 0)
++ [ "0" != "$PID" ] && kill $PID && sleep 0.5 && kill -9 $PID >/dev/null 2>&1 \
++ || :
++ rm -rf "$RSYNC_CONF"
++ rm -rf "$MAGIC_FILE"
++ rm -rf "$RSYNC_PID"
++ wsrep_log_info "Joiner cleanup done."
++ if [ "${WSREP_SST_OPT_ROLE}" = "joiner" ];then
++ wsrep_cleanup_progress_file
++ fi
++}
++
++check_pid()
++{
++ local pid_file=$1
++ [ -r "$pid_file" ] && ps -p $(cat $pid_file) >/dev/null 2>&1
++}
++
++check_pid_and_port()
++{
++ local pid_file=$1
++ local rsync_pid=$2
++ local rsync_port=$3
++
++ local port_info=$(lsof -i :$rsync_port -Pn 2>/dev/null | \
++ grep "(LISTEN)")
++ local is_rsync=$(echo $port_info | \
++ grep -w '^rsync[[:space:]]\+'"$rsync_pid" 2>/dev/null)
++
++ if [ -n "$port_info" -a -z "$is_rsync" ]; then
++ wsrep_log_error "rsync daemon port '$rsync_port' has been taken"
++ exit 16 # EBUSY
++ fi
++ check_pid $pid_file && \
++ [ -n "$port_info" ] && [ -n "$is_rsync" ] && \
++ [ $(cat $pid_file) -eq $rsync_pid ]
++}
++
++MAGIC_FILE="$WSREP_SST_OPT_DATA/rsync_sst_complete"
++rm -rf "$MAGIC_FILE"
++
++BINLOG_TAR_FILE="$WSREP_SST_OPT_DATA/wsrep_sst_binlog.tar"
++BINLOG_N_FILES=1
++rm -f "$BINLOG_TAR_FILE" || :
++
++if ! [ -z $WSREP_SST_OPT_BINLOG ]
++then
++ BINLOG_DIRNAME=$(dirname $WSREP_SST_OPT_BINLOG)
++ BINLOG_FILENAME=$(basename $WSREP_SST_OPT_BINLOG)
++fi
++
++WSREP_LOG_DIR=${WSREP_LOG_DIR:-""}
++# if WSREP_LOG_DIR env. variable is not set, try to get it from my.cnf
++if [ -z "$WSREP_LOG_DIR" ]; then
++ WSREP_LOG_DIR=$($MY_PRINT_DEFAULTS --defaults-file \
++ "$WSREP_SST_OPT_CONF" mysqld server mysqld-5.6 \
++ | grep -- '--innodb[-_]log[-_]group[-_]home[-_]dir=' \
++ | cut -b 29- )
++fi
++
++if [ -n "$WSREP_LOG_DIR" ]; then
++ # handle both relative and absolute paths
++ WSREP_LOG_DIR=$(cd $WSREP_SST_OPT_DATA; mkdir -p "$WSREP_LOG_DIR"; cd $WSREP_LOG_DIR; pwd -P)
++else
++ # default to datadir
++ WSREP_LOG_DIR=$(cd $WSREP_SST_OPT_DATA; pwd -P)
++fi
++
++# Old filter - include everything except selected
++# FILTER=(--exclude '*.err' --exclude '*.pid' --exclude '*.sock' \
++# --exclude '*.conf' --exclude core --exclude 'galera.*' \
++# --exclude grastate.txt --exclude '*.pem' \
++# --exclude '*.[0-9][0-9][0-9][0-9][0-9][0-9]' --exclude '*.index')
++
++# New filter - exclude everything except dirs (schemas) and innodb files
++FILTER=(-f '- /lost+found' -f '- /.fseventsd' -f '- /.Trashes'
++ -f '+ /wsrep_sst_binlog.tar' -f '+ /ib_lru_dump' -f '+ /ibdata*' -f '+ /*/' -f '- /*')
++
++if [ "$WSREP_SST_OPT_ROLE" = "donor" ]
++then
++
++ if [ $WSREP_SST_OPT_BYPASS -eq 0 ]
++ then
++
++ FLUSHED="$WSREP_SST_OPT_DATA/tables_flushed"
++ rm -rf "$FLUSHED"
++
++ # Use deltaxfer only for WAN
++ inv=$(basename $0)
++ [ "$inv" = "wsrep_sst_rsync_wan" ] && WHOLE_FILE_OPT="" \
++ || WHOLE_FILE_OPT="--whole-file"
++
++ echo "flush tables"
++
++ # wait for tables flushed and state ID written to the file
++ while [ ! -r "$FLUSHED" ] && ! grep -q ':' "$FLUSHED" >/dev/null 2>&1
++ do
++ sleep 0.2
++ done
++
++ STATE="$(cat $FLUSHED)"
++ rm -rf "$FLUSHED"
++
++ sync
++
++ if ! [ -z $WSREP_SST_OPT_BINLOG ]
++ then
++ # Prepare binlog files
++ pushd $BINLOG_DIRNAME &> /dev/null
++ binlog_files_full=$(tail -n $BINLOG_N_FILES ${BINLOG_FILENAME}.index)
++ binlog_files=""
++ for ii in $binlog_files_full
++ do
++ binlog_files="$binlog_files $(basename $ii)"
++ done
++ if ! [ -z "$binlog_files" ]
++ then
++ wsrep_log_info "Preparing binlog files for transfer:"
++ tar -cvf $BINLOG_TAR_FILE $binlog_files >&2
++ fi
++ popd &> /dev/null
++ fi
++
++ # first, the normal directories, so that we can detect incompatible protocol
++ RC=0
++ rsync --owner --group --perms --links --specials \
++ --ignore-times --inplace --dirs --delete --quiet \
++ $WHOLE_FILE_OPT "${FILTER[@]}" "$WSREP_SST_OPT_DATA/" \
++ rsync://$WSREP_SST_OPT_ADDR >&2 || RC=$?
++
++ if [ "$RC" -ne 0 ]; then
++ wsrep_log_error "rsync returned code $RC:"
++
++ case $RC in
++ 12) RC=71 # EPROTO
++ wsrep_log_error \
++ "rsync server on the other end has incompatible protocol. " \
++ "Make sure you have the same version of rsync on all nodes."
++ ;;
++ 22) RC=12 # ENOMEM
++ ;;
++ *) RC=255 # unknown error
++ ;;
++ esac
++ exit $RC
++ fi
++
++ # second, we transfer InnoDB log files
++ rsync --owner --group --perms --links --specials \
++ --ignore-times --inplace --dirs --delete --quiet \
++ $WHOLE_FILE_OPT -f '+ /ib_logfile[0-9]*' -f '- **' "$WSREP_LOG_DIR/" \
++ rsync://$WSREP_SST_OPT_ADDR-log_dir >&2 || RC=$?
++
++ if [ $RC -ne 0 ]; then
++ wsrep_log_error "rsync innodb_log_group_home_dir returned code $RC:"
++ exit 255 # unknown error
++ fi
++
++ # then, we parallelize the transfer of database directories, use . so that pathconcatenation works
++ pushd "$WSREP_SST_OPT_DATA" >/dev/null
++
++ count=1
++ [ "$OS" == "Linux" ] && count=$(grep -c processor /proc/cpuinfo)
++ [ "$OS" == "Darwin" -o "$OS" == "FreeBSD" ] && count=$(sysctl -n hw.ncpu)
++
++ find . -maxdepth 1 -mindepth 1 -type d -print0 | xargs -I{} -0 -P $count \
++ rsync --owner --group --perms --links --specials \
++ --ignore-times --inplace --recursive --delete --quiet \
++ $WHOLE_FILE_OPT --exclude '*/ib_logfile*' "$WSREP_SST_OPT_DATA"/{}/ \
++ rsync://$WSREP_SST_OPT_ADDR/{} >&2 || RC=$?
++
++ popd >/dev/null
++
++ if [ $RC -ne 0 ]; then
++ wsrep_log_error "find/rsync returned code $RC:"
++ exit 255 # unknown error
++ fi
++
++ else # BYPASS
++ wsrep_log_info "Bypassing state dump."
++ STATE="$WSREP_SST_OPT_GTID"
++ fi
++
++ echo "continue" # now server can resume updating data
++
++ echo "$STATE" > "$MAGIC_FILE"
++ rsync --archive --quiet --checksum "$MAGIC_FILE" rsync://$WSREP_SST_OPT_ADDR
++
++ echo "done $STATE"
++
++elif [ "$WSREP_SST_OPT_ROLE" = "joiner" ]
++then
++ wsrep_check_programs lsof
++
++ touch $SST_PROGRESS_FILE
++ MYSQLD_PID=$WSREP_SST_OPT_PARENT
++
++ MODULE="rsync_sst"
++
++ RSYNC_PID="$WSREP_SST_OPT_DATA/$MODULE.pid"
++
++ if check_pid $RSYNC_PID
++ then
++ wsrep_log_error "rsync daemon already running."
++ exit 114 # EALREADY
++ fi
++ rm -rf "$RSYNC_PID"
++
++ ADDR=$WSREP_SST_OPT_ADDR
++ RSYNC_PORT=$(echo $ADDR | awk -F ':' '{ print $2 }')
++ if [ -z "$RSYNC_PORT" ]
++ then
++ RSYNC_PORT=4444
++ ADDR="$(echo $ADDR | awk -F ':' '{ print $1 }'):$RSYNC_PORT"
++ fi
++
++ trap "exit 32" HUP PIPE
++ trap "exit 3" INT TERM ABRT
++ trap cleanup_joiner EXIT
++
++ RSYNC_CONF="$WSREP_SST_OPT_DATA/$MODULE.conf"
++
++cat << EOF > "$RSYNC_CONF"
++pid file = $RSYNC_PID
++use chroot = no
++read only = no
++timeout = 300
++[$MODULE]
++ path = $WSREP_SST_OPT_DATA
++[$MODULE-log_dir]
++ path = $WSREP_LOG_DIR
++EOF
++
++# rm -rf "$DATA"/ib_logfile* # we don't want old logs around
++
++ # listen at all interfaces (for firewalled setups)
++ rsync --daemon --no-detach --port $RSYNC_PORT --config "$RSYNC_CONF" &
++ RSYNC_REAL_PID=$!
++
++ until check_pid_and_port $RSYNC_PID $RSYNC_REAL_PID $RSYNC_PORT
++ do
++ sleep 0.2
++ done
++
++ echo "ready $ADDR/$MODULE"
++
++ # wait for SST to complete by monitoring magic file
++ while [ ! -r "$MAGIC_FILE" ] && check_pid "$RSYNC_PID" && \
++ ps -p $MYSQLD_PID >/dev/null
++ do
++ sleep 1
++ done
++
++ if ! ps -p $MYSQLD_PID >/dev/null
++ then
++ wsrep_log_error \
++ "Parent mysqld process (PID:$MYSQLD_PID) terminated unexpectedly."
++ exit 32
++ fi
++
++ if ! [ -z $WSREP_SST_OPT_BINLOG ]
++ then
++
++ pushd $BINLOG_DIRNAME &> /dev/null
++ if [ -f $BINLOG_TAR_FILE ]
++ then
++ # Clean up old binlog files first
++ rm -f ${BINLOG_FILENAME}.*
++ wsrep_log_info "Extracting binlog files:"
++ tar -xvf $BINLOG_TAR_FILE >&2
++ for ii in $(ls -1 ${BINLOG_FILENAME}.*)
++ do
++ echo ${BINLOG_DIRNAME}/${ii} >> ${BINLOG_FILENAME}.index
++ done
++ fi
++ popd &> /dev/null
++ fi
++ if [ -r "$MAGIC_FILE" ]
++ then
++ cat "$MAGIC_FILE" # output UUID:seqno
++ else
++ # this message should cause joiner to abort
++ echo "rsync process ended without creating '$MAGIC_FILE'"
++ fi
++ wsrep_cleanup_progress_file
++# cleanup_joiner
++else
++ wsrep_log_error "Unrecognized role: '$WSREP_SST_OPT_ROLE'"
++ exit 22 # EINVAL
++fi
++
++rm -f $BINLOG_TAR_FILE || :
++
++exit 0
+diff --git a/scripts/wsrep_sst_xtrabackup-v2.sh b/scripts/wsrep_sst_xtrabackup-v2.sh
+new file mode 100644
+index 0000000..1d1267d
+--- /dev/null
++++ b/scripts/wsrep_sst_xtrabackup-v2.sh
+@@ -0,0 +1,930 @@
++#!/bin/bash -ue
++# Copyright (C) 2013 Percona Inc
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; see the file COPYING. If not, write to the
++# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston
++# MA 02110-1301 USA.
++
++# Documentation: http://www.percona.com/doc/percona-xtradb-cluster/manual/xtrabackup_sst.html
++# Make sure to read that before proceeding!
++
++
++
++
++. $(dirname $0)/wsrep_sst_common
++
++ealgo=""
++ekey=""
++ekeyfile=""
++encrypt=0
++nproc=1
++ecode=0
++XTRABACKUP_PID=""
++SST_PORT=""
++REMOTEIP=""
++tcert=""
++tpem=""
++tkey=""
++sockopt=""
++progress=""
++ttime=0
++totime=0
++lsn=""
++incremental=0
++ecmd=""
++rlimit=""
++# Initially
++stagemsg="${WSREP_SST_OPT_ROLE}"
++cpat=""
++speciald=0
++ib_home_dir=""
++ib_log_dir=""
++
++sfmt="tar"
++strmcmd=""
++tfmt=""
++tcmd=""
++rebuild=0
++rebuildcmd=""
++payload=0
++pvformat="-F '%N => Rate:%r Avg:%a Elapsed:%t %e Bytes: %b %p' "
++pvopts="-f -i 10 -N $WSREP_SST_OPT_ROLE "
++STATDIR=""
++uextra=0
++disver=""
++
++tmpopts=""
++itmpdir=""
++xtmpdir=""
++
++scomp=""
++sdecomp=""
++
++if which pv &>/dev/null && pv --help | grep -q FORMAT;then
++ pvopts+=$pvformat
++fi
++pcmd="pv $pvopts"
++declare -a RC
++
++INNOBACKUPEX_BIN=innobackupex
++readonly AUTH=(${WSREP_SST_OPT_AUTH//:/ })
++DATA="${WSREP_SST_OPT_DATA}"
++INFO_FILE="xtrabackup_galera_info"
++IST_FILE="xtrabackup_ist"
++MAGIC_FILE="${DATA}/${INFO_FILE}"
++
++# Setting the path for ss and ip
++export PATH="/usr/sbin:/sbin:$PATH"
++
++timeit(){
++ local stage=$1
++ shift
++ local cmd="$@"
++ local x1 x2 took extcode
++
++ if [[ $ttime -eq 1 ]];then
++ x1=$(date +%s)
++ wsrep_log_info "Evaluating $cmd"
++ eval "$cmd"
++ extcode=$?
++ x2=$(date +%s)
++ took=$(( x2-x1 ))
++ wsrep_log_info "NOTE: $stage took $took seconds"
++ totime=$(( totime+took ))
++ else
++ wsrep_log_info "Evaluating $cmd"
++ eval "$cmd"
++ extcode=$?
++ fi
++ return $extcode
++}
++
++get_keys()
++{
++ # $encrypt -eq 1 is for internal purposes only
++ if [[ $encrypt -ge 2 || $encrypt -eq -1 ]];then
++ return
++ fi
++
++ if [[ $encrypt -eq 0 ]];then
++ if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF xtrabackup | grep -q encrypt;then
++ wsrep_log_error "Unexpected option combination. SST may fail. Refer to http://www.percona.com/doc/percona-xtradb-cluster/manual/xtrabackup_sst.html "
++ fi
++ return
++ fi
++
++ if [[ $sfmt == 'tar' ]];then
++ wsrep_log_info "NOTE: Xtrabackup-based encryption - encrypt=1 - cannot be enabled with tar format"
++ encrypt=-1
++ return
++ fi
++
++ wsrep_log_info "Xtrabackup based encryption enabled in my.cnf - Supported only from Xtrabackup 2.1.4"
++
++ if [[ -z $ealgo ]];then
++ wsrep_log_error "FATAL: Encryption algorithm empty from my.cnf, bailing out"
++ exit 3
++ fi
++
++ if [[ -z $ekey && ! -r $ekeyfile ]];then
++ wsrep_log_error "FATAL: Either key or keyfile must be readable"
++ exit 3
++ fi
++
++ if [[ -z $ekey ]];then
++ ecmd="xbcrypt --encrypt-algo=$ealgo --encrypt-key-file=$ekeyfile"
++ else
++ ecmd="xbcrypt --encrypt-algo=$ealgo --encrypt-key=$ekey"
++ fi
++
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ ecmd+=" -d"
++ fi
++
++ stagemsg+="-XB-Encrypted"
++}
++
++get_transfer()
++{
++ if [[ -z $SST_PORT ]];then
++ TSST_PORT=4444
++ else
++ TSST_PORT=$SST_PORT
++ fi
++
++ if [[ $tfmt == 'nc' ]];then
++ if [[ ! -x `which nc` ]];then
++ wsrep_log_error "nc(netcat) not found in path: $PATH"
++ exit 2
++ fi
++ wsrep_log_info "Using netcat as streamer"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ tcmd="nc -dl ${TSST_PORT}"
++ else
++ tcmd="nc ${REMOTEIP} ${TSST_PORT}"
++ fi
++ else
++ tfmt='socat'
++ wsrep_log_info "Using socat as streamer"
++ if [[ ! -x `which socat` ]];then
++ wsrep_log_error "socat not found in path: $PATH"
++ exit 2
++ fi
++
++ if [[ $encrypt -eq 2 || $encrypt -eq 3 ]] && ! socat -V | grep -q WITH_OPENSSL;then
++ wsrep_log_info "NOTE: socat is not openssl enabled, falling back to plain transfer"
++ encrypt=-1
++ fi
++
++ if [[ $encrypt -eq 2 ]];then
++ wsrep_log_info "Using openssl based encryption with socat: with crt and pem"
++ if [[ -z $tpem || -z $tcert ]];then
++ wsrep_log_error "Both PEM and CRT files required"
++ exit 22
++ fi
++ stagemsg+="-OpenSSL-Encrypted-2"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ wsrep_log_info "Decrypting with PEM $tpem, CA: $tcert"
++ tcmd="socat -u openssl-listen:${TSST_PORT},reuseaddr,cert=$tpem,cafile=${tcert}${sockopt} stdio"
++ else
++ wsrep_log_info "Encrypting with PEM $tpem, CA: $tcert"
++ tcmd="socat -u stdio openssl-connect:${REMOTEIP}:${TSST_PORT},cert=$tpem,cafile=${tcert}${sockopt}"
++ fi
++ elif [[ $encrypt -eq 3 ]];then
++ wsrep_log_info "Using openssl based encryption with socat: with key and crt"
++ if [[ -z $tpem || -z $tkey ]];then
++ wsrep_log_error "Both certificate and key files required"
++ exit 22
++ fi
++ stagemsg+="-OpenSSL-Encrypted-3"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ wsrep_log_info "Decrypting with certificate $tpem, key $tkey"
++ tcmd="socat -u openssl-listen:${TSST_PORT},reuseaddr,cert=$tpem,key=${tkey},verify=0${sockopt} stdio"
++ else
++ wsrep_log_info "Encrypting with certificate $tpem, key $tkey"
++ tcmd="socat -u stdio openssl-connect:${REMOTEIP}:${TSST_PORT},cert=$tpem,key=${tkey},verify=0${sockopt}"
++ fi
++
++ else
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ tcmd="socat -u TCP-LISTEN:${TSST_PORT},reuseaddr${sockopt} stdio"
++ else
++ tcmd="socat -u stdio TCP:${REMOTEIP}:${TSST_PORT}${sockopt}"
++ fi
++ fi
++ fi
++
++}
++
++parse_cnf()
++{
++ local group=$1
++ local var=$2
++ reval=$($MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF $group | awk -F= '{if ($1 ~ /_/) { gsub(/_/,"-",$1); print $1"="$2 } else { print $0 }}' | grep -- "--$var=" | cut -d= -f2-)
++ if [[ -z $reval ]];then
++ [[ -n $3 ]] && reval=$3
++ fi
++ echo $reval
++}
++
++get_footprint()
++{
++ pushd $WSREP_SST_OPT_DATA 1>/dev/null
++ payload=$(find . -regex '.*\.ibd$\|.*\.MYI$\|.*\.MYD$\|.*ibdata1$' -type f -print0 | xargs -0 du --block-size=1 -c | awk 'END { print $1 }')
++ if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF xtrabackup | grep -q -- "--compress";then
++ # QuickLZ has around 50% compression ratio
++ # When compression/compaction used, the progress is only an approximate.
++ payload=$(( payload*1/2 ))
++ fi
++ popd 1>/dev/null
++ pcmd+=" -s $payload"
++ adjust_progress
++}
++
++adjust_progress()
++{
++ if [[ -n $progress && $progress != '1' ]];then
++ if [[ -e $progress ]];then
++ pcmd+=" 2>>$progress"
++ else
++ pcmd+=" 2>$progress"
++ fi
++ elif [[ -z $progress && -n $rlimit ]];then
++ # When rlimit is non-zero
++ pcmd="pv -q"
++ fi
++
++ if [[ -n $rlimit && "$WSREP_SST_OPT_ROLE" == "donor" ]];then
++ wsrep_log_info "Rate-limiting SST to $rlimit"
++ pcmd+=" -L \$rlimit"
++ fi
++}
++
++read_cnf()
++{
++ sfmt=$(parse_cnf sst streamfmt "xbstream")
++ tfmt=$(parse_cnf sst transferfmt "socat")
++ tcert=$(parse_cnf sst tca "")
++ tpem=$(parse_cnf sst tcert "")
++ tkey=$(parse_cnf sst tkey "")
++ encrypt=$(parse_cnf sst encrypt 0)
++ sockopt=$(parse_cnf sst sockopt "")
++ progress=$(parse_cnf sst progress "")
++ rebuild=$(parse_cnf sst rebuild 0)
++ ttime=$(parse_cnf sst time 0)
++ cpat=$(parse_cnf sst cpat '.*galera\.cache$\|.*sst_in_progress$\|.*grastate\.dat$\|.*gvwstate\.dat$\|.*\.err$\|.*\.log$\|.*RPM_UPGRADE_MARKER$\|.*RPM_UPGRADE_HISTORY$')
++ incremental=$(parse_cnf sst incremental 0)
++ ealgo=$(parse_cnf xtrabackup encrypt "")
++ ekey=$(parse_cnf xtrabackup encrypt-key "")
++ ekeyfile=$(parse_cnf xtrabackup encrypt-key-file "")
++ scomp=$(parse_cnf sst compressor "")
++ sdecomp=$(parse_cnf sst decompressor "")
++
++
++ # Refer to http://www.percona.com/doc/percona-xtradb-cluster/manual/xtrabackup_sst.html
++ if [[ -z $ealgo ]];then
++ ealgo=$(parse_cnf sst encrypt-algo "")
++ ekey=$(parse_cnf sst encrypt-key "")
++ ekeyfile=$(parse_cnf sst encrypt-key-file "")
++ fi
++ rlimit=$(parse_cnf sst rlimit "")
++ uextra=$(parse_cnf sst use-extra 0)
++ speciald=$(parse_cnf sst sst-special-dirs 1)
++ iopts=$(parse_cnf sst inno-backup-opts "")
++ iapts=$(parse_cnf sst inno-apply-opts "")
++ impts=$(parse_cnf sst inno-move-opts "")
++ stimeout=$(parse_cnf sst sst-initial-timeout 100)
++}
++
++get_stream()
++{
++ if [[ $sfmt == 'xbstream' ]];then
++ wsrep_log_info "Streaming with xbstream"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ strmcmd="xbstream -x"
++ else
++ strmcmd="xbstream -c \${INFO_FILE}"
++ fi
++ else
++ sfmt="tar"
++ wsrep_log_info "Streaming with tar"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ strmcmd="tar xfi - "
++ else
++ strmcmd="tar cf - \${INFO_FILE} "
++ fi
++
++ fi
++}
++
++get_proc()
++{
++ set +e
++ nproc=$(grep -c processor /proc/cpuinfo)
++ [[ -z $nproc || $nproc -eq 0 ]] && nproc=1
++ set -e
++}
++
++sig_joiner_cleanup()
++{
++ wsrep_log_error "Removing $MAGIC_FILE file due to signal"
++ rm -f "$MAGIC_FILE"
++}
++
++cleanup_joiner()
++{
++ # Since this is invoked just after exit NNN
++ local estatus=$?
++ if [[ $estatus -ne 0 ]];then
++ wsrep_log_error "Cleanup after exit with status:$estatus"
++ fi
++ if [ "${WSREP_SST_OPT_ROLE}" = "joiner" ];then
++ wsrep_log_info "Removing the sst_in_progress file"
++ wsrep_cleanup_progress_file
++ fi
++ if [[ -n $progress && -p $progress ]];then
++ wsrep_log_info "Cleaning up fifo file $progress"
++ rm $progress
++ fi
++ if [[ -n ${STATDIR:-} ]];then
++ [[ -d $STATDIR ]] && rm -rf $STATDIR
++ fi
++}
++
++check_pid()
++{
++ local pid_file="$1"
++ [ -r "$pid_file" ] && ps -p $(cat "$pid_file") >/dev/null 2>&1
++}
++
++cleanup_donor()
++{
++ # Since this is invoked just after exit NNN
++ local estatus=$?
++ if [[ $estatus -ne 0 ]];then
++ wsrep_log_error "Cleanup after exit with status:$estatus"
++ fi
++
++ if [[ -n ${XTRABACKUP_PID:-} ]];then
++ if check_pid $XTRABACKUP_PID
++ then
++ wsrep_log_error "xtrabackup process is still running. Killing... "
++ kill_xtrabackup
++ fi
++
++ fi
++ rm -f ${DATA}/${IST_FILE} || true
++
++ if [[ -n $progress && -p $progress ]];then
++ wsrep_log_info "Cleaning up fifo file $progress"
++ rm -f $progress || true
++ fi
++
++ wsrep_log_info "Cleaning up temporary directories"
++
++ if [[ -n $xtmpdir ]];then
++ [[ -d $xtmpdir ]] && rm -rf $xtmpdir || true
++ fi
++
++ if [[ -n $itmpdir ]];then
++ [[ -d $itmpdir ]] && rm -rf $itmpdir || true
++ fi
++}
++
++kill_xtrabackup()
++{
++ local PID=$(cat $XTRABACKUP_PID)
++ [ -n "$PID" -a "0" != "$PID" ] && kill $PID && (kill $PID && kill -9 $PID) || :
++ wsrep_log_info "Removing xtrabackup pid file $XTRABACKUP_PID"
++ rm -f "$XTRABACKUP_PID" || true
++}
++
++setup_ports()
++{
++ if [[ "$WSREP_SST_OPT_ROLE" == "donor" ]];then
++ SST_PORT=$(echo $WSREP_SST_OPT_ADDR | awk -F '[:/]' '{ print $2 }')
++ REMOTEIP=$(echo $WSREP_SST_OPT_ADDR | awk -F ':' '{ print $1 }')
++ lsn=$(echo $WSREP_SST_OPT_ADDR | awk -F '[:/]' '{ print $4 }')
++ else
++ SST_PORT=$(echo ${WSREP_SST_OPT_ADDR} | awk -F ':' '{ print $2 }')
++ fi
++}
++
++# waits ~10 seconds for nc to open the port and then reports ready
++# (regardless of timeout)
++wait_for_listen()
++{
++ local PORT=$1
++ local ADDR=$2
++ local MODULE=$3
++ for i in {1..50}
++ do
++ ss -p state listening "( sport = :$PORT )" | grep -qE 'socat|nc' && break
++ sleep 0.2
++ done
++ if [[ $incremental -eq 1 ]];then
++ echo "ready ${ADDR}/${MODULE}/$lsn"
++ else
++ echo "ready ${ADDR}/${MODULE}"
++ fi
++}
++
++check_extra()
++{
++ local use_socket=1
++ if [[ $uextra -eq 1 ]];then
++ if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF mysqld | tr '_' '-' | grep -- "--thread-handling=" | grep -q 'pool-of-threads';then
++ local eport=$($MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF mysqld | tr '_' '-' | grep -- "--extra-port=" | cut -d= -f2)
++ if [[ -n $eport ]];then
++ # Xtrabackup works only locally.
++ # Hence, setting host to 127.0.0.1 unconditionally.
++ wsrep_log_info "SST through extra_port $eport"
++ INNOEXTRA+=" --host=127.0.0.1 --port=$eport "
++ use_socket=0
++ else
++ wsrep_log_error "Extra port $eport null, failing"
++ exit 1
++ fi
++ else
++ wsrep_log_info "Thread pool not set, ignore the option use_extra"
++ fi
++ fi
++ if [[ $use_socket -eq 1 ]] && [[ -n "${WSREP_SST_OPT_SOCKET}" ]];then
++ INNOEXTRA+=" --socket=${WSREP_SST_OPT_SOCKET}"
++ fi
++}
++
++recv_joiner()
++{
++ local dir=$1
++ local msg=$2
++ local tmt=$3
++ local ltcmd
++
++ pushd ${dir} 1>/dev/null
++ set +e
++
++ if [[ $tmt -gt 0 && -x `which timeout` ]];then
++ if timeout --help | grep -q -- '-k';then
++ ltcmd="timeout -k $(( tmt+10 )) $tmt $tcmd"
++ else
++ ltcmd="timeout $tmt $tcmd"
++ fi
++ timeit "$msg" "$ltcmd | $strmcmd; RC=( "\${PIPESTATUS[@]}" )"
++ else
++ timeit "$msg" "$tcmd | $strmcmd; RC=( "\${PIPESTATUS[@]}" )"
++ fi
++
++ set -e
++ popd 1>/dev/null
++
++ if [[ ${RC[0]} -eq 124 ]];then
++ wsrep_log_error "Possible timeout in receving first data from donor in gtid stage"
++ exit 32
++ fi
++
++ for ecode in "${RC[@]}";do
++ if [[ $ecode -ne 0 ]];then
++ wsrep_log_error "Error while getting data from donor node: " \
++ "exit codes: ${RC[@]}"
++ exit 32
++ fi
++ done
++
++ if [ ! -r "${MAGIC_FILE}" ];then
++ # this message should cause joiner to abort
++ wsrep_log_error "xtrabackup process ended without creating '${MAGIC_FILE}'"
++ wsrep_log_info "Contents of datadir"
++ wsrep_log_info "$(ls -l ${dir}/*)"
++ exit 32
++ fi
++}
++
++
++send_donor()
++{
++ local dir=$1
++ local msg=$2
++
++ pushd ${dir} 1>/dev/null
++ set +e
++ timeit "$msg" "$strmcmd | $tcmd; RC=( "\${PIPESTATUS[@]}" )"
++ set -e
++ popd 1>/dev/null
++
++
++ for ecode in "${RC[@]}";do
++ if [[ $ecode -ne 0 ]];then
++ wsrep_log_error "Error while getting data from donor node: " \
++ "exit codes: ${RC[@]}"
++ exit 32
++ fi
++ done
++
++}
++
++if [[ ! -x `which $INNOBACKUPEX_BIN` ]];then
++ wsrep_log_error "innobackupex not in path: $PATH"
++ exit 2
++fi
++
++rm -f "${MAGIC_FILE}"
++
++if [[ ! ${WSREP_SST_OPT_ROLE} == 'joiner' && ! ${WSREP_SST_OPT_ROLE} == 'donor' ]];then
++ wsrep_log_error "Invalid role ${WSREP_SST_OPT_ROLE}"
++ exit 22
++fi
++
++read_cnf
++setup_ports
++get_stream
++get_transfer
++
++if ${INNOBACKUPEX_BIN} /tmp --help | grep -q -- '--version-check'; then
++ disver="--no-version-check"
++fi
++
++
++INNOEXTRA=""
++INNOAPPLY="${INNOBACKUPEX_BIN} $disver $iapts --apply-log \$rebuildcmd \${DATA} &>\${DATA}/innobackup.prepare.log"
++INNOMOVE="${INNOBACKUPEX_BIN} --defaults-file=${WSREP_SST_OPT_CONF} --defaults-group=mysqld${WSREP_SST_OPT_CONF_SUFFIX} $disver $impts --move-back --force-non-empty-directories \${DATA} &>\${DATA}/innobackup.move.log"
++INNOBACKUP="${INNOBACKUPEX_BIN} --defaults-file=${WSREP_SST_OPT_CONF} --defaults-group=mysqld${WSREP_SST_OPT_CONF_SUFFIX} $disver $iopts \$tmpopts \$INNOEXTRA --galera-info --stream=\$sfmt \$itmpdir 2>\${DATA}/innobackup.backup.log"
++
++if [ "$WSREP_SST_OPT_ROLE" = "donor" ]
++then
++ trap cleanup_donor EXIT
++
++ if [ $WSREP_SST_OPT_BYPASS -eq 0 ]
++ then
++
++ if [[ -z $(parse_cnf mysqld tmpdir "") && -z $(parse_cnf xtrabackup tmpdir "") ]];then
++ xtmpdir=$(mktemp -d)
++ tmpopts=" --tmpdir=$xtmpdir "
++ wsrep_log_info "Using $xtmpdir as xtrabackup temporary directory"
++ fi
++
++ itmpdir=$(mktemp -d)
++ wsrep_log_info "Using $itmpdir as innobackupex temporary directory"
++
++ if [ "${AUTH[0]}" != "(null)" ]; then
++ INNOEXTRA+=" --user=${AUTH[0]}"
++ fi
++
++ if [ ${#AUTH[*]} -eq 2 ]; then
++ INNOEXTRA+=" --password=${AUTH[1]}"
++ elif [ "${AUTH[0]}" != "(null)" ]; then
++ # Empty password, used for testing, debugging etc.
++ INNOEXTRA+=" --password="
++ fi
++
++ get_keys
++ if [[ $encrypt -eq 1 ]];then
++ if [[ -n $ekey ]];then
++ INNOEXTRA+=" --encrypt=$ealgo --encrypt-key=$ekey "
++ else
++ INNOEXTRA+=" --encrypt=$ealgo --encrypt-key-file=$ekeyfile "
++ fi
++ fi
++
++ if [[ -n $lsn ]];then
++ INNOEXTRA+=" --incremental --incremental-lsn=$lsn "
++ fi
++
++ check_extra
++
++ wsrep_log_info "Streaming GTID file before SST"
++
++ echo "${WSREP_SST_OPT_GTID}" > "${MAGIC_FILE}"
++
++ ttcmd="$tcmd"
++
++ if [[ $encrypt -eq 1 ]];then
++ if [[ -n $scomp ]];then
++ tcmd=" $ecmd | $scomp | $tcmd "
++ else
++ tcmd=" $ecmd | $tcmd "
++ fi
++ elif [[ -n $scomp ]];then
++ tcmd=" $scomp | $tcmd "
++ fi
++
++
++ send_donor $DATA "${stagemsg}-gtid"
++
++ tcmd="$ttcmd"
++ if [[ -n $progress ]];then
++ get_footprint
++ tcmd="$pcmd | $tcmd"
++ elif [[ -n $rlimit ]];then
++ adjust_progress
++ tcmd="$pcmd | $tcmd"
++ fi
++
++ wsrep_log_info "Sleeping before data transfer for SST"
++ sleep 10
++
++ wsrep_log_info "Streaming the backup to joiner at ${REMOTEIP} ${SST_PORT:-4444}"
++
++ if [[ -n $scomp ]];then
++ tcmd="$scomp | $tcmd"
++ fi
++
++ set +e
++ timeit "${stagemsg}-SST" "$INNOBACKUP | $tcmd; RC=( "\${PIPESTATUS[@]}" )"
++ set -e
++
++ if [ ${RC[0]} -ne 0 ]; then
++ wsrep_log_error "${INNOBACKUPEX_BIN} finished with error: ${RC[0]}. " \
++ "Check ${DATA}/innobackup.backup.log"
++ exit 22
++ elif [[ ${RC[$(( ${#RC[@]}-1 ))]} -eq 1 ]];then
++ wsrep_log_error "$tcmd finished with error: ${RC[1]}"
++ exit 22
++ fi
++
++ # innobackupex implicitly writes PID to fixed location in $xtmpdir
++ XTRABACKUP_PID="$xtmpdir/xtrabackup_pid"
++
++
++ else # BYPASS FOR IST
++
++ wsrep_log_info "Bypassing the SST for IST"
++ echo "continue" # now server can resume updating data
++ echo "${WSREP_SST_OPT_GTID}" > "${MAGIC_FILE}"
++ echo "1" > "${DATA}/${IST_FILE}"
++ get_keys
++ if [[ $encrypt -eq 1 ]];then
++ if [[ -n $scomp ]];then
++ tcmd=" $ecmd | $scomp | $tcmd "
++ else
++ tcmd=" $ecmd | $tcmd "
++ fi
++ elif [[ -n $scomp ]];then
++ tcmd=" $scomp | $tcmd "
++ fi
++ strmcmd+=" \${IST_FILE}"
++
++ send_donor $DATA "${stagemsg}-IST"
++
++ fi
++
++ echo "done ${WSREP_SST_OPT_GTID}"
++ wsrep_log_info "Total time on donor: $totime seconds"
++
++elif [ "${WSREP_SST_OPT_ROLE}" = "joiner" ]
++then
++ [[ -e $SST_PROGRESS_FILE ]] && wsrep_log_info "Stale sst_in_progress file: $SST_PROGRESS_FILE"
++ [[ -n $SST_PROGRESS_FILE ]] && touch $SST_PROGRESS_FILE
++
++ if [[ $speciald -eq 1 ]];then
++ ib_home_dir=$(parse_cnf mysqld innodb-data-home-dir "")
++ ib_log_dir=$(parse_cnf mysqld innodb-log-group-home-dir "")
++ if [[ -z $ib_home_dir && -z $ib_log_dir ]];then
++ speciald=0
++ fi
++ fi
++
++ stagemsg="Joiner-Recv"
++
++ if [[ ! -e ${DATA}/ibdata1 ]];then
++ incremental=0
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ wsrep_log_info "Incremental SST enabled: NOT SUPPORTED yet"
++ lsn=$(grep to_lsn xtrabackup_checkpoints | cut -d= -f2 | tr -d ' ')
++ wsrep_log_info "Recovered LSN: $lsn"
++ fi
++
++ sencrypted=1
++ nthreads=1
++
++ MODULE="xtrabackup_sst"
++
++ rm -f "${DATA}/${IST_FILE}"
++
++ # May need xtrabackup_checkpoints later on
++ rm -f ${DATA}/xtrabackup_binary ${DATA}/xtrabackup_galera_info ${DATA}/xtrabackup_logfile
++
++ ADDR=${WSREP_SST_OPT_ADDR}
++ if [ -z "${SST_PORT}" ]
++ then
++ SST_PORT=4444
++ ADDR="$(echo ${WSREP_SST_OPT_ADDR} | awk -F ':' '{ print $1 }'):${SST_PORT}"
++ fi
++
++ wait_for_listen ${SST_PORT} ${ADDR} ${MODULE} &
++
++ trap sig_joiner_cleanup HUP PIPE INT TERM
++ trap cleanup_joiner EXIT
++
++ if [[ -n $progress ]];then
++ adjust_progress
++ tcmd+=" | $pcmd"
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ BDATA=$DATA
++ DATA=$(mktemp -d)
++ MAGIC_FILE="${DATA}/${INFO_FILE}"
++ fi
++
++ get_keys
++ if [[ $encrypt -eq 1 && $sencrypted -eq 1 ]];then
++ if [[ -n $sdecomp ]];then
++ strmcmd=" $sdecomp | $ecmd | $strmcmd"
++ else
++ strmcmd=" $ecmd | $strmcmd"
++ fi
++ elif [[ -n $sdecomp ]];then
++ strmcmd=" $sdecomp | $strmcmd"
++ fi
++
++ STATDIR=$(mktemp -d)
++ MAGIC_FILE="${STATDIR}/${INFO_FILE}"
++ recv_joiner $STATDIR "${stagemsg}-gtid" $stimeout
++
++ if ! ps -p ${WSREP_SST_OPT_PARENT} &>/dev/null
++ then
++ wsrep_log_error "Parent mysqld process (PID:${WSREP_SST_OPT_PARENT}) terminated unexpectedly."
++ exit 32
++ fi
++
++ if [ ! -r "${STATDIR}/${IST_FILE}" ]
++ then
++ wsrep_log_info "Proceeding with SST"
++
++ if [[ $speciald -eq 1 && -d ${DATA}/.sst ]];then
++ wsrep_log_info "WARNING: Stale temporary SST directory: ${DATA}/.sst from previous SST"
++ fi
++
++ if [[ $incremental -ne 1 ]];then
++ if [[ $speciald -eq 1 ]];then
++ wsrep_log_info "Cleaning the existing datadir and innodb-data/log directories"
++ find $ib_home_dir $ib_log_dir $DATA -mindepth 1 -regex $cpat -prune -o -exec rm -rfv {} 1>&2 \+
++ else
++ wsrep_log_info "Cleaning the existing datadir"
++ find $DATA -mindepth 1 -regex $cpat -prune -o -exec rm -rfv {} 1>&2 \+
++ fi
++ tempdir=$(parse_cnf mysqld log-bin "")
++ if [[ -n ${tempdir:-} ]];then
++ binlog_dir=$(dirname $tempdir)
++ binlog_file=$(basename $tempdir)
++ if [[ -n ${binlog_dir:-} && $binlog_dir != '.' && $binlog_dir != $DATA ]];then
++ pattern="$binlog_dir/$binlog_file\.[0-9]+$"
++ wsrep_log_info "Cleaning the binlog directory $binlog_dir as well"
++ find $binlog_dir -maxdepth 1 -type f -regex $pattern -exec rm -fv {} 1>&2 \+
++ rm $binlog_dir/*.index || true
++ fi
++ fi
++
++ else
++ wsrep_log_info "Removing existing ib_logfile files"
++ rm -f ${BDATA}/ib_logfile*
++ fi
++
++
++ if [[ $speciald -eq 1 ]];then
++ mkdir -p ${DATA}/.sst
++ TDATA=${DATA}
++ DATA="${DATA}/.sst"
++ fi
++
++
++ MAGIC_FILE="${DATA}/${INFO_FILE}"
++ recv_joiner $DATA "${stagemsg}-SST" 0
++
++ get_proc
++
++ # Rebuild indexes for compact backups
++ if grep -q 'compact = 1' ${DATA}/xtrabackup_checkpoints;then
++ wsrep_log_info "Index compaction detected"
++ rebuild=1
++ fi
++
++ if [[ $rebuild -eq 1 ]];then
++ nthreads=$(parse_cnf xtrabackup rebuild-threads $nproc)
++ wsrep_log_info "Rebuilding during prepare with $nthreads threads"
++ rebuildcmd="--rebuild-indexes --rebuild-threads=$nthreads"
++ fi
++
++ if test -n "$(find ${DATA} -maxdepth 1 -type f -name '*.qp' -print -quit)";then
++
++ wsrep_log_info "Compressed qpress files found"
++
++ if [[ ! -x `which qpress` ]];then
++ wsrep_log_error "qpress not found in path: $PATH"
++ exit 22
++ fi
++
++ if [[ -n $progress ]] && pv --help | grep -q 'line-mode';then
++ count=$(find ${DATA} -type f -name '*.qp' | wc -l)
++ count=$(( count*2 ))
++ if pv --help | grep -q FORMAT;then
++ pvopts="-f -s $count -l -N Decompression -F '%N => Rate:%r Elapsed:%t %e Progress: [%b/$count]'"
++ else
++ pvopts="-f -s $count -l -N Decompression"
++ fi
++ pcmd="pv $pvopts"
++ adjust_progress
++ dcmd="$pcmd | xargs -n 2 qpress -T${nproc}d"
++ else
++ dcmd="xargs -n 2 qpress -T${nproc}d"
++ fi
++
++
++ # Decompress the qpress files
++ wsrep_log_info "Decompression with $nproc threads"
++ timeit "Joiner-Decompression" "find ${DATA} -type f -name '*.qp' -printf '%p\n%h\n' | $dcmd"
++ extcode=$?
++
++ if [[ $extcode -eq 0 ]];then
++ wsrep_log_info "Removing qpress files after decompression"
++ find ${DATA} -type f -name '*.qp' -delete
++ if [[ $? -ne 0 ]];then
++ wsrep_log_error "Something went wrong with deletion of qpress files. Investigate"
++ fi
++ else
++ wsrep_log_error "Decompression failed. Exit code: $extcode"
++ exit 22
++ fi
++ fi
++
++
++ if [[ ! -z $WSREP_SST_OPT_BINLOG ]];then
++
++ BINLOG_DIRNAME=$(dirname $WSREP_SST_OPT_BINLOG)
++ BINLOG_FILENAME=$(basename $WSREP_SST_OPT_BINLOG)
++
++ # To avoid comparing data directory and BINLOG_DIRNAME
++ mv $DATA/${BINLOG_FILENAME}.* $BINLOG_DIRNAME/ 2>/dev/null || true
++
++ pushd $BINLOG_DIRNAME &>/dev/null
++ for bfiles in $(ls -1 ${BINLOG_FILENAME}.*);do
++ echo ${BINLOG_DIRNAME}/${bfiles} >> ${BINLOG_FILENAME}.index
++ done
++ popd &> /dev/null
++
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ # Added --ibbackup=xtrabackup_55 because it fails otherwise citing connection issues.
++ INNOAPPLY="${INNOBACKUPEX_BIN} $disver --defaults-file=${WSREP_SST_OPT_CONF} --defaults-group=mysqld${WSREP_SST_OPT_CONF_SUFFIX} \
++ --ibbackup=xtrabackup_56 --apply-log $rebuildcmd --redo-only $BDATA --incremental-dir=${DATA} &>>${BDATA}/innobackup.prepare.log"
++ fi
++
++ wsrep_log_info "Preparing the backup at ${DATA}"
++ timeit "Xtrabackup prepare stage" "$INNOAPPLY"
++
++ if [ $? -ne 0 ];
++ then
++ wsrep_log_error "${INNOBACKUPEX_BIN} apply finished with errors. Check ${DATA}/innobackup.prepare.log"
++ exit 22
++ fi
++
++ if [[ $speciald -eq 1 ]];then
++ MAGIC_FILE="${TDATA}/${INFO_FILE}"
++ set +e
++ rm $TDATA/innobackup.prepare.log $TDATA/innobackup.move.log
++ set -e
++ wsrep_log_info "Moving the backup to ${TDATA}"
++ timeit "Xtrabackup move stage" "$INNOMOVE"
++ if [[ $? -eq 0 ]];then
++ wsrep_log_info "Move successful, removing ${DATA}"
++ rm -rf $DATA
++ DATA=${TDATA}
++ else
++ wsrep_log_error "Move failed, keeping ${DATA} for further diagnosis"
++ wsrep_log_error "Check ${DATA}/innobackup.move.log for details"
++ fi
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ wsrep_log_info "Cleaning up ${DATA} after incremental SST"
++ [[ -d ${DATA} ]] && rm -rf ${DATA}
++ DATA=$BDATA
++ fi
++
++ else
++ wsrep_log_info "${IST_FILE} received from donor: Running IST"
++ fi
++
++ if [[ ! -r ${MAGIC_FILE} ]];then
++ wsrep_log_error "SST magic file ${MAGIC_FILE} not found/readable"
++ exit 2
++ fi
++ cat "${MAGIC_FILE}" # output UUID:seqno
++ wsrep_log_info "Total time on joiner: $totime seconds"
++fi
++
++exit 0
+diff --git a/scripts/wsrep_sst_xtrabackup.sh b/scripts/wsrep_sst_xtrabackup.sh
+new file mode 100644
+index 0000000..05dbcea
+--- /dev/null
++++ b/scripts/wsrep_sst_xtrabackup.sh
+@@ -0,0 +1,715 @@
++#!/bin/bash -ue
++# Copyright (C) 2013 Percona Inc
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; see the file COPYING. If not, write to the
++# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston
++# MA 02110-1301 USA.
++
++# Optional dependencies and options documented here: http://www.percona.com/doc/percona-xtradb-cluster/manual/xtrabackup_sst.html
++# Make sure to read that before proceeding!
++
++
++
++
++. $(dirname $0)/wsrep_sst_common
++
++ealgo=""
++ekey=""
++ekeyfile=""
++encrypt=0
++nproc=1
++ecode=0
++XTRABACKUP_PID=""
++SST_PORT=""
++REMOTEIP=""
++tcert=""
++tpem=""
++sockopt=""
++progress=""
++ttime=0
++totime=0
++lsn=""
++incremental=0
++ecmd=""
++rlimit=""
++
++sfmt="tar"
++strmcmd=""
++tfmt=""
++tcmd=""
++rebuild=0
++rebuildcmd=""
++payload=0
++pvformat="-F '%N => Rate:%r Avg:%a Elapsed:%t %e Bytes: %b %p' "
++pvopts="-f -i 10 -N $WSREP_SST_OPT_ROLE "
++uextra=0
++
++if which pv &>/dev/null && pv --help | grep -q FORMAT;then
++ pvopts+=$pvformat
++fi
++pcmd="pv $pvopts"
++declare -a RC
++
++INNOBACKUPEX_BIN=innobackupex
++readonly AUTH=(${WSREP_SST_OPT_AUTH//:/ })
++DATA="${WSREP_SST_OPT_DATA}"
++INFO_FILE="xtrabackup_galera_info"
++IST_FILE="xtrabackup_ist"
++MAGIC_FILE="${DATA}/${INFO_FILE}"
++
++# Setting the path for ss and ip
++export PATH="/usr/sbin:/sbin:$PATH"
++
++timeit(){
++ local stage=$1
++ shift
++ local cmd="$@"
++ local x1 x2 took extcode
++
++ if [[ $ttime -eq 1 ]];then
++ x1=$(date +%s)
++ wsrep_log_info "Evaluating $cmd"
++ eval "$cmd"
++ extcode=$?
++ x2=$(date +%s)
++ took=$(( x2-x1 ))
++ wsrep_log_info "NOTE: $stage took $took seconds"
++ totime=$(( totime+took ))
++ else
++ wsrep_log_info "Evaluating $cmd"
++ eval "$cmd"
++ extcode=$?
++ fi
++ return $extcode
++}
++
++get_keys()
++{
++ if [[ $encrypt -eq 2 ]];then
++ return
++ fi
++
++ if [[ $encrypt -eq 0 ]];then
++ if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF xtrabackup | grep -q encrypt;then
++ wsrep_log_error "Unexpected option combination. SST may fail. Refer to http://www.percona.com/doc/percona-xtradb-cluster/manual/xtrabackup_sst.html "
++ fi
++ return
++ fi
++
++ if [[ $sfmt == 'tar' ]];then
++ wsrep_log_info "NOTE: Xtrabackup-based encryption - encrypt=1 - cannot be enabled with tar format"
++ encrypt=0
++ return
++ fi
++
++ wsrep_log_info "Xtrabackup based encryption enabled in my.cnf - Supported only from Xtrabackup 2.1.4"
++
++ if [[ -z $ealgo ]];then
++ wsrep_log_error "FATAL: Encryption algorithm empty from my.cnf, bailing out"
++ exit 3
++ fi
++
++ if [[ -z $ekey && ! -r $ekeyfile ]];then
++ wsrep_log_error "FATAL: Either key or keyfile must be readable"
++ exit 3
++ fi
++
++ if [[ -z $ekey ]];then
++ ecmd="xbcrypt --encrypt-algo=$ealgo --encrypt-key-file=$ekeyfile"
++ else
++ ecmd="xbcrypt --encrypt-algo=$ealgo --encrypt-key=$ekey"
++ fi
++
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ ecmd+=" -d"
++ fi
++}
++
++get_transfer()
++{
++ if [[ -z $SST_PORT ]];then
++ TSST_PORT=4444
++ else
++ TSST_PORT=$SST_PORT
++ fi
++
++ if [[ $tfmt == 'nc' ]];then
++ if [[ ! -x `which nc` ]];then
++ wsrep_log_error "nc(netcat) not found in path: $PATH"
++ exit 2
++ fi
++ wsrep_log_info "Using netcat as streamer"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ tcmd="nc -dl ${TSST_PORT}"
++ else
++ tcmd="nc ${REMOTEIP} ${TSST_PORT}"
++ fi
++ else
++ tfmt='socat'
++ wsrep_log_info "Using socat as streamer"
++ if [[ ! -x `which socat` ]];then
++ wsrep_log_error "socat not found in path: $PATH"
++ exit 2
++ fi
++
++ if [[ $encrypt -eq 2 ]] && ! socat -V | grep -q OPENSSL;then
++ wsrep_log_info "NOTE: socat is not openssl enabled, falling back to plain transfer"
++ encrypt=0
++ fi
++
++ if [[ $encrypt -eq 2 ]];then
++ wsrep_log_info "Using openssl based encryption with socat"
++ if [[ -z $tpem || -z $tcert ]];then
++ wsrep_log_error "Both PEM and CRT files required"
++ exit 22
++ fi
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ wsrep_log_info "Decrypting with PEM $tpem, CA: $tcert"
++ tcmd="socat -u openssl-listen:${TSST_PORT},reuseaddr,cert=$tpem,cafile=${tcert}${sockopt} stdio"
++ else
++ wsrep_log_info "Encrypting with PEM $tpem, CA: $tcert"
++ tcmd="socat -u stdio openssl-connect:${REMOTEIP}:${TSST_PORT},cert=$tpem,cafile=${tcert}${sockopt}"
++ fi
++ else
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ tcmd="socat -u TCP-LISTEN:${TSST_PORT},reuseaddr${sockopt} stdio"
++ else
++ tcmd="socat -u stdio TCP:${REMOTEIP}:${TSST_PORT}${sockopt}"
++ fi
++ fi
++ fi
++
++}
++
++parse_cnf()
++{
++ local group=$1
++ local var=$2
++ reval=$($MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF $group | awk -F= '{if ($1 ~ /_/) { gsub(/_/,"-",$1); print $1"="$2 } else { print $0 }}' | grep -- "--$var=" | cut -d= -f2-)
++ if [[ -z $reval ]];then
++ [[ -n $3 ]] && reval=$3
++ fi
++ echo $reval
++}
++
++get_footprint()
++{
++ pushd $WSREP_SST_OPT_DATA 1>/dev/null
++ payload=$(find . -regex '.*\.ibd$\|.*\.MYI$\|.*\.MYD$\|.*ibdata1$' -type f -print0 | xargs -0 du --block-size=1 -c | awk 'END { print $1 }')
++ if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF xtrabackup | grep -q -- "--compress";then
++ # QuickLZ has around 50% compression ratio
++ # When compression/compaction used, the progress is only an approximate.
++ payload=$(( payload*1/2 ))
++ fi
++ popd 1>/dev/null
++ pcmd+=" -s $payload"
++ adjust_progress
++}
++
++adjust_progress()
++{
++ if [[ -n $progress && $progress != '1' ]];then
++ if [[ -e $progress ]];then
++ pcmd+=" 2>>$progress"
++ else
++ pcmd+=" 2>$progress"
++ fi
++ elif [[ -z $progress && -n $rlimit ]];then
++ # When rlimit is non-zero
++ pcmd="pv -q"
++ fi
++
++ if [[ -n $rlimit && "$WSREP_SST_OPT_ROLE" == "donor" ]];then
++ wsrep_log_info "Rate-limiting SST to $rlimit"
++ pcmd+=" -L \$rlimit"
++ fi
++}
++
++read_cnf()
++{
++ sfmt=$(parse_cnf sst streamfmt "tar")
++ tfmt=$(parse_cnf sst transferfmt "socat")
++ tcert=$(parse_cnf sst tca "")
++ tpem=$(parse_cnf sst tcert "")
++ encrypt=$(parse_cnf sst encrypt 0)
++ sockopt=$(parse_cnf sst sockopt "")
++ progress=$(parse_cnf sst progress "")
++ rebuild=$(parse_cnf sst rebuild 0)
++ ttime=$(parse_cnf sst time 0)
++ incremental=$(parse_cnf sst incremental 0)
++ ealgo=$(parse_cnf xtrabackup encrypt "")
++ ekey=$(parse_cnf xtrabackup encrypt-key "")
++ ekeyfile=$(parse_cnf xtrabackup encrypt-key-file "")
++
++ # Refer to http://www.percona.com/doc/percona-xtradb-cluster/manual/xtrabackup_sst.html
++ if [[ -z $ealgo ]];then
++ ealgo=$(parse_cnf sst encrypt-algo "")
++ ekey=$(parse_cnf sst encrypt-key "")
++ ekeyfile=$(parse_cnf sst encrypt-key-file "")
++ fi
++ rlimit=$(parse_cnf sst rlimit "")
++ uextra=$(parse_cnf sst use_extra 0)
++}
++
++get_stream()
++{
++ if [[ $sfmt == 'xbstream' ]];then
++ wsrep_log_info "Streaming with xbstream"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ strmcmd="xbstream -x"
++ else
++ strmcmd="xbstream -c \${INFO_FILE} \${IST_FILE}"
++ fi
++ else
++ sfmt="tar"
++ wsrep_log_info "Streaming with tar"
++ if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then
++ strmcmd="tar xfi - --recursive-unlink -h"
++ else
++ strmcmd="tar cf - \${INFO_FILE} \${IST_FILE}"
++ fi
++
++ fi
++}
++
++get_proc()
++{
++ set +e
++ nproc=$(grep -c processor /proc/cpuinfo)
++ [[ -z $nproc || $nproc -eq 0 ]] && nproc=1
++ set -e
++}
++
++sig_joiner_cleanup()
++{
++ wsrep_log_error "Removing $MAGIC_FILE file due to signal"
++ rm -f "$MAGIC_FILE"
++}
++
++cleanup_joiner()
++{
++ # Since this is invoked just after exit NNN
++ local estatus=$?
++ if [[ $estatus -ne 0 ]];then
++ wsrep_log_error "Cleanup after exit with status:$estatus"
++ fi
++ if [ "${WSREP_SST_OPT_ROLE}" = "joiner" ];then
++ wsrep_log_info "Removing the sst_in_progress file"
++ wsrep_cleanup_progress_file
++ fi
++ if [[ -n $progress && -p $progress ]];then
++ wsrep_log_info "Cleaning up fifo file $progress"
++ rm $progress
++ fi
++}
++
++check_pid()
++{
++ local pid_file="$1"
++ [ -r "$pid_file" ] && ps -p $(cat "$pid_file") >/dev/null 2>&1
++}
++
++cleanup_donor()
++{
++ # Since this is invoked just after exit NNN
++ local estatus=$?
++ if [[ $estatus -ne 0 ]];then
++ wsrep_log_error "Cleanup after exit with status:$estatus"
++ fi
++
++ if [[ -n $XTRABACKUP_PID ]];then
++ if check_pid $XTRABACKUP_PID
++ then
++ wsrep_log_error "xtrabackup process is still running. Killing... "
++ kill_xtrabackup
++ fi
++
++ rm -f $XTRABACKUP_PID
++ fi
++ rm -f ${DATA}/${IST_FILE}
++
++ if [[ -n $progress && -p $progress ]];then
++ wsrep_log_info "Cleaning up fifo file $progress"
++ rm $progress
++ fi
++}
++
++kill_xtrabackup()
++{
++ local PID=$(cat $XTRABACKUP_PID)
++ [ -n "$PID" -a "0" != "$PID" ] && kill $PID && (kill $PID && kill -9 $PID) || :
++ rm -f "$XTRABACKUP_PID"
++}
++
++setup_ports()
++{
++ if [[ "$WSREP_SST_OPT_ROLE" == "donor" ]];then
++ SST_PORT=$(echo $WSREP_SST_OPT_ADDR | awk -F '[:/]' '{ print $2 }')
++ REMOTEIP=$(echo $WSREP_SST_OPT_ADDR | awk -F ':' '{ print $1 }')
++ lsn=$(echo $WSREP_SST_OPT_ADDR | awk -F '[:/]' '{ print $4 }')
++ else
++ SST_PORT=$(echo ${WSREP_SST_OPT_ADDR} | awk -F ':' '{ print $2 }')
++ fi
++}
++
++# waits ~10 seconds for nc to open the port and then reports ready
++# (regardless of timeout)
++wait_for_listen()
++{
++ local PORT=$1
++ local ADDR=$2
++ local MODULE=$3
++ for i in {1..50}
++ do
++ ss -p state listening "( sport = :$PORT )" | grep -qE 'socat|nc' && break
++ sleep 0.2
++ done
++ if [[ $incremental -eq 1 ]];then
++ echo "ready ${ADDR}/${MODULE}/$lsn"
++ else
++ echo "ready ${ADDR}/${MODULE}"
++ fi
++}
++
++check_extra()
++{
++ local use_socket=1
++ if [[ $uextra -eq 1 ]];then
++ if $MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF mysqld | tr '_' '-' | grep -- "--thread-handling=" | grep -q 'pool-of-threads';then
++ local eport=$($MY_PRINT_DEFAULTS -c $WSREP_SST_OPT_CONF mysqld | tr '_' '-' | grep -- "--extra-port=" | cut -d= -f2)
++ if [[ -n $eport ]];then
++ # Xtrabackup works only locally.
++ # Hence, setting host to 127.0.0.1 unconditionally.
++ wsrep_log_info "SST through extra_port $eport"
++ INNOEXTRA+=" --host=127.0.0.1 --port=$eport "
++ use_socket=0
++ else
++ wsrep_log_error "Extra port $eport null, failing"
++ exit 1
++ fi
++ else
++ wsrep_log_info "Thread pool not set, ignore the option use_extra"
++ fi
++ fi
++ if [[ $use_socket -eq 1 ]] && [[ -n "${WSREP_SST_OPT_SOCKET}" ]];then
++ INNOEXTRA+=" --socket=${WSREP_SST_OPT_SOCKET}"
++ fi
++}
++
++if [[ ! -x `which innobackupex` ]];then
++ wsrep_log_error "innobackupex not in path: $PATH"
++ exit 2
++fi
++
++rm -f "${MAGIC_FILE}"
++
++if [[ ! ${WSREP_SST_OPT_ROLE} == 'joiner' && ! ${WSREP_SST_OPT_ROLE} == 'donor' ]];then
++ wsrep_log_error "Invalid role ${WSREP_SST_OPT_ROLE}"
++ exit 22
++fi
++
++read_cnf
++setup_ports
++get_stream
++get_transfer
++
++INNOEXTRA=""
++INNOAPPLY="${INNOBACKUPEX_BIN} --defaults-file=${WSREP_SST_OPT_CONF} --apply-log \$rebuildcmd \${DATA} &>\${DATA}/innobackup.prepare.log"
++INNOBACKUP="${INNOBACKUPEX_BIN} --defaults-file=${WSREP_SST_OPT_CONF} \$INNOEXTRA --galera-info --stream=\$sfmt \${TMPDIR} 2>\${DATA}/innobackup.backup.log"
++
++if [ "$WSREP_SST_OPT_ROLE" = "donor" ]
++then
++ trap cleanup_donor EXIT
++
++ if [ $WSREP_SST_OPT_BYPASS -eq 0 ]
++ then
++ TMPDIR="${TMPDIR:-/tmp}"
++
++ if [ "${AUTH[0]}" != "(null)" ]; then
++ INNOEXTRA+=" --user=${AUTH[0]}"
++ fi
++
++ if [ ${#AUTH[*]} -eq 2 ]; then
++ INNOEXTRA+=" --password=${AUTH[1]}"
++ elif [ "${AUTH[0]}" != "(null)" ]; then
++ # Empty password, used for testing, debugging etc.
++ INNOEXTRA+=" --password="
++ fi
++
++ get_keys
++ if [[ $encrypt -eq 1 ]];then
++ if [[ -n $ekey ]];then
++ INNOEXTRA+=" --encrypt=$ealgo --encrypt-key=$ekey "
++ else
++ INNOEXTRA+=" --encrypt=$ealgo --encrypt-key-file=$ekeyfile "
++ fi
++ fi
++
++ if [[ -n $lsn ]];then
++ INNOEXTRA+=" --incremental --incremental-lsn=$lsn "
++ fi
++
++ check_extra
++
++ wsrep_log_info "Streaming the backup to joiner at ${REMOTEIP} ${SST_PORT}"
++
++ if [[ -n $progress ]];then
++ get_footprint
++ tcmd="$pcmd | $tcmd"
++ elif [[ -n $rlimit ]];then
++ adjust_progress
++ tcmd="$pcmd | $tcmd"
++ fi
++
++ set +e
++ timeit "Donor-Transfer" "$INNOBACKUP | $tcmd; RC=( "\${PIPESTATUS[@]}" )"
++ set -e
++
++ if [ ${RC[0]} -ne 0 ]; then
++ wsrep_log_error "${INNOBACKUPEX_BIN} finished with error: ${RC[0]}. " \
++ "Check ${DATA}/innobackup.backup.log"
++ exit 22
++ elif [[ ${RC[$(( ${#RC[@]}-1 ))]} -eq 1 ]];then
++ wsrep_log_error "$tcmd finished with error: ${RC[1]}"
++ exit 22
++ fi
++
++ # innobackupex implicitly writes PID to fixed location in ${TMPDIR}
++ XTRABACKUP_PID="${TMPDIR}/xtrabackup_pid"
++
++
++ else # BYPASS FOR IST
++
++ wsrep_log_info "Bypassing the SST for IST"
++ STATE="${WSREP_SST_OPT_GTID}"
++ echo "continue" # now server can resume updating data
++ echo "${STATE}" > "${MAGIC_FILE}"
++ echo "1" > "${DATA}/${IST_FILE}"
++ get_keys
++ pushd ${DATA} 1>/dev/null
++ set +e
++ if [[ $encrypt -eq 1 ]];then
++ tcmd=" $ecmd | $tcmd"
++ fi
++ timeit "Donor-IST-Unencrypted-transfer" "$strmcmd | $tcmd; RC=( "\${PIPESTATUS[@]}" )"
++ set -e
++ popd 1>/dev/null
++
++ for ecode in "${RC[@]}";do
++ if [[ $ecode -ne 0 ]];then
++ wsrep_log_error "Error while streaming data to joiner node: " \
++ "exit codes: ${RC[@]}"
++ exit 1
++ fi
++ done
++ fi
++
++ echo "done ${WSREP_SST_OPT_GTID}"
++ wsrep_log_info "Total time on donor: $totime seconds"
++
++elif [ "${WSREP_SST_OPT_ROLE}" = "joiner" ]
++then
++ [[ -e $SST_PROGRESS_FILE ]] && wsrep_log_info "Stale sst_in_progress file: $SST_PROGRESS_FILE"
++ touch $SST_PROGRESS_FILE
++
++ if [[ ! -e ${DATA}/ibdata1 ]];then
++ incremental=0
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ wsrep_log_info "Incremental SST enabled"
++ #lsn=$(/pxc/bin/mysqld --defaults-file=$WSREP_SST_OPT_CONF --basedir=/pxc --wsrep-recover 2>&1 | grep -o 'log sequence number .*' | cut -d " " -f 4 | head -1)
++ lsn=$(grep to_lsn xtrabackup_checkpoints | cut -d= -f2 | tr -d ' ')
++ wsrep_log_info "Recovered LSN: $lsn"
++ fi
++
++ sencrypted=1
++ nthreads=1
++
++ MODULE="xtrabackup_sst"
++
++ # May need xtrabackup_checkpoints later on
++ rm -f ${DATA}/xtrabackup_binary ${DATA}/xtrabackup_galera_info ${DATA}/xtrabackup_logfile
++
++ ADDR=${WSREP_SST_OPT_ADDR}
++ if [ -z "${SST_PORT}" ]
++ then
++ SST_PORT=4444
++ ADDR="$(echo ${WSREP_SST_OPT_ADDR} | awk -F ':' '{ print $1 }'):${SST_PORT}"
++ fi
++
++ wait_for_listen ${SST_PORT} ${ADDR} ${MODULE} &
++
++ trap sig_joiner_cleanup HUP PIPE INT TERM
++ trap cleanup_joiner EXIT
++
++ if [[ -n $progress ]];then
++ adjust_progress
++ tcmd+=" | $pcmd"
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ BDATA=$DATA
++ DATA=$(mktemp -d)
++ MAGIC_FILE="${DATA}/${INFO_FILE}"
++ fi
++
++ get_keys
++ set +e
++ if [[ $encrypt -eq 1 && $sencrypted -eq 1 ]];then
++ strmcmd=" $ecmd | $strmcmd"
++ fi
++
++ pushd ${DATA} 1>/dev/null
++ timeit "Joiner-Recv-Unencrypted" "$tcmd | $strmcmd; RC=( "\${PIPESTATUS[@]}" )"
++ popd 1>/dev/null
++
++ set -e
++
++ if [[ $sfmt == 'xbstream' ]];then
++ # Special handling till lp:1193240 is fixed"
++ if [[ ${RC[$(( ${#RC[@]}-1 ))]} -eq 1 ]];then
++ wsrep_log_error "Xbstream failed"
++ wsrep_log_error "Data directory ${DATA} may not be empty: lp:1193240" \
++ "Manual intervention required in that case"
++ exit 32
++ fi
++ fi
++
++ wait %% # join for wait_for_listen thread
++
++ for ecode in "${RC[@]}";do
++ if [[ $ecode -ne 0 ]];then
++ wsrep_log_error "Error while getting data from donor node: " \
++ "exit codes: ${RC[@]}"
++ exit 32
++ fi
++ done
++
++ if [ ! -r "${MAGIC_FILE}" ]
++ then
++ # this message should cause joiner to abort
++ wsrep_log_error "xtrabackup process ended without creating '${MAGIC_FILE}'"
++ wsrep_log_info "Contents of datadir"
++ wsrep_log_info "$(ls -l ${DATA}/**/*)"
++ exit 32
++ fi
++
++ if ! ps -p ${WSREP_SST_OPT_PARENT} &>/dev/null
++ then
++ wsrep_log_error "Parent mysqld process (PID:${WSREP_SST_OPT_PARENT}) terminated unexpectedly."
++ exit 32
++ fi
++
++ if [ ! -r "${DATA}/${IST_FILE}" ]
++ then
++ wsrep_log_info "Proceeding with SST"
++ wsrep_log_info "Removing existing ib_logfile files"
++ if [[ $incremental -ne 1 ]];then
++ rm -f ${DATA}/ib_logfile*
++ else
++ rm -f ${BDATA}/ib_logfile*
++ fi
++
++ get_proc
++
++ # Rebuild indexes for compact backups
++ if grep -q 'compact = 1' ${DATA}/xtrabackup_checkpoints;then
++ wsrep_log_info "Index compaction detected"
++ rebuild=1
++ fi
++
++ if [[ $rebuild -eq 1 ]];then
++ nthreads=$(parse_cnf xtrabackup rebuild-threads $nproc)
++ wsrep_log_info "Rebuilding during prepare with $nthreads threads"
++ rebuildcmd="--rebuild-indexes --rebuild-threads=$nthreads"
++ fi
++
++ if test -n "$(find ${DATA} -maxdepth 1 -type f -name '*.qp' -print -quit)";then
++
++ wsrep_log_info "Compressed qpress files found"
++
++ if [[ ! -x `which qpress` ]];then
++ wsrep_log_error "qpress not found in path: $PATH"
++ exit 22
++ fi
++
++ if [[ -n $progress ]] && pv --help | grep -q 'line-mode';then
++ count=$(find ${DATA} -type f -name '*.qp' | wc -l)
++ count=$(( count*2 ))
++ if pv --help | grep -q FORMAT;then
++ pvopts="-f -s $count -l -N Decompression -F '%N => Rate:%r Elapsed:%t %e Progress: [%b/$count]'"
++ else
++ pvopts="-f -s $count -l -N Decompression"
++ fi
++ pcmd="pv $pvopts"
++ adjust_progress
++ dcmd="$pcmd | xargs -n 2 qpress -T${nproc}d"
++ else
++ dcmd="xargs -n 2 qpress -T${nproc}d"
++ fi
++
++ wsrep_log_info "Removing existing ibdata1 file"
++ rm -f ${DATA}/ibdata1
++
++ # Decompress the qpress files
++ wsrep_log_info "Decompression with $nproc threads"
++ timeit "Decompression" "find ${DATA} -type f -name '*.qp' -printf '%p\n%h\n' | $dcmd"
++ extcode=$?
++
++ if [[ $extcode -eq 0 ]];then
++ wsrep_log_info "Removing qpress files after decompression"
++ find ${DATA} -type f -name '*.qp' -delete
++ if [[ $? -ne 0 ]];then
++ wsrep_log_error "Something went wrong with deletion of qpress files. Investigate"
++ fi
++ else
++ wsrep_log_error "Decompression failed. Exit code: $extcode"
++ exit 22
++ fi
++ fi
++
++ if [[ $incremental -eq 1 ]];then
++ # Added --ibbackup=xtrabackup_55 because it fails otherwise citing connection issues.
++ INNOAPPLY="${INNOBACKUPEX_BIN} --defaults-file=${WSREP_SST_OPT_CONF} \
++ --ibbackup=xtrabackup_55 --apply-log $rebuildcmd --redo-only $BDATA --incremental-dir=${DATA} &>>${BDATA}/innobackup.prepare.log"
++ fi
++
++ wsrep_log_info "Preparing the backup at ${DATA}"
++ timeit "Xtrabackup prepare stage" "$INNOAPPLY"
++
++ if [[ $incremental -eq 1 ]];then
++ wsrep_log_info "Cleaning up ${DATA} after incremental SST"
++ [[ -d ${DATA} ]] && rm -rf ${DATA}
++ DATA=$BDATA
++ fi
++
++ if [ $? -ne 0 ];
++ then
++ wsrep_log_error "${INNOBACKUPEX_BIN} finished with errors. Check ${DATA}/innobackup.prepare.log"
++ exit 22
++ fi
++ else
++ wsrep_log_info "${IST_FILE} received from donor: Running IST"
++ fi
++
++ if [[ ! -r ${MAGIC_FILE} ]];then
++ wsrep_log_error "SST magic file ${MAGIC_FILE} not found/readable"
++ exit 2
++ fi
++
++ cat "${MAGIC_FILE}" # output UUID:seqno
++ wsrep_log_info "Total time on joiner: $totime seconds"
++fi
++
++exit 0
+diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
+index 3eeb07d..a63d98e 100644
+--- a/sql/CMakeLists.txt
++++ b/sql/CMakeLists.txt
+@@ -13,6 +13,10 @@
+ # along with this program; if not, write to the Free Software
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
++IF(WITH_WSREP)
++ SET(WSREP_INCLUDES ${CMAKE_SOURCE_DIR}/wsrep)
++ENDIF()
++
+ INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/sql
+@@ -20,6 +24,7 @@ INCLUDE_DIRECTORIES(
+ ${ZLIB_INCLUDE_DIR}
+ ${SSL_INCLUDE_DIRS}
+ ${CMAKE_BINARY_DIR}/sql
++ ${WSREP_INCLUDES}
+ )
+
+ SET(GEN_SOURCES
+@@ -171,7 +176,24 @@ SET(SQL_SHARED_SOURCES
+
+ SET(SQL_EXPORTED_SOURCES ${SQL_SHARED_SOURCES} PARENT_SCOPE)
+
++IF(WITH_WSREP)
++ SET(WSREP_SOURCES
++ wsrep_check_opts.cc
++ wsrep_hton.cc
++ wsrep_mysqld.cc
++ wsrep_notify.cc
++ wsrep_sst.cc
++ wsrep_utils.cc
++ wsrep_var.cc
++ wsrep_binlog.cc
++ wsrep_applier.cc
++ wsrep_thd.cc
++ )
++ SET(WSREP_LIB wsrep)
++ENDIF()
++
+ SET(SQL_SOURCE
++ ${WSREP_SOURCES}
+ ${GEN_SOURCES}
+ ${MYSYS_LIBWRAP_SOURCE}
+ ${SQL_SHARED_SOURCES}
+@@ -208,6 +230,7 @@ DTRACE_INSTRUMENT(sql)
+ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS}
+ mysys mysys_ssl dbug strings vio regex
+ ${LIBWRAP} ${LIBCRYPT} ${LIBDL}
++ ${WSREP_LIB}
+ ${SSL_LIBRARIES})
+
+ #
+@@ -246,7 +269,6 @@ ADD_LIBRARY(sqlgunitlib
+ )
+ ADD_DEPENDENCIES(sqlgunitlib GenError)
+
+-
+ IF(WIN32)
+ SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h message.rc)
+ ELSE()
+diff --git a/sql/binlog.cc b/sql/binlog.cc
+index d1babfe..bbf87dd 100644
+--- a/sql/binlog.cc
++++ b/sql/binlog.cc
+@@ -60,7 +60,11 @@ static ulonglong limit_unsafe_suppression_start_time= 0;
+ static bool unsafe_warning_suppression_is_activated= false;
+ static int limit_unsafe_warning_count= 0;
+
++#ifndef WITH_WSREP
+ static handlerton *binlog_hton;
++#else
++handlerton *binlog_hton; // we need it in wsrep_binlog.cc
++#endif
+ bool opt_binlog_order_commits= true;
+
+ const char *log_bin_index= 0;
+@@ -795,7 +799,9 @@ static binlog_cache_mngr *thd_get_cache_mngr(const THD *thd)
+ If opt_bin_log is not set, binlog_hton->slot == -1 and hence
+ thd_get_ha_data(thd, hton) segfaults.
+ */
++#ifndef WITH_WSREP
+ DBUG_ASSERT(opt_bin_log);
++#endif
+ return (binlog_cache_mngr *)thd_get_ha_data(thd, binlog_hton);
+ }
+
+@@ -881,7 +887,11 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos)
+ DBUG_ENTER("binlog_trans_log_savepos");
+ DBUG_ASSERT(pos != NULL);
+ binlog_cache_mngr *const cache_mngr= thd_get_cache_mngr(thd);
++#ifdef WITH_WSREP
++ DBUG_ASSERT((WSREP_EMULATE_BINLOG(thd)) || mysql_bin_log.is_open());
++#else
+ DBUG_ASSERT(mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
+ *pos= cache_mngr->trx_cache.get_byte_position();
+ DBUG_PRINT("return", ("position: %lu", (ulong) *pos));
+ DBUG_VOID_RETURN;
+@@ -897,7 +907,16 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos)
+ static int binlog_init(void *p)
+ {
+ binlog_hton= (handlerton *)p;
++#ifdef WITH_WSREP
++ if (WSREP_ON)
++ binlog_hton->state= SHOW_OPTION_YES;
++ else
++ {
++#endif /* WITH_WSREP */
+ binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ binlog_hton->db_type=DB_TYPE_BINLOG;
+ binlog_hton->savepoint_offset= sizeof(my_off_t);
+ binlog_hton->close_connection= binlog_close_connection;
+@@ -912,10 +931,30 @@ static int binlog_init(void *p)
+ return 0;
+ }
+
++#ifdef WITH_WSREP
++#include "wsrep_binlog.h"
++#endif /* WITH_WSREP */
+ static int binlog_close_connection(handlerton *hton, THD *thd)
+ {
+ DBUG_ENTER("binlog_close_connection");
+ binlog_cache_mngr *const cache_mngr= thd_get_cache_mngr(thd);
++#ifdef WITH_WSREP
++ if (!cache_mngr->is_binlog_empty()) {
++ IO_CACHE* cache= get_trans_log(thd);
++ uchar *buf;
++ size_t len=0;
++ wsrep_write_cache_buf(cache, &buf, &len);
++ WSREP_WARN("binlog trx cache not empty (%lu bytes) @ connection close %lu",
++ len, thd->thread_id);
++ if (len > 0) wsrep_dump_rbr_buf(thd, buf, len);
++
++ cache = cache_mngr->get_binlog_cache_log(false);
++ wsrep_write_cache_buf(cache, &buf, &len);
++ WSREP_WARN("binlog stmt cache not empty (%lu bytes) @ connection close %lu",
++ len, thd->thread_id);
++ if (len > 0) wsrep_dump_rbr_buf(thd, buf, len);
++ }
++#endif /* WITH_WSREP */
+ DBUG_ASSERT(cache_mngr->is_binlog_empty());
+ DBUG_ASSERT(cache_mngr->trx_cache.is_group_cache_empty() &&
+ cache_mngr->stmt_cache.is_group_cache_empty());
+@@ -1666,7 +1705,11 @@ int MYSQL_BIN_LOG::rollback(THD *thd, bool all)
+ if (error == 0 && stuff_logged)
+ error= ordered_commit(thd, all, /* skip_commit */ true);
+
++#ifdef WITH_WSREP
++ if (!WSREP_EMULATE_BINLOG(thd) && check_write_error(thd))
++#else
+ if (check_write_error(thd))
++#endif
+ {
+ /*
+ "all == true" means that a "rollback statement" triggered the error and
+@@ -4771,6 +4814,43 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time, bool auto_purge)
+ no_of_log_files_purged, no_of_log_files_to_purge);
+ }
+
++ if (log_is_active)
++ {
++ if(!auto_purge)
++ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
++ ER_WARN_PURGE_LOG_IS_ACTIVE,
++ ER(ER_WARN_PURGE_LOG_IS_ACTIVE),
++ log_info.log_file_name);
++
++ }
++
++ if (log_is_in_use)
++ {
++ int no_of_log_files_to_purge= no_of_log_files_purged+1;
++ while (strcmp(log_file_name, log_info.log_file_name))
++ {
++ if (mysql_file_stat(m_key_file_log, log_info.log_file_name,
++ &stat_area, MYF(0)))
++ {
++ if (stat_area.st_mtime < purge_time)
++ no_of_log_files_to_purge++;
++ else
++ break;
++ }
++ if (find_next_log(&log_info, false/*need_lock_index=false*/))
++ {
++ no_of_log_files_to_purge++;
++ break;
++ }
++ }
++
++ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
++ ER_WARN_PURGE_LOG_IN_USE,
++ ER(ER_WARN_PURGE_LOG_IN_USE),
++ copy_log_in_use, no_of_threads_locking_log,
++ no_of_log_files_purged, no_of_log_files_to_purge);
++ }
++
+ error= (to_log[0] ? purge_logs(to_log, true,
+ false/*need_lock_index=false*/,
+ true/*need_update_threads=true*/,
+@@ -5190,7 +5270,11 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
+ bool is_transactional)
+ {
+ DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)");
++#ifdef WITH_WSREP
++ DBUG_ASSERT(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open());
++#else
+ DBUG_ASSERT(mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
+ DBUG_PRINT("enter", ("event: 0x%lx", (long) event));
+
+ int error= 0;
+@@ -5265,7 +5349,13 @@ bool MYSQL_BIN_LOG::write_event(Log_event *event_info)
+ mostly called if is_open() *was* true a few instructions before, but it
+ could have changed since.
+ */
++#ifdef WITH_WSREP
++ /* applier and replayer can skip writing binlog events */
++ if ((WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV)) ||
++ is_open())
++#else
+ if (likely(is_open()))
++#endif
+ {
+ #ifdef HAVE_REPLICATION
+ /*
+@@ -5412,6 +5502,15 @@ int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
+ {
+ int error= 0;
+ DBUG_ENTER("MYSQL_BIN_LOG::rotate");
++#ifdef WITH_WSREP
++ if (WSREP_ON && wsrep_to_isolation)
++ {
++ *check_purge= false;
++ WSREP_DEBUG("avoiding binlog rotate due to TO isolation: %d",
++ wsrep_to_isolation);
++ DBUG_RETURN(0);
++ }
++#endif
+
+ DBUG_ASSERT(!is_relay_log);
+ mysql_mutex_assert_owner(&LOCK_log);
+@@ -5898,6 +5997,9 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd, bool need_lock_log,
+ bool MYSQL_BIN_LOG::write_cache(THD *thd, binlog_cache_data *cache_data)
+ {
+ DBUG_ENTER("MYSQL_BIN_LOG::write_cache(THD *, binlog_cache_data *, bool)");
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd)) DBUG_RETURN(0);
++#endif /* WITH_WSREP */
+
+ IO_CACHE *cache= &cache_data->cache_log;
+ bool incident= cache_data->has_incident();
+@@ -6353,7 +6455,13 @@ TC_LOG::enum_result MYSQL_BIN_LOG::commit(THD *thd, bool all)
+ DBUG_ENTER("MYSQL_BIN_LOG::commit");
+
+ binlog_cache_mngr *cache_mngr= thd_get_cache_mngr(thd);
++#ifdef WITH_WSREP
++ my_xid xid= (wsrep_is_wsrep_xid(&thd->transaction.xid_state.xid) ?
++ wsrep_xid_seqno(&thd->transaction.xid_state.xid) :
++ thd->transaction.xid_state.xid.get_my_xid());
++#else
+ my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
++#endif /* WITH_WSREP */
+ int error= RESULT_SUCCESS;
+ bool stuff_logged= false;
+
+@@ -6939,6 +7047,27 @@ int MYSQL_BIN_LOG::ordered_commit(THD *thd, bool all, bool skip_commit)
+ my_off_t total_bytes= 0;
+ bool do_rotate= false;
+
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd))
++ {
++ /*
++ Skip group commit, just do storage engine commit.
++ */
++ int rcode = ha_commit_low(thd, all);
++
++ /* if there is myisam statement inside innodb transaction, we may
++ have events in stmt cache
++ */
++ binlog_cache_mngr *const cache_mngr= thd_get_cache_mngr(thd);
++ if(!cache_mngr->stmt_cache.is_binlog_empty())
++ {
++ WSREP_DEBUG("stmt transaction inside MST, SQL: %s", thd->query());
++ cache_mngr->stmt_cache.reset();
++ }
++ DBUG_RETURN(rcode);
++ }
++#endif /* WITH_WSREP */
++
+ /*
+ These values are used while flushing a transaction, so clear
+ everything.
+@@ -7143,6 +7272,24 @@ int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle,
+ */
+ bool in_transaction= FALSE;
+
++#ifdef WITH_WSREP
++ /*
++ Read current wsrep position from storage engines to have consistent
++ end position for binlog scan.
++ */
++ XID xid;
++ memset(&xid, 0, sizeof(xid));
++ xid.formatID= -1;
++ wsrep_get_SE_checkpoint(&xid);
++ char uuid_str[40];
++ wsrep_uuid_print(wsrep_xid_uuid(&xid), uuid_str, sizeof(uuid_str));
++ WSREP_INFO("Binlog recovery, found wsrep position %s:%lld", uuid_str,
++ (long long)wsrep_xid_seqno(&xid));
++ const wsrep_seqno_t last_xid_seqno= wsrep_xid_seqno(&xid);
++ wsrep_seqno_t cur_xid_seqno=WSREP_SEQNO_UNDEFINED;
++#endif /* WITH_WSREP */
++
++
+ if (! fdle->is_valid() ||
+ my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
+ sizeof(my_xid), 0, 0, MYF(0)))
+@@ -7151,7 +7298,12 @@ int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle,
+ init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
+
+ while ((ev= Log_event::read_log_event(log, 0, fdle, TRUE))
+- && ev->is_valid())
++ && ev->is_valid()
++#ifdef WITH_WSREP
++ && (last_xid_seqno == WSREP_SEQNO_UNDEFINED ||
++ last_xid_seqno != cur_xid_seqno)
++#endif
++ )
+ {
+ if (ev->get_type_code() == QUERY_EVENT &&
+ !strcmp(((Query_log_event*)ev)->query, "BEGIN"))
+@@ -7172,6 +7324,9 @@ int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle,
+ sizeof(xev->xid));
+ if (!x || my_hash_insert(&xids, x))
+ goto err2;
++#ifdef WITH_WSREP
++ cur_xid_seqno= xev->xid;
++#endif /* WITH_WSREP */
+ }
+
+ /*
+@@ -7216,6 +7371,11 @@ int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle,
+ delete ev;
+ }
+
++#ifdef WITH_WSREP
++ WSREP_INFO("Binlog recovery scan stopped at Xid event %lld",
++ (long long)cur_xid_seqno);
++#endif /* WITH_WSREP */
++
+ if (ha_recover(&xids))
+ goto err2;
+
+@@ -7240,7 +7400,9 @@ Group_cache *THD::get_group_cache(bool is_transactional)
+
+ // If opt_bin_log==0, it is not safe to call thd_get_cache_mngr
+ // because binlog_hton has not been completely set up.
++#ifndef WITH_WSREP
+ DBUG_ASSERT(opt_bin_log);
++#endif
+ binlog_cache_mngr *cache_mngr= thd_get_cache_mngr(this);
+
+ // cache_mngr is NULL until we call thd->binlog_setup_trx_data, so
+@@ -7433,7 +7595,12 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
+ table->s->table_map_id.id()));
+
+ /* Pre-conditions */
++#ifdef WITH_WSREP
++ DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
++ (WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open()));
++#else
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
+ DBUG_ASSERT(table->s->table_map_id.is_valid());
+
+ Table_map_log_event
+@@ -7721,9 +7888,16 @@ int THD::decide_logging_format(TABLE_LIST *tables)
+ binlogging is off, or if the statement is filtered out from the
+ binlog by filtering rules.
+ */
++#ifdef WITH_WSREP
++ if ((WSREP_EMULATE_BINLOG(this) ||
++ (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG))) &&
++ !(WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT &&
++ !binlog_filter->db_ok(db)))
++#else
+ if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
+ !(variables.binlog_format == BINLOG_FORMAT_STMT &&
+ !binlog_filter->db_ok(db)))
++#endif /* WITH_WSREP */
+ {
+ /*
+ Compute one bit field with the union of all the engine
+@@ -7967,7 +8141,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
+ */
+ my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
+ }
+- else if (variables.binlog_format == BINLOG_FORMAT_ROW &&
++ else if (WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_ROW &&
+ sqlcom_can_generate_row_events(this))
+ {
+ /*
+@@ -7996,8 +8170,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
+ else
+ {
+ /* binlog_format = STATEMENT */
+- if (variables.binlog_format == BINLOG_FORMAT_STMT)
+- {
++ if (WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT)
++ {
+ if (lex->is_stmt_row_injection())
+ {
+ /*
+@@ -8013,7 +8187,14 @@ int THD::decide_logging_format(TABLE_LIST *tables)
+ 5. Error: Cannot modify table that uses a storage engine
+ limited to row-logging when binlog_format = STATEMENT
+ */
++#ifdef WITH_WSREP
++ if (!WSREP(this) || wsrep_exec_mode == LOCAL_STATE)
++ {
++#endif /* WITH_WSREP */
+ my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ }
+ else if (is_write && (unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
+ {
+@@ -8159,7 +8340,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
+ "and binlog_filter->db_ok(db) = %d",
+ mysql_bin_log.is_open(),
+ (variables.option_bits & OPTION_BIN_LOG),
+- variables.binlog_format,
++ WSREP_BINLOG_FORMAT(variables.binlog_format),
+ binlog_filter->db_ok(db)));
+ #endif
+
+@@ -8469,7 +8650,12 @@ int THD::binlog_write_row(TABLE* table, bool is_trans,
+ uchar const *record,
+ const uchar* extra_row_info)
+ {
++#ifdef WITH_WSREP
++ DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
++ ((WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open())));
++#else
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
+
+ /*
+ Pack records into format for transfer. We are allocating more
+@@ -8499,7 +8685,13 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
+ const uchar *after_record,
+ const uchar* extra_row_info)
+ {
++#ifdef WITH_WSREP
++ DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
++ ((WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open())));
++#else
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
++
+ int error= 0;
+
+ /**
+@@ -8565,7 +8757,13 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
+ uchar const *record,
+ const uchar* extra_row_info)
+ {
++#ifdef WITH_WSREP
++ DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
++ ((WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open())));
++#else
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
++
+ int error= 0;
+
+ /**
+@@ -8683,7 +8881,11 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
+ mode: it might be the case that we left row-based mode before
+ flushing anything (e.g., if we have explicitly locked tables).
+ */
+- if (!mysql_bin_log.is_open())
++#ifdef WITH_WSREP
++ if (!(WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open()))
++#else
++ if (!mysql_bin_log.is_open())
++#endif /* WITH_WSREP */
+ DBUG_RETURN(0);
+
+ /*
+@@ -8952,7 +9154,21 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
+ DBUG_ENTER("THD::binlog_query");
+ DBUG_PRINT("enter", ("qtype: %s query: '%s'",
+ show_query_type(qtype), query_arg));
++#ifdef WITH_WSREP
++ DBUG_ASSERT(query_arg && (WSREP_EMULATE_BINLOG(this)
++ || mysql_bin_log.is_open()));
++#else
+ DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
++#endif /* WITH_WSREP */
++
++ if (get_binlog_local_stmt_filter() == BINLOG_FILTER_SET)
++ {
++ /*
++ The current statement is to be ignored, and not written to
++ the binlog. Do not call issue_unsafe_warnings().
++ */
++ DBUG_RETURN(0);
++ }
+
+ if (get_binlog_local_stmt_filter() == BINLOG_FILTER_SET)
+ {
+@@ -9056,6 +9272,72 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
+ }
+
+ #endif /* !defined(MYSQL_CLIENT) */
++#ifdef WITH_WSREP
++IO_CACHE * get_trans_log(THD * thd)
++{
++ binlog_cache_mngr *const cache_mngr= thd_get_cache_mngr(thd);
++ if (cache_mngr)
++ {
++ return cache_mngr->get_binlog_cache_log(true);
++ }
++ else
++ {
++ WSREP_DEBUG("binlog cache not initialized, conn :%ld", thd->thread_id);
++ return NULL;
++ }
++}
++
++bool wsrep_trans_cache_is_empty(THD *thd)
++{
++ binlog_cache_mngr *const cache_mngr=
++ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
++ return (!cache_mngr || cache_mngr->trx_cache.is_binlog_empty());
++}
++
++void thd_binlog_flush_pending_rows_event(THD *thd, bool stmt_end)
++{
++ thd->binlog_flush_pending_rows_event(stmt_end);
++}
++void thd_binlog_trx_reset(THD * thd)
++{
++ /*
++ todo: fix autocommit select to not call the caller
++ */
++ if (thd_get_ha_data(thd, binlog_hton) != NULL)
++ {
++ binlog_cache_mngr *const cache_mngr= thd_get_cache_mngr(thd);
++ if (cache_mngr)
++ {
++ cache_mngr->trx_cache.reset();
++ if (!cache_mngr->stmt_cache.is_binlog_empty())
++ {
++ WSREP_DEBUG("pending events in stmt cache, sql: %s", thd->query());
++ cache_mngr->stmt_cache.reset();
++ }
++ }
++ }
++ thd->clear_binlog_table_maps();
++}
++
++TC_LOG::enum_result wsrep_thd_binlog_commit(THD* thd, bool all)
++{
++ /* applier and replayer can skip binlog commit */
++ if (WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV))
++ return mysql_bin_log.commit(thd, all);
++ else
++ return (ha_commit_low(thd, all) ?
++ TC_LOG::RESULT_ABORTED : TC_LOG::RESULT_SUCCESS);
++}
++
++int wsrep_thd_binlog_rollback(THD* thd, bool all)
++{
++ /* applier and replayer can skip binlog rollback */
++ if (WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV))
++ return mysql_bin_log.rollback(thd, all);
++ else
++ return ha_rollback_low(thd, all);
++}
++#endif /* WITH_WSREP */
+
+ struct st_mysql_storage_engine binlog_storage_engine=
+ { MYSQL_HANDLERTON_INTERFACE_VERSION };
+diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
+index 13a7691..139f0dc 100644
+--- a/sql/event_data_objects.cc
++++ b/sql/event_data_objects.cc
+@@ -1477,8 +1477,25 @@ end:
+ bool save_tx_read_only= thd->tx_read_only;
+ thd->tx_read_only= false;
+
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ // sql_print_information("sizeof(LEX) = %d", sizeof(struct LEX));
++ // sizeof(LEX) = 4512, so it's relatively safe to allocate it on stack.
++ LEX lex;
++ LEX* saved = thd->lex;
++ lex.sql_command = SQLCOM_DROP_EVENT;
++ thd->lex = &lex;
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
++ thd->lex = saved;
++ }
++#endif
++
+ ret= Events::drop_event(thd, dbname, name, FALSE);
+
++#ifdef WITH_WSREP
++ WSREP_TO_ISOLATION_END;
++ error:
++#endif
+ thd->tx_read_only= save_tx_read_only;
+ thd->security_ctx->master_access= saved_master_access;
+ }
+diff --git a/sql/events.cc b/sql/events.cc
+index ff87dd4..25e2e04 100644
+--- a/sql/events.cc
++++ b/sql/events.cc
+@@ -1139,7 +1139,20 @@ Events::load_events_from_db(THD *thd)
+ delete et;
+ goto end;
+ }
+-
++#ifdef WITH_WSREP
++ // when SST from master node who initials event, the event status is ENABLED
++ // this is problematic because there are two nodes with same events and both enabled.
++ if (et->originator != thd->server_id)
++ {
++ store_record(table, record[1]);
++ table->field[ET_FIELD_STATUS]->
++ store((longlong) Event_parse_data::SLAVESIDE_DISABLED,
++ TRUE);
++ (void) table->file->ha_update_row(table->record[1], table->record[0]);
++ delete et;
++ continue;
++ }
++#endif
+ /**
+ Since the Event_queue_element object could be deleted inside
+ Event_queue::create_event we should save the value of dropped flag
+@@ -1183,7 +1196,46 @@ end:
+ close_mysql_tables(thd);
+ DBUG_RETURN(ret);
+ }
++#ifdef WITH_WSREP
++int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len)
++{
++ String log_query;
+
++ if (create_query_string(thd, &log_query))
++ {
++ WSREP_WARN("events create string failed: %s", thd->query());
++ return 1;
++ }
++ return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
++}
++static int
++wsrep_alter_query_string(THD *thd, String *buf)
++{
++ /* Append the "ALTER" part of the query */
++ if (buf->append(STRING_WITH_LEN("ALTER ")))
++ return 1;
++ /* Append definer */
++ append_definer(thd, buf, &(thd->lex->definer->user), &(thd->lex->definer->host));
++ /* Append the left part of thd->query after event name part */
++ if (buf->append(thd->lex->stmt_definition_begin,
++ thd->lex->stmt_definition_end -
++ thd->lex->stmt_definition_begin))
++ return 1;
++
++ return 0;
++}
++int wsrep_alter_event_query(THD *thd, uchar** buf, size_t* buf_len)
++{
++ String log_query;
++
++ if (wsrep_alter_query_string(thd, &log_query))
++ {
++ WSREP_WARN("events alter string failed: %s", thd->query());
++ return 1;
++ }
++ return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
++}
++#endif /* WITH_WSREP */
+ /**
+ @} (End of group Event_Scheduler)
+ */
+diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
+index 26172ca..f92eb5d 100644
+--- a/sql/ha_partition.cc
++++ b/sql/ha_partition.cc
+@@ -411,7 +411,13 @@ const char *ha_partition::table_type() const
+ // we can do this since we only support a single engine type
+ return m_file[0]->table_type();
+ }
+-
++#ifdef WITH_WSREP
++int ha_partition::wsrep_db_type() const
++{
++ // we can do this since we only support a single engine type
++ return ha_legacy_type(m_file[0]->ht);
++}
++#endif /* WITH_WSREP */
+
+ /*
+ Destructor method
+diff --git a/sql/ha_partition.h b/sql/ha_partition.h
+index 6b20de0..acdfeb9 100644
+--- a/sql/ha_partition.h
++++ b/sql/ha_partition.h
+@@ -1244,6 +1244,9 @@ public:
+ -------------------------------------------------------------------------
+ virtual void append_create_info(String *packet)
+ */
++#ifdef WITH_WSREP
++ virtual int wsrep_db_type() const;
++#endif /* WITH_WSREP */
+ };
+
+ #endif /* HA_PARTITION_INCLUDED */
+diff --git a/sql/handler.cc b/sql/handler.cc
+index f91c04c..e39e0d8 100644
+--- a/sql/handler.cc
++++ b/sql/handler.cc
+@@ -60,7 +60,9 @@ inline double log2(double x)
+ return (log(x) / M_LN2);
+ }
+ #endif
+-
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif
+ /*
+ While we have legacy_db_type, we have this array to
+ check for dups and to find handlerton from legacy_db_type.
+@@ -1219,10 +1221,27 @@ int ha_prepare(THD *thd)
+ {
+ if ((err= ht->prepare(ht, thd, all)))
+ {
++#ifdef WITH_WSREP
++ if (WSREP(thd) && ht->db_type== DB_TYPE_WSREP)
++ {
++ error= 1;
++ /* avoid sending error, if we need to replay */
++ if (thd->wsrep_conflict_state!= MUST_REPLAY)
++ {
++ my_error(ER_LOCK_DEADLOCK, MYF(0), err);
++ }
++ }
++ else
++ {
++ /* not wsrep hton, bail to native mysql behavior */
++#endif
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
+ ha_rollback_trans(thd, all);
+ error=1;
+ break;
++#ifdef WITH_WSREP
++ }
++#endif
+ }
+ }
+ else
+@@ -1233,7 +1252,6 @@ int ha_prepare(THD *thd)
+ }
+ }
+ }
+-
+ DBUG_RETURN(error);
+ }
+
+@@ -1408,7 +1426,12 @@ int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock)
+ MDL_EXPLICIT);
+
+ DBUG_PRINT("debug", ("Acquire MDL commit lock"));
++#ifdef WITH_WSREP
++ if (!WSREP(thd) &&
++ thd->mdl_context.acquire_lock(&mdl_request,
++#else
+ if (thd->mdl_context.acquire_lock(&mdl_request,
++#endif /* WITH_WSREP */
+ thd->variables.lock_wait_timeout))
+ {
+ ha_rollback_trans(thd, all);
+@@ -1482,7 +1505,19 @@ int ha_commit_low(THD *thd, bool all, bool run_after_commit)
+ int error=0;
+ THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
+ Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
++
+ DBUG_ENTER("ha_commit_low");
++#ifdef WITH_WSREP
++#ifdef WSREP_PROC_INFO
++ char info[64]= { 0, };
++ snprintf (info, sizeof(info) - 1, "ha_commit_one_phase(%lld)",
++ (long long)wsrep_thd_trx_seqno(thd));
++#else
++ const char info[]="ha_commit_one_phase()";
++#endif /* WSREP_PROC_INFO */
++ char* tmp_info= NULL;
++ if (WSREP(thd)) tmp_info= (char *)thd_proc_info(thd, info);
++#endif /* WITH_WSREP */
+
+ if (ha_info)
+ {
+@@ -1513,6 +1548,9 @@ int ha_commit_low(THD *thd, bool all, bool run_after_commit)
+ /* Free resources and perform other cleanup even for 'empty' transactions. */
+ if (all)
+ thd->transaction.cleanup();
++#ifdef WITH_WSREP
++ if (WSREP(thd)) thd_proc_info(thd, tmp_info);
++#endif /* WITH_WSREP */
+ /*
+ When the transaction has been committed, we clear the commit_low
+ flag. This allow other parts of the system to check if commit_low
+@@ -1798,7 +1836,13 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
+ got, ha_resolve_storage_engine_name(hton));
+ for (int i=0; i < got; i ++)
+ {
++#ifdef WITH_WSREP
++ my_xid x=(wsrep_is_wsrep_xid(&info->list[i]) ?
++ wsrep_xid_seqno(&info->list[i]) :
++ info->list[i].get_my_xid());
++#else
+ my_xid x=info->list[i].get_my_xid();
++#endif /* WITH_WSREP */
+ if (!x) // not "mine" - that is generated by external TM
+ {
+ #ifndef DBUG_OFF
+@@ -1867,7 +1911,9 @@ int ha_recover(HASH *commit_list)
+ for now, only InnoDB supports 2pc. It means we can always safely
+ rollback all pending transactions, without risking inconsistent data
+ */
++#ifndef WITH_WSREP
+ DBUG_ASSERT(total_ha_2pc == (ulong) opt_bin_log+1); // only InnoDB and binlog
++#endif
+ tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
+ info.dry_run=FALSE;
+ #endif
+@@ -2103,8 +2149,35 @@ int ha_prepare_low(THD *thd, bool all)
+ continue;
+ if ((err= ht->prepare(ht, thd, all)))
+ {
++#ifdef WITH_WSREP
++ if (WSREP(thd) && ht->db_type== DB_TYPE_WSREP)
++ {
++ error= 1;
++ switch (err)
++ {
++ case WSREP_TRX_SIZE_EXCEEDED:
++ /* give user size exeeded erro from wsrep_api.h */
++ my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED);
++ break;
++ case WSREP_TRX_CERT_FAIL:
++ case WSREP_TRX_ERROR:
++ /* avoid sending error, if we need to replay */
++ if (thd->wsrep_conflict_state!= MUST_REPLAY)
++ {
++ my_error(ER_LOCK_DEADLOCK, MYF(0), err);
++ }
++ }
++ }
++
++ else
++ {
++ /* not wsrep hton, bail to native mysql behavior */
++#endif
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
+ error= 1;
++#ifdef WITH_WSREP
++ }
++#endif
+ }
+ status_var_increment(thd->status_var.ha_prepare_count);
+ }
+@@ -3336,7 +3409,12 @@ int handler::update_auto_increment()
+ variables->auto_increment_increment);
+ auto_inc_intervals_count++;
+ /* Row-based replication does not need to store intervals in binlog */
++#ifdef WITH_WSREP
++ if (((WSREP_EMULATE_BINLOG(thd)) || mysql_bin_log.is_open()) &&
++ !thd->is_current_stmt_binlog_format_row())
++#else
+ if (mysql_bin_log.is_open() && !thd->is_current_stmt_binlog_format_row())
++#endif /* WITH_WSREP */
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(),
+ auto_inc_interval_for_cur_row.values(),
+ variables->auto_increment_increment);
+@@ -4621,7 +4699,9 @@ int ha_enable_transaction(THD *thd, bool on)
+ int error=0;
+ DBUG_ENTER("ha_enable_transaction");
+ DBUG_PRINT("enter", ("on: %d", (int) on));
+-
++#ifdef WITH_WSREP
++ if (thd->wsrep_applier) DBUG_RETURN(0);
++#endif
+ if ((thd->transaction.flags.enabled= on))
+ {
+ /*
+@@ -7017,7 +7097,13 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
+ return (thd->is_current_stmt_binlog_format_row() &&
+ table->s->cached_row_logging_check &&
+ (thd->variables.option_bits & OPTION_BIN_LOG) &&
++#ifdef WITH_WSREP
++ /* applier and replayer should not binlog */
++ ((WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV)) ||
++ mysql_bin_log.is_open()));
++#else
+ mysql_bin_log.is_open());
++#endif
+ }
+
+
+@@ -7118,6 +7204,17 @@ int binlog_log_row(TABLE* table,
+ bool error= 0;
+ THD *const thd= table->in_use;
+
++#ifdef WITH_WSREP
++ /* only InnoDB tables will be replicated through binlog emulation */
++ if (WSREP_EMULATE_BINLOG(thd) &&
++ table->file->ht->db_type != DB_TYPE_INNODB &&
++ !(table->file->ht->db_type == DB_TYPE_PARTITION_DB &&
++ (((ha_partition*)(table->file))->wsrep_db_type() == DB_TYPE_INNODB)))
++ // !strcmp(table->file->table_type(), "InnoDB"))
++ {
++ return 0;
++ }
++#endif /* WITH_WSREP */
+ if (check_table_binlog_row_based(thd, table))
+ {
+ DBUG_DUMP("read_set 10", (uchar*) table->read_set->bitmap,
+@@ -7438,6 +7535,64 @@ void signal_log_not_needed(struct handlerton, char *log_file)
+ DBUG_VOID_RETURN;
+ }
+
++#ifdef WITH_WSREP
++/**
++ @details
++ This function makes the storage engine to force the victim transaction
++ to abort. Currently, only innodb has this functionality, but any SE
++ implementing the wsrep API should provide this service to support
++ multi-master operation.
++
++ @param bf_thd brute force THD asking for the abort
++ @param victim_thd victim THD to be aborted
++
++ @return
++ always 0
++*/
++
++int ha_wsrep_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
++{
++ DBUG_ENTER("ha_wsrep_abort_transaction");
++ if (!WSREP(bf_thd) &&
++ !(wsrep_OSU_method_options == WSREP_OSU_RSU &&
++ bf_thd->wsrep_exec_mode == TOTAL_ORDER)) {
++ DBUG_RETURN(0);
++ }
++
++ handlerton *hton= installed_htons[DB_TYPE_INNODB];
++ if (hton && hton->wsrep_abort_transaction)
++ {
++ hton->wsrep_abort_transaction(hton, bf_thd, victim_thd, signal);
++ }
++ else
++ {
++ WSREP_WARN("cannot abort InnoDB transaction");
++ }
++
++ DBUG_RETURN(0);
++}
++
++void ha_wsrep_fake_trx_id(THD *thd)
++{
++ DBUG_ENTER("ha_wsrep_fake_trx_id");
++ if (!WSREP(thd))
++ {
++ DBUG_VOID_RETURN;
++ }
++
++ handlerton *hton= installed_htons[DB_TYPE_INNODB];
++ if (hton && hton->wsrep_fake_trx_id)
++ {
++ hton->wsrep_fake_trx_id(hton, thd);
++ }
++ else
++ {
++ WSREP_WARN("cannot get get fake InnoDB transaction ID");
++ }
++
++ DBUG_VOID_RETURN;
++}
++#endif /* WITH_WSREP */
+ #ifdef TRANS_LOG_MGM_EXAMPLE_CODE
+ /*
+ Example of transaction log management functions based on assumption that logs
+diff --git a/sql/handler.h b/sql/handler.h
+index 3d7d4fa..702b291 100644
+--- a/sql/handler.h
++++ b/sql/handler.h
+@@ -392,6 +392,7 @@ enum legacy_db_type
+ DB_TYPE_MARIA,
+ /** Performance schema engine. */
+ DB_TYPE_PERFORMANCE_SCHEMA,
++ DB_TYPE_WSREP,
+ DB_TYPE_FIRST_DYNAMIC=42,
+ DB_TYPE_DEFAULT=127 // Must be last
+ };
+@@ -924,6 +925,11 @@ struct handlerton
+ const char *wild, bool dir, List<LEX_STRING> *files);
+ int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db,
+ const char *name);
++ int (*wsrep_abort_transaction)(handlerton *hton, THD *bf_thd,
++ THD *victim_thd, my_bool signal);
++ int (*wsrep_set_checkpoint)(handlerton *hton, const XID* xid);
++ int (*wsrep_get_checkpoint)(handlerton *hton, XID* xid);
++ void (*wsrep_fake_trx_id)(handlerton *hton, THD *thd);
+ int (*make_pushed_join)(handlerton *hton, THD* thd,
+ const AQP::Join_plan* plan);
+
+@@ -3332,6 +3338,9 @@ private:
+ extern const char *ha_row_type[];
+ extern MYSQL_PLUGIN_IMPORT const char *tx_isolation_names[];
+ extern MYSQL_PLUGIN_IMPORT const char *binlog_format_names[];
++#ifdef WITH_WSREP
++extern MYSQL_PLUGIN_IMPORT const char *wsrep_binlog_format_names[];
++#endif /* WITH_WSREP */
+ extern TYPELIB tx_isolation_typelib;
+ extern const char *myisam_stats_method_names[];
+ extern ulong total_ha, total_ha_2pc;
+@@ -3440,6 +3449,10 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv);
+ bool ha_rollback_to_savepoint_can_release_mdl(THD *thd);
+ int ha_savepoint(THD *thd, SAVEPOINT *sv);
+ int ha_release_savepoint(THD *thd, SAVEPOINT *sv);
++#ifdef WITH_WSREP
++int ha_wsrep_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal);
++void ha_wsrep_fake_trx_id(THD *thd);
++#endif /* WITH_WSREP */
+
+ /* Build pushed joins in handlers implementing this feature */
+ int ha_make_pushed_joins(THD *thd, const AQP::Join_plan* plan);
+@@ -3471,6 +3484,12 @@ void ha_binlog_wait(THD *thd);
+ #define ha_binlog_log_query(a,b,c,d,e,f,g) do {} while (0)
+ #define ha_binlog_wait(a) do {} while (0)
+ #endif
++#ifdef WITH_WSREP
++void wsrep_brute_force_aborts();
++#endif
++
++/* It is required by basic binlog features on both MySQL server and libmysqld */
++int ha_binlog_end(THD *thd);
+
+ /* It is required by basic binlog features on both MySQL server and libmysqld */
+ int ha_binlog_end(THD *thd);
+diff --git a/sql/item_func.cc b/sql/item_func.cc
+index e5ff0b8..9030a02 100644
+--- a/sql/item_func.cc
++++ b/sql/item_func.cc
+@@ -2618,7 +2618,19 @@ void Item_func_rand::seed_random(Item *arg)
+ TODO: do not do reinit 'rand' for every execute of PS/SP if
+ args[0] is a constant.
+ */
++#ifdef WITH_WSREP
++ uint32 tmp;
++ if (WSREP(current_thd))
++ {
++ if (current_thd->wsrep_exec_mode==REPL_RECV)
++ tmp= current_thd->wsrep_rand;
++ else
++ tmp= current_thd->wsrep_rand= (uint32) arg->val_int();
++ } else
++ tmp= (uint32) arg->val_int();
++#else
+ uint32 tmp= (uint32) arg->val_int();
++#endif
+ randominit(rand, (uint32) (tmp*0x10001L+55555555L),
+ (uint32) (tmp*0x10000001L));
+ }
+diff --git a/sql/lock.cc b/sql/lock.cc
+index 43f2773..e3f613d 100644
+--- a/sql/lock.cc
++++ b/sql/lock.cc
+@@ -83,6 +83,10 @@
+ #include <hash.h>
+ #include <assert.h>
+
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif /* WITH_WSREP */
++
+ /**
+ @defgroup Locking Locking
+ @{
+@@ -314,6 +318,10 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
+ /* Copy the lock data array. thr_multi_lock() reorders its contents. */
+ memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
+ sql_lock->lock_count * sizeof(*sql_lock->locks));
++#ifdef WITH_WSREP
++ //thd->main_lock_id.info->in_lock_tables= thd->in_lock_tables;
++ thd->lock_info.in_lock_tables= thd->in_lock_tables;
++#endif /* Lock on the copied half of the lock data array. */
+ /* Lock on the copied half of the lock data array. */
+ rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
+ sql_lock->lock_count,
+@@ -1014,11 +1022,15 @@ void Global_read_lock::unlock_global_read_lock(THD *thd)
+ {
+ thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
+ m_mdl_blocks_commits_lock= NULL;
++#ifdef WITH_WSREP
++ wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
++ wsrep->resume(wsrep);
++#endif /* WITH_WSREP */
+ }
+ thd->mdl_context.release_lock(m_mdl_global_shared_lock);
+ m_mdl_global_shared_lock= NULL;
+ m_state= GRL_NONE;
+-
++
+ DBUG_VOID_RETURN;
+ }
+
+@@ -1046,6 +1058,20 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
+ If we didn't succeed lock_global_read_lock(), or if we already suceeded
+ make_global_read_lock_block_commit(), do nothing.
+ */
++
++#ifdef WITH_WSREP
++ if (m_mdl_blocks_commits_lock)
++ {
++ WSREP_DEBUG("GRL was in block commit mode when entering "
++ "make_global_read_lock_block_commit");
++ thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
++ m_mdl_blocks_commits_lock= NULL;
++ wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
++ wsrep->resume(wsrep);
++ m_state= GRL_ACQUIRED;
++ }
++#endif /* WITH_WSREP */
++
+ if (m_state != GRL_ACQUIRED)
+ DBUG_RETURN(0);
+
+@@ -1058,6 +1084,22 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
+ m_mdl_blocks_commits_lock= mdl_request.ticket;
+ m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
+
++#ifdef WITH_WSREP
++ long long ret = wsrep->pause(wsrep);
++ if (ret >= 0)
++ {
++ wsrep_locked_seqno= ret;
++ }
++ else if (ret != -ENOSYS) /* -ENOSYS - no provider */
++ {
++ WSREP_ERROR("Failed to pause provider: %lld (%s)", -ret, strerror(-ret));
++
++ /* m_mdl_blocks_commits_lock is always NULL here */
++ wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
++ my_error(ER_LOCK_DEADLOCK, MYF(0));
++ DBUG_RETURN(TRUE);
++ }
++#endif /* WITH_WSREP */
+ DBUG_RETURN(FALSE);
+ }
+
+diff --git a/sql/log.cc b/sql/log.cc
+index 808c176..caa23d2 100644
+--- a/sql/log.cc
++++ b/sql/log.cc
+@@ -39,6 +39,9 @@
+ #include <my_dir.h>
+ #include <stdarg.h>
+ #include <m_ctype.h> // For test_if_number
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif /* WITH_WSREP */
+
+ #ifdef _WIN32
+ #include "message.h"
+@@ -300,7 +303,6 @@ bool LOGGER::is_log_table_enabled(uint log_table_type)
+ }
+ }
+
+-
+ /* Check if a given table is opened log table */
+ int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
+ const char *table_name, bool check_if_opened)
+@@ -2435,6 +2437,7 @@ int my_plugin_log_message(MYSQL_PLUGIN *plugin_ptr, plugin_log_level level,
+
+ /********* transaction coordinator log for 2pc - mmap() based solution *******/
+
++
+ /*
+ the log consists of a file, mmapped to a memory.
+ file is divided on pages of tc_log_page_size size.
+@@ -2550,8 +2553,14 @@ int TC_LOG_MMAP::open(const char *opt_name)
+ mysql_mutex_init(key_PAGE_lock, &pg->lock, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_PAGE_cond, &pg->cond, 0);
+ pg->start=(my_xid *)(data + i*tc_log_page_size);
++#ifdef WITH_WSREP
++ if (!WSREP_ON)
++#endif /* WITH_WSREP */
+ pg->end=(my_xid *)(pg->start + tc_log_page_size);
+ pg->size=pg->free=tc_log_page_size/sizeof(my_xid);
++#ifdef WITH_WSREP
++ if (WSREP_ON) pg->end=pg->start + pg->size;
++#endif /* WITH_WSREP */
+ }
+ pages[0].size=pages[0].free=
+ (tc_log_page_size-TC_LOG_HEADER_SIZE)/sizeof(my_xid);
+diff --git a/sql/log.h b/sql/log.h
+index 6536c9e..aaefce9 100644
+--- a/sql/log.h
++++ b/sql/log.h
+@@ -104,6 +104,14 @@ class TC_LOG
+ virtual int prepare(THD *thd, bool all) = 0;
+ };
+
++#ifdef WITH_WSREP
++/*
++ Wrappers to MYSQL_BIN_LOG commit()/rollback() when wsrep_emulate_bin_log
++ is on.
++ */
++TC_LOG::enum_result wsrep_thd_binlog_commit(THD* thd, bool all);
++int wsrep_thd_binlog_rollback(THD * thd, bool all);
++#endif /* WITH_WSREP */
+
+ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
+ {
+@@ -112,10 +120,18 @@ public:
+ int open(const char *opt_name) { return 0; }
+ void close() { }
+ enum_result commit(THD *thd, bool all) {
++#ifdef WITH_WSREP
++ return wsrep_thd_binlog_commit(thd, all);
++#else
+ return ha_commit_low(thd, all) ? RESULT_ABORTED : RESULT_SUCCESS;
++#endif /* WITH_WSREP */
+ }
+ int rollback(THD *thd, bool all) {
++#ifdef WITH_WSREP
++ return wsrep_thd_binlog_rollback(thd, all);
++#else
+ return ha_rollback_low(thd, all);
++#endif /* WITH_WSREP */
+ }
+ int prepare(THD *thd, bool all) {
+ return ha_prepare_low(thd, all);
+@@ -542,12 +558,28 @@ enum enum_binlog_row_image {
+ };
+
+ enum enum_binlog_format {
++ /*
++ statement-based except for cases where only row-based can work (UUID()
++ etc):
++ */
+ BINLOG_FORMAT_MIXED= 0, ///< statement if safe, otherwise row - autodetected
+ BINLOG_FORMAT_STMT= 1, ///< statement-based
+ BINLOG_FORMAT_ROW= 2, ///< row-based
+ BINLOG_FORMAT_UNSPEC=3 ///< thd_binlog_format() returns it when binlog is closed
+ };
+
++#ifdef WITH_WSREP
++IO_CACHE* get_trans_log(THD * thd);
++bool wsrep_trans_cache_is_empty(THD *thd);
++void thd_binlog_flush_pending_rows_event(THD *thd, bool stmt_end);
++void thd_binlog_trx_reset(THD * thd);
++
++#define WSREP_BINLOG_FORMAT(my_format) \
++ ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) ? \
++ wsrep_forced_binlog_format : my_format)
++#else
++#define WSREP_BINLOG_FORMAT(my_format) my_format
++#endif /* WITH_WSREP */
+ int query_error_code(THD *thd, bool not_killed);
+ uint purge_log_get_error_code(int res);
+
+diff --git a/sql/log_event.cc b/sql/log_event.cc
+index d4176cb..9173566 100644
+--- a/sql/log_event.cc
++++ b/sql/log_event.cc
+@@ -45,6 +45,9 @@
+ #include "rpl_rli_pdb.h"
+ #include "sql_show.h" // append_identifier
+ #include <mysql/psi/mysql_statement.h>
++#if WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif
+ #define window_size Log_throttle::LOG_THROTTLE_WINDOW_SIZE
+ Error_log_throttle
+ slave_ignored_err_throttle(window_size,
+@@ -3583,6 +3586,14 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
+ master_data_written(0), mts_accessed_dbs(0)
+ {
+
++#ifdef WITH_WSREP
++ /*
++ If Query_log_event will contain non trans keyword (not BEGIN, COMMIT,
++ SAVEPOINT or ROLLBACK) we disable PA for this transaction.
++ */
++ if (!is_trans_keyword())
++ thd->wsrep_PA_safe= false;
++#endif /* WITH_WSREP */
+ memset(&user, 0, sizeof(user));
+ memset(&host, 0, sizeof(host));
+
+@@ -11160,6 +11171,18 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
+ if (open_and_lock_tables(thd, rli->tables_to_lock, FALSE, 0))
+ {
+ uint actual_error= thd->get_stmt_da()->sql_errno();
++#ifdef WITH_WSREP
++ if (WSREP(thd))
++ {
++ WSREP_WARN("BF applier failed to open_and_lock_tables: %u, fatal: %d "
++ "wsrep = (exec_mode: %d conflict_state: %d seqno: %lld)",
++ thd->get_stmt_da()->sql_errno(),
++ thd->is_fatal_error,
++ thd->wsrep_exec_mode,
++ thd->wsrep_conflict_state,
++ (long long)wsrep_thd_trx_seqno(thd));
++ }
++#endif
+ if (thd->is_slave_error || thd->is_fatal_error)
+ {
+ /*
+@@ -12132,7 +12155,12 @@ check_table_map(Relay_log_info const *rli, RPL_TABLE_LIST *table_list)
+ DBUG_ENTER("check_table_map");
+ enum_tbl_map_status res= OK_TO_PROCESS;
+
++#ifdef WITH_WSREP
++ if ((rli->info_thd->slave_thread /* filtering is for slave only */ ||
++ (WSREP(rli->info_thd) && rli->info_thd->wsrep_applier)) &&
++#else
+ if (rli->info_thd->slave_thread /* filtering is for slave only */ &&
++#endif /* WITH_WSREP */
+ (!rpl_filter->db_ok(table_list->db) ||
+ (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list))))
+ res= FILTERED_OUT;
+@@ -12865,8 +12893,23 @@ int
+ Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+ {
+ DBUG_ASSERT(m_table != NULL);
++#ifdef WITH_WSREP
++#ifdef WSREP_PROC_INFO
++ char info[64];
++ info[sizeof(info) - 1] = '\0';
++ snprintf(info, sizeof(info) - 1, "Write_rows_log_event::write_row(%lld)",
++ (long long) wsrep_thd_trx_seqno(thd));
++ const char* tmp = (WSREP(thd)) ? thd_proc_info(thd, info) : NULL;
++#else
++ const char* tmp = (WSREP(thd)) ?
++ thd_proc_info(thd,"Write_rows_log_event::write_row()") : NULL;
++#endif /* WSREP_PROC_INFO */
++#endif /* WITH_WSREP */
+ int error= write_row(rli, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
+
++#ifdef WITH_WSREP
++ if (WSREP(thd)) thd_proc_info(thd, tmp);
++#endif /* WITH_WSREP */
+ if (error && !thd->is_error())
+ {
+ DBUG_ASSERT(0);
+diff --git a/sql/mdl.cc b/sql/mdl.cc
+index 70a33ab..ea79bec 100644
+--- a/sql/mdl.cc
++++ b/sql/mdl.cc
+@@ -24,6 +24,17 @@
+ #include <mysql/psi/mysql_stage.h>
+ #include <my_murmur3.h>
+
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#include "wsrep_thd.h"
++extern "C" my_thread_id wsrep_thd_thread_id(THD *thd);
++extern "C" char *wsrep_thd_query(THD *thd);
++void sql_print_information(const char *format, ...)
++ ATTRIBUTE_FORMAT(printf, 1, 2);
++extern bool
++wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
++ MDL_ticket *ticket);
++#endif /* WITH_WSREP */
+ #ifdef HAVE_PSI_INTERFACE
+ static PSI_mutex_key key_MDL_map_mutex;
+ static PSI_mutex_key key_MDL_wait_LOCK_wait_status;
+@@ -1389,11 +1400,54 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
+ called by other threads.
+ */
+ DBUG_ASSERT(ticket->get_lock());
++#ifdef WITH_WSREP
++ if ((this == &(ticket->get_lock()->m_waiting)) &&
++ wsrep_thd_is_BF((void *)(ticket->get_ctx()->wsrep_get_thd()), false))
++ {
++ Ticket_iterator itw(ticket->get_lock()->m_waiting);
++ Ticket_iterator itg(ticket->get_lock()->m_granted);
++
++ MDL_ticket *waiting, *granted;
++ MDL_ticket *prev=NULL;
++ bool added= false;
++
++ while ((waiting= itw++) && !added)
++ {
++ if (!wsrep_thd_is_BF((void *)(waiting->get_ctx()->wsrep_get_thd()), true))
++ {
++ WSREP_DEBUG("MDL add_ticket inserted before: %lu %s",
++ wsrep_thd_thread_id(waiting->get_ctx()->wsrep_get_thd()),
++ wsrep_thd_query(waiting->get_ctx()->wsrep_get_thd()));
++ m_list.insert_after(prev, ticket);
++ added= true;
++ }
++ prev= waiting;
++ }
++ if (!added) m_list.push_back(ticket);
++
++ while ((granted= itg++))
++ {
++ if (granted->get_ctx() != ticket->get_ctx() &&
++ granted->is_incompatible_when_granted(ticket->get_type()))
++ {
++ if (!wsrep_grant_mdl_exception(ticket->get_ctx(), granted))
++ {
++ WSREP_DEBUG("MDL victim killed at add_ticket");
++ }
++ }
++ }
++ }
++ else
++ {
++#endif /* WITH_WSREP */
+ /*
+ Add ticket to the *back* of the queue to ensure fairness
+ among requests with the same priority.
+ */
+ m_list.push_back(ticket);
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ m_bitmap|= MDL_BIT(ticket->get_type());
+ }
+
+@@ -1709,7 +1763,6 @@ MDL_object_lock::m_waiting_incompatible[MDL_TYPE_END] =
+ 0
+ };
+
+-
+ /**
+ Check if request for the metadata lock can be satisfied given its
+ current state.
+@@ -1734,6 +1787,9 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
+ bool can_grant= FALSE;
+ bitmap_t waiting_incompat_map= incompatible_waiting_types_bitmap()[type_arg];
+ bitmap_t granted_incompat_map= incompatible_granted_types_bitmap()[type_arg];
++#ifdef WITH_WSREP
++ bool wsrep_can_grant= TRUE;
++#endif /* WITH_WSREP */
+
+ /*
+ New lock request can be satisfied iff:
+@@ -1756,12 +1812,59 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
+ {
+ if (ticket->get_ctx() != requestor_ctx &&
+ ticket->is_incompatible_when_granted(type_arg))
++#ifdef WITH_WSREP
++ {
++ if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()),false) &&
++ key.mdl_namespace() == MDL_key::GLOBAL)
++ {
++ WSREP_DEBUG("global lock granted for BF: %lu %s",
++ wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
++ wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
++ can_grant = true;
++ }
++ else if (!wsrep_grant_mdl_exception(requestor_ctx, ticket))
++ {
++ wsrep_can_grant= FALSE;
++ if (wsrep_log_conflicts)
++ {
++ MDL_lock * lock = ticket->get_lock();
++ WSREP_INFO(
++ "MDL conflict db=%s table=%s ticket=%d solved by %s",
++ lock->key.db_name(), lock->key.name(), ticket->get_type(), "abort"
++ );
++ }
++ }
++ else
++ {
++ can_grant= TRUE;
++ }
++ }
++#else
+ break;
++#endif /* WITH_WSREP */
+ }
++#ifdef WITH_WSREP
++ if ((ticket == NULL) && wsrep_can_grant)
++#else
+ if (ticket == NULL) /* Incompatible locks are our own. */
++#endif /* WITH_WSREP */
++
+ can_grant= TRUE;
+ }
+ }
++#ifdef WITH_WSREP
++ else
++ {
++ if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()), false) &&
++ key.mdl_namespace() == MDL_key::GLOBAL)
++ {
++ WSREP_DEBUG("global lock granted for BF (waiting queue): %lu %s",
++ wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
++ wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
++ can_grant = true;
++ }
++ }
++#endif /* WITH_WSREP */
+ return can_grant;
+ }
+
+@@ -2780,6 +2883,12 @@ void MDL_context::release_locks_stored_before(enum_mdl_duration duration,
+ }
+
+
++#ifdef WITH_WSREP
++void MDL_context::release_explicit_locks()
++{
++ release_locks_stored_before(MDL_EXPLICIT, NULL);
++}
++#endif
+ /**
+ Release all explicit locks in the context which correspond to the
+ same name/object as this lock request.
+@@ -3068,3 +3177,32 @@ void MDL_context::set_transaction_duration_for_all_locks()
+ ticket->m_duration= MDL_TRANSACTION;
+ #endif
+ }
++#ifdef WITH_WSREP
++void MDL_ticket::wsrep_report(bool debug)
++{
++ if (debug)
++ {
++ WSREP_DEBUG("MDL ticket: type: %s space: %s db: %s name: %s",
++ (get_type() == MDL_INTENTION_EXCLUSIVE) ? "intention exclusive" :
++ ((get_type() == MDL_SHARED) ? "shared" :
++ ((get_type() == MDL_SHARED_HIGH_PRIO ? "shared high prio" :
++ ((get_type() == MDL_SHARED_READ) ? "shared read" :
++ ((get_type() == MDL_SHARED_WRITE) ? "shared write" :
++ ((get_type() == MDL_SHARED_NO_WRITE) ? "shared no write" :
++ ((get_type() == MDL_SHARED_NO_READ_WRITE) ? "shared no read write" :
++ ((get_type() == MDL_EXCLUSIVE) ? "exclusive" :
++ "UNKNOWN")))))))),
++ (m_lock->key.mdl_namespace() == MDL_key::GLOBAL) ? "GLOBAL" :
++ ((m_lock->key.mdl_namespace() == MDL_key::SCHEMA) ? "SCHEMA" :
++ ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "TABLE" :
++ ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "FUNCTION" :
++ ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "PROCEDURE" :
++ ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "TRIGGER" :
++ ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "EVENT" :
++ ((m_lock->key.mdl_namespace() == MDL_key::COMMIT) ? "COMMIT" :
++ (char *)"UNKNOWN"))))))),
++ m_lock->key.db_name(),
++ m_lock->key.name());
++ }
++}
++#endif /* WITH_WSREP */
+diff --git a/sql/mdl.h b/sql/mdl.h
+index ddbd55a..2d2502e 100644
+--- a/sql/mdl.h
++++ b/sql/mdl.h
+@@ -577,6 +577,9 @@ public:
+ MDL_ticket *next_in_lock;
+ MDL_ticket **prev_in_lock;
+ public:
++#ifdef WITH_WSREP
++ void wsrep_report(bool debug);
++#endif /* WITH_WSREP */
+ bool has_pending_conflicting_lock() const;
+
+ MDL_context *get_ctx() const { return m_ctx; }
+@@ -763,6 +766,13 @@ public:
+ m_tickets[MDL_EXPLICIT].is_empty());
+ }
+
++#ifdef WITH_WSREP
++ inline bool has_transactional_locks() const
++ {
++ return !m_tickets[MDL_TRANSACTION].is_empty();
++ }
++#endif /* WITH_WSREP */
++
+ MDL_savepoint mdl_savepoint()
+ {
+ return MDL_savepoint(m_tickets[MDL_STATEMENT].front(),
+@@ -775,6 +785,9 @@ public:
+
+ void release_statement_locks();
+ void release_transactional_locks();
++#ifdef WITH_WSREP
++ void release_explicit_locks();
++#endif
+ void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
+
+ MDL_context_owner *get_owner() { return m_owner; }
+@@ -908,6 +921,9 @@ private:
+ MDL_ticket **out_ticket);
+
+ public:
++#ifdef WITH_WSREP
++ THD *wsrep_get_thd() const { return get_thd(); }
++#endif /* WITH_WSREP */
+ void find_deadlock();
+
+ bool visit_subgraph(MDL_wait_for_graph_visitor *dvisitor);
+diff --git a/sql/mysqld.cc b/sql/mysqld.cc
+index fa68613..8ed210f 100644
+--- a/sql/mysqld.cc
++++ b/sql/mysqld.cc
+@@ -72,6 +72,12 @@
+ #include "probes_mysql.h"
+ #include "scheduler.h"
+ #include "debug_sync.h"
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#include "wsrep_var.h"
++#include "wsrep_thd.h"
++#include "wsrep_sst.h"
++#endif
+ #include "sql_callback.h"
+ #include "opt_trace_context.h"
+
+@@ -483,6 +489,10 @@ ulong binlog_checksum_options;
+ my_bool opt_master_verify_checksum= 0;
+ my_bool opt_slave_sql_verify_checksum= 1;
+ const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS};
++#ifdef WITH_WSREP
++const char *wsrep_binlog_format_names[]=
++ {"MIXED", "STATEMENT", "ROW", "NONE", NullS};
++#endif /*WITH_WSREP */
+ my_bool enforce_gtid_consistency;
+ my_bool binlog_gtid_simple_recovery;
+ ulong binlog_error_action;
+@@ -747,6 +757,23 @@ pthread_attr_t connection_attrib;
+ mysql_mutex_t LOCK_server_started;
+ mysql_cond_t COND_server_started;
+
++#ifdef WITH_WSREP
++mysql_mutex_t LOCK_wsrep_ready;
++mysql_cond_t COND_wsrep_ready;
++mysql_mutex_t LOCK_wsrep_sst;
++mysql_cond_t COND_wsrep_sst;
++mysql_mutex_t LOCK_wsrep_sst_init;
++mysql_cond_t COND_wsrep_sst_init;
++mysql_mutex_t LOCK_wsrep_rollback;
++mysql_cond_t COND_wsrep_rollback;
++wsrep_aborting_thd_t wsrep_aborting_thd= NULL;
++mysql_mutex_t LOCK_wsrep_replaying;
++mysql_cond_t COND_wsrep_replaying;
++mysql_mutex_t LOCK_wsrep_slave_threads;
++mysql_mutex_t LOCK_wsrep_desync;
++int wsrep_replaying= 0;
++static void wsrep_close_threads(THD* thd);
++#endif /* WITH_WSREP */
+ int mysqld_server_started= 0;
+
+ File_parser_dummy_hook file_parser_dummy_hook;
+@@ -1209,7 +1236,9 @@ bool mysqld_embedded=0;
+ bool mysqld_embedded=1;
+ #endif
+
++#ifndef EMBEDDED_LIBRARY
+ static my_bool plugins_are_initialized= FALSE;
++#endif
+
+ #ifndef DBUG_OFF
+ static const char* default_dbug_option;
+@@ -1421,6 +1450,11 @@ static void close_connections(void)
+ /* We skip slave threads & scheduler on this first loop through. */
+ if (tmp->slave_thread)
+ continue;
++#ifdef WITH_WSREP
++ /* skip wsrep system threads as well */
++ if (WSREP(tmp) && (tmp->wsrep_exec_mode==REPL_RECV || tmp->wsrep_applier))
++ continue;
++#endif /* WITH_WSREP */
+ if (tmp->get_command() == COM_BINLOG_DUMP ||
+ tmp->get_command() == COM_BINLOG_DUMP_GTID)
+ {
+@@ -1523,6 +1557,33 @@ static void close_connections(void)
+ tmp->main_security_ctx.user : ""));
+ close_connection(tmp);
+ }
++#ifdef WITH_WSREP
++ /*
++ * TODO: this code block may turn out redundant. wsrep->disconnect()
++ * should terminate slave threads gracefully, and we don't need
++ * to signal them here.
++ * The code here makes sure mysqld will not hang during shutdown
++ * even if wsrep provider has problems in shutting down.
++ */
++ if (WSREP(tmp) && tmp->wsrep_exec_mode==REPL_RECV)
++ {
++ sql_print_information("closing wsrep system thread");
++ tmp->killed= THD::KILL_CONNECTION;
++ MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp));
++ if (tmp->mysys_var)
++ {
++ tmp->mysys_var->abort=1;
++ mysql_mutex_lock(&tmp->mysys_var->mutex);
++ if (tmp->mysys_var->current_cond)
++ {
++ mysql_mutex_lock(tmp->mysys_var->current_mutex);
++ mysql_cond_broadcast(tmp->mysys_var->current_cond);
++ mysql_mutex_unlock(tmp->mysys_var->current_mutex);
++ }
++ mysql_mutex_unlock(&tmp->mysys_var->mutex);
++ }
++ }
++#endif
+ }
+ DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
+ mysql_mutex_unlock(&LOCK_thread_count);
+@@ -1672,8 +1733,14 @@ static void __cdecl kill_server(int sig_ptr)
+ }
+ }
+ #endif
++#ifdef WITH_WSREP
++ if (WSREP_ON) wsrep_stop_replication(NULL);
++#endif
+
+ close_connections();
++#ifdef WITH_WSREP
++ if (WSREP_ON) wsrep_deinit();
++#endif
+ if (sig != MYSQL_KILL_SIGNAL &&
+ sig != 0)
+ unireg_abort(1); /* purecov: inspected */
+@@ -1768,6 +1835,23 @@ extern "C" void unireg_abort(int exit_code)
+ usage();
+ if (exit_code)
+ sql_print_error("Aborting\n");
++
++#ifdef WITH_WSREP
++ if (wsrep)
++ {
++ /* This is an abort situation, we cannot expect to gracefully close all
++ * wsrep threads here, we can only diconnect from service */
++ wsrep_close_client_connections(FALSE);
++ shutdown_in_progress= 1;
++ THD* thd(0);
++ wsrep->disconnect(wsrep);
++ WSREP_INFO("Service disconnected.");
++ wsrep_close_threads(thd); /* this won't close all threads */
++ sleep(1); /* so give some time to exit for those which can */
++ WSREP_INFO("Some threads may fail to exit.");
++ }
++#endif // WITH_WSREP
++
+ clean_up(!opt_help && (exit_code || !opt_bootstrap)); /* purecov: inspected */
+ DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
+ mysqld_exit(exit_code);
+@@ -2015,6 +2099,20 @@ static void clean_up_mutexes()
+ mysql_cond_destroy(&COND_thread_cache);
+ mysql_cond_destroy(&COND_flush_thread_cache);
+ mysql_cond_destroy(&COND_manager);
++#ifdef WITH_WSREP
++ (void) mysql_mutex_destroy(&LOCK_wsrep_ready);
++ (void) mysql_cond_destroy(&COND_wsrep_ready);
++ (void) mysql_mutex_destroy(&LOCK_wsrep_sst);
++ (void) mysql_cond_destroy(&COND_wsrep_sst);
++ (void) mysql_mutex_destroy(&LOCK_wsrep_sst_init);
++ (void) mysql_cond_destroy(&COND_wsrep_sst_init);
++ (void) mysql_mutex_destroy(&LOCK_wsrep_rollback);
++ (void) mysql_cond_destroy(&COND_wsrep_rollback);
++ (void) mysql_mutex_destroy(&LOCK_wsrep_replaying);
++ (void) mysql_cond_destroy(&COND_wsrep_replaying);
++ (void) mysql_mutex_destroy(&LOCK_wsrep_slave_threads);
++ (void) mysql_mutex_destroy(&LOCK_wsrep_desync);
++#endif
+ }
+ #endif /*EMBEDDED_LIBRARY*/
+
+@@ -2463,6 +2561,9 @@ static void network_init(void)
+ socket_errno);
+ unireg_abort(1);
+ }
++#if defined(WITH_WSREP) && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
++ (void) fcntl(mysql_socket_getfd(ip_sock), F_SETFD, FD_CLOEXEC);
++#endif /* WITH_WSREP */
+ }
+
+ #ifdef _WIN32
+@@ -2559,6 +2660,9 @@ static void network_init(void)
+ if (mysql_socket_listen(unix_sock, (int)back_log) < 0)
+ sql_print_warning("listen() on Unix socket failed with error %d",
+ socket_errno);
++#if defined(WITH_WSREP) && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
++ (void) fcntl(mysql_socket_getfd(unix_sock), F_SETFD, FD_CLOEXEC);
++#endif /* WITH_WSREP */
+ }
+ #endif
+ DBUG_PRINT("info",("server started"));
+@@ -2578,7 +2682,11 @@ static void network_init(void)
+ @note
+ For the connection that is doing shutdown, this is called twice
+ */
++#ifdef WITH_WSREP
++void close_connection(THD *thd, uint sql_errno, bool lock)
++#else
+ void close_connection(THD *thd, uint sql_errno)
++#endif
+ {
+ DBUG_ENTER("close_connection");
+
+@@ -2752,6 +2860,13 @@ bool one_thread_per_connection_end(THD *thd, bool block_pthread)
+ block_pthread= false;
+ }
+
++#ifdef WITH_WSREP
++ if (WSREP(thd) && thd->wsrep_applier)
++ {
++ WSREP_DEBUG("avoiding thread re-use for applier, thd: %lu", thd->thread_id);
++ block_pthread= false;
++ }
++#endif /* WITH_WSREP */
+ // Clean up errors now, before possibly waiting for a new connection.
+ #ifndef EMBEDDED_LIBRARY
+ ERR_remove_state(0);
+@@ -3792,6 +3907,13 @@ int init_common_variables()
+ strmake(default_logfile_name, glob_hostname,
+ sizeof(default_logfile_name)-5);
+
++#ifdef WITH_WSREP
++ if (0 == wsrep_node_name || 0 == wsrep_node_name[0])
++ {
++ my_free((void *)wsrep_node_name);
++ wsrep_node_name= my_strdup(glob_hostname, MYF(MY_WME));
++ }
++#endif /* WITH_WSREP */
+ strmake(pidfile_name, default_logfile_name, sizeof(pidfile_name)-5);
+ strmov(fn_ext(pidfile_name),".pid"); // Add proper extension
+
+@@ -3851,7 +3973,11 @@ int init_common_variables()
+ compile_time_assert(sizeof(com_status_vars)/sizeof(com_status_vars[0]) - 1 ==
+ SQLCOM_END + 8);
+ #endif
+-
++#ifdef WITH_WSREP
++ /* This is a protection against mutually incompatible option values. */
++ if (WSREP_ON && wsrep_check_opts (remaining_argc, remaining_argv))
++ return 1;
++#endif /* WITH_WSREP */
+ if (get_options(&remaining_argc, &remaining_argv))
+ return 1;
+ set_server_version();
+@@ -4260,6 +4386,27 @@ static int init_thread_environment()
+ sql_print_error("Can't create thread-keys");
+ return 1;
+ }
++#ifdef WITH_WSREP
++ mysql_mutex_init(key_LOCK_wsrep_ready,
++ &LOCK_wsrep_ready, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_ready, &COND_wsrep_ready, NULL);
++ mysql_mutex_init(key_LOCK_wsrep_sst,
++ &LOCK_wsrep_sst, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_sst, &COND_wsrep_sst, NULL);
++ mysql_mutex_init(key_LOCK_wsrep_sst_init,
++ &LOCK_wsrep_sst_init, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_sst_init, &COND_wsrep_sst_init, NULL);
++ mysql_mutex_init(key_LOCK_wsrep_rollback,
++ &LOCK_wsrep_rollback, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_rollback, &COND_wsrep_rollback, NULL);
++ mysql_mutex_init(key_LOCK_wsrep_replaying,
++ &LOCK_wsrep_replaying, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_replaying, &COND_wsrep_replaying, NULL);
++ mysql_mutex_init(key_LOCK_wsrep_slave_threads,
++ &LOCK_wsrep_slave_threads, MY_MUTEX_INIT_FAST);
++ mysql_mutex_init(key_LOCK_wsrep_desync,
++ &LOCK_wsrep_desync, MY_MUTEX_INIT_FAST);
++#endif
+ return 0;
+ }
+
+@@ -4602,7 +4749,6 @@ initialize_storage_engine(char *se_name, const char *se_kind,
+ return false;
+ }
+
+-
+ static int init_server_components()
+ {
+ DBUG_ENTER("init_server_components");
+@@ -4700,7 +4846,11 @@ static int init_server_components()
+ sql_print_warning("You need to use --log-bin to make "
+ "--log-slave-updates work.");
+ }
++#ifdef WITH_WSREP
++ if (!WSREP_ON && binlog_format_used && !opt_bin_log)
++#else
+ if (binlog_format_used && !opt_bin_log)
++#endif
+ sql_print_warning("You need to use --log-bin to make "
+ "--binlog-format work.");
+
+@@ -4783,10 +4933,67 @@ a file name for --log-bin-index option", opt_binlog_index_name);
+ my_free(opt_bin_logname);
+ opt_bin_logname=my_strdup(buf, MYF(0));
+ }
++#ifdef WITH_WSREP /* WSREP BEFORE SE */
++ /*
++ Wsrep initialization must happen at this point, because:
++ - opt_bin_logname must be known when starting replication
++ since SST may need it
++ - SST may modify binlog index file, so it must be opened
++ after SST has happened
++ */
++ }
++ if (!wsrep_recovery)
++ {
++ if (opt_bootstrap) // bootsrap option given - disable wsrep functionality
++ {
++ wsrep_provider_init(WSREP_NONE);
++ if (wsrep_init()) unireg_abort(1);
++ }
++ else // full wsrep initialization
++ {
++ // add basedir/bin to PATH to resolve wsrep script names
++ char* const tmp_path((char*)alloca(strlen(mysql_home) +
++ strlen("/bin") + 1));
++ if (tmp_path)
++ {
++ strcpy(tmp_path, mysql_home);
++ strcat(tmp_path, "/bin");
++ wsrep_prepend_PATH(tmp_path);
++ }
++ else
++ {
++ WSREP_ERROR("Could not append %s/bin to PATH", mysql_home);
++ }
++
++ if (wsrep_before_SE())
++ {
++ set_ports(); // this is also called in network_init() later but we need
++ // to know mysqld_port now - lp:1071882
++ wsrep_init_startup(true);
++ }
++ }
++ }
++ if (opt_bin_log)
++ {
++ /*
++ Variable ln is not defined at this scope. We use opt_bin_logname instead.
++ It should be the same as ln since
++ - mysql_bin_log.generate_name() returns first argument if new log name
++ is not generated
++ - if new log name is generated, return value is assigned to ln and copied
++ to opt_bin_logname above
++ */
++ if (mysql_bin_log.open_index_file(opt_binlog_index_name, opt_bin_logname,
++ TRUE))
++ {
++ unireg_abort(1);
++ }
++#else
+ if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln, TRUE))
+ {
+ unireg_abort(1);
+ }
++#endif /* WITH_WSREP */
+ }
+
+ if (opt_bin_log)
+@@ -4844,6 +5051,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
+ unireg_abort(1);
+ }
+
++#ifdef WITH_WSREP
+ if (plugin_init(&remaining_argc, remaining_argv,
+ (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) |
+ (opt_help ? PLUGIN_INIT_SKIP_INITIALIZATION : 0)))
+@@ -4852,7 +5060,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
+ unireg_abort(1);
+ }
+ plugins_are_initialized= TRUE; /* Don't separate from init function */
+-
++#endif /* WITH_WSREP */
+ /* we do want to exit if there are any other unknown options */
+ if (remaining_argc > 1)
+ {
+@@ -4947,16 +5155,34 @@ a file name for --log-bin-index option", opt_binlog_index_name);
+ &global_system_variables.temp_table_plugin))
+ unireg_abort(1);
+
++#ifdef WITH_WSREP
++ if (!opt_bin_log)
++ {
++ wsrep_emulate_bin_log= 1;
++ }
++#endif
+ if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log))
+ {
+ if (opt_bin_log)
+ tc_log= &mysql_bin_log;
+ else
++#ifdef WITH_WSREP
++ if (WSREP_ON)
++ tc_log= &tc_log_dummy;
++ else
++#endif /* WITH_WSREP */
+ tc_log= &tc_log_mmap;
+ }
+ else
+ tc_log= &tc_log_dummy;
+
++#ifdef WITH_WSREP
++ WSREP_DEBUG("Initial TC log open: %s",
++ (tc_log == &mysql_bin_log) ? "binlog" :
++ (tc_log == &tc_log_mmap) ? "mmap" :
++ (tc_log == &tc_log_dummy) ? "dummy" : "unknown"
++ );
++#endif
+ if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file))
+ {
+ sql_print_error("Can't init tc log");
+@@ -5047,8 +5273,6 @@ a file name for --log-bin-index option", opt_binlog_index_name);
+ init_update_queries();
+ DBUG_RETURN(0);
+ }
+-
+-
+ #ifndef EMBEDDED_LIBRARY
+
+ static void create_shutdown_thread()
+@@ -5070,6 +5294,421 @@ static void create_shutdown_thread()
+
+ #endif /* EMBEDDED_LIBRARY */
+
++#ifdef WITH_WSREP
++typedef void (*wsrep_thd_processor_fun)(THD *);
++
++pthread_handler_t start_wsrep_THD(void *arg)
++{
++ THD *thd;
++ wsrep_thd_processor_fun processor= (wsrep_thd_processor_fun)arg;
++
++ if (my_thread_init())
++ {
++ WSREP_ERROR("Could not initialize thread");
++ return(NULL);
++ }
++
++ if (!(thd= new THD(true, true)))
++ {
++ return(NULL);
++ }
++ mysql_mutex_lock(&LOCK_thread_count);
++ thd->thread_id=thread_id++;
++
++ thd->real_id=pthread_self(); // Keep purify happy
++ add_global_thread(thd);
++ thread_created++;
++
++ my_net_init(&thd->net,(st_vio*) 0);
++
++ DBUG_PRINT("wsrep",(("creating thread %lld"), (long long)thd->thread_id));
++ thd->prior_thr_create_utime= thd->start_utime= my_micro_time();
++ (void) mysql_mutex_unlock(&LOCK_thread_count);
++
++ /* from bootstrap()... */
++ thd->bootstrap=1;
++ thd->max_client_packet_length= thd->net.max_packet;
++ thd->security_ctx->master_access= ~(ulong)0;
++
++ /* from handle_one_connection... */
++ pthread_detach_this_thread();
++
++ mysql_thread_set_psi_id(thd->thread_id);
++ thd->thr_create_utime= my_micro_time();
++ if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
++ {
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++ statistic_increment(aborted_connects,&LOCK_status);
++ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
++
++ return(NULL);
++ }
++
++ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
++
++ thd->thread_stack= (char*) &thd;
++ if (thd->store_globals())
++ {
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++ statistic_increment(aborted_connects,&LOCK_status);
++ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
++ delete thd;
++
++ return(NULL);
++ }
++
++ /* from handle_bootstrap() */
++
++ thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
++ thd->security_ctx->skip_grants();
++
++ /* handle_one_connection() again... */
++ //thd->version= refresh_version;
++ thd->proc_info= 0;
++ thd->set_command(COM_SLEEP);
++ thd->set_time();
++ thd->init_for_queries();
++
++ mysql_mutex_lock(&LOCK_connection_count);
++ ++connection_count;
++ mysql_mutex_unlock(&LOCK_connection_count);
++
++ processor(thd);
++
++ close_connection(thd, 0, 1);
++
++ // Note: We can't call THD destructor without crashing
++ // if plugins have not been initialized. However, in most of the
++ // cases this means that pre SE initialization SST failed and
++ // we are going to exit anyway.
++ if (plugins_are_initialized)
++ {
++ net_end(&thd->net);
++ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 1));
++ }
++ else
++ {
++ // TODO: lightweight cleanup to get rid of:
++ // 'Error in my_thread_global_end(): 2 threads didn't exit'
++ // at server shutdown
++ }
++ return(NULL);
++}
++
++/**/
++static bool abort_replicated(THD *thd)
++{
++ bool ret_code= false;
++ if (thd->wsrep_query_state== QUERY_COMMITTING)
++ {
++ if (wsrep_debug) WSREP_INFO("aborting replicated trx: %lu", thd->real_id);
++
++ (void)wsrep_abort_thd(thd, thd, TRUE);
++ ret_code= true;
++ }
++ return ret_code;
++}
++/**/
++static inline bool is_client_connection(THD *thd)
++{
++#if REMOVE
++// REMOVE THIS LATER (lp:777201). Below we had to add an explicit check for
++// wsrep_applier since wsrep_exec_mode didn't seem to always work
++if (thd->wsrep_applier && thd->wsrep_exec_mode != REPL_RECV)
++WSREP_WARN("applier has wsrep_exec_mode = %d", thd->wsrep_exec_mode);
++
++ if ( thd->slave_thread || /* declared as mysql slave */
++ thd->system_thread || /* declared as system thread */
++ !thd->vio_ok() || /* server internal thread */
++ thd->wsrep_exec_mode==REPL_RECV || /* applier or replaying thread */
++ thd->wsrep_applier || /* wsrep slave applier */
++ !thd->variables.wsrep_on) /* client, but fenced outside wsrep */
++ return false;
++
++ return true;
++#else
++ return (thd->wsrep_client_thread && thd->variables.wsrep_on);
++#endif /* REMOVE */
++}
++
++static inline bool is_replaying_connection(THD *thd)
++{
++ bool ret;
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ ret= (thd->wsrep_conflict_state == REPLAYING) ? true : false;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ return ret;
++}
++
++static inline bool is_committing_connection(THD *thd)
++{
++ bool ret;
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ ret= (thd->wsrep_query_state == QUERY_COMMITTING) ? true : false;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ return ret;
++}
++
++static bool have_client_connections()
++{
++ Thread_iterator it= global_thread_list->begin();
++ for (; it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++ DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
++ tmp->thread_id));
++ if (is_client_connection(tmp) && tmp->killed == THD::KILL_CONNECTION)
++ {
++ (void)abort_replicated(tmp);
++ return true;
++ }
++ }
++ return false;
++}
++
++/*
++ returns the number of wsrep appliers running.
++ However, the caller (thd parameter) is not taken in account
++ */
++static int have_wsrep_appliers(THD *thd)
++{
++ int ret= 0;
++
++ Thread_iterator it= global_thread_list->begin();
++ for (; it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++ ret+= (tmp != thd && tmp->wsrep_applier);
++ }
++ return ret;
++}
++
++static void wsrep_close_thread(THD *thd)
++{
++ thd->killed= THD::KILL_CONNECTION;
++ MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd));
++ if (thd->mysys_var)
++ {
++ thd->mysys_var->abort=1;
++ mysql_mutex_lock(&thd->mysys_var->mutex);
++ if (thd->mysys_var->current_cond)
++ {
++ mysql_mutex_lock(thd->mysys_var->current_mutex);
++ mysql_cond_broadcast(thd->mysys_var->current_cond);
++ mysql_mutex_unlock(thd->mysys_var->current_mutex);
++ }
++ mysql_mutex_unlock(&thd->mysys_var->mutex);
++ }
++}
++
++static my_bool have_committing_connections()
++{
++ Thread_iterator it= global_thread_list->begin();
++ for (; it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++
++ if (!is_client_connection(tmp))
++ continue;
++
++ if (is_committing_connection(tmp))
++ {
++ return TRUE;
++ }
++ }
++ return FALSE;
++}
++
++int wsrep_wait_committing_connections_close(int wait_time)
++{
++ int sleep_time= 100;
++
++ while (have_committing_connections() && wait_time > 0)
++ {
++ WSREP_DEBUG("wait for committing transaction to close: %d", wait_time);
++ my_sleep(sleep_time);
++ wait_time -= sleep_time;
++ }
++ if (have_committing_connections())
++ {
++ return 1;
++ }
++ return 0;
++}
++
++void wsrep_close_client_connections(my_bool wait_to_end)
++{
++ /*
++ First signal all threads that it's time to die
++ */
++
++ /* Kill blocked pthreads */
++ kill_blocked_pthreads_flag++;
++ kill_blocked_pthreads();
++
++ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
++
++ Thread_iterator it= global_thread_list->begin();
++ for (; it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++ DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
++ tmp->thread_id));
++ /* We skip slave threads & scheduler on this first loop through. */
++ if (!is_client_connection(tmp))
++ continue;
++
++ if (is_replaying_connection(tmp))
++ {
++ tmp->killed= THD::KILL_CONNECTION;
++ continue;
++ }
++
++ /* replicated transactions must be skipped */
++ if (abort_replicated(tmp))
++ continue;
++
++ WSREP_DEBUG("closing connection %ld", tmp->thread_id);
++ wsrep_close_thread(tmp);
++ }
++ mysql_mutex_unlock(&LOCK_thread_count);
++
++ if (get_thread_count() > 0)
++ sleep(2); // Give threads time to die
++
++ mysql_mutex_lock(&LOCK_thread_count);
++ /*
++ Force remaining threads to die by closing the connection to the client
++ */
++
++ for (it= global_thread_list->begin(); it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++#ifndef __bsdi__ // Bug in BSDI kernel
++ if (is_client_connection(tmp) &&
++ !abort_replicated(tmp) &&
++ !is_replaying_connection(tmp))
++ {
++ WSREP_INFO("killing local connection: %ld",tmp->thread_id);
++ close_connection(tmp,0,0);
++ }
++#endif
++ }
++
++ DBUG_PRINT("quit",("Waiting for threads to die (count=%u)", get_thread_count()));
++ if (wsrep_debug)
++ WSREP_INFO("waiting for client connections to close: %u",
++ get_thread_count());
++
++ while (wait_to_end && have_client_connections())
++ {
++ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
++ DBUG_PRINT("quit",("One thread died (count=%u)", get_thread_count()));
++ }
++
++ mysql_mutex_unlock(&LOCK_thread_count);
++
++ /* All client connection threads have now been aborted */
++}
++
++void wsrep_close_applier(THD *thd)
++{
++ WSREP_DEBUG("closing applier %ld", thd->thread_id);
++ wsrep_close_thread(thd);
++}
++
++static void wsrep_close_threads(THD *thd)
++{
++ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
++
++ Thread_iterator it= global_thread_list->begin();
++ for (; it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++ DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
++ tmp->thread_id));
++ /* We skip slave threads & scheduler on this first loop through. */
++ if (tmp->wsrep_applier && tmp != thd)
++ {
++ WSREP_DEBUG("closing wsrep thread %ld", tmp->thread_id);
++ wsrep_close_thread (tmp);
++ }
++ }
++
++ mysql_mutex_unlock(&LOCK_thread_count);
++}
++
++void wsrep_close_applier_threads(int count)
++{
++ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
++
++ Thread_iterator it= global_thread_list->begin();
++ for (; it != global_thread_list->end(); ++it)
++ {
++ THD *tmp= *it;
++ DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
++ tmp->thread_id));
++ /* We skip slave threads & scheduler on this first loop through. */
++ if (tmp->wsrep_applier)
++ {
++ WSREP_DEBUG("closing wsrep applier thread %ld", tmp->thread_id);
++ tmp->wsrep_applier_closing= TRUE;
++ count--;
++ }
++ }
++
++ mysql_mutex_unlock(&LOCK_thread_count);
++}
++
++void wsrep_wait_appliers_close(THD *thd)
++{
++ /* Wait for wsrep appliers to gracefully exit */
++ mysql_mutex_lock(&LOCK_thread_count);
++ while (have_wsrep_appliers(thd) > 1)
++ // 1 is for rollbacker thread which needs to be killed explicitly.
++ // This gotta be fixed in a more elegant manner if we gonna have arbitrary
++ // number of non-applier wsrep threads.
++ {
++ mysql_cond_wait(&COND_thread_count,&LOCK_thread_count);
++ DBUG_PRINT("quit",("One applier died (count=%u)", get_thread_count()));
++ }
++ mysql_mutex_unlock(&LOCK_thread_count);
++ /* Now kill remaining wsrep threads: rollbacker */
++ wsrep_close_threads (thd);
++ /* and wait for them to die */
++ mysql_mutex_lock(&LOCK_thread_count);
++ while (have_wsrep_appliers(thd) > 0)
++ {
++ mysql_cond_wait(&COND_thread_count,&LOCK_thread_count);
++ DBUG_PRINT("quit",("One thread died (count=%u)", get_thread_count()));
++ }
++ mysql_mutex_unlock(&LOCK_thread_count);
++
++ /* All wsrep applier threads have now been aborted. However, if this thread
++ is also applier, we are still running...
++ */
++}
++
++void wsrep_kill_mysql(THD *thd)
++{
++ if (mysqld_server_started)
++ {
++ if (!shutdown_in_progress)
++ {
++ WSREP_INFO("starting shutdown");
++ kill_mysql();
++ }
++ }
++ else
++ {
++ unireg_abort(1);
++ }
++}
++#endif /* WITH_WSREP */
+
+ #if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+ static void handle_connections_methods()
+@@ -5204,6 +5843,9 @@ int mysqld_main(int argc, char **argv)
+ return 1;
+ }
+ #endif
++#ifdef WITH_WSREP
++ wsrep_filter_new_cluster (&argc, argv);
++#endif /* WITH_WSREP */
+
+ orig_argc= argc;
+ orig_argv= argv;
+@@ -5537,6 +6179,14 @@ int mysqld_main(int argc, char **argv)
+ my_str_free= &my_str_free_mysqld;
+ my_str_realloc= &my_str_realloc_mysqld;
+
++#ifdef WITH_WSREP /* WSREP AFTER SE */
++ if (wsrep_recovery)
++ {
++ select_thread_in_use= 0;
++ wsrep_recover();
++ unireg_abort(0);
++ }
++#endif /* WITH_WSREP */
+ /*
+ init signals & alarm
+ After this we can't quit by a simple unireg_abort
+@@ -5615,6 +6265,31 @@ int mysqld_main(int argc, char **argv)
+ if (Events::init(opt_noacl || opt_bootstrap))
+ unireg_abort(1);
+
++#ifdef WITH_WSREP /* WSREP AFTER SE */
++ if (opt_bootstrap)
++ {
++ /*! bootstrap wsrep init was taken care of above */
++ }
++ else
++ {
++ wsrep_SE_initialized();
++
++ if (wsrep_before_SE())
++ {
++ /*! in case of no SST wsrep waits in view handler callback */
++ wsrep_SE_init_grab();
++ wsrep_SE_init_done();
++ /*! in case of SST wsrep waits for wsrep->sst_received */
++ wsrep_sst_continue();
++ }
++ else
++ {
++ wsrep_init_startup (false);
++ }
++
++ wsrep_create_appliers(wsrep_slave_threads - 1);
++ }
++#endif /* WITH_WSREP */
+ if (opt_bootstrap)
+ {
+ select_thread_in_use= 0; // Allow 'kill' to work
+@@ -5676,6 +6351,9 @@ int mysqld_main(int argc, char **argv)
+ #ifdef EXTRA_DEBUG2
+ sql_print_error("Before Lock_thread_count");
+ #endif
++#ifdef WITH_WSREP
++ WSREP_DEBUG("Before Lock_thread_count");
++#endif
+ mysql_mutex_lock(&LOCK_thread_count);
+ DBUG_PRINT("quit", ("Got thread_count mutex"));
+ select_thread_in_use=0; // For close_connections
+@@ -5941,6 +6619,9 @@ static void bootstrap(MYSQL_FILE *file)
+ DBUG_ENTER("bootstrap");
+
+ THD *thd= new THD;
++#ifdef WITH_WSREP
++ thd->variables.wsrep_on= 0;
++#endif
+ thd->bootstrap=1;
+ my_net_init(&thd->net,(st_vio*) 0);
+ thd->max_client_packet_length= thd->net.max_packet;
+@@ -6080,7 +6761,11 @@ void create_thread_to_handle_connection(THD *thd)
+ my_snprintf(error_message_buff, sizeof(error_message_buff),
+ ER_THD(thd, ER_CANT_CREATE_THREAD), error);
+ net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
++#ifdef WITH_WSREP
++ close_connection(thd,0,0);
++#else
+ close_connection(thd);
++#endif
+ delete thd;
+ return;
+ /* purecov: end */
+@@ -6132,7 +6817,11 @@ static void create_new_thread(THD *thd)
+ with no sqlstate.
+ A client expecting a SQLSTATE will not find any, and assume 'HY000'.
+ */
++#ifdef WITH_WSREP
++ close_connection(thd, ER_CON_COUNT_ERROR, 1);
++#else
+ close_connection(thd, ER_CON_COUNT_ERROR);
++#endif
+ delete thd;
+ statistic_increment(connection_errors_max_connection, &LOCK_status);
+ DBUG_VOID_RETURN;
+@@ -6352,6 +7041,9 @@ void handle_connections_sockets()
+ sleep(1); // Give other threads some time
+ continue;
+ }
++#if defined(WITH_WSREP) && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
++ (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC);
++#endif /* WITH_WSREP */
+
+ #ifdef HAVE_LIBWRAP
+ {
+@@ -6531,7 +7223,11 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
+ if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) ||
+ my_net_init(&thd->net, thd->net.vio))
+ {
++#ifdef WITH_WSREP
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++#else
+ close_connection(thd, ER_OUT_OF_RESOURCES);
++#endif
+ delete thd;
+ continue;
+ }
+@@ -6726,7 +7422,11 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
+ event_conn_closed)) ||
+ my_net_init(&thd->net, thd->net.vio))
+ {
++#ifdef WITH_WSREP
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++#else
+ close_connection(thd, ER_OUT_OF_RESOURCES);
++#endif
+ errmsg= 0;
+ goto errorconn;
+ }
+@@ -8029,6 +8729,20 @@ SHOW_VAR status_vars[]= {
+ #ifdef ENABLED_PROFILING
+ {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_FUNC},
+ #endif
++#ifdef WITH_WSREP
++ {"wsrep_connected", (char*) &wsrep_connected, SHOW_BOOL},
++ {"wsrep_ready", (char*) &wsrep_ready, SHOW_BOOL},
++ {"wsrep_cluster_state_uuid", (char*) &wsrep_cluster_state_uuid,SHOW_CHAR_PTR},
++ {"wsrep_cluster_conf_id", (char*) &wsrep_cluster_conf_id, SHOW_LONGLONG},
++ {"wsrep_cluster_status", (char*) &wsrep_cluster_status, SHOW_CHAR_PTR},
++ {"wsrep_cluster_size", (char*) &wsrep_cluster_size, SHOW_LONG_NOFLUSH},
++ {"wsrep_local_index", (char*) &wsrep_local_index, SHOW_LONG_NOFLUSH},
++ {"wsrep_local_bf_aborts", (char*) &wsrep_show_bf_aborts, SHOW_FUNC},
++ {"wsrep_provider_name", (char*) &wsrep_provider_name, SHOW_CHAR_PTR},
++ {"wsrep_provider_version", (char*) &wsrep_provider_version, SHOW_CHAR_PTR},
++ {"wsrep_provider_vendor", (char*) &wsrep_provider_vendor, SHOW_CHAR_PTR},
++ {"wsrep", (char*) &wsrep_show_status, SHOW_FUNC},
++#endif
+ {NullS, NullS, SHOW_LONG}
+ };
+
+@@ -8356,6 +9070,10 @@ static int mysql_init_variables(void)
+ tmpenv = DEFAULT_MYSQL_HOME;
+ (void) strmake(mysql_home, tmpenv, sizeof(mysql_home)-1);
+ #endif
++#ifdef WITH_WSREP
++ if (WSREP_ON && wsrep_init_vars())
++ return 1;
++#endif
+ return 0;
+ }
+
+@@ -8567,6 +9285,14 @@ mysqld_get_one_option(int optid,
+ case OPT_LOWER_CASE_TABLE_NAMES:
+ lower_case_table_names_used= 1;
+ break;
++#ifdef WITH_WSREP
++ case OPT_WSREP_START_POSITION:
++ wsrep_start_position_init (argument);
++ break;
++ case OPT_WSREP_SST_AUTH:
++ wsrep_sst_auth_init (argument);
++ break;
++#endif
+ #if defined(ENABLED_DEBUG_SYNC)
+ case OPT_DEBUG_SYNC_TIMEOUT:
+ /*
+@@ -8926,6 +9652,31 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
+ else
+ global_system_variables.option_bits&= ~OPTION_BIG_SELECTS;
+
++#ifdef WITH_WSREP
++ if (global_system_variables.wsrep_causal_reads) {
++ WSREP_WARN("option --wsrep-casual-reads is deprecated");
++ if (!(global_system_variables.wsrep_sync_wait &
++ WSREP_SYNC_WAIT_BEFORE_READ)) {
++ WSREP_WARN("--wsrep-casual-reads=ON takes precedence over --wsrep-sync-wait=%u. "
++ "WSREP_SYNC_WAIT_BEFORE_READ is on",
++ global_system_variables.wsrep_sync_wait);
++ global_system_variables.wsrep_sync_wait |= WSREP_SYNC_WAIT_BEFORE_READ;
++ } else {
++ // they are turned on both.
++ }
++ } else {
++ if (global_system_variables.wsrep_sync_wait &
++ WSREP_SYNC_WAIT_BEFORE_READ) {
++ WSREP_WARN("--wsrep-sync-wait=%u takes precedence over --wsrep-causal-reads=OFF. "
++ "WSREP_SYNC_WAIT_BEFORE_READ is on",
++ global_system_variables.wsrep_sync_wait);
++ global_system_variables.wsrep_causal_reads = 1;
++ } else {
++ // they are turned off both.
++ }
++ }
++#endif // WITH_WSREP
++
+ // Synchronize @@global.autocommit on --autocommit
+ const ulonglong turn_bit_on= opt_autocommit ?
+ OPTION_AUTOCOMMIT : OPTION_NOT_AUTOCOMMIT;
+@@ -9320,6 +10071,9 @@ void refresh_status(THD *thd)
+
+ /* Reset some global variables */
+ reset_status_vars();
++#ifdef WITH_WSREP
++ wsrep->stats_reset(wsrep);
++#endif /* WITH_WSREP */
+
+ /* Reset the counters of all key caches (default and named). */
+ process_key_caches(reset_key_cache_counters);
+@@ -9383,6 +10137,12 @@ PSI_mutex_key
+ key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
+ key_LOCK_error_messages, key_LOG_INFO_lock, key_LOCK_thread_count,
+ key_LOCK_log_throttle_qni;
++#ifdef WITH_WSREP
++PSI_mutex_key key_LOCK_wsrep_rollback, key_LOCK_wsrep_thd,
++ key_LOCK_wsrep_replaying, key_LOCK_wsrep_ready, key_LOCK_wsrep_sst,
++ key_LOCK_wsrep_sst_thread, key_LOCK_wsrep_sst_init,
++ key_LOCK_wsrep_slave_threads, key_LOCK_wsrep_desync;
++#endif
+ PSI_mutex_key key_LOCK_thd_remove;
+ PSI_mutex_key key_RELAYLOG_LOCK_commit;
+ PSI_mutex_key key_RELAYLOG_LOCK_commit_queue;
+@@ -9468,6 +10228,18 @@ static PSI_mutex_info all_server_mutexes[]=
+ { &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL},
+ { &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
+ { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
++#ifdef WITH_WSREP
++ { &key_LOCK_wsrep_ready, "LOCK_wsrep_ready", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_sst, "LOCK_wsrep_sst", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_sst_thread, "wsrep_sst_thread", 0},
++ { &key_LOCK_wsrep_sst_init, "LOCK_wsrep_sst_init", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_sst, "LOCK_wsrep_sst", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_rollback, "LOCK_wsrep_rollback", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_thd, "THD::LOCK_wsrep_thd", 0},
++ { &key_LOCK_wsrep_replaying, "LOCK_wsrep_replaying", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_slave_threads, "LOCK_wsrep_slave_threads", PSI_FLAG_GLOBAL},
++ { &key_LOCK_wsrep_desync, "LOCK_wsrep_desync", PSI_FLAG_GLOBAL},
++#endif
+ { &key_LOCK_thd_remove, "LOCK_thd_remove", PSI_FLAG_GLOBAL},
+ { &key_LOCK_log_throttle_qni, "LOCK_log_throttle_qni", PSI_FLAG_GLOBAL},
+ { &key_gtid_ensure_index_mutex, "Gtid_state", PSI_FLAG_GLOBAL},
+@@ -9523,6 +10295,12 @@ PSI_cond_key key_BINLOG_update_cond,
+ key_cond_slave_parallel_worker,
+ key_TABLE_SHARE_cond, key_user_level_lock_cond,
+ key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache;
++#ifdef WITH_WSREP
++PSI_cond_key key_COND_wsrep_rollback, key_COND_wsrep_thd,
++ key_COND_wsrep_replaying, key_COND_wsrep_ready, key_COND_wsrep_sst,
++ key_COND_wsrep_sst_init, key_COND_wsrep_sst_thread;
++
++#endif /* WITH_WSREP */
+ PSI_cond_key key_RELAYLOG_update_cond;
+ PSI_cond_key key_BINLOG_COND_done;
+ PSI_cond_key key_RELAYLOG_COND_done;
+@@ -9567,6 +10345,15 @@ static PSI_cond_info all_server_conds[]=
+ { &key_user_level_lock_cond, "User_level_lock::cond", 0},
+ { &key_COND_thread_count, "COND_thread_count", PSI_FLAG_GLOBAL},
+ { &key_COND_thread_cache, "COND_thread_cache", PSI_FLAG_GLOBAL},
++#ifdef WITH_WSREP
++ { &key_COND_wsrep_ready, "COND_wsrep_ready", PSI_FLAG_GLOBAL},
++ { &key_COND_wsrep_sst, "COND_wsrep_sst", PSI_FLAG_GLOBAL},
++ { &key_COND_wsrep_sst_init, "COND_wsrep_sst_init", PSI_FLAG_GLOBAL},
++ { &key_COND_wsrep_sst_thread, "wsrep_sst_thread", 0},
++ { &key_COND_wsrep_rollback, "COND_wsrep_rollback", PSI_FLAG_GLOBAL},
++ { &key_COND_wsrep_thd, "THD::COND_wsrep_thd", 0},
++ { &key_COND_wsrep_replaying, "COND_wsrep_replaying", PSI_FLAG_GLOBAL},
++#endif
+ { &key_COND_flush_thread_cache, "COND_flush_thread_cache", PSI_FLAG_GLOBAL},
+ { &key_gtid_ensure_index_cond, "Gtid_state", PSI_FLAG_GLOBAL}
+ };
+diff --git a/sql/mysqld.h b/sql/mysqld.h
+index 2844275..17b601a 100644
+--- a/sql/mysqld.h
++++ b/sql/mysqld.h
+@@ -65,7 +65,11 @@ typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
+ some places */
+ /* Function prototypes */
+ void kill_mysql(void);
++#ifdef WITH_WSREP
++void close_connection(THD *thd, uint sql_errno= 0, bool lock=1);
++#else
+ void close_connection(THD *thd, uint sql_errno= 0);
++#endif
+ void handle_connection_in_main_thread(THD *thd);
+ void create_thread_to_handle_connection(THD *thd);
+ void destroy_thd(THD *thd);
+@@ -309,6 +313,10 @@ extern pthread_key(MEM_ROOT**,THR_MALLOC);
+ extern PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active,
+ key_LOCK_pool;
+ #endif /* HAVE_MMAP */
++#ifdef WITH_WSREP
++extern PSI_mutex_key key_LOCK_wsrep_thd;
++extern PSI_cond_key key_COND_wsrep_thd;
++#endif /* HAVE_WSREP */
+
+ #ifdef HAVE_OPENSSL
+ extern PSI_mutex_key key_LOCK_des_key_file;
+@@ -658,6 +666,14 @@ enum options_mysqld
+ OPT_WANT_CORE,
+ OPT_ENGINE_CONDITION_PUSHDOWN,
+ OPT_LOG_ERROR,
++#ifdef WITH_WSREP
++ OPT_WSREP_PROVIDER,
++ OPT_WSREP_PROVIDER_OPTIONS,
++ OPT_WSREP_CLUSTER_ADDRESS,
++ OPT_WSREP_START_POSITION,
++ OPT_WSREP_SST_AUTH,
++ OPT_WSREP_RECOVER,
++#endif /* WITH_WSREP */
+ OPT_MAX_LONG_DATA_SIZE,
+ OPT_PLUGIN_LOAD,
+ OPT_PLUGIN_LOAD_ADD,
+@@ -766,4 +782,9 @@ inline THD *_current_thd(void)
+
+ extern const char *MY_BIND_ALL_ADDRESSES;
+
++#ifdef WITH_WSREP
++#include "my_pthread.h"
++pthread_handler_t start_wsrep_THD(void*);
++#endif /* WITH_WSREP */
++
+ #endif /* MYSQLD_INCLUDED */
+diff --git a/sql/protocol.cc b/sql/protocol.cc
+index c03d78b..8e6ba54 100644
+--- a/sql/protocol.cc
++++ b/sql/protocol.cc
+@@ -485,6 +485,14 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
+
+ void Protocol::end_statement()
+ {
++#ifdef WITH_WSREP
++ /*sanity check, can be removed before 1.0 release */
++ if (WSREP(thd) && thd->wsrep_conflict_state== REPLAYING)
++ {
++ WSREP_ERROR("attempting net_end_statement while replaying");
++ return;
++ }
++#endif
+ DBUG_ENTER("Protocol::end_statement");
+ DBUG_ASSERT(! thd->get_stmt_da()->is_sent());
+ bool error= FALSE;
+diff --git a/sql/rpl_gtid_cache.cc b/sql/rpl_gtid_cache.cc
+index 271a99d..7e8a368 100644
+--- a/sql/rpl_gtid_cache.cc
++++ b/sql/rpl_gtid_cache.cc
+@@ -147,7 +147,22 @@ enum_return_status Group_cache::generate_automatic_gno(THD *thd)
+ else
+ {
+ automatic_type= GTID_GROUP;
++#ifdef WITH_WSREP
++ /*
++ Replace sidno with wsrep_sidno
++ if transaction went through wsrep commit
++ */
++ if (WSREP(thd) && thd->wsrep_trx_meta.gtid.seqno != -1)
++ {
++ automatic_gtid.sidno= wsrep_sidno;
++ }
++ else
++ {
++#endif /* WITH_WSREP */
+ automatic_gtid.sidno= gtid_state->get_server_sidno();
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ gtid_state->lock_sidno(automatic_gtid.sidno);
+ automatic_gtid.gno=
+ gtid_state->get_automatic_gno(automatic_gtid.sidno);
+diff --git a/sql/rpl_slave.cc b/sql/rpl_slave.cc
+index 736d790..af86bec 100644
+--- a/sql/rpl_slave.cc
++++ b/sql/rpl_slave.cc
+@@ -55,6 +55,9 @@
+ #include "rpl_rli_pdb.h"
+ #include "global_threads.h"
+
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif
+ #ifdef HAVE_REPLICATION
+
+ #include "rpl_tblmap.h"
+@@ -3704,6 +3707,94 @@ apply_event_and_update_pos(Log_event** ptr_ev, THD* thd, Relay_log_info* rli)
+ rli->mts_recovery_index));
+ }
+ #endif
++#ifdef WITH_WSREP
++ if (wsrep_preordered_opt && WSREP_ON &&
++ (ev->get_type_code() == QUERY_EVENT ||
++ ev->get_type_code() == XID_EVENT ||
++ ev->get_type_code() == TABLE_MAP_EVENT ||
++ ev->get_type_code() == WRITE_ROWS_EVENT ||
++ ev->get_type_code() == UPDATE_ROWS_EVENT ||
++ ev->get_type_code() == DELETE_ROWS_EVENT ||
++ ev->get_type_code() == GTID_LOG_EVENT))
++ {
++ if (ev->get_type_code() == GTID_LOG_EVENT)
++ {
++ thd->wsrep_po_sid= *((Gtid_log_event*)ev)->get_sid();
++ }
++ wsrep_status_t err;
++ if (thd->wsrep_po_cnt == 0)
++ {
++ /* First event in write set, write format description event
++ as a write set header so that the receiver will know how
++ to interpret following events. */
++ Log_event* fde= rli->get_rli_description_event();
++ ulong len= uint4korr(fde->temp_buf + EVENT_LEN_OFFSET);
++ wsrep_buf_t data= {fde->temp_buf, len};
++ if ((err= wsrep->preordered_collect(
++ wsrep, &thd->wsrep_po_handle, &data, 1, true)) != WSREP_OK)
++ {
++ WSREP_ERROR("wsrep preordered collect failed: %d", err);
++ if (err == WSREP_TRX_FAIL &&
++ wsrep->preordered_commit(wsrep, &thd->wsrep_po_handle, NULL,
++ 0, 0, false))
++ {
++ WSREP_WARN("failed to cancel preordered write set");
++ }
++ DBUG_RETURN(SLAVE_APPLY_EVENT_AND_UPDATE_POS_APPLY_ERROR);
++ }
++ }
++ ++thd->wsrep_po_cnt;
++ ulong len= uint4korr(ev->temp_buf + EVENT_LEN_OFFSET);
++ wsrep_buf_t data= {ev->temp_buf, len};
++ if ((err= wsrep->preordered_collect(wsrep, &thd->wsrep_po_handle,
++ &data, 1, 1)) != WSREP_OK)
++ {
++ WSREP_ERROR("wsrep preordered collect failed: %d", err);
++ DBUG_RETURN(SLAVE_APPLY_EVENT_AND_UPDATE_POS_APPLY_ERROR);
++ }
++
++ if (ev->get_type_code() == QUERY_EVENT &&
++ ((Query_log_event*)ev)->starts_group())
++ {
++ thd->wsrep_po_in_trans= TRUE;
++ }
++ else if (ev->get_type_code() == XID_EVENT ||
++ (ev->get_type_code() == QUERY_EVENT &&
++ (thd->wsrep_po_in_trans == FALSE ||
++ ((Query_log_event*)ev)->ends_group())))
++ {
++ int flags= WSREP_FLAG_COMMIT | (thd->wsrep_po_in_trans == FALSE ?
++ WSREP_FLAG_ISOLATION : 0);
++ thd->wsrep_po_in_trans= FALSE;
++ thd->wsrep_po_cnt= 0;
++ wsrep_uuid_t source;
++ memcpy(source.data, thd->wsrep_po_sid.bytes, sizeof(source.data));
++ if ((err= wsrep->preordered_commit(wsrep, &thd->wsrep_po_handle,
++ &source, flags, 1, true)) != WSREP_OK)
++ {
++ WSREP_ERROR("failed to commit preordered event: %d", err);
++ DBUG_RETURN(SLAVE_APPLY_EVENT_AND_UPDATE_POS_APPLY_ERROR);
++ }
++ }
++ reason= Log_event::EVENT_SKIP_IGNORE;
++ skip_event= TRUE;
++ }
++ else if (WSREP_ON && (ev->get_type_code() == XID_EVENT ||
++ (ev->get_type_code() == QUERY_EVENT && thd->wsrep_mysql_replicated > 0 &&
++ (!strncasecmp(((Query_log_event*)ev)->query , "BEGIN", 5) ||
++ !strncasecmp(((Query_log_event*)ev)->query , "COMMIT", 6) ))))
++ {
++ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
++ {
++ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
++ reason = Log_event::EVENT_SKIP_IGNORE;
++ }
++ else
++ {
++ thd->wsrep_mysql_replicated = 0;
++ }
++ }
++#endif /* WITH_WSREP */
+ if (reason == Log_event::EVENT_SKIP_COUNT)
+ {
+ sql_slave_skip_counter= --rli->slave_skip_counter;
+@@ -5825,6 +5916,9 @@ pthread_handler_t handle_slave_sql(void *arg)
+ my_off_t saved_log_pos= 0;
+ my_off_t saved_master_log_pos= 0;
+ my_off_t saved_skip= 0;
++#ifdef WITH_WSREP
++ my_bool wsrep_node_dropped= FALSE;
++#endif /* WITH_WSREP */
+
+ Relay_log_info* rli = ((Master_info*)arg)->rli;
+ const char *errmsg;
+@@ -5833,6 +5927,9 @@ pthread_handler_t handle_slave_sql(void *arg)
+ // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
+ my_thread_init();
+ DBUG_ENTER("handle_slave_sql");
++#ifdef WITH_WSREP
++ wsrep_restart_point:
++#endif /* WITH_WSREP */
+
+ DBUG_ASSERT(rli->inited);
+ mysql_mutex_lock(&rli->run_lock);
+@@ -5976,6 +6073,18 @@ pthread_handler_t handle_slave_sql(void *arg)
+ }
+ #endif
+
++#ifdef WITH_WSREP
++ thd->wsrep_exec_mode= LOCAL_STATE;
++ /* synchronize with wsrep replication */
++ if (WSREP_ON)
++ {
++ thd->wsrep_po_handle= WSREP_PO_INITIALIZER;
++ thd->wsrep_po_cnt= 0;
++ thd->wsrep_po_in_trans= FALSE;
++ memset(&thd->wsrep_po_sid, 0, sizeof(thd->wsrep_po_sid));
++ wsrep_ready_wait();
++ }
++#endif
+ DBUG_PRINT("master_info",("log_file_name: %s position: %s",
+ rli->get_group_master_log_name(),
+ llstr(rli->get_group_master_log_pos(),llbuff)));
+@@ -6118,6 +6227,12 @@ Error running query, slave SQL thread aborted. Fix the problem, and restart \
+ the slave SQL thread with \"SLAVE START\". We stopped at log \
+ '%s' position %s", rli->get_rpl_log_name(),
+ llstr(rli->get_group_master_log_pos(), llbuff));
++#ifdef WITH_WSREP
++ if (WSREP_ON && last_errno == ER_UNKNOWN_COM_ERROR)
++ {
++ wsrep_node_dropped= TRUE;
++ }
++#endif /* WITH_WSREP */
+ }
+ goto err;
+ }
+@@ -6139,6 +6254,16 @@ llstr(rli->get_group_master_log_pos(), llbuff));
+ rli->recovery_groups_inited= false;
+ }
+
++#ifdef WITH_WSREP
++ if (WSREP_ON)
++ {
++ if (wsrep->preordered_commit(wsrep, &thd->wsrep_po_handle,
++ NULL, 0, 0, false))
++ {
++ WSREP_WARN("preordered cleanup failed");
++ }
++ }
++#endif /* WITH_WSREP */
+ /*
+ Some events set some playgrounds, which won't be cleared because thread
+ stops. Stopping of this thread may not be known to these events ("stop"
+@@ -6191,6 +6316,27 @@ llstr(rli->get_group_master_log_pos(), llbuff));
+ if (thd_added)
+ remove_global_thread(thd);
+ delete thd;
++#ifdef WITH_WSREP
++ /* if slave stopped due to node going non primary, we set global flag to
++ trigger automatic restart of slave when node joins back to cluster
++ */
++ if (wsrep_node_dropped && wsrep_restart_slave)
++ {
++ if (wsrep_ready)
++ {
++ WSREP_INFO("Slave error due to node temporarily non-primary"
++ "SQL slave will continue");
++ wsrep_node_dropped= FALSE;
++ mysql_mutex_unlock(&rli->run_lock);
++ goto wsrep_restart_point;
++ } else {
++ WSREP_INFO("Slave error due to node going non-primary");
++ WSREP_INFO("wsrep_restart_slave was set and therefore slave will be "
++ "automatically restarted when node joins back to cluster");
++ wsrep_restart_slave_activated= TRUE;
++ }
++ }
++#endif /* WITH_WSREP */
+ /*
+ Note: the order of the broadcast and unlock calls below (first broadcast, then unlock)
+ is important. Otherwise a killer_thread can execute between the calls and
+diff --git a/sql/set_var.h b/sql/set_var.h
+index fbc7e16..6995619 100644
+--- a/sql/set_var.h
++++ b/sql/set_var.h
+@@ -256,6 +256,9 @@ public:
+ int check(THD *thd);
+ int update(THD *thd);
+ int light_check(THD *thd);
++#ifdef WITH_WSREP
++ int wsrep_store_variable(THD *thd);
++#endif
+ void print(THD *thd, String *str); /* To self-print */
+ #ifdef OPTIMIZER_TRACE
+ virtual bool is_var_optimizer_trace() const
+@@ -354,6 +357,9 @@ extern sys_var *Sys_gtid_purged_ptr;
+
+ const CHARSET_INFO *get_old_charset_by_name(const char *old_name);
+
++#ifdef WITH_WSREP
++int sql_set_wsrep_variables(THD *thd, List<set_var_base> *var_list);
++#endif
+ int sys_var_init();
+ int sys_var_add_options(std::vector<my_option> *long_options, int parse_flags);
+ void sys_var_end(void);
+diff --git a/sql/sp.cc b/sql/sp.cc
+index 915a6af..d52a5ee 100644
+--- a/sql/sp.cc
++++ b/sql/sp.cc
+@@ -2734,3 +2734,37 @@ String *sp_get_item_value(THD *thd, Item *item, String *str)
+ return NULL;
+ }
+ }
++#ifdef WITH_WSREP
++int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
++{
++ String log_query;
++ sp_head *sp = thd->lex->sphead;
++ ulong saved_mode= thd->variables.sql_mode;
++ String retstr(64);
++ retstr.set_charset(system_charset_info);
++
++ log_query.set_charset(system_charset_info);
++
++ if (sp->m_type == TYPE_ENUM_FUNCTION)
++ {
++ sp_returns_type(thd, retstr, sp);
++ }
++
++ if (!create_string(thd, &log_query,
++ sp->m_type,
++ (sp->m_explicit_name ? sp->m_db.str : NULL),
++ (sp->m_explicit_name ? sp->m_db.length : 0),
++ sp->m_name.str, sp->m_name.length,
++ sp->m_params.str, sp->m_params.length,
++ retstr.c_ptr(), retstr.length(),
++ sp->m_body.str, sp->m_body.length,
++ sp->m_chistics, &(thd->lex->definer->user),
++ &(thd->lex->definer->host),
++ saved_mode))
++ {
++ WSREP_WARN("SP create string failed: %s", thd->query());
++ return 1;
++ }
++ return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
++}
++#endif /* WITH_WSREP */
+diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
+index dc059b0..524b764 100644
+--- a/sql/sql_acl.cc
++++ b/sql/sql_acl.cc
+@@ -2304,6 +2304,9 @@ int check_change_password(THD *thd, const char *host, const char *user,
+ return(1);
+ }
+ if (!thd->slave_thread &&
++#ifdef WITH_WSREP
++ (!WSREP(thd) || !thd->wsrep_applier) &&
++#endif /* WITH_WSREP */
+ (strcmp(thd->security_ctx->user, user) ||
+ my_strcasecmp(system_charset_info, host,
+ thd->security_ctx->priv_host)))
+@@ -2311,7 +2314,12 @@ int check_change_password(THD *thd, const char *host, const char *user,
+ if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
+ return(1);
+ }
++#ifdef WITH_WSREP
++ if ((!WSREP(thd) || !thd->wsrep_applier) &&
++ !thd->slave_thread && !thd->security_ctx->user[0])
++#else
+ if (!thd->slave_thread && !thd->security_ctx->user[0])
++#endif /* WITH_WSREP */
+ {
+ my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
+ MYF(0));
+@@ -2392,7 +2400,7 @@ bool change_password(THD *thd, const char *host, const char *user,
+ TABLE *table;
+ /* Buffer should be extended when password length is extended. */
+ char buff[512];
+- ulong query_length;
++ ulong query_length=0;
+ bool save_binlog_row_based;
+ uchar user_key[MAX_KEY_LENGTH];
+ char *plugin_temp= NULL;
+@@ -2400,6 +2408,9 @@ bool change_password(THD *thd, const char *host, const char *user,
+ uint new_password_len= (uint) strlen(new_password);
+ bool result= 1;
+ enum mysql_user_table_field password_field= MYSQL_USER_FIELD_PASSWORD;
++#ifdef WITH_WSREP
++ const CSET_STRING query_save = thd->query_string;
++#endif /* WITH_WSREP */
+ DBUG_ENTER("change_password");
+ DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
+ host,user,new_password));
+@@ -2407,6 +2418,18 @@ bool change_password(THD *thd, const char *host, const char *user,
+
+ if (check_change_password(thd, host, user, new_password, new_password_len))
+ DBUG_RETURN(1);
++#ifdef WITH_WSREP
++ if (WSREP(thd) && !thd->wsrep_applier)
++ {
++ query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
++ user ? user : "",
++ host ? host : "",
++ new_password);
++ thd->set_query_inner(buff, query_length, system_charset_info);
++
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
++ }
++#endif /* WITH_WSREP */
+
+ tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
+
+@@ -2677,13 +2700,26 @@ bool change_password(THD *thd, const char *host, const char *user,
+ table->file->has_transactions());
+ end:
+ result|= acl_trans_commit_and_close_tables(thd);
++#ifdef WITH_WSREP
++ if (WSREP(thd) && !thd->wsrep_applier)
++ {
++ WSREP_TO_ISOLATION_END;
+
++ thd->query_string = query_save;
++ thd->wsrep_exec_mode = LOCAL_STATE;
++ }
++#endif /* WITH_WSREP */
+ /* Restore the state of binlog format */
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
+
+ DBUG_RETURN(result);
++#ifdef WITH_WSREP
++ error:
++ WSREP_ERROR("Replication of SET PASSWORD failed: %s", buff);
++ DBUG_RETURN(result);
++#endif /* WITH_WSREP */
+ }
+
+
+@@ -10114,6 +10150,12 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
+ if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
+ DBUG_RETURN(1);
+ }
++ else
++ {
++ sql_print_warning("Client failed to provide its character set. "
++ "'%s' will be used as client character set.",
++ mpvio->charset_adapter->charset()->csname);
++ }
+
+ /* Convert database and user names to utf8 */
+ db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
+diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
+index 52b7ff4..7b53fa6 100644
+--- a/sql/sql_admin.cc
++++ b/sql/sql_admin.cc
+@@ -1124,6 +1124,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
+ FALSE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
++ WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL)
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+ res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
+ mysql_recreate_table(thd, first_table, true) :
+@@ -1156,6 +1157,7 @@ bool Sql_cmd_repair_table::execute(THD *thd)
+ FALSE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+ thd->enable_slow_log= opt_log_slow_admin_statements;
++ WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL)
+ res= mysql_admin_table(thd, first_table, &thd->lex->check_opt, "repair",
+ TL_WRITE, 1,
+ MY_TEST(thd->lex->check_opt.sql_flags & TT_USEFRM),
+diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
+index 3cf1c93..167daff 100644
+--- a/sql/sql_alter.cc
++++ b/sql/sql_alter.cc
+@@ -18,6 +18,9 @@
+ // mysql_exchange_partition
+ #include "sql_base.h" // open_temporary_tables
+ #include "sql_alter.h"
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif /* WITH_WSREP */
+
+
+ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
+@@ -304,6 +307,17 @@ bool Sql_cmd_alter_table::execute(THD *thd)
+
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+
++#ifdef WITH_WSREP
++ TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
++
++ if ((!thd->is_current_stmt_binlog_format_row() ||
++ !find_temporary_table(thd, first_table)))
++ {
++ WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db : NULL),
++ ((lex->name.str) ? lex->name.str : NULL),
++ first_table);
++ }
++#endif /* WITH_WSREP */
+ result= mysql_alter_table(thd, select_lex->db, lex->name.str,
+ &create_info,
+ first_table,
+@@ -313,6 +327,13 @@ bool Sql_cmd_alter_table::execute(THD *thd)
+ lex->ignore);
+
+ DBUG_RETURN(result);
++#ifdef WITH_WSREP
++ error:
++ {
++ WSREP_WARN("ALTER TABLE isolation failure");
++ DBUG_RETURN(TRUE);
++ }
++#endif /* WITH_WSREP */
+ }
+
+
+diff --git a/sql/sql_base.cc b/sql/sql_base.cc
+index ef5e7dc..a325273 100644
+--- a/sql/sql_base.cc
++++ b/sql/sql_base.cc
+@@ -57,6 +57,9 @@
+ #include <io.h>
+ #endif
+ #include "table_cache.h" // Table_cache_manager, Table_cache
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif /* WITH_WSREP */
+
+
+ bool
+@@ -5229,6 +5232,22 @@ restart:
+ goto err;
+ }
+ }
++#ifdef WITH_WSREP
++ if ((thd->lex->sql_command== SQLCOM_INSERT ||
++ thd->lex->sql_command== SQLCOM_INSERT_SELECT ||
++ thd->lex->sql_command== SQLCOM_REPLACE ||
++ thd->lex->sql_command== SQLCOM_REPLACE_SELECT ||
++ thd->lex->sql_command== SQLCOM_UPDATE ||
++ thd->lex->sql_command== SQLCOM_UPDATE_MULTI ||
++ thd->lex->sql_command== SQLCOM_LOAD ||
++ thd->lex->sql_command== SQLCOM_DELETE) &&
++ wsrep_replicate_myisam &&
++ (*start)->table && (*start)->table->file->ht->db_type == DB_TYPE_MYISAM)
++ {
++ WSREP_TO_ISOLATION_BEGIN(NULL, NULL, (*start));
++ }
++ error:
++#endif
+
+ /* Set appropriate TABLE::lock_type. */
+ if (tbl && tables->lock_type != TL_UNLOCK &&
+diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in
+index cf7006f..a89bd06 100644
+--- a/sql/sql_builtin.cc.in
++++ b/sql/sql_builtin.cc.in
+@@ -23,7 +23,11 @@ extern "C"
+ extern
+ #endif
+ builtin_plugin
+- @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin, builtin_mysql_password_plugin;
++ @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin,
++#ifdef WITH_WSREP
++ builtin_wsrep_plugin@mysql_plugin_defs@,
++#endif /* WITH_WSREP */
++ builtin_mysql_password_plugin;
+
+ struct st_mysql_plugin *mysql_optional_plugins[]=
+ {
+@@ -32,5 +36,9 @@ struct st_mysql_plugin *mysql_optional_plugins[]=
+
+ struct st_mysql_plugin *mysql_mandatory_plugins[]=
+ {
+- builtin_binlog_plugin, builtin_mysql_password_plugin, @mysql_mandatory_plugins@ 0
++ builtin_binlog_plugin,
++#ifdef WITH_WSREP
++ builtin_wsrep_plugin@mysql_plugin_defs@,
++#endif /* WITH_WSREP */
++ builtin_mysql_password_plugin, @mysql_mandatory_plugins@ 0
+ };
+diff --git a/sql/sql_class.cc b/sql/sql_class.cc
+index 79d56ad..cc35779 100644
+--- a/sql/sql_class.cc
++++ b/sql/sql_class.cc
+@@ -57,6 +57,10 @@
+ #include "debug_sync.h"
+ #include "sql_parse.h" // is_update_query
+ #include "sql_callback.h"
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#include "wsrep_thd.h"
++#endif
+ #include "lock.h"
+ #include "global_threads.h"
+ #include "mysqld.h"
+@@ -816,6 +820,176 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
+ return buffer;
+ }
+
++#ifdef WITH_WSREP
++extern int wsrep_on(void *thd)
++{
++ return (int)(WSREP(((THD*)thd)));
++}
++extern "C" bool wsrep_thd_is_wsrep_on(THD *thd)
++{
++ return thd->variables.wsrep_on;
++}
++
++extern "C" bool wsrep_consistency_check(void *thd)
++{
++ return ((THD*)thd)->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING;
++}
++
++extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode)
++{
++ thd->wsrep_exec_mode= mode;
++}
++extern "C" void wsrep_thd_set_query_state(
++ THD *thd, enum wsrep_query_state state)
++{
++ thd->wsrep_query_state= state;
++}
++extern "C" void wsrep_thd_set_conflict_state(
++ THD *thd, enum wsrep_conflict_state state)
++{
++ thd->wsrep_conflict_state= state;
++}
++
++
++extern "C" enum wsrep_exec_mode wsrep_thd_exec_mode(THD *thd)
++{
++ return thd->wsrep_exec_mode;
++}
++
++extern "C" const char *wsrep_thd_exec_mode_str(THD *thd)
++{
++ return
++ (!thd) ? "void" :
++ (thd->wsrep_exec_mode == LOCAL_STATE) ? "local" :
++ (thd->wsrep_exec_mode == REPL_RECV) ? "applier" :
++ (thd->wsrep_exec_mode == TOTAL_ORDER) ? "total order" :
++ (thd->wsrep_exec_mode == LOCAL_COMMIT) ? "local commit" : "void";
++}
++
++extern "C" enum wsrep_query_state wsrep_thd_query_state(THD *thd)
++{
++ return thd->wsrep_query_state;
++}
++
++extern "C" const char *wsrep_thd_query_state_str(THD *thd)
++{
++ return
++ (!thd) ? "void" :
++ (thd->wsrep_query_state == QUERY_IDLE) ? "idle" :
++ (thd->wsrep_query_state == QUERY_EXEC) ? "executing" :
++ (thd->wsrep_query_state == QUERY_COMMITTING) ? "committing" :
++ (thd->wsrep_query_state == QUERY_EXITING) ? "exiting" :
++ (thd->wsrep_query_state == QUERY_ROLLINGBACK) ? "rolling back" : "void";
++}
++
++extern "C" enum wsrep_conflict_state wsrep_thd_conflict_state(THD *thd)
++{
++ return thd->wsrep_conflict_state;
++}
++extern "C" const char *wsrep_thd_conflict_state_str(THD *thd)
++{
++ return
++ (!thd) ? "void" :
++ (thd->wsrep_conflict_state == NO_CONFLICT) ? "no conflict" :
++ (thd->wsrep_conflict_state == MUST_ABORT) ? "must abort" :
++ (thd->wsrep_conflict_state == ABORTING) ? "aborting" :
++ (thd->wsrep_conflict_state == MUST_REPLAY) ? "must replay" :
++ (thd->wsrep_conflict_state == REPLAYING) ? "replaying" :
++ (thd->wsrep_conflict_state == RETRY_AUTOCOMMIT) ? "retrying" :
++ (thd->wsrep_conflict_state == CERT_FAILURE) ? "cert failure" : "void";
++}
++
++extern "C" wsrep_ws_handle_t* wsrep_thd_ws_handle(THD *thd)
++{
++ return &thd->wsrep_ws_handle;
++}
++
++extern "C"void wsrep_thd_LOCK(THD *thd)
++{
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++}
++extern "C"void wsrep_thd_UNLOCK(THD *thd)
++{
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++}
++extern "C" time_t wsrep_thd_query_start(THD *thd)
++{
++ return thd->query_start();
++}
++extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd)
++{
++ return thd->wsrep_rand;
++}
++extern "C" my_thread_id wsrep_thd_thread_id(THD *thd)
++{
++ return thd->thread_id;
++}
++extern "C" wsrep_seqno_t wsrep_thd_trx_seqno(THD *thd)
++{
++ return (thd) ? thd->wsrep_trx_meta.gtid.seqno : WSREP_SEQNO_UNDEFINED;
++}
++extern "C" query_id_t wsrep_thd_query_id(THD *thd)
++{
++ return thd->query_id;
++}
++extern "C" char *wsrep_thd_query(THD *thd)
++{
++ return (thd) ? thd->query() : NULL;
++}
++extern "C" query_id_t wsrep_thd_wsrep_last_query_id(THD *thd)
++{
++ return thd->wsrep_last_query_id;
++}
++extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id)
++{
++ thd->wsrep_last_query_id= id;
++}
++extern "C" void wsrep_thd_awake(THD *thd, my_bool signal)
++{
++ if (signal)
++ {
++ mysql_mutex_lock(&thd->LOCK_thd_data);
++ thd->awake(THD::KILL_QUERY);
++ mysql_mutex_unlock(&thd->LOCK_thd_data);
++ }
++ else
++ {
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ mysql_cond_broadcast(&COND_wsrep_replaying);
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++ }
++}
++extern "C" int wsrep_thd_retry_counter(THD *thd)
++{
++ return(thd->wsrep_retry_counter);
++}
++
++extern int
++wsrep_trx_order_before(void *thd1, void *thd2)
++{
++ if (wsrep_thd_trx_seqno((THD*)thd1) < wsrep_thd_trx_seqno((THD*)thd2)) {
++ WSREP_DEBUG("BF conflict, order: %lld %lld\n",
++ (long long)wsrep_thd_trx_seqno((THD*)thd1),
++ (long long)wsrep_thd_trx_seqno((THD*)thd2));
++ return 1;
++ }
++ WSREP_DEBUG("waiting for BF, trx order: %lld %lld\n",
++ (long long)wsrep_thd_trx_seqno((THD*)thd1),
++ (long long)wsrep_thd_trx_seqno((THD*)thd2));
++ return 0;
++}
++extern "C" int
++wsrep_trx_is_aborting(void *thd_ptr)
++{
++ if (thd_ptr) {
++ if ((((THD *)thd_ptr)->wsrep_conflict_state == MUST_ABORT) ||
++ (((THD *)thd_ptr)->wsrep_conflict_state == ABORTING)) {
++ return 1;
++ }
++ }
++ return 0;
++}
++#endif
+
+ /**
+ Implementation of Drop_table_error_handler::handle_condition().
+@@ -843,7 +1017,6 @@ bool Drop_table_error_handler::handle_condition(THD *thd,
+ sql_errno == ER_TRG_NO_DEFINER);
+ }
+
+-
+ void Open_tables_state::set_open_tables_state(Open_tables_state *state)
+ {
+ this->open_tables= state->open_tables;
+@@ -878,7 +1051,11 @@ void Open_tables_state::reset_open_tables_state()
+ }
+
+
++#ifdef WITH_WSREP
++THD::THD(bool enable_plugins, bool is_applier)
++#else
+ THD::THD(bool enable_plugins)
++#endif
+ :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
+ /* statement id */ 0),
+ rli_fake(0), rli_slave(NULL),
+@@ -911,6 +1088,16 @@ THD::THD(bool enable_plugins)
+ bootstrap(0),
+ derived_tables_processing(FALSE),
+ sp_runtime_ctx(NULL),
++#ifdef WITH_WSREP
++ wsrep_applier(is_applier),
++ wsrep_applier_closing(FALSE),
++ wsrep_client_thread(0),
++ wsrep_po_handle(WSREP_PO_INITIALIZER),
++ wsrep_po_cnt(0),
++ wsrep_po_in_trans(FALSE),
++ wsrep_apply_format(0),
++ wsrep_apply_toi(false),
++#endif
+ m_parser_state(NULL),
+ #if defined(ENABLED_DEBUG_SYNC)
+ debug_sync_control(0),
+@@ -1001,6 +1188,22 @@ THD::THD(bool enable_plugins)
+ *scramble= '\0';
+ skip_gtid_rollback= false;
+
++#ifdef WITH_WSREP
++ mysql_mutex_init(key_LOCK_wsrep_thd, &LOCK_wsrep_thd, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_thd, &COND_wsrep_thd, NULL);
++ wsrep_ws_handle.trx_id = WSREP_UNDEFINED_TRX_ID;
++ wsrep_ws_handle.opaque = NULL;
++ wsrep_retry_counter = 0;
++ wsrep_PA_safe = true;
++ wsrep_retry_query = NULL;
++ wsrep_retry_query_len = 0;
++ wsrep_retry_command = COM_CONNECT;
++ wsrep_consistency_check = NO_CONSISTENCY_CHECK;
++ wsrep_status_vars = 0;
++ wsrep_mysql_replicated = 0;
++ wsrep_TOI_pre_query = NULL;
++ wsrep_TOI_pre_query_len = 0;
++#endif
+ /* Call to init() below requires fully initialized Open_tables_state. */
+ reset_open_tables_state();
+
+@@ -1033,6 +1236,13 @@ THD::THD(bool enable_plugins)
+ randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::global_query_id);
+ substitute_null_with_insert_id = FALSE;
+ thr_lock_info_init(&lock_info); /* safety: will be reset after start */
++#ifdef WITH_WSREP
++ lock_info.mysql_thd= (void *)this;
++ lock_info.in_lock_tables= false;
++#ifdef WSREP_PROC_INFO
++ wsrep_info[sizeof(wsrep_info) - 1] = '\0'; /* make sure it is 0-terminated */
++#endif /* WSREP_PROC_INFO */
++#endif /* WITH_WSREP */
+
+ m_internal_handler= NULL;
+ m_binlog_invoker= FALSE;
+@@ -1379,6 +1589,22 @@ void THD::init(void)
+ reset_current_stmt_binlog_format_row();
+ reset_binlog_local_stmt_filter();
+ memset(&status_var, 0, sizeof(status_var));
++#ifdef WITH_WSREP
++ wsrep_exec_mode= wsrep_applier ? REPL_RECV : LOCAL_STATE;
++ wsrep_conflict_state= NO_CONFLICT;
++ wsrep_query_state= QUERY_IDLE;
++ wsrep_last_query_id= 0;
++ wsrep_trx_meta.gtid= WSREP_GTID_UNDEFINED;
++ wsrep_trx_meta.depends_on= WSREP_SEQNO_UNDEFINED;
++ wsrep_converted_lock_session= false;
++ wsrep_retry_counter= 0;
++ wsrep_rli= NULL;
++ wsrep_PA_safe= true;
++ wsrep_consistency_check = NO_CONSISTENCY_CHECK;
++ wsrep_mysql_replicated = 0;
++ wsrep_TOI_pre_query = NULL;
++ wsrep_TOI_pre_query_len = 0;
++#endif
+ binlog_row_event_extra_data= 0;
+
+ if (variables.sql_log_bin)
+@@ -1573,6 +1799,13 @@ void THD::release_resources()
+ plugin_thdvar_cleanup(this);
+
+ m_release_resources_done= true;
++#ifdef WITH_WSREP
++ mysql_mutex_lock(&LOCK_wsrep_thd);
++ mysql_mutex_unlock(&LOCK_wsrep_thd);
++ mysql_mutex_destroy(&LOCK_wsrep_thd);
++ if (wsrep_rli) delete wsrep_rli;
++ if (wsrep_status_vars) wsrep->stats_free(wsrep, wsrep_status_vars);
++#endif
+ }
+
+
+@@ -1873,7 +2106,19 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
+ (e.g. see partitioning code).
+ */
+ if (!thd_table->needs_reopen())
++#ifdef WITH_WSREP
++ {
++ signalled|= mysql_lock_abort_for_thread(this, thd_table);
++ if (this && WSREP(this) && wsrep_thd_is_BF((void *)this, FALSE))
++ {
++ WSREP_DEBUG("remove_table_from_cache: %llu",
++ (unsigned long long) this->real_id);
++ wsrep_abort_thd((void *)this, (void *)in_use, FALSE);
++ }
++ }
++#else
+ signalled|= mysql_lock_abort_for_thread(this, thd_table);
++#endif
+ }
+ mysql_mutex_unlock(&in_use->LOCK_thd_data);
+ }
+@@ -2438,6 +2683,13 @@ bool sql_exchange::escaped_given(void)
+ bool select_send::send_result_set_metadata(List<Item> &list, uint flags)
+ {
+ bool res;
++#ifdef WITH_WSREP
++ if (WSREP(thd) && thd->wsrep_retry_query)
++ {
++ WSREP_DEBUG("skipping select metadata");
++ return FALSE;
++ }
++#endif /* WITH_WSREP */
+ if (!(res= thd->protocol->send_result_set_metadata(&list, flags)))
+ is_result_set_started= 1;
+ return res;
+@@ -4206,8 +4458,13 @@ extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
+
+ extern "C" int thd_binlog_format(const MYSQL_THD thd)
+ {
++#ifdef WITH_WSREP
++ if (((WSREP(thd) && wsrep_emulate_bin_log) || mysql_bin_log.is_open()) &&
++ (thd->variables.option_bits & OPTION_BIN_LOG))
++#else
+ if (mysql_bin_log.is_open() && (thd->variables.option_bits & OPTION_BIN_LOG))
+- return (int) thd->variables.binlog_format;
++#endif
++ return (int) WSREP_BINLOG_FORMAT(thd->variables.binlog_format);
+ else
+ return BINLOG_FORMAT_UNSPEC;
+ }
+diff --git a/sql/sql_class.h b/sql/sql_class.h
+index 17f0055..ded32a5 100644
+--- a/sql/sql_class.h
++++ b/sql/sql_class.h
+@@ -70,6 +70,18 @@ void set_thd_stage_info(void *thd,
+ #define THD_STAGE_INFO(thd, stage) \
+ (thd)->enter_stage(& stage, NULL, __func__, __FILE__, __LINE__)
+
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++struct wsrep_thd_shadow {
++ ulonglong options;
++ uint server_status;
++ enum wsrep_exec_mode wsrep_exec_mode;
++ Vio *vio;
++ ulong tx_isolation;
++ char *db;
++ size_t db_length;
++};
++#endif
+ class Reprepare_observer;
+ class Relay_log_info;
+
+@@ -547,6 +559,12 @@ typedef struct system_variables
+ my_bool sysdate_is_now;
+ my_bool binlog_rows_query_log_events;
+
++#ifdef WITH_WSREP
++ my_bool wsrep_on;
++ my_bool wsrep_causal_reads;
++ uint wsrep_sync_wait;
++ ulong wsrep_retry_autocommit;
++#endif
+ double long_query_time_double;
+
+ my_bool pseudo_slave_mode;
+@@ -2321,7 +2339,8 @@ public:
+ int is_current_stmt_binlog_format_row() const {
+ DBUG_ASSERT(current_stmt_binlog_format == BINLOG_FORMAT_STMT ||
+ current_stmt_binlog_format == BINLOG_FORMAT_ROW);
+- return current_stmt_binlog_format == BINLOG_FORMAT_ROW;
++ return (WSREP_BINLOG_FORMAT((ulong)current_stmt_binlog_format) ==
++ BINLOG_FORMAT_ROW);
+ }
+ /** Tells whether the given optimizer_switch flag is on */
+ inline bool optimizer_switch_flag(ulonglong flag) const
+@@ -3114,6 +3133,45 @@ public:
+ query_id_t first_query_id;
+ } binlog_evt_union;
+
++#ifdef WITH_WSREP
++ const bool wsrep_applier; /* dedicated slave applier thread */
++ bool wsrep_applier_closing; /* applier marked to close */
++ bool wsrep_client_thread; /* to identify client threads*/
++ enum wsrep_exec_mode wsrep_exec_mode;
++ query_id_t wsrep_last_query_id;
++ enum wsrep_query_state wsrep_query_state;
++ enum wsrep_conflict_state wsrep_conflict_state;
++ mysql_mutex_t LOCK_wsrep_thd;
++ mysql_cond_t COND_wsrep_thd;
++ // changed from wsrep_seqno_t to wsrep_trx_meta_t in wsrep API rev 75
++ // wsrep_seqno_t wsrep_trx_seqno;
++ wsrep_trx_meta_t wsrep_trx_meta;
++ uint32 wsrep_rand;
++ Relay_log_info* wsrep_rli;
++ bool wsrep_converted_lock_session;
++ wsrep_ws_handle_t wsrep_ws_handle;
++#ifdef WSREP_PROC_INFO
++ char wsrep_info[128]; /* string for dynamic proc info */
++#endif /* WSREP_PROC_INFO */
++ ulong wsrep_retry_counter; // of autocommit
++ bool wsrep_PA_safe;
++ char* wsrep_retry_query;
++ size_t wsrep_retry_query_len;
++ enum enum_server_command wsrep_retry_command;
++ enum wsrep_consistency_check_mode
++ wsrep_consistency_check;
++ wsrep_stats_var* wsrep_status_vars;
++ int wsrep_mysql_replicated;
++ const char* wsrep_TOI_pre_query; /* a query to apply before
++ the actual TOI query */
++ size_t wsrep_TOI_pre_query_len;
++ wsrep_po_handle_t wsrep_po_handle;
++ size_t wsrep_po_cnt;
++ my_bool wsrep_po_in_trans;
++ rpl_sid wsrep_po_sid;
++ void* wsrep_apply_format;
++ bool wsrep_apply_toi; /* applier processing in TOI */
++#endif /* WITH_WSREP */
+ /**
+ Internal parser state.
+ Note that since the parser is not re-entrant, we keep only one parser
+@@ -3149,7 +3207,11 @@ public:
+ // We don't want to load/unload plugins for unit tests.
+ bool m_enable_plugins;
+
++#ifdef WITH_WSREP
++ THD(bool enable_plugins= true, bool is_applier = false);
++#else
+ THD(bool enable_plugins= true);
++#endif
+
+ /*
+ The THD dtor is effectively split in two:
+@@ -3657,7 +3719,7 @@ public:
+ tests fail and so force them to propagate the
+ lex->binlog_row_based_if_mixed upwards to the caller.
+ */
+- if ((variables.binlog_format == BINLOG_FORMAT_MIXED) &&
++ if ((WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_MIXED)&&
+ (in_sub_stmt == 0))
+ set_current_stmt_binlog_format_row();
+
+@@ -3699,7 +3761,7 @@ public:
+ show_system_thread(system_thread)));
+ if (in_sub_stmt == 0)
+ {
+- if (variables.binlog_format == BINLOG_FORMAT_ROW)
++ if (WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_ROW)
+ set_current_stmt_binlog_format_row();
+ else if (temporary_tables == NULL)
+ clear_current_stmt_binlog_format_row();
+diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
+index 67b671b..9034a6e 100644
+--- a/sql/sql_connect.cc
++++ b/sql/sql_connect.cc
+@@ -57,6 +57,9 @@ using std::max;
+ #else
+ #define MIN_HANDSHAKE_SIZE 6
+ #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif
+
+ /*
+ Get structure for logging connection data for the current user
+@@ -706,7 +709,11 @@ bool setup_connection_thread_globals(THD *thd)
+ {
+ if (thd->store_globals())
+ {
++#ifdef WITH_WSREP
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++#else
+ close_connection(thd, ER_OUT_OF_RESOURCES);
++#endif
+ statistic_increment(aborted_connects,&LOCK_status);
+ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
+ return 1; // Error
+@@ -772,6 +779,17 @@ bool login_connection(THD *thd)
+ void end_connection(THD *thd)
+ {
+ NET *net= &thd->net;
++#ifdef WITH_WSREP
++ if (WSREP(thd))
++ {
++ wsrep_status_t rcode= wsrep->free_connection(wsrep, thd->thread_id);
++ if (rcode) {
++ WSREP_WARN("wsrep failed to free connection context: %lu, code: %d",
++ thd->thread_id, rcode);
++ }
++ }
++ thd->wsrep_client_thread= 0;
++#endif
+ plugin_thdvar_cleanup(thd);
+
+ /*
+@@ -912,6 +930,9 @@ bool thd_prepare_connection(THD *thd)
+ (char *) thd->security_ctx->host_or_ip);
+
+ prepare_new_connection_state(thd);
++#ifdef WITH_WSREP
++ thd->wsrep_client_thread= 1;
++#endif /* WITH_WSREP */
+ return FALSE;
+ }
+
+@@ -933,7 +954,11 @@ void do_handle_one_connection(THD *thd_arg)
+
+ if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
+ {
++#ifdef WITH_WSREP
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++#else
+ close_connection(thd, ER_OUT_OF_RESOURCES);
++#endif
+ statistic_increment(aborted_connects,&LOCK_status);
+ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
+ return;
+@@ -983,9 +1008,21 @@ void do_handle_one_connection(THD *thd_arg)
+ break;
+ }
+ end_connection(thd);
++#ifdef WITH_WSREP
++ if (WSREP(thd))
++ {
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_query_state= QUERY_EXITING;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++#endif
+
+ end_thread:
++#ifdef WITH_WSREP
++ close_connection(thd, 0, 1);
++#else
+ close_connection(thd);
++#endif
+ if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
+ return; // Probably no-threads
+
+diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
+index f3e8876..1b9ef9b 100644
+--- a/sql/sql_delete.cc
++++ b/sql/sql_delete.cc
+@@ -451,7 +451,11 @@ cleanup:
+ /* See similar binlogging code in sql_update.cc, for comments */
+ if ((error < 0) || thd->transaction.stmt.cannot_safely_rollback())
+ {
++#ifdef WITH_WSREP
++ if ((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()))
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= 0;
+ if (error < 0)
+@@ -896,7 +900,11 @@ void multi_delete::abort_result_set()
+ /*
+ there is only side effects; to binlog with the error
+ */
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+ /* possible error of writing binary log is ignored deliberately */
+@@ -1067,7 +1075,11 @@ bool multi_delete::send_eof()
+ }
+ if ((local_error == 0) || thd->transaction.stmt.cannot_safely_rollback())
+ {
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= 0;
+ if (local_error == 0)
+diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
+index 114beaa..3da18a4 100644
+--- a/sql/sql_insert.cc
++++ b/sql/sql_insert.cc
+@@ -1124,7 +1124,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
+ thd->transaction.stmt.cannot_safely_rollback() ||
+ was_insert_delayed)
+ {
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= 0;
+ if (error <= 0)
+@@ -3773,7 +3777,13 @@ bool select_insert::send_eof()
+ DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'",
+ trans_table, table->file->table_type()));
+
++#ifdef WITH_WSREP
++ error= (thd->wsrep_conflict_state == MUST_ABORT ||
++ thd->wsrep_conflict_state == CERT_FAILURE) ? -1 :
++ (bulk_insert_started ?
++#else
+ error= (bulk_insert_started ?
++#endif /* WITH_WSREP */
+ table->file->ha_end_bulk_insert() : 0);
+ if (!error && thd->is_error())
+ error= thd->get_stmt_da()->sql_errno();
+@@ -3800,8 +3810,13 @@ bool select_insert::send_eof()
+ events are in the transaction cache and will be written when
+ ha_autocommit_or_rollback() is issued below.
+ */
++#ifdef WITH_WSREP
++ if ((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
++ (!error || thd->transaction.stmt.cannot_safely_rollback()))
++#else
+ if (mysql_bin_log.is_open() &&
+ (!error || thd->transaction.stmt.cannot_safely_rollback()))
++#endif
+ {
+ int errcode= 0;
+ if (!error)
+@@ -3886,7 +3901,11 @@ void select_insert::abort_result_set() {
+ transactional_table= table->file->has_transactions();
+ if (thd->transaction.stmt.cannot_safely_rollback())
+ {
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+ /* error of writing binary log is ignored */
+@@ -4293,7 +4312,11 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
+ /* show_database */ TRUE);
+ DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */
+
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif /* WITH_WSREP */
+ {
+ int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+ result= thd->binlog_query(THD::STMT_QUERY_TYPE,
+@@ -4303,6 +4326,9 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
+ /* suppress_use */ FALSE,
+ errcode);
+ }
++#ifdef WITH_WSREP
++ ha_wsrep_fake_trx_id(thd);
++#endif
+ return result;
+ }
+
+@@ -4368,6 +4394,18 @@ bool select_create::send_eof()
+ {
+ trans_commit_stmt(thd);
+ trans_commit_implicit(thd);
++#ifdef WITH_WSREP
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if (thd->wsrep_conflict_state != NO_CONFLICT)
++ {
++ WSREP_DEBUG("select_create commit failed, thd: %lu err: %d %s",
++ thd->thread_id, thd->wsrep_conflict_state, thd->query());
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ abort_result_set();
++ return TRUE;
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++#endif /* WITH_WSREP */
+ }
+
+ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
+index 0ba8bcb..f9e1e6b 100644
+--- a/sql/sql_parse.cc
++++ b/sql/sql_parse.cc
+@@ -107,6 +107,12 @@ using std::min;
+
+ #define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
+
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#include "wsrep_thd.h"
++static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
++ Parser_state *parser_state);
++#endif /* WITH_WSREP */
+ /**
+ @defgroup Runtime_Environment Runtime Environment
+ @{
+@@ -823,7 +829,11 @@ void do_handle_bootstrap(THD *thd)
+ if (my_thread_init() || thd->store_globals())
+ {
+ #ifndef EMBEDDED_LIBRARY
++#ifdef WITH_WSREP
++ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++#else
+ close_connection(thd, ER_OUT_OF_RESOURCES);
++#endif /* WITH_WSREP */
+ #endif
+ thd->fatal_error();
+ goto end;
+@@ -913,7 +923,18 @@ bool do_command(THD *thd)
+ enum enum_server_command command;
+
+ DBUG_ENTER("do_command");
+-
++#ifdef WITH_WSREP
++ if (WSREP(thd))
++ {
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_query_state= QUERY_IDLE;
++ if (thd->wsrep_conflict_state==MUST_ABORT)
++ {
++ wsrep_client_rollback(thd);
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++#endif /* WITH_WSREP */
+ /*
+ indicator of uninitialized lex => normal flow of errors handling
+ (see my_message_sql)
+@@ -952,7 +973,6 @@ bool do_command(THD *thd)
+ matter here, because the read/recv() below doesn't use it.
+ */
+ DEBUG_SYNC(thd, "before_do_command_net_read");
+-
+ /*
+ Because of networking layer callbacks in place,
+ this call will maintain the following instrumentation:
+@@ -968,12 +988,45 @@ bool do_command(THD *thd)
+ packet_length= my_net_read(net);
+ thd->m_server_idle= false;
+
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ /* these THD's are aborted or are aborting during being idle */
++ if (thd->wsrep_conflict_state == ABORTING)
++ {
++ while (thd->wsrep_conflict_state == ABORTING) {
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ my_sleep(1000);
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ }
++ thd->store_globals();
++ }
++ else if (thd->wsrep_conflict_state == ABORTED)
++ {
++ thd->store_globals();
++ }
++
++ thd->wsrep_query_state= QUERY_EXEC;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++#endif /* WITH_WSREP */
+ if (packet_length == packet_error)
+ {
+ DBUG_PRINT("info",("Got error %d reading command from socket %s",
+ net->error,
+ vio_description(net->vio)));
+
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if (thd->wsrep_conflict_state == MUST_ABORT)
++ {
++ DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu", thd->real_id));
++ wsrep_client_rollback(thd);
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++#endif /* WITH_WSREP */
+ /* Instrument this broken statement as "statement/com/error" */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[COM_END].m_key);
+@@ -1026,6 +1079,33 @@ bool do_command(THD *thd)
+ vio_description(net->vio), command,
+ command_name[command].str));
+
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ /*
++ * bail out if DB snapshot has not been installed. We however,
++ * allow queries "SET" and "SHOW", they are trapped later in execute_command
++ */
++ if (thd->variables.wsrep_on && !thd->wsrep_applier && !wsrep_ready &&
++ command != COM_QUERY &&
++ command != COM_PING &&
++ command != COM_QUIT &&
++ command != COM_PROCESS_INFO &&
++ command != COM_PROCESS_KILL &&
++ command != COM_SET_OPTION &&
++ command != COM_SHUTDOWN &&
++ command != COM_SLEEP &&
++ command != COM_STATISTICS &&
++ command != COM_TIME &&
++ command != COM_END
++ ) {
++ my_error(ER_UNKNOWN_COM_ERROR, MYF(0),
++ "WSREP has not yet prepared node for application use");
++ thd->protocol->end_statement();
++ return_value= FALSE;
++ goto out;
++ }
++ }
++#endif /* WITH_WSREP */
+ /* Restore read timeout value */
+ my_net_set_read_timeout(net, thd->variables.net_read_timeout);
+
+@@ -1033,6 +1113,22 @@ bool do_command(THD *thd)
+
+ return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
+
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
++ {
++ return_value= dispatch_command(command, thd, thd->wsrep_retry_query,
++ thd->wsrep_retry_query_len);
++ }
++ }
++ if (thd->wsrep_retry_query && thd->wsrep_conflict_state != REPLAYING)
++ {
++ my_free(thd->wsrep_retry_query);
++ thd->wsrep_retry_query = NULL;
++ thd->wsrep_retry_query_len = 0;
++ thd->wsrep_retry_command = COM_CONNECT;
++ }
++#endif /* WITH_WSREP */
+ out:
+ /* The statement instrumentation must be closed in all cases. */
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
+@@ -1108,6 +1204,36 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
+ DBUG_RETURN(FALSE);
+ }
+
++#ifdef WITH_WSREP
++static my_bool wsrep_read_only_option(THD *thd, TABLE_LIST *all_tables)
++{
++ int opt_readonly_saved = opt_readonly;
++ ulong flag_saved = (ulong)(thd->security_ctx->master_access & SUPER_ACL);
++
++ opt_readonly = 0;
++ thd->security_ctx->master_access &= ~SUPER_ACL;
++
++ my_bool ret = !deny_updates_if_read_only_option(thd, all_tables);
++
++ opt_readonly = opt_readonly_saved;
++ thd->security_ctx->master_access |= flag_saved;
++
++ return ret;
++}
++
++static void wsrep_copy_query(THD *thd)
++{
++ thd->wsrep_retry_command = thd->get_command();
++ thd->wsrep_retry_query_len = thd->query_length();
++ if (thd->wsrep_retry_query) {
++ my_free(thd->wsrep_retry_query);
++ }
++ thd->wsrep_retry_query = (char *)my_malloc(
++ thd->wsrep_retry_query_len + 1, MYF(0));
++ strncpy(thd->wsrep_retry_query, thd->query(), thd->wsrep_retry_query_len);
++ thd->wsrep_retry_query[thd->wsrep_retry_query_len] = '\0';
++}
++#endif /* WITH_WSREP */
+ /**
+ Perform one connection-level (COM_XXXX) command.
+
+@@ -1136,7 +1262,42 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
+ bool error= 0;
+ DBUG_ENTER("dispatch_command");
+ DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ if (!thd->in_multi_stmt_transaction_mode())
++ {
++ thd->wsrep_PA_safe= true;
++ }
+
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_query_state= QUERY_EXEC;
++ if (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
++ {
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ }
++ if (thd->wsrep_conflict_state== MUST_ABORT)
++ {
++ wsrep_client_rollback(thd);
++ }
++ if (thd->wsrep_conflict_state== ABORTED)
++ {
++ my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
++ WSREP_DEBUG("Deadlock error for: %s", thd->query());
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ thd->killed = THD::NOT_KILLED;
++ thd->mysys_var->abort = 0;
++ thd->wsrep_conflict_state = NO_CONFLICT;
++ thd->wsrep_retry_counter = 0;
++ /*
++ Increment threads running to compensate dec_thread_running() called
++ after dispatch_end label.
++ */
++ inc_thread_running();
++ goto dispatch_end;
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++#endif /* WITH_WSREP */
+ /* SHOW PROFILE instrumentation, begin */
+ #if defined(ENABLED_PROFILING)
+ thd->profiling.start_new_query();
+@@ -1319,6 +1480,37 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
+
+ DBUG_PRINT("query",("%-.4096s",thd->query()));
+
++#ifdef WITH_WSREP
++
++ if (WSREP(thd)) {
++ if (!thd->in_multi_stmt_transaction_mode())
++ {
++ thd->wsrep_PA_safe= true;
++ }
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_query_state= QUERY_EXEC;
++ if (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
++ {
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ }
++
++ if (thd->wsrep_conflict_state== MUST_ABORT)
++ {
++ wsrep_client_rollback(thd);
++ }
++ if (thd->wsrep_conflict_state== ABORTED)
++ {
++ my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
++ WSREP_DEBUG("Deadlock error for: %s", thd->query());
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ thd->killed= THD::NOT_KILLED;
++ thd->mysys_var->abort= 0;
++ goto dispatch_end;
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++#endif /* WITH_WSREP */
+ #if defined(ENABLED_PROFILING)
+ thd->profiling.set_query_source(thd->query(), thd->query_length());
+ #endif
+@@ -1329,7 +1521,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
+ if (parser_state.init(thd, thd->query(), thd->query_length()))
+ break;
+
++#ifdef WITH_WSREP
++ wsrep_mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
++#else
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
++#endif /* WITH_WSREP */
+
+ while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
+ ! thd->is_error())
+@@ -1401,10 +1597,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
+ Count each statement from the client.
+ */
+ statistic_increment(thd->status_var.questions, &LOCK_status);
++#ifdef WITH_WSREP
++ if (!WSREP(thd))
++ thd->set_time(); /* Reset the query start time. */
++#else
+ thd->set_time(); /* Reset the query start time. */
++#endif /* WITH_WSREP */
+ parser_state.reset(beginning_of_next_stmt, length);
+ /* TODO: set thd->lex->sql_command to SQLCOM_END here */
++#ifdef WITH_WSREP
++ wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
++#else
+ mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
++#endif /* WITH_WSREP */
+ }
+
+ DBUG_PRINT("info",("query ready"));
+@@ -1746,6 +1951,26 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ break;
+ }
++#ifdef WITH_WSREP
++ dispatch_end:
++
++ if (WSREP(thd)) {
++ /* wsrep BF abort in query exec phase */
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if ((thd->wsrep_conflict_state != REPLAYING) &&
++ (thd->wsrep_conflict_state != RETRY_AUTOCOMMIT)) {
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ thd->update_server_status();
++ thd->protocol->end_statement();
++ query_cache_end_of_result(thd);
++ }
++ else
++ {
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++ } else { /* if (WSREP(thd))... */
++#endif /* WITH_WSREP */
+
+ done:
+ DBUG_ASSERT(thd->derived_tables == NULL &&
+@@ -1758,6 +1983,9 @@ done:
+ thd->send_kill_message();
+ thd->protocol->end_statement();
+ query_cache_end_of_result(thd);
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+
+ if (!thd->is_error() && !thd->killed_errno())
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
+@@ -2256,6 +2484,13 @@ err:
+ return TRUE;
+ }
+
++#ifdef WITH_WSREP
++static bool wsrep_is_show_query(enum enum_sql_command command)
++{
++ DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
++ return (sql_command_flags[command] & CF_STATUS_COMMAND) != 0;
++}
++#endif /* WITH_WSREP */
+
+ /**
+ Execute command saved in thd and lex->sql_command.
+@@ -2497,7 +2732,6 @@ mysql_execute_command(THD *thd)
+ #ifdef HAVE_REPLICATION
+ } /* endif unlikely slave */
+ #endif
+-
+ status_var_increment(thd->status_var.com_stat[lex->sql_command]);
+
+ Opt_trace_start ots(thd, all_tables, lex->sql_command, &lex->var_list,
+@@ -2506,6 +2740,45 @@ mysql_execute_command(THD *thd)
+
+ Opt_trace_object trace_command(&thd->opt_trace);
+ Opt_trace_array trace_command_steps(&thd->opt_trace, "steps");
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ /*
++ change LOCK TABLE WRITE to transaction
++ */
++ if (lex->sql_command== SQLCOM_LOCK_TABLES && wsrep_convert_LOCK_to_trx)
++ {
++ for (TABLE_LIST *table= all_tables; table; table= table->next_global)
++ {
++ if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
++ {
++ lex->sql_command= SQLCOM_BEGIN;
++ thd->wsrep_converted_lock_session= true;
++ break;
++ }
++ }
++ }
++ if (lex->sql_command== SQLCOM_UNLOCK_TABLES &&
++ thd->wsrep_converted_lock_session)
++ {
++ thd->wsrep_converted_lock_session= false;
++ lex->sql_command= SQLCOM_COMMIT;
++ lex->tx_release= TVL_NO;
++ }
++
++ /*
++ * bail out if DB snapshot has not been installed. We however,
++ * allow SET and SHOW queries
++ */
++ if (thd->variables.wsrep_on && !thd->wsrep_applier && !wsrep_ready &&
++ lex->sql_command != SQLCOM_SET_OPTION &&
++ !wsrep_is_show_query(lex->sql_command))
++ {
++ my_error(ER_UNKNOWN_COM_ERROR, MYF(0),
++ "WSREP has not yet prepared node for application use");
++ goto error;
++ }
++ }
++#endif /* WITH_WSREP */
+
+ DBUG_ASSERT(thd->transaction.stmt.cannot_safely_rollback() == FALSE);
+
+@@ -2546,7 +2819,13 @@ mysql_execute_command(THD *thd)
+
+ /* Commit the normal transaction if one is active. */
+ if (trans_commit_implicit(thd))
++ {
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ goto error;
++ }
+ /* Release metadata locks acquired in this transaction. */
+ thd->mdl_context.release_transactional_locks();
+ }
+@@ -2600,7 +2879,9 @@ mysql_execute_command(THD *thd)
+ {
+ system_status_var old_status_var= thd->status_var;
+ thd->initial_status_var= &old_status_var;
+-
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
++#endif /* WITH_WSREP */
+ if (!(res= select_precheck(thd, lex, all_tables, first_table)))
+ res= execute_sqlcom_select(thd, all_tables);
+
+@@ -2616,6 +2897,9 @@ mysql_execute_command(THD *thd)
+ &old_status_var);
+ thd->status_var= old_status_var;
+ mysql_mutex_unlock(&LOCK_status);
++#ifdef WITH_WSREP
++ if (lex->sql_command == SQLCOM_SHOW_STATUS) wsrep_free_status(thd);
++#endif /* WITH_WSREP */
+ break;
+ }
+ case SQLCOM_SHOW_EVENTS:
+@@ -2633,12 +2917,22 @@ mysql_execute_command(THD *thd)
+ case SQLCOM_SHOW_PLUGINS:
+ case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_SHOW_KEYS:
++#ifndef WITH_WSREP
+ case SQLCOM_SHOW_VARIABLES:
+ case SQLCOM_SHOW_CHARSETS:
+ case SQLCOM_SHOW_COLLATIONS:
+ case SQLCOM_SHOW_STORAGE_ENGINES:
+ case SQLCOM_SHOW_PROFILE:
++#endif /* WITH_WSREP */
+ case SQLCOM_SELECT:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
++ case SQLCOM_SHOW_VARIABLES:
++ case SQLCOM_SHOW_CHARSETS:
++ case SQLCOM_SHOW_COLLATIONS:
++ case SQLCOM_SHOW_STORAGE_ENGINES:
++ case SQLCOM_SHOW_PROFILE:
++#endif /* WITH_WSREP */
+ {
+ thd->status_var.last_query_cost= 0.0;
+ thd->status_var.last_query_partial_plans= 0;
+@@ -2938,7 +3232,7 @@ case SQLCOM_PREPARE:
+ */
+ if (thd->query_name_consts &&
+ mysql_bin_log.is_open() &&
+- thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
++ WSREP_BINLOG_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT &&
+ !mysql_bin_log.is_query_in_union(thd, thd->query_id))
+ {
+ List_iterator_fast<Item> it(select_lex->item_list);
+@@ -3056,6 +3350,15 @@ case SQLCOM_PREPARE:
+ }
+ else
+ {
++#ifdef WITH_WSREP
++ /* in STATEMENT format, we probably have to replicate also temporary
++ tables, like mysql replication does
++ */
++ if (!thd->is_current_stmt_binlog_format_row() ||
++ !(create_info.options & HA_LEX_CREATE_TMP_TABLE))
++ WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name,
++ NULL)
++#endif /* WITH_WSREP */
+ /* Regular CREATE TABLE */
+ res= mysql_create_table(thd, create_table,
+ &create_info, &alter_info);
+@@ -3089,6 +3392,7 @@ end_with_restore_list:
+ 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, first_table->table_name, NULL)
+ /*
+ Currently CREATE INDEX or DROP INDEX cause a full table rebuild
+ and thus classify as slow administrative statements just like
+@@ -3181,8 +3485,11 @@ end_with_restore_list:
+ goto error;
+ }
+
++ WSREP_TO_ISOLATION_BEGIN(0, 0, first_table)
+ if (mysql_rename_tables(thd, first_table, 0))
++ {
+ goto error;
++ }
+ break;
+ }
+ #ifndef EMBEDDED_LIBRARY
+@@ -3208,6 +3515,10 @@ end_with_restore_list:
+ goto error;
+ #else
+ {
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
++#endif /* WITH_WSREP */
++
+ /*
+ Access check:
+ SHOW CREATE TABLE require any privileges on the table level (ie
+@@ -3270,6 +3581,10 @@ end_with_restore_list:
+ case SQLCOM_CHECKSUM:
+ {
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
++#endif /* WITH_WSREP */
++
+ if (check_table_access(thd, SELECT_ACL, all_tables,
+ FALSE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+@@ -3278,6 +3593,10 @@ end_with_restore_list:
+ break;
+ }
+ case SQLCOM_UPDATE:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE)) goto error;
++#endif /* WITH_WSREP */
+ {
+ ha_rows found= 0, updated= 0;
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+@@ -3317,6 +3636,10 @@ end_with_restore_list:
+ /* if we switched from normal update, rights are checked */
+ if (up_result != 2)
+ {
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE)) goto error;
++#endif /* WITH_WSREP */
+ if ((res= multi_update_precheck(thd, all_tables)))
+ break;
+ }
+@@ -3386,6 +3709,10 @@ end_with_restore_list:
+ break;
+ }
+ case SQLCOM_REPLACE:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE)) goto error;
++#endif /* WITH_WSREP */
+ #ifndef DBUG_OFF
+ if (mysql_bin_log.is_open())
+ {
+@@ -3420,6 +3747,10 @@ end_with_restore_list:
+ }
+ #endif
+ case SQLCOM_INSERT:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE)) goto error;
++#endif /* WITH_WSREP */
+ {
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+
+@@ -3465,11 +3796,23 @@ end_with_restore_list:
+ }
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_INSERT_SELECT:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE)) goto error;
++#endif /* WITH_WSREP */
+ {
+ select_insert *sel_result;
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= insert_precheck(thd, all_tables)))
+ break;
++#ifdef WITH_WSREP
++ if (thd->wsrep_consistency_check == CONSISTENCY_CHECK_DECLARED)
++ {
++ thd->wsrep_consistency_check = CONSISTENCY_CHECK_RUNNING;
++ WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL);
++ }
++
++#endif
+ /*
+ INSERT...SELECT...ON DUPLICATE KEY UPDATE/REPLACE SELECT/
+ INSERT...IGNORE...SELECT can be unsafe, unless ORDER BY PRIMARY KEY
+@@ -3555,6 +3898,10 @@ end_with_restore_list:
+ break;
+ }
+ case SQLCOM_DELETE:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE)) goto error;
++#endif /* WITH_WSREP */
+ {
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= delete_precheck(thd, all_tables)))
+@@ -3570,6 +3917,10 @@ end_with_restore_list:
+ break;
+ }
+ case SQLCOM_DELETE_MULTI:
++#ifdef WITH_WSREP
++ if (WSREP_CLIENT(thd) &&
++ wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE)) goto error;
++#endif /* WITH_WSREP */
+ {
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
+@@ -3636,6 +3987,18 @@ end_with_restore_list:
+ if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+ }
++#ifdef WITH_WSREP
++ for (TABLE_LIST *table= all_tables; table; table= table->next_global)
++ {
++ if (!lex->drop_temporary &&
++ (!thd->is_current_stmt_binlog_format_row() ||
++ !find_temporary_table(thd, table)))
++ {
++ WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables);
++ break;
++ }
++ }
++#endif /* WITH_WSREP */
+ /* DDL and binlog write order are protected by metadata locks. */
+ res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
+ lex->drop_temporary);
+@@ -3673,7 +4036,6 @@ end_with_restore_list:
+
+ if (!mysql_change_db(thd, &db_str, FALSE))
+ my_ok(thd);
+-
+ break;
+ }
+
+@@ -3836,6 +4198,7 @@ end_with_restore_list:
+ #endif
+ if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
+ break;
++ WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL)
+ res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
+ lex->name.str), &create_info, 0);
+ break;
+@@ -3860,6 +4223,7 @@ end_with_restore_list:
+ #endif
+ if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
+ break;
++ WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL)
+ res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
+ break;
+ }
+@@ -3883,6 +4247,7 @@ end_with_restore_list:
+ res= 1;
+ break;
+ }
++ WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL)
+ res= mysql_upgrade_db(thd, db);
+ if (!res)
+ my_ok(thd);
+@@ -3910,6 +4275,7 @@ end_with_restore_list:
+ #endif
+ if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
+ break;
++ WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL)
+ res= mysql_alter_db(thd, db->str, &create_info);
+ break;
+ }
+@@ -3939,6 +4305,7 @@ end_with_restore_list:
+ if (res)
+ break;
+
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ switch (lex->sql_command) {
+ case SQLCOM_CREATE_EVENT:
+ {
+@@ -3973,6 +4340,7 @@ end_with_restore_list:
+ lex->spname->m_name);
+ break;
+ case SQLCOM_DROP_EVENT:
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (!(res= Events::drop_event(thd,
+ lex->spname->m_db, lex->spname->m_name,
+ lex->drop_if_exists)))
+@@ -3987,6 +4355,7 @@ end_with_restore_list:
+ if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 0))
+ 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
+@@ -4001,6 +4370,7 @@ end_with_restore_list:
+ if (check_access(thd, INSERT_ACL, "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)))
+ my_ok(thd);
+@@ -4012,6 +4382,7 @@ end_with_restore_list:
+ check_global_access(thd,CREATE_USER_ACL))
+ break;
+ /* Conditionally writes to binlog */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (!(res= mysql_drop_user(thd, lex->users_list)))
+ my_ok(thd);
+ break;
+@@ -4022,6 +4393,7 @@ end_with_restore_list:
+ check_global_access(thd,CREATE_USER_ACL))
+ break;
+ /* Conditionally writes to binlog */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (!(res= mysql_rename_user(thd, lex->users_list)))
+ my_ok(thd);
+ break;
+@@ -4036,6 +4408,7 @@ end_with_restore_list:
+ thd->binlog_invoker();
+
+ /* Conditionally writes to binlog */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (!(res = mysql_revoke_all(thd, lex->users_list)))
+ my_ok(thd);
+ break;
+@@ -4102,6 +4475,7 @@ end_with_restore_list:
+ lex->type == TYPE_ENUM_PROCEDURE, 0))
+ goto error;
+ /* Conditionally writes to binlog */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ res= mysql_routine_grant(thd, all_tables,
+ lex->type == TYPE_ENUM_PROCEDURE,
+ lex->users_list, grants,
+@@ -4115,6 +4489,7 @@ end_with_restore_list:
+ all_tables, FALSE, UINT_MAX, FALSE))
+ goto error;
+ /* Conditionally writes to binlog */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ res= mysql_table_grant(thd, all_tables, lex->users_list,
+ lex->columns, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE);
+@@ -4130,6 +4505,7 @@ end_with_restore_list:
+ }
+ else
+ {
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ /* Conditionally writes to binlog */
+ res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE,
+@@ -4258,7 +4634,13 @@ end_with_restore_list:
+ #endif
+ case SQLCOM_BEGIN:
+ if (trans_begin(thd, lex->start_transaction_opt))
++ {
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("BEGIN failed, MDL released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ goto error;
++ }
+ my_ok(thd);
+ break;
+ case SQLCOM_COMMIT:
+@@ -4272,7 +4654,13 @@ end_with_restore_list:
+ (thd->variables.completion_type == 2 &&
+ lex->tx_release != TVL_NO));
+ if (trans_commit(thd))
++ {
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("COMMIT failed, MDL released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ goto error;
++ }
+ thd->mdl_context.release_transactional_locks();
+ /* Begin transaction with the same isolation level. */
+ if (tx_chain)
+@@ -4289,7 +4677,20 @@ end_with_restore_list:
+ /* Disconnect the current client connection. */
+ if (tx_release)
+ thd->killed= THD::KILL_CONNECTION;
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++
++ if (thd->wsrep_conflict_state == NO_CONFLICT ||
++ thd->wsrep_conflict_state == REPLAYING)
++ {
++ my_ok(thd);
++ }
++ } else {
++#endif /* WITH_WSREP */
+ my_ok(thd);
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ break;
+ }
+ case SQLCOM_ROLLBACK:
+@@ -4303,7 +4704,13 @@ end_with_restore_list:
+ (thd->variables.completion_type == 2 &&
+ lex->tx_release != TVL_NO));
+ if (trans_rollback(thd))
++ {
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("rollback failed, MDL released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ goto error;
++ }
+ thd->mdl_context.release_transactional_locks();
+ /* Begin transaction with the same isolation level. */
+ if (tx_chain)
+@@ -4320,7 +4727,17 @@ end_with_restore_list:
+ /* Disconnect the current client connection. */
+ if (tx_release)
+ thd->killed= THD::KILL_CONNECTION;
++#ifdef WITH_WSREP
++ if (WSREP(thd)) {
++ if (thd->wsrep_conflict_state == NO_CONFLICT) {
++ my_ok(thd);
++ }
++ } else {
++#endif /* WITH_WSREP */
+ my_ok(thd);
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ break;
+ }
+ case SQLCOM_RELEASE_SAVEPOINT:
+@@ -4386,6 +4803,7 @@ end_with_restore_list:
+ if (sp_process_definer(thd))
+ goto create_sp_error;
+
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ res= (sp_result= sp_create_routine(thd, lex->sphead));
+ switch (sp_result) {
+ case SP_OK: {
+@@ -4602,6 +5020,7 @@ create_sp_error:
+ already puts on CREATE FUNCTION.
+ */
+ /* Conditionally writes to binlog */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ int sp_result= sp_update_routine(thd, sp_type, lex->spname,
+ &lex->sp_chistics);
+ if (thd->killed)
+@@ -4673,6 +5092,7 @@ create_sp_error:
+ lex->sql_command == SQLCOM_DROP_PROCEDURE,
+ false))
+ goto error;
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+
+ enum_sp_type sp_type= (lex->sql_command == SQLCOM_DROP_PROCEDURE) ?
+ SP_TYPE_PROCEDURE : SP_TYPE_FUNCTION;
+@@ -4793,6 +5213,7 @@ create_sp_error:
+ Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
+ as specified through the thd->lex->create_view_mode flag.
+ */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
+ break;
+ }
+@@ -4801,12 +5222,14 @@ create_sp_error:
+ 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)
+ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
+ break;
+ }
+ case SQLCOM_CREATE_TRIGGER:
+ {
+ /* Conditionally writes to binlog. */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ res= mysql_create_or_drop_trigger(thd, all_tables, 1);
+
+ break;
+@@ -4814,6 +5237,7 @@ create_sp_error:
+ case SQLCOM_DROP_TRIGGER:
+ {
+ /* Conditionally writes to binlog. */
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ res= mysql_create_or_drop_trigger(thd, all_tables, 0);
+ break;
+ }
+@@ -4834,7 +5258,13 @@ create_sp_error:
+ break;
+ case SQLCOM_XA_COMMIT:
+ if (trans_xa_commit(thd))
++ {
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("XA commit failed, MDL released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ goto error;
++ }
+ thd->mdl_context.release_transactional_locks();
+ /*
+ We've just done a commit, reset transaction
+@@ -4846,7 +5276,13 @@ create_sp_error:
+ break;
+ case SQLCOM_XA_ROLLBACK:
+ if (trans_xa_rollback(thd))
++ {
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("XA rollback failed, MDL released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ goto error;
++ }
+ thd->mdl_context.release_transactional_locks();
+ /*
+ We've just done a rollback, reset transaction
+@@ -4866,11 +5302,13 @@ create_sp_error:
+ my_ok(thd);
+ break;
+ case SQLCOM_INSTALL_PLUGIN:
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
+ &thd->lex->ident)))
+ my_ok(thd);
+ break;
+ case SQLCOM_UNINSTALL_PLUGIN:
++ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
+ my_ok(thd);
+ break;
+@@ -5012,6 +5450,9 @@ finish:
+ /* Free tables */
+ THD_STAGE_INFO(thd, stage_closing_tables);
+ close_thread_tables(thd);
++#ifdef WITH_WSREP
++ thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
++#endif /* WITH_WSREP */
+
+ #ifndef DBUG_OFF
+ if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
+@@ -5057,6 +5498,22 @@ finish:
+ {
+ thd->mdl_context.release_statement_locks();
+ }
++ WSREP_TO_ISOLATION_END;
++
++#ifdef WITH_WSREP
++ /*
++ Force release of transactional locks if not in active MST and wsrep is on.
++ */
++ if (WSREP(thd) &&
++ ! thd->in_sub_stmt &&
++ ! thd->in_active_multi_stmt_transaction() &&
++ thd->mdl_context.has_transactional_locks())
++ {
++ WSREP_DEBUG("Forcing release of transactional locks for thd %lu",
++ thd->thread_id);
++ thd->mdl_context.release_transactional_locks();
++ }
++#endif /* WITH_WSREP */
+
+ DBUG_RETURN(res || thd->is_error());
+ }
+@@ -5986,6 +6443,26 @@ void THD::reset_for_next_command()
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+
++#ifdef WITH_WSREP
++ /*
++ Autoinc variables should be adjusted only for locally executed
++ transactions. Appliers and replayers are either processing ROW
++ events or get autoinc variable values from Query_log_event.
++ */
++ if (WSREP(thd) && thd->wsrep_exec_mode == LOCAL_STATE) {
++ if (wsrep_auto_increment_control)
++ {
++ if (thd->variables.auto_increment_offset !=
++ global_system_variables.auto_increment_offset)
++ thd->variables.auto_increment_offset=
++ global_system_variables.auto_increment_offset;
++ if (thd->variables.auto_increment_increment !=
++ global_system_variables.auto_increment_increment)
++ thd->variables.auto_increment_increment=
++ global_system_variables.auto_increment_increment;
++ }
++ }
++#endif /* WITH_WSREP */
+ thd->query_start_used= thd->query_start_usec_used= 0;
+ thd->is_fatal_error= thd->time_zone_used= 0;
+ /*
+@@ -6211,6 +6688,132 @@ void mysql_init_multi_delete(LEX *lex)
+ lex->query_tables_last= &lex->query_tables;
+ }
+
++#ifdef WITH_WSREP
++static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
++ Parser_state *parser_state)
++{
++ bool is_autocommit=
++ !thd->in_multi_stmt_transaction_mode() &&
++ thd->wsrep_conflict_state == NO_CONFLICT &&
++ !thd->wsrep_applier &&
++ wsrep_read_only_option(thd, thd->lex->query_tables);
++
++ do
++ {
++ if (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
++ {
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ }
++ mysql_parse(thd, rawbuf, length, parser_state);
++
++ if (WSREP(thd)) {
++ /* wsrep BF abort in query exec phase */
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if (thd->wsrep_conflict_state == MUST_ABORT) {
++ wsrep_client_rollback(thd);
++
++ WSREP_DEBUG("abort in exec query state, avoiding autocommit");
++ }
++
++ /* checking if BF trx must be replayed */
++ if (thd->wsrep_conflict_state== MUST_REPLAY)
++ {
++ wsrep_replay_transaction(thd);
++ }
++
++ /* setting error code for BF aborted trxs */
++ if (thd->wsrep_conflict_state == ABORTED ||
++ thd->wsrep_conflict_state == CERT_FAILURE)
++ {
++ mysql_reset_thd_for_next_command(thd);
++ thd->killed= THD::NOT_KILLED;
++ if (is_autocommit &&
++ thd->lex->sql_command != SQLCOM_SELECT &&
++ (thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit))
++ {
++ WSREP_DEBUG("wsrep retrying AC query: %s",
++ (thd->query()) ? thd->query() : "void");
++
++ close_thread_tables(thd);
++
++ thd->wsrep_conflict_state= RETRY_AUTOCOMMIT;
++ thd->wsrep_retry_counter++; // grow
++ wsrep_copy_query(thd);
++ thd->set_time();
++ parser_state->reset(rawbuf, length);
++
++ /* PSI end */
++ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
++ thd->m_statement_psi= NULL;
++
++ /* DTRACE end */
++ if (MYSQL_QUERY_DONE_ENABLED())
++ {
++ MYSQL_QUERY_DONE(thd->is_error());
++ }
++
++ /* SHOW PROFILE end */
++#if defined(ENABLED_PROFILING)
++ thd->profiling.finish_current_query();
++#endif
++
++ /* SHOW PROFILE begin */
++#if defined(ENABLED_PROFILING)
++ thd->profiling.start_new_query("continuing");
++ thd->profiling.set_query_source(rawbuf, length);
++#endif
++
++ /* DTRACE begin */
++ MYSQL_QUERY_START(rawbuf, thd->thread_id,
++ (char *) (thd->db ? thd->db : ""),
++ &thd->security_ctx->priv_user[0],
++ (char *) thd->security_ctx->host_or_ip);
++
++ /* PSI begin */
++ thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
++ com_statement_info[thd->get_command()].m_key,
++ thd->db, thd->db_length,
++ thd->charset());
++ }
++ else
++ {
++ WSREP_DEBUG("%s, thd: %lu is_AC: %d, retry: %lu - %lu SQL: %s",
++ (thd->wsrep_conflict_state == ABORTED) ?
++ "BF Aborted" : "cert failure",
++ thd->thread_id, is_autocommit, thd->wsrep_retry_counter,
++ thd->variables.wsrep_retry_autocommit, thd->query());
++ my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
++ thd->killed= THD::NOT_KILLED;
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ if (thd->wsrep_conflict_state != REPLAYING)
++ thd->wsrep_retry_counter= 0; // reset
++ }
++ }
++ else
++ {
++ set_if_smaller(thd->wsrep_retry_counter, 0); // reset; eventually ok
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++ } while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT);
++
++ if (thd->wsrep_retry_query)
++ {
++ WSREP_DEBUG("releasing retry_query: "
++ "conf %d sent %d kill %d errno %d SQL %s",
++ thd->wsrep_conflict_state,
++ thd->get_stmt_da()->is_sent(),
++ thd->killed,
++ thd->get_stmt_da()->is_error() ?
++ thd->get_stmt_da()->sql_errno() : 0,
++ thd->wsrep_retry_query);
++ my_free(thd->wsrep_retry_query);
++ thd->wsrep_retry_query = NULL;
++ thd->wsrep_retry_query_len = 0;
++ thd->wsrep_retry_command = COM_CONNECT;
++ }
++}
++#endif /* WITH_WSREP */
+
+ /*
+ When you modify mysql_parse(), you may need to mofify
+@@ -7265,8 +7868,14 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
+ slayage if both are string-equal.
+ */
+
++#ifdef WITH_WSREP
++ if (((thd->security_ctx->master_access & SUPER_ACL) ||
++ thd->security_ctx->user_matches(tmp->security_ctx)) &&
++ !wsrep_thd_is_BF((void *)tmp, true))
++#else
+ if ((thd->security_ctx->master_access & SUPER_ACL) ||
+ thd->security_ctx->user_matches(tmp->security_ctx))
++#endif /* WITH_WSREP */
+ {
+ /* process the kill only if thread is not already undergoing any kill
+ connection.
+diff --git a/sql/sql_parse.h b/sql/sql_parse.h
+index b1124d1..c54bc96 100644
+--- a/sql/sql_parse.h
++++ b/sql/sql_parse.h
+@@ -210,6 +210,22 @@ inline bool is_supported_parser_charset(const CHARSET_INFO *cs)
+ {
+ return (cs->mbminlen == 1);
+ }
++#ifdef WITH_WSREP
++
++#define WSREP_MYSQL_DB (char *)"mysql"
++#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \
++ if (WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) goto error;
++
++#define WSREP_TO_ISOLATION_END \
++ if (WSREP(thd) || (thd && thd->wsrep_exec_mode==TOTAL_ORDER)) \
++ wsrep_to_isolation_end(thd);
++
++#else
++
++#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_)
++#define WSREP_TO_ISOLATION_END
++
++#endif /* WITH_WSREP */
+
+ extern "C" bool sqlcom_can_generate_row_events(const THD *thd);
+
+diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
+index 15ead35..dbf2be0 100644
+--- a/sql/sql_partition_admin.cc
++++ b/sql/sql_partition_admin.cc
+@@ -767,6 +767,19 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
+ if (check_one_table_access(thd, DROP_ACL, first_table))
+ DBUG_RETURN(TRUE);
+
++#ifdef WITH_WSREP
++ TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
++
++ if ((!thd->is_current_stmt_binlog_format_row() ||
++ !find_temporary_table(thd, first_table)) &&
++ wsrep_to_isolation_begin(
++ thd, first_table->db, first_table->table_name, NULL)
++ )
++ {
++ WSREP_WARN("ALTER TABLE isolation failure");
++ DBUG_RETURN(TRUE);
++ }
++#endif /* WITH_WSREP */
+ if (open_tables(thd, &first_table, &table_counter, 0))
+ DBUG_RETURN(true);
+
+diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
+index bc89a8c..e43b2f2 100644
+--- a/sql/sql_plugin.cc
++++ b/sql/sql_plugin.cc
+@@ -2827,7 +2827,11 @@ void plugin_thdvar_init(THD *thd, bool enable_plugins)
+ thd->variables.dynamic_variables_size= 0;
+ thd->variables.dynamic_variables_ptr= 0;
+
++#ifdef WITH_WSREP
++ if ((!WSREP(thd) || !thd->wsrep_applier) && enable_plugins)
++#else
+ if (enable_plugins)
++#endif
+ {
+ mysql_mutex_lock(&LOCK_plugin);
+ thd->variables.table_plugin=
+diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
+index d187418..5576e69 100644
+--- a/sql/sql_prepare.cc
++++ b/sql/sql_prepare.cc
+@@ -3584,7 +3584,9 @@ Prepared_statement::set_parameters(String *expanded_query,
+ return res;
+ }
+
+-
++#ifdef WITH_WSREP
++void wsrep_replay_transaction(THD *thd);
++#endif /* WITH_WSREP */
+ /**
+ Execute a prepared statement. Re-prepare it a limited number
+ of times if necessary.
+@@ -3661,6 +3663,22 @@ reexecute:
+ thd->push_reprepare_observer(stmt_reprepare_observer);
+
+ error= execute(expanded_query, open_cursor) || thd->is_error();
++#ifdef WITH_WSREP
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ switch (thd->wsrep_conflict_state)
++ {
++ case CERT_FAILURE:
++ WSREP_DEBUG("PS execute fail for CERT_FAILURE: thd: %ld err: %d",
++ thd->thread_id, thd->get_stmt_da()->sql_errno() );
++ thd->wsrep_conflict_state = NO_CONFLICT;
++ break;
++
++ case MUST_REPLAY:
++ (void)wsrep_replay_transaction(thd);
++ default: break;
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++#endif /* WITH_WSREP */
+
+ thd->pop_reprepare_observer();
+
+diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
+index 574ed4c..60ab476 100644
+--- a/sql/sql_reload.cc
++++ b/sql/sql_reload.cc
+@@ -249,7 +249,18 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
+ thd->global_read_lock.unlock_global_read_lock(thd);
+ return 1;
+ }
+- }
++#ifdef WITH_WSREP
++ /*
++ We need to do it second time after wsrep appliers were blocked in
++ make_global_read_lock_block_commit(thd) above since they could have
++ modified the tables too.
++ */
++ if (WSREP(thd) &&
++ close_cached_tables(thd, tables, (options & REFRESH_FAST) ?
++ FALSE : TRUE, TRUE))
++ result= 1;
++#endif /* WITH_WSREP */
++ }
+ else
+ {
+ if (thd && thd->locked_tables_mode)
+diff --git a/sql/sql_show.cc b/sql/sql_show.cc
+index 0dc4da5..9f73f8a 100644
+--- a/sql/sql_show.cc
++++ b/sql/sql_show.cc
+@@ -61,6 +61,9 @@
+ using std::max;
+ using std::min;
+
++#if !defined(MYSQL_MAX_VARIABLE_VALUE_LEN)
++#define MYSQL_MAX_VARIABLE_VALUE_LEN 1024
++#endif // !defined(MYSQL_MAX_VARIABLE_VALUE_LEN)
+ #define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
+
+ #ifdef WITH_PARTITION_STORAGE_ENGINE
+@@ -7938,7 +7941,8 @@ ST_FIELD_INFO variables_fields_info[]=
+ {
+ {"VARIABLE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Variable_name",
+ SKIP_OPEN_TABLE},
+- {"VARIABLE_VALUE", 1024, MYSQL_TYPE_STRING, 0, 1, "Value", SKIP_OPEN_TABLE},
++ {"VARIABLE_VALUE", MYSQL_MAX_VARIABLE_VALUE_LEN, MYSQL_TYPE_STRING, 0, 1,
++ "Value", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ };
+
+diff --git a/sql/sql_table.cc b/sql/sql_table.cc
+index d85585e..26b0e77 100644
+--- a/sql/sql_table.cc
++++ b/sql/sql_table.cc
+@@ -5301,6 +5301,60 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
+ uint not_used;
+ DBUG_ENTER("mysql_create_like_table");
+
++#ifdef WITH_WSREP
++ if (WSREP(thd) && !thd->wsrep_applier)
++ {
++ TABLE *tmp_table;
++ bool is_tmp_table= FALSE;
++
++ for (tmp_table= thd->temporary_tables; tmp_table; tmp_table=tmp_table->next)
++ {
++ if (!strcmp(src_table->db, tmp_table->s->db.str) &&
++ !strcmp(src_table->table_name, tmp_table->s->table_name.str))
++ {
++ is_tmp_table= TRUE;
++ break;
++ }
++ }
++ if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
++ {
++ /* CREATE TEMPORARY TABLE LIKE must be skipped from replication */
++ WSREP_DEBUG("CREATE TEMPORARY TABLE LIKE... skipped replication\n %s",
++ thd->query());
++ }
++ else if (!is_tmp_table)
++ {
++ /* this is straight CREATE TABLE LIKE... eith no tmp tables */
++ WSREP_TO_ISOLATION_BEGIN(table->db, table->table_name, NULL);
++ }
++ else
++ {
++ /* here we have CREATE TABLE LIKE <temporary table>
++ the temporary table definition will be needed in slaves to
++ enable the create to succeed
++ */
++ TABLE_LIST tbl;
++ memset(&tbl, 0, sizeof(tbl));
++ tbl.db= src_table->db;
++ tbl.table_name= tbl.alias= src_table->table_name;
++ tbl.table= tmp_table;
++ char buf[2048];
++ String query(buf, sizeof(buf), system_charset_info);
++ query.length(0); // Have to zero it since constructor doesn't
++
++ (void) store_create_info(thd, &tbl, &query, NULL, TRUE);
++ WSREP_DEBUG("TMP TABLE: %s", query.ptr());
++
++ thd->wsrep_TOI_pre_query= query.ptr();
++ thd->wsrep_TOI_pre_query_len= query.length();
++
++ WSREP_TO_ISOLATION_BEGIN(table->db, table->table_name, NULL);
++
++ thd->wsrep_TOI_pre_query= NULL;
++ thd->wsrep_TOI_pre_query_len= 0;
++ }
++ }
++#endif
+
+ /*
+ We the open source table to get its description in HA_CREATE_INFO
+@@ -5474,6 +5528,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
+
+ err:
+ DBUG_RETURN(res);
++#ifdef WITH_WSREP
++ error:
++ thd->wsrep_TOI_pre_query= NULL;
++ DBUG_RETURN(TRUE);
++#endif /* WITH_WSREP */
+ }
+
+
+@@ -7883,6 +7942,17 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
+
+ DEBUG_SYNC(thd, "alter_opened_table");
+
++#ifdef WITH_WSREP
++ DBUG_EXECUTE_IF("sync.alter_opened_table",
++ {
++ const char act[]=
++ "now "
++ "wait_for signal.alter_opened_table";
++ DBUG_ASSERT(!debug_sync_set_action(thd,
++ STRING_WITH_LEN(act)));
++ };);
++#endif // WITH_WSREP
++
+ if (error)
+ DBUG_RETURN(true);
+
+diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
+index e98b327..c9e92ad 100644
+--- a/sql/sql_trigger.cc
++++ b/sql/sql_trigger.cc
+@@ -434,8 +434,14 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
+ binlogged, so they share the same danger, so trust_function_creators
+ applies to them too.
+ */
++#ifdef WITH_WSREP
++ if (!trust_function_creators &&
++ (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
++ !(thd->security_ctx->master_access & SUPER_ACL))
++#else
+ if (!trust_function_creators && mysql_bin_log.is_open() &&
+ !(thd->security_ctx->master_access & SUPER_ACL))
++#endif /* WITH_WSREP */
+ {
+ my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0));
+ DBUG_RETURN(TRUE);
+@@ -2528,3 +2534,55 @@ bool load_table_name_for_trigger(THD *thd,
+
+ DBUG_RETURN(FALSE);
+ }
++#ifdef WITH_WSREP
++int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
++{
++ LEX *lex= thd->lex;
++ String stmt_query;
++
++ LEX_STRING definer_user;
++ LEX_STRING definer_host;
++
++ if (!lex->definer)
++ {
++ if (!thd->slave_thread)
++ {
++ if (!(lex->definer= create_default_definer(thd)))
++ return 1;
++ }
++ }
++
++ if (lex->definer)
++ {
++ /* SUID trigger. */
++
++ definer_user= lex->definer->user;
++ definer_host= lex->definer->host;
++ }
++ else
++ {
++ /* non-SUID trigger. */
++
++ definer_user.str= 0;
++ definer_user.length= 0;
++
++ definer_host.str= 0;
++ definer_host.length= 0;
++ }
++
++ stmt_query.append(STRING_WITH_LEN("CREATE "));
++
++ append_definer(thd, &stmt_query, &definer_user, &definer_host);
++
++ LEX_STRING stmt_definition;
++ stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
++ stmt_definition.length= thd->lex->stmt_definition_end
++ - thd->lex->stmt_definition_begin;
++ trim_whitespace(thd->charset(), & stmt_definition);
++
++ stmt_query.append(stmt_definition.str, stmt_definition.length);
++
++ return wsrep_to_buf_helper(thd, stmt_query.c_ptr(), stmt_query.length(),
++ buf, buf_len);
++}
++#endif /* WITH_WSREP */
+diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
+index 4d3d4e9..3b6173b 100644
+--- a/sql/sql_truncate.cc
++++ b/sql/sql_truncate.cc
+@@ -24,6 +24,9 @@
+ #include "sql_parse.h" // check_one_table_access()
+ #include "sql_truncate.h"
+ #include "sql_show.h" //append_identifier()
++#ifdef WITH_WSREP
++#include "wsrep_mysqld.h"
++#endif /* WITH_WSREP */
+
+
+ /**
+@@ -461,6 +464,12 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
+ {
+ bool hton_can_recreate;
+
++#ifdef WITH_WSREP
++ if (WSREP(thd) && wsrep_to_isolation_begin(thd,
++ table_ref->db,
++ table_ref->table_name, NULL))
++ DBUG_RETURN(TRUE);
++#endif /* WITH_WSREP */
+ if (lock_table(thd, table_ref, &hton_can_recreate))
+ DBUG_RETURN(TRUE);
+
+@@ -542,7 +551,6 @@ bool Sql_cmd_truncate_table::execute(THD *thd)
+
+ if (! (res= truncate_table(thd, first_table)))
+ my_ok(thd);
+-
+ DBUG_RETURN(res);
+ }
+
+diff --git a/sql/sql_update.cc b/sql/sql_update.cc
+index 4c4714e..cb34af5 100644
+--- a/sql/sql_update.cc
++++ b/sql/sql_update.cc
+@@ -988,7 +988,11 @@ int mysql_update(THD *thd,
+ */
+ if ((error < 0) || thd->transaction.stmt.cannot_safely_rollback())
+ {
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= 0;
+ if (error < 0)
+@@ -2203,7 +2207,11 @@ void multi_update::abort_result_set()
+ The query has to binlog because there's a modified non-transactional table
+ either from the query's list or via a stored routine: bug#13270,23333
+ */
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ /*
+ THD::killed status might not have been set ON at time of an error
+@@ -2435,7 +2443,11 @@ bool multi_update::send_eof()
+
+ if (local_error == 0 || thd->transaction.stmt.cannot_safely_rollback())
+ {
++#ifdef WITH_WSREP
++ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++#else
+ if (mysql_bin_log.is_open())
++#endif
+ {
+ int errcode= 0;
+ if (local_error == 0)
+diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
+index 180b7cd..4b6ef7a 100644
+--- a/sql/sql_yacc.yy
++++ b/sql/sql_yacc.yy
+@@ -7433,7 +7433,7 @@ alter:
+ }
+ view_tail
+ {}
+- | ALTER definer_opt EVENT_SYM sp_name
++ | ALTER definer_opt remember_name EVENT_SYM sp_name
+ {
+ /*
+ It is safe to use Lex->spname because
+@@ -7445,9 +7445,12 @@ alter:
+
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
+ MYSQL_YYABORT;
+- Lex->event_parse_data->identifier= $4;
++ Lex->event_parse_data->identifier= $5;
+
+ Lex->sql_command= SQLCOM_ALTER_EVENT;
++#ifdef WITH_WSREP
++ Lex->stmt_definition_begin= $3;
++#endif
+ }
+ ev_alter_on_schedule_completion
+ opt_ev_rename_to
+@@ -7455,7 +7458,7 @@ alter:
+ opt_ev_comment
+ opt_ev_sql_stmt
+ {
+- if (!($6 || $7 || $8 || $9 || $10))
++ if (!($7 || $8 || $9 || $10 || $11))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+@@ -7465,6 +7468,9 @@ alter:
+ can overwrite it
+ */
+ Lex->sql_command= SQLCOM_ALTER_EVENT;
++#ifdef WITH_WSREP
++ Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr();
++#endif
+ }
+ | ALTER TABLESPACE alter_tablespace_info
+ {
+diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
+index f44196e..f26605a 100644
+--- a/sql/sys_vars.cc
++++ b/sql/sys_vars.cc
+@@ -3222,6 +3222,10 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
+ if (trans_commit_stmt(thd) || trans_commit(thd))
+ {
+ thd->variables.option_bits&= ~OPTION_AUTOCOMMIT;
++ thd->mdl_context.release_transactional_locks();
++#ifdef WITH_WSREP
++ WSREP_DEBUG("autocommit, MDL TRX lock released: %lu", thd->thread_id);
++#endif /* WITH_WSREP */
+ return true;
+ }
+ /*
+@@ -4216,6 +4220,274 @@ static Sys_var_tz Sys_time_zone(
+ "time_zone", "time_zone",
+ SESSION_VAR(time_zone), NO_CMD_LINE,
+ DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
++#ifdef WITH_WSREP
++#include "wsrep_var.h"
++#include "wsrep_sst.h"
++#include "wsrep_binlog.h"
++
++static Sys_var_charptr Sys_wsrep_provider(
++ "wsrep_provider", "Path to replication provider library",
++ PREALLOCATED GLOBAL_VAR(wsrep_provider), CMD_LINE(REQUIRED_ARG, OPT_WSREP_PROVIDER),
++ IN_FS_CHARSET, DEFAULT(wsrep_provider),
++ // IN_FS_CHARSET, DEFAULT(wsrep_provider_default),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_provider_check), ON_UPDATE(wsrep_provider_update));
++
++static Sys_var_charptr Sys_wsrep_provider_options(
++ "wsrep_provider_options", "provider specific options",
++ PREALLOCATED GLOBAL_VAR(wsrep_provider_options),
++ CMD_LINE(REQUIRED_ARG, OPT_WSREP_PROVIDER_OPTIONS),
++ IN_FS_CHARSET, DEFAULT(wsrep_provider_options),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_provider_options_check),
++ ON_UPDATE(wsrep_provider_options_update));
++
++static Sys_var_charptr Sys_wsrep_data_home_dir(
++ "wsrep_data_home_dir", "home directory for wsrep provider",
++ READ_ONLY GLOBAL_VAR(wsrep_data_home_dir), CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(""),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG);
++
++static Sys_var_charptr Sys_wsrep_cluster_name(
++ "wsrep_cluster_name", "Name for the cluster",
++ PREALLOCATED GLOBAL_VAR(wsrep_cluster_name), CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(wsrep_cluster_name),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_cluster_name_check),
++ ON_UPDATE(wsrep_cluster_name_update));
++
++static PolyLock_mutex PLock_wsrep_slave_threads(&LOCK_wsrep_slave_threads);
++static Sys_var_charptr Sys_wsrep_cluster_address (
++ "wsrep_cluster_address", "Address to initially connect to cluster",
++ PREALLOCATED GLOBAL_VAR(wsrep_cluster_address),
++ CMD_LINE(REQUIRED_ARG, OPT_WSREP_CLUSTER_ADDRESS),
++ IN_FS_CHARSET, DEFAULT(wsrep_cluster_address),
++ &PLock_wsrep_slave_threads, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_cluster_address_check),
++ ON_UPDATE(wsrep_cluster_address_update));
++
++static Sys_var_charptr Sys_wsrep_node_name (
++ "wsrep_node_name", "Node name",
++ PREALLOCATED GLOBAL_VAR(wsrep_node_name), CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(wsrep_node_name),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG);
++
++static Sys_var_charptr Sys_wsrep_node_address (
++ "wsrep_node_address", "Node address",
++ PREALLOCATED GLOBAL_VAR(wsrep_node_address), CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(wsrep_node_address),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_node_address_check),
++ ON_UPDATE(wsrep_node_address_update));
++
++static Sys_var_charptr Sys_wsrep_node_incoming_address(
++ "wsrep_node_incoming_address", "Client connection address",
++ PREALLOCATED GLOBAL_VAR(wsrep_node_incoming_address),CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(wsrep_node_incoming_address),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG);
++
++static Sys_var_ulong Sys_wsrep_slave_threads(
++ "wsrep_slave_threads", "Number of slave appliers to launch",
++ GLOBAL_VAR(wsrep_slave_threads), CMD_LINE(REQUIRED_ARG),
++ VALID_RANGE(1, 512), DEFAULT(1), BLOCK_SIZE(1),
++ &PLock_wsrep_slave_threads, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_slave_threads_check),
++ ON_UPDATE(wsrep_slave_threads_update));
++
++static Sys_var_charptr Sys_wsrep_dbug_option(
++ "wsrep_dbug_option", "DBUG options to provider library",
++ GLOBAL_VAR(wsrep_dbug_option),CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(""),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG);
++
++static Sys_var_mybool Sys_wsrep_debug(
++ "wsrep_debug", "To enable debug level logging",
++ GLOBAL_VAR(wsrep_debug), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_mybool Sys_wsrep_convert_LOCK_to_trx(
++ "wsrep_convert_LOCK_to_trx", "To convert locking sessions "
++ "into transactions",
++ GLOBAL_VAR(wsrep_convert_LOCK_to_trx),
++ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_ulong Sys_wsrep_retry_autocommit(
++ "wsrep_retry_autocommit", "Max number of times to retry "
++ "a failed autocommit statement",
++ SESSION_VAR(wsrep_retry_autocommit), CMD_LINE(REQUIRED_ARG),
++ VALID_RANGE(0, 10000), DEFAULT(1), BLOCK_SIZE(1));
++
++static Sys_var_mybool Sys_wsrep_auto_increment_control(
++ "wsrep_auto_increment_control", "To automatically control the "
++ "assignment of autoincrement variables",
++ GLOBAL_VAR(wsrep_auto_increment_control),
++ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
++
++static Sys_var_mybool Sys_wsrep_drupal_282555_workaround(
++ "wsrep_drupal_282555_workaround", "To use a workaround for"
++ "bad autoincrement value",
++ GLOBAL_VAR(wsrep_drupal_282555_workaround),
++ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_charptr sys_wsrep_sst_method(
++ "wsrep_sst_method", "State snapshot transfer method",
++ GLOBAL_VAR(wsrep_sst_method),CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(wsrep_sst_method), NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_sst_method_check),
++ ON_UPDATE(wsrep_sst_method_update));
++
++static Sys_var_charptr Sys_wsrep_sst_receive_address(
++ "wsrep_sst_receive_address", "Address where node is waiting for "
++ "SST contact",
++ GLOBAL_VAR(wsrep_sst_receive_address),CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(wsrep_sst_receive_address), NO_MUTEX_GUARD,
++ NOT_IN_BINLOG,
++ ON_CHECK(wsrep_sst_receive_address_check),
++ ON_UPDATE(wsrep_sst_receive_address_update));
++
++static Sys_var_charptr Sys_wsrep_sst_auth(
++ "wsrep_sst_auth", "Authentication for SST connection",
++ PREALLOCATED GLOBAL_VAR(wsrep_sst_auth), CMD_LINE(REQUIRED_ARG, OPT_WSREP_SST_AUTH),
++ IN_FS_CHARSET, DEFAULT(wsrep_sst_auth), NO_MUTEX_GUARD,
++ NOT_IN_BINLOG,
++ ON_CHECK(wsrep_sst_auth_check),
++ ON_UPDATE(wsrep_sst_auth_update));
++
++static Sys_var_charptr Sys_wsrep_sst_donor(
++ "wsrep_sst_donor", "preferred donor node for the SST",
++ GLOBAL_VAR(wsrep_sst_donor),CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_sst_donor_check),
++ ON_UPDATE(wsrep_sst_donor_update));
++
++static Sys_var_mybool Sys_wsrep_sst_donor_rejects_queries(
++ "wsrep_sst_donor_rejects_queries", "Reject client queries "
++ "when donating state snapshot transfer",
++ GLOBAL_VAR(wsrep_sst_donor_rejects_queries),
++ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_mybool Sys_wsrep_on (
++ "wsrep_on", "To enable wsrep replication ",
++ SESSION_VAR(wsrep_on),
++ CMD_LINE(OPT_ARG), DEFAULT(TRUE),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
++ ON_UPDATE(wsrep_on_update));
++
++static Sys_var_charptr Sys_wsrep_start_position (
++ "wsrep_start_position", "global transaction position to start from ",
++ PREALLOCATED GLOBAL_VAR(wsrep_start_position),
++ CMD_LINE(REQUIRED_ARG, OPT_WSREP_START_POSITION),
++ IN_FS_CHARSET, DEFAULT(wsrep_start_position),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG,
++ ON_CHECK(wsrep_start_position_check),
++ ON_UPDATE(wsrep_start_position_update));
++
++static Sys_var_ulong Sys_wsrep_max_ws_size (
++ "wsrep_max_ws_size", "Max write set size (bytes)",
++ GLOBAL_VAR(wsrep_max_ws_size), CMD_LINE(REQUIRED_ARG),
++ /* Upper limit is 65K short of 4G to avoid overlows on 32-bit systems */
++ VALID_RANGE(1024, WSREP_MAX_WS_SIZE), DEFAULT(1073741824UL), BLOCK_SIZE(1));
++
++static Sys_var_ulong Sys_wsrep_max_ws_rows (
++ "wsrep_max_ws_rows", "Max number of rows in write set",
++ GLOBAL_VAR(wsrep_max_ws_rows), CMD_LINE(REQUIRED_ARG),
++ VALID_RANGE(1, 1048576), DEFAULT(131072), BLOCK_SIZE(1));
++
++static Sys_var_charptr Sys_wsrep_notify_cmd(
++ "wsrep_notify_cmd", "",
++ GLOBAL_VAR(wsrep_notify_cmd),CMD_LINE(REQUIRED_ARG),
++ IN_FS_CHARSET, DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG);
++
++static Sys_var_mybool Sys_wsrep_certify_nonPK(
++ "wsrep_certify_nonPK", "Certify tables with no primary key",
++ GLOBAL_VAR(wsrep_certify_nonPK),
++ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
++
++static Sys_var_mybool Sys_wsrep_causal_reads(
++ "wsrep_causal_reads", "(DEPRECATED) setting this variable is equivalent to setting wsrep_sync_wait READ flag",
++ SESSION_VAR(wsrep_causal_reads),
++ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
++ ON_UPDATE(wsrep_causal_reads_update));
++
++static Sys_var_uint Sys_wsrep_sync_wait(
++ "wsrep_sync_wait", "Ensure \"synchronous\" read view before executing an operation of the type specified by bitmask: 1 - READ(includes SELECT, SHOW and BEGIN/START TRANSACTION); 2 - UPDATE and DELETE; 4 - INSERT and REPLACE",
++ SESSION_VAR(wsrep_sync_wait),
++ CMD_LINE(OPT_ARG),
++ VALID_RANGE(WSREP_SYNC_WAIT_NONE, WSREP_SYNC_WAIT_MAX),
++ DEFAULT(WSREP_SYNC_WAIT_NONE),
++ BLOCK_SIZE(1),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
++ ON_UPDATE(wsrep_sync_wait_update));
++
++static const char *wsrep_OSU_method_names[]= { "TOI", "RSU", NullS };
++static Sys_var_enum Sys_wsrep_OSU_method(
++ "wsrep_OSU_method", "Method for Online Schema Upgrade",
++ GLOBAL_VAR(wsrep_OSU_method_options), CMD_LINE(OPT_ARG),
++ wsrep_OSU_method_names, DEFAULT(WSREP_OSU_TOI),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
++ ON_UPDATE(0));
++
++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),
++ 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_enum Sys_wsrep_forced_binlog_format(
++ "wsrep_forced_binlog_format", "binlog format to take effect over user's choice",
++ GLOBAL_VAR(wsrep_forced_binlog_format),
++ CMD_LINE(REQUIRED_ARG, OPT_BINLOG_FORMAT),
++ wsrep_binlog_format_names, DEFAULT(BINLOG_FORMAT_UNSPEC),
++ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
++ ON_UPDATE(0));
++
++static Sys_var_mybool Sys_wsrep_recover_datadir(
++ "wsrep_recover", "Recover database state after crash and exit",
++ READ_ONLY GLOBAL_VAR(wsrep_recovery),
++ CMD_LINE(OPT_ARG, OPT_WSREP_RECOVER), DEFAULT(FALSE));
++
++static Sys_var_mybool Sys_wsrep_replicate_myisam(
++ "wsrep_replicate_myisam", "To enable myisam replication",
++ GLOBAL_VAR(wsrep_replicate_myisam), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_mybool Sys_wsrep_log_conflicts(
++ "wsrep_log_conflicts", "To log multi-master conflicts",
++ GLOBAL_VAR(wsrep_log_conflicts), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_ulong Sys_wsrep_mysql_replication_bundle(
++ "wsrep_mysql_replication_bundle", "mysql replication group commit ",
++ GLOBAL_VAR(wsrep_mysql_replication_bundle), CMD_LINE(REQUIRED_ARG),
++ VALID_RANGE(0, 1000), DEFAULT(0), BLOCK_SIZE(1));
++
++static Sys_var_mybool Sys_wsrep_preordered(
++ "wsrep_preordered", "To enable preordered write set processing",
++ GLOBAL_VAR(wsrep_preordered_opt), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_mybool Sys_wsrep_load_data_splitting(
++ "wsrep_load_data_splitting", "To commit LOAD DATA "
++ "transaction after every 10K rows inserted",
++ GLOBAL_VAR(wsrep_load_data_splitting),
++ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
++
++static Sys_var_mybool Sys_wsrep_slave_FK_checks(
++ "wsrep_slave_FK_checks", "Should slave thread do "
++ "foreign key constraint checks",
++ GLOBAL_VAR(wsrep_slave_FK_checks),
++ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
++
++static Sys_var_mybool Sys_wsrep_slave_UK_checks(
++ "wsrep_slave_UK_checks", "Should slave thread do "
++ "secondary index uniqueness chesks",
++ GLOBAL_VAR(wsrep_slave_UK_checks),
++ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++
++static Sys_var_mybool Sys_wsrep_restart_slave(
++ "wsrep_restart_slave", "Should MySQL slave be restarted automatically, when node joins back to cluster",
++ GLOBAL_VAR(wsrep_restart_slave), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
++#endif /* WITH_WSREP */
+
+ static bool fix_host_cache_size(sys_var *, THD *, enum_var_type)
+ {
+diff --git a/sql/table.cc b/sql/table.cc
+index afd3947..95eb03d 100644
+--- a/sql/table.cc
++++ b/sql/table.cc
+@@ -5321,8 +5321,17 @@ void TABLE::mark_columns_needed_for_delete()
+ in mark_columns_per_binlog_row_image, if not, then use
+ the hidden primary key
+ */
++#ifdef WITH_WSREP
++ /* this does not affect wsrep patch as long as we use RBR only,
++ but this condition is just preparing for possible future STATEMENT
++ format support
++ */
++ if (!((WSREP_EMULATE_BINLOG(current_thd) || mysql_bin_log.is_open()) &&
++ in_use && in_use->is_current_stmt_binlog_format_row()))
++#else
+ if (!(mysql_bin_log.is_open() && in_use &&
+ in_use->is_current_stmt_binlog_format_row()))
++#endif /* WITH_WSREP */
+ file->use_hidden_primary_key();
+ }
+ else
+@@ -5388,8 +5397,17 @@ void TABLE::mark_columns_needed_for_update()
+ in mark_columns_per_binlog_row_image, if not, then use
+ the hidden primary key
+ */
++#ifdef WITH_WSREP
++ /* this does not affect wsrep patch as long as we use RBR only,
++ but this condition is just preparing for possible future STATEMENT
++ format support
++ */
++ if (!((WSREP_EMULATE_BINLOG(current_thd) || mysql_bin_log.is_open()) &&
++ in_use && in_use->is_current_stmt_binlog_format_row()))
++#else
+ if (!(mysql_bin_log.is_open() && in_use &&
+ in_use->is_current_stmt_binlog_format_row()))
++#endif /* WITH_WSREP */
+ file->use_hidden_primary_key();
+ }
+ else
+@@ -5441,9 +5459,15 @@ void TABLE::mark_columns_per_binlog_row_image()
+ If in RBR we may need to mark some extra columns,
+ depending on the binlog-row-image command line argument.
+ */
++#ifdef WITH_WSREP
++ if (((WSREP_EMULATE_BINLOG(current_thd) || mysql_bin_log.is_open()) &&
++ in_use && in_use->is_current_stmt_binlog_format_row() &&
++ !ha_check_storage_engine_flag(s->db_type(), HTON_NO_BINLOG_ROW_OPT)))
++#else
+ if ((mysql_bin_log.is_open() && in_use &&
+ in_use->is_current_stmt_binlog_format_row() &&
+ !ha_check_storage_engine_flag(s->db_type(), HTON_NO_BINLOG_ROW_OPT)))
++#endif /* WITH_WSREP */
+ {
+
+ THD *thd= current_thd;
+diff --git a/sql/transaction.cc b/sql/transaction.cc
+index a307e8a..1247917 100644
+--- a/sql/transaction.cc
++++ b/sql/transaction.cc
+@@ -102,6 +102,9 @@ static bool xa_trans_force_rollback(THD *thd)
+ by ha_rollback()/THD::transaction::cleanup().
+ */
+ thd->transaction.xid_state.rm_error= 0;
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
+ if (ha_rollback_trans(thd, true))
+ {
+ my_error(ER_XAER_RMERR, MYF(0));
+@@ -140,10 +143,16 @@ bool trans_begin(THD *thd, uint flags)
+ (thd->variables.option_bits & OPTION_TABLE_LOCK))
+ {
+ thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= MY_TEST(ha_commit_trans(thd, TRUE));
++#ifdef WITH_WSREP
++ wsrep_post_commit(thd, TRUE);
++#endif /* WITH_WSREP */
+ }
+
+ thd->variables.option_bits&= ~OPTION_BEGIN;
+@@ -181,6 +190,12 @@ bool trans_begin(THD *thd, uint flags)
+ thd->tx_read_only= false;
+ }
+
++#ifdef WITH_WSREP
++ thd->wsrep_PA_safe= true;
++ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd))
++ DBUG_RETURN(TRUE);
++#endif /* WITH_WSREP */
++
+ thd->variables.option_bits|= OPTION_BEGIN;
+ thd->server_status|= SERVER_STATUS_IN_TRANS;
+ if (thd->tx_read_only)
+@@ -222,12 +237,18 @@ bool trans_commit(THD *thd)
+ if (trans_check_state(thd))
+ DBUG_RETURN(TRUE);
+
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= ha_commit_trans(thd, TRUE);
+ thd->variables.option_bits&= ~OPTION_BEGIN;
+ thd->transaction.all.reset_unsafe_rollback_flags();
++#ifdef WITH_WSREP
++ wsrep_post_commit(thd, TRUE);
++#endif /* WITH_WSREP */
+ thd->lex->start_transaction_opt= 0;
+
+ DBUG_RETURN(MY_TEST(res));
+@@ -275,10 +296,16 @@ bool trans_commit_implicit(THD *thd)
+ /* Safety if one did "drop table" on locked tables */
+ if (!thd->locked_tables_mode)
+ thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
+- thd->server_status&=
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
++ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= MY_TEST(ha_commit_trans(thd, TRUE));
++#ifdef WITH_WSREP
++ wsrep_post_commit(thd, TRUE);
++#endif /* WITH_WSREP */
+ }
+ else if (tc_log)
+ tc_log->commit(thd, true);
+@@ -312,6 +339,9 @@ bool trans_rollback(THD *thd)
+ {
+ int res;
+ DBUG_ENTER("trans_rollback");
++#ifdef WITH_WSREP
++ thd->wsrep_PA_safe= true;
++#endif /* WITH_WSREP */
+
+ #ifndef DBUG_OFF
+ char buf1[256], buf2[256];
+@@ -326,6 +356,9 @@ bool trans_rollback(THD *thd)
+ if (trans_check_state(thd))
+ DBUG_RETURN(TRUE);
+
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+@@ -366,6 +399,9 @@ bool trans_rollback_implicit(THD *thd)
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt);
+
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, true);
++#endif /* WITH_WSREP */
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+@@ -431,12 +467,20 @@ bool trans_commit_stmt(THD *thd)
+
+ if (thd->transaction.stmt.ha_list)
+ {
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, FALSE);
++#endif /* WITH_WSREP */
+ res= ha_commit_trans(thd, FALSE);
+ if (! thd->in_active_multi_stmt_transaction())
++#ifdef WITH_WSREP
+ {
++#endif /* WITH_WSREP */
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
++#ifdef WITH_WSREP
++ wsrep_post_commit(thd, FALSE);
+ }
++#endif /* WITH_WSREP */
+ }
+ else if (tc_log)
+ tc_log->commit(thd, false);
+@@ -481,6 +525,9 @@ bool trans_rollback_stmt(THD *thd)
+
+ if (thd->transaction.stmt.ha_list)
+ {
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, FALSE);
++#endif /* WITH_WSREP */
+ ha_rollback_trans(thd, FALSE);
+ if (! thd->in_active_multi_stmt_transaction())
+ {
+@@ -652,9 +699,16 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
+ For backward-compatibility reasons we always release MDL if binary
+ logging is off.
+ */
++#ifdef WITH_WSREP
++ bool mdl_can_safely_rollback_to_savepoint=
++ (!((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
++ && thd->variables.sql_log_bin) ||
++ ha_rollback_to_savepoint_can_release_mdl(thd));
++#else
+ bool mdl_can_safely_rollback_to_savepoint=
+ (!(mysql_bin_log.is_open() && thd->variables.sql_log_bin) ||
+ ha_rollback_to_savepoint_can_release_mdl(thd));
++#endif /* WITH_WSREP */
+
+ if (ha_rollback_to_savepoint(thd, sv))
+ res= TRUE;
+@@ -871,9 +925,15 @@ bool trans_xa_commit(THD *thd)
+ }
+ else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
+ {
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
+ int r= ha_commit_trans(thd, TRUE);
+ if ((res= MY_TEST(r)))
+ my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
++#ifdef WITH_WSREP
++ wsrep_post_commit(thd, TRUE);
++#endif /* WITH_WSREP */
+ }
+ else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
+ {
+@@ -892,6 +952,9 @@ bool trans_xa_commit(THD *thd)
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
++#ifdef WITH_WSREP
++ wsrep_register_hton(thd, TRUE);
++#endif /* WITH_WSREP */
+ ha_rollback_trans(thd, TRUE);
+ my_error(ER_XAER_RMERR, MYF(0));
+ }
+diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
+new file mode 100644
+index 0000000..bb95a66
+--- /dev/null
++++ b/sql/wsrep_applier.cc
+@@ -0,0 +1,386 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#include "wsrep_priv.h"
++#include "wsrep_binlog.h" // wsrep_dump_rbr_buf()
++
++#include "log_event.h" // class THD, EVENT_LEN_OFFSET, etc.
++#include "wsrep_applier.h"
++#include "debug_sync.h"
++
++/*
++ read the first event from (*buf). The size of the (*buf) is (*buf_len).
++ At the end (*buf) is shitfed to point to the following event or NULL and
++ (*buf_len) will be changed to account just being read bytes of the 1st event.
++*/
++
++static Log_event* wsrep_read_log_event(
++ char **arg_buf, size_t *arg_buf_len,
++ const Format_description_log_event *description_event)
++{
++ DBUG_ENTER("wsrep_read_log_event");
++ char *head= (*arg_buf);
++
++ uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
++ char *buf= (*arg_buf);
++ const char *error= 0;
++ Log_event *res= 0;
++
++ if (data_len > wsrep_max_ws_size)
++ {
++ error = "Event too big";
++ goto err;
++ }
++
++ res= Log_event::read_log_event(buf, data_len, &error, description_event, 0);
++
++err:
++ if (!res)
++ {
++ DBUG_ASSERT(error != 0);
++ sql_print_error("Error in Log_event::read_log_event(): "
++ "'%s', data_len: %d, event_type: %d",
++ error,data_len,head[EVENT_TYPE_OFFSET]);
++ }
++ (*arg_buf)+= data_len;
++ (*arg_buf_len)-= data_len;
++ DBUG_RETURN(res);
++}
++
++#include "transaction.h" // trans_commit(), trans_rollback()
++#include "rpl_rli.h" // class Relay_log_info;
++#include "sql_base.h" // close_temporary_table()
++
++static inline void
++wsrep_set_apply_format(THD* thd, Format_description_log_event* ev)
++{
++ if (thd->wsrep_apply_format)
++ {
++ delete (Format_description_log_event*)thd->wsrep_apply_format;
++ }
++ thd->wsrep_apply_format= ev;
++}
++
++static inline Format_description_log_event*
++wsrep_get_apply_format(THD* thd)
++{
++ if (thd->wsrep_apply_format)
++ return (Format_description_log_event*) thd->wsrep_apply_format;
++ return thd->wsrep_rli->get_rli_description_event();
++}
++
++static wsrep_cb_status_t wsrep_apply_events(THD* thd,
++ const void* events_buf,
++ size_t buf_len)
++{
++ char *buf= (char *)events_buf;
++ int rcode= 0;
++ int event= 1;
++
++ DBUG_ENTER("wsrep_apply_events");
++
++ if (thd->killed == THD::KILL_CONNECTION &&
++ thd->wsrep_conflict_state != REPLAYING)
++ {
++ WSREP_INFO("applier has been aborted, skipping apply_rbr: %lld",
++ (long long) wsrep_thd_trx_seqno(thd));
++ DBUG_RETURN(WSREP_CB_FAILURE);
++ }
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_query_state= QUERY_EXEC;
++ if (thd->wsrep_conflict_state!= REPLAYING)
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ if (!buf_len) WSREP_DEBUG("empty rbr buffer to apply: %lld",
++ (long long) wsrep_thd_trx_seqno(thd));
++
++ while(buf_len)
++ {
++ int exec_res;
++ Log_event* ev= wsrep_read_log_event(&buf, &buf_len,
++ wsrep_get_apply_format(thd));
++
++ if (!ev)
++ {
++ WSREP_ERROR("applier could not read binlog event, seqno: %lld, len: %zu",
++ (long long)wsrep_thd_trx_seqno(thd), buf_len);
++ rcode= 1;
++ goto error;
++ }
++
++ switch (ev->get_type_code()) {
++ case FORMAT_DESCRIPTION_EVENT:
++ wsrep_set_apply_format(thd, (Format_description_log_event*)ev);
++ continue;
++ case GTID_LOG_EVENT:
++ {
++ Gtid_log_event* gev= (Gtid_log_event*)ev;
++ if (gev->get_gno() == 0)
++ {
++ /* Skip GTID log event to make binlog to generate LTID on commit */
++ delete ev;
++ continue;
++ }
++ }
++ default:
++ break;
++ }
++
++ thd->server_id = ev->server_id; // use the original server id for logging
++ thd->set_time(); // time the query
++ wsrep_xid_init(&thd->transaction.xid_state.xid,
++ &thd->wsrep_trx_meta.gtid.uuid,
++ thd->wsrep_trx_meta.gtid.seqno);
++ thd->lex->current_select= 0;
++ if (!ev->when.tv_sec)
++ my_micro_time_to_timeval(my_micro_time(), &ev->when);
++ ev->thd = thd;
++ exec_res = ev->apply_event(thd->wsrep_rli);
++ DBUG_PRINT("info", ("exec_event result: %d", exec_res));
++
++ if (exec_res)
++ {
++ WSREP_WARN("RBR event %d %s apply warning: %d, %lld",
++ event, ev->get_type_str(), exec_res,
++ (long long) wsrep_thd_trx_seqno(thd));
++ rcode= exec_res;
++ /* stop processing for the first error */
++ delete ev;
++ goto error;
++ }
++ event++;
++
++ if (thd->wsrep_conflict_state!= NO_CONFLICT &&
++ thd->wsrep_conflict_state!= REPLAYING)
++ WSREP_WARN("conflict state after RBR event applying: %d, %lld",
++ thd->wsrep_query_state, (long long)wsrep_thd_trx_seqno(thd));
++
++ if (thd->wsrep_conflict_state == MUST_ABORT) {
++ WSREP_WARN("RBR event apply failed, rolling back: %lld",
++ (long long) wsrep_thd_trx_seqno(thd));
++ trans_rollback(thd);
++ thd->locked_tables_list.unlock_locked_tables(thd);
++ /* Release transactional metadata locks. */
++ thd->mdl_context.release_transactional_locks();
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ DBUG_RETURN(WSREP_CB_FAILURE);
++ }
++
++ delete ev;
++ }
++
++ error:
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_query_state= QUERY_IDLE;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ assert(thd->wsrep_exec_mode== REPL_RECV);
++
++ if (thd->killed == THD::KILL_CONNECTION)
++ WSREP_INFO("applier aborted: %lld", (long long)wsrep_thd_trx_seqno(thd));
++
++ if (rcode) DBUG_RETURN(WSREP_CB_FAILURE);
++ DBUG_RETURN(WSREP_CB_SUCCESS);
++}
++
++wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
++ const void* const buf,
++ size_t const buf_len,
++ uint32_t const flags,
++ const wsrep_trx_meta_t* meta)
++{
++ THD* const thd((THD*)ctx);
++
++// Allow tests to block the applier thread using the DBUG facilities
++ DBUG_EXECUTE_IF("sync.wsrep_apply_cb",
++ {
++ const char act[]=
++ "now "
++ "wait_for signal.wsrep_apply_cb";
++ DBUG_ASSERT(!debug_sync_set_action(thd,
++ STRING_WITH_LEN(act)));
++ };);
++
++ thd->wsrep_trx_meta = *meta;
++
++#ifdef WSREP_PROC_INFO
++ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
++ "applying write set %lld: %p, %zu",
++ (long long)wsrep_thd_trx_seqno(thd), buf, buf_len);
++ thd_proc_info(thd, thd->wsrep_info);
++#else
++ thd_proc_info(thd, "applying write set");
++#endif /* WSREP_PROC_INFO */
++
++ /* tune FK and UK checking policy */
++ if (wsrep_slave_UK_checks == FALSE)
++ thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
++ else
++ thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
++
++ if (wsrep_slave_FK_checks == FALSE)
++ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
++ else
++ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
++
++ if (flags & WSREP_FLAG_ISOLATION)
++ {
++ thd->wsrep_apply_toi= true;
++ /*
++ Don't run in transaction mode with TOI actions.
++ */
++ thd->variables.option_bits&= ~OPTION_BEGIN;
++ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
++ }
++ wsrep_cb_status_t rcode(wsrep_apply_events(thd, buf, buf_len));
++
++#ifdef WSREP_PROC_INFO
++ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
++ "applied write set %lld", (long long)wsrep_thd_trx_seqno(thd));
++ thd_proc_info(thd, thd->wsrep_info);
++#else
++ thd_proc_info(thd, "applied write set");
++#endif /* WSREP_PROC_INFO */
++
++ if (WSREP_CB_SUCCESS != rcode)
++ {
++ wsrep_dump_rbr_buf(thd, buf, buf_len);
++ }
++
++ TABLE *tmp;
++ while ((tmp = thd->temporary_tables))
++ {
++ WSREP_DEBUG("Applier %lu, has temporary tables: %s.%s",
++ thd->thread_id,
++ (tmp->s) ? tmp->s->db.str : "void",
++ (tmp->s) ? tmp->s->table_name.str : "void");
++ close_temporary_table(thd, tmp, 1, 1);
++ }
++
++ return rcode;
++}
++
++static wsrep_cb_status_t wsrep_commit(THD* const thd,
++ wsrep_seqno_t const global_seqno)
++{
++#ifdef WSREP_PROC_INFO
++ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
++ "committing %lld", (long long)wsrep_thd_trx_seqno(thd));
++ thd_proc_info(thd, thd->wsrep_info);
++#else
++ thd_proc_info(thd, "committing");
++#endif /* WSREP_PROC_INFO */
++
++ wsrep_cb_status_t const rcode(trans_commit(thd) ?
++ WSREP_CB_FAILURE : WSREP_CB_SUCCESS);
++
++#ifdef WSREP_PROC_INFO
++ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
++ "committed %lld", (long long)wsrep_thd_trx_seqno(thd));
++ thd_proc_info(thd, thd->wsrep_info);
++#else
++ thd_proc_info(thd, "committed");
++#endif /* WSREP_PROC_INFO */
++
++ if (WSREP_CB_SUCCESS == rcode)
++ {
++ thd->wsrep_rli->cleanup_context(thd, 0);
++ thd->variables.gtid_next.set_automatic();
++ // TODO: mark snapshot with global_seqno.
++ }
++
++ return rcode;
++}
++
++static wsrep_cb_status_t wsrep_rollback(THD* const thd,
++ wsrep_seqno_t const global_seqno)
++{
++#ifdef WSREP_PROC_INFO
++ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
++ "rolling back %lld", (long long)wsrep_thd_trx_seqno(thd));
++ thd_proc_info(thd, thd->wsrep_info);
++#else
++ thd_proc_info(thd, "rolling back");
++#endif /* WSREP_PROC_INFO */
++
++ wsrep_cb_status_t const rcode(trans_rollback(thd) ?
++ WSREP_CB_FAILURE : WSREP_CB_SUCCESS);
++
++#ifdef WSREP_PROC_INFO
++ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
++ "rolled back %lld", (long long)wsrep_thd_trx_seqno(thd));
++ thd_proc_info(thd, thd->wsrep_info);
++#else
++ thd_proc_info(thd, "rolled back");
++#endif /* WSREP_PROC_INFO */
++ thd->wsrep_rli->cleanup_context(thd, 0);
++ thd->variables.gtid_next.set_automatic();
++
++ return rcode;
++}
++
++wsrep_cb_status_t wsrep_commit_cb(void* const ctx,
++ uint32_t const flags,
++ const wsrep_trx_meta_t* meta,
++ wsrep_bool_t* const exit,
++ bool const commit)
++{
++ THD* const thd((THD*)ctx);
++
++ assert(meta->gtid.seqno == wsrep_thd_trx_seqno(thd));
++
++ wsrep_cb_status_t rcode;
++
++ if (commit)
++ rcode = wsrep_commit(thd, meta->gtid.seqno);
++ else
++ rcode = wsrep_rollback(thd, meta->gtid.seqno);
++
++ wsrep_set_apply_format(thd, NULL);
++ thd->mdl_context.release_transactional_locks();
++ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
++ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
++
++ if (wsrep_slave_count_change < 0 && commit && WSREP_CB_SUCCESS == rcode)
++ {
++ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
++ if (wsrep_slave_count_change < 0)
++ {
++ wsrep_slave_count_change++;
++ *exit = true;
++ }
++ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
++ }
++
++ if (*exit == false && thd->wsrep_applier)
++ {
++ /* From trans_begin() */
++ thd->variables.option_bits|= OPTION_BEGIN;
++ thd->server_status|= SERVER_STATUS_IN_TRANS;
++ thd->wsrep_apply_toi= false;
++ }
++
++ return rcode;
++}
++
++
++wsrep_cb_status_t wsrep_unordered_cb(void* const ctx,
++ const void* const data,
++ size_t const size)
++{
++ return WSREP_CB_SUCCESS;
++}
+diff --git a/sql/wsrep_applier.h b/sql/wsrep_applier.h
+new file mode 100644
+index 0000000..816970d
+--- /dev/null
++++ b/sql/wsrep_applier.h
+@@ -0,0 +1,38 @@
++/* Copyright 2013 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#ifndef WSREP_APPLIER_H
++#define WSREP_APPLIER_H
++
++#include <sys/types.h>
++
++/* wsrep callback prototypes */
++
++wsrep_cb_status_t wsrep_apply_cb(void *ctx,
++ const void* buf, size_t buf_len,
++ uint32_t flags,
++ const wsrep_trx_meta_t* meta);
++
++wsrep_cb_status_t wsrep_commit_cb(void *ctx,
++ uint32_t flags,
++ const wsrep_trx_meta_t* meta,
++ wsrep_bool_t* exit,
++ bool commit);
++
++wsrep_cb_status_t wsrep_unordered_cb(void* ctx,
++ const void* data,
++ size_t size);
++
++#endif /* WSREP_APPLIER_H */
+diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
+new file mode 100644
+index 0000000..e33422f
+--- /dev/null
++++ b/sql/wsrep_binlog.cc
+@@ -0,0 +1,414 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#include "wsrep_binlog.h"
++#include "wsrep_priv.h"
++
++/*
++ Write the contents of a cache to a memory buffer.
++
++ This function quite the same as MYSQL_BIN_LOG::write_cache(),
++ with the exception that here we write in buffer instead of log file.
++ */
++int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
++{
++ *buf= NULL;
++ *buf_len= 0;
++
++ my_off_t const saved_pos(my_b_tell(cache));
++
++ if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
++ {
++ WSREP_ERROR("failed to initialize io-cache");
++ return ER_ERROR_ON_WRITE;
++ }
++
++ uint length = my_b_bytes_in_cache(cache);
++ if (unlikely(0 == length)) length = my_b_fill(cache);
++
++ size_t total_length = 0;
++
++ if (likely(length > 0)) do
++ {
++ total_length += length;
++ /*
++ Bail out if buffer grows too large.
++ A temporary fix to avoid allocating indefinitely large buffer,
++ not a real limit on a writeset size which includes other things
++ like header and keys.
++ */
++ if (total_length > wsrep_max_ws_size)
++ {
++ WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
++ wsrep_max_ws_size, total_length);
++ goto error;
++ }
++
++ uchar* tmp = (uchar *)my_realloc(*buf, total_length, MYF(0));
++ if (!tmp)
++ {
++ WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
++ *buf_len, length);
++ goto error;
++ }
++ *buf = tmp;
++
++ memcpy(*buf + *buf_len, cache->read_pos, length);
++ *buf_len = total_length;
++ cache->read_pos = cache->read_end;
++ } while ((cache->file >= 0) && (length = my_b_fill(cache)));
++
++ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
++ {
++ WSREP_WARN("failed to initialize io-cache");
++ goto cleanup;
++ }
++
++ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
++ {
++ WSREP_ERROR("failed to initialize io-cache");
++ goto cleanup;
++ }
++
++ return 0;
++
++error:
++ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
++ {
++ WSREP_WARN("failed to initialize io-cache");
++ }
++cleanup:
++ my_free(*buf);
++ *buf= NULL;
++ *buf_len= 0;
++ return ER_ERROR_ON_WRITE;
++}
++
++#define STACK_SIZE 4096 /* 4K - for buffer preallocated on the stack:
++ * many transactions would fit in there
++ * so there is no need to reach for the heap */
++
++/* Returns minimum multiple of HEAP_PAGE_SIZE that is >= length */
++static inline size_t
++heap_size(size_t length)
++{
++ return (length + HEAP_PAGE_SIZE - 1)/HEAP_PAGE_SIZE*HEAP_PAGE_SIZE;
++}
++
++/* append data to writeset */
++static inline wsrep_status_t
++wsrep_append_data(wsrep_t* const wsrep,
++ wsrep_ws_handle_t* const ws,
++ const void* const data,
++ size_t const len)
++{
++ struct wsrep_buf const buff = { data, len };
++ wsrep_status_t const rc(wsrep->append_data(wsrep, ws, &buff, 1,
++ WSREP_DATA_ORDERED, true));
++ if (rc != WSREP_OK)
++ {
++ WSREP_WARN("append_data() returned %d", rc);
++ }
++
++ return rc;
++}
++
++/*
++ Write the contents of a cache to wsrep provider.
++
++ This function quite the same as MYSQL_BIN_LOG::write_cache(),
++ with the exception that here we write in buffer instead of log file.
++
++ This version reads all of cache into single buffer and then appends to a
++ writeset at once.
++ */
++static int wsrep_write_cache_once(wsrep_t* const wsrep,
++ THD* const thd,
++ IO_CACHE* const cache,
++ size_t* const len)
++{
++ my_off_t const saved_pos(my_b_tell(cache));
++
++ if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
++ {
++ WSREP_ERROR("failed to initialize io-cache");
++ return ER_ERROR_ON_WRITE;
++ }
++
++ int err(WSREP_OK);
++
++ size_t total_length(0);
++ uchar stack_buf[STACK_SIZE]; /* to avoid dynamic allocations for few data*/
++ uchar* heap_buf(NULL);
++ uchar* buf(stack_buf);
++ size_t allocated(sizeof(stack_buf));
++ size_t used(0);
++
++ uint length(my_b_bytes_in_cache(cache));
++ if (unlikely(0 == length)) length = my_b_fill(cache);
++
++ if (likely(length > 0)) do
++ {
++ total_length += length;
++ /*
++ Bail out if buffer grows too large.
++ A temporary fix to avoid allocating indefinitely large buffer,
++ not a real limit on a writeset size which includes other things
++ like header and keys.
++ */
++ if (unlikely(total_length > wsrep_max_ws_size))
++ {
++ WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
++ wsrep_max_ws_size, total_length);
++ err = WSREP_TRX_SIZE_EXCEEDED;
++ goto cleanup;
++ }
++
++ if (total_length > allocated)
++ {
++ size_t const new_size(heap_size(total_length));
++ uchar* tmp = (uchar *)my_realloc(heap_buf, new_size, MYF(0));
++ if (!tmp)
++ {
++ WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
++ allocated, length);
++ err = WSREP_TRX_SIZE_EXCEEDED;
++ goto cleanup;
++ }
++
++ heap_buf = tmp;
++ buf = heap_buf;
++ allocated = new_size;
++
++ if (used <= STACK_SIZE && used > 0) // there's data in stack_buf
++ {
++ DBUG_ASSERT(buf == stack_buf);
++ memcpy(heap_buf, stack_buf, used);
++ }
++ }
++
++ memcpy(buf + used, cache->read_pos, length);
++ used = total_length;
++ cache->read_pos = cache->read_end;
++ } while ((cache->file >= 0) && (length = my_b_fill(cache)));
++
++ if (used > 0)
++ err = wsrep_append_data(wsrep, &thd->wsrep_ws_handle, buf, used);
++
++ if (WSREP_OK == err) *len = total_length;
++
++cleanup:
++ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
++ {
++ WSREP_ERROR("failed to reinitialize io-cache");
++ }
++
++ if (unlikely(WSREP_OK != err)) wsrep_dump_rbr_buf(thd, buf, used);
++
++ my_free(heap_buf);
++ return err;
++}
++
++/*
++ Write the contents of a cache to wsrep provider.
++
++ This function quite the same as MYSQL_BIN_LOG::write_cache(),
++ with the exception that here we write in buffer instead of log file.
++
++ This version uses incremental data appending as it reads it from cache.
++ */
++static int wsrep_write_cache_inc(wsrep_t* const wsrep,
++ THD* const thd,
++ IO_CACHE* const cache,
++ size_t* const len)
++{
++ my_off_t const saved_pos(my_b_tell(cache));
++
++ if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
++ {
++ WSREP_ERROR("failed to initialize io-cache");
++ return WSREP_TRX_ERROR;
++ }
++
++ int err(WSREP_OK);
++
++ size_t total_length(0);
++
++ uint length(my_b_bytes_in_cache(cache));
++ if (unlikely(0 == length)) length = my_b_fill(cache);
++
++ if (likely(length > 0)) do
++ {
++ total_length += length;
++ /* bail out if buffer grows too large
++ not a real limit on a writeset size which includes other things
++ like header and keys.
++ */
++ if (unlikely(total_length > wsrep_max_ws_size))
++ {
++ WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
++ wsrep_max_ws_size, total_length);
++ err = WSREP_TRX_SIZE_EXCEEDED;
++ goto cleanup;
++ }
++
++ if(WSREP_OK != (err=wsrep_append_data(wsrep, &thd->wsrep_ws_handle,
++ cache->read_pos, length)))
++ goto cleanup;
++
++ cache->read_pos = cache->read_end;
++ } while ((cache->file >= 0) && (length = my_b_fill(cache)));
++
++ if (WSREP_OK == err) *len = total_length;
++
++cleanup:
++ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
++ {
++ WSREP_ERROR("failed to reinitialize io-cache");
++ }
++
++ return err;
++}
++
++/*
++ Write the contents of a cache to wsrep provider.
++
++ This function quite the same as MYSQL_BIN_LOG::write_cache(),
++ with the exception that here we write in buffer instead of log file.
++ */
++int wsrep_write_cache(wsrep_t* const wsrep,
++ THD* const thd,
++ IO_CACHE* const cache,
++ size_t* const len)
++{
++ if (wsrep_incremental_data_collection) {
++ return wsrep_write_cache_inc(wsrep, thd, cache, len);
++ }
++ else {
++ return wsrep_write_cache_once(wsrep, thd, cache, len);
++ }
++}
++
++void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
++{
++ char filename[PATH_MAX]= {0};
++ int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
++ wsrep_data_home_dir, thd->thread_id,
++ (long long)wsrep_thd_trx_seqno(thd));
++ if (len >= PATH_MAX)
++ {
++ WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
++ return;
++ }
++
++ FILE *of= fopen(filename, "wb");
++ if (of)
++ {
++ fwrite (rbr_buf, buf_len, 1, of);
++ fclose(of);
++ }
++ else
++ {
++ WSREP_ERROR("Failed to open file '%s': %d (%s)",
++ filename, errno, strerror(errno));
++ }
++}
++
++void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
++{
++ char filename[PATH_MAX]= {0};
++ int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
++ wsrep_data_home_dir, thd->thread_id,
++ (long long)wsrep_thd_trx_seqno(thd));
++ size_t bytes_in_cache = 0;
++ // check path
++ if (len >= PATH_MAX)
++ {
++ WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
++ return ;
++ }
++ // init cache
++ my_off_t const saved_pos(my_b_tell(cache));
++ if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
++ {
++ WSREP_ERROR("failed to initialize io-cache");
++ return ;
++ }
++ // open file
++ FILE* of = fopen(filename, "wb");
++ if (!of)
++ {
++ WSREP_ERROR("Failed to open file '%s': %d (%s)",
++ filename, errno, strerror(errno));
++ goto cleanup;
++ }
++ // ready to write
++ bytes_in_cache= my_b_bytes_in_cache(cache);
++ if (unlikely(bytes_in_cache == 0)) bytes_in_cache = my_b_fill(cache);
++ if (likely(bytes_in_cache > 0)) do
++ {
++ if (my_fwrite(of, cache->read_pos, bytes_in_cache,
++ MYF(MY_WME | MY_NABP)) == (size_t) -1)
++ {
++ WSREP_ERROR("Failed to write file '%s'", filename);
++ goto cleanup;
++ }
++ cache->read_pos= cache->read_end;
++ } while ((cache->file >= 0) && (bytes_in_cache= my_b_fill(cache)));
++ if(cache->error == -1)
++ {
++ WSREP_ERROR("RBR inconsistent");
++ goto cleanup;
++ }
++cleanup:
++ // init back
++ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
++ {
++ WSREP_ERROR("failed to reinitialize io-cache");
++ }
++ // close file
++ if (of) fclose(of);
++}
++
++extern handlerton *binlog_hton;
++
++/*
++ wsrep exploits binlog's caches even if binlogging itself is not
++ activated. In such case connection close needs calling
++ actual binlog's method.
++ Todo: split binlog hton from its caches to use ones by wsrep
++ without referring to binlog's stuff.
++*/
++int wsrep_binlog_close_connection(THD* thd)
++{
++ DBUG_ENTER("wsrep_binlog_close_connection");
++ if (thd_get_ha_data(thd, binlog_hton) != NULL)
++ binlog_hton->close_connection (binlog_hton, thd);
++ DBUG_RETURN(0);
++}
++
++int wsrep_binlog_savepoint_set(THD *thd, void *sv)
++{
++ if (!wsrep_emulate_bin_log) return 0;
++ int rcode = binlog_hton->savepoint_set(binlog_hton, thd, sv);
++ return rcode;
++}
++
++int wsrep_binlog_savepoint_rollback(THD *thd, void *sv)
++{
++ if (!wsrep_emulate_bin_log) return 0;
++ int rcode = binlog_hton->savepoint_rollback(binlog_hton, thd, sv);
++ return rcode;
++}
+diff --git a/sql/wsrep_binlog.h b/sql/wsrep_binlog.h
+new file mode 100644
+index 0000000..a7b680f
+--- /dev/null
++++ b/sql/wsrep_binlog.h
+@@ -0,0 +1,56 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#ifndef WSREP_BINLOG_H
++#define WSREP_BINLOG_H
++
++#include "sql_class.h" // THD, IO_CACHE
++
++#define HEAP_PAGE_SIZE 65536 /* 64K */
++#define WSREP_MAX_WS_SIZE (0xFFFFFFFFUL - HEAP_PAGE_SIZE)
++
++/*
++ Write the contents of a cache to a memory buffer.
++
++ This function quite the same as MYSQL_BIN_LOG::write_cache(),
++ with the exception that here we write in buffer instead of log file.
++ */
++int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len);
++
++/*
++ Write the contents of a cache to wsrep provider.
++
++ This function quite the same as MYSQL_BIN_LOG::write_cache(),
++ with the exception that here we write in buffer instead of log file.
++
++ @param len total amount of data written
++ @return wsrep error status
++ */
++int wsrep_write_cache (wsrep_t* wsrep,
++ THD* thd,
++ IO_CACHE* cache,
++ size_t* len);
++
++/* Dump replication buffer to disk */
++void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len);
++
++/* Dump replication buffer to disk without intermediate buffer */
++void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache);
++
++int wsrep_binlog_close_connection(THD* thd);
++int wsrep_binlog_savepoint_set(THD *thd, void *sv);
++int wsrep_binlog_savepoint_rollback(THD *thd, void *sv);
++
++#endif /* WSREP_BINLOG_H */
+diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc
+new file mode 100644
+index 0000000..5ec18c7
+--- /dev/null
++++ b/sql/wsrep_check_opts.cc
+@@ -0,0 +1,379 @@
++/* Copyright 2011 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++//#include <mysqld.h>
++#include <sql_class.h>
++//#include <sql_plugin.h>
++//#include <set_var.h>
++
++#include "wsrep_mysqld.h"
++
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <ctype.h>
++
++/* This file is about checking for correctness of mysql configuration options */
++
++struct opt
++{
++ const char* const name;
++ const char* value;
++};
++
++/* A list of options to check.
++ * At first we assume default values and then see if they are changed on CLI or
++ * in my.cnf */
++static struct opt opts[] =
++{
++ { "wsrep_slave_threads", "1" }, // mysqld.cc
++ { "bind_address", "0.0.0.0" }, // mysqld.cc
++ { "wsrep_sst_method", "rsync" }, // mysqld.cc
++ { "wsrep_sst_receive_address","AUTO"}, // mysqld.cc
++ { "binlog_format", "ROW" }, // mysqld.cc
++ { "wsrep_provider", "none" }, // mysqld.cc
++ { "query_cache_type", "0" }, // mysqld.cc
++ { "query_cache_size", "0" }, // mysqld.cc
++ { "locked_in_memory", "0" }, // mysqld.cc
++ { "wsrep_cluster_address", "0" }, // mysqld.cc
++ { "locks_unsafe_for_binlog", "0" }, // ha_innodb.cc
++ { "autoinc_lock_mode", "1" }, // ha_innodb.cc
++ { 0, 0 }
++};
++
++enum
++{
++ WSREP_SLAVE_THREADS,
++ BIND_ADDRESS,
++ WSREP_SST_METHOD,
++ WSREP_SST_RECEIVE_ADDRESS,
++ BINLOG_FORMAT,
++ WSREP_PROVIDER,
++ QUERY_CACHE_TYPE,
++ QUERY_CACHE_SIZE,
++ LOCKED_IN_MEMORY,
++ WSREP_CLUSTER_ADDRESS,
++ LOCKS_UNSAFE_FOR_BINLOG,
++ AUTOINC_LOCK_MODE
++};
++
++
++/* A class to make a copy of argv[] vector */
++struct argv_copy
++{
++ int const argc_;
++ char** argv_;
++
++ argv_copy (int const argc, const char* const argv[]) :
++ argc_ (argc),
++ argv_ (reinterpret_cast<char**>(calloc(argc_, sizeof(char*))))
++ {
++ if (argv_)
++ {
++ for (int i = 0; i < argc_; ++i)
++ {
++ argv_[i] = strdup(argv[i]);
++
++ if (!argv_[i])
++ {
++ argv_free (); // free whatever bee allocated
++ return;
++ }
++ }
++ }
++ }
++
++ ~argv_copy () { argv_free (); }
++
++private:
++ argv_copy (const argv_copy&);
++ argv_copy& operator= (const argv_copy&);
++
++ void argv_free()
++ {
++ if (argv_)
++ {
++ for (int i = 0; (i < argc_) && argv_[i] ; ++i) free (argv_[i]);
++ free (argv_);
++ argv_ = 0;
++ }
++ }
++};
++
++/* a short corresponding to '--' byte sequence */
++static short const long_opt_prefix ('-' + ('-' << 8));
++
++/* Normalizes long options to have '_' instead of '-' */
++static int
++normalize_opts (argv_copy& a)
++{
++ if (a.argv_)
++ {
++ for (int i = 0; i < a.argc_; ++i)
++ {
++ char* ptr = a.argv_[i];
++ if (long_opt_prefix == *(short*)ptr) // long option
++ {
++ ptr += 2;
++ const char* end = strchr(ptr, '=');
++
++ if (!end) end = ptr + strlen(ptr);
++
++ for (; ptr != end; ++ptr) if ('-' == *ptr) *ptr = '_';
++ }
++ }
++
++ return 0;
++ }
++
++ return EINVAL;
++}
++
++/* Find required options in the argument list and change their values */
++static int
++find_opts (argv_copy& a, struct opt* const opts)
++{
++ for (int i = 0; i < a.argc_; ++i)
++ {
++ char* ptr = a.argv_[i] + 2; // we're interested only in long options
++
++ struct opt* opt = opts;
++ for (; 0 != opt->name; ++opt)
++ {
++ if (!strstr(ptr, opt->name)) continue; // try next option
++
++ /* 1. try to find value after the '=' */
++ opt->value = strchr(ptr, '=') + 1;
++
++ /* 2. if no '=', try next element in the argument vector */
++ if (reinterpret_cast<void*>(1) == opt->value)
++ {
++ /* also check that the next element is not an option itself */
++ if (i + 1 < a.argc_ && *(a.argv_[i + 1]) != '-')
++ {
++ ++i;
++ opt->value = a.argv_[i];
++ }
++ else opt->value = ""; // no value supplied (like boolean opt)
++ }
++
++ break; // option found, break inner loop
++ }
++ }
++
++ return 0;
++}
++
++/* Parses string for an integer. Returns 0 on success. */
++int get_long_long (const struct opt& opt, long long* const val, int const base)
++{
++ const char* const str = opt.value;
++
++ if ('\0' != *str)
++ {
++ char* endptr;
++
++ *val = strtoll (str, &endptr, base);
++
++ if ('k' == *endptr || 'K' == *endptr)
++ {
++ *val *= 1024L;
++ endptr++;
++ }
++ else if ('m' == *endptr || 'M' == *endptr)
++ {
++ *val *= 1024L * 1024L;
++ endptr++;
++ }
++ else if ('g' == *endptr || 'G' == *endptr)
++ {
++ *val *= 1024L * 1024L * 1024L;
++ endptr++;
++ }
++
++ if ('\0' == *endptr) return 0; // the whole string was a valid integer
++ }
++
++ WSREP_ERROR ("Bad value for *%s: '%s'. Should be integer.",
++ opt.name, opt.value);
++
++ return EINVAL;
++}
++
++/* This is flimzy coz hell knows how mysql interprets boolean strings...
++ * and, no, I'm not going to become versed in how mysql handles options -
++ * I'd rather sing.
++
++ Aha, http://dev.mysql.com/doc/refman/5.1/en/dynamic-system-variables.html:
++ Variables that have a type of “boolean” can be set to 0, 1, ON or OFF. (If you
++ set them on the command line or in an option file, use the numeric values.)
++
++ So it is '0' for FALSE, '1' or empty string for TRUE
++
++ */
++int get_bool (const struct opt& opt, bool* const val)
++{
++ const char* str = opt.value;
++
++ while (isspace(*str)) ++str; // skip initial whitespaces
++
++ ssize_t str_len = strlen(str);
++ switch (str_len)
++ {
++ case 0:
++ *val = true;
++ return 0;
++ case 1:
++ if ('0' == *str || '1' == *str)
++ {
++ *val = ('1' == *str);
++ return 0;
++ }
++ }
++
++ WSREP_ERROR ("Bad value for *%s: '%s'. Should be '0', '1' or empty string.",
++ opt.name, opt.value);
++
++ return EINVAL;
++}
++
++static int
++check_opts (int const argc, const char* const argv[], struct opt opts[])
++{
++ /* First, make a copy of argv to be able to manipulate it */
++ argv_copy a(argc, argv);
++
++ if (!a.argv_)
++ {
++ WSREP_ERROR ("Could not copy argv vector: not enough memory.");
++ return ENOMEM;
++ }
++
++ int err = normalize_opts (a);
++ if (err)
++ {
++ WSREP_ERROR ("Failed to normalize options.");
++ return err;
++ }
++
++ err = find_opts (a, opts);
++ if (err)
++ {
++ WSREP_ERROR ("Failed to parse options.");
++ return err;
++ }
++
++ /* At this point we have updated default values in our option list to
++ what has been specified on the command line / my.cnf */
++
++ long long slave_threads;
++ err = get_long_long (opts[WSREP_SLAVE_THREADS], &slave_threads, 10);
++ if (err) return err;
++
++ int rcode = 0;
++
++ if (slave_threads > 1)
++ /* Need to check AUTOINC_LOCK_MODE and LOCKS_UNSAFE_FOR_BINLOG */
++ {
++ long long autoinc_lock_mode;
++ err = get_long_long (opts[AUTOINC_LOCK_MODE], &autoinc_lock_mode, 10);
++ if (err) return err;
++
++ bool locks_unsafe_for_binlog;
++ err = get_bool (opts[LOCKS_UNSAFE_FOR_BINLOG],&locks_unsafe_for_binlog);
++ if (err) return err;
++
++ if (autoinc_lock_mode != 2)
++ {
++ WSREP_ERROR ("Parallel applying (wsrep_slave_threads > 1) requires"
++ " innodb_autoinc_lock_mode = 2.");
++ rcode = EINVAL;
++ }
++ }
++
++ bool locked_in_memory;
++ err = get_bool (opts[LOCKED_IN_MEMORY], &locked_in_memory);
++ if (err) { WSREP_ERROR("get_bool error: %s", strerror(err)); return err; }
++ if (locked_in_memory)
++ {
++ WSREP_ERROR ("Memory locking is not supported (locked_in_memory=%s)",
++ locked_in_memory ? "ON" : "OFF");
++ rcode = EINVAL;
++ }
++
++ if (!strcasecmp(opts[WSREP_SST_METHOD].value,"mysqldump"))
++ {
++ if (!strcasecmp(opts[BIND_ADDRESS].value, "127.0.0.1") ||
++ !strcasecmp(opts[BIND_ADDRESS].value, "localhost"))
++ {
++ WSREP_ERROR ("wsrep_sst_method is set to 'mysqldump' yet "
++ "mysqld bind_address is set to '%s', which makes it "
++ "impossible to receive state transfer from another "
++ "node, since mysqld won't accept such connections. "
++ "If you wish to use mysqldump state transfer method, "
++ "set bind_address to allow mysql client connections "
++ "from other cluster members (e.g. 0.0.0.0).",
++ opts[BIND_ADDRESS].value);
++ rcode = EINVAL;
++ }
++ }
++ else
++ {
++ // non-mysqldump SST requires wsrep_cluster_address on startup
++ if (strlen(opts[WSREP_CLUSTER_ADDRESS].value) == 0)
++ {
++ WSREP_ERROR ("%s SST method requires wsrep_cluster_address to be "
++ "configured on startup.",opts[WSREP_SST_METHOD].value);
++ rcode = EINVAL;
++ }
++ }
++
++ if (strcasecmp(opts[WSREP_SST_RECEIVE_ADDRESS].value, "AUTO"))
++ {
++ if (!strncasecmp(opts[WSREP_SST_RECEIVE_ADDRESS].value,
++ "127.0.0.1", strlen("127.0.0.1")) ||
++ !strncasecmp(opts[WSREP_SST_RECEIVE_ADDRESS].value,
++ "localhost", strlen("localhost")))
++ {
++ WSREP_WARN ("wsrep_sst_receive_address is set to '%s' which "
++ "makes it impossible for another host to reach this "
++ "one. Please set it to the address which this node "
++ "can be connected at by other cluster members.",
++ opts[WSREP_SST_RECEIVE_ADDRESS].value);
++// rcode = EINVAL;
++ }
++ }
++
++ if (strcasecmp(opts[WSREP_PROVIDER].value, "none"))
++ {
++ if (strcasecmp(opts[BINLOG_FORMAT].value, "ROW"))
++ {
++ WSREP_ERROR ("Only binlog_format = 'ROW' is currently supported. "
++ "Configured value: '%s'. Please adjust your "
++ "configuration.", opts[BINLOG_FORMAT].value);
++
++ rcode = EINVAL;
++ }
++ }
++
++ return rcode;
++}
++
++int
++wsrep_check_opts (int const argc, char* const* const argv)
++{
++ return check_opts (argc, argv, opts);
++}
++
+diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc
+new file mode 100644
+index 0000000..e89858c
+--- /dev/null
++++ b/sql/wsrep_hton.cc
+@@ -0,0 +1,590 @@
++/* Copyright 2008 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include <mysqld.h>
++#include "sql_base.h"
++#include "binlog.h"
++#include "rpl_filter.h"
++#include <sql_class.h>
++#include "wsrep_mysqld.h"
++#include "wsrep_binlog.h"
++#include <cstdio>
++#include <cstdlib>
++
++extern ulonglong thd_to_trx_id(THD *thd);
++
++extern "C" int thd_binlog_format(const MYSQL_THD thd);
++// todo: share interface with ha_innodb.c
++
++enum wsrep_trx_status wsrep_run_wsrep_commit(THD *thd, handlerton *hton,
++ bool all);
++
++/*
++ Cleanup after local transaction commit/rollback, replay or TOI.
++*/
++void wsrep_cleanup_transaction(THD *thd)
++{
++ if (wsrep_emulate_bin_log) thd_binlog_trx_reset(thd);
++ thd->wsrep_ws_handle.trx_id= WSREP_UNDEFINED_TRX_ID;
++ thd->wsrep_trx_meta.gtid= WSREP_GTID_UNDEFINED;
++ thd->wsrep_trx_meta.depends_on= WSREP_SEQNO_UNDEFINED;
++ thd->wsrep_exec_mode= LOCAL_STATE;
++ return;
++}
++
++/*
++ wsrep hton
++*/
++handlerton *wsrep_hton;
++
++
++/*
++ Registers wsrep hton at commit time if transaction has registered htons
++ for supported engine types.
++
++ Hton should not be registered for TOTAL_ORDER operations.
++
++ Registration is needed for both LOCAL_MODE and REPL_RECV transactions to run
++ commit in 2pc so that wsrep position gets properly recorded in storage
++ engines.
++
++ Note that all hton calls should immediately return for threads that are
++ in REPL_RECV mode as their states are controlled by wsrep appliers or
++ replaying code. Only threads in LOCAL_MODE should run wsrep callbacks
++ from hton methods.
++*/
++void wsrep_register_hton(THD* thd, bool all)
++{
++ if (thd->wsrep_exec_mode != TOTAL_ORDER && !thd->wsrep_apply_toi)
++ {
++ THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
++ for (Ha_trx_info *i= trans->ha_list; WSREP(thd) && i; i = i->next())
++ {
++ if (i->ht()->db_type == DB_TYPE_INNODB)
++ {
++ trans_register_ha(thd, all, wsrep_hton);
++
++ /* follow innodb read/write settting
++ * but, as an exception: CTAS with empty result set will not be
++ * replicated unless we declare wsrep hton as read/write here
++ */
++ if (i->is_trx_read_write() ||
++ (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
++ thd->wsrep_exec_mode == LOCAL_STATE))
++ {
++ thd->ha_data[wsrep_hton->slot].ha_info[all].set_trx_read_write();
++ }
++ break;
++ }
++ }
++ }
++}
++
++/*
++ Calls wsrep->post_commit() for locally executed transactions that have
++ got seqno from provider (must commit) and don't require replaying.
++ */
++void wsrep_post_commit(THD* thd, bool all)
++{
++ switch (thd->wsrep_exec_mode)
++ {
++ case LOCAL_COMMIT:
++ {
++ DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
++ if (wsrep->post_commit(wsrep, &thd->wsrep_ws_handle))
++ {
++ DBUG_PRINT("wsrep", ("set committed fail"));
++ WSREP_WARN("set committed fail: %llu %d",
++ (long long)thd->real_id, thd->get_stmt_da()->status());
++ }
++ wsrep_cleanup_transaction(thd);
++ break;
++ }
++ case LOCAL_STATE:
++ {
++ /* non-InnoDB statements may have populated events in stmt cache
++ => cleanup
++ */
++ WSREP_DEBUG("cleanup transaction for LOCAL_STATE: %s", thd->query());
++ wsrep_cleanup_transaction(thd);
++ break;
++ }
++ default: break;
++ }
++
++}
++
++/*
++ wsrep exploits binlog's caches even if binlogging itself is not
++ activated. In such case connection close needs calling
++ actual binlog's method.
++ Todo: split binlog hton from its caches to use ones by wsrep
++ without referring to binlog's stuff.
++*/
++static int
++wsrep_close_connection(handlerton* hton, THD* thd)
++{
++ DBUG_ENTER("wsrep_close_connection");
++
++ if (thd->wsrep_exec_mode == REPL_RECV)
++ {
++ DBUG_RETURN(0);
++ }
++ DBUG_RETURN(wsrep_binlog_close_connection (thd));
++}
++
++/*
++ prepare/wsrep_run_wsrep_commit can fail in two ways
++ - certification test or an equivalent. As a result,
++ the current transaction just rolls back
++ Error codes:
++ WSREP_TRX_CERT_FAIL, WSREP_TRX_SIZE_EXCEEDED, WSREP_TRX_ERROR
++ - a post-certification failure makes this server unable to
++ commit its own WS and therefore the server must abort
++*/
++static int wsrep_prepare(handlerton *hton, THD *thd, bool all)
++{
++ DBUG_ENTER("wsrep_prepare");
++
++ if (thd->wsrep_exec_mode == REPL_RECV)
++ {
++ DBUG_RETURN(0);
++ }
++
++ DBUG_ASSERT(thd->ha_data[wsrep_hton->slot].ha_info[all].is_trx_read_write());
++ DBUG_ASSERT(thd->wsrep_exec_mode == LOCAL_STATE);
++ DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno == WSREP_SEQNO_UNDEFINED);
++
++ if ((all ||
++ !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
++ (thd->variables.wsrep_on && !wsrep_trans_cache_is_empty(thd)))
++ {
++ DBUG_RETURN (wsrep_run_wsrep_commit(thd, hton, all));
++ }
++ DBUG_RETURN(0);
++}
++
++static int wsrep_savepoint_set(handlerton *hton, THD *thd, void *sv)
++{
++ DBUG_ENTER("wsrep_savepoint_set");
++
++ if (thd->wsrep_exec_mode == REPL_RECV)
++ {
++ DBUG_RETURN(0);
++ }
++
++ if (!wsrep_emulate_bin_log) DBUG_RETURN(0);
++ int rcode = wsrep_binlog_savepoint_set(thd, sv);
++ DBUG_RETURN(rcode);
++}
++
++static int wsrep_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
++{
++ DBUG_ENTER("wsrep_savepoint_rollback");
++
++ if (thd->wsrep_exec_mode == REPL_RECV)
++ {
++ DBUG_RETURN(0);
++ }
++
++ if (!wsrep_emulate_bin_log) DBUG_RETURN(0);
++ int rcode = wsrep_binlog_savepoint_rollback(thd, sv);
++ DBUG_RETURN(rcode);
++}
++
++static int wsrep_rollback(handlerton *hton, THD *thd, bool all)
++{
++ DBUG_ENTER("wsrep_rollback");
++
++ if (thd->wsrep_exec_mode == REPL_RECV)
++ {
++ DBUG_RETURN(0);
++ }
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ switch (thd->wsrep_exec_mode)
++ {
++ case TOTAL_ORDER:
++ case REPL_RECV:
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ WSREP_DEBUG("Avoiding wsrep rollback for failed DDL: %s", thd->query());
++ DBUG_RETURN(0);
++ default: break;
++ }
++
++ if ((all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
++ (thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY))
++ {
++ if (wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
++ {
++ DBUG_PRINT("wsrep", ("setting rollback fail"));
++ WSREP_ERROR("settting rollback fail: thd: %llu SQL: %s",
++ (long long)thd->real_id, thd->query());
++ }
++ wsrep_cleanup_transaction(thd);
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ DBUG_RETURN(0);
++}
++
++int wsrep_commit(handlerton *hton, THD *thd, bool all)
++{
++ DBUG_ENTER("wsrep_commit");
++
++ if (thd->wsrep_exec_mode == REPL_RECV)
++ {
++ DBUG_RETURN(0);
++ }
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if ((all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
++ (thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY))
++ {
++ if (thd->wsrep_exec_mode == LOCAL_COMMIT)
++ {
++ DBUG_ASSERT(thd->ha_data[wsrep_hton->slot].ha_info[all].is_trx_read_write());
++ /*
++ Call to wsrep->post_commit() (moved to wsrep_post_commit()) must
++ be done only after commit has done for all involved htons.
++ */
++ DBUG_PRINT("wsrep", ("commit"));
++ }
++ else
++ {
++ /*
++ Transaction didn't go through wsrep->pre_commit() so just roll back
++ possible changes to clean state.
++ */
++ if (wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
++ {
++ DBUG_PRINT("wsrep", ("setting rollback fail"));
++ WSREP_ERROR("settting rollback fail: thd: %llu SQL: %s",
++ (long long)thd->real_id, thd->query());
++ }
++ wsrep_cleanup_transaction(thd);
++ }
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ DBUG_RETURN(0);
++}
++
++
++extern Rpl_filter* binlog_filter;
++extern my_bool opt_log_slave_updates;
++
++enum wsrep_trx_status
++wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
++{
++ int rcode= -1;
++ size_t data_len= 0;
++ IO_CACHE *cache;
++ int replay_round= 0;
++
++ if (thd->get_stmt_da()->is_error()) {
++ WSREP_ERROR("commit issue, error: %d %s",
++ thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
++ }
++
++ DBUG_ENTER("wsrep_run_wsrep_commit");
++
++ if (thd->slave_thread && !opt_log_slave_updates) DBUG_RETURN(WSREP_TRX_OK);
++
++ if (thd->wsrep_exec_mode == REPL_RECV) {
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if (thd->wsrep_conflict_state == MUST_ABORT) {
++ if (wsrep_debug)
++ WSREP_INFO("WSREP: must abort for BF");
++ DBUG_PRINT("wsrep", ("BF apply commit fail"));
++ thd->wsrep_conflict_state = NO_CONFLICT;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ //
++ // TODO: test all calls of the rollback.
++ // rollback must happen automagically innobase_rollback(hton, thd, 1);
++ //
++ DBUG_RETURN(WSREP_TRX_ERROR);
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++
++ if (thd->wsrep_exec_mode != LOCAL_STATE) DBUG_RETURN(WSREP_TRX_OK);
++
++ if (thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING) {
++ WSREP_DEBUG("commit for consistency check: %s", thd->query());
++ DBUG_RETURN(WSREP_TRX_OK);
++ }
++
++ DBUG_PRINT("wsrep", ("replicating commit"));
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ if (thd->wsrep_conflict_state == MUST_ABORT) {
++ DBUG_PRINT("wsrep", ("replicate commit fail"));
++ thd->wsrep_conflict_state = ABORTED;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ if (wsrep_debug) {
++ WSREP_INFO("innobase_commit, abort %s",
++ (thd->query()) ? thd->query() : "void");
++ }
++ DBUG_RETURN(WSREP_TRX_CERT_FAIL);
++ }
++
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++
++ while (wsrep_replaying > 0 &&
++ thd->wsrep_conflict_state == NO_CONFLICT &&
++ thd->killed == THD::NOT_KILLED &&
++ !shutdown_in_progress)
++ {
++
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ mysql_mutex_lock(&thd->mysys_var->mutex);
++ thd_proc_info(thd, "wsrep waiting on replaying");
++ thd->mysys_var->current_mutex= &LOCK_wsrep_replaying;
++ thd->mysys_var->current_cond= &COND_wsrep_replaying;
++ mysql_mutex_unlock(&thd->mysys_var->mutex);
++
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ // Using timedwait is a hack to avoid deadlock in case if BF victim
++ // misses the signal.
++ struct timespec wtime = {0, 1000000};
++ mysql_cond_timedwait(&COND_wsrep_replaying, &LOCK_wsrep_replaying,
++ &wtime);
++
++ if (replay_round++ % 100000 == 0)
++ WSREP_DEBUG("commit waiting for replaying: replayers %d, thd: (%lu) "
++ "conflict: %d (round: %d)",
++ wsrep_replaying, thd->thread_id,
++ thd->wsrep_conflict_state, replay_round);
++
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++
++ mysql_mutex_lock(&thd->mysys_var->mutex);
++ thd->mysys_var->current_mutex= 0;
++ thd->mysys_var->current_cond= 0;
++ mysql_mutex_unlock(&thd->mysys_var->mutex);
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ }
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++
++ if (thd->wsrep_conflict_state == MUST_ABORT) {
++ DBUG_PRINT("wsrep", ("replicate commit fail"));
++ thd->wsrep_conflict_state = ABORTED;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ WSREP_DEBUG("innobase_commit abort after replaying wait %s",
++ (thd->query()) ? thd->query() : "void");
++ DBUG_RETURN(WSREP_TRX_CERT_FAIL);
++ }
++
++ thd->wsrep_query_state = QUERY_COMMITTING;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ cache = get_trans_log(thd);
++ rcode = 0;
++ if (cache) {
++ thd->binlog_flush_pending_rows_event(true);
++ rcode = wsrep_write_cache(wsrep, thd, cache, &data_len);
++ if (WSREP_OK != rcode) {
++ WSREP_ERROR("rbr write fail, data_len: %zu, %d", data_len, rcode);
++ DBUG_RETURN(WSREP_TRX_SIZE_EXCEEDED);
++ }
++ }
++
++ if (data_len == 0)
++ {
++ if (thd->get_stmt_da()->is_ok() &&
++ thd->get_stmt_da()->affected_rows() > 0 &&
++ !binlog_filter->is_on())
++ {
++ WSREP_DEBUG("empty rbr buffer, query: %s, "
++ "affected rows: %llu, "
++ "changed tables: %d, "
++ "sql_log_bin: %d, "
++ "wsrep status (%d %d %d)",
++ thd->query(), thd->get_stmt_da()->affected_rows(),
++ stmt_has_updated_trans_table(thd), thd->variables.sql_log_bin,
++ thd->wsrep_exec_mode, thd->wsrep_query_state,
++ thd->wsrep_conflict_state);
++ }
++ else
++ {
++ WSREP_DEBUG("empty rbr buffer, query: %s", thd->query());
++ }
++ thd->wsrep_query_state= QUERY_EXEC;
++ DBUG_RETURN(WSREP_TRX_OK);
++ }
++
++ if (WSREP_UNDEFINED_TRX_ID == thd->wsrep_ws_handle.trx_id)
++ {
++ WSREP_WARN("SQL statement was ineffective, THD: %lu, buf: %zu\n"
++ "QUERY: %s\n"
++ " => Skipping replication",
++ thd->thread_id, data_len, thd->query());
++ rcode = WSREP_TRX_FAIL;
++ }
++ else if (!rcode)
++ {
++ if (WSREP_OK == rcode)
++ rcode = wsrep->pre_commit(wsrep,
++ (wsrep_conn_id_t)thd->thread_id,
++ &thd->wsrep_ws_handle,
++ WSREP_FLAG_COMMIT |
++ ((thd->wsrep_PA_safe) ?
++ 0ULL : WSREP_FLAG_PA_UNSAFE),
++ &thd->wsrep_trx_meta);
++
++ if (rcode == WSREP_TRX_MISSING) {
++ WSREP_WARN("Transaction missing in provider, thd: %ld, SQL: %s",
++ thd->thread_id, thd->query());
++ rcode = WSREP_TRX_FAIL;
++ } else if (rcode == WSREP_BF_ABORT) {
++ WSREP_DEBUG("thd %lu seqno %lld BF aborted by provider, will replay",
++ thd->thread_id, (long long)thd->wsrep_trx_meta.gtid.seqno);
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_conflict_state = MUST_REPLAY;
++ DBUG_ASSERT(wsrep_thd_trx_seqno(thd) > 0);
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ wsrep_replaying++;
++ WSREP_DEBUG("replaying increased: %d, thd: %lu",
++ wsrep_replaying, thd->thread_id);
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++ }
++ } else {
++ WSREP_ERROR("I/O error reading from thd's binlog iocache: "
++ "errno=%d, io cache code=%d", my_errno, cache->error);
++ DBUG_ASSERT(0); // failure like this can not normally happen
++ DBUG_RETURN(WSREP_TRX_ERROR);
++ }
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ switch(rcode) {
++ case 0:
++ /*
++ About MUST_ABORT: We assume that even if thd conflict state was set
++ to MUST_ABORT, underlying transaction was not rolled back or marked
++ as deadlock victim in QUERY_COMMITTING state. Conflict state is
++ set to NO_CONFLICT and commit proceeds as usual.
++ */
++ if (thd->wsrep_conflict_state == MUST_ABORT)
++ thd->wsrep_conflict_state= NO_CONFLICT;
++
++ if (thd->wsrep_conflict_state != NO_CONFLICT)
++ {
++ WSREP_WARN("thd %lu seqno %lld: conflict state %d after post commit",
++ thd->thread_id,
++ (long long)thd->wsrep_trx_meta.gtid.seqno,
++ thd->wsrep_conflict_state);
++ }
++ thd->wsrep_exec_mode= LOCAL_COMMIT;
++ DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
++ /* Override XID iff it was generated by mysql */
++ if (thd->transaction.xid_state.xid.get_my_xid())
++ {
++ wsrep_xid_init(&thd->transaction.xid_state.xid,
++ &thd->wsrep_trx_meta.gtid.uuid,
++ thd->wsrep_trx_meta.gtid.seqno);
++ }
++ DBUG_PRINT("wsrep", ("replicating commit success"));
++ break;
++ case WSREP_BF_ABORT:
++ DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
++ case WSREP_TRX_FAIL:
++ WSREP_DEBUG("commit failed for reason: %d %lu %s", rcode, thd->thread_id, thd->query());
++ DBUG_PRINT("wsrep", ("replicating commit fail"));
++
++ thd->wsrep_query_state= QUERY_EXEC;
++
++ if (thd->wsrep_conflict_state == MUST_ABORT) {
++ thd->wsrep_conflict_state= ABORTED;
++ }
++ else
++ {
++ WSREP_DEBUG("conflict state: %d", thd->wsrep_conflict_state);
++ if (thd->wsrep_conflict_state == NO_CONFLICT)
++ {
++ thd->wsrep_conflict_state = CERT_FAILURE;
++ WSREP_LOG_CONFLICT(NULL, thd, FALSE);
++ }
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ DBUG_RETURN(WSREP_TRX_CERT_FAIL);
++
++ case WSREP_SIZE_EXCEEDED:
++ WSREP_ERROR("transaction size exceeded");
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ DBUG_RETURN(WSREP_TRX_SIZE_EXCEEDED);
++ case WSREP_CONN_FAIL:
++ WSREP_ERROR("connection failure");
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ DBUG_RETURN(WSREP_TRX_ERROR);
++ default:
++ WSREP_ERROR("unknown connection failure");
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ DBUG_RETURN(WSREP_TRX_ERROR);
++ }
++
++ thd->wsrep_query_state= QUERY_EXEC;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ DBUG_RETURN(WSREP_TRX_OK);
++}
++
++
++static int wsrep_hton_init(void *p)
++{
++ wsrep_hton= (handlerton *)p;
++ //wsrep_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
++ wsrep_hton->state= SHOW_OPTION_YES;
++ wsrep_hton->db_type=DB_TYPE_WSREP;
++ wsrep_hton->savepoint_offset= sizeof(my_off_t);
++ wsrep_hton->close_connection= wsrep_close_connection;
++ wsrep_hton->savepoint_set= wsrep_savepoint_set;
++ wsrep_hton->savepoint_rollback= wsrep_savepoint_rollback;
++ wsrep_hton->commit= wsrep_commit;
++ wsrep_hton->rollback= wsrep_rollback;
++ wsrep_hton->prepare= wsrep_prepare;
++ wsrep_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN; // todo: fix flags
++ wsrep_hton->slot= 0;
++ return 0;
++}
++
++
++struct st_mysql_storage_engine wsrep_storage_engine=
++{ MYSQL_HANDLERTON_INTERFACE_VERSION };
++
++
++mysql_declare_plugin(wsrep)
++{
++ MYSQL_STORAGE_ENGINE_PLUGIN,
++ &wsrep_storage_engine,
++ "wsrep",
++ "Codership Oy",
++ "A pseudo storage engine to represent transactions in multi-master "
++ "synchornous replication",
++ PLUGIN_LICENSE_GPL,
++ wsrep_hton_init, /* Plugin Init */
++ NULL, /* Plugin Deinit */
++ 0x0100 /* 1.0 */,
++ NULL, /* status variables */
++ NULL, /* system variables */
++ NULL, /* config options */
++ 0, /* flags */
++}
++mysql_declare_plugin_end;
+diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
+new file mode 100644
+index 0000000..e2816c4
+--- /dev/null
++++ b/sql/wsrep_mysqld.cc
+@@ -0,0 +1,1564 @@
++/* Copyright 2008-2013 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include <mysqld.h>
++#include <sql_class.h>
++#include <sql_parse.h>
++#include "wsrep_priv.h"
++#include "wsrep_thd.h"
++#include "wsrep_sst.h"
++#include "wsrep_utils.h"
++#include "wsrep_var.h"
++#include "wsrep_binlog.h"
++#include "wsrep_applier.h"
++#include <cstdio>
++#include <cstdlib>
++#include "log_event.h"
++#include <rpl_slave.h>
++
++wsrep_t *wsrep = NULL;
++my_bool wsrep_emulate_bin_log = FALSE; // activating parts of binlog interface
++/* Sidno in global_sid_map corresponding to group uuid */
++rpl_sidno wsrep_sidno= -1;
++my_bool wsrep_preordered_opt= FALSE;
++
++/*
++ * Begin configuration options and their default values
++ */
++
++const char* wsrep_data_home_dir = NULL;
++const char* wsrep_dbug_option = "";
++
++long wsrep_slave_threads = 1; // # of slave action appliers wanted
++int wsrep_slave_count_change = 0; // # of appliers to stop or start
++my_bool wsrep_debug = 0; // enable debug level logging
++my_bool wsrep_convert_LOCK_to_trx = 1; // convert locking sessions to trx
++ulong wsrep_retry_autocommit = 5; // retry aborted autocommit trx
++my_bool wsrep_auto_increment_control = 1; // control auto increment variables
++my_bool wsrep_drupal_282555_workaround = 1; // retry autoinc insert after dupkey
++my_bool wsrep_incremental_data_collection = 0; // incremental data collection
++ulong wsrep_max_ws_size = 1073741824UL;//max ws (RBR buffer) size
++ulong wsrep_max_ws_rows = 65536; // max number of rows in ws
++int wsrep_to_isolation = 0; // # of active TO isolation threads
++my_bool wsrep_certify_nonPK = 1; // certify, even when no primary key
++long wsrep_max_protocol_version = 3; // maximum protocol version to use
++ulong wsrep_forced_binlog_format = BINLOG_FORMAT_UNSPEC;
++my_bool wsrep_recovery = 0; // recovery
++my_bool wsrep_replicate_myisam = 0; // enable myisam replication
++my_bool wsrep_log_conflicts = 0;
++ulong wsrep_mysql_replication_bundle = 0;
++my_bool wsrep_desync = 0; // desynchronize the node from the
++ // cluster
++my_bool wsrep_load_data_splitting = 1; // commit load data every 10K intervals
++my_bool wsrep_restart_slave = 0; // should mysql slave thread be
++ // restarted, if node joins back
++my_bool wsrep_restart_slave_activated = 0; // node has dropped, and slave
++ // restart will be needed
++my_bool wsrep_slave_UK_checks = 0; // slave thread does UK checks
++my_bool wsrep_slave_FK_checks = 0; // slave thread does FK checks
++/*
++ * End configuration options
++ */
++
++static const wsrep_uuid_t cluster_uuid = WSREP_UUID_UNDEFINED;
++static char cluster_uuid_str[40]= { 0, };
++static const char* cluster_status_str[WSREP_VIEW_MAX] =
++{
++ "Primary",
++ "non-Primary",
++ "Disconnected"
++};
++
++static char provider_name[256]= { 0, };
++static char provider_version[256]= { 0, };
++static char provider_vendor[256]= { 0, };
++
++/*
++ * wsrep status variables
++ */
++my_bool wsrep_connected = FALSE;
++my_bool wsrep_ready = FALSE; // node can accept queries
++const char* wsrep_cluster_state_uuid = cluster_uuid_str;
++long long wsrep_cluster_conf_id = WSREP_SEQNO_UNDEFINED;
++const char* wsrep_cluster_status = cluster_status_str[WSREP_VIEW_DISCONNECTED];
++long wsrep_cluster_size = 0;
++long wsrep_local_index = -1;
++long long wsrep_local_bf_aborts = 0;
++const char* wsrep_provider_name = provider_name;
++const char* wsrep_provider_version = provider_version;
++const char* wsrep_provider_vendor = provider_vendor;
++/* End wsrep status variables */
++
++wsrep_uuid_t local_uuid = WSREP_UUID_UNDEFINED;
++wsrep_seqno_t local_seqno = WSREP_SEQNO_UNDEFINED;
++wsp::node_status local_status;
++long wsrep_protocol_version = 3;
++
++// Boolean denoting if server is in initial startup phase. This is needed
++// to make sure that main thread waiting in wsrep_sst_wait() is signaled
++// if there was no state gap on receiving first view event.
++static my_bool wsrep_startup = TRUE;
++
++
++static void wsrep_log_cb(wsrep_log_level_t level, const char *msg) {
++ switch (level) {
++ case WSREP_LOG_INFO:
++ sql_print_information("WSREP: %s", msg);
++ break;
++ case WSREP_LOG_WARN:
++ sql_print_warning("WSREP: %s", msg);
++ break;
++ case WSREP_LOG_ERROR:
++ case WSREP_LOG_FATAL:
++ sql_print_error("WSREP: %s", msg);
++ break;
++ case WSREP_LOG_DEBUG:
++ if (wsrep_debug) sql_print_information ("[Debug] WSREP: %s", msg);
++ default:
++ break;
++ }
++}
++
++static void wsrep_log_states (wsrep_log_level_t const level,
++ const wsrep_uuid_t* const group_uuid,
++ wsrep_seqno_t const group_seqno,
++ const wsrep_uuid_t* const node_uuid,
++ wsrep_seqno_t const node_seqno)
++{
++ char uuid_str[37];
++ char msg[256];
++
++ wsrep_uuid_print (group_uuid, uuid_str, sizeof(uuid_str));
++ snprintf (msg, 255, "WSREP: Group state: %s:%lld",
++ uuid_str, (long long)group_seqno);
++ wsrep_log_cb (level, msg);
++
++ wsrep_uuid_print (node_uuid, uuid_str, sizeof(uuid_str));
++ snprintf (msg, 255, "WSREP: Local state: %s:%lld",
++ uuid_str, (long long)node_seqno);
++ wsrep_log_cb (level, msg);
++}
++
++static my_bool set_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
++{
++ XID* xid= reinterpret_cast<XID*>(arg);
++ handlerton* hton= plugin_data(plugin, handlerton *);
++ if (hton->db_type == DB_TYPE_INNODB)
++ {
++ const wsrep_uuid_t* uuid(wsrep_xid_uuid(xid));
++ char uuid_str[40] = {0, };
++ wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
++ WSREP_DEBUG("Set WSREPXid for InnoDB: %s:%lld",
++ uuid_str, (long long)wsrep_xid_seqno(xid));
++ hton->wsrep_set_checkpoint(hton, xid);
++ }
++ return FALSE;
++}
++
++void wsrep_set_SE_checkpoint(XID* xid)
++{
++ plugin_foreach(NULL, set_SE_checkpoint, MYSQL_STORAGE_ENGINE_PLUGIN, xid);
++}
++
++static my_bool get_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
++{
++ XID* xid= reinterpret_cast<XID*>(arg);
++ handlerton* hton= plugin_data(plugin, handlerton *);
++ if (hton->db_type == DB_TYPE_INNODB)
++ {
++ hton->wsrep_get_checkpoint(hton, xid);
++ const wsrep_uuid_t* uuid(wsrep_xid_uuid(xid));
++ char uuid_str[40] = {0, };
++ wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
++ WSREP_DEBUG("Read WSREPXid from InnoDB: %s:%lld",
++ uuid_str, (long long)wsrep_xid_seqno(xid));
++
++ }
++ return FALSE;
++}
++
++void wsrep_get_SE_checkpoint(XID* xid)
++{
++ plugin_foreach(NULL, get_SE_checkpoint, MYSQL_STORAGE_ENGINE_PLUGIN, xid);
++}
++
++void wsrep_init_sidno(const wsrep_uuid_t& uuid)
++{
++ /* generate new Sid map entry from inverted uuid */
++ rpl_sid sid;
++ wsrep_uuid_t ltid_uuid;
++ for (size_t i= 0; i < sizeof(ltid_uuid.data); ++i)
++ {
++ ltid_uuid.data[i] = ~local_uuid.data[i];
++ }
++ sid.copy_from(ltid_uuid.data);
++ global_sid_lock->wrlock();
++ wsrep_sidno= global_sid_map->add_sid(sid);
++ WSREP_INFO("inited wsrep sidno %d", wsrep_sidno);
++ global_sid_lock->unlock();
++}
++
++
++static wsrep_cb_status_t
++wsrep_view_handler_cb (void* app_ctx,
++ void* recv_ctx,
++ const wsrep_view_info_t* view,
++ const char* state,
++ size_t state_len,
++ void** sst_req,
++ size_t* sst_req_len)
++{
++ *sst_req = NULL;
++ *sst_req_len = 0;
++
++ wsrep_member_status_t new_status= local_status.get();
++
++ if (memcmp(&cluster_uuid, &view->state_id.uuid, sizeof(wsrep_uuid_t)))
++ {
++ memcpy((wsrep_uuid_t*)&cluster_uuid, &view->state_id.uuid,
++ sizeof(cluster_uuid));
++
++ wsrep_uuid_print (&cluster_uuid, cluster_uuid_str,
++ sizeof(cluster_uuid_str));
++ }
++
++ wsrep_cluster_conf_id= view->view;
++ wsrep_cluster_status= cluster_status_str[view->status];
++ wsrep_cluster_size= view->memb_num;
++ wsrep_local_index= view->my_idx;
++
++ WSREP_INFO("New cluster view: global state: %s:%lld, view# %lld: %s, "
++ "number of nodes: %ld, my index: %ld, protocol version %d",
++ wsrep_cluster_state_uuid, (long long)view->state_id.seqno,
++ (long long)wsrep_cluster_conf_id, wsrep_cluster_status,
++ wsrep_cluster_size, wsrep_local_index, view->proto_ver);
++
++ /* Proceed further only if view is PRIMARY */
++ if (WSREP_VIEW_PRIMARY != view->status) {
++ wsrep_ready_set(FALSE);
++ new_status= WSREP_MEMBER_UNDEFINED;
++ /* Always record local_uuid and local_seqno in non-prim since this
++ * may lead to re-initializing provider and start position is
++ * determined according to these variables */
++ // WRONG! local_uuid should be the last primary configuration uuid we were
++ // a member of. local_seqno should be updated in commit calls.
++ // local_uuid= cluster_uuid;
++ // local_seqno= view->first - 1;
++ goto out;
++ }
++
++ switch (view->proto_ver)
++ {
++ case 0:
++ case 1:
++ case 2:
++ case 3:
++ // version change
++ if (view->proto_ver != wsrep_protocol_version)
++ {
++ my_bool wsrep_ready_saved= wsrep_ready;
++ wsrep_ready_set(FALSE);
++ WSREP_INFO("closing client connections for "
++ "protocol change %ld -> %d",
++ wsrep_protocol_version, view->proto_ver);
++ wsrep_close_client_connections(TRUE);
++ wsrep_protocol_version= view->proto_ver;
++ wsrep_ready_set(wsrep_ready_saved);
++ }
++ break;
++ default:
++ WSREP_ERROR("Unsupported application protocol version: %d",
++ view->proto_ver);
++ unireg_abort(1);
++ }
++
++ if (view->state_gap)
++ {
++ WSREP_WARN("Gap in state sequence. Need state transfer.");
++
++ /* After that wsrep will call wsrep_sst_prepare. */
++ /* keep ready flag 0 until we receive the snapshot */
++ wsrep_ready_set(FALSE);
++
++ /* Close client connections to ensure that they don't interfere
++ * with SST. Necessary only if storage engines are initialized
++ * before SST.
++ * TODO: Just killing all ongoing transactions should be enough
++ * since wsrep_ready is OFF and no new transactions can start.
++ */
++ if (!wsrep_before_SE())
++ {
++ WSREP_DEBUG("[debug]: closing client connections for PRIM");
++ wsrep_close_client_connections(TRUE);
++ }
++
++ ssize_t const req_len= wsrep_sst_prepare (sst_req);
++
++ if (req_len < 0)
++ {
++ WSREP_ERROR("SST preparation failed: %zd (%s)", -req_len,
++ strerror(-req_len));
++ new_status= WSREP_MEMBER_UNDEFINED;
++ }
++ else
++ {
++ assert(sst_req != NULL);
++ *sst_req_len= req_len;
++ new_status= WSREP_MEMBER_JOINER;
++ }
++ }
++ else
++ {
++ /*
++ * NOTE: Initialize wsrep_group_uuid here only if it wasn't initialized
++ * before - OR - it was reinitilized on startup (lp:992840)
++ */
++ if (wsrep_startup)
++ {
++ if (wsrep_before_SE())
++ {
++ wsrep_SE_init_grab();
++ // Signal mysqld init thread to continue
++ wsrep_sst_complete (&cluster_uuid, view->state_id.seqno, false);
++ // and wait for SE initialization
++ wsrep_SE_init_wait();
++ }
++ else
++ {
++ local_uuid= cluster_uuid;
++ local_seqno= view->state_id.seqno;
++ }
++ /* Init storage engine XIDs from first view */
++ XID xid;
++ wsrep_xid_init(&xid, &local_uuid, local_seqno);
++ wsrep_set_SE_checkpoint(&xid);
++ new_status= WSREP_MEMBER_JOINED;
++ wsrep_init_sidno(local_uuid);
++ }
++
++ // just some sanity check
++ if (memcmp (&local_uuid, &cluster_uuid, sizeof (wsrep_uuid_t)))
++ {
++ WSREP_ERROR("Undetected state gap. Can't continue.");
++ wsrep_log_states(WSREP_LOG_FATAL, &cluster_uuid, view->state_id.seqno,
++ &local_uuid, -1);
++ unireg_abort(1);
++ }
++ }
++
++ if (wsrep_auto_increment_control)
++ {
++ global_system_variables.auto_increment_offset= view->my_idx + 1;
++ global_system_variables.auto_increment_increment= view->memb_num;
++ }
++
++ { /* capabilities may be updated on new configuration */
++ uint64_t const caps(wsrep->capabilities (wsrep));
++
++ my_bool const idc((caps & WSREP_CAP_INCREMENTAL_WRITESET) != 0);
++ if (TRUE == wsrep_incremental_data_collection && FALSE == idc)
++ {
++ WSREP_WARN("Unsupported protocol downgrade: "
++ "incremental data collection disabled. Expect abort.");
++ }
++ wsrep_incremental_data_collection = idc;
++ }
++
++out:
++ if (view->status == WSREP_VIEW_PRIMARY) wsrep_startup= FALSE;
++ local_status.set(new_status, view);
++
++ return WSREP_CB_SUCCESS;
++}
++
++void wsrep_ready_set (my_bool x)
++{
++ WSREP_DEBUG("Setting wsrep_ready to %d", x);
++ if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort();
++ if (wsrep_ready != x)
++ {
++ wsrep_ready= x;
++ mysql_cond_signal (&COND_wsrep_ready);
++ }
++ mysql_mutex_unlock (&LOCK_wsrep_ready);
++}
++
++// Wait until wsrep has reached ready state
++void wsrep_ready_wait ()
++{
++ if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort();
++ while (!wsrep_ready)
++ {
++ WSREP_INFO("Waiting to reach ready state");
++ mysql_cond_wait (&COND_wsrep_ready, &LOCK_wsrep_ready);
++ }
++ WSREP_INFO("ready state reached");
++ mysql_mutex_unlock (&LOCK_wsrep_ready);
++}
++
++static void wsrep_synced_cb(void* app_ctx)
++{
++ WSREP_INFO("Synchronized with group, ready for connections");
++ bool signal_main= false;
++ if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort();
++ if (!wsrep_ready)
++ {
++ wsrep_ready= TRUE;
++ mysql_cond_signal (&COND_wsrep_ready);
++ signal_main= true;
++
++ }
++ local_status.set(WSREP_MEMBER_SYNCED);
++ mysql_mutex_unlock (&LOCK_wsrep_ready);
++
++ if (signal_main)
++ {
++ wsrep_SE_init_grab();
++ // Signal mysqld init thread to continue
++ wsrep_sst_complete (&local_uuid, local_seqno, false);
++ // and wait for SE initialization
++ wsrep_SE_init_wait();
++ }
++ if (wsrep_restart_slave_activated)
++ {
++ int rcode;
++ WSREP_INFO("MySQL slave restart");
++ wsrep_restart_slave_activated= FALSE;
++
++ mysql_mutex_lock(&LOCK_active_mi);
++ if ((rcode = start_slave_threads(1 /* need mutex */,
++ 0 /* no wait for start*/,
++ active_mi,
++ SLAVE_SQL)))
++ {
++ WSREP_WARN("Failed to create slave threads: %d", rcode);
++ }
++ mysql_mutex_unlock(&LOCK_active_mi);
++
++ }
++}
++
++static void wsrep_init_position()
++{
++ /* read XIDs from storage engines */
++ XID xid;
++ memset(&xid, 0, sizeof(xid));
++ xid.formatID= -1;
++ wsrep_get_SE_checkpoint(&xid);
++
++ if (xid.formatID == -1)
++ {
++ WSREP_INFO("Read nil XID from storage engines, skipping position init");
++ return;
++ }
++ else if (!wsrep_is_wsrep_xid(&xid))
++ {
++ WSREP_WARN("Read non-wsrep XID from storage engines, skipping position init");
++ return;
++ }
++
++ const wsrep_uuid_t* uuid= wsrep_xid_uuid(&xid);
++ const wsrep_seqno_t seqno= wsrep_xid_seqno(&xid);
++
++ char uuid_str[40] = {0, };
++ wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
++ WSREP_INFO("Initial position: %s:%lld", uuid_str, (long long)seqno);
++
++
++ if (!memcmp(&local_uuid, &WSREP_UUID_UNDEFINED, sizeof(local_uuid)) &&
++ local_seqno == WSREP_SEQNO_UNDEFINED)
++ {
++ // Initial state
++ local_uuid= *uuid;
++ local_seqno= seqno;
++ }
++ else if (memcmp(&local_uuid, uuid, sizeof(local_uuid)) ||
++ local_seqno != seqno)
++ {
++ WSREP_WARN("Initial position was provided by configuration or SST, "
++ "avoiding override");
++ }
++}
++
++int wsrep_init()
++{
++ int rcode= -1;
++
++ wsrep_ready_set(FALSE);
++ assert(wsrep_provider);
++
++ wsrep_init_position();
++
++ if ((rcode= wsrep_load(wsrep_provider, &wsrep, wsrep_log_cb)) != WSREP_OK)
++ {
++ if (strcasecmp(wsrep_provider, WSREP_NONE))
++ {
++ WSREP_ERROR("wsrep_load(%s) failed: %s (%d). Reverting to no provider.",
++ wsrep_provider, strerror(rcode), rcode);
++ strcpy((char*)wsrep_provider, WSREP_NONE); // damn it's a dirty hack
++ (void) wsrep_init();
++ return rcode;
++ }
++ else /* this is for recursive call above */
++ {
++ WSREP_ERROR("Could not revert to no provider: %s (%d). Need to abort.",
++ strerror(rcode), rcode);
++ unireg_abort(1);
++ }
++ }
++
++ if (strlen(wsrep_provider)== 0 ||
++ !strcmp(wsrep_provider, WSREP_NONE))
++ {
++ // enable normal operation in case no provider is specified
++ wsrep_ready_set(TRUE);
++ global_system_variables.wsrep_on = 0;
++ wsrep_init_args args;
++ args.logger_cb = wsrep_log_cb;
++ args.options = (wsrep_provider_options) ?
++ wsrep_provider_options : "";
++ rcode = wsrep->init(wsrep, &args);
++ if (rcode)
++ {
++ DBUG_PRINT("wsrep",("wsrep::init() failed: %d", rcode));
++ WSREP_ERROR("wsrep::init() failed: %d, must shutdown", rcode);
++ wsrep->free(wsrep);
++ free(wsrep);
++ wsrep = NULL;
++ }
++ return rcode;
++ }
++ else
++ {
++ global_system_variables.wsrep_on = 1;
++ strncpy(provider_name,
++ wsrep->provider_name, sizeof(provider_name) - 1);
++ strncpy(provider_version,
++ wsrep->provider_version, sizeof(provider_version) - 1);
++ strncpy(provider_vendor,
++ wsrep->provider_vendor, sizeof(provider_vendor) - 1);
++ }
++
++ if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0)
++ wsrep_data_home_dir = mysql_real_data_home;
++
++ char node_addr[512]= { 0, };
++ size_t const node_addr_max= sizeof(node_addr) - 1;
++ if (!wsrep_node_address || !strcmp(wsrep_node_address, ""))
++ {
++ size_t const ret= wsrep_guess_ip(node_addr, node_addr_max);
++ if (!(ret > 0 && ret < node_addr_max))
++ {
++ WSREP_WARN("Failed to guess base node address. Set it explicitly via "
++ "wsrep_node_address.");
++ node_addr[0]= '\0';
++ }
++ }
++ else
++ {
++ strncpy(node_addr, wsrep_node_address, node_addr_max);
++ }
++
++ char inc_addr[512]= { 0, };
++ size_t const inc_addr_max= sizeof (inc_addr);
++ if ((!wsrep_node_incoming_address ||
++ !strcmp (wsrep_node_incoming_address, WSREP_NODE_INCOMING_AUTO)))
++ {
++ unsigned int my_bind_ip= INADDR_ANY; // default if not set
++ if (my_bind_addr_str && strlen(my_bind_addr_str))
++ {
++ my_bind_ip= wsrep_check_ip(my_bind_addr_str);
++ }
++
++ if (INADDR_ANY != my_bind_ip)
++ {
++ if (INADDR_NONE != my_bind_ip && INADDR_LOOPBACK != my_bind_ip)
++ {
++ snprintf(inc_addr, inc_addr_max, "%s:%u",
++ my_bind_addr_str, (int)mysqld_port);
++ } // else leave inc_addr an empty string - mysqld is not listening for
++ // client connections on network interfaces.
++ }
++ else // mysqld binds to 0.0.0.0, take IP from wsrep_node_address if possible
++ {
++ size_t const node_addr_len= strlen(node_addr);
++ if (node_addr_len > 0)
++ {
++ const char* const colon= strrchr(node_addr, ':');
++ if (strchr(node_addr, ':') == colon) // 1 or 0 ':'
++ {
++ size_t const ip_len= colon ? colon - node_addr : node_addr_len;
++ if (ip_len + 7 /* :55555\0 */ < inc_addr_max)
++ {
++ memcpy (inc_addr, node_addr, ip_len);
++ snprintf(inc_addr + ip_len, inc_addr_max - ip_len, ":%u",
++ (int)mysqld_port);
++ }
++ else
++ {
++ WSREP_WARN("Guessing address for incoming client connections: "
++ "address too long.");
++ inc_addr[0]= '\0';
++ }
++ }
++ else
++ {
++ WSREP_WARN("Guessing address for incoming client connections: "
++ "too many colons :) .");
++ inc_addr[0]= '\0';
++ }
++ }
++
++ if (!strlen(inc_addr))
++ {
++ WSREP_WARN("Guessing address for incoming client connections failed. "
++ "Try setting wsrep_node_incoming_address explicitly.");
++ }
++ }
++ }
++ else if (!strchr(wsrep_node_incoming_address, ':')) // no port included
++ {
++ if ((int)inc_addr_max <=
++ snprintf(inc_addr, inc_addr_max, "%s:%u",
++ wsrep_node_incoming_address,(int)mysqld_port))
++ {
++ WSREP_WARN("Guessing address for incoming client connections: "
++ "address too long.");
++ inc_addr[0]= '\0';
++ }
++ }
++ else
++ {
++ size_t const need = strlen (wsrep_node_incoming_address);
++ if (need >= inc_addr_max) {
++ WSREP_WARN("wsrep_node_incoming_address too long: %zu", need);
++ inc_addr[0]= '\0';
++ }
++ else {
++ memcpy (inc_addr, wsrep_node_incoming_address, need);
++ }
++ }
++
++ struct wsrep_init_args wsrep_args;
++
++ struct wsrep_gtid const state_id = { local_uuid, local_seqno };
++
++ wsrep_args.data_dir = wsrep_data_home_dir;
++ wsrep_args.node_name = (wsrep_node_name) ? wsrep_node_name : "";
++ wsrep_args.node_address = node_addr;
++ wsrep_args.node_incoming = inc_addr;
++ wsrep_args.options = (wsrep_provider_options) ?
++ wsrep_provider_options : "";
++ wsrep_args.proto_ver = wsrep_max_protocol_version;
++
++ wsrep_args.state_id = &state_id;
++
++ wsrep_args.logger_cb = wsrep_log_cb;
++ wsrep_args.view_handler_cb = wsrep_view_handler_cb;
++ wsrep_args.apply_cb = wsrep_apply_cb;
++ wsrep_args.commit_cb = wsrep_commit_cb;
++ wsrep_args.unordered_cb = wsrep_unordered_cb;
++ wsrep_args.sst_donate_cb = wsrep_sst_donate_cb;
++ wsrep_args.synced_cb = wsrep_synced_cb;
++
++ rcode = wsrep->init(wsrep, &wsrep_args);
++
++ if (rcode)
++ {
++ DBUG_PRINT("wsrep",("wsrep::init() failed: %d", rcode));
++ WSREP_ERROR("wsrep::init() failed: %d, must shutdown", rcode);
++ wsrep->free(wsrep);
++ free(wsrep);
++ wsrep = NULL;
++ }
++
++ return rcode;
++}
++
++extern int wsrep_on(void *);
++
++void wsrep_init_startup (bool first)
++{
++ if (wsrep_init()) unireg_abort(1);
++
++ wsrep_thr_lock_init(wsrep_thd_is_BF, wsrep_abort_thd,
++ wsrep_debug, wsrep_convert_LOCK_to_trx, wsrep_on);
++
++ /* Skip replication start if no cluster address */
++ if (!wsrep_cluster_address || strlen(wsrep_cluster_address) == 0) return;
++
++ if (first) wsrep_sst_grab(); // do it so we can wait for SST below
++
++ if (!wsrep_start_replication()) unireg_abort(1);
++
++ wsrep_create_rollbacker();
++ wsrep_create_appliers(1);
++
++ if (first && !wsrep_sst_wait()) unireg_abort(1);// wait until SST is completed
++}
++
++
++void wsrep_deinit()
++{
++ wsrep_unload(wsrep);
++ wsrep= 0;
++ provider_name[0]= '\0';
++ provider_version[0]= '\0';
++ provider_vendor[0]= '\0';
++}
++
++void wsrep_recover()
++{
++ if (!memcmp(&local_uuid, &WSREP_UUID_UNDEFINED, sizeof(wsrep_uuid_t)) &&
++ local_seqno == -2)
++ {
++ char uuid_str[40];
++ wsrep_uuid_print(&local_uuid, uuid_str, sizeof(uuid_str));
++ WSREP_INFO("Position %s:%lld given at startup, skipping position recovery",
++ uuid_str, (long long)local_seqno);
++ return;
++ }
++ XID xid;
++ memset(&xid, 0, sizeof(xid));
++ xid.formatID= -1;
++ wsrep_get_SE_checkpoint(&xid);
++ char uuid_str[40];
++ wsrep_uuid_print(wsrep_xid_uuid(&xid), uuid_str, sizeof(uuid_str));
++ WSREP_INFO("Recovered position: %s:%lld", uuid_str,
++ (long long)wsrep_xid_seqno(&xid));
++}
++
++
++void wsrep_stop_replication(THD *thd)
++{
++ WSREP_INFO("Stop replication");
++ if (!wsrep)
++ {
++ WSREP_INFO("Provider was not loaded, in stop replication");
++ return;
++ }
++
++ /* disconnect from group first to get wsrep_ready == FALSE */
++ WSREP_DEBUG("Provider disconnect");
++ wsrep->disconnect(wsrep);
++
++ wsrep_connected= FALSE;
++
++ wsrep_close_client_connections(TRUE);
++
++ /* wait until appliers have stopped */
++ wsrep_wait_appliers_close(thd);
++
++ return;
++}
++
++/* This one is set to true when --wsrep-new-cluster is found in the command
++ * line arguments */
++static my_bool wsrep_new_cluster= FALSE;
++#define WSREP_NEW_CLUSTER "--wsrep-new-cluster"
++/* Finds and hides --wsrep-new-cluster from the arguments list
++ * by moving it to the end of the list and decrementing argument count */
++void wsrep_filter_new_cluster (int* argc, char* argv[])
++{
++ int i;
++ for (i= *argc - 1; i > 0; i--)
++ {
++ /* make a copy of the argument to convert possible underscores to hyphens.
++ * the copy need not to be longer than WSREP_NEW_CLUSTER option */
++ char arg[sizeof(WSREP_NEW_CLUSTER) + 1]= { 0, };
++ strncpy(arg, argv[i], sizeof(arg) - 1);
++ char* underscore(arg);
++ while (NULL != (underscore= strchr(underscore, '_'))) *underscore= '-';
++
++ if (!strcmp(arg, WSREP_NEW_CLUSTER))
++ {
++ wsrep_new_cluster= TRUE;
++ *argc -= 1;
++ /* preserve the order of remaining arguments AND
++ * preserve the original argument pointers - just in case */
++ char* wnc= argv[i];
++ memmove(&argv[i], &argv[i + 1], (*argc - i)*sizeof(argv[i]));
++ argv[*argc]= wnc; /* this will be invisible to the rest of the program */
++ }
++ }
++}
++
++bool wsrep_start_replication()
++{
++ wsrep_status_t rcode;
++
++ /*
++ if provider is trivial, don't even try to connect,
++ but resume local node operation
++ */
++ if (strlen(wsrep_provider)== 0 ||
++ !strcmp(wsrep_provider, WSREP_NONE))
++ {
++ // enable normal operation in case no provider is specified
++ wsrep_ready_set(TRUE);
++ return true;
++ }
++
++ if (!wsrep_cluster_address || strlen(wsrep_cluster_address)== 0)
++ {
++ // if provider is non-trivial, but no address is specified, wait for address
++ wsrep_ready_set(FALSE);
++ return true;
++ }
++
++ bool const bootstrap(TRUE == wsrep_new_cluster);
++ wsrep_new_cluster= FALSE;
++
++ WSREP_INFO("Start replication");
++
++ if ((rcode = wsrep->connect(wsrep,
++ wsrep_cluster_name,
++ wsrep_cluster_address,
++ wsrep_sst_donor,
++ bootstrap)))
++ {
++ if (-ESOCKTNOSUPPORT == rcode)
++ {
++ DBUG_PRINT("wsrep",("unrecognized cluster address: '%s', rcode: %d",
++ wsrep_cluster_address, rcode));
++ WSREP_ERROR("unrecognized cluster address: '%s', rcode: %d",
++ wsrep_cluster_address, rcode);
++ }
++ else
++ {
++ DBUG_PRINT("wsrep",("wsrep->connect() failed: %d", rcode));
++ WSREP_ERROR("wsrep::connect() failed: %d", rcode);
++ }
++
++ return false;
++ }
++ else
++ {
++ wsrep_connected= TRUE;
++
++ char* opts= wsrep->options_get(wsrep);
++ if (opts)
++ {
++ wsrep_provider_options_init(opts);
++ free(opts);
++ }
++ else
++ {
++ WSREP_WARN("Failed to get wsrep options");
++ }
++ }
++
++ return true;
++}
++
++bool wsrep_sync_wait (THD* thd, uint mask)
++{
++ if ((thd->variables.wsrep_sync_wait & mask) &&
++ thd->variables.wsrep_on &&
++ !thd->in_active_multi_stmt_transaction() &&
++ thd->wsrep_conflict_state != REPLAYING)
++ {
++ WSREP_DEBUG("wsrep_sync_wait: thd->variables.wsrep_sync_wait = %u, mask = %u",
++ thd->variables.wsrep_sync_wait, mask);
++ // This allows autocommit SELECTs and a first SELECT after SET AUTOCOMMIT=0
++ // TODO: modify to check if thd has locked any rows.
++ wsrep_gtid_t gtid;
++ wsrep_status_t ret= wsrep->causal_read (wsrep, >id);
++
++ if (unlikely(WSREP_OK != ret))
++ {
++ const char* msg;
++ int err;
++
++ // Possibly relevant error codes:
++ // ER_CHECKREAD, ER_ERROR_ON_READ, ER_INVALID_DEFAULT, ER_EMPTY_QUERY,
++ // ER_FUNCTION_NOT_DEFINED, ER_NOT_ALLOWED_COMMAND, ER_NOT_SUPPORTED_YET,
++ // ER_FEATURE_DISABLED, ER_QUERY_INTERRUPTED
++
++ switch (ret)
++ {
++ case WSREP_NOT_IMPLEMENTED:
++ msg= "synchronous reads by wsrep backend. "
++ "Please unset wsrep_causal_reads variable.";
++ err= ER_NOT_SUPPORTED_YET;
++ break;
++ default:
++ msg= "Synchronous wait failed.";
++ err= ER_LOCK_WAIT_TIMEOUT; // NOTE: the above msg won't be displayed
++ // with ER_LOCK_WAIT_TIMEOUT
++ }
++
++ my_error(err, MYF(0), msg);
++
++ return true;
++ }
++ }
++
++ return false;
++}
++
++/*
++ * Helpers to deal with TOI key arrays
++ */
++typedef struct wsrep_key_arr
++{
++ wsrep_key_t* keys;
++ size_t keys_len;
++} wsrep_key_arr_t;
++
++
++static void wsrep_keys_free(wsrep_key_arr_t* key_arr)
++{
++ for (size_t i= 0; i < key_arr->keys_len; ++i)
++ {
++ my_free((void*)key_arr->keys[i].key_parts);
++ }
++ my_free(key_arr->keys);
++ key_arr->keys= 0;
++ key_arr->keys_len= 0;
++}
++
++
++/*!
++ * @param db Database string
++ * @param table Table string
++ * @param key Array of wsrep_key_t
++ * @param key_len In: number of elements in key array, Out: number of
++ * elements populated
++ *
++ * @return true if preparation was successful, otherwise false.
++ */
++
++static bool wsrep_prepare_key_for_isolation(const char* db,
++ const char* table,
++ wsrep_buf_t* key,
++ size_t* key_len)
++{
++ if (*key_len < 2) return false;
++
++ switch (wsrep_protocol_version)
++ {
++ case 0:
++ *key_len= 0;
++ break;
++ case 1:
++ case 2:
++ case 3:
++ {
++ *key_len= 0;
++ if (db)
++ {
++ // sql_print_information("%s.%s", db, table);
++ if (db)
++ {
++ key[*key_len].ptr= db;
++ key[*key_len].len= strlen(db);
++ ++(*key_len);
++ if (table)
++ {
++ key[*key_len].ptr= table;
++ key[*key_len].len= strlen(table);
++ ++(*key_len);
++ }
++ }
++ }
++ break;
++ }
++ default:
++ return false;
++ }
++
++ return true;
++}
++
++/* Prepare key list from db/table and table_list */
++static bool wsrep_prepare_keys_for_isolation(THD* thd,
++ const char* db,
++ const char* table,
++ const TABLE_LIST* table_list,
++ wsrep_key_arr_t* ka)
++{
++ ka->keys= 0;
++ ka->keys_len= 0;
++
++ extern TABLE* find_temporary_table(THD*, const TABLE_LIST*);
++
++ if (db || table)
++ {
++ TABLE_LIST tmp_table;
++ MDL_request mdl_request;
++
++ memset(&tmp_table, 0, sizeof(tmp_table));
++ tmp_table.table_name= (char*)table;
++ tmp_table.db= (char*)db;
++ tmp_table.mdl_request.init(MDL_key::GLOBAL, (db) ? db : "",
++ (table) ? table : "",
++ MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
++
++ if (!table || !find_temporary_table(thd, &tmp_table))
++ {
++ if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0))))
++ {
++ WSREP_ERROR("Can't allocate memory for key_array");
++ goto err;
++ }
++ ka->keys_len= 1;
++ if (!(ka->keys[0].key_parts= (wsrep_buf_t*)
++ my_malloc(sizeof(wsrep_buf_t)*2, MYF(0))))
++ {
++ WSREP_ERROR("Can't allocate memory for key_parts");
++ goto err;
++ }
++ ka->keys[0].key_parts_num= 2;
++ if (!wsrep_prepare_key_for_isolation(
++ db, table,
++ (wsrep_buf_t*)ka->keys[0].key_parts,
++ &ka->keys[0].key_parts_num))
++ {
++ WSREP_ERROR("Preparing keys for isolation failed");
++ goto err;
++ }
++ }
++ }
++
++ for (const TABLE_LIST* table= table_list; table; table= table->next_global)
++ {
++ if (!find_temporary_table(thd, table))
++ {
++ wsrep_key_t* tmp;
++ tmp= (wsrep_key_t*)my_realloc(
++ ka->keys, (ka->keys_len + 1) * sizeof(wsrep_key_t), MYF(0));
++ if (!tmp)
++ {
++ WSREP_ERROR("Can't allocate memory for key_array");
++ goto err;
++ }
++ ka->keys= tmp;
++ if (!(ka->keys[ka->keys_len].key_parts= (wsrep_buf_t*)
++ my_malloc(sizeof(wsrep_buf_t)*2, MYF(0))))
++ {
++ WSREP_ERROR("Can't allocate memory for key_parts");
++ goto err;
++ }
++ ka->keys[ka->keys_len].key_parts_num= 2;
++ ++ka->keys_len;
++ if (!wsrep_prepare_key_for_isolation(
++ table->db, table->table_name,
++ (wsrep_buf_t*)ka->keys[ka->keys_len - 1].key_parts,
++ &ka->keys[ka->keys_len - 1].key_parts_num))
++ {
++ WSREP_ERROR("Preparing keys for isolation failed");
++ goto err;
++ }
++ }
++ }
++ return true;
++err:
++ wsrep_keys_free(ka);
++ return false;
++}
++
++
++bool wsrep_prepare_key_for_innodb(const uchar* cache_key,
++ size_t cache_key_len,
++ const uchar* row_id,
++ size_t row_id_len,
++ wsrep_buf_t* key,
++ size_t* key_len)
++{
++ if (*key_len < 3) return false;
++
++ *key_len= 0;
++ switch (wsrep_protocol_version)
++ {
++ case 0:
++ {
++ key[0].ptr = cache_key;
++ key[0].len = cache_key_len;
++
++ *key_len = 1;
++ break;
++ }
++ case 1:
++ case 2:
++ case 3:
++ {
++ key[0].ptr = cache_key;
++ key[0].len = strlen( (char*)cache_key );
++
++ key[1].ptr = cache_key + strlen( (char*)cache_key ) + 1;
++ key[1].len = strlen( (char*)(key[1].ptr) );
++
++ *key_len = 2;
++ break;
++ }
++ default:
++ return false;
++ }
++
++ key[*key_len].ptr = row_id;
++ key[*key_len].len = row_id_len;
++ ++(*key_len);
++
++ return true;
++}
++
++
++/*
++ * Construct Query_log_Event from thd query and serialize it
++ * into buffer.
++ *
++ * Return 0 in case of success, 1 in case of error.
++ */
++int wsrep_to_buf_helper(
++ THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len)
++{
++ IO_CACHE tmp_io_cache;
++ if (open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX,
++ 65536, MYF(MY_WME)))
++ return 1;
++ int ret(0);
++
++ if (thd->variables.gtid_next.type == GTID_GROUP)
++ {
++ Gtid_log_event gtid_ev(thd, FALSE, &thd->variables.gtid_next);
++ if (!gtid_ev.is_valid()) ret= 0;
++ if (!ret && gtid_ev.write(&tmp_io_cache)) ret= 1;
++ }
++
++ /* if there is prepare query, add event for it */
++ if (!ret && thd->wsrep_TOI_pre_query)
++ {
++ Query_log_event ev(thd, thd->wsrep_TOI_pre_query,
++ thd->wsrep_TOI_pre_query_len,
++ FALSE, FALSE, FALSE, 0);
++ if (ev.write(&tmp_io_cache)) ret= 1;
++ }
++
++ /* continue to append the actual query */
++ Query_log_event ev(thd, query, query_len, FALSE, FALSE, FALSE, 0);
++ if (!ret && ev.write(&tmp_io_cache)) ret= 1;
++ if (!ret && wsrep_write_cache_buf(&tmp_io_cache, buf, buf_len)) ret= 1;
++ close_cached_file(&tmp_io_cache);
++ return ret;
++}
++
++#include "sql_show.h"
++static int
++create_view_query(THD *thd, uchar** buf, size_t* buf_len)
++{
++ LEX *lex= thd->lex;
++ SELECT_LEX *select_lex= &lex->select_lex;
++ TABLE_LIST *first_table= select_lex->table_list.first;
++ TABLE_LIST *views = first_table;
++
++ 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);
++
++ if (!lex->definer)
++ {
++ /*
++ DEFINER-clause is missing; we have to create default definer in
++ persistent arena to be PS/SP friendly.
++ If this is an ALTER VIEW then the current user should be set as
++ the definer.
++ */
++
++ if (!(lex->definer= create_default_definer(thd)))
++ {
++ WSREP_WARN("view default definer issue");
++ }
++ }
++
++ views->algorithm = lex->create_view_algorithm;
++ views->definer.user = lex->definer->user;
++ views->definer.host = lex->definer->host;
++ views->view_suid = lex->create_view_suid;
++ views->with_check = lex->create_view_check;
++
++ 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)
++ {
++ List_iterator_fast<LEX_STRING> names(lex->view_list);
++ LEX_STRING *name;
++ int i;
++
++ 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);
++ buff.append(thd->lex->create_view_select.str,
++ thd->lex->create_view_select.length);
++ //int errcode= query_error_code(thd, TRUE);
++ //if (thd->binlog_query(THD::STMT_QUERY_TYPE,
++ // buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcod
++ return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len);
++}
++
++/*
++ returns:
++ 0: statement was replicated as TOI
++ 1: TOI replication was skipped
++ -1: TOI replication failed
++ */
++static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
++ const TABLE_LIST* table_list)
++{
++ wsrep_status_t ret(WSREP_WARNING);
++ uchar* buf(0);
++ size_t buf_len(0);
++ int buf_err;
++
++ WSREP_DEBUG("TO BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
++ thd->wsrep_exec_mode, thd->query() );
++ switch (thd->lex->sql_command)
++ {
++ case SQLCOM_CREATE_VIEW:
++ buf_err= create_view_query(thd, &buf, &buf_len);
++ break;
++ case SQLCOM_CREATE_PROCEDURE:
++ case SQLCOM_CREATE_SPFUNCTION:
++ buf_err= wsrep_create_sp(thd, &buf, &buf_len);
++ break;
++ case SQLCOM_CREATE_TRIGGER:
++ buf_err= wsrep_create_trigger_query(thd, &buf, &buf_len);
++ break;
++ case SQLCOM_CREATE_EVENT:
++ buf_err= wsrep_create_event_query(thd, &buf, &buf_len);
++ break;
++ case SQLCOM_ALTER_EVENT:
++ buf_err= wsrep_alter_event_query(thd, &buf, &buf_len);
++ break;
++ default:
++ buf_err= wsrep_to_buf_helper(thd, thd->query(), thd->query_length(), &buf,
++ &buf_len);
++ break;
++ }
++
++ wsrep_key_arr_t key_arr= {0, 0};
++ struct wsrep_buf buff = { buf, buf_len };
++ if (!buf_err &&
++ wsrep_prepare_keys_for_isolation(thd, db_, table_, table_list, &key_arr)&&
++ key_arr.keys_len > 0 &&
++ WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id,
++ key_arr.keys, key_arr.keys_len,
++ &buff, 1,
++ &thd->wsrep_trx_meta)))
++ {
++ thd->wsrep_exec_mode= TOTAL_ORDER;
++ wsrep_to_isolation++;
++ if (buf) my_free(buf);
++ wsrep_keys_free(&key_arr);
++ WSREP_DEBUG("TO BEGIN: %lld, %d",(long long)wsrep_thd_trx_seqno(thd),
++ thd->wsrep_exec_mode);
++ }
++ else if (key_arr.keys_len > 0) {
++ /* jump to error handler in mysql_execute_command() */
++ WSREP_WARN("TO isolation failed for: %d, sql: %s. Check wsrep "
++ "connection state and retry the query.",
++ ret, (thd->query()) ? thd->query() : "void");
++ my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check "
++ "your wsrep connection state and retry the query.");
++ if (buf) my_free(buf);
++ wsrep_keys_free(&key_arr);
++ return -1;
++ }
++ else {
++ /* non replicated DDL, affecting temporary tables only */
++ WSREP_DEBUG("TO isolation skipped for: %d, sql: %s."
++ "Only temporary tables affected.",
++ ret, (thd->query()) ? thd->query() : "void");
++ return 1;
++ }
++ return 0;
++}
++
++static void wsrep_TOI_end(THD *thd) {
++ wsrep_status_t ret;
++ wsrep_to_isolation--;
++
++ WSREP_DEBUG("TO END: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
++ thd->wsrep_exec_mode, (thd->query()) ? thd->query() : "void");
++
++ XID xid;
++ wsrep_xid_init(&xid, &thd->wsrep_trx_meta.gtid.uuid,
++ thd->wsrep_trx_meta.gtid.seqno);
++ wsrep_set_SE_checkpoint(&xid);
++ WSREP_DEBUG("TO END: %lld, update seqno",
++ (long long)wsrep_thd_trx_seqno(thd));
++
++ if (WSREP_OK == (ret = wsrep->to_execute_end(wsrep, thd->thread_id))) {
++ WSREP_DEBUG("TO END: %lld", (long long)wsrep_thd_trx_seqno(thd));
++ }
++ else {
++ WSREP_WARN("TO isolation end failed for: %d, sql: %s",
++ ret, (thd->query()) ? thd->query() : "void");
++ }
++}
++
++static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
++{
++ wsrep_status_t ret(WSREP_WARNING);
++ WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
++ thd->wsrep_exec_mode, thd->query() );
++
++ ret = wsrep->desync(wsrep);
++ if (ret != WSREP_OK)
++ {
++ WSREP_WARN("RSU desync failed %d for %s", ret, thd->query());
++ my_error(ER_LOCK_DEADLOCK, MYF(0));
++ return(ret);
++ }
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ wsrep_replaying++;
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++
++ if (wsrep_wait_committing_connections_close(5000))
++ {
++ /* no can do, bail out from DDL */
++ WSREP_WARN("RSU failed due to pending transactions, %s", thd->query());
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ wsrep_replaying--;
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++
++ ret = wsrep->resync(wsrep);
++ if (ret != WSREP_OK)
++ {
++ WSREP_WARN("resync failed %d for %s", ret, thd->query());
++ }
++ my_error(ER_LOCK_DEADLOCK, MYF(0));
++ return(1);
++ }
++
++ wsrep_seqno_t seqno = wsrep->pause(wsrep);
++ if (seqno == WSREP_SEQNO_UNDEFINED)
++ {
++ WSREP_WARN("pause failed %lld for %s", (long long)seqno, thd->query());
++ return(1);
++ }
++ WSREP_DEBUG("paused at %lld", (long long)seqno);
++ thd->variables.wsrep_on = 0;
++ return 0;
++}
++
++static void wsrep_RSU_end(THD *thd)
++{
++ wsrep_status_t ret(WSREP_WARNING);
++ WSREP_DEBUG("RSU END: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
++ thd->wsrep_exec_mode, thd->query() );
++
++
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ wsrep_replaying--;
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++
++ ret = wsrep->resume(wsrep);
++ if (ret != WSREP_OK)
++ {
++ WSREP_WARN("resume failed %d for %s", ret, thd->query());
++ }
++ ret = wsrep->resync(wsrep);
++ if (ret != WSREP_OK)
++ {
++ WSREP_WARN("resync failed %d for %s", ret, thd->query());
++ return;
++ }
++ thd->variables.wsrep_on = 1;
++}
++
++int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
++ const TABLE_LIST* table_list)
++{
++
++ /*
++ No isolation for applier or replaying threads.
++ */
++ if (thd->wsrep_exec_mode == REPL_RECV) return 0;
++
++ int ret= 0;
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++
++ if (thd->wsrep_conflict_state == MUST_ABORT)
++ {
++ WSREP_INFO("thread: %lu, %s has been aborted due to multi-master conflict",
++ thd->thread_id, thd->query());
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ return WSREP_TRX_FAIL;
++ }
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ DBUG_ASSERT(thd->wsrep_exec_mode == LOCAL_STATE);
++ DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno == WSREP_SEQNO_UNDEFINED);
++
++ if (thd->global_read_lock.can_acquire_protection())
++ {
++ WSREP_DEBUG("Aborting TOI: Global Read-Lock (FTWRL) in place: %s %lu",
++ thd->query(), thd->thread_id);
++ return -1;
++ }
++
++ if (wsrep_debug && thd->mdl_context.has_locks())
++ {
++ WSREP_DEBUG("thread holds MDL locks at TI begin: %s %lu",
++ thd->query(), thd->thread_id);
++ }
++
++ /*
++ It makes sense to set auto_increment_* to defaults in TOI operations.
++ Must be done before wsrep_TOI_begin() since Query_log_event encapsulating
++ TOI statement and auto inc variables for wsrep replication is constructed
++ there. Variables are reset back in THD::reset_for_next_command() before
++ processing of next command.
++ */
++ if (wsrep_auto_increment_control)
++ {
++ thd->variables.auto_increment_offset = 1;
++ thd->variables.auto_increment_increment = 1;
++ }
++
++ if (thd->variables.wsrep_on && thd->wsrep_exec_mode==LOCAL_STATE)
++ {
++ switch (wsrep_OSU_method_options) {
++ case WSREP_OSU_TOI: ret = wsrep_TOI_begin(thd, db_, table_,
++ table_list); break;
++ case WSREP_OSU_RSU: ret = wsrep_RSU_begin(thd, db_, table_); break;
++ }
++ switch (ret) {
++ case 0: thd->wsrep_exec_mode= TOTAL_ORDER; break;
++ case 1:
++ /* TOI replication skipped, treat as success */
++ ret = 0;
++ break;
++ case -1:
++ /* TOI replication failed, treat as error */
++ break;
++ }
++ }
++ return ret;
++}
++
++void wsrep_to_isolation_end(THD *thd)
++{
++ if (thd->wsrep_exec_mode == TOTAL_ORDER)
++ {
++ switch(wsrep_OSU_method_options)
++ {
++ case WSREP_OSU_TOI: wsrep_TOI_end(thd); break;
++ case WSREP_OSU_RSU: wsrep_RSU_end(thd); break;
++ }
++ wsrep_cleanup_transaction(thd);
++ }
++}
++
++#define WSREP_MDL_LOG(severity, msg, req, gra) \
++ WSREP_##severity( \
++ "%s\n" \
++ "request: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)\n" \
++ "granted: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)", \
++ msg, \
++ req->thread_id, (long long)wsrep_thd_trx_seqno(req), \
++ req->wsrep_exec_mode, req->wsrep_query_state, req->wsrep_conflict_state, \
++ req->get_command(), req->lex->sql_command, req->query(), \
++ gra->thread_id, (long long)wsrep_thd_trx_seqno(gra), \
++ gra->wsrep_exec_mode, gra->wsrep_query_state, gra->wsrep_conflict_state, \
++ gra->get_command(), gra->lex->sql_command, gra->query());
++
++bool
++wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
++ MDL_ticket *ticket
++) {
++ if (!WSREP_ON) return FALSE;
++
++ THD *request_thd = requestor_ctx->wsrep_get_thd();
++ THD *granted_thd = ticket->get_ctx()->wsrep_get_thd();
++ bool ret = FALSE;
++
++ mysql_mutex_lock(&request_thd->LOCK_wsrep_thd);
++ if (request_thd->wsrep_exec_mode == TOTAL_ORDER ||
++ request_thd->wsrep_exec_mode == REPL_RECV)
++ {
++ mysql_mutex_unlock(&request_thd->LOCK_wsrep_thd);
++ WSREP_MDL_LOG(DEBUG, "MDL conflict ", request_thd, granted_thd);
++ ticket->wsrep_report(wsrep_debug);
++
++ mysql_mutex_lock(&granted_thd->LOCK_wsrep_thd);
++ if (granted_thd->wsrep_exec_mode == TOTAL_ORDER ||
++ granted_thd->wsrep_exec_mode == REPL_RECV)
++ {
++ WSREP_MDL_LOG(INFO, "MDL BF-BF conflict", request_thd, granted_thd);
++ ticket->wsrep_report(true);
++ mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
++ ret = TRUE;
++ }
++ else if (granted_thd->lex->sql_command == SQLCOM_FLUSH)
++ {
++ WSREP_DEBUG("mdl granted over FLUSH BF");
++ ticket->wsrep_report(wsrep_debug);
++ mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
++ ret = TRUE;
++ }
++ else if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE)
++ {
++ WSREP_DEBUG("DROP caused BF abort");
++ ticket->wsrep_report(wsrep_debug);
++ mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
++ wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
++ ret = FALSE;
++ }
++ else if (granted_thd->wsrep_query_state == QUERY_COMMITTING)
++ {
++ WSREP_DEBUG("mdl granted, but commiting thd abort scheduled");
++ ticket->wsrep_report(wsrep_debug);
++ mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
++ wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
++ ret = FALSE;
++ }
++ else
++ {
++ WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", request_thd, granted_thd);
++ ticket->wsrep_report(wsrep_debug);
++ mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
++ wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
++ ret = FALSE;
++ }
++ }
++ else
++ {
++ mysql_mutex_unlock(&request_thd->LOCK_wsrep_thd);
++ }
++ return ret;
++}
+diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
+new file mode 100644
+index 0000000..3df3911
+--- /dev/null
++++ b/sql/wsrep_mysqld.h
+@@ -0,0 +1,321 @@
++/* Copyright 2008-2013 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#ifndef WSREP_MYSQLD_H
++#define WSREP_MYSQLD_H
++
++#include "mysqld.h"
++typedef struct st_mysql_show_var SHOW_VAR;
++#include <sql_priv.h>
++#include "rpl_gtid.h"
++#include "../wsrep/wsrep_api.h"
++
++#define WSREP_UNDEFINED_TRX_ID ULONGLONG_MAX
++
++class set_var;
++class THD;
++
++enum wsrep_exec_mode {
++ LOCAL_STATE,
++ REPL_RECV,
++ TOTAL_ORDER,
++ LOCAL_COMMIT
++};
++
++enum wsrep_query_state {
++ QUERY_IDLE,
++ QUERY_EXEC,
++ QUERY_COMMITTING,
++ QUERY_EXITING,
++ QUERY_ROLLINGBACK,
++};
++
++enum wsrep_conflict_state {
++ NO_CONFLICT,
++ MUST_ABORT,
++ ABORTING,
++ ABORTED,
++ MUST_REPLAY,
++ REPLAYING,
++ RETRY_AUTOCOMMIT,
++ CERT_FAILURE,
++};
++
++enum wsrep_consistency_check_mode {
++ NO_CONSISTENCY_CHECK,
++ CONSISTENCY_CHECK_DECLARED,
++ CONSISTENCY_CHECK_RUNNING,
++};
++
++
++// Global wsrep parameters
++extern wsrep_t* wsrep;
++
++// MySQL wsrep options
++extern const char* wsrep_provider;
++extern const char* wsrep_provider_options;
++extern const char* wsrep_cluster_name;
++extern const char* wsrep_cluster_address;
++extern const char* wsrep_node_name;
++extern const char* wsrep_node_address;
++extern const char* wsrep_node_incoming_address;
++extern const char* wsrep_data_home_dir;
++extern const char* wsrep_dbug_option;
++extern long wsrep_slave_threads;
++extern int wsrep_slave_count_change;
++extern MYSQL_PLUGIN_IMPORT my_bool wsrep_debug;
++extern my_bool wsrep_convert_LOCK_to_trx;
++extern ulong wsrep_retry_autocommit;
++extern my_bool wsrep_auto_increment_control;
++extern my_bool wsrep_drupal_282555_workaround;
++extern my_bool wsrep_incremental_data_collection;
++extern const char* wsrep_start_position;
++extern ulong wsrep_max_ws_size;
++extern ulong wsrep_max_ws_rows;
++extern const char* wsrep_notify_cmd;
++extern my_bool wsrep_certify_nonPK;
++extern long wsrep_max_protocol_version;
++extern long wsrep_protocol_version;
++extern ulong wsrep_forced_binlog_format;
++extern ulong wsrep_OSU_method_options;
++extern my_bool wsrep_desync;
++extern my_bool wsrep_recovery;
++extern my_bool wsrep_replicate_myisam;
++extern my_bool wsrep_log_conflicts;
++extern ulong wsrep_mysql_replication_bundle;
++extern my_bool wsrep_load_data_splitting;
++extern my_bool wsrep_restart_slave;
++extern my_bool wsrep_restart_slave_activated;
++extern my_bool wsrep_slave_FK_checks;
++extern my_bool wsrep_slave_UK_checks;
++
++enum enum_wsrep_OSU_method { WSREP_OSU_TOI, WSREP_OSU_RSU };
++enum enum_wsrep_sync_wait {
++ WSREP_SYNC_WAIT_NONE = 0x0,
++ // show, select, begin
++ WSREP_SYNC_WAIT_BEFORE_READ = 0x1,
++ WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE = 0x2,
++ WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE = 0x4,
++ WSREP_SYNC_WAIT_MAX = 0x7
++};
++
++// MySQL status variables
++extern my_bool wsrep_connected;
++extern my_bool wsrep_ready;
++extern const char* wsrep_cluster_state_uuid;
++extern long long wsrep_cluster_conf_id;
++extern const char* wsrep_cluster_status;
++extern long wsrep_cluster_size;
++extern long wsrep_local_index;
++extern long long wsrep_local_bf_aborts;
++extern const char* wsrep_provider_name;
++extern const char* wsrep_provider_version;
++extern const char* wsrep_provider_vendor;
++
++int wsrep_show_status(THD *thd, SHOW_VAR *var, char *buff);
++void wsrep_free_status(THD *thd);
++
++/* Filters out --wsrep-new-cluster oprtion from argv[]
++ * should be called in the very beginning of main() */
++void wsrep_filter_new_cluster (int* argc, char* argv[]);
++
++int wsrep_init();
++void wsrep_deinit();
++void wsrep_recover();
++bool wsrep_before_SE(); // initialize wsrep before storage
++ // engines (true) or after (false)
++/* wsrep initialization sequence at startup
++ * @param before wsrep_before_SE() value */
++void wsrep_init_startup(bool before);
++
++
++extern "C" enum wsrep_exec_mode wsrep_thd_exec_mode(THD *thd);
++extern "C" enum wsrep_conflict_state wsrep_thd_conflict_state(THD *thd);
++extern "C" enum wsrep_query_state wsrep_thd_query_state(THD *thd);
++extern "C" const char * wsrep_thd_exec_mode_str(THD *thd);
++extern "C" const char * wsrep_thd_conflict_state_str(THD *thd);
++extern "C" const char * wsrep_thd_query_state_str(THD *thd);
++extern "C" wsrep_ws_handle_t* wsrep_thd_ws_handle(THD *thd);
++
++extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode);
++extern "C" void wsrep_thd_set_query_state(
++ THD *thd, enum wsrep_query_state state);
++extern "C" void wsrep_thd_set_conflict_state(
++ THD *thd, enum wsrep_conflict_state state);
++
++extern "C" void wsrep_thd_set_trx_to_replay(THD *thd, uint64 trx_id);
++
++extern "C" void wsrep_thd_LOCK(THD *thd);
++extern "C" void wsrep_thd_UNLOCK(THD *thd);
++extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd);
++extern "C" time_t wsrep_thd_query_start(THD *thd);
++extern "C" my_thread_id wsrep_thd_thread_id(THD *thd);
++extern "C" int64_t wsrep_thd_trx_seqno(THD *thd);
++extern "C" query_id_t wsrep_thd_query_id(THD *thd);
++extern "C" char * wsrep_thd_query(THD *thd);
++extern "C" query_id_t wsrep_thd_wsrep_last_query_id(THD *thd);
++extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id);
++extern "C" void wsrep_thd_awake(THD *thd, my_bool signal);
++extern "C" int wsrep_thd_retry_counter(THD *thd);
++
++
++extern void wsrep_close_client_connections(my_bool wait_to_end);
++extern int wsrep_wait_committing_connections_close(int wait_time);
++extern void wsrep_close_applier(THD *thd);
++extern void wsrep_wait_appliers_close(THD *thd);
++extern void wsrep_close_applier_threads(int count);
++extern void wsrep_kill_mysql(THD *thd);
++
++/* new defines */
++extern void wsrep_stop_replication(THD *thd);
++extern bool wsrep_start_replication();
++extern bool wsrep_sync_wait (THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_READ);
++extern int wsrep_check_opts (int argc, char* const* argv);
++extern void wsrep_prepend_PATH (const char* path);
++/* some inline functions are defined in wsrep_mysqld_inl.h */
++
++/* Other global variables */
++extern wsrep_seqno_t wsrep_locked_seqno;
++
++#define WSREP_ON \
++ (global_system_variables.wsrep_on)
++
++#define WSREP(thd) \
++ (WSREP_ON && wsrep && (thd && thd->variables.wsrep_on))
++
++#define WSREP_CLIENT(thd) \
++ (WSREP(thd) && thd->wsrep_client_thread)
++
++#define WSREP_EMULATE_BINLOG(thd) \
++ (WSREP(thd) && wsrep_emulate_bin_log)
++
++// MySQL logging functions don't seem to understand long long length modifer.
++// This is a workaround. It also prefixes all messages with "WSREP"
++#define WSREP_LOG(fun, ...) \
++ { \
++ char msg[1024] = {'\0'}; \
++ snprintf(msg, sizeof(msg) - 1, ## __VA_ARGS__); \
++ fun("WSREP: %s", msg); \
++ }
++
++#define WSREP_DEBUG(...) \
++ if (wsrep_debug) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
++#define WSREP_INFO(...) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
++#define WSREP_WARN(...) WSREP_LOG(sql_print_warning, ##__VA_ARGS__)
++#define WSREP_ERROR(...) WSREP_LOG(sql_print_error, ##__VA_ARGS__)
++
++#define WSREP_LOG_CONFLICT_THD(thd, role) \
++ WSREP_LOG(sql_print_information, \
++ "%s: \n " \
++ " THD: %lu, mode: %s, state: %s, conflict: %s, seqno: %lld\n " \
++ " SQL: %s", \
++ role, wsrep_thd_thread_id(thd), wsrep_thd_exec_mode_str(thd), \
++ wsrep_thd_query_state_str(thd), \
++ wsrep_thd_conflict_state_str(thd), (long long)wsrep_thd_trx_seqno(thd), \
++ wsrep_thd_query(thd) \
++ );
++
++#define WSREP_LOG_CONFLICT(bf_thd, victim_thd, bf_abort) \
++ if (wsrep_debug || wsrep_log_conflicts) \
++ { \
++ WSREP_LOG(sql_print_information, "cluster conflict due to %s for threads:",\
++ (bf_abort) ? "high priority abort" : "certification failure" \
++ ); \
++ if (bf_thd) WSREP_LOG_CONFLICT_THD(bf_thd, "Winning thread"); \
++ if (victim_thd) WSREP_LOG_CONFLICT_THD(victim_thd, "Victim thread"); \
++ }
++
++extern void wsrep_ready_wait();
++
++enum wsrep_trx_status {
++ WSREP_TRX_OK,
++ WSREP_TRX_CERT_FAIL, /* certification failure, must abort */
++ WSREP_TRX_SIZE_EXCEEDED, /* trx size exceeded */
++ WSREP_TRX_ERROR, /* native mysql error */
++};
++
++extern enum wsrep_trx_status
++wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all);
++class Ha_trx_info;
++struct THD_TRANS;
++void wsrep_register_hton(THD* thd, bool all);
++void wsrep_post_commit(THD* thd, bool all);
++void wsrep_brute_force_killer(THD *thd);
++int wsrep_hire_brute_force_killer(THD *thd, uint64_t trx_id);
++
++extern "C" bool wsrep_consistency_check(void *thd_ptr);
++
++/* this is visible for client build so that innodb plugin gets this */
++typedef struct wsrep_aborting_thd {
++ struct wsrep_aborting_thd *next;
++ THD *aborting_thd;
++} *wsrep_aborting_thd_t;
++
++extern mysql_mutex_t LOCK_wsrep_ready;
++extern mysql_cond_t COND_wsrep_ready;
++extern mysql_mutex_t LOCK_wsrep_sst;
++extern mysql_cond_t COND_wsrep_sst;
++extern mysql_mutex_t LOCK_wsrep_sst_init;
++extern mysql_cond_t COND_wsrep_sst_init;
++extern mysql_mutex_t LOCK_wsrep_rollback;
++extern mysql_cond_t COND_wsrep_rollback;
++extern int wsrep_replaying;
++extern mysql_mutex_t LOCK_wsrep_replaying;
++extern mysql_cond_t COND_wsrep_replaying;
++extern mysql_mutex_t LOCK_wsrep_slave_threads;
++extern mysql_mutex_t LOCK_wsrep_desync;
++extern wsrep_aborting_thd_t wsrep_aborting_thd;
++extern my_bool wsrep_emulate_bin_log;
++extern int wsrep_to_isolation;
++extern rpl_sidno wsrep_sidno;
++extern my_bool wsrep_preordered_opt;
++#ifdef HAVE_PSI_INTERFACE
++extern PSI_mutex_key key_LOCK_wsrep_ready;
++extern PSI_mutex_key key_COND_wsrep_ready;
++extern PSI_mutex_key key_LOCK_wsrep_sst;
++extern PSI_cond_key key_COND_wsrep_sst;
++extern PSI_mutex_key key_LOCK_wsrep_sst_init;
++extern PSI_cond_key key_COND_wsrep_sst_init;
++extern PSI_mutex_key key_LOCK_wsrep_sst_thread;
++extern PSI_cond_key key_COND_wsrep_sst_thread;
++extern PSI_mutex_key key_LOCK_wsrep_rollback;
++extern PSI_cond_key key_COND_wsrep_rollback;
++extern PSI_mutex_key key_LOCK_wsrep_replaying;
++extern PSI_cond_key key_COND_wsrep_replaying;
++extern PSI_mutex_key key_LOCK_wsrep_slave_threads;
++extern PSI_mutex_key key_LOCK_wsrep_desync;
++#endif /* HAVE_PSI_INTERFACE */
++struct TABLE_LIST;
++int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
++ const TABLE_LIST* table_list);
++void wsrep_to_isolation_end(THD *thd);
++void wsrep_cleanup_transaction(THD *thd);
++int wsrep_to_buf_helper(
++ THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len);
++int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len);
++int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
++int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len);
++int wsrep_alter_event_query(THD *thd, uchar** buf, size_t* buf_len);
++
++struct xid_t;
++void wsrep_get_SE_checkpoint(xid_t*);
++void wsrep_set_SE_checkpoint(xid_t*);
++void wsrep_init_sidno(const wsrep_uuid_t&);
++void wsrep_xid_init(xid_t*, const wsrep_uuid_t*, wsrep_seqno_t);
++const wsrep_uuid_t* wsrep_xid_uuid(const xid_t*);
++wsrep_seqno_t wsrep_xid_seqno(const xid_t*);
++int wsrep_is_wsrep_xid(const void* xid);
++
++#endif /* WSREP_MYSQLD_H */
+diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc
+new file mode 100644
+index 0000000..0fc76f5
+--- /dev/null
++++ b/sql/wsrep_notify.cc
+@@ -0,0 +1,111 @@
++/* Copyright 2010 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include <mysqld.h>
++#include "wsrep_priv.h"
++#include "wsrep_utils.h"
++
++const char* wsrep_notify_cmd="";
++
++static const char* _status_str(wsrep_member_status_t status)
++{
++ switch (status)
++ {
++ case WSREP_MEMBER_UNDEFINED: return "Undefined";
++ case WSREP_MEMBER_JOINER: return "Joiner";
++ case WSREP_MEMBER_DONOR: return "Donor";
++ case WSREP_MEMBER_JOINED: return "Joined";
++ case WSREP_MEMBER_SYNCED: return "Synced";
++ default: return "Error(?)";
++ }
++}
++
++void wsrep_notify_status (wsrep_member_status_t status,
++ const wsrep_view_info_t* view)
++{
++ if (!wsrep_notify_cmd || 0 == strlen(wsrep_notify_cmd))
++ {
++ WSREP_INFO("wsrep_notify_cmd is not defined, skipping notification.");
++ return;
++ }
++
++ char cmd_buf[1 << 16]; // this can be long
++ long cmd_len = sizeof(cmd_buf) - 1;
++ char* cmd_ptr = cmd_buf;
++ long cmd_off = 0;
++
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, "%s",
++ wsrep_notify_cmd);
++
++ if (status >= WSREP_MEMBER_UNDEFINED && status < WSREP_MEMBER_ERROR)
++ {
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, " --status %s",
++ _status_str(status));
++ }
++ else
++ {
++ /* here we preserve provider error codes */
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
++ " --status 'Error(%d)'", status);
++ }
++
++ if (0 != view)
++ {
++ char uuid_str[40];
++
++ wsrep_uuid_print (&view->state_id.uuid, uuid_str, sizeof(uuid_str));
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
++ " --uuid %s", uuid_str);
++
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
++ " --primary %s", view->view >= 0 ? "yes" : "no");
++
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
++ " --index %d", view->my_idx);
++
++ if (view->memb_num)
++ {
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, " --members");
++
++ for (int i = 0; i < view->memb_num; i++)
++ {
++ wsrep_uuid_print (&view->members[i].id, uuid_str, sizeof(uuid_str));
++ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
++ "%c%s/%s/%s", i > 0 ? ',' : ' ',
++ uuid_str, view->members[i].name,
++ view->members[i].incoming);
++ }
++ }
++ }
++
++ if (cmd_off == cmd_len)
++ {
++ WSREP_ERROR("Notification buffer too short (%ld). Aborting notification.",
++ cmd_len);
++ return;
++ }
++
++ wsp::process p(cmd_ptr, "r");
++
++ p.wait();
++ int err = p.error();
++
++ if (err)
++ {
++ WSREP_ERROR("Notification command failed: %d (%s): \"%s\"",
++ err, strerror(err), cmd_ptr);
++ }
++}
++
+diff --git a/sql/wsrep_priv.h b/sql/wsrep_priv.h
+new file mode 100644
+index 0000000..93640fb
+--- /dev/null
++++ b/sql/wsrep_priv.h
+@@ -0,0 +1,51 @@
++/* Copyright 2010 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++//! @file declares symbols private to wsrep integration layer
++
++#ifndef WSREP_PRIV_H
++#define WSREP_PRIV_H
++
++#include "wsrep_mysqld.h"
++#include "../wsrep/wsrep_api.h"
++
++#include <log.h>
++#include <pthread.h>
++#include <cstdio>
++
++void wsrep_ready_set (my_bool x);
++
++ssize_t wsrep_sst_prepare (void** msg);
++wsrep_cb_status wsrep_sst_donate_cb (void* app_ctx,
++ void* recv_ctx,
++ const void* msg, size_t msg_len,
++ const wsrep_gtid_t* state_id,
++ const char* state, size_t state_len,
++ bool bypass);
++
++extern wsrep_uuid_t local_uuid;
++extern wsrep_seqno_t local_seqno;
++
++// a helper function
++void wsrep_sst_received(wsrep_t*, const wsrep_uuid_t*, wsrep_seqno_t,
++ const void*, size_t);
++/*! SST thread signals init thread about sst completion */
++void wsrep_sst_complete(const wsrep_uuid_t*, wsrep_seqno_t, bool);
++
++void wsrep_notify_status (wsrep_member_status_t new_status,
++ const wsrep_view_info_t* view = 0);
++
++#endif /* WSREP_PRIV_H */
+diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
+new file mode 100644
+index 0000000..ed45674
+--- /dev/null
++++ b/sql/wsrep_sst.cc
+@@ -0,0 +1,1102 @@
++/* Copyright 2008-2012 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include "wsrep_sst.h"
++
++#include <mysqld.h>
++#include <sql_class.h>
++#include <set_var.h>
++#include <sql_acl.h>
++#include <sql_reload.h>
++#include <sql_parse.h>
++#include "wsrep_priv.h"
++#include "wsrep_utils.h"
++#include <cstdio>
++#include <cstdlib>
++
++extern const char wsrep_defaults_file[];
++extern const char wsrep_defaults_group_suffix[];
++
++#define WSREP_SST_OPT_ROLE "--role"
++#define WSREP_SST_OPT_ADDR "--address"
++#define WSREP_SST_OPT_AUTH "--auth"
++#define WSREP_SST_OPT_DATA "--datadir"
++#define WSREP_SST_OPT_CONF "--defaults-file"
++#define WSREP_SST_OPT_CONF_SUFFIX "--defaults-group-suffix"
++#define WSREP_SST_OPT_PARENT "--parent"
++#define WSREP_SST_OPT_BINLOG "--binlog"
++
++// mysqldump-specific options
++#define WSREP_SST_OPT_USER "--user"
++#define WSREP_SST_OPT_PSWD "--password"
++#define WSREP_SST_OPT_HOST "--host"
++#define WSREP_SST_OPT_PORT "--port"
++#define WSREP_SST_OPT_LPORT "--local-port"
++
++// donor-specific
++#define WSREP_SST_OPT_SOCKET "--socket"
++#define WSREP_SST_OPT_GTID "--gtid"
++#define WSREP_SST_OPT_BYPASS "--bypass"
++
++#define WSREP_SST_MYSQLDUMP "mysqldump"
++#define WSREP_SST_RSYNC "rsync"
++#define WSREP_SST_SKIP "skip"
++#define WSREP_SST_XTRABACKUP "xtrabackup"
++#define WSREP_SST_XTRABACKUP_V2 "xtrabackup-v2"
++#define WSREP_SST_DEFAULT WSREP_SST_RSYNC
++#define WSREP_SST_ADDRESS_AUTO "AUTO"
++#define WSREP_SST_AUTH_MASK "********"
++
++const char* wsrep_sst_method = WSREP_SST_DEFAULT;
++const char* wsrep_sst_receive_address = WSREP_SST_ADDRESS_AUTO;
++const char* wsrep_sst_donor = "";
++ char* wsrep_sst_auth = NULL;
++
++// container for real auth string
++static const char* sst_auth_real = NULL;
++my_bool wsrep_sst_donor_rejects_queries = FALSE;
++
++bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* c_str = NULL;
++
++ if ((res = var->value->val_str(&str)) &&
++ (c_str = res->c_ptr()) &&
++ strlen(c_str) > 0)
++ return 0;
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "wsrep_sst_method", c_str ? c_str : "NULL");
++ return 1;
++}
++
++bool wsrep_sst_method_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ return 0;
++}
++
++static bool sst_receive_address_check (const char* str)
++{
++ if (!strncasecmp(str, "127.0.0.1", strlen("127.0.0.1")) ||
++ !strncasecmp(str, "localhost", strlen("localhost")))
++ {
++ return 1;
++ }
++
++ return 0;
++}
++
++bool wsrep_sst_receive_address_check (sys_var *self, THD* thd, set_var* var)
++{
++ const char* c_str = var->value->str_value.c_ptr();
++
++ if (sst_receive_address_check (c_str))
++ {
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "wsrep_sst_receive_address", c_str ? c_str : "NULL");
++ return 1;
++ }
++
++ return 0;
++}
++
++bool wsrep_sst_receive_address_update (sys_var *self, THD* thd,
++ enum_var_type type)
++{
++ return 0;
++}
++
++bool wsrep_sst_auth_check (sys_var *self, THD* thd, set_var* var)
++{
++ return 0;
++}
++static bool sst_auth_real_set (const char* value)
++{
++ const char* v = strdup (value);
++
++ if (v)
++ {
++ if (sst_auth_real) free (const_cast<char*>(sst_auth_real));
++ sst_auth_real = v;
++
++ if (strlen(sst_auth_real))
++ {
++ if (wsrep_sst_auth)
++ {
++ my_free ((void*)wsrep_sst_auth);
++ wsrep_sst_auth = my_strdup(WSREP_SST_AUTH_MASK, MYF(0));
++ //strncpy (wsrep_sst_auth, WSREP_SST_AUTH_MASK,
++ // sizeof(wsrep_sst_auth) - 1);
++ }
++ else
++ wsrep_sst_auth = my_strdup (WSREP_SST_AUTH_MASK, MYF(0));
++ }
++ return 0;
++ }
++
++ return 1;
++}
++
++bool wsrep_sst_auth_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ return sst_auth_real_set (wsrep_sst_auth);
++}
++
++void wsrep_sst_auth_init (const char* value)
++{
++ if (wsrep_sst_auth == value) wsrep_sst_auth = NULL;
++ if (value) sst_auth_real_set (value);
++}
++
++bool wsrep_sst_donor_check (sys_var *self, THD* thd, set_var* var)
++{
++ return 0;
++}
++
++bool wsrep_sst_donor_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ return 0;
++}
++
++static wsrep_uuid_t cluster_uuid = WSREP_UUID_UNDEFINED;
++
++bool wsrep_before_SE()
++{
++ return (wsrep_provider != NULL
++ && strcmp (wsrep_provider, WSREP_NONE)
++ && strcmp (wsrep_sst_method, WSREP_SST_SKIP)
++ && strcmp (wsrep_sst_method, WSREP_SST_MYSQLDUMP));
++}
++
++static bool sst_complete = false;
++static bool sst_needed = false;
++
++void wsrep_sst_grab ()
++{
++ WSREP_INFO("wsrep_sst_grab()");
++ if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort();
++ sst_complete = false;
++ mysql_mutex_unlock (&LOCK_wsrep_sst);
++}
++
++// Wait for end of SST
++bool wsrep_sst_wait ()
++{
++ if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort();
++ while (!sst_complete)
++ {
++ WSREP_INFO("Waiting for SST to complete.");
++ mysql_cond_wait (&COND_wsrep_sst, &LOCK_wsrep_sst);
++ }
++
++ if (local_seqno >= 0)
++ {
++ WSREP_INFO("SST complete, seqno: %lld", (long long) local_seqno);
++ }
++ else
++ {
++ WSREP_ERROR("SST failed: %d (%s)",
++ int(-local_seqno), strerror(-local_seqno));
++ }
++
++ mysql_mutex_unlock (&LOCK_wsrep_sst);
++
++ return (local_seqno >= 0);
++}
++
++// Signal end of SST
++void wsrep_sst_complete (const wsrep_uuid_t* sst_uuid,
++ wsrep_seqno_t sst_seqno,
++ bool needed)
++{
++ if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort();
++ if (!sst_complete)
++ {
++ sst_complete = true;
++ sst_needed = needed;
++ local_uuid = *sst_uuid;
++ local_seqno = sst_seqno;
++ mysql_cond_signal (&COND_wsrep_sst);
++ }
++ else
++ {
++ /* This can happen when called from wsrep_synced_cb().
++ At the moment there is no way to check there
++ if main thread is still waiting for signal,
++ so wsrep_sst_complete() is called from there
++ each time wsrep_ready changes from FALSE -> TRUE.
++ */
++ WSREP_DEBUG("Nobody is waiting for SST.");
++ }
++ mysql_mutex_unlock (&LOCK_wsrep_sst);
++}
++
++void wsrep_sst_received (wsrep_t* const wsrep,
++ const wsrep_uuid_t* const uuid,
++ wsrep_seqno_t const seqno,
++ const void* const state,
++ size_t const state_len)
++{
++ int const rcode(seqno < 0 ? seqno : 0);
++ wsrep_gtid_t const state_id = {
++ *uuid, (rcode ? WSREP_SEQNO_UNDEFINED : seqno)
++ };
++ wsrep_init_sidno(state_id.uuid);
++ wsrep->sst_received(wsrep, &state_id, state, state_len, rcode);
++}
++
++// Let applier threads to continue
++void wsrep_sst_continue ()
++{
++ if (sst_needed)
++ {
++ WSREP_INFO("Signalling provider to continue.");
++ wsrep_sst_received (wsrep, &local_uuid, local_seqno, NULL, 0);
++ }
++}
++
++struct sst_thread_arg
++{
++ const char* cmd;
++ int err;
++ char* ret_str;
++ mysql_mutex_t lock;
++ mysql_cond_t cond;
++
++ sst_thread_arg (const char* c) : cmd(c), err(-1), ret_str(0)
++ {
++ mysql_mutex_init(key_LOCK_wsrep_sst_thread, &lock, MY_MUTEX_INIT_FAST);
++ mysql_cond_init(key_COND_wsrep_sst_thread, &cond, NULL);
++ }
++
++ ~sst_thread_arg()
++ {
++ mysql_cond_destroy (&cond);
++ mysql_mutex_unlock (&lock);
++ mysql_mutex_destroy (&lock);
++ }
++};
++
++static int sst_scan_uuid_seqno (const char* str,
++ wsrep_uuid_t* uuid, wsrep_seqno_t* seqno)
++{
++ int offt = wsrep_uuid_scan (str, strlen(str), uuid);
++ if (offt > 0 && strlen(str) > (unsigned int)offt && ':' == str[offt])
++ {
++ *seqno = strtoll (str + offt + 1, NULL, 10);
++ if (*seqno != LLONG_MAX || errno != ERANGE)
++ {
++ return 0;
++ }
++ }
++
++ WSREP_ERROR("Failed to parse uuid:seqno pair: '%s'", str);
++ return EINVAL;
++}
++
++// get rid of trailing \n
++static char* my_fgets (char* buf, size_t buf_len, FILE* stream)
++{
++ char* ret= fgets (buf, buf_len, stream);
++
++ if (ret)
++ {
++ size_t len = strlen(ret);
++ if (len > 0 && ret[len - 1] == '\n') ret[len - 1] = '\0';
++ }
++
++ return ret;
++}
++
++/*
++ Generate opt_binlog_opt_val for sst_donate_other(), sst_prepare_other().
++
++ Returns zero on success, negative error code otherwise.
++
++ String containing binlog name is stored in param ret if binlog is enabled
++ and GTID mode is on, otherwise empty string. Returned string should be
++ freed with my_free().
++ */
++static int generate_binlog_opt_val(char** ret)
++{
++ DBUG_ASSERT(ret);
++ *ret= NULL;
++ if (opt_bin_log && gtid_mode > 0)
++ {
++ assert(opt_bin_logname);
++ *ret= strcmp(opt_bin_logname, "0") ?
++ my_strdup(opt_bin_logname, MYF(0)) : my_strdup("", MYF(0));
++ }
++ else
++ {
++ *ret= my_strdup("", MYF(0));
++ }
++ if (!*ret) return -ENOMEM;
++ return 0;
++}
++
++static void* sst_joiner_thread (void* a)
++{
++ sst_thread_arg* arg= (sst_thread_arg*) a;
++ int err= 1;
++
++ {
++ const char magic[] = "ready";
++ const size_t magic_len = sizeof(magic) - 1;
++ const size_t out_len = 512;
++ char out[out_len];
++
++ WSREP_INFO("Running: '%s'", arg->cmd);
++
++ wsp::process proc (arg->cmd, "r");
++
++ if (proc.pipe() && !proc.error())
++ {
++ const char* tmp= my_fgets (out, out_len, proc.pipe());
++
++ if (!tmp || strlen(tmp) < (magic_len + 2) ||
++ strncasecmp (tmp, magic, magic_len))
++ {
++ WSREP_ERROR("Failed to read '%s <addr>' from: %s\n\tRead: '%s'",
++ magic, arg->cmd, tmp);
++ proc.wait();
++ if (proc.error()) err = proc.error();
++ }
++ else
++ {
++ err = 0;
++ }
++ }
++ else
++ {
++ err = proc.error();
++ WSREP_ERROR("Failed to execute: %s : %d (%s)",
++ arg->cmd, err, strerror(err));
++ }
++
++ // signal sst_prepare thread with ret code,
++ // it will go on sending SST request
++ mysql_mutex_lock (&arg->lock);
++ if (!err)
++ {
++ arg->ret_str = strdup (out + magic_len + 1);
++ if (!arg->ret_str) err = ENOMEM;
++ }
++ arg->err = -err;
++ mysql_cond_signal (&arg->cond);
++ mysql_mutex_unlock (&arg->lock); //! @note arg is unusable after that.
++
++ if (err) return NULL; /* lp:808417 - return immediately, don't signal
++ * initializer thread to ensure single thread of
++ * shutdown. */
++
++ wsrep_uuid_t ret_uuid = WSREP_UUID_UNDEFINED;
++ wsrep_seqno_t ret_seqno = WSREP_SEQNO_UNDEFINED;
++
++ // in case of successfull receiver start, wait for SST completion/end
++ char* tmp = my_fgets (out, out_len, proc.pipe());
++
++ proc.wait();
++ err= EINVAL;
++
++ if (!tmp)
++ {
++ WSREP_ERROR("Failed to read uuid:seqno from joiner script.");
++ if (proc.error()) err = proc.error();
++ }
++ else
++ {
++ err= sst_scan_uuid_seqno (out, &ret_uuid, &ret_seqno);
++ }
++
++ if (err)
++ {
++ ret_uuid= WSREP_UUID_UNDEFINED;
++ ret_seqno= -err;
++ }
++
++ // Tell initializer thread that SST is complete
++ wsrep_sst_complete (&ret_uuid, ret_seqno, true);
++ }
++
++ return NULL;
++}
++
++static ssize_t sst_prepare_other (const char* method,
++ const char* addr_in,
++ const char** addr_out)
++{
++ ssize_t cmd_len= 1024;
++ char cmd_str[1024];
++ const char* sst_dir= mysql_real_data_home;
++ const char* binlog_opt= "";
++ char* binlog_opt_val= NULL;
++
++ int ret;
++ if ((ret= generate_binlog_opt_val(&binlog_opt_val)))
++ {
++ WSREP_ERROR("sst_prepare_other(): generate_binlog_opt_val() failed: %d",
++ ret);
++ return ret;
++ }
++ if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG;
++
++
++ ret= snprintf (cmd_str, cmd_len,
++ "wsrep_sst_%s "
++ WSREP_SST_OPT_ROLE" 'joiner' "
++ WSREP_SST_OPT_ADDR" '%s' "
++ WSREP_SST_OPT_AUTH" '%s' "
++ WSREP_SST_OPT_DATA" '%s' "
++ WSREP_SST_OPT_CONF" '%s' "
++ WSREP_SST_OPT_CONF_SUFFIX" '%s' "
++ WSREP_SST_OPT_PARENT" '%d'"
++ " %s '%s' ",
++ method, addr_in, (sst_auth_real) ? sst_auth_real : "",
++ sst_dir, wsrep_defaults_file, wsrep_defaults_group_suffix, (int)getpid(),
++ binlog_opt, binlog_opt_val);
++ my_free(binlog_opt_val);
++
++ if (ret < 0 || ret >= cmd_len)
++ {
++ WSREP_ERROR("sst_prepare_other(): snprintf() failed: %d", ret);
++ return (ret < 0 ? ret : -EMSGSIZE);
++ }
++
++ pthread_t tmp;
++ sst_thread_arg arg(cmd_str);
++ mysql_mutex_lock (&arg.lock);
++ ret = pthread_create (&tmp, NULL, sst_joiner_thread, &arg);
++ if (ret)
++ {
++ WSREP_ERROR("sst_prepare_other(): pthread_create() failed: %d (%s)",
++ ret, strerror(ret));
++ return ret;
++ }
++ mysql_cond_wait (&arg.cond, &arg.lock);
++
++ *addr_out= arg.ret_str;
++
++ if (!arg.err)
++ ret = strlen(*addr_out);
++ else
++ {
++ assert (arg.err < 0);
++ ret = arg.err;
++ }
++
++ pthread_detach (tmp);
++
++ return ret;
++}
++
++extern uint mysqld_port;
++
++/*! Just tells donor where to send mysqldump */
++static ssize_t sst_prepare_mysqldump (const char* addr_in,
++ const char** addr_out)
++{
++ ssize_t ret = strlen (addr_in);
++
++ if (!strrchr(addr_in, ':'))
++ {
++ ssize_t s = ret + 7;
++ char* tmp = (char*) malloc (s);
++
++ if (tmp)
++ {
++ ret= snprintf (tmp, s, "%s:%u", addr_in, mysqld_port);
++
++ if (ret > 0 && ret < s)
++ {
++ *addr_out= tmp;
++ return ret;
++ }
++ if (ret > 0) /* buffer too short */ ret = -EMSGSIZE;
++ free (tmp);
++ }
++ else {
++ ret= -ENOMEM;
++ }
++
++ WSREP_ERROR ("Could not prepare state transfer request: "
++ "adding default port failed: %zd.", ret);
++ }
++ else {
++ *addr_out= addr_in;
++ }
++
++ return ret;
++}
++
++static bool SE_initialized = false;
++
++ssize_t wsrep_sst_prepare (void** msg)
++{
++ const ssize_t ip_max= 256;
++ char ip_buf[ip_max];
++ const char* addr_in= NULL;
++ const char* addr_out= NULL;
++
++ if (!strcmp(wsrep_sst_method, WSREP_SST_SKIP))
++ {
++ ssize_t ret = strlen(WSREP_STATE_TRANSFER_TRIVIAL) + 1;
++ *msg = strdup(WSREP_STATE_TRANSFER_TRIVIAL);
++ if (!msg)
++ {
++ WSREP_ERROR("Could not allocate %zd bytes for state request", ret);
++ unireg_abort(1);
++ }
++ return ret;
++ }
++
++ // Figure out SST address. Common for all SST methods
++ if (wsrep_sst_receive_address &&
++ strcmp (wsrep_sst_receive_address, WSREP_SST_ADDRESS_AUTO))
++ {
++ addr_in= wsrep_sst_receive_address;
++ }
++ else if (wsrep_node_address && strlen(wsrep_node_address))
++ {
++ const char* const colon= strchr (wsrep_node_address, ':');
++ if (colon)
++ {
++ ptrdiff_t const len= colon - wsrep_node_address;
++ strncpy (ip_buf, wsrep_node_address, len);
++ ip_buf[len]= '\0';
++ addr_in= ip_buf;
++ }
++ else
++ {
++ addr_in= wsrep_node_address;
++ }
++ }
++ else
++ {
++ ssize_t ret= wsrep_guess_ip (ip_buf, ip_max);
++
++ if (ret && ret < ip_max)
++ {
++ addr_in= ip_buf;
++ }
++ else
++ {
++ WSREP_ERROR("Could not prepare state transfer request: "
++ "failed to guess address to accept state transfer at. "
++ "wsrep_sst_receive_address must be set manually.");
++ unireg_abort(1);
++ }
++ }
++
++ ssize_t addr_len= -ENOSYS;
++ if (!strcmp(wsrep_sst_method, WSREP_SST_MYSQLDUMP))
++ {
++ addr_len= sst_prepare_mysqldump (addr_in, &addr_out);
++ if (addr_len < 0) unireg_abort(1);
++ }
++ else
++ {
++ /*! A heuristic workaround until we learn how to stop and start engines */
++ if (SE_initialized)
++ {
++ // we already did SST at initializaiton, now engines are running
++ // sql_print_information() is here because the message is too long
++ // for WSREP_INFO.
++ sql_print_information ("WSREP: "
++ "You have configured '%s' state snapshot transfer method "
++ "which cannot be performed on a running server. "
++ "Wsrep provider won't be able to fall back to it "
++ "if other means of state transfer are unavailable. "
++ "In that case you will need to restart the server.",
++ wsrep_sst_method);
++ *msg = 0;
++ return 0;
++ }
++
++ addr_len = sst_prepare_other (wsrep_sst_method, addr_in, &addr_out);
++ if (addr_len < 0)
++ {
++ WSREP_ERROR("Failed to prepare for '%s' SST. Unrecoverable.",
++ wsrep_sst_method);
++ unireg_abort(1);
++ }
++ }
++
++ size_t const method_len(strlen(wsrep_sst_method));
++ size_t const msg_len (method_len + addr_len + 2 /* + auth_len + 1*/);
++
++ *msg = malloc (msg_len);
++ if (NULL != *msg) {
++ char* const method_ptr(reinterpret_cast<char*>(*msg));
++ strcpy (method_ptr, wsrep_sst_method);
++ char* const addr_ptr(method_ptr + method_len + 1);
++ strcpy (addr_ptr, addr_out);
++
++ WSREP_INFO ("Prepared SST request: %s|%s", method_ptr, addr_ptr);
++ }
++ else {
++ WSREP_ERROR("Failed to allocate SST request of size %zu. Can't continue.",
++ msg_len);
++ unireg_abort(1);
++ }
++
++ if (addr_out != addr_in) /* malloc'ed */ free ((char*)addr_out);
++
++ return msg_len;
++}
++
++// helper method for donors
++static int sst_run_shell (const char* cmd_str, int max_tries)
++{
++ int ret = 0;
++
++ for (int tries=1; tries <= max_tries; tries++)
++ {
++ wsp::process proc (cmd_str, "r");
++
++ if (NULL != proc.pipe())
++ {
++ proc.wait();
++ }
++
++ if ((ret = proc.error()))
++ {
++ WSREP_ERROR("Try %d/%d: '%s' failed: %d (%s)",
++ tries, max_tries, proc.cmd(), ret, strerror(ret));
++ sleep (1);
++ }
++ else
++ {
++ WSREP_DEBUG("SST script successfully completed.");
++ break;
++ }
++ }
++
++ return -ret;
++}
++
++static void sst_reject_queries(my_bool close_conn)
++{
++ wsrep_ready_set (FALSE); // this will be resotred when donor becomes synced
++ WSREP_INFO("Rejecting client queries for the duration of SST.");
++ if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
++}
++
++static int sst_mysqldump_check_addr (const char* user, const char* pswd,
++ const char* host, const char* port)
++{
++ return 0;
++}
++
++static int sst_donate_mysqldump (const char* addr,
++ const wsrep_uuid_t* uuid,
++ const char* uuid_str,
++ wsrep_seqno_t seqno,
++ bool bypass)
++{
++ size_t host_len;
++ const char* port = strchr (addr, ':');
++
++ if (port)
++ {
++ port += 1;
++ host_len = port - addr;
++ }
++ else
++ {
++ port = "";
++ host_len = strlen (addr) + 1;
++ }
++
++ char *host= (char*) malloc(host_len);
++
++ strncpy (host, addr, host_len - 1);
++ host[host_len - 1] = '\0';
++
++ const char* auth = sst_auth_real;
++ const char* pswd = (auth) ? strchr (auth, ':') : NULL;
++ size_t user_len;
++
++ if (pswd)
++ {
++ pswd += 1;
++ user_len = pswd - auth;
++ }
++ else
++ {
++ pswd = "";
++ user_len = (auth) ? strlen (auth) + 1 : 1;
++ }
++
++ char* user= (char*) malloc(user_len);
++
++ strncpy (user, (auth) ? auth : "", user_len - 1);
++ user[user_len - 1] = '\0';
++
++ int ret = sst_mysqldump_check_addr (user, pswd, host, port);
++ if (!ret)
++ {
++ size_t cmd_len= 1024;
++ char cmd_str[1024];
++
++ if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(TRUE);
++
++ snprintf (cmd_str, cmd_len,
++ "wsrep_sst_mysqldump "
++ WSREP_SST_OPT_USER" '%s' "
++ WSREP_SST_OPT_PSWD" '%s' "
++ WSREP_SST_OPT_HOST" '%s' "
++ WSREP_SST_OPT_PORT" '%s' "
++ WSREP_SST_OPT_LPORT" '%u' "
++ WSREP_SST_OPT_SOCKET" '%s' "
++ WSREP_SST_OPT_CONF" '%s' "
++ WSREP_SST_OPT_GTID" '%s:%lld'"
++ "%s",
++ user, pswd, host, port, mysqld_port, mysqld_unix_port,
++ wsrep_defaults_file, uuid_str,
++ (long long)seqno, bypass ? " "WSREP_SST_OPT_BYPASS : "");
++
++ WSREP_DEBUG("Running: '%s'", cmd_str);
++
++ ret= sst_run_shell (cmd_str, 3);
++ }
++
++ wsrep_gtid_t const state_id = { *uuid, (ret ? WSREP_SEQNO_UNDEFINED : seqno)};
++
++ wsrep->sst_sent (wsrep, &state_id, ret);
++
++ free(user);
++ free(host);
++
++ return ret;
++}
++
++wsrep_seqno_t wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
++
++static int run_sql_command(THD *thd, const char *query)
++{
++ thd->set_query((char *)query, strlen(query));
++
++ Parser_state ps;
++ if (ps.init(thd, thd->query(), thd->query_length()))
++ {
++ WSREP_ERROR("SST query: %s failed", query);
++ return -1;
++ }
++
++ mysql_parse(thd, thd->query(), thd->query_length(), &ps);
++ if (thd->is_error())
++ {
++ int const err= thd->get_stmt_da()->sql_errno();
++ WSREP_WARN ("error executing '%s': %d (%s)%s",
++ query, err, thd->get_stmt_da()->message(),
++ err == ER_UNKNOWN_SYSTEM_VARIABLE ?
++ ". Was mysqld built with --with-innodb-disallow-writes ?" : "");
++ thd->clear_error();
++ return -1;
++ }
++ return 0;
++}
++
++static int sst_flush_tables(THD* thd)
++{
++ WSREP_INFO("Flushing tables for SST...");
++
++ int err;
++ int not_used;
++ if (run_sql_command(thd, "FLUSH TABLES WITH READ LOCK"))
++ {
++ WSREP_ERROR("Failed to flush and lock tables");
++ err = -1;
++ }
++ else
++ {
++ /* make sure logs are flushed after global read lock acquired */
++ err= reload_acl_and_cache(thd, REFRESH_ENGINE_LOG | REFRESH_BINARY_LOG,
++ (TABLE_LIST*) 0, ¬_used);
++ }
++
++ if (err)
++ {
++ WSREP_ERROR("Failed to flush tables: %d (%s)", err, strerror(err));
++ }
++ else
++ {
++ WSREP_INFO("Tables flushed.");
++ const char base_name[]= "tables_flushed";
++ ssize_t const full_len= strlen(mysql_real_data_home) + strlen(base_name)+2;
++ char *real_name = (char*) malloc(full_len);
++ sprintf(real_name, "%s/%s", mysql_real_data_home, base_name);
++ char *tmp_name = (char*) malloc(full_len + 4);
++ sprintf(tmp_name, "%s.tmp", real_name);
++
++ FILE* file= fopen(tmp_name, "w+");
++ if (0 == file)
++ {
++ err= errno;
++ WSREP_ERROR("Failed to open '%s': %d (%s)", tmp_name, err,strerror(err));
++ }
++ else
++ {
++ fprintf(file, "%s:%lld\n",
++ wsrep_cluster_state_uuid, (long long)wsrep_locked_seqno);
++ fsync(fileno(file));
++ fclose(file);
++ if (rename(tmp_name, real_name) == -1)
++ {
++ err= errno;
++ WSREP_ERROR("Failed to rename '%s' to '%s': %d (%s)",
++ tmp_name, real_name, err,strerror(err));
++ }
++ }
++ free(real_name);
++ free(tmp_name);
++ }
++
++ return err;
++}
++
++static void sst_disallow_writes (THD* thd, bool yes)
++{
++ char query_str[64] = { 0, };
++ ssize_t const query_max = sizeof(query_str) - 1;
++ snprintf (query_str, query_max, "SET GLOBAL innodb_disallow_writes=%d",
++ yes ? 1 : 0);
++
++ if (run_sql_command(thd, query_str))
++ {
++ WSREP_ERROR("Failed to disallow InnoDB writes");
++ }
++}
++
++static void* sst_donor_thread (void* a)
++{
++ sst_thread_arg* arg= (sst_thread_arg*)a;
++
++ WSREP_INFO("Running: '%s'", arg->cmd);
++
++ int err= 1;
++ bool locked= false;
++
++ const char* out= NULL;
++ const size_t out_len= 128;
++ char out_buf[out_len];
++
++ wsrep_uuid_t ret_uuid= WSREP_UUID_UNDEFINED;
++ wsrep_seqno_t ret_seqno= WSREP_SEQNO_UNDEFINED; // seqno of complete SST
++
++ wsp::thd thd(FALSE); // we turn off wsrep_on for this THD so that it can
++ // operate with wsrep_ready == OFF
++ wsp::process proc(arg->cmd, "r");
++
++ err= proc.error();
++
++/* Inform server about SST script startup and release TO isolation */
++ mysql_mutex_lock (&arg->lock);
++ arg->err = -err;
++ mysql_cond_signal (&arg->cond);
++ mysql_mutex_unlock (&arg->lock); //! @note arg is unusable after that.
++
++ if (proc.pipe() && !err)
++ {
++wait_signal:
++ out= my_fgets (out_buf, out_len, proc.pipe());
++
++ if (out)
++ {
++ const char magic_flush[]= "flush tables";
++ const char magic_cont[]= "continue";
++ const char magic_done[]= "done";
++
++ if (!strcasecmp (out, magic_flush))
++ {
++ err= sst_flush_tables (thd.ptr);
++ if (!err)
++ {
++ sst_disallow_writes (thd.ptr, true);
++ locked= true;
++ goto wait_signal;
++ }
++ }
++ else if (!strcasecmp (out, magic_cont))
++ {
++ if (locked)
++ {
++ sst_disallow_writes (thd.ptr, false);
++ thd.ptr->global_read_lock.unlock_global_read_lock (thd.ptr);
++ locked= false;
++ }
++ err= 0;
++ goto wait_signal;
++ }
++ else if (!strncasecmp (out, magic_done, strlen(magic_done)))
++ {
++ err= sst_scan_uuid_seqno (out + strlen(magic_done) + 1,
++ &ret_uuid, &ret_seqno);
++ }
++ else
++ {
++ WSREP_WARN("Received unknown signal: '%s'", out);
++ }
++ }
++ else
++ {
++ WSREP_ERROR("Failed to read from: %s", proc.cmd());
++ proc.wait();
++ }
++ if (!err && proc.error()) err= proc.error();
++ }
++ else
++ {
++ WSREP_ERROR("Failed to execute: %s : %d (%s)",
++ proc.cmd(), err, strerror(err));
++ }
++
++ if (locked) // don't forget to unlock server before return
++ {
++ sst_disallow_writes (thd.ptr, false);
++ thd.ptr->global_read_lock.unlock_global_read_lock (thd.ptr);
++ }
++
++ // signal to donor that SST is over
++ struct wsrep_gtid const state_id = {
++ ret_uuid, err ? WSREP_SEQNO_UNDEFINED : ret_seqno
++ };
++ wsrep->sst_sent (wsrep, &state_id, -err);
++ proc.wait();
++
++ return NULL;
++}
++
++
++
++static int sst_donate_other (const char* method,
++ const char* addr,
++ const char* uuid,
++ wsrep_seqno_t seqno,
++ bool bypass)
++{
++ ssize_t cmd_len = 4096;
++ char cmd_str[4096];
++ const char* binlog_opt= "";
++ char* binlog_opt_val= NULL;
++
++ int ret;
++ if ((ret= generate_binlog_opt_val(&binlog_opt_val)))
++ {
++ WSREP_ERROR("sst_donate_other(): generate_binlog_opt_val() failed: %d",ret);
++ return ret;
++ }
++ if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG;
++
++ ret= snprintf (cmd_str, cmd_len,
++ "wsrep_sst_%s "
++ WSREP_SST_OPT_ROLE" 'donor' "
++ WSREP_SST_OPT_ADDR" '%s' "
++ WSREP_SST_OPT_AUTH" '%s' "
++ WSREP_SST_OPT_SOCKET" '%s' "
++ WSREP_SST_OPT_DATA" '%s' "
++ WSREP_SST_OPT_CONF" '%s' "
++ WSREP_SST_OPT_CONF_SUFFIX" '%s' "
++ " %s '%s' "
++ WSREP_SST_OPT_GTID" '%s:%lld'"
++ "%s",
++ method, addr, sst_auth_real, mysqld_unix_port,
++ mysql_real_data_home, wsrep_defaults_file, wsrep_defaults_group_suffix,
++ binlog_opt, binlog_opt_val,
++ uuid, (long long) seqno,
++ bypass ? " "WSREP_SST_OPT_BYPASS : "");
++ my_free(binlog_opt_val);
++
++ if (ret < 0 || ret >= cmd_len)
++ {
++ WSREP_ERROR("sst_donate_other(): snprintf() failed: %d", ret);
++ return (ret < 0 ? ret : -EMSGSIZE);
++ }
++
++ if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(FALSE);
++
++ pthread_t tmp;
++ sst_thread_arg arg(cmd_str);
++ mysql_mutex_lock (&arg.lock);
++ ret = pthread_create (&tmp, NULL, sst_donor_thread, &arg);
++ if (ret)
++ {
++ WSREP_ERROR("sst_donate_other(): pthread_create() failed: %d (%s)",
++ ret, strerror(ret));
++ return ret;
++ }
++ mysql_cond_wait (&arg.cond, &arg.lock);
++
++ WSREP_INFO("sst_donor_thread signaled with %d", arg.err);
++ return arg.err;
++}
++
++wsrep_cb_status_t wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx,
++ const void* msg, size_t msg_len,
++ const wsrep_gtid_t* current_gtid,
++ const char* state, size_t state_len,
++ bool bypass)
++{
++ /* This will be reset when sync callback is called.
++ * Should we set wsrep_ready to FALSE here too? */
++// wsrep_notify_status(WSREP_MEMBER_DONOR);
++ local_status.set(WSREP_MEMBER_DONOR);
++
++ const char* method = (char*)msg;
++ size_t method_len = strlen (method);
++ const char* data = method + method_len + 1;
++
++ char uuid_str[37];
++ wsrep_uuid_print (¤t_gtid->uuid, uuid_str, sizeof(uuid_str));
++
++ int ret;
++ if (!strcmp (WSREP_SST_MYSQLDUMP, method))
++ {
++ ret = sst_donate_mysqldump(data, ¤t_gtid->uuid, uuid_str,
++ current_gtid->seqno, bypass);
++ }
++ else
++ {
++ ret = sst_donate_other(method, data, uuid_str, current_gtid->seqno,bypass);
++ }
++
++ return (ret > 0 ? WSREP_CB_SUCCESS : WSREP_CB_FAILURE);
++}
++
++void wsrep_SE_init_grab()
++{
++ if (mysql_mutex_lock (&LOCK_wsrep_sst_init)) abort();
++}
++
++void wsrep_SE_init_wait()
++{
++ while (SE_initialized == false)
++ {
++ mysql_cond_wait (&COND_wsrep_sst_init, &LOCK_wsrep_sst_init);
++ }
++ mysql_mutex_unlock (&LOCK_wsrep_sst_init);
++}
++
++void wsrep_SE_init_done()
++{
++ mysql_cond_signal (&COND_wsrep_sst_init);
++ mysql_mutex_unlock (&LOCK_wsrep_sst_init);
++}
++
++void wsrep_SE_initialized()
++{
++ SE_initialized = true;
++}
+diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h
+new file mode 100644
+index 0000000..b7f0e26
+--- /dev/null
++++ b/sql/wsrep_sst.h
+@@ -0,0 +1,40 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#ifndef WSREP_SST_H
++#define WSREP_SST_H
++
++#include <mysql.h> // my_bool
++
++/* system variables */
++extern const char* wsrep_sst_method;
++extern const char* wsrep_sst_receive_address;
++extern const char* wsrep_sst_donor;
++extern char* wsrep_sst_auth;
++extern my_bool wsrep_sst_donor_rejects_queries;
++
++/*! Synchronizes applier thread start with init thread */
++extern void wsrep_sst_grab();
++/*! Init thread waits for SST completion */
++extern bool wsrep_sst_wait();
++/*! Signals wsrep that initialization is complete, writesets can be applied */
++extern void wsrep_sst_continue();
++
++extern void wsrep_SE_init_grab(); /*! grab init critical section */
++extern void wsrep_SE_init_wait(); /*! wait for SE init to complete */
++extern void wsrep_SE_init_done(); /*! signal that SE init is complte */
++extern void wsrep_SE_initialized(); /*! mark SE initialization complete */
++
++#endif /* WSREP_SST_H */
+diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
+new file mode 100644
+index 0000000..707a8fe
+--- /dev/null
++++ b/sql/wsrep_thd.cc
+@@ -0,0 +1,576 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#include "wsrep_thd.h"
++
++#include "transaction.h"
++#include "rpl_rli.h"
++#include "log_event.h"
++#include "sql_parse.h"
++#include "global_threads.h" // LOCK_thread_count, etc.
++#include "sql_base.h" // close_thread_tables()
++#include "mysqld.h" // start_wsrep_THD();
++
++static long long wsrep_bf_aborts_counter = 0;
++
++int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff)
++{
++ wsrep_local_bf_aborts = my_atomic_load64(&wsrep_bf_aborts_counter);
++ var->type = SHOW_LONGLONG;
++ var->value = (char*)&wsrep_local_bf_aborts;
++ return 0;
++}
++
++/* must have (&thd->LOCK_wsrep_thd) */
++void wsrep_client_rollback(THD *thd)
++{
++ WSREP_DEBUG("client rollback due to BF abort for (%ld), query: %s",
++ thd->thread_id, thd->query());
++
++ my_atomic_add64(&wsrep_bf_aborts_counter, 1);
++
++ thd->wsrep_conflict_state= ABORTING;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ trans_rollback(thd);
++
++ if (thd->locked_tables_mode && thd->lock)
++ {
++ WSREP_DEBUG("unlocking tables for BF abort (%ld)", thd->thread_id);
++ thd->locked_tables_list.unlock_locked_tables(thd);
++ thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
++ }
++
++ if (thd->global_read_lock.is_acquired())
++ {
++ WSREP_DEBUG("unlocking GRL for BF abort (%ld)", thd->thread_id);
++ thd->global_read_lock.unlock_global_read_lock(thd);
++ }
++
++ /* Release transactional metadata locks. */
++ thd->mdl_context.release_transactional_locks();
++
++ /* release explicit MDL locks */
++ thd->mdl_context.release_explicit_locks();
++
++ if (thd->get_binlog_table_maps())
++ {
++ WSREP_DEBUG("clearing binlog table map for BF abort (%ld)", thd->thread_id);
++ thd->clear_binlog_table_maps();
++ }
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++ thd->wsrep_conflict_state= ABORTED;
++}
++
++#define NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR 1
++#define NUMBER_OF_FIELDS_TO_IDENTIFY_WORKER 2
++#include "rpl_info_factory.h"
++
++static Relay_log_info* wsrep_relay_log_init(const char* log_fname)
++{
++ uint rli_option = INFO_REPOSITORY_DUMMY;
++ Relay_log_info *rli= NULL;
++ rli = Rpl_info_factory::create_rli(rli_option, false);
++ rli->set_rli_description_event(
++ new Format_description_log_event(BINLOG_VERSION));
++
++ return (rli);
++
++#ifdef REMOVED
++ Rpl_info_handler* handler_src= NULL;
++ Rpl_info_handler* handler_dest= NULL;
++ ulong *key_info_idx= NULL;
++ const char *msg= "Failed to allocate memory for the relay log info "
++ "structure";
++
++ DBUG_ENTER("Rpl_info_factory::create_rli");
++
++ if (!(rli= new Relay_log_info(false
++#ifdef HAVE_PSI_INTERFACE
++ ,&key_relay_log_info_run_lock,
++ &key_relay_log_info_data_lock,
++ &key_relay_log_info_sleep_lock,
++ &key_relay_log_info_data_cond,
++ &key_relay_log_info_start_cond,
++ &key_relay_log_info_stop_cond,
++ &key_relay_log_info_sleep_cond
++#endif /* HAVE_PSI_INTERFACE */
++ )))
++ goto err;
++
++ if (!(key_info_idx= new ulong[NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR]))
++ goto err;
++ key_info_idx[0]= server_id;
++ rli->set_idx_info(key_info_idx, NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR);
++
++ if(Rpl_info_factory::init_rli_repositories(rli, rli_option, &handler_src,
++ &handler_dest, &msg))
++ goto err;
++
++ if (Rpl_info_factory::decide_repository(rli, rli_option, &handler_src,
++ &handler_dest, &msg))
++ goto err;
++
++ DBUG_RETURN(rli);
++err:
++ delete handler_src;
++ delete handler_dest;
++ delete []key_info_idx;
++ if (rli)
++ {
++ /*
++ The handler was previously deleted so we need to remove
++ any reference to it.
++ */
++ rli->set_idx_info(NULL, 0);
++ rli->set_rpl_info_handler(NULL);
++ rli->set_rpl_info_type(INVALID_INFO_REPOSITORY);
++ delete rli;
++ }
++ WSREP_ERROR("Error creating relay log info: %s.", msg);
++ DBUG_RETURN(NULL);
++#endif /* REMOVED */
++}
++
++static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
++{
++ shadow->options = thd->variables.option_bits;
++ shadow->server_status = thd->server_status;
++ shadow->wsrep_exec_mode = thd->wsrep_exec_mode;
++ shadow->vio = thd->net.vio;
++
++ if (opt_log_slave_updates)
++ thd->variables.option_bits|= OPTION_BIN_LOG;
++ else
++ thd->variables.option_bits&= ~(OPTION_BIN_LOG);
++
++ if (!thd->wsrep_rli) thd->wsrep_rli= wsrep_relay_log_init("wsrep_relay");
++ thd->wsrep_rli->info_thd = thd;
++
++ thd->wsrep_exec_mode= REPL_RECV;
++ thd->net.vio= 0;
++ thd->clear_error();
++
++ shadow->tx_isolation = thd->variables.tx_isolation;
++ thd->variables.tx_isolation = ISO_READ_COMMITTED;
++ thd->tx_isolation = ISO_READ_COMMITTED;
++
++ shadow->db = thd->db;
++ shadow->db_length = thd->db_length;
++ thd->reset_db(NULL, 0);
++}
++
++static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
++{
++ thd->variables.option_bits = shadow->options;
++ thd->server_status = shadow->server_status;
++ thd->wsrep_exec_mode = shadow->wsrep_exec_mode;
++ thd->net.vio = shadow->vio;
++ thd->variables.tx_isolation = shadow->tx_isolation;
++ thd->reset_db(shadow->db, shadow->db_length);
++}
++
++void wsrep_replay_transaction(THD *thd)
++{
++ /* checking if BF trx must be replayed */
++ if (thd->wsrep_conflict_state== MUST_REPLAY) {
++ DBUG_ASSERT(wsrep_thd_trx_seqno(thd));
++ if (thd->wsrep_exec_mode!= REPL_RECV) {
++ if (thd->get_stmt_da()->is_sent())
++ {
++ WSREP_ERROR("replay issue, thd has reported status already");
++ }
++ thd->get_stmt_da()->reset_diagnostics_area();
++
++ thd->wsrep_conflict_state= REPLAYING;
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++
++ mysql_reset_thd_for_next_command(thd);
++ thd->killed= THD::NOT_KILLED;
++ close_thread_tables(thd);
++ if (thd->locked_tables_mode && thd->lock)
++ {
++ WSREP_DEBUG("releasing table lock for replaying (%ld)",
++ thd->thread_id);
++ thd->locked_tables_list.unlock_locked_tables(thd);
++ thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
++ }
++ thd->mdl_context.release_transactional_locks();
++ /*
++ Replaying will call MYSQL_START_STATEMENT when handling
++ BEGIN Query_log_event so end statement must be called before
++ replaying.
++ */
++ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
++ thd->m_statement_psi= NULL;
++ thd_proc_info(thd, "wsrep replaying trx");
++ WSREP_DEBUG("replay trx: %s %lld",
++ thd->query() ? thd->query() : "void",
++ (long long)wsrep_thd_trx_seqno(thd));
++ struct wsrep_thd_shadow shadow;
++ wsrep_prepare_bf_thd(thd, &shadow);
++
++ /* From trans_begin() */
++ thd->variables.option_bits|= OPTION_BEGIN;
++ thd->server_status|= SERVER_STATUS_IN_TRANS;
++
++ int rcode = wsrep->replay_trx(wsrep,
++ &thd->wsrep_ws_handle,
++ (void *)thd);
++
++ wsrep_return_from_bf_mode(thd, &shadow);
++ if (thd->wsrep_conflict_state!= REPLAYING)
++ WSREP_WARN("lost replaying mode: %d", thd->wsrep_conflict_state );
++
++ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++
++ switch (rcode)
++ {
++ case WSREP_OK:
++ thd->wsrep_conflict_state= NO_CONFLICT;
++ wsrep->post_commit(wsrep, &thd->wsrep_ws_handle);
++ WSREP_DEBUG("trx_replay successful for: %ld %llu",
++ thd->thread_id, (long long)thd->real_id);
++ if (thd->get_stmt_da()->is_sent())
++ {
++ WSREP_WARN("replay ok, thd has reported status");
++ }
++ else if (thd->get_stmt_da()->is_set())
++ {
++ if (thd->get_stmt_da()->status() != Diagnostics_area::DA_OK)
++ {
++ WSREP_WARN("replay ok, thd has error status %d",
++ thd->get_stmt_da()->status());
++ }
++ }
++ else
++ {
++ my_ok(thd);
++ }
++ break;
++ case WSREP_TRX_FAIL:
++ if (thd->get_stmt_da()->is_sent())
++ {
++ WSREP_ERROR("replay failed, thd has reported status");
++ }
++ else
++ {
++ WSREP_DEBUG("replay failed, rolling back");
++ //my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
++ }
++ thd->wsrep_conflict_state= ABORTED;
++ wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle);
++ break;
++ default:
++ WSREP_ERROR("trx_replay failed for: %d, query: %s",
++ rcode, thd->query() ? thd->query() : "void");
++ /* we're now in inconsistent state, must abort */
++ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ unireg_abort(1);
++ break;
++ }
++
++ wsrep_cleanup_transaction(thd);
++
++ mysql_mutex_lock(&LOCK_wsrep_replaying);
++ wsrep_replaying--;
++ WSREP_DEBUG("replaying decreased: %d, thd: %lu",
++ wsrep_replaying, thd->thread_id);
++ mysql_cond_broadcast(&COND_wsrep_replaying);
++ mysql_mutex_unlock(&LOCK_wsrep_replaying);
++ }
++ }
++}
++
++static void wsrep_replication_process(THD *thd)
++{
++ int rcode;
++ DBUG_ENTER("wsrep_replication_process");
++
++ struct wsrep_thd_shadow shadow;
++
++ wsrep_prepare_bf_thd(thd, &shadow);
++
++ /* From trans_begin() */
++ thd->variables.option_bits|= OPTION_BEGIN;
++ thd->server_status|= SERVER_STATUS_IN_TRANS;
++
++ rcode = wsrep->recv(wsrep, (void *)thd);
++ DBUG_PRINT("wsrep",("wsrep_repl returned: %d", rcode));
++
++ WSREP_INFO("applier thread exiting (code:%d)", rcode);
++
++ switch (rcode) {
++ case WSREP_OK:
++ case WSREP_NOT_IMPLEMENTED:
++ case WSREP_CONN_FAIL:
++ /* provider does not support slave operations / disconnected from group,
++ * just close applier thread */
++ break;
++ case WSREP_NODE_FAIL:
++ /* data inconsistency => SST is needed */
++ /* Note: we cannot just blindly restart replication here,
++ * SST might require server restart if storage engines must be
++ * initialized after SST */
++ WSREP_ERROR("node consistency compromised, aborting");
++ wsrep_kill_mysql(thd);
++ break;
++ case WSREP_WARNING:
++ case WSREP_TRX_FAIL:
++ case WSREP_TRX_MISSING:
++ /* these suggests a bug in provider code */
++ WSREP_WARN("bad return from recv() call: %d", rcode);
++ /* fall through to node shutdown */
++ case WSREP_FATAL:
++ /* Cluster connectivity is lost.
++ *
++ * If applier was killed on purpose (KILL_CONNECTION), we
++ * avoid mysql shutdown. This is because the killer will then handle
++ * shutdown processing (or replication restarting)
++ */
++ if (thd->killed != THD::KILL_CONNECTION)
++ {
++ wsrep_kill_mysql(thd);
++ }
++ break;
++ }
++
++ mysql_mutex_lock(&LOCK_thread_count);
++ wsrep_close_applier(thd);
++ mysql_cond_broadcast(&COND_thread_count);
++ mysql_mutex_unlock(&LOCK_thread_count);
++
++ TABLE *tmp;
++ while ((tmp = thd->temporary_tables))
++ {
++ WSREP_WARN("Applier %lu, has temporary tables at exit: %s.%s",
++ thd->thread_id,
++ (tmp->s) ? tmp->s->db.str : "void",
++ (tmp->s) ? tmp->s->table_name.str : "void");
++ }
++ wsrep_return_from_bf_mode(thd, &shadow);
++ DBUG_VOID_RETURN;
++}
++
++void wsrep_create_appliers(long threads)
++{
++ if (!wsrep_connected)
++ {
++ /* see wsrep_replication_start() for the logic */
++ if (wsrep_cluster_address && strlen(wsrep_cluster_address) &&
++ wsrep_provider && strcasecmp(wsrep_provider, "none"))
++ {
++ WSREP_ERROR("Trying to launch slave threads before creating "
++ "connection at '%s'", wsrep_cluster_address);
++ assert(0);
++ }
++ return;
++ }
++
++ long wsrep_threads=0;
++ pthread_t hThread;
++ while (wsrep_threads++ < threads) {
++ if (pthread_create(
++ &hThread, &connection_attrib,
++ start_wsrep_THD, (void*)wsrep_replication_process))
++ WSREP_WARN("Can't create thread to manage wsrep replication");
++ }
++}
++
++static void wsrep_rollback_process(THD *thd)
++{
++ DBUG_ENTER("wsrep_rollback_process");
++
++ mysql_mutex_lock(&LOCK_wsrep_rollback);
++ wsrep_aborting_thd= NULL;
++
++ while (thd->killed == THD::NOT_KILLED) {
++ thd_proc_info(thd, "wsrep aborter idle");
++ thd->mysys_var->current_mutex= &LOCK_wsrep_rollback;
++ thd->mysys_var->current_cond= &COND_wsrep_rollback;
++
++ mysql_cond_wait(&COND_wsrep_rollback,&LOCK_wsrep_rollback);
++
++ WSREP_DEBUG("WSREP rollback thread wakes for signal");
++
++ mysql_mutex_lock(&thd->mysys_var->mutex);
++ thd_proc_info(thd, "wsrep aborter active");
++ thd->mysys_var->current_mutex= 0;
++ thd->mysys_var->current_cond= 0;
++ mysql_mutex_unlock(&thd->mysys_var->mutex);
++
++ /* check for false alarms */
++ if (!wsrep_aborting_thd)
++ {
++ WSREP_DEBUG("WSREP rollback thread has empty abort queue");
++ }
++ /* process all entries in the queue */
++ while (wsrep_aborting_thd) {
++ THD *aborting;
++ wsrep_aborting_thd_t next = wsrep_aborting_thd->next;
++ aborting = wsrep_aborting_thd->aborting_thd;
++ my_free(wsrep_aborting_thd);
++ wsrep_aborting_thd= next;
++ /*
++ * must release mutex, appliers my want to add more
++ * aborting thds in our work queue, while we rollback
++ */
++ mysql_mutex_unlock(&LOCK_wsrep_rollback);
++
++ mysql_mutex_lock(&aborting->LOCK_wsrep_thd);
++ if (aborting->wsrep_conflict_state== ABORTED)
++ {
++ WSREP_DEBUG("WSREP, thd already aborted: %llu state: %d",
++ (long long)aborting->real_id,
++ aborting->wsrep_conflict_state);
++
++ mysql_mutex_unlock(&aborting->LOCK_wsrep_thd);
++ mysql_mutex_lock(&LOCK_wsrep_rollback);
++ continue;
++ }
++ aborting->wsrep_conflict_state= ABORTING;
++
++ mysql_mutex_unlock(&aborting->LOCK_wsrep_thd);
++
++ aborting->store_globals();
++
++ mysql_mutex_lock(&aborting->LOCK_wsrep_thd);
++ wsrep_client_rollback(aborting);
++ WSREP_DEBUG("WSREP rollbacker aborted thd: (%lu %llu)",
++ aborting->thread_id, (long long)aborting->real_id);
++ mysql_mutex_unlock(&aborting->LOCK_wsrep_thd);
++
++ mysql_mutex_lock(&LOCK_wsrep_rollback);
++ }
++ }
++
++ mysql_mutex_unlock(&LOCK_wsrep_rollback);
++ sql_print_information("WSREP: rollbacker thread exiting");
++
++ DBUG_PRINT("wsrep",("wsrep rollbacker thread exiting"));
++ DBUG_VOID_RETURN;
++}
++
++void wsrep_create_rollbacker()
++{
++ if (wsrep_provider && strcasecmp(wsrep_provider, "none"))
++ {
++ pthread_t hThread;
++ /* create rollbacker */
++ if (pthread_create( &hThread, &connection_attrib,
++ start_wsrep_THD, (void*)wsrep_rollback_process))
++ WSREP_WARN("Can't create thread to manage wsrep rollback");
++ }
++}
++
++void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe)
++{
++ if (thd_ptr)
++ {
++ THD* thd = (THD*)thd_ptr;
++ thd->wsrep_PA_safe = safe;
++ }
++}
++
++int wsrep_thd_conflict_state(void *thd_ptr, my_bool sync)
++{
++ int state = -1;
++ if (thd_ptr)
++ {
++ THD* thd = (THD*)thd_ptr;
++ if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++
++ state = thd->wsrep_conflict_state;
++ if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++ return state;
++}
++
++my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync)
++{
++ my_bool status = FALSE;
++ if (thd_ptr)
++ {
++ THD* thd = (THD*)thd_ptr;
++ if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++
++ status = ((thd->wsrep_exec_mode == REPL_RECV) ||
++ (thd->wsrep_exec_mode == TOTAL_ORDER));
++ if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++ return status;
++}
++
++extern "C"
++my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync)
++{
++ bool status = FALSE;
++ if (thd_ptr)
++ {
++ THD* thd = (THD*)thd_ptr;
++ if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++
++ status = ((thd->wsrep_exec_mode == REPL_RECV) ||
++ (thd->wsrep_exec_mode == TOTAL_ORDER) ||
++ (thd->wsrep_exec_mode == LOCAL_COMMIT));
++ if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++ return status;
++}
++
++extern "C"
++my_bool wsrep_thd_is_local(void *thd_ptr, my_bool sync)
++{
++ bool status = FALSE;
++ if (thd_ptr)
++ {
++ THD* thd = (THD*)thd_ptr;
++ if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
++
++ status = (thd->wsrep_exec_mode == LOCAL_STATE);
++ if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
++ }
++ return status;
++}
++
++int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr, my_bool signal)
++{
++ THD *victim_thd = (THD *) victim_thd_ptr;
++ THD *bf_thd = (THD *) bf_thd_ptr;
++ DBUG_ENTER("wsrep_abort_thd");
++
++ if ( (WSREP(bf_thd) ||
++ ( (WSREP_ON || wsrep_OSU_method_options == WSREP_OSU_RSU) &&
++ bf_thd->wsrep_exec_mode == TOTAL_ORDER) ) &&
++ victim_thd)
++ {
++ WSREP_DEBUG("wsrep_abort_thd, by: %llu, victim: %llu", (bf_thd) ?
++ (long long)bf_thd->real_id : 0, (long long)victim_thd->real_id);
++ ha_wsrep_abort_transaction(bf_thd, victim_thd, signal);
++ }
++ else
++ {
++ WSREP_DEBUG("wsrep_abort_thd not effective: %p %p", bf_thd, victim_thd);
++ }
++
++ DBUG_RETURN(1);
++}
++
++int wsrep_thd_in_locking_session(void *thd_ptr)
++{
++ if (thd_ptr && ((THD *)thd_ptr)->in_lock_tables) {
++ return 1;
++ }
++ return 0;
++}
+diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h
+new file mode 100644
+index 0000000..cbf03e6
+--- /dev/null
++++ b/sql/wsrep_thd.h
+@@ -0,0 +1,38 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#ifndef WSREP_THD_H
++#define WSREP_THD_H
++
++#include "sql_class.h"
++
++int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff);
++void wsrep_client_rollback(THD *thd);
++void wsrep_replay_transaction(THD *thd);
++void wsrep_create_appliers(long threads);
++void wsrep_create_rollbacker();
++
++int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr,
++ my_bool signal);
++
++extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
++extern my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync);
++extern int wsrep_thd_conflict_state(void *thd_ptr, my_bool sync);
++//extern "C" my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync);
++extern "C" my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync);
++extern "C" my_bool wsrep_thd_is_local(void *thd_ptr, my_bool sync);
++int wsrep_thd_in_locking_session(void *thd_ptr);
++
++#endif /* WSREP_THD_H */
+diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc
+new file mode 100644
+index 0000000..2a073ac
+--- /dev/null
++++ b/sql/wsrep_utils.cc
+@@ -0,0 +1,524 @@
++/* Copyright 2010 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++//! @file some utility functions and classes not directly related to replication
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE // POSIX_SPAWN_USEVFORK flag
++#endif
++
++#include "wsrep_utils.h"
++#include "wsrep_mysqld.h"
++
++#include <sql_class.h>
++
++#include <spawn.h> // posix_spawn()
++#include <unistd.h> // pipe()
++#include <errno.h> // errno
++#include <string.h> // strerror()
++#include <sys/wait.h> // waitpid()
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h> // getaddrinfo()
++
++#ifdef HAVE_GETIFADDRS
++#include <net/if.h>
++#include <ifaddrs.h>
++#endif // HAVE_GETIFADDRS
++
++extern char** environ; // environment variables
++
++static wsp::string wsrep_PATH;
++
++void
++wsrep_prepend_PATH (const char* path)
++{
++ int count = 0;
++
++ while (environ[count])
++ {
++ if (strncmp (environ[count], "PATH=", 5))
++ {
++ count++;
++ continue;
++ }
++
++ char* const old_path (environ[count]);
++
++ if (strstr (old_path, path)) return; // path already there
++
++ size_t const new_path_len(strlen(old_path) + strlen(":") +
++ strlen(path) + 1);
++
++ char* const new_path (reinterpret_cast<char*>(malloc(new_path_len)));
++
++ if (new_path)
++ {
++ snprintf (new_path, new_path_len, "PATH=%s:%s", path,
++ old_path + strlen("PATH="));
++
++ wsrep_PATH.set (new_path);
++ environ[count] = new_path;
++ }
++ else
++ {
++ WSREP_ERROR ("Failed to allocate 'PATH' environment variable "
++ "buffer of size %zu.", new_path_len);
++ }
++
++ return;
++ }
++
++ WSREP_ERROR ("Failed to find 'PATH' environment variable. "
++ "State snapshot transfer may not be working.");
++}
++
++namespace wsp
++{
++
++#define PIPE_READ 0
++#define PIPE_WRITE 1
++#define STDIN_FD 0
++#define STDOUT_FD 1
++
++#ifndef POSIX_SPAWN_USEVFORK
++# define POSIX_SPAWN_USEVFORK 0
++#endif
++
++process::process (const char* cmd, const char* type)
++ : str_(cmd ? strdup(cmd) : strdup("")), io_(NULL), err_(EINVAL), pid_(0)
++{
++ if (0 == str_)
++ {
++ WSREP_ERROR ("Can't allocate command line of size: %zu", strlen(cmd));
++ err_ = ENOMEM;
++ return;
++ }
++
++ if (0 == strlen(str_))
++ {
++ WSREP_ERROR ("Can't start a process: null or empty command line.");
++ return;
++ }
++
++ if (NULL == type || (strcmp (type, "w") && strcmp(type, "r")))
++ {
++ WSREP_ERROR ("type argument should be either \"r\" or \"w\".");
++ return;
++ }
++
++ int pipe_fds[2] = { -1, };
++ if (::pipe(pipe_fds))
++ {
++ err_ = errno;
++ WSREP_ERROR ("pipe() failed: %d (%s)", err_, strerror(err_));
++ return;
++ }
++
++ // which end of pipe will be returned to parent
++ int const parent_end (strcmp(type,"w") ? PIPE_READ : PIPE_WRITE);
++ int const child_end (parent_end == PIPE_READ ? PIPE_WRITE : PIPE_READ);
++ int const close_fd (parent_end == PIPE_READ ? STDOUT_FD : STDIN_FD);
++
++ char* const pargv[4] = { strdup("sh"), strdup("-c"), strdup(str_), NULL };
++ if (!(pargv[0] && pargv[1] && pargv[2]))
++ {
++ err_ = ENOMEM;
++ WSREP_ERROR ("Failed to allocate pargv[] array.");
++ goto cleanup_pipe;
++ }
++
++ posix_spawnattr_t attr;
++ err_ = posix_spawnattr_init (&attr);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawnattr_init() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_pipe;
++ }
++
++ /* make sure that no signlas are masked in child process */
++ sigset_t sigmask_empty; sigemptyset(&sigmask_empty);
++ err_ = posix_spawnattr_setsigmask(&attr, &sigmask_empty);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawnattr_setsigmask() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_attr;
++ }
++
++ /* make sure the following signals are not ignored in child process */
++ sigset_t default_signals; sigemptyset(&default_signals);
++ sigaddset(&default_signals, SIGHUP);
++ sigaddset(&default_signals, SIGINT);
++ sigaddset(&default_signals, SIGQUIT);
++ sigaddset(&default_signals, SIGPIPE);
++ sigaddset(&default_signals, SIGTERM);
++ sigaddset(&default_signals, SIGCHLD);
++ err_ = posix_spawnattr_setsigdefault(&attr, &default_signals);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawnattr_setsigdefault() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_attr;
++ }
++
++ err_ = posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSIGDEF |
++ POSIX_SPAWN_SETSIGMASK |
++ /* start a new process group */ POSIX_SPAWN_SETPGROUP |
++ POSIX_SPAWN_USEVFORK);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawnattr_setflags() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_attr;
++ }
++
++ posix_spawn_file_actions_t fact;
++ err_ = posix_spawn_file_actions_init (&fact);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawn_file_actions_init() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_attr;
++ }
++
++ // close child's stdout|stdin depending on what we returning
++ err_ = posix_spawn_file_actions_addclose (&fact, close_fd);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawn_file_actions_addclose() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_fact;
++ }
++
++ // substitute our pipe descriptor in place of the closed one
++ err_ = posix_spawn_file_actions_adddup2 (&fact,
++ pipe_fds[child_end], close_fd);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawn_file_actions_addup2() failed: %d (%s)",
++ err_, strerror(err_));
++ goto cleanup_fact;
++ }
++
++ err_ = posix_spawnp (&pid_, pargv[0], &fact, &attr, pargv, environ);
++ if (err_)
++ {
++ WSREP_ERROR ("posix_spawnp(%s) failed: %d (%s)",
++ pargv[2], err_, strerror(err_));
++ pid_ = 0; // just to make sure it was not messed up in the call
++ goto cleanup_fact;
++ }
++
++ io_ = fdopen (pipe_fds[parent_end], type);
++
++ if (io_)
++ {
++ pipe_fds[parent_end] = -1; // skip close on cleanup
++ }
++ else
++ {
++ err_ = errno;
++ WSREP_ERROR ("fdopen() failed: %d (%s)", err_, strerror(err_));
++ }
++
++cleanup_fact:
++ int err; // to preserve err_ code
++ err = posix_spawn_file_actions_destroy (&fact);
++ if (err)
++ {
++ WSREP_ERROR ("posix_spawn_file_actions_destroy() failed: %d (%s)\n",
++ err, strerror(err));
++ }
++
++cleanup_attr:
++ err = posix_spawnattr_destroy (&attr);
++ if (err)
++ {
++ WSREP_ERROR ("posix_spawnattr_destroy() failed: %d (%s)",
++ err, strerror(err));
++ }
++
++cleanup_pipe:
++ if (pipe_fds[0] >= 0) close (pipe_fds[0]);
++ if (pipe_fds[1] >= 0) close (pipe_fds[1]);
++
++ free (pargv[0]);
++ free (pargv[1]);
++ free (pargv[2]);
++}
++
++process::~process ()
++{
++ if (io_)
++ {
++ assert (pid_);
++ assert (str_);
++
++ WSREP_WARN("Closing pipe to child process: %s, PID(%ld) "
++ "which might still be running.", str_, (long)pid_);
++
++ if (fclose (io_) == -1)
++ {
++ err_ = errno;
++ WSREP_ERROR("fclose() failed: %d (%s)", err_, strerror(err_));
++ }
++ }
++
++ if (str_) free (const_cast<char*>(str_));
++}
++
++int
++process::wait ()
++{
++ if (pid_)
++ {
++ int status;
++ if (-1 == waitpid(pid_, &status, 0))
++ {
++ err_ = errno; assert (err_);
++ WSREP_ERROR("Waiting for process failed: %s, PID(%ld): %d (%s)",
++ str_, (long)pid_, err_, strerror (err_));
++ }
++ else
++ { // command completed, check exit status
++ if (WIFEXITED (status)) {
++ err_ = WEXITSTATUS (status);
++ }
++ else { // command didn't complete with exit()
++ WSREP_ERROR("Process was aborted.");
++ err_ = errno ? errno : ECHILD;
++ }
++
++ if (err_) {
++ switch (err_) /* Translate error codes to more meaningful */
++ {
++ case 126: err_ = EACCES; break; /* Permission denied */
++ case 127: err_ = ENOENT; break; /* No such file or directory */
++ }
++ WSREP_ERROR("Process completed with error: %s: %d (%s)",
++ str_, err_, strerror(err_));
++ }
++
++ pid_ = 0;
++ if (io_) fclose (io_);
++ io_ = NULL;
++ }
++ }
++ else {
++ assert (NULL == io_);
++ WSREP_ERROR("Command did not run: %s", str_);
++ }
++
++ return err_;
++}
++
++thd::thd (my_bool won) : init(), ptr(new THD)
++{
++ if (ptr)
++ {
++ ptr->thread_stack= (char*) &ptr;
++ ptr->store_globals();
++ ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog
++ ptr->variables.wsrep_on = won;
++ ptr->security_ctx->master_access= ~(ulong)0;
++ lex_start(ptr);
++ }
++}
++
++thd::~thd ()
++{
++ if (ptr)
++ {
++ delete ptr;
++ my_pthread_setspecific_ptr (THR_THD, 0);
++ }
++}
++
++} // namespace wsp
++
++/* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
++unsigned int wsrep_check_ip (const char* const addr)
++{
++ if (addr && 0 == strcasecmp(addr, MY_BIND_ALL_ADDRESSES)) return INADDR_ANY;
++
++ unsigned int ret = INADDR_NONE;
++ struct addrinfo *res, hints;
++
++ memset (&hints, 0, sizeof(hints));
++ hints.ai_flags= AI_PASSIVE/*|AI_ADDRCONFIG*/;
++ hints.ai_socktype= SOCK_STREAM;
++ hints.ai_family= AF_UNSPEC;
++
++ int gai_ret = getaddrinfo(addr, NULL, &hints, &res);
++ if (0 == gai_ret)
++ {
++ if (AF_INET == res->ai_family) /* IPv4 */
++ {
++ struct sockaddr_in* a= (struct sockaddr_in*)res->ai_addr;
++ ret= htonl(a->sin_addr.s_addr);
++ }
++ else /* IPv6 */
++ {
++ struct sockaddr_in6* a= (struct sockaddr_in6*)res->ai_addr;
++ if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
++ ret= INADDR_ANY;
++ else if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr))
++ ret= INADDR_LOOPBACK;
++ else
++ ret= 0xdeadbeef;
++ }
++ freeaddrinfo (res);
++ }
++ else {
++ WSREP_ERROR ("getaddrinfo() failed on '%s': %d (%s)",
++ addr, gai_ret, gai_strerror(gai_ret));
++ }
++
++ // uint8_t* b= (uint8_t*)&ret;
++ // fprintf (stderr, "########## wsrep_check_ip returning: %hhu.%hhu.%hhu.%hhu\n",
++ // b[0], b[1], b[2], b[3]);
++
++ return ret;
++}
++
++size_t wsrep_guess_ip (char* buf, size_t buf_len)
++{
++ size_t ip_len = 0;
++
++ if (my_bind_addr_str && my_bind_addr_str[0] != '\0')
++ {
++ unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str);
++
++ if (INADDR_NONE == ip_type) {
++ WSREP_ERROR("Networking not configured, cannot receive state transfer.");
++ return 0;
++ }
++
++ if (INADDR_ANY != ip_type) {
++ strncpy (buf, my_bind_addr_str, buf_len);
++ return strlen(buf);
++ }
++ }
++
++ // mysqld binds to all interfaces - try IP from wsrep_node_address
++ if (wsrep_node_address && wsrep_node_address[0] != '\0') {
++ const char* const colon_ptr = strchr(wsrep_node_address, ':');
++
++ if (colon_ptr)
++ ip_len = colon_ptr - wsrep_node_address;
++ else
++ ip_len = strlen(wsrep_node_address);
++
++ if (ip_len >= buf_len) {
++ WSREP_WARN("default_ip(): buffer too short: %zu <= %zd", buf_len, ip_len);
++ return 0;
++ }
++
++ memcpy (buf, wsrep_node_address, ip_len);
++ buf[ip_len] = '\0';
++ return ip_len;
++ }
++
++
++//
++// getifaddrs() is avaiable at least on Linux since glib 2.3, FreeBSD
++// MAC OS X, opensolaris, Solaris.
++//
++// On platforms which do not support getifaddrs() this function returns
++// a failure and user is prompted to do manual configuration.
++//
++#if HAVE_GETIFADDRS
++ struct ifaddrs *ifaddr, *ifa;
++ if (getifaddrs(&ifaddr) == 0)
++ {
++ for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next)
++ {
++ if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) // TODO AF_INET6
++ continue;
++
++ // Skip loopback interfaces (like lo:127.0.0.1)
++ if (ifa->ifa_flags & IFF_LOOPBACK)
++ continue;
++
++ if (vio_getnameinfo(ifa->ifa_addr, buf, buf_len, NULL, 0, NI_NUMERICHOST))
++ continue;
++
++ freeifaddrs(ifaddr);
++ return strlen(buf);
++ }
++ freeifaddrs(ifaddr);
++ }
++#endif // HAVE_GETIFADDRS
++
++ return 0;
++}
++
++/*
++ * WSREPXid
++ */
++
++#define WSREP_XID_PREFIX "WSREPXid"
++#define WSREP_XID_PREFIX_LEN MYSQL_XID_PREFIX_LEN
++#define WSREP_XID_UUID_OFFSET 8
++#define WSREP_XID_SEQNO_OFFSET (WSREP_XID_UUID_OFFSET + sizeof(wsrep_uuid_t))
++#define WSREP_XID_GTRID_LEN (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t))
++
++void wsrep_xid_init(XID* xid, const wsrep_uuid_t* uuid, wsrep_seqno_t seqno)
++{
++ xid->formatID= 1;
++ xid->gtrid_length= WSREP_XID_GTRID_LEN;
++ xid->bqual_length= 0;
++ memset(xid->data, 0, sizeof(xid->data));
++ memcpy(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN);
++ memcpy(xid->data + WSREP_XID_UUID_OFFSET, uuid, sizeof(wsrep_uuid_t));
++ memcpy(xid->data + WSREP_XID_SEQNO_OFFSET, &seqno, sizeof(wsrep_seqno_t));
++}
++
++const wsrep_uuid_t* wsrep_xid_uuid(const XID* xid)
++{
++ if (wsrep_is_wsrep_xid(xid))
++ return reinterpret_cast<const wsrep_uuid_t*>(xid->data
++ + WSREP_XID_UUID_OFFSET);
++ else
++ return &WSREP_UUID_UNDEFINED;
++}
++
++wsrep_seqno_t wsrep_xid_seqno(const XID* xid)
++{
++
++ if (wsrep_is_wsrep_xid(xid))
++ {
++ wsrep_seqno_t seqno;
++ memcpy(&seqno, xid->data + WSREP_XID_SEQNO_OFFSET, sizeof(wsrep_seqno_t));
++ return seqno;
++ }
++ else
++ {
++ return WSREP_SEQNO_UNDEFINED;
++ }
++}
++
++extern
++int wsrep_is_wsrep_xid(const void* xid_ptr)
++{
++ const XID* xid= reinterpret_cast<const XID*>(xid_ptr);
++ return (xid->formatID == 1 &&
++ xid->gtrid_length == WSREP_XID_GTRID_LEN &&
++ xid->bqual_length == 0 &&
++ !memcmp(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN));
++}
+diff --git a/sql/wsrep_utils.h b/sql/wsrep_utils.h
+new file mode 100644
+index 0000000..dfb68bc
+--- /dev/null
++++ b/sql/wsrep_utils.h
+@@ -0,0 +1,207 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#ifndef WSREP_UTILS_H
++#define WSREP_UTILS_H
++
++#include "wsrep_priv.h"
++
++unsigned int wsrep_check_ip (const char* addr);
++size_t wsrep_guess_ip (char* buf, size_t buf_len);
++
++namespace wsp {
++class node_status
++{
++public:
++ node_status() : status(WSREP_MEMBER_UNDEFINED) {}
++ void set(wsrep_member_status_t new_status,
++ const wsrep_view_info_t* view = 0)
++ {
++ if (status != new_status || 0 != view)
++ {
++ wsrep_notify_status(new_status, view);
++ status = new_status;
++ }
++ }
++ wsrep_member_status_t get() const { return status; }
++private:
++ wsrep_member_status_t status;
++};
++} /* namespace wsp */
++
++extern wsp::node_status local_status;
++
++namespace wsp {
++/* A small class to run external programs. */
++class process
++{
++private:
++ const char* const str_;
++ FILE* io_;
++ int err_;
++ pid_t pid_;
++
++public:
++/*! @arg type is a pointer to a null-terminated string which must contain
++ either the letter 'r' for reading or the letter 'w' for writing.
++ */
++ process (const char* cmd, const char* type);
++ ~process ();
++
++ FILE* pipe () { return io_; }
++ int error() { return err_; }
++ int wait ();
++ const char* cmd() { return str_; }
++};
++
++class thd
++{
++ class thd_init
++ {
++ public:
++ thd_init() { my_thread_init(); }
++ ~thd_init() { my_thread_end(); }
++ }
++ init;
++
++ thd (const thd&);
++ thd& operator= (const thd&);
++
++public:
++
++ thd(my_bool wsrep_on);
++ ~thd();
++ THD* const ptr;
++};
++
++class string
++{
++public:
++ string() : string_(0) {}
++ void set(char* str) { if (string_) free (string_); string_ = str; }
++ ~string() { set (0); }
++private:
++ char* string_;
++};
++
++#ifdef REMOVED
++class lock
++{
++ pthread_mutex_t* const mtx_;
++
++public:
++
++ lock (pthread_mutex_t* mtx) : mtx_(mtx)
++ {
++ int err = pthread_mutex_lock (mtx_);
++
++ if (err)
++ {
++ WSREP_ERROR("Mutex lock failed: %s", strerror(err));
++ abort();
++ }
++ }
++
++ virtual ~lock ()
++ {
++ int err = pthread_mutex_unlock (mtx_);
++
++ if (err)
++ {
++ WSREP_ERROR("Mutex unlock failed: %s", strerror(err));
++ abort();
++ }
++ }
++
++ inline void wait (pthread_cond_t* cond)
++ {
++ pthread_cond_wait (cond, mtx_);
++ }
++
++private:
++
++ lock (const lock&);
++ lock& operator=(const lock&);
++
++};
++
++class monitor
++{
++ int mutable refcnt;
++ pthread_mutex_t mutable mtx;
++ pthread_cond_t mutable cond;
++
++public:
++
++ monitor() : refcnt(0)
++ {
++ pthread_mutex_init (&mtx, NULL);
++ pthread_cond_init (&cond, NULL);
++ }
++
++ ~monitor()
++ {
++ pthread_mutex_destroy (&mtx);
++ pthread_cond_destroy (&cond);
++ }
++
++ void enter() const
++ {
++ lock l(&mtx);
++
++ while (refcnt)
++ {
++ l.wait(&cond);
++ }
++ refcnt++;
++ }
++
++ void leave() const
++ {
++ lock l(&mtx);
++
++ refcnt--;
++ if (refcnt == 0)
++ {
++ pthread_cond_signal (&cond);
++ }
++ }
++
++private:
++
++ monitor (const monitor&);
++ monitor& operator= (const monitor&);
++};
++
++class critical
++{
++ const monitor& mon;
++
++public:
++
++ critical(const monitor& m) : mon(m) { mon.enter(); }
++
++ ~critical() { mon.leave(); }
++
++private:
++
++ critical (const critical&);
++ critical& operator= (const critical&);
++};
++#endif
++
++} // namespace wsrep
++
++#endif /* WSREP_UTILS_H */
+diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
+new file mode 100644
+index 0000000..0eea405
+--- /dev/null
++++ b/sql/wsrep_var.cc
+@@ -0,0 +1,628 @@
++/* Copyright 2008 Codership Oy <http://www.codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include "wsrep_var.h"
++
++#include <mysqld.h>
++#include <sql_class.h>
++#include <sql_plugin.h>
++#include <set_var.h>
++#include <sql_acl.h>
++#include "wsrep_priv.h"
++#include "wsrep_thd.h"
++#include <my_dir.h>
++#include <cstdio>
++#include <cstdlib>
++
++#define WSREP_START_POSITION_ZERO "00000000-0000-0000-0000-000000000000:-1"
++#define WSREP_CLUSTER_NAME "my_wsrep_cluster"
++
++const char* wsrep_provider = 0;
++const char* wsrep_provider_options = 0;
++const char* wsrep_cluster_address = 0;
++const char* wsrep_cluster_name = 0;
++const char* wsrep_node_name = 0;
++const char* wsrep_node_address = 0;
++const char* wsrep_node_incoming_address = 0;
++const char* wsrep_start_position = 0;
++ulong wsrep_OSU_method_options;
++
++int wsrep_init_vars()
++{
++ wsrep_provider = my_strdup(WSREP_NONE, MYF(MY_WME));
++ wsrep_provider_options= my_strdup("", MYF(MY_WME));
++ wsrep_cluster_address = my_strdup("", MYF(MY_WME));
++ wsrep_cluster_name = my_strdup(WSREP_CLUSTER_NAME, MYF(MY_WME));
++ wsrep_node_name = my_strdup("", MYF(MY_WME));
++ wsrep_node_address = my_strdup("", MYF(MY_WME));
++ wsrep_node_incoming_address= my_strdup(WSREP_NODE_INCOMING_AUTO, MYF(MY_WME));
++ wsrep_start_position = my_strdup(WSREP_START_POSITION_ZERO, MYF(MY_WME));
++
++ global_system_variables.binlog_format=BINLOG_FORMAT_ROW;
++ return 0;
++}
++
++bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
++{
++ if (var_type == OPT_GLOBAL) {
++ // FIXME: this variable probably should be changed only per session
++ thd->variables.wsrep_on = global_system_variables.wsrep_on;
++ }
++ return false;
++}
++
++bool wsrep_causal_reads_update (sys_var *self, THD* thd, enum_var_type var_type)
++{
++ // global setting should not affect session setting.
++ // if (var_type == OPT_GLOBAL) {
++ // thd->variables.wsrep_causal_reads = global_system_variables.wsrep_causal_reads;
++ // }
++ if (thd->variables.wsrep_causal_reads) {
++ thd->variables.wsrep_sync_wait |= WSREP_SYNC_WAIT_BEFORE_READ;
++ } else {
++ thd->variables.wsrep_sync_wait &= ~WSREP_SYNC_WAIT_BEFORE_READ;
++ }
++
++ // update global settings too.
++ if (global_system_variables.wsrep_causal_reads) {
++ global_system_variables.wsrep_sync_wait |= WSREP_SYNC_WAIT_BEFORE_READ;
++ } else {
++ global_system_variables.wsrep_sync_wait &= ~WSREP_SYNC_WAIT_BEFORE_READ;
++ }
++ return false;
++}
++
++bool wsrep_sync_wait_update (sys_var* self, THD* thd, enum_var_type var_type)
++{
++ // global setting should not affect session setting.
++ // if (var_type == OPT_GLOBAL) {
++ // thd->variables.wsrep_sync_wait = global_system_variables.wsrep_sync_wait;
++ // }
++ thd->variables.wsrep_causal_reads = thd->variables.wsrep_sync_wait &
++ WSREP_SYNC_WAIT_BEFORE_READ;
++
++ // update global settings too
++ global_system_variables.wsrep_causal_reads = global_system_variables.wsrep_sync_wait &
++ WSREP_SYNC_WAIT_BEFORE_READ;
++ return false;
++}
++
++static int wsrep_start_position_verify (const char* start_str)
++{
++ size_t start_len;
++ wsrep_uuid_t uuid;
++ ssize_t uuid_len;
++
++ start_len = strlen (start_str);
++ if (start_len < 34)
++ return 1;
++
++ uuid_len = wsrep_uuid_scan (start_str, start_len, &uuid);
++ if (uuid_len < 0 || (start_len - uuid_len) < 2)
++ return 1;
++
++ if (start_str[uuid_len] != ':') // separator should follow UUID
++ return 1;
++
++ char* endptr;
++ wsrep_seqno_t const seqno __attribute__((unused)) // to avoid GCC warnings
++ (strtoll(&start_str[uuid_len + 1], &endptr, 10));
++
++ if (*endptr == '\0') return 0; // remaining string was seqno
++
++ return 1;
++}
++
++bool wsrep_start_position_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* start_str = NULL;
++
++ if (!(res = var->value->val_str(&str))) goto err;
++
++ start_str = res->c_ptr();
++
++ if (!start_str) goto err;
++
++ if (!wsrep_start_position_verify(start_str)) return 0;
++
++err:
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
++ start_str ? start_str : "NULL");
++ return 1;
++}
++
++void wsrep_set_local_position (const char* value)
++{
++ size_t value_len = strlen (value);
++ size_t uuid_len = wsrep_uuid_scan (value, value_len, &local_uuid);
++
++ local_seqno = strtoll (value + uuid_len + 1, NULL, 10);
++
++ XID xid;
++ wsrep_xid_init(&xid, &local_uuid, local_seqno);
++ wsrep_set_SE_checkpoint(&xid);
++ WSREP_INFO ("wsrep_start_position var submitted: '%s'", wsrep_start_position);
++}
++
++bool wsrep_start_position_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ // since this value passed wsrep_start_position_check, don't check anything
++ // here
++ wsrep_set_local_position (wsrep_start_position);
++
++ if (wsrep) {
++ wsrep_sst_received (wsrep, &local_uuid, local_seqno, NULL, 0);
++ }
++
++ return 0;
++}
++
++void wsrep_start_position_init (const char* val)
++{
++ if (NULL == val || wsrep_start_position_verify (val))
++ {
++ WSREP_ERROR("Bad initial value for wsrep_start_position: %s",
++ (val ? val : ""));
++ return;
++ }
++
++ wsrep_set_local_position (val);
++}
++
++static bool refresh_provider_options()
++{
++ WSREP_DEBUG("refresh_provider_options: %s",
++ (wsrep_provider_options) ? wsrep_provider_options : "null");
++ char* opts= wsrep->options_get(wsrep);
++ if (opts)
++ {
++ if (wsrep_provider_options) my_free((void *)wsrep_provider_options);
++ wsrep_provider_options = (char*)my_memdup(opts, strlen(opts) + 1,
++ MYF(MY_WME));
++ }
++ else
++ {
++ WSREP_ERROR("Failed to get provider options");
++ return true;
++ }
++ return false;
++}
++
++static int wsrep_provider_verify (const char* provider_str)
++{
++ MY_STAT f_stat;
++ char path[FN_REFLEN];
++
++ if (!provider_str || strlen(provider_str)== 0)
++ return 1;
++
++ if (!strcmp(provider_str, WSREP_NONE))
++ return 0;
++
++ if (!unpack_filename(path, provider_str))
++ return 1;
++
++ /* check that provider file exists */
++ memset(&f_stat, 0, sizeof(MY_STAT));
++ if (!my_stat(path, &f_stat, MYF(0)))
++ {
++ return 1;
++ }
++ return 0;
++}
++
++bool wsrep_provider_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* provider_str = NULL;
++
++ if (!(res = var->value->val_str(&str))) goto err;
++
++ provider_str = res->c_ptr();
++
++ if (!provider_str) goto err;
++
++ if (!wsrep_provider_verify(provider_str)) return 0;
++
++err:
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
++ provider_str ? provider_str : "NULL");
++ return 1;
++}
++
++bool wsrep_provider_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ bool rcode= false;
++
++ bool wsrep_on_saved= thd->variables.wsrep_on;
++ thd->variables.wsrep_on= false;
++
++ WSREP_DEBUG("wsrep_provider_update: %s", wsrep_provider);
++
++ /* stop replication is heavy operation, and includes closing all client
++ connections. Closing clients may need to get LOCK_global_system_variables
++ at least in MariaDB.
++
++ Note: releasing LOCK_global_system_variables may cause race condition, if
++ there can be several concurrent clients changing wsrep_provider
++ */
++ mysql_mutex_unlock(&LOCK_global_system_variables);
++ wsrep_stop_replication(thd);
++ mysql_mutex_lock(&LOCK_global_system_variables);
++
++ wsrep_deinit();
++
++ char* tmp= strdup(wsrep_provider); // wsrep_init() rewrites provider
++ //when fails
++ if (wsrep_init())
++ {
++ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp);
++ rcode = true;
++ }
++ free(tmp);
++
++ // we sure don't want to use old address with new provider
++ wsrep_cluster_address_init(NULL);
++ wsrep_provider_options_init(NULL);
++
++ thd->variables.wsrep_on= wsrep_on_saved;
++
++ refresh_provider_options();
++
++ return rcode;
++}
++
++void wsrep_provider_init (const char* value)
++{
++ WSREP_DEBUG("wsrep_provider_init: %s -> %s",
++ (wsrep_provider) ? wsrep_provider : "null",
++ (value) ? value : "null");
++ if (NULL == value || wsrep_provider_verify (value))
++ {
++ WSREP_ERROR("Bad initial value for wsrep_provider: %s",
++ (value ? value : ""));
++ return;
++ }
++
++ if (wsrep_provider) my_free((void *)wsrep_provider);
++ wsrep_provider = my_strdup(value, MYF(0));
++}
++
++bool wsrep_provider_options_check(sys_var *self, THD* thd, set_var* var)
++{
++ return 0;
++}
++
++bool wsrep_provider_options_update(sys_var *self, THD* thd, enum_var_type type)
++{
++ wsrep_status_t ret= wsrep->options_set(wsrep, wsrep_provider_options);
++ if (ret != WSREP_OK)
++ {
++ WSREP_ERROR("Set options returned %d", ret);
++ refresh_provider_options();
++ return true;
++ }
++ return refresh_provider_options();
++}
++
++void wsrep_provider_options_init(const char* value)
++{
++ if (wsrep_provider_options && wsrep_provider_options != value)
++ my_free((void *)wsrep_provider_options);
++ wsrep_provider_options = (value) ? my_strdup(value, MYF(0)) : NULL;
++}
++
++static int wsrep_cluster_address_verify (const char* cluster_address_str)
++{
++ /* There is no predefined address format, it depends on provider. */
++ return 0;
++}
++
++bool wsrep_cluster_address_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* cluster_address_str = NULL;
++
++ if (!(res = var->value->val_str(&str))) goto err;
++
++ cluster_address_str = res->c_ptr();
++
++ if (!wsrep_cluster_address_verify(cluster_address_str)) return 0;
++
++ err:
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
++ cluster_address_str ? cluster_address_str : "NULL");
++ return 1 ;
++}
++
++bool wsrep_cluster_address_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ bool wsrep_on_saved= thd->variables.wsrep_on;
++ thd->variables.wsrep_on= false;
++
++ /* stop replication is heavy operation, and includes closing all client
++ connections. Closing clients may need to get LOCK_global_system_variables
++ at least in MariaDB.
++
++ Note: releasing LOCK_global_system_variables may cause race condition, if
++ there can be several concurrent clients changing wsrep_provider
++ */
++ mysql_mutex_unlock(&LOCK_global_system_variables);
++ wsrep_stop_replication(thd);
++ mysql_mutex_lock(&LOCK_global_system_variables);
++
++ if (wsrep_start_replication())
++ {
++ wsrep_create_rollbacker();
++ wsrep_create_appliers(wsrep_slave_threads);
++ }
++
++ thd->variables.wsrep_on= wsrep_on_saved;
++
++ return false;
++}
++
++void wsrep_cluster_address_init (const char* value)
++{
++ WSREP_DEBUG("wsrep_cluster_address_init: %s -> %s",
++ (wsrep_cluster_address) ? wsrep_cluster_address : "null",
++ (value) ? value : "null");
++
++ if (wsrep_cluster_address) my_free ((void*)wsrep_cluster_address);
++ wsrep_cluster_address = (value) ? my_strdup(value, MYF(0)) : NULL;
++}
++
++bool wsrep_cluster_name_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* cluster_name_str = NULL;
++
++ if (!(res = var->value->val_str(&str))) goto err;
++
++ cluster_name_str = res->c_ptr();
++
++ if (!cluster_name_str || strlen(cluster_name_str) == 0) goto err;
++
++ return 0;
++
++ err:
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
++ cluster_name_str ? cluster_name_str : "NULL");
++ return 1;
++}
++
++bool wsrep_cluster_name_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ return 0;
++}
++
++bool wsrep_node_name_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* node_name_str = NULL;
++
++ if (!(res = var->value->val_str(&str))) goto err;
++
++ node_name_str = res->c_ptr();
++
++ if (!node_name_str || strlen(node_name_str) == 0) goto err;
++
++ return 0;
++
++ err:
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
++ node_name_str ? node_name_str : "NULL");
++ return 1;
++}
++
++bool wsrep_node_name_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ return 0;
++}
++
++// TODO: do something more elaborate, like checking connectivity
++bool wsrep_node_address_check (sys_var *self, THD* thd, set_var* var)
++{
++ char buff[FN_REFLEN];
++ String str(buff, sizeof(buff), system_charset_info), *res;
++ const char* node_address_str = NULL;
++
++ if (!(res = var->value->val_str(&str))) goto err;
++
++ node_address_str = res->c_ptr();
++
++ if (!node_address_str || strlen(node_address_str) == 0) goto err;
++
++ return 0;
++
++ err:
++
++ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
++ node_address_str ? node_address_str : "NULL");
++ return 1;
++}
++
++bool wsrep_node_address_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ return 0;
++}
++
++void wsrep_node_address_init (const char* value)
++{
++ if (wsrep_node_address && strcmp(wsrep_node_address, value))
++ my_free ((void*)wsrep_node_address);
++
++ wsrep_node_address = (value) ? my_strdup(value, MYF(0)) : NULL;
++}
++
++bool wsrep_slave_threads_check (sys_var *self, THD* thd, set_var* var)
++{
++ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
++ wsrep_slave_count_change = var->value->val_int() - wsrep_slave_threads;
++ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
++
++ return 0;
++}
++
++bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ if (wsrep_slave_count_change > 0)
++ {
++ wsrep_create_appliers(wsrep_slave_count_change);
++ wsrep_slave_count_change = 0;
++ }
++ return false;
++}
++
++bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
++{
++ bool new_wsrep_desync = var->value->val_bool();
++ if (wsrep_desync == new_wsrep_desync) {
++ if (new_wsrep_desync) {
++ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
++ ER_WRONG_VALUE_FOR_VAR,
++ "'wsrep_desync' is already ON.");
++ } else {
++ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
++ ER_WRONG_VALUE_FOR_VAR,
++ "'wsrep_desync' is already OFF.");
++ }
++ }
++ return 0;
++}
++
++bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
++{
++ wsrep_status_t ret(WSREP_WARNING);
++ if (wsrep_desync) {
++ ret = wsrep->desync (wsrep);
++ if (ret != WSREP_OK) {
++ WSREP_WARN ("SET desync failed %d for %s", ret, thd->query());
++ my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query());
++ return true;
++ }
++ } else {
++ ret = wsrep->resync (wsrep);
++ if (ret != WSREP_OK) {
++ WSREP_WARN ("SET resync failed %d for %s", ret, thd->query());
++ my_error (ER_CANNOT_USER, MYF(0), "'resync'", thd->query());
++ return true;
++ }
++ }
++ return false;
++}
++
++/*
++ * Status variables stuff below
++ */
++static inline void
++wsrep_assign_to_mysql (SHOW_VAR* mysql, wsrep_stats_var* wsrep)
++{
++ mysql->name = wsrep->name;
++ switch (wsrep->type) {
++ case WSREP_VAR_INT64:
++ mysql->value = (char*) &wsrep->value._int64;
++ mysql->type = SHOW_LONGLONG;
++ break;
++ case WSREP_VAR_STRING:
++ mysql->value = (char*) &wsrep->value._string;
++ mysql->type = SHOW_CHAR_PTR;
++ break;
++ case WSREP_VAR_DOUBLE:
++ mysql->value = (char*) &wsrep->value._double;
++ mysql->type = SHOW_DOUBLE;
++ break;
++ }
++}
++
++#if DYNAMIC
++// somehow this mysql status thing works only with statically allocated arrays.
++static SHOW_VAR* mysql_status_vars = NULL;
++static int mysql_status_len = -1;
++#else
++static SHOW_VAR mysql_status_vars[512 + 1];
++static const int mysql_status_len = 512;
++#endif
++
++static void export_wsrep_status_to_mysql(THD* thd)
++{
++ int wsrep_status_len, i;
++
++ thd->wsrep_status_vars = wsrep->stats_get(wsrep);
++
++ if (!thd->wsrep_status_vars) {
++ return;
++ }
++
++ for (wsrep_status_len = 0;
++ thd->wsrep_status_vars[wsrep_status_len].name != NULL;
++ wsrep_status_len++) {
++ /* */
++ }
++
++#if DYNAMIC
++ if (wsrep_status_len != mysql_status_len) {
++ void* tmp = realloc (mysql_status_vars,
++ (wsrep_status_len + 1) * sizeof(SHOW_VAR));
++ if (!tmp) {
++
++ sql_print_error ("Out of memory for wsrep status variables."
++ "Number of variables: %d", wsrep_status_len);
++ return;
++ }
++
++ mysql_status_len = wsrep_status_len;
++ mysql_status_vars = (SHOW_VAR*)tmp;
++ }
++ /* @TODO: fix this: */
++#else
++ if (mysql_status_len < wsrep_status_len) wsrep_status_len= mysql_status_len;
++#endif
++
++ for (i = 0; i < wsrep_status_len; i++)
++ wsrep_assign_to_mysql (mysql_status_vars + i, thd->wsrep_status_vars + i);
++
++ mysql_status_vars[wsrep_status_len].name = NullS;
++ mysql_status_vars[wsrep_status_len].value = NullS;
++ mysql_status_vars[wsrep_status_len].type = SHOW_LONG;
++}
++
++int wsrep_show_status (THD *thd, SHOW_VAR *var, char *buff)
++{
++ export_wsrep_status_to_mysql(thd);
++ var->type= SHOW_ARRAY;
++ var->value= (char *) &mysql_status_vars;
++ return 0;
++}
++
++void wsrep_free_status (THD* thd)
++{
++ if (thd->wsrep_status_vars)
++ {
++ wsrep->stats_free (wsrep, thd->wsrep_status_vars);
++ thd->wsrep_status_vars = 0;
++ }
++}
+diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h
+new file mode 100644
+index 0000000..d845987
+--- /dev/null
++++ b/sql/wsrep_var.h
+@@ -0,0 +1,84 @@
++/* Copyright (C) 2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
++
++#ifndef WSREP_VAR_H
++#define WSREP_VAR_H
++
++#define WSREP_NODE_INCOMING_AUTO "AUTO"
++
++// MySQL variables funcs
++
++#include "sql_priv.h"
++class sys_var;
++class set_var;
++class THD;
++
++int wsrep_init_vars();
++
++#define CHECK_ARGS (sys_var *self, THD* thd, set_var *var)
++#define UPDATE_ARGS (sys_var *self, THD* thd, enum_var_type type)
++#define DEFAULT_ARGS (THD* thd, enum_var_type var_type)
++#define INIT_ARGS (const char* opt)
++
++extern bool wsrep_on_update UPDATE_ARGS;
++extern bool wsrep_causal_reads_update UPDATE_ARGS;
++extern bool wsrep_sync_wait_update UPDATE_ARGS;
++extern bool wsrep_start_position_check CHECK_ARGS;
++extern bool wsrep_start_position_update UPDATE_ARGS;
++extern void wsrep_start_position_init INIT_ARGS;
++
++extern bool wsrep_provider_check CHECK_ARGS;
++extern bool wsrep_provider_update UPDATE_ARGS;
++extern void wsrep_provider_init INIT_ARGS;
++
++extern bool wsrep_provider_options_check CHECK_ARGS;
++extern bool wsrep_provider_options_update UPDATE_ARGS;
++extern void wsrep_provider_options_init INIT_ARGS;
++
++extern bool wsrep_cluster_address_check CHECK_ARGS;
++extern bool wsrep_cluster_address_update UPDATE_ARGS;
++extern void wsrep_cluster_address_init INIT_ARGS;
++
++extern bool wsrep_cluster_name_check CHECK_ARGS;
++extern bool wsrep_cluster_name_update UPDATE_ARGS;
++
++extern bool wsrep_node_name_check CHECK_ARGS;
++extern bool wsrep_node_name_update UPDATE_ARGS;
++
++extern bool wsrep_node_address_check CHECK_ARGS;
++extern bool wsrep_node_address_update UPDATE_ARGS;
++extern void wsrep_node_address_init INIT_ARGS;
++
++extern bool wsrep_sst_method_check CHECK_ARGS;
++extern bool wsrep_sst_method_update UPDATE_ARGS;
++extern void wsrep_sst_method_init INIT_ARGS;
++
++extern bool wsrep_sst_receive_address_check CHECK_ARGS;
++extern bool wsrep_sst_receive_address_update UPDATE_ARGS;
++
++extern bool wsrep_sst_auth_check CHECK_ARGS;
++extern bool wsrep_sst_auth_update UPDATE_ARGS;
++extern void wsrep_sst_auth_init INIT_ARGS;
++
++extern bool wsrep_sst_donor_check CHECK_ARGS;
++extern bool wsrep_sst_donor_update UPDATE_ARGS;
++
++extern bool wsrep_slave_threads_check CHECK_ARGS;
++extern bool wsrep_slave_threads_update UPDATE_ARGS;
++
++extern bool wsrep_desync_check CHECK_ARGS;
++extern bool wsrep_desync_update UPDATE_ARGS;
++
++#endif /* WSREP_VAR_H */
+diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
+index 1611fb6..392c9e3 100644
+--- a/storage/innobase/btr/btr0cur.cc
++++ b/storage/innobase/btr/btr0cur.cc
+@@ -2946,7 +2946,9 @@ btr_cur_del_mark_set_clust_rec(
+ #endif /* UNIV_DEBUG */
+
+ ut_ad(dict_index_is_clust(index));
++#ifndef WITH_WSREP
+ ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets)));
++#endif
+
+ err = lock_clust_rec_modify_check_and_lock(BTR_NO_LOCKING_FLAG, block,
+ rec, index, offsets, thr);
+diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
+index 8045389..fb5dcae 100644
+--- a/storage/innobase/dict/dict0dict.cc
++++ b/storage/innobase/dict/dict0dict.cc
+@@ -3347,7 +3347,29 @@ dict_foreign_find_index(
+
+ return(NULL);
+ }
+-
++#ifdef WITH_WSREP
++dict_index_t*
++wsrep_dict_foreign_find_index(
++/*====================*/
++ dict_table_t* table, /*!< in: table */
++ const char** col_names, /*!< in: column names, or NULL
++ to use table->col_names */
++ const char** columns,/*!< in: array of column names */
++ ulint n_cols, /*!< in: number of columns */
++ dict_index_t* types_idx, /*!< in: NULL or an index to whose types the
++ column types must match */
++ ibool check_charsets,
++ /*!< in: whether to check charsets.
++ only has an effect if types_idx != NULL */
++ ulint check_null)
++ /*!< in: nonzero if none of the columns must
++ be declared NOT NULL */
++{
++ return dict_foreign_find_index(
++ table, col_names, columns, n_cols, types_idx, check_charsets,
++ check_null);
++}
++#endif /* WITH_WSREP */
+ /**********************************************************************//**
+ Report an error in a foreign key definition. */
+ static
+diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc
+index 5891b53..2e2bd06 100644
+--- a/storage/innobase/fts/fts0opt.cc
++++ b/storage/innobase/fts/fts0opt.cc
+@@ -2577,6 +2577,8 @@ fts_optimize_add_table(
+ return;
+ }
+
++ ut_ad(table->cached && table->fts != NULL);
++
+ /* Make sure table with FTS index cannot be evicted */
+ if (table->can_be_evicted) {
+ dict_table_move_from_lru_to_non_lru(table);
+diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
+index b8f6351..b54ab75 100644
+--- a/storage/innobase/handler/ha_innodb.cc
++++ b/storage/innobase/handler/ha_innodb.cc
+@@ -95,6 +95,9 @@ this program; if not, write to the Free Software Foundation, Inc.,
+ #include "fts0priv.h"
+ #include "page0zip.h"
+
++#ifdef WITH_WSREP
++#include "dict0priv.h"
++#endif /* WITH_WSREP */
+ enum_tx_isolation thd_get_trx_isolation(const THD* thd);
+
+ #include "ha_innodb.h"
+@@ -103,6 +106,35 @@ enum_tx_isolation thd_get_trx_isolation(const THD* thd);
+ # ifndef MYSQL_PLUGIN_IMPORT
+ # define MYSQL_PLUGIN_IMPORT /* nothing */
+ # endif /* MYSQL_PLUGIN_IMPORT */
++#ifdef WITH_WSREP
++#include "../storage/innobase/include/ut0byte.h"
++#include <wsrep_mysqld.h>
++#include <my_md5.h>
++extern my_bool wsrep_certify_nonPK;
++class binlog_trx_data;
++extern handlerton *binlog_hton;
++
++extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_wsrep_rollback;
++extern MYSQL_PLUGIN_IMPORT mysql_cond_t COND_wsrep_rollback;
++extern MYSQL_PLUGIN_IMPORT wsrep_aborting_thd_t wsrep_aborting_thd;
++
++static inline wsrep_ws_handle_t*
++wsrep_ws_handle(THD* thd, const trx_t* trx) {
++ return wsrep_ws_handle_for_trx(wsrep_thd_ws_handle(thd),
++ (wsrep_trx_id_t)trx->id);
++}
++
++extern bool wsrep_prepare_key_for_innodb(const uchar *cache_key,
++ size_t cache_key_len,
++ const uchar* row_id,
++ size_t row_id_len,
++ wsrep_buf_t* key,
++ size_t* key_len);
++
++extern handlerton * wsrep_hton;
++extern TC_LOG* tc_log;
++extern void wsrep_cleanup_transaction(THD *thd);
++#endif /* WITH_WSREP */
+
+ /** to protect innobase_open_files */
+ static mysql_mutex_t innobase_share_mutex;
+@@ -1152,6 +1184,10 @@ innobase_srv_conc_enter_innodb(
+ /*===========================*/
+ trx_t* trx) /*!< in: transaction handle */
+ {
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd) &&
++ wsrep_thd_is_BF(trx->mysql_thd, FALSE)) return;
++#endif /* WITH_WSREP */
+ if (srv_thread_concurrency) {
+ if (trx->n_tickets_to_enter_innodb > 0) {
+
+@@ -1186,6 +1222,10 @@ innobase_srv_conc_exit_innodb(
+ #ifdef UNIV_SYNC_DEBUG
+ ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
+ #endif /* UNIV_SYNC_DEBUG */
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd) &&
++ wsrep_thd_is_BF(trx->mysql_thd, FALSE)) return;
++#endif /* WITH_WSREP */
+
+ /* This is to avoid making an unnecessary function call. */
+ if (trx->declared_to_be_inside_innodb
+@@ -1306,6 +1346,15 @@ thd_to_trx(
+ {
+ return(*(trx_t**) thd_ha_data(thd, innodb_hton_ptr));
+ }
++#ifdef WITH_WSREP
++ulonglong
++thd_to_trx_id(
++/*=======*/
++ THD* thd) /*!< in: MySQL thread */
++{
++ return(thd_to_trx(thd)->id);
++}
++#endif
+
+ /********************************************************************//**
+ Call this function when mysqld passes control to the client. That is to
+@@ -1335,6 +1384,15 @@ innobase_release_temporary_latches(
+ return(0);
+ }
+
++#ifdef WITH_WSREP
++static int
++wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd,
++ my_bool signal);
++static void
++wsrep_fake_trx_id(handlerton* hton, THD *thd);
++static int innobase_wsrep_set_checkpoint(handlerton* hton, const XID* xid);
++static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid);
++#endif
+ /********************************************************************//**
+ Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
+ time calls srv_active_wake_master_thread. This function should be used
+@@ -1800,6 +1858,9 @@ int
+ innobase_mysql_tmpfile(void)
+ /*========================*/
+ {
++#ifdef WITH_INNODB_DISALLOW_WRITES
++ os_event_wait(srv_allow_writes_event);
++#endif /* WITH_INNODB_DISALLOW_WRITES */
+ int fd2 = -1;
+ File fd;
+
+@@ -2844,6 +2905,12 @@ innobase_init(
+ innobase_release_temporary_latches;
+
+ innobase_hton->data = &innodb_api_cb;
++#ifdef WITH_WSREP
++ innobase_hton->wsrep_abort_transaction=wsrep_abort_transaction;
++ innobase_hton->wsrep_set_checkpoint=innobase_wsrep_set_checkpoint;
++ innobase_hton->wsrep_get_checkpoint=innobase_wsrep_get_checkpoint;
++ innobase_hton->wsrep_fake_trx_id=wsrep_fake_trx_id;
++#endif /* WITH_WSREP */
+
+ ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);
+
+@@ -3407,10 +3474,30 @@ innobase_commit_low(
+ /*================*/
+ trx_t* trx) /*!< in: transaction handle */
+ {
++#ifdef WITH_WSREP
++ THD* thd = (THD*)trx->mysql_thd;
++ const char* tmp = 0;
++ if (wsrep_on((void*)thd)) {
++#ifdef WSREP_PROC_INFO
++ char info[64];
++ info[sizeof(info) - 1] = '\0';
++ snprintf(info, sizeof(info) - 1,
++ "innobase_commit_low():trx_commit_for_mysql(%lld)",
++ (long long) wsrep_thd_trx_seqno(thd));
++ tmp = thd_proc_info(thd, info);
++
++#else
++ tmp = thd_proc_info(thd, "innobase_commit_low()");
++#endif /* WSREP_PROC_INFO */
++ }
++#endif /* WITH_WSREP */
+ if (trx_is_started(trx)) {
+
+ trx_commit_for_mysql(trx);
+ }
++#ifdef WITH_WSREP
++ if (wsrep_on((void*)thd)) { thd_proc_info(thd, tmp); }
++#endif /* WITH_WSREP */
+ }
+
+ /*****************************************************************//**
+@@ -4063,7 +4150,11 @@ ha_innobase::max_supported_key_length() const
+ case 8192:
+ return(1536);
+ default:
++#ifdef WITH_WSREP
++ return(3500);
++#else
+ return(3500);
++#endif
+ }
+ }
+
+@@ -5163,6 +5254,112 @@ get_field_offset(
+ {
+ return((uint) (field->ptr - table->record[0]));
+ }
++#ifdef WITH_WSREP
++UNIV_INTERN
++int
++wsrep_innobase_mysql_sort(
++/*===============*/
++ /* out: str contains sort string */
++ int mysql_type, /* in: MySQL type */
++ uint charset_number, /* in: number of the charset */
++ unsigned char* str, /* in: data field */
++ unsigned int str_length, /* in: data field length,
++ not UNIV_SQL_NULL */
++ unsigned int buf_length) /* in: total str buffer length */
++
++{
++ CHARSET_INFO* charset;
++ enum_field_types mysql_tp;
++ int ret_length = str_length;
++
++ DBUG_ASSERT(str_length != UNIV_SQL_NULL);
++
++ mysql_tp = (enum_field_types) mysql_type;
++
++ switch (mysql_tp) {
++
++ case MYSQL_TYPE_BIT:
++ case MYSQL_TYPE_STRING:
++ case MYSQL_TYPE_VAR_STRING:
++ case MYSQL_TYPE_TINY_BLOB:
++ case MYSQL_TYPE_MEDIUM_BLOB:
++ case MYSQL_TYPE_BLOB:
++ case MYSQL_TYPE_LONG_BLOB:
++ case MYSQL_TYPE_VARCHAR:
++ {
++ uchar tmp_str[REC_VERSION_56_MAX_INDEX_COL_LEN] = {'\0'};
++ uint tmp_length = REC_VERSION_56_MAX_INDEX_COL_LEN;
++
++ /* Use the charset number to pick the right charset struct for
++ the comparison. Since the MySQL function get_charset may be
++ slow before Bar removes the mutex operation there, we first
++ look at 2 common charsets directly. */
++
++ if (charset_number == default_charset_info->number) {
++ charset = default_charset_info;
++ } else if (charset_number == my_charset_latin1.number) {
++ charset = &my_charset_latin1;
++ } else {
++ charset = get_charset(charset_number, MYF(MY_WME));
++
++ if (charset == NULL) {
++ sql_print_error("InnoDB needs charset %lu for doing "
++ "a comparison, but MySQL cannot "
++ "find that charset.",
++ (ulong) charset_number);
++ ut_a(0);
++ }
++ }
++
++ ut_a(str_length <= tmp_length);
++ memcpy(tmp_str, str, str_length);
++
++ if (wsrep_protocol_version < 3) {
++ tmp_length = charset->coll->strnxfrm(
++ charset, str, str_length,
++ str_length, tmp_str, tmp_length, 0);
++ DBUG_ASSERT(tmp_length <= str_length);
++ } else {
++ /* strnxfrm will expand the destination string,
++ protocols < 3 truncated the sorted sring
++ protocols >= 3 gets full sorted sring
++ */
++ tmp_length = charset->coll->strnxfrm(
++ charset, str, buf_length,
++ str_length, tmp_str, str_length, 0);
++ DBUG_ASSERT(tmp_length <= buf_length);
++ ret_length = tmp_length;
++ }
++
++ break;
++ }
++ case MYSQL_TYPE_DECIMAL :
++ case MYSQL_TYPE_TINY :
++ case MYSQL_TYPE_SHORT :
++ case MYSQL_TYPE_LONG :
++ case MYSQL_TYPE_FLOAT :
++ case MYSQL_TYPE_DOUBLE :
++ case MYSQL_TYPE_NULL :
++ case MYSQL_TYPE_TIMESTAMP :
++ case MYSQL_TYPE_LONGLONG :
++ case MYSQL_TYPE_INT24 :
++ case MYSQL_TYPE_DATE :
++ case MYSQL_TYPE_TIME :
++ case MYSQL_TYPE_DATETIME :
++ case MYSQL_TYPE_YEAR :
++ case MYSQL_TYPE_NEWDATE :
++ case MYSQL_TYPE_NEWDECIMAL :
++ case MYSQL_TYPE_ENUM :
++ case MYSQL_TYPE_SET :
++ case MYSQL_TYPE_GEOMETRY :
++ break;
++ default:
++ break;
++ }
++
++ return ret_length;
++}
++#endif // WITH_WSREP
+
+ /*************************************************************//**
+ InnoDB uses this function to compare two data fields for which the data type
+@@ -5675,6 +5872,307 @@ innobase_read_from_2_little_endian(
+ /*******************************************************************//**
+ Stores a key value for a row to a buffer.
+ @return key value length as stored in buff */
++#ifdef WITH_WSREP
++UNIV_INTERN
++uint
++wsrep_store_key_val_for_row(
++/*===============================*/
++ THD* thd,
++ TABLE* table,
++ uint keynr, /*!< in: key number */
++ char* buff, /*!< in/out: buffer for the key value (in MySQL
++ format) */
++ uint buff_len,/*!< in: buffer length */
++ const uchar* record,
++ ibool* key_is_null)/*!< out: full key was null */
++{
++ KEY* key_info = table->key_info + keynr;
++ KEY_PART_INFO* key_part = key_info->key_part;
++ KEY_PART_INFO* end =
++ key_part + key_info->user_defined_key_parts;
++ char* buff_start = buff;
++ enum_field_types mysql_type;
++ Field* field;
++ uint buff_space = buff_len;
++
++ DBUG_ENTER("wsrep_store_key_val_for_row");
++
++ memset(buff, 0, buff_len);
++ *key_is_null = TRUE;
++
++ for (; key_part != end; key_part++) {
++
++ uchar sorted[REC_VERSION_56_MAX_INDEX_COL_LEN] = {'\0'};
++ ibool part_is_null = FALSE;
++
++ if (key_part->null_bit) {
++ if (buff_space > 0) {
++ if (record[key_part->null_offset]
++ & key_part->null_bit) {
++ *buff = 1;
++ part_is_null = TRUE;
++ } else {
++ *buff = 0;
++ }
++ buff++;
++ buff_space--;
++ } else {
++ fprintf (stderr, "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ }
++ }
++ if (!part_is_null) *key_is_null = FALSE;
++
++ field = key_part->field;
++ mysql_type = field->type();
++
++ if (mysql_type == MYSQL_TYPE_VARCHAR) {
++ /* >= 5.0.3 true VARCHAR */
++ ulint lenlen;
++ ulint len;
++ const byte* data;
++ ulint key_len;
++ ulint true_len;
++ const CHARSET_INFO* cs;
++ int error=0;
++
++ key_len = key_part->length;
++
++ if (part_is_null) {
++ true_len = key_len + 2;
++ if (true_len > buff_space) {
++ fprintf (stderr,
++ "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ true_len = buff_space;
++ }
++ buff += true_len;
++ buff_space -= true_len;
++ continue;
++ }
++ cs = field->charset();
++
++ lenlen = (ulint)
++ (((Field_varstring*)field)->length_bytes);
++
++ data = row_mysql_read_true_varchar(&len,
++ (byte*) (record
++ + (ulint)get_field_offset(table, field)),
++ lenlen);
++
++ true_len = len;
++
++ /* For multi byte character sets we need to calculate
++ the true length of the key */
++
++ if (len > 0 && cs->mbmaxlen > 1) {
++ true_len = (ulint) cs->cset->well_formed_len(cs,
++ (const char *) data,
++ (const char *) data + len,
++ (uint) (key_len / cs->mbmaxlen),
++ &error);
++ }
++
++ /* In a column prefix index, we may need to truncate
++ the stored value: */
++
++ if (true_len > key_len) {
++ true_len = key_len;
++ }
++
++ memcpy(sorted, data, true_len);
++ true_len = wsrep_innobase_mysql_sort(
++ mysql_type, cs->number, sorted, true_len,
++ REC_VERSION_56_MAX_INDEX_COL_LEN);
++
++ if (wsrep_protocol_version > 1) {
++ /* Note that we always reserve the maximum possible
++ length of the true VARCHAR in the key value, though
++ only len first bytes after the 2 length bytes contain
++ actual data. The rest of the space was reset to zero
++ in the bzero() call above. */
++ if (true_len > buff_space) {
++ fprintf (stderr,
++ "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ true_len = buff_space;
++ }
++ memcpy(buff, sorted, true_len);
++ buff += true_len;
++ buff_space -= true_len;
++ } else {
++ buff += key_len;
++ }
++ } else if (mysql_type == MYSQL_TYPE_TINY_BLOB
++ || mysql_type == MYSQL_TYPE_MEDIUM_BLOB
++ || mysql_type == MYSQL_TYPE_BLOB
++ || mysql_type == MYSQL_TYPE_LONG_BLOB
++ /* MYSQL_TYPE_GEOMETRY data is treated
++ as BLOB data in innodb. */
++ || mysql_type == MYSQL_TYPE_GEOMETRY) {
++
++ const CHARSET_INFO* cs;
++ ulint key_len;
++ ulint true_len;
++ int error=0;
++ ulint blob_len;
++ const byte* blob_data;
++
++ ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
++
++ key_len = key_part->length;
++
++ if (part_is_null) {
++ true_len = key_len + 2;
++ if (true_len > buff_space) {
++ fprintf (stderr,
++ "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ true_len = buff_space;
++ }
++ buff += true_len;
++ buff_space -= true_len;
++
++ continue;
++ }
++
++ cs = field->charset();
++
++ blob_data = row_mysql_read_blob_ref(&blob_len,
++ (byte*) (record
++ + (ulint)get_field_offset(table, field)),
++ (ulint) field->pack_length());
++
++ true_len = blob_len;
++
++ ut_a(get_field_offset(table, field)
++ == key_part->offset);
++
++ /* For multi byte character sets we need to calculate
++ the true length of the key */
++
++ if (blob_len > 0 && cs->mbmaxlen > 1) {
++ true_len = (ulint) cs->cset->well_formed_len(cs,
++ (const char *) blob_data,
++ (const char *) blob_data
++ + blob_len,
++ (uint) (key_len / cs->mbmaxlen),
++ &error);
++ }
++
++ /* All indexes on BLOB and TEXT are column prefix
++ indexes, and we may need to truncate the data to be
++ stored in the key value: */
++
++ if (true_len > key_len) {
++ true_len = key_len;
++ }
++
++ memcpy(sorted, blob_data, true_len);
++ true_len = wsrep_innobase_mysql_sort(
++ mysql_type, cs->number, sorted, true_len,
++ REC_VERSION_56_MAX_INDEX_COL_LEN);
++
++
++ /* Note that we always reserve the maximum possible
++ length of the BLOB prefix in the key value. */
++ if (wsrep_protocol_version > 1) {
++ if (true_len > buff_space) {
++ fprintf (stderr,
++ "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ true_len = buff_space;
++ }
++ buff += true_len;
++ buff_space -= true_len;
++ } else {
++ buff += key_len;
++ }
++ memcpy(buff, sorted, true_len);
++ } else {
++ /* Here we handle all other data types except the
++ true VARCHAR, BLOB and TEXT. Note that the column
++ value we store may be also in a column prefix
++ index. */
++
++ const CHARSET_INFO* cs = NULL;
++ ulint true_len;
++ ulint key_len;
++ const uchar* src_start;
++ int error=0;
++ enum_field_types real_type;
++
++ key_len = key_part->length;
++
++ if (part_is_null) {
++ true_len = key_len;
++ if (true_len > buff_space) {
++ fprintf (stderr,
++ "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ true_len = buff_space;
++ }
++ buff += true_len;
++ buff_space -= true_len;
++
++ continue;
++ }
++
++ src_start = record + key_part->offset;
++ real_type = field->real_type();
++ true_len = key_len;
++
++ /* Character set for the field is defined only
++ to fields whose type is string and real field
++ type is not enum or set. For these fields check
++ if character set is multi byte. */
++
++ if (real_type != MYSQL_TYPE_ENUM
++ && real_type != MYSQL_TYPE_SET
++ && ( mysql_type == MYSQL_TYPE_VAR_STRING
++ || mysql_type == MYSQL_TYPE_STRING)) {
++
++ cs = field->charset();
++
++ /* For multi byte character sets we need to
++ calculate the true length of the key */
++
++ if (key_len > 0 && cs->mbmaxlen > 1) {
++
++ true_len = (ulint)
++ cs->cset->well_formed_len(cs,
++ (const char *)src_start,
++ (const char *)src_start
++ + key_len,
++ (uint) (key_len /
++ cs->mbmaxlen),
++ &error);
++ }
++ memcpy(sorted, src_start, true_len);
++ true_len = wsrep_innobase_mysql_sort(
++ mysql_type, cs->number, sorted, true_len,
++ REC_VERSION_56_MAX_INDEX_COL_LEN);
++
++ if (true_len > buff_space) {
++ fprintf (stderr,
++ "WSREP: key truncated: %s\n",
++ wsrep_thd_query(thd));
++ true_len = buff_space;
++ }
++ memcpy(buff, sorted, true_len);
++ } else {
++ memcpy(buff, src_start, true_len);
++ }
++ buff += true_len;
++ buff_space -= true_len;
++ }
++ }
++
++ ut_a(buff <= buff_start + buff_len);
++
++ DBUG_RETURN((uint)(buff - buff_start));
++}
++#endif /* WITH_WSREP */
+ UNIV_INTERN
+ uint
+ ha_innobase::store_key_val_for_row(
+@@ -6502,6 +7000,9 @@ ha_innobase::write_row(
+ dberr_t error;
+ int error_result= 0;
+ ibool auto_inc_used= FALSE;
++#ifdef WITH_WSREP
++ ibool auto_inc_inserted= FALSE; /* if NULL was inserted */
++#endif
+ ulint sql_command;
+ trx_t* trx = thd_to_trx(user_thd);
+
+@@ -6535,8 +7036,20 @@ ha_innobase::write_row(
+ if ((sql_command == SQLCOM_ALTER_TABLE
+ || sql_command == SQLCOM_OPTIMIZE
+ || sql_command == SQLCOM_CREATE_INDEX
++#ifdef WITH_WSREP
++ || (wsrep_on(user_thd) && wsrep_load_data_splitting &&
++ sql_command == SQLCOM_LOAD &&
++ !thd_test_options(
++ user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
++#endif /* WITH_WSREP */
+ || sql_command == SQLCOM_DROP_INDEX)
+ && num_write_row >= 10000) {
++#ifdef WITH_WSREP
++ if (wsrep_on(user_thd) && sql_command == SQLCOM_LOAD) {
++ WSREP_DEBUG("forced trx split for LOAD: %s",
++ wsrep_thd_query(user_thd));
++ }
++#endif /* WITH_WSREP */
+ /* ALTER TABLE is COMMITted at every 10000 copied rows.
+ The IX table lock for the original table has to be re-issued.
+ As this method will be called on a temporary table where the
+@@ -6570,6 +7083,26 @@ no_commit:
+ */
+ ;
+ } else if (src_table == prebuilt->table) {
++#ifdef WITH_WSREP
++ if (wsrep_on(user_thd) && wsrep_load_data_splitting &&
++ sql_command == SQLCOM_LOAD &&
++ !thd_test_options(
++ user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
++ {
++ switch (wsrep_run_wsrep_commit(user_thd, wsrep_hton, 1))
++ {
++ case WSREP_TRX_OK:
++ break;
++ case WSREP_TRX_SIZE_EXCEEDED:
++ case WSREP_TRX_CERT_FAIL:
++ case WSREP_TRX_ERROR:
++ DBUG_RETURN(1);
++ }
++
++ if (tc_log->commit(user_thd, 1)) DBUG_RETURN(1);
++ wsrep_post_commit(user_thd, TRUE);
++ }
++#endif /* WITH_WSREP */
+ /* Source table is not in InnoDB format:
+ no need to re-acquire locks on it. */
+
+@@ -6580,6 +7113,25 @@ no_commit:
+ /* We will need an IX lock on the destination table. */
+ prebuilt->sql_stat_start = TRUE;
+ } else {
++#ifdef WITH_WSREP
++ if (wsrep_on(user_thd) && wsrep_load_data_splitting &&
++ sql_command == SQLCOM_LOAD &&
++ !thd_test_options(
++ user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
++ {
++ switch (wsrep_run_wsrep_commit(user_thd, wsrep_hton, 1))
++ {
++ case WSREP_TRX_OK:
++ break;
++ case WSREP_TRX_SIZE_EXCEEDED:
++ case WSREP_TRX_CERT_FAIL:
++ case WSREP_TRX_ERROR:
++ DBUG_RETURN(1);
++ }
++ if (tc_log->commit(user_thd, 1)) DBUG_RETURN(1);
++ wsrep_post_commit(user_thd, TRUE);
++ }
++#endif /* WITH_WSREP */
+ /* Ensure that there are no other table locks than
+ LOCK_IX and LOCK_AUTO_INC on the destination table. */
+
+@@ -6609,6 +7161,9 @@ no_commit:
+ innobase_get_auto_increment(). */
+ prebuilt->autoinc_error = DB_SUCCESS;
+
++#ifdef WITH_WSREP
++ auto_inc_inserted= (table->next_number_field->val_int() == 0);
++#endif
+ if ((error_result = update_auto_increment())) {
+ /* We don't want to mask autoinc overflow errors. */
+
+@@ -6687,6 +7242,40 @@ no_commit:
+ case SQLCOM_REPLACE_SELECT:
+ goto set_max_autoinc;
+
++#ifdef WITH_WSREP
++ /* workaround for LP bug #355000, retrying the insert */
++ case SQLCOM_INSERT:
++
++ WSREP_DEBUG("DUPKEY error for autoinc\n"
++ "THD %ld, value %llu, off %llu inc %llu",
++ wsrep_thd_thread_id(current_thd),
++ auto_inc,
++ prebuilt->autoinc_offset,
++ prebuilt->autoinc_increment);
++
++ if (wsrep_on(current_thd) &&
++ auto_inc_inserted &&
++ wsrep_drupal_282555_workaround &&
++ wsrep_thd_retry_counter(current_thd) == 0 &&
++ !thd_test_options(current_thd,
++ OPTION_NOT_AUTOCOMMIT |
++ OPTION_BEGIN)) {
++ WSREP_DEBUG(
++ "retrying insert: %s",
++ (*wsrep_thd_query(current_thd)) ?
++ wsrep_thd_query(current_thd) :
++ (char *)"void");
++ error= DB_SUCCESS;
++ wsrep_thd_set_conflict_state(
++ current_thd, MUST_ABORT);
++ innobase_srv_conc_exit_innodb(
++ prebuilt->trx);
++ /* jump straight to func exit over
++ * later wsrep hooks */
++ goto func_exit;
++ }
++ break;
++#endif
+ default:
+ break;
+ }
+@@ -6745,6 +7334,20 @@ report_error:
+ error_result = convert_error_code_to_mysql(error,
+ prebuilt->table->flags,
+ user_thd);
++#ifdef WITH_WSREP
++ if (!error_result && wsrep_thd_exec_mode(user_thd) == LOCAL_STATE &&
++ wsrep_on(user_thd) && !wsrep_consistency_check(user_thd) &&
++ (sql_command != SQLCOM_LOAD ||
++ thd_binlog_format(user_thd) == BINLOG_FORMAT_ROW)) {
++
++ if (wsrep_append_keys(user_thd, false, record, NULL)) {
++ DBUG_PRINT("wsrep", ("row key failed"));
++ error_result = HA_ERR_INTERNAL_ERROR;
++ goto wsrep_error;
++ }
++ }
++wsrep_error:
++#endif
+
+ if (error_result == HA_FTS_INVALID_DOCID) {
+ my_error(HA_FTS_INVALID_DOCID, MYF(0));
+@@ -6755,7 +7358,88 @@ func_exit:
+
+ DBUG_RETURN(error_result);
+ }
++#ifdef WITH_WSREP
++#if defined(HAVE_YASSL)
++#include "my_config.h"
++#include "md5.hpp"
++#elif defined(HAVE_OPENSSL)
++#include <openssl/md5.h>
++#endif
++static
++int
++wsrep_calc_row_hash(
++/*================*/
++ byte* digest, /*!< in/out: md5 sum */
++ const uchar* row, /*!< in: row in MySQL format */
++ TABLE* table, /*!< in: table in MySQL data
++ dictionary */
++ row_prebuilt_t* prebuilt, /*!< in: InnoDB prebuilt struct */
++ THD* thd) /*!< in: user thread */
++{
++ Field* field;
++ enum_field_types field_mysql_type;
++ uint n_fields;
++ ulint len;
++ const byte* ptr;
++ ulint col_type;
++ uint i;
+
++
++ void *ctx = wsrep_md5_init();
++
++ n_fields = table->s->fields;
++
++ for (i = 0; i < n_fields; i++) {
++ byte null_byte=0;
++ byte true_byte=1;
++
++ field = table->field[i];
++
++ ptr = (const byte*) row + get_field_offset(table, field);
++ len = field->pack_length();
++
++ field_mysql_type = field->type();
++
++ col_type = prebuilt->table->cols[i].mtype;
++
++ switch (col_type) {
++
++ case DATA_BLOB:
++ ptr = row_mysql_read_blob_ref(&len, ptr, len);
++
++ break;
++
++ case DATA_VARCHAR:
++ case DATA_BINARY:
++ case DATA_VARMYSQL:
++ if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
++ /* This is a >= 5.0.3 type true VARCHAR where
++ the real payload data length is stored in
++ 1 or 2 bytes */
++
++ ptr = row_mysql_read_true_varchar(
++ &len, ptr,
++ (ulint)
++ (((Field_varstring*)field)->length_bytes));
++
++ }
++
++ break;
++ default:
++ ;
++ }
++
++ if (field->is_null_in_record(row)) {
++ wsrep_md5_update(ctx, (char*)&null_byte, 1);
++ } else {
++ wsrep_md5_update(ctx, (char*)&true_byte, 1);
++ wsrep_md5_update(ctx, (char*)ptr, len);
++ }
++ }
++ wsrep_compute_md5_hash((char*)digest, ctx);
++ return(0);
++}
++#endif /* WITH_WSREP */
+ /**********************************************************************//**
+ Checks which fields have changed in a row and stores information
+ of them to an update vector.
+@@ -7164,6 +7848,20 @@ func_exit:
+
+ innobase_active_small();
+
++#ifdef WITH_WSREP
++ if (!err && wsrep_thd_exec_mode(user_thd) == LOCAL_STATE &&
++ wsrep_on(user_thd)) {
++
++ DBUG_PRINT("wsrep", ("update row key"));
++
++ if (wsrep_append_keys(user_thd, false, old_row, new_row)) {
++ DBUG_PRINT("wsrep", ("row key failed"));
++ err = HA_ERR_INTERNAL_ERROR;
++ goto wsrep_error;
++ }
++ }
++wsrep_error:
++#endif
+ DBUG_RETURN(err);
+ }
+
+@@ -7211,6 +7909,18 @@ ha_innobase::delete_row(
+
+ innobase_active_small();
+
++#ifdef WITH_WSREP
++ if (error == DB_SUCCESS && wsrep_thd_exec_mode(user_thd) == LOCAL_STATE &&
++ wsrep_on(user_thd)) {
++
++ if (wsrep_append_keys(user_thd, false, record, NULL)) {
++ DBUG_PRINT("wsrep", ("delete fail"));
++ error = DB_ERROR;
++ goto wsrep_error;
++ }
++ }
++wsrep_error:
++#endif
+ DBUG_RETURN(convert_error_code_to_mysql(
+ error, prebuilt->table->flags, user_thd));
+ }
+@@ -8198,7 +8908,6 @@ innobase_fts_create_doc_id_key(
+ dfield_t* dfield = dtuple_get_nth_field(tuple, 0);
+
+ ut_a(dict_index_get_n_unique(index) == 1);
+-
+ dtuple_set_n_fields(tuple, index->n_fields);
+ dict_index_copy_types(tuple, index, index->n_fields);
+
+@@ -8371,7 +9080,395 @@ ha_innobase::ft_end()
+
+ rnd_end();
+ }
++#ifdef WITH_WSREP
++dict_index_t*
++wsrep_dict_foreign_find_index(
++ dict_table_t* table,
++ const char** col_names,
++ const char** columns,
++ ulint n_cols,
++ dict_index_t* types_idx,
++ ibool check_charsets,
++ ulint check_null);
++
++extern
++dberr_t
++wsrep_append_foreign_key(
++/*===========================*/
++ trx_t* trx, /*!< in: trx */
++ dict_foreign_t* foreign, /*!< in: foreign key constraint */
++ const rec_t* rec, /*!<in: clustered index record */
++ dict_index_t* index, /*!<in: clustered index */
++ ibool referenced, /*!<in: is check for referenced table */
++ ibool shared) /*!<in: is shared access */
++{
++ THD* thd = (THD*)trx->mysql_thd;
++ int rcode = 0;
++ char cache_key[513] = {'\0'};
++ int cache_key_len;
++ bool const copy = true;
++ ut_a(trx);
+
++ if (!wsrep_on(trx->mysql_thd) ||
++ wsrep_thd_exec_mode(thd) != LOCAL_STATE)
++ return DB_SUCCESS;
++
++ if (!thd || !foreign ||
++ (!foreign->referenced_table && !foreign->foreign_table))
++ {
++ WSREP_INFO("FK: %s missing in: %s",
++ (!thd) ? "thread" :
++ ((!foreign) ? "constraint" :
++ ((!foreign->referenced_table) ?
++ "referenced table" : "foreign table")),
++ (thd && wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void");
++ return DB_ERROR;
++ }
++
++ if ( !((referenced) ?
++ foreign->referenced_table : foreign->foreign_table))
++ {
++ WSREP_DEBUG("pulling %s table into cache",
++ (referenced) ? "referenced" : "foreign");
++ mutex_enter(&(dict_sys->mutex));
++ if (referenced)
++ {
++ foreign->referenced_table =
++ dict_table_get_low(
++ foreign->referenced_table_name_lookup);
++ if (foreign->referenced_table)
++ {
++ foreign->referenced_index =
++ wsrep_dict_foreign_find_index(
++ foreign->referenced_table, NULL,
++ foreign->referenced_col_names,
++ foreign->n_fields,
++ foreign->foreign_index,
++ TRUE, FALSE);
++ }
++ }
++ else
++ {
++ foreign->foreign_table =
++ dict_table_get_low(
++ foreign->foreign_table_name_lookup);
++ if (foreign->foreign_table)
++ {
++ foreign->foreign_index =
++ wsrep_dict_foreign_find_index(
++ foreign->foreign_table, NULL,
++ foreign->foreign_col_names,
++ foreign->n_fields,
++ foreign->referenced_index,
++ TRUE, FALSE);
++ }
++ }
++ mutex_exit(&(dict_sys->mutex));
++ }
++
++ if ( !((referenced) ?
++ foreign->referenced_table : foreign->foreign_table))
++ {
++ WSREP_WARN("FK: %s missing in query: %s",
++ (!foreign->referenced_table) ?
++ "referenced table" : "foreign table",
++ (wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void");
++ return DB_ERROR;
++ }
++ byte key[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
++ ulint len = WSREP_MAX_SUPPORTED_KEY_LENGTH;
++
++ dict_index_t *idx_target = (referenced) ?
++ foreign->referenced_index : index;
++ dict_index_t *idx = (referenced) ?
++ UT_LIST_GET_FIRST(foreign->referenced_table->indexes) :
++ UT_LIST_GET_FIRST(foreign->foreign_table->indexes);
++ int i = 0;
++ while (idx != NULL && idx != idx_target) {
++ if (innobase_strcasecmp (idx->name, innobase_index_reserve_name) != 0) {
++ i++;
++ }
++ idx = UT_LIST_GET_NEXT(indexes, idx);
++ }
++ ut_a(idx);
++ key[0] = (char)i;
++
++ rcode = wsrep_rec_get_foreign_key(
++ &key[1], &len, rec, index, idx,
++ wsrep_protocol_version > 1);
++ if (rcode != DB_SUCCESS) {
++ WSREP_ERROR(
++ "FK key set failed: %d (%lu %lu), index: %s %s, %s",
++ rcode, referenced, shared,
++ (index && index->name) ? index->name :
++ "void index",
++ (index && index->table_name) ? index->table_name :
++ "void table",
++ wsrep_thd_query(thd));
++ return DB_ERROR;
++ }
++ strncpy(cache_key,
++ (wsrep_protocol_version > 1) ?
++ ((referenced) ?
++ foreign->referenced_table->name :
++ foreign->foreign_table->name) :
++ foreign->foreign_table->name, sizeof(cache_key) - 1);
++ cache_key_len = strlen(cache_key);
++// #define WSREP_DEBUG_PRINT
++#ifdef WSREP_DEBUG_PRINT
++ ulint j;
++ fprintf(stderr, "FK parent key, table: %s %s len: %lu ",
++ cache_key, (shared) ? "shared" : "exclusive", len+1);
++ for (j=0; j<len+1; j++) {
++ fprintf(stderr, " %hhX, ", key[j]);
++ }
++ fprintf(stderr, "\n");
++#endif
++ char *p = strchr(cache_key, '/');
++ if (p) {
++ *p = '\0';
++ } else {
++ WSREP_WARN("unexpected foreign key table %s %s",
++ foreign->referenced_table->name,
++ foreign->foreign_table->name);
++ }
++
++ wsrep_buf_t wkey_part[3];
++ wsrep_key_t wkey = {wkey_part, 3};
++ if (!wsrep_prepare_key_for_innodb(
++ (const uchar*)cache_key,
++ cache_key_len + 1,
++ (const uchar*)key, len+1,
++ wkey_part,
++ &wkey.key_parts_num)) {
++ WSREP_WARN("key prepare failed for cascaded FK: %s",
++ (wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void");
++ return DB_ERROR;
++ }
++ rcode = wsrep->append_key(
++ wsrep,
++ wsrep_ws_handle(thd, trx),
++ &wkey,
++ 1,
++ shared ? WSREP_KEY_SHARED : WSREP_KEY_EXCLUSIVE,
++ copy);
++ if (rcode) {
++ DBUG_PRINT("wsrep", ("row key failed: %d", rcode));
++ WSREP_ERROR("Appending cascaded fk row key failed: %s, %d",
++ (wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void", rcode);
++ return DB_ERROR;
++ }
++
++ return DB_SUCCESS;
++}
++
++static int
++wsrep_append_key(
++/*==================*/
++ THD *thd,
++ trx_t *trx,
++ TABLE_SHARE *table_share,
++ TABLE *table,
++ const char* key,
++ uint16_t key_len,
++ bool shared
++)
++{
++ DBUG_ENTER("wsrep_append_key");
++ bool const copy = true;
++#ifdef WSREP_DEBUG_PRINT
++ fprintf(stderr, "%s conn %ld, trx %llu, keylen %d, table %s\n SQL: %s ",
++ (shared) ? "Shared" : "Exclusive",
++ wsrep_thd_thread_id(thd), (long long)trx->id, key_len,
++ table_share->table_name.str, wsrep_thd_query(thd));
++ for (int i=0; i<key_len; i++) {
++ fprintf(stderr, "%hhX, ", key[i]);
++ }
++ fprintf(stderr, "\n");
++#endif
++ wsrep_buf_t wkey_part[3];
++ wsrep_key_t wkey = {wkey_part, 3};
++ if (!wsrep_prepare_key_for_innodb(
++ (const uchar*)table_share->table_cache_key.str,
++ table_share->table_cache_key.length,
++ (const uchar*)key, key_len,
++ wkey_part,
++ &wkey.key_parts_num)) {
++ WSREP_WARN("key prepare failed for: %s",
++ (wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void");
++ DBUG_RETURN(-1);
++ }
++
++ int rcode = wsrep->append_key(
++ wsrep,
++ wsrep_ws_handle(thd, trx),
++ &wkey,
++ 1,
++ shared ? WSREP_KEY_SHARED : WSREP_KEY_EXCLUSIVE,
++ copy);
++ if (rcode) {
++ DBUG_PRINT("wsrep", ("row key failed: %d", rcode));
++ WSREP_WARN("Appending row key failed: %s, %d",
++ (wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void", rcode);
++ DBUG_RETURN(-1);
++ }
++ DBUG_RETURN(0);
++}
++
++extern void compute_md5_hash(char *digest, const char *buf, int len);
++#define MD5_HASH compute_md5_hash
++
++int
++ha_innobase::wsrep_append_keys(
++/*==================*/
++ THD *thd,
++ bool shared,
++ const uchar* record0, /* in: row in MySQL format */
++ const uchar* record1) /* in: row in MySQL format */
++{
++ int rcode;
++ DBUG_ENTER("wsrep_append_keys");
++
++ bool key_appended = false;
++ trx_t *trx = thd_to_trx(thd);
++
++ if (table_share && table_share->tmp_table != NO_TMP_TABLE) {
++ WSREP_DEBUG("skipping tmp table DML: THD: %lu tmp: %d SQL: %s",
++ wsrep_thd_thread_id(thd),
++ table_share->tmp_table,
++ (wsrep_thd_query(thd)) ?
++ wsrep_thd_query(thd) : "void");
++ DBUG_RETURN(0);
++ }
++
++ if (wsrep_protocol_version == 0) {
++ uint len;
++ char keyval[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
++ char *key = &keyval[0];
++ ibool is_null;
++
++ len = wsrep_store_key_val_for_row(
++ thd, table, 0, key, WSREP_MAX_SUPPORTED_KEY_LENGTH,
++ record0, &is_null);
++
++ if (!is_null) {
++ rcode = wsrep_append_key(
++ thd, trx, table_share, table, keyval,
++ len, shared);
++ if (rcode) DBUG_RETURN(rcode);
++ }
++ else
++ {
++ WSREP_DEBUG("NULL key skipped (proto 0): %s",
++ wsrep_thd_query(thd));
++ }
++ } else {
++ ut_a(table->s->keys <= 256);
++ uint i;
++ bool hasPK= false;
++
++ for (i=0; i<table->s->keys; ++i) {
++ KEY* key_info = table->key_info + i;
++ if (key_info->flags & HA_NOSAME) {
++ hasPK = true;
++ }
++ }
++
++ for (i=0; i<table->s->keys; ++i) {
++ uint len;
++ char keyval0[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
++ char keyval1[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
++ char* key0 = &keyval0[1];
++ char* key1 = &keyval1[1];
++ KEY* key_info = table->key_info + i;
++ ibool is_null;
++
++ dict_index_t* idx = innobase_get_index(i);
++ dict_table_t* tab = (idx) ? idx->table : NULL;
++
++ keyval0[0] = (char)i;
++ keyval1[0] = (char)i;
++
++ if (!tab) {
++ WSREP_WARN("MySQL-InnoDB key mismatch %s %s",
++ table->s->table_name.str,
++ key_info->name);
++ }
++ /* !hasPK == table with no PK, must append all non-unique keys */
++ if (!hasPK || key_info->flags & HA_NOSAME ||
++ ((tab &&
++ dict_table_get_referenced_constraint(tab, idx)) ||
++ (!tab && referenced_by_foreign_key()))) {
++
++ len = wsrep_store_key_val_for_row(
++ thd, table, i, key0,
++ WSREP_MAX_SUPPORTED_KEY_LENGTH,
++ record0, &is_null);
++ if (!is_null) {
++ rcode = wsrep_append_key(
++ thd, trx, table_share, table,
++ keyval0, len+1, shared);
++ if (rcode) DBUG_RETURN(rcode);
++
++ if (key_info->flags & HA_NOSAME || shared)
++ key_appended = true;
++ }
++ else
++ {
++ WSREP_DEBUG("NULL key skipped: %s",
++ wsrep_thd_query(thd));
++ }
++ if (record1) {
++ len = wsrep_store_key_val_for_row(
++ thd, table, i, key1,
++ WSREP_MAX_SUPPORTED_KEY_LENGTH,
++ record1, &is_null);
++ if (!is_null && memcmp(key0, key1, len)) {
++ rcode = wsrep_append_key(
++ thd, trx, table_share,
++ table,
++ keyval1, len+1, shared);
++ if (rcode) DBUG_RETURN(rcode);
++ }
++ }
++ }
++ }
++ }
++
++ /* if no PK, calculate hash of full row, to be the key value */
++ if (!key_appended && wsrep_certify_nonPK) {
++ uchar digest[16];
++ int rcode;
++
++ wsrep_calc_row_hash(digest, record0, table, prebuilt, thd);
++ if ((rcode = wsrep_append_key(thd, trx, table_share, table,
++ (const char*) digest, 16,
++ shared))) {
++ DBUG_RETURN(rcode);
++ }
++
++ if (record1) {
++ wsrep_calc_row_hash(
++ digest, record1, table, prebuilt, thd);
++ if ((rcode = wsrep_append_key(thd, trx, table_share,
++ table,
++ (const char*) digest,
++ 16, shared))) {
++ DBUG_RETURN(rcode);
++ }
++ }
++ DBUG_RETURN(0);
++ }
++
++ DBUG_RETURN(0);
++}
++#endif
+ /*********************************************************************//**
+ Stores a reference to the current row to 'ref' field of the handle. Note
+ that in the case where we have generated the clustered index for the
+@@ -12128,11 +13225,18 @@ ha_innobase::external_lock(
+ /* used by test case */
+ DBUG_EXECUTE_IF("no_innodb_binlog_errors", skip = true;);
+ if (!skip) {
++#ifdef WITH_WSREP
++ if (!wsrep_on(thd) || wsrep_thd_exec_mode(thd) == LOCAL_STATE)
++ {
++#endif /* WITH_WSREP */
+ my_error(ER_BINLOG_STMT_MODE_AND_ROW_ENGINE, MYF(0),
+ " InnoDB is limited to row-logging when "
+ "transaction isolation level is "
+ "READ COMMITTED or READ UNCOMMITTED.");
+ DBUG_RETURN(HA_ERR_LOGGING_IMPOSSIBLE);
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ }
+ }
+
+@@ -12163,6 +13267,33 @@ ha_innobase::external_lock(
+
+ }
+
++ /* Check for UPDATEs in read-only mode. */
++ if (srv_read_only_mode
++ && (thd_sql_command(thd) == SQLCOM_UPDATE
++ || thd_sql_command(thd) == SQLCOM_INSERT
++ || thd_sql_command(thd) == SQLCOM_REPLACE
++ || thd_sql_command(thd) == SQLCOM_DROP_TABLE
++ || thd_sql_command(thd) == SQLCOM_ALTER_TABLE
++ || thd_sql_command(thd) == SQLCOM_OPTIMIZE
++ || (thd_sql_command(thd) == SQLCOM_CREATE_TABLE
++ && lock_type == F_WRLCK)
++ || thd_sql_command(thd) == SQLCOM_CREATE_INDEX
++ || thd_sql_command(thd) == SQLCOM_DROP_INDEX
++ || thd_sql_command(thd) == SQLCOM_DELETE)) {
++
++ if (thd_sql_command(thd) == SQLCOM_CREATE_TABLE)
++ {
++ ib_senderrf(thd, IB_LOG_LEVEL_WARN,
++ ER_INNODB_READ_ONLY);
++ DBUG_RETURN(HA_ERR_INNODB_READ_ONLY);
++ } else {
++ ib_senderrf(thd, IB_LOG_LEVEL_WARN,
++ ER_READ_ONLY_MODE);
++ DBUG_RETURN(HA_ERR_TABLE_READONLY);
++ }
++
++ }
++
+ trx = prebuilt->trx;
+
+ prebuilt->sql_stat_start = TRUE;
+@@ -13218,8 +14349,20 @@ ha_innobase::get_auto_increment(
+ next value in the series. */
+ if (prebuilt->autoinc_increment > increment) {
+
++#ifdef WITH_WSREP
++ WSREP_DEBUG("autoinc decrease: %llu -> %llu\n"
++ "THD: %ld, current: %llu, autoinc: %llu",
++ prebuilt->autoinc_increment,
++ increment,
++ wsrep_thd_thread_id(ha_thd()),
++ current, autoinc);
++ if (!wsrep_on(ha_thd()))
++ {
++#endif /* WITH_WSREP */
+ current = autoinc - prebuilt->autoinc_increment;
+-
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ current = innobase_next_autoinc(
+ current, 1, increment, 1, col_max_value);
+
+@@ -13580,6 +14723,9 @@ innobase_xa_prepare(
+ to the session variable take effect only in the next transaction */
+ if (!trx->support_xa) {
+
++#ifdef WITH_WSREP
++ thd_get_xid(thd, (MYSQL_XID*) &trx->xid);
++#endif // WITH_WSREP
+ return(0);
+ }
+
+@@ -15648,6 +16794,292 @@ static SHOW_VAR innodb_status_variables_export[]= {
+ static struct st_mysql_storage_engine innobase_storage_engine=
+ { MYSQL_HANDLERTON_INTERFACE_VERSION };
+
++#ifdef WITH_WSREP
++void
++wsrep_abort_slave_trx(wsrep_seqno_t bf_seqno, wsrep_seqno_t victim_seqno)
++{
++ WSREP_ERROR("Trx %lld tries to abort slave trx %lld. This could be "
++ "caused by:\n\t"
++ "1) unsupported configuration options combination, please check documentation.\n\t"
++ "2) a bug in the code.\n\t"
++ "3) a database corruption.\n Node consistency compromized, "
++ "need to abort. Restart the node to resync with cluster.",
++ (long long)bf_seqno, (long long)victim_seqno);
++ abort();
++}
++
++int
++wsrep_innobase_kill_one_trx(void * const bf_thd_ptr,
++ const trx_t * const bf_trx,
++ trx_t *victim_trx, ibool signal)
++{
++ ut_ad(lock_mutex_own());
++ ut_ad(trx_mutex_own(victim_trx));
++ ut_ad(bf_thd_ptr);
++ ut_ad(victim_trx);
++
++ DBUG_ENTER("wsrep_innobase_kill_one_trx");
++ THD *bf_thd = bf_thd_ptr ? (THD*) bf_thd_ptr : NULL;
++ THD *thd = (THD *) victim_trx->mysql_thd;
++ int64_t bf_seqno = (bf_thd) ? wsrep_thd_trx_seqno(bf_thd) : 0;
++
++ if (!thd) {
++ DBUG_PRINT("wsrep", ("no thd for conflicting lock"));
++ WSREP_WARN("no THD for trx: %llu", (long long)victim_trx->id);
++ DBUG_RETURN(1);
++ }
++ if (!bf_thd) {
++ DBUG_PRINT("wsrep", ("no BF thd for conflicting lock"));
++ WSREP_WARN("no BF THD for trx: %llu",
++ (bf_trx) ? (long long)bf_trx->id : 0);
++ DBUG_RETURN(1);
++ }
++
++ WSREP_LOG_CONFLICT(bf_thd, thd, TRUE);
++
++ WSREP_DEBUG("BF kill (%lu, seqno: %lld), victim: (%llu) trx: %llu",
++ signal, (long long)bf_seqno,
++ (long long)wsrep_thd_thread_id(thd),
++ (long long)victim_trx->id);
++
++ WSREP_DEBUG("Aborting query: %s",
++ (thd && wsrep_thd_query(thd)) ? wsrep_thd_query(thd) : "void");
++
++ wsrep_thd_LOCK(thd);
++
++ if (wsrep_thd_query_state(thd) == QUERY_EXITING) {
++ WSREP_DEBUG("kill trx EXITING for %llu",
++ (long long)victim_trx->id);
++ wsrep_thd_UNLOCK(thd);
++ DBUG_RETURN(0);
++ }
++ if(wsrep_thd_exec_mode(thd) != LOCAL_STATE) {
++ WSREP_DEBUG("withdraw for BF trx: %llu, state: %d",
++ (long long)victim_trx->id,
++ wsrep_thd_conflict_state(thd));
++ }
++
++ switch (wsrep_thd_conflict_state(thd)) {
++ case NO_CONFLICT:
++ wsrep_thd_set_conflict_state(thd, MUST_ABORT);
++ break;
++ case MUST_ABORT:
++ WSREP_DEBUG("victim %llu in MUST ABORT state",
++ (long long)victim_trx->id);
++ wsrep_thd_UNLOCK(thd);
++ wsrep_thd_awake(thd, signal);
++ DBUG_RETURN(0);
++ break;
++ case ABORTED:
++ case ABORTING: // fall through
++ default:
++ WSREP_DEBUG("victim %llu in state %d",
++ (long long)victim_trx->id,
++ wsrep_thd_conflict_state(thd));
++ wsrep_thd_UNLOCK(thd);
++ DBUG_RETURN(0);
++ break;
++ }
++
++ switch (wsrep_thd_query_state(thd)) {
++ case QUERY_COMMITTING:
++ enum wsrep_status rcode;
++
++ WSREP_DEBUG("kill trx QUERY_COMMITTING for %llu",
++ (long long)victim_trx->id);
++ wsrep_thd_awake(thd, signal);
++
++ if (wsrep_thd_exec_mode(thd) == REPL_RECV) {
++ wsrep_abort_slave_trx(bf_seqno,
++ wsrep_thd_trx_seqno(thd));
++ } else {
++ rcode = wsrep->abort_pre_commit(
++ wsrep, bf_seqno,
++ (wsrep_trx_id_t)victim_trx->id
++ );
++
++ switch (rcode) {
++ case WSREP_WARNING:
++ WSREP_DEBUG("cancel commit warning: %llu",
++ (long long)victim_trx->id);
++ wsrep_thd_UNLOCK(thd);
++ DBUG_RETURN(1);
++ break;
++ case WSREP_OK:
++ break;
++ default:
++ WSREP_ERROR(
++ "cancel commit bad exit: %d %llu",
++ rcode,
++ (long long)victim_trx->id);
++ /* unable to interrupt, must abort */
++ /* note: kill_mysql() will block, if we cannot.
++ * kill the lock holder first.
++ */
++ abort();
++ break;
++ }
++ }
++ break;
++ case QUERY_EXEC:
++ /* it is possible that victim trx is itself waiting for some
++ * other lock. We need to cancel this waiting
++ */
++ WSREP_DEBUG("kill trx QUERY_EXEC for %llu", (long long)victim_trx->id);
++
++ victim_trx->lock.was_chosen_as_deadlock_victim= TRUE;
++ if (victim_trx->lock.wait_lock) {
++ WSREP_DEBUG("victim has wait flag: %ld",
++ wsrep_thd_thread_id(thd));
++ lock_t* wait_lock = victim_trx->lock.wait_lock;
++ if (wait_lock) {
++ WSREP_DEBUG("canceling wait lock");
++ victim_trx->lock.was_chosen_as_deadlock_victim= TRUE;
++ lock_cancel_waiting_and_release(wait_lock);
++ }
++
++ wsrep_thd_awake(thd, signal);
++ } else {
++ /* abort currently executing query */
++ DBUG_PRINT("wsrep",("sending KILL_QUERY to: %ld",
++ wsrep_thd_thread_id(thd)));
++ WSREP_DEBUG("kill query for: %ld",
++ wsrep_thd_thread_id(thd));
++ wsrep_thd_awake(thd, signal);
++
++ /* for BF thd, we need to prevent him from committing */
++ if (wsrep_thd_exec_mode(thd) == REPL_RECV) {
++ wsrep_abort_slave_trx(bf_seqno,
++ wsrep_thd_trx_seqno(thd));
++ }
++ }
++ break;
++ case QUERY_IDLE:
++ {
++ bool skip_abort= false;
++ wsrep_aborting_thd_t abortees;
++
++ WSREP_DEBUG("kill IDLE for %llu", (long long)victim_trx->id);
++
++ if (wsrep_thd_exec_mode(thd) == REPL_RECV) {
++ WSREP_DEBUG("kill BF IDLE, seqno: %lld",
++ (long long)wsrep_thd_trx_seqno(thd));
++ wsrep_thd_UNLOCK(thd);
++ wsrep_abort_slave_trx(bf_seqno,
++ wsrep_thd_trx_seqno(thd));
++ DBUG_RETURN(0);
++ }
++ /* This will lock thd from proceeding after net_read() */
++ wsrep_thd_set_conflict_state(thd, ABORTING);
++
++ mysql_mutex_lock(&LOCK_wsrep_rollback);
++
++ abortees = wsrep_aborting_thd;
++ while (abortees && !skip_abort) {
++ /* check if we have a kill message for this already */
++ if (abortees->aborting_thd == thd) {
++ skip_abort = true;
++ WSREP_WARN("duplicate thd aborter %lu",
++ wsrep_thd_thread_id(thd));
++ }
++ abortees = abortees->next;
++ }
++ if (!skip_abort) {
++ wsrep_aborting_thd_t aborting = (wsrep_aborting_thd_t)
++ my_malloc(sizeof(struct wsrep_aborting_thd),
++ MYF(0));
++ aborting->aborting_thd = thd;
++ aborting->next = wsrep_aborting_thd;
++ wsrep_aborting_thd = aborting;
++ DBUG_PRINT("wsrep",("enqueuing trx abort for %lu",
++ wsrep_thd_thread_id(thd)));
++ WSREP_DEBUG("enqueuing trx abort for (%lu)",
++ wsrep_thd_thread_id(thd));
++ }
++
++ DBUG_PRINT("wsrep",("signalling wsrep rollbacker"));
++ WSREP_DEBUG("signaling aborter");
++ mysql_cond_signal(&COND_wsrep_rollback);
++ mysql_mutex_unlock(&LOCK_wsrep_rollback);
++
++ break;
++ }
++ default:
++ WSREP_WARN("bad wsrep query state: %d",
++ wsrep_thd_query_state(thd));
++ break;
++ }
++ wsrep_thd_UNLOCK(thd);
++
++ DBUG_RETURN(0);
++}
++static int
++wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd,
++ my_bool signal)
++{
++ DBUG_ENTER("wsrep_innobase_abort_thd");
++ trx_t* victim_trx = thd_to_trx(victim_thd);
++ trx_t* bf_trx = (bf_thd) ? thd_to_trx(bf_thd) : NULL;
++ WSREP_DEBUG("abort transaction: BF: %s victim: %s",
++ wsrep_thd_query(bf_thd),
++ wsrep_thd_query(victim_thd));
++
++ if (victim_trx)
++ {
++ lock_mutex_enter();
++ trx_mutex_enter(victim_trx);
++ int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
++ victim_trx, signal);
++ trx_mutex_exit(victim_trx);
++ lock_mutex_exit();
++ wsrep_srv_conc_cancel_wait(victim_trx);
++
++ DBUG_RETURN(rcode);
++ } else {
++ WSREP_DEBUG("victim does not have transaction");
++ wsrep_thd_LOCK(victim_thd);
++ wsrep_thd_set_conflict_state(victim_thd, MUST_ABORT);
++ wsrep_thd_UNLOCK(victim_thd);
++ wsrep_thd_awake(victim_thd, signal);
++ }
++ DBUG_RETURN(-1);
++}
++
++static int innobase_wsrep_set_checkpoint(handlerton* hton, const XID* xid)
++{
++ DBUG_ASSERT(hton == innodb_hton_ptr);
++ if (wsrep_is_wsrep_xid(xid)) {
++ mtr_t mtr;
++ mtr_start(&mtr);
++ trx_sysf_t* sys_header = trx_sysf_get(&mtr);
++ trx_sys_update_wsrep_checkpoint(xid, sys_header, &mtr);
++ mtr_commit(&mtr);
++ innobase_flush_logs(hton);
++ return 0;
++ } else {
++ return 1;
++ }
++}
++
++static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid)
++{
++ DBUG_ASSERT(hton == innodb_hton_ptr);
++ trx_sys_read_wsrep_checkpoint(xid);
++ return 0;
++}
++
++static void
++wsrep_fake_trx_id(
++/*==================*/
++ handlerton *hton,
++ THD *thd) /*!< in: user thread handle */
++{
++ trx_id_t trx_id = trx_sys_get_new_trx_id();
++
++ (void *)wsrep_ws_handle_for_trx(wsrep_thd_ws_handle(thd), trx_id);
++}
++
++#endif /* WITH_WSREP */
+ /* plugin options */
+
+ static MYSQL_SYSVAR_ENUM(checksum_algorithm, srv_checksum_algorithm,
+@@ -16359,6 +17791,40 @@ static MYSQL_SYSVAR_BOOL(disable_background_merge,
+ NULL, NULL, FALSE);
+ #endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
+
++#ifdef WITH_INNODB_DISALLOW_WRITES
++/*******************************************************
++ * innobase_disallow_writes variable definition *
++ *******************************************************/
++
++/* Must always init to FALSE. */
++static my_bool innobase_disallow_writes = FALSE;
++
++/**************************************************************************
++An "update" method for innobase_disallow_writes variable. */
++static
++void
++innobase_disallow_writes_update(
++/*============================*/
++ THD* thd, /* in: thread handle */
++ st_mysql_sys_var* var, /* in: pointer to system
++ variable */
++ void* var_ptr, /* out: pointer to dynamic
++ variable */
++ const void* save) /* in: temporary storage */
++{
++ *(my_bool*)var_ptr = *(my_bool*)save;
++ ut_a(srv_allow_writes_event);
++ if (*(my_bool*)var_ptr)
++ os_event_reset(srv_allow_writes_event);
++ else
++ os_event_set(srv_allow_writes_event);
++}
++
++static MYSQL_SYSVAR_BOOL(disallow_writes, innobase_disallow_writes,
++ PLUGIN_VAR_NOCMDOPT,
++ "Tell InnoDB to stop any writes to disk",
++ NULL, innobase_disallow_writes_update, FALSE);
++#endif /* WITH_INNODB_DISALLOW_WRITES */
+ static MYSQL_SYSVAR_BOOL(random_read_ahead, srv_random_read_ahead,
+ PLUGIN_VAR_NOCMDARG,
+ "Whether to use read ahead for random access within an extent.",
+@@ -16577,6 +18043,9 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
+ MYSQL_SYSVAR(change_buffering_debug),
+ MYSQL_SYSVAR(disable_background_merge),
+ #endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
++#ifdef WITH_INNODB_DISALLOW_WRITES
++ MYSQL_SYSVAR(disallow_writes),
++#endif /* WITH_INNODB_DISALLOW_WRITES */
+ MYSQL_SYSVAR(random_read_ahead),
+ MYSQL_SYSVAR(read_ahead_threshold),
+ MYSQL_SYSVAR(read_only),
+diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h
+index f735b6f..45b8fd3 100644
+--- a/storage/innobase/handler/ha_innodb.h
++++ b/storage/innobase/handler/ha_innodb.h
+@@ -95,6 +95,10 @@ class ha_innobase: public handler
+ void innobase_initialize_autoinc();
+ dict_index_t* innobase_get_index(uint keynr);
+
++#ifdef WITH_WSREP
++ int wsrep_append_keys(THD *thd, bool shared,
++ const uchar* record0, const uchar* record1);
++#endif
+ /* Init values for the class: */
+ public:
+ ha_innobase(handlerton *hton, TABLE_SHARE *table_arg);
+@@ -440,6 +444,38 @@ bool thd_is_strict_mode(const MYSQL_THD thd)
+ __attribute__((nonnull));
+ } /* extern "C" */
+
++#ifdef WITH_WSREP
++#include <wsrep_mysqld.h>
++extern "C" bool wsrep_thd_is_wsrep_on(THD *thd);
++
++extern "C" enum wsrep_exec_mode wsrep_thd_exec_mode(THD *thd);
++extern "C" enum wsrep_conflict_state wsrep_thd_conflict_state(THD *thd);
++extern "C" enum wsrep_query_state wsrep_thd_query_state(THD *thd);
++extern "C" const char * wsrep_thd_exec_mode_str(THD *thd);
++extern "C" const char * wsrep_thd_conflict_state_str(THD *thd);
++extern "C" const char * wsrep_thd_query_state_str(THD *thd);
++extern "C" wsrep_ws_handle_t* wsrep_thd_ws_handle(THD *thd);
++
++extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode);
++extern "C" void wsrep_thd_set_query_state(
++ THD *thd, enum wsrep_query_state state);
++extern "C" void wsrep_thd_set_conflict_state(
++ THD *thd, enum wsrep_conflict_state state);
++
++extern "C" void wsrep_thd_set_trx_to_replay(THD *thd, uint64 trx_id);
++
++extern "C"void wsrep_thd_LOCK(THD *thd);
++extern "C"void wsrep_thd_UNLOCK(THD *thd);
++extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd);
++extern "C" time_t wsrep_thd_query_start(THD *thd);
++extern "C" my_thread_id wsrep_thd_thread_id(THD *thd);
++extern "C" int64_t wsrep_thd_trx_seqno(THD *thd);
++extern "C" query_id_t wsrep_thd_query_id(THD *thd);
++extern "C" char * wsrep_thd_query(THD *thd);
++extern "C" query_id_t wsrep_thd_wsrep_last_query_id(THD *thd);
++extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id);
++extern "C" void wsrep_thd_awake(THD *thd, my_bool signal);
++#endif
+ struct trx_t;
+
+ extern const struct _ft_vft ft_vft_result;
+diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
+index 19812ce..4f2c8d4 100644
+--- a/storage/innobase/handler/handler0alter.cc
++++ b/storage/innobase/handler/handler0alter.cc
+@@ -46,6 +46,10 @@ Smart ALTER TABLE
+ #include "srv0mon.h"
+ #include "fts0priv.h"
+ #include "pars0pars.h"
++#ifdef WITH_WSREP
++//#include "wsrep_api.h"
++#include <sql_acl.h> // PROCESS_ACL
++#endif
+ #include "row0sel.h"
+ #include "ha_innodb.h"
+
+diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
+index 460a7e1..5b9f052 100644
+--- a/storage/innobase/include/dict0mem.h
++++ b/storage/innobase/include/dict0mem.h
+@@ -484,6 +484,9 @@ be REC_VERSION_56_MAX_INDEX_COL_LEN (3072) bytes */
+
+ /** Defines the maximum fixed length column size */
+ #define DICT_MAX_FIXED_COL_LEN DICT_ANTELOPE_MAX_INDEX_COL_LEN
++#ifdef WITH_WSREP
++#define WSREP_MAX_SUPPORTED_KEY_LENGTH 3500
++#endif /* WITH_WSREP */
+
+ /** Data structure for a field in an index */
+ struct dict_field_t{
+@@ -767,6 +770,23 @@ struct dict_foreign_with_index {
+ const dict_index_t* m_index;
+ };
+
++#ifdef WITH_WSREP
++/** A function object to find a foreign key with the given index as the
++foreign index. Return the foreign key with matching criteria or NULL */
++struct dict_foreign_with_foreign_index {
++
++ dict_foreign_with_foreign_index(const dict_index_t* index)
++ : m_index(index)
++ {}
++
++ bool operator()(const dict_foreign_t* foreign) const
++ {
++ return(foreign->foreign_index == m_index);
++ }
++
++ const dict_index_t* m_index;
++};
++#endif
+ /* A function object to check if the foreign constraint is between different
+ tables. Returns true if foreign key constraint is between different tables,
+ false otherwise. */
+diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h
+index fa202aa..330bd3b 100644
+--- a/storage/innobase/include/ha_prototypes.h
++++ b/storage/innobase/include/ha_prototypes.h
+@@ -273,6 +273,23 @@ innobase_casedn_str(
+ /*================*/
+ char* a); /*!< in/out: string to put in lower case */
+
++#ifdef WITH_WSREP
++UNIV_INTERN
++int
++wsrep_innobase_kill_one_trx(void *thd_ptr,
++ const trx_t *bf_trx, trx_t *victim_trx, ibool signal);
++my_bool wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
++int wsrep_thd_conflict_state(void *thd_ptr, my_bool sync);
++my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync);
++int wsrep_trx_order_before(void *thd1, void *thd2);
++int wsrep_innobase_mysql_sort(int mysql_type, uint charset_number,
++ unsigned char* str, unsigned int str_length,
++ unsigned int buf_length);
++UNIV_INTERN
++int
++wsrep_on(void *thd_ptr);
++int wsrep_is_wsrep_xid(const void*);
++#endif /* WITH_WSREP */
+ /**********************************************************************//**
+ Determines the connection character set.
+ @return connection character set */
+diff --git a/storage/innobase/include/hash0hash.h b/storage/innobase/include/hash0hash.h
+index 6f9a628..9a4077b 100644
+--- a/storage/innobase/include/hash0hash.h
++++ b/storage/innobase/include/hash0hash.h
+@@ -144,6 +144,33 @@ do {\
+ }\
+ } while (0)
+
++#ifdef WITH_WSREP
++/*******************************************************************//**
++Inserts a struct to the head of hash table. */
++
++#define HASH_PREPEND(TYPE, NAME, TABLE, FOLD, DATA) \
++do { \
++ hash_cell_t* cell3333; \
++ TYPE* struct3333; \
++ \
++ HASH_ASSERT_OWN(TABLE, FOLD) \
++ \
++ (DATA)->NAME = NULL; \
++ \
++ cell3333 = hash_get_nth_cell(TABLE, hash_calc_hash(FOLD, TABLE));\
++ \
++ if (cell3333->node == NULL) { \
++ cell3333->node = DATA; \
++ DATA->NAME = NULL; \
++ } else { \
++ struct3333 = (TYPE*) cell3333->node; \
++ \
++ DATA->NAME = struct3333; \
++ \
++ cell3333->node = DATA; \
++ } \
++} while (0)
++#endif /*WITH_WSREP */
+ #ifdef UNIV_HASH_DEBUG
+ # define HASH_ASSERT_VALID(DATA) ut_a((void*) (DATA) != (void*) -1)
+ # define HASH_INVALIDATE(DATA, NAME) *(void**) (&DATA->NAME) = (void*) -1
+diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
+index 6d5ed35..385853b 100644
+--- a/storage/innobase/include/lock0lock.h
++++ b/storage/innobase/include/lock0lock.h
+@@ -972,6 +972,16 @@ extern lock_sys_t* lock_sys;
+ mutex_exit(&lock_sys->wait_mutex); \
+ } while (0)
+
++#ifdef WITH_WSREP
++/*********************************************************************//**
++Cancels a waiting lock request and releases possible other transactions
++waiting behind it. */
++UNIV_INTERN
++void
++lock_cancel_waiting_and_release(
++/*============================*/
++ lock_t* lock); /*!< in/out: waiting lock request */
++#endif /* WITH_WSREP */
+ #ifndef UNIV_NONINL
+ #include "lock0lock.ic"
+ #endif
+diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h
+index 8e7d5ff..5dc4971 100644
+--- a/storage/innobase/include/rem0rec.h
++++ b/storage/innobase/include/rem0rec.h
+@@ -981,6 +981,15 @@ are given in one byte (resp. two byte) format. */
+ two upmost bits in a two byte offset for special purposes */
+ #define REC_MAX_DATA_SIZE (16 * 1024)
+
++#ifdef WITH_WSREP
++dberr_t wsrep_rec_get_foreign_key(
++ byte *buf, /* out: extracted key */
++ ulint *buf_len, /* in/out: length of buf */
++ const rec_t* rec, /* in: physical record */
++ dict_index_t* index_for, /* in: index for foreign table */
++ dict_index_t* index_ref, /* in: index for referenced table */
++ ibool new_protocol); /* in: protocol > 1 */
++#endif /* WITH_WSREP */
+ #ifndef UNIV_NONINL
+ #include "rem0rec.ic"
+ #endif
+diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h
+index 7a6c9f9..4b40c36 100644
+--- a/storage/innobase/include/srv0srv.h
++++ b/storage/innobase/include/srv0srv.h
+@@ -257,6 +257,10 @@ extern ulong srv_flush_log_at_trx_commit;
+ extern uint srv_flush_log_at_timeout;
+ extern char srv_adaptive_flushing;
+
++#ifdef WITH_INNODB_DISALLOW_WRITES
++/* When this event is reset we do not allow any file writes to take place. */
++extern os_event_t srv_allow_writes_event;
++#endif /* WITH_INNODB_DISALLOW_WRITES */
+ /* If this flag is TRUE, then we will load the indexes' (and tables') metadata
+ even if they are marked as "corrupted". Mostly it is for DBA to process
+ corrupted index and table */
+@@ -884,5 +888,13 @@ struct srv_slot_t{
+ # define srv_start_raw_disk_in_use 0
+ # define srv_file_per_table 1
+ #endif /* !UNIV_HOTBACKUP */
++#ifdef WITH_WSREP
++UNIV_INTERN
++void
++wsrep_srv_conc_cancel_wait(
++/*==================*/
++ trx_t* trx); /*!< in: transaction object associated with the
++ thread */
++#endif /* WITH_WSREP */
+
+ #endif
+diff --git a/storage/innobase/include/sync0sync.ic b/storage/innobase/include/sync0sync.ic
+index 616e53d..2a85d2b 100644
+--- a/storage/innobase/include/sync0sync.ic
++++ b/storage/innobase/include/sync0sync.ic
+@@ -213,8 +213,10 @@ mutex_enter_func(
+ ulint line) /*!< in: line where locked */
+ {
+ ut_ad(mutex_validate(mutex));
++#ifndef WITH_WSREP
++ /* this cannot be be granted when BF trx kills a trx in lock wait state */
+ ut_ad(!mutex_own(mutex));
+-
++#endif /* WITH_WSREP */
+ /* Note that we do not peek at the value of lock_word before trying
+ the atomic test_and_set; we could peek, and possibly save time. */
+
+diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h
+index 70f214d..28ed5bf 100644
+--- a/storage/innobase/include/trx0sys.h
++++ b/storage/innobase/include/trx0sys.h
+@@ -41,6 +41,9 @@ Created 3/26/1996 Heikki Tuuri
+ #include "ut0bh.h"
+ #include "read0types.h"
+ #include "page0types.h"
++#ifdef WITH_WSREP
++#include "trx0xa.h"
++#endif /* WITH_WSREP */
+ #include "ut0bh.h"
+
+ typedef UT_LIST_BASE_NODE_T(trx_t) trx_list_t;
+@@ -293,6 +296,9 @@ trx_sys_update_mysql_binlog_offset(
+ ib_int64_t offset, /*!< in: position in that log file */
+ ulint field, /*!< in: offset of the MySQL log info field in
+ the trx sys header */
++#ifdef WITH_WSREP
++ trx_sysf_t* sys_header, /*!< in: trx sys header */
++#endif /* WITH_WSREP */
+ mtr_t* mtr); /*!< in: mtr */
+ /*****************************************************************//**
+ Prints to stderr the MySQL binlog offset info in the trx system header if
+@@ -301,6 +307,19 @@ UNIV_INTERN
+ void
+ trx_sys_print_mysql_binlog_offset(void);
+ /*===================================*/
++#ifdef WITH_WSREP
++/** Update WSREP checkpoint XID in sys header. */
++void
++trx_sys_update_wsrep_checkpoint(
++ const XID* xid, /*!< in: WSREP XID */
++ trx_sysf_t* sys_header, /*!< in: sys_header */
++ mtr_t* mtr); /*!< in: mtr */
++
++void
++/** Read WSREP checkpoint XID from sys header. */
++trx_sys_read_wsrep_checkpoint(
++ XID* xid); /*!< out: WSREP XID */
++#endif /* WITH_WSREP */
+ /*****************************************************************//**
+ Prints to stderr the MySQL master log offset info in the trx system header if
+ the magic number shows it valid. */
+@@ -529,6 +548,20 @@ this contains the same fields as TRX_SYS_MYSQL_LOG_INFO below */
+ within that file */
+ #define TRX_SYS_MYSQL_LOG_NAME 12 /*!< MySQL log file name */
+
++#ifdef WITH_WSREP
++/* The offset to WSREP XID headers */
++#define TRX_SYS_WSREP_XID_INFO (UNIV_PAGE_SIZE - 3500)
++#define TRX_SYS_WSREP_XID_MAGIC_N_FLD 0
++#define TRX_SYS_WSREP_XID_MAGIC_N 0x77737265
++
++/* XID field: formatID, gtrid_len, bqual_len, xid_data */
++#define TRX_SYS_WSREP_XID_LEN (4 + 4 + 4 + XIDDATASIZE)
++#define TRX_SYS_WSREP_XID_FORMAT 4
++#define TRX_SYS_WSREP_XID_GTRID_LEN 8
++#define TRX_SYS_WSREP_XID_BQUAL_LEN 12
++#define TRX_SYS_WSREP_XID_DATA 16
++#endif /* WITH_WSREP*/
++
+ /** Doublewrite buffer */
+ /* @{ */
+ /** The offset of the doublewrite buffer header on the trx system header page */
+diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic
+index e097e29..7265a97 100644
+--- a/storage/innobase/include/trx0sys.ic
++++ b/storage/innobase/include/trx0sys.ic
+@@ -445,7 +445,10 @@ trx_id_t
+ trx_sys_get_new_trx_id(void)
+ /*========================*/
+ {
++#ifndef WITH_WSREP
++ /* wsrep_fake_trx_id violates this assert */
+ ut_ad(mutex_own(&trx_sys->mutex));
++#endif /* WITH_WSREP */
+
+ /* VERY important: after the database is started, max_trx_id value is
+ divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if
+diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
+index 144e180..6adbe2c 100644
+--- a/storage/innobase/include/trx0trx.h
++++ b/storage/innobase/include/trx0trx.h
+@@ -1004,6 +1004,9 @@ struct trx_t{
+ /*------------------------------*/
+ char detailed_error[256]; /*!< detailed error message for last
+ error, or empty. */
++#ifdef WITH_WSREP
++ os_event_t wsrep_event; /* event waited for in srv_conc_slot */
++#endif /* WITH_WSREP */
+ };
+
+ /* Transaction isolation levels (trx->isolation_level) */
+diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
+index bf7ca16..881507a 100644
+--- a/storage/innobase/lock/lock0lock.cc
++++ b/storage/innobase/lock/lock0lock.cc
+@@ -50,6 +50,10 @@ Created 5/7/1996 Heikki Tuuri
+ #include "dict0boot.h"
+ #include <set>
+
++#ifdef WITH_WSREP
++extern my_bool wsrep_debug;
++extern my_bool wsrep_log_conflicts;
++#endif
+ /* Restricts the length of search we will do in the waits-for
+ graph of transactions */
+ #define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
+@@ -945,6 +949,9 @@ UNIV_INLINE
+ ibool
+ lock_rec_has_to_wait(
+ /*=================*/
++#ifdef WITH_WSREP
++ ibool for_locking, /*!< is caller locking or releasing */
++#endif /* WITH_WSREP */
+ const trx_t* trx, /*!< in: trx of new lock */
+ ulint type_mode,/*!< in: precise mode of the new lock
+ to set: LOCK_S or LOCK_X, possibly
+@@ -1015,9 +1022,59 @@ lock_rec_has_to_wait(
+ return(FALSE);
+ }
+
++#ifdef WITH_WSREP
++ /* if BF thread is locking and has conflict with another BF
++ thread, we need to look at trx ordering and lock types */
++ if (wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
++ wsrep_thd_is_BF(lock2->trx->mysql_thd, TRUE)) {
++
++ if (wsrep_debug) {
++ fprintf(stderr,
++ "BF-BF lock conflict, locking: %lu\n",
++ for_locking);
++ lock_rec_print(stderr, lock2);
++ }
++
++ if (wsrep_trx_order_before(trx->mysql_thd,
++ lock2->trx->mysql_thd) &&
++ (type_mode & LOCK_MODE_MASK) == LOCK_X &&
++ (lock2->type_mode & LOCK_MODE_MASK) == LOCK_X)
++ {
++ if (for_locking || wsrep_debug) {
++ /* exclusive lock conflicts are not
++ accepted */
++ fprintf(stderr,
++ "BF-BF X lock conflict,"
++ "mode: %lu supremum: %lu\n",
++ type_mode, lock_is_on_supremum);
++ fprintf(stderr,
++ "conflicts states: my %d locked %d\n",
++ wsrep_thd_conflict_state(trx->mysql_thd, FALSE),
++ wsrep_thd_conflict_state(lock2->trx->mysql_thd, FALSE) );
++ lock_rec_print(stderr, lock2);
++ if (for_locking) return FALSE;
++ //abort();
++ }
++ } else {
++ /* if lock2->index->n_uniq <=
++ lock2->index->n_user_defined_cols
++ operation is on uniq index
++ */
++ if (wsrep_debug) fprintf(stderr,
++ "BF conflict, modes: %lu %lu, "
++ "idx: %s-%s n_uniq %u n_user %u\n",
++ type_mode, lock2->type_mode,
++ lock2->index->name,
++ lock2->index->table_name,
++ lock2->index->n_uniq,
++ lock2->index->n_user_defined_cols);
++ return FALSE;
++ }
++ }
++#endif /* WITH_WSREP */
+ return(TRUE);
+ }
+-
++
+ return(FALSE);
+ }
+
+@@ -1045,7 +1102,11 @@ lock_has_to_wait(
+ /* If this lock request is for a supremum record
+ then the second bit on the lock bitmap is set */
+
++#ifdef WITH_WSREP
++ return(lock_rec_has_to_wait(FALSE, lock1->trx,
++#else
+ return(lock_rec_has_to_wait(lock1->trx,
++#endif /* WITH_WSREP */
+ lock1->type_mode, lock2,
+ lock_rec_get_nth_bit(
+ lock1, 1)));
+@@ -1514,6 +1575,11 @@ lock_rec_has_expl(
+ return(NULL);
+ }
+
++#ifdef WITH_WSREP
++static
++void
++lock_rec_discard(lock_t* in_lock);
++#endif
+ #ifdef UNIV_DEBUG
+ /*********************************************************************//**
+ Checks if some other transaction has a lock request in the queue.
+@@ -1562,6 +1628,58 @@ lock_rec_other_has_expl_req(
+ }
+ #endif /* UNIV_DEBUG */
+
++#ifdef WITH_WSREP
++static void
++wsrep_kill_victim(const trx_t * const trx, const lock_t *lock) {
++ ut_ad(lock_mutex_own());
++ ut_ad(trx_mutex_own(lock->trx));
++ my_bool bf_this = wsrep_thd_is_BF(trx->mysql_thd, FALSE);
++ my_bool bf_other = wsrep_thd_is_BF(lock->trx->mysql_thd, TRUE);
++
++ if ((bf_this && !bf_other) ||
++ (bf_this && bf_other && wsrep_trx_order_before(
++ trx->mysql_thd, lock->trx->mysql_thd))) {
++
++ if (lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
++ if (wsrep_debug)
++ fprintf(stderr, "WSREP: BF victim waiting\n");
++ /* cannot release lock, until our lock
++ is in the queue*/
++ } else if (lock->trx != trx) {
++ if (wsrep_log_conflicts) {
++ mutex_enter(&trx_sys->mutex);
++ if (bf_this)
++ fputs("\n*** Priority TRANSACTION:\n",
++ stderr);
++ else
++ fputs("\n*** Victim TRANSACTION:\n",
++ stderr);
++ trx_print_latched(stderr, trx, 3000);
++
++ if (bf_other)
++ fputs("\n*** Priority TRANSACTION:\n",
++ stderr);
++ else
++ fputs("\n*** Victim TRANSACTION:\n",
++ stderr);
++ trx_print_latched(stderr, lock->trx, 3000);
++
++ mutex_exit(&trx_sys->mutex);
++ fputs("*** WAITING FOR THIS LOCK TO BE GRANTED:\n",
++ stderr);
++
++ if (lock_get_type(lock) == LOCK_REC) {
++ lock_rec_print(stderr, lock);
++ } else {
++ lock_table_print(stderr, lock);
++ }
++ }
++ wsrep_innobase_kill_one_trx(trx->mysql_thd,
++ (const trx_t*) trx, lock->trx, TRUE);
++ }
++ }
++}
++#endif
+ /*********************************************************************//**
+ Checks if some other transaction has a conflicting explicit lock request
+ in the queue, so that we have to wait.
+@@ -1590,7 +1708,14 @@ lock_rec_other_has_conflicting(
+ lock != NULL;
+ lock = lock_rec_get_next_const(heap_no, lock)) {
+
++#ifdef WITH_WSREP
++ if (lock_rec_has_to_wait(TRUE, trx, mode, lock, is_supremum)) {
++ trx_mutex_enter(lock->trx);
++ wsrep_kill_victim(trx, lock);
++ trx_mutex_exit(lock->trx);
++#else
+ if (lock_rec_has_to_wait(trx, mode, lock, is_supremum)) {
++#endif /* WITH_WSREP */
+ return(lock);
+ }
+ }
+@@ -1683,6 +1808,7 @@ lock_sec_rec_some_has_impl(
+ return(trx_id);
+ }
+
++#ifndef WITH_WSREP
+ #ifdef UNIV_DEBUG
+ /*********************************************************************//**
+ Checks if some transaction, other than given trx_id, has an explicit
+@@ -1733,6 +1859,7 @@ lock_rec_other_trx_holds_expl(
+ return(holds);
+ }
+ #endif /* UNIV_DEBUG */
++#endif /* !WITH_WSREP */
+
+ /*********************************************************************//**
+ Return approximate number or record locks (bits set in the bitmap) for
+@@ -1770,6 +1897,27 @@ lock_number_of_rows_locked(
+ }
+
+ /*============== RECORD LOCK CREATION AND QUEUE MANAGEMENT =============*/
++#ifdef WITH_WSREP
++static
++void
++wsrep_print_wait_locks(
++/*============*/
++ lock_t* c_lock) /* conflicting lock to print */
++{
++ if (wsrep_debug && c_lock->trx->lock.wait_lock != c_lock) {
++ fprintf(stderr, "WSREP: c_lock != wait lock\n");
++ if (lock_get_type_low(c_lock) & LOCK_TABLE)
++ lock_table_print(stderr, c_lock);
++ else
++ lock_rec_print(stderr, c_lock);
++
++ if (lock_get_type_low(c_lock->trx->lock.wait_lock) & LOCK_TABLE)
++ lock_table_print(stderr, c_lock->trx->lock.wait_lock);
++ else
++ lock_rec_print(stderr, c_lock->trx->lock.wait_lock);
++ }
++}
++#endif /* WITH_WSREP */
+
+ /*********************************************************************//**
+ Creates a new record lock and inserts it to the lock queue. Does NOT check
+@@ -1779,6 +1927,10 @@ static
+ lock_t*
+ lock_rec_create(
+ /*============*/
++#ifdef WITH_WSREP
++ lock_t* const c_lock, /* conflicting lock */
++ que_thr_t* thr,
++#endif
+ ulint type_mode,/*!< in: lock mode and wait
+ flag, type is ignored and
+ replaced by LOCK_REC */
+@@ -1850,9 +2002,79 @@ lock_rec_create(
+
+ ut_ad(index->table->n_ref_count > 0 || !index->table->can_be_evicted);
+
++#ifdef WITH_WSREP
++
++ if (c_lock && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
++ lock_t *hash = (lock_t *)c_lock->hash;
++ lock_t *prev = NULL;
++
++ while (hash &&
++ wsrep_thd_is_BF(((lock_t *)hash)->trx->mysql_thd, TRUE) &&
++ wsrep_trx_order_before(
++ ((lock_t *)hash)->trx->mysql_thd,
++ trx->mysql_thd)) {
++ prev = hash;
++ hash = (lock_t *)hash->hash;
++ }
++ lock->hash = hash;
++ if (prev) {
++ prev->hash = lock;
++ } else {
++ c_lock->hash = lock;
++ }
++ /*
++ * delayed conflict resolution '...kill_one_trx' was not called,
++ * if victim was waiting for some other lock
++ */
++ trx_mutex_enter(c_lock->trx);
++ if (c_lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
++
++ c_lock->trx->lock.was_chosen_as_deadlock_victim = TRUE;
++
++ if (wsrep_debug) wsrep_print_wait_locks(c_lock);
++
++ trx->lock.que_state = TRX_QUE_LOCK_WAIT;
++ lock_set_lock_and_trx_wait(lock, trx);
++ UT_LIST_ADD_LAST(trx_locks, trx->lock.trx_locks, lock);
++
++ ut_ad(thr != NULL);
++ trx->lock.wait_thr = thr;
++ thr->state = QUE_THR_LOCK_WAIT;
++
++ /* have to release trx mutex for the duration of
++ victim lock release. This will eventually call
++ lock_grant, which wants to grant trx mutex again
++ */
++ if (caller_owns_trx_mutex) trx_mutex_exit(trx);
++ lock_cancel_waiting_and_release(
++ c_lock->trx->lock.wait_lock);
++ if (caller_owns_trx_mutex) trx_mutex_enter(trx);
++
++ /* trx might not wait for c_lock, but some other lock
++ does not matter if wait_lock was released above
++ */
++ if (c_lock->trx->lock.wait_lock == c_lock) {
++ lock_reset_lock_and_trx_wait(lock);
++ }
++ trx_mutex_exit(c_lock->trx);
++
++ if (wsrep_debug) fprintf(
++ stderr,
++ "WSREP: c_lock canceled %llu\n",
++ (ulonglong) c_lock->trx->id);
++
++ /* have to bail out here to avoid lock_set_lock... */
++ return(lock);
++ }
++ trx_mutex_exit(c_lock->trx);
++ } else {
++ HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
++ lock_rec_fold(space, page_no), lock);
++ }
++#else
+ HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
+ lock_rec_fold(space, page_no), lock);
+-
++#endif /* WITH_WSREP */
+ if (!caller_owns_trx_mutex) {
+ trx_mutex_enter(trx);
+ }
+@@ -1871,7 +2093,6 @@ lock_rec_create(
+
+ MONITOR_INC(MONITOR_RECLOCK_CREATED);
+ MONITOR_INC(MONITOR_NUM_RECLOCK);
+-
+ return(lock);
+ }
+
+@@ -1886,6 +2107,9 @@ static
+ dberr_t
+ lock_rec_enqueue_waiting(
+ /*=====================*/
++#ifdef WITH_WSREP
++ lock_t* c_lock, /* conflicting lock */
++#endif
+ ulint type_mode,/*!< in: lock mode this
+ transaction is requesting:
+ LOCK_S or LOCK_X, possibly
+@@ -1942,8 +2166,17 @@ lock_rec_enqueue_waiting(
+
+ /* Enqueue the lock request that will wait to be granted, note that
+ we already own the trx mutex. */
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd) &&
++ trx->lock.was_chosen_as_deadlock_victim) {
++ return(DB_DEADLOCK);
++ }
++ lock = lock_rec_create(c_lock, thr,
++ type_mode | LOCK_WAIT, block, heap_no, index, trx, TRUE);
++#else
+ lock = lock_rec_create(
+ type_mode | LOCK_WAIT, block, heap_no, index, trx, TRUE);
++#endif /* WITH_WSREP */
+
+ /* Release the mutex to obey the latching order.
+ This is safe, because lock_deadlock_check_and_resolve()
+@@ -2044,7 +2277,19 @@ lock_rec_add_to_queue(
+ const lock_t* other_lock
+ = lock_rec_other_has_expl_req(mode, 0, LOCK_WAIT,
+ block, heap_no, trx);
++#ifdef WITH_WSREP
++ /* this can potentionally assert with wsrep */
++ if (wsrep_on(trx->mysql_thd)) {
++ if (wsrep_debug && other_lock) {
++ fprintf(stderr,
++ "WSREP: InnoDB assert ignored\n");
++ }
++ } else {
++ ut_a(!other_lock);
++ }
++#else
+ ut_a(!other_lock);
++#endif /* WITH_WSREP */
+ }
+ #endif /* UNIV_DEBUG */
+
+@@ -2072,7 +2317,16 @@ lock_rec_add_to_queue(
+
+ if (lock_get_wait(lock)
+ && lock_rec_get_nth_bit(lock, heap_no)) {
+-
++#ifdef WITH_WSREP
++ if (wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
++ if (wsrep_debug) {
++ fprintf(stderr,
++ "BF skipping wait: %lu\n",
++ trx->id);
++ lock_rec_print(stderr, lock);
++ }
++ } else
++#endif
+ goto somebody_waits;
+ }
+ }
+@@ -2095,9 +2349,15 @@ lock_rec_add_to_queue(
+ }
+
+ somebody_waits:
++#ifdef WITH_WSREP
++ return(lock_rec_create(NULL, NULL,
++ type_mode, block, heap_no, index, trx,
++ caller_owns_trx_mutex));
++#else
+ return(lock_rec_create(
+ type_mode, block, heap_no, index, trx,
+ caller_owns_trx_mutex));
++#endif
+ }
+
+ /** Record locking request status */
+@@ -2160,9 +2420,13 @@ lock_rec_lock_fast(
+ if (lock == NULL) {
+ if (!impl) {
+ /* Note that we don't own the trx mutex. */
++#ifdef WITH_WSREP
++ lock = lock_rec_create(NULL, thr,
++ mode, block, heap_no, index, trx, FALSE);
++#else
+ lock = lock_rec_create(
+ mode, block, heap_no, index, trx, FALSE);
+-
++#endif
+ }
+ status = LOCK_REC_SUCCESS_CREATED;
+ } else {
+@@ -2215,6 +2479,9 @@ lock_rec_lock_slow(
+ que_thr_t* thr) /*!< in: query thread */
+ {
+ trx_t* trx;
++#ifdef WITH_WSREP
++ lock_t* c_lock(NULL);
++#endif
+ dberr_t err = DB_SUCCESS;
+
+ ut_ad(lock_mutex_own());
+@@ -2239,18 +2506,30 @@ lock_rec_lock_slow(
+ /* The trx already has a strong enough lock on rec: do
+ nothing */
+
++#ifdef WITH_WSREP
++ } else if ((c_lock = (lock_t *)lock_rec_other_has_conflicting(
++ static_cast<enum lock_mode>(mode),
++ block, heap_no, trx))) {
++#else
+ } else if (lock_rec_other_has_conflicting(
+ static_cast<enum lock_mode>(mode),
+ block, heap_no, trx)) {
+-
++#endif /* WITH_WSREP */
+ /* If another transaction has a non-gap conflicting
+ request in the queue, as this transaction does not
+ have a lock strong enough already granted on the
+ record, we have to wait. */
+
++#ifdef WITH_WSREP
++ /* c_lock is NULL here if jump to enqueue_waiting happened
++ but it's ok because lock is not NULL in that case and c_lock
++ is not used. */
++ err = lock_rec_enqueue_waiting(c_lock,
++ mode, block, heap_no, index, thr);
++#else
+ err = lock_rec_enqueue_waiting(
+ mode, block, heap_no, index, thr);
+-
++#endif /* WITH_WSREP */
+ } else if (!impl) {
+ /* Set the requested lock on the record, note that
+ we already own the transaction mutex. */
+@@ -2355,7 +2634,13 @@ lock_rec_has_to_wait_in_queue(
+ if (heap_no < lock_rec_get_n_bits(lock)
+ && (p[bit_offset] & bit_mask)
+ && lock_has_to_wait(wait_lock, lock)) {
+-
++#ifdef WITH_WSREP
++ if (wsrep_thd_is_BF(wait_lock->trx->mysql_thd, FALSE) &&
++ wsrep_thd_is_BF(lock->trx->mysql_thd, TRUE)) {
++ /* don't wait for another BF lock */
++ continue;
++ }
++#endif
+ return(lock);
+ }
+ }
+@@ -3739,9 +4024,19 @@ lock_deadlock_select_victim(
+ /* The joining transaction is 'smaller',
+ choose it as the victim and roll it back. */
+
++#ifdef WITH_WSREP
++ if (wsrep_thd_is_BF(ctx->start->mysql_thd, TRUE))
++ return(ctx->wait_lock->trx);
++ else
++#endif /* WITH_WSREP */
+ return(ctx->start);
+ }
+
++#ifdef WITH_WSREP
++ if (wsrep_thd_is_BF(ctx->wait_lock->trx->mysql_thd, TRUE))
++ return(ctx->start);
++ else
++#endif /* WITH_WSREP */
+ return(ctx->wait_lock->trx);
+ }
+
+@@ -3872,6 +4167,11 @@ lock_deadlock_search(
+ ctx->too_deep = TRUE;
+
+ /* Select the joining transaction as the victim. */
++#ifdef WITH_WSREP
++ if (wsrep_thd_is_BF(ctx->start->mysql_thd, TRUE))
++ return(ctx->wait_lock->trx->id);
++ else
++#endif /* WITH_WSREP */
+ return(ctx->start->id);
+
+ } else if (lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
+@@ -3889,6 +4189,11 @@ lock_deadlock_search(
+
+ ctx->too_deep = TRUE;
+
++#ifdef WITH_WSREP
++ if (wsrep_thd_is_BF(ctx->start->mysql_thd, TRUE))
++ return(lock->trx->id);
++ else
++#endif /* WITH_WSREP */
+ return(ctx->start->id);
+ }
+
+@@ -4005,11 +4310,21 @@ lock_deadlock_check_and_resolve(
+ if (ctx.too_deep) {
+
+ ut_a(trx == ctx.start);
++
++#ifdef WITH_WSREP
++ if (!wsrep_thd_is_BF(ctx.start->mysql_thd, TRUE))
++ {
++#endif /* WITH_WSREP */
+ ut_a(victim_trx_id == trx->id);
+
+ if (!srv_read_only_mode) {
+ lock_deadlock_joining_trx_print(trx, lock);
+ }
++#ifdef WITH_WSREP
++ } else {
++ /* BF processor */;
++ }
++#endif /* WITH_WSREP */
+
+ MONITOR_INC(MONITOR_DEADLOCK);
+
+@@ -4047,6 +4362,9 @@ UNIV_INLINE
+ lock_t*
+ lock_table_create(
+ /*==============*/
++#ifdef WITH_WSREP
++ lock_t* c_lock, /* conflicting lock */
++#endif
+ dict_table_t* table, /*!< in/out: database table
+ in dictionary cache */
+ ulint type_mode,/*!< in: lock mode possibly ORed with
+@@ -4090,7 +4408,46 @@ lock_table_create(
+ ut_ad(table->n_ref_count > 0 || !table->can_be_evicted);
+
+ UT_LIST_ADD_LAST(trx_locks, trx->lock.trx_locks, lock);
++#ifdef WITH_WSREP
++ if (c_lock && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
++ UT_LIST_INSERT_AFTER(
++ un_member.tab_lock.locks, table->locks, c_lock, lock);
++ } else {
++ UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock);
++ }
++
++ if (c_lock) trx_mutex_enter(c_lock->trx);
++ if (c_lock && c_lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
++
++ c_lock->trx->lock.was_chosen_as_deadlock_victim = TRUE;
++
++ if (wsrep_debug) wsrep_print_wait_locks(c_lock);
++
++ /* have to release trx mutex for the duration of
++ victim lock release. This will eventually call
++ lock_grant, which wants to grant trx mutex again
++ */
++ /* caller has trx_mutex, have to release for lock cancel */
++ trx_mutex_exit(trx);
++ lock_cancel_waiting_and_release(c_lock->trx->lock.wait_lock);
++ trx_mutex_enter(trx);
++
++ /* trx might not wait for c_lock, but some other lock
++ does not matter if wait_lock was released above
++ */
++ if (c_lock->trx->lock.wait_lock == c_lock) {
++ lock_reset_lock_and_trx_wait(lock);
++ }
++
++ if (wsrep_debug) {
++ fprintf(stderr, "WSREP: c_lock canceled %llu\n",
++ (ulonglong) c_lock->trx->id);
++ }
++ }
++ if (c_lock) trx_mutex_exit(c_lock->trx);
++#else
+ UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock);
++#endif /* WITH_WSREP */
+
+ if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) {
+
+@@ -4247,12 +4604,15 @@ static
+ dberr_t
+ lock_table_enqueue_waiting(
+ /*=======================*/
++#ifdef WITH_WSREP
++ lock_t* c_lock, /* conflicting lock */
++#endif
+ ulint mode, /*!< in: lock mode this transaction is
+ requesting */
+ dict_table_t* table, /*!< in/out: table */
+ que_thr_t* thr) /*!< in: query thread */
+ {
+- trx_t* trx;
++ trx_t* trx;
+ lock_t* lock;
+ trx_id_t victim_trx_id;
+
+@@ -4291,7 +4651,14 @@ lock_table_enqueue_waiting(
+
+ /* Enqueue the lock request that will wait to be granted */
+
++#ifdef WITH_WSREP
++ if (trx->lock.was_chosen_as_deadlock_victim) {
++ return(DB_DEADLOCK);
++ }
++ lock = lock_table_create(c_lock, table, mode | LOCK_WAIT, trx);
++#else
+ lock = lock_table_create(table, mode | LOCK_WAIT, trx);
++#endif
+
+ /* Release the mutex to obey the latching order.
+ This is safe, because lock_deadlock_check_and_resolve()
+@@ -4363,6 +4730,13 @@ lock_table_other_has_incompatible(
+ && !lock_mode_compatible(lock_get_mode(lock), mode)
+ && (wait || !lock_get_wait(lock))) {
+
++#ifdef WITH_WSREP
++ if (wsrep_debug)
++ fprintf(stderr, "WSREP: table lock abort");
++ trx_mutex_enter(lock->trx);
++ wsrep_kill_victim((trx_t *)trx, (lock_t *)lock);
++ trx_mutex_exit(lock->trx);
++#endif
+ return(lock);
+ }
+ }
+@@ -4424,9 +4798,18 @@ lock_table(
+ mode: this trx may have to wait */
+
+ if (wait_for != NULL) {
++#ifdef WITH_WSREP
++ err = lock_table_enqueue_waiting((lock_t *)wait_for,
++ mode | flags, table, thr);
++#else
+ err = lock_table_enqueue_waiting(mode | flags, table, thr);
++#endif /* WITH_WSREP */
+ } else {
++#ifdef WITH_WSREP
++ lock_table_create((lock_t *)wait_for, table, mode | flags, trx);
++#else
+ lock_table_create(table, mode | flags, trx);
++#endif /* WITH_WSREP */
+
+ ut_a(!flags || mode == LOCK_S || mode == LOCK_X);
+
+@@ -4464,7 +4847,11 @@ lock_table_ix_resurrect(
+ trx, LOCK_WAIT, table, LOCK_IX));
+
+ trx_mutex_enter(trx);
++#ifdef WITH_WSREP
++ lock_table_create(NULL, table, LOCK_IX, trx);
++#else
+ lock_table_create(table, LOCK_IX, trx);
++#endif /* WITH_WSREP */
+ lock_mutex_exit();
+ trx_mutex_exit(trx);
+ }
+@@ -5612,6 +5999,7 @@ lock_rec_queue_validate(
+
+ if (!lock_rec_get_gap(lock) && !lock_get_wait(lock)) {
+
++#ifndef WITH_WSREP
+ enum lock_mode mode;
+
+ if (lock_get_mode(lock) == LOCK_S) {
+@@ -5621,6 +6009,7 @@ lock_rec_queue_validate(
+ }
+ ut_a(!lock_rec_other_has_expl_req(
+ mode, 0, 0, block, heap_no, lock->trx));
++#endif /* WITH_WSREP */
+
+ } else if (lock_get_wait(lock) && !lock_rec_get_gap(lock)) {
+
+@@ -5924,6 +6313,9 @@ lock_rec_insert_check_and_lock(
+ lock_t* lock;
+ dberr_t err;
+ ulint next_rec_heap_no;
++#ifdef WITH_WSREP
++ lock_t *c_lock;
++#endif
+ ibool inherit_in = *inherit;
+
+ ut_ad(block->frame == page_align(rec));
+@@ -5981,17 +6373,29 @@ lock_rec_insert_check_and_lock(
+ had to wait for their insert. Both had waiting gap type lock requests
+ on the successor, which produced an unnecessary deadlock. */
+
++#ifdef WITH_WSREP
++ if ((c_lock = (lock_t *)lock_rec_other_has_conflicting(
++ static_cast<enum lock_mode>(
++ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION),
++ block, next_rec_heap_no, trx))) {
++#else
+ if (lock_rec_other_has_conflicting(
+ static_cast<enum lock_mode>(
+ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION),
+ block, next_rec_heap_no, trx)) {
+-
++#endif /* WITH_WSREP */
+ /* Note that we may get DB_SUCCESS also here! */
+ trx_mutex_enter(trx);
+
++#ifdef WITH_WSREP
++ err = lock_rec_enqueue_waiting(c_lock,
++ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,
++ block, next_rec_heap_no, index, thr);
++#else
+ err = lock_rec_enqueue_waiting(
+ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,
+ block, next_rec_heap_no, index, thr);
++#endif /* WITH_WSREP */
+
+ trx_mutex_exit(trx);
+ } else {
+@@ -6070,8 +6474,10 @@ lock_rec_convert_impl_to_expl(
+ trx_is_active(trx_id, NULL) check below, because we are not
+ holding lock_mutex. */
+
++#ifndef WITH_WSREP
+ ut_ad(!lock_rec_other_trx_holds_expl(LOCK_S | LOCK_REC_NOT_GAP,
+ trx_id, rec, block));
++#endif /* WITH_WSREP */
+ }
+
+ if (trx_id != 0) {
+diff --git a/storage/innobase/lock/lock0wait.cc b/storage/innobase/lock/lock0wait.cc
+index a1c35e2..9d1a4da 100644
+--- a/storage/innobase/lock/lock0wait.cc
++++ b/storage/innobase/lock/lock0wait.cc
+@@ -184,6 +184,28 @@ lock_wait_table_reserve_slot(
+ return(NULL);
+ }
+
++#ifdef WITH_WSREP
++/*********************************************************************//**
++check if lock timeout was for priority thread,
++as a side effect trigger lock monitor
++@return false for regular lock timeout */
++static ibool
++wsrep_is_BF_lock_timeout(
++/*====================*/
++ trx_t* trx) /* in: trx to check for lock priority */
++{
++ if (wsrep_on(trx->mysql_thd) &&
++ wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
++ fprintf(stderr, "WSREP: BF lock wait long\n");
++ srv_print_innodb_monitor = TRUE;
++ srv_print_innodb_lock_monitor = TRUE;
++ os_event_set(srv_monitor_event);
++ return TRUE;
++ }
++ return FALSE;
++ }
++#endif /* WITH_WSREP */
++
+ /***************************************************************//**
+ Puts a user OS thread to wait for a lock to be released. If an error
+ occurs during the wait trx->error_state associated with thr is
+@@ -375,9 +397,15 @@ lock_wait_suspend_thread(
+
+ if (lock_wait_timeout < 100000000
+ && wait_time > (double) lock_wait_timeout) {
++#ifdef WITH_WSREP
++ if (!wsrep_is_BF_lock_timeout(trx)) {
++#endif /* WITH_WSREP */
+
+ trx->error_state = DB_LOCK_WAIT_TIMEOUT;
+
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ MONITOR_INC(MONITOR_TIMEOUT);
+ }
+
+@@ -461,8 +489,14 @@ lock_wait_check_and_cancel(
+ if (trx->lock.wait_lock) {
+
+ ut_a(trx->lock.que_state == TRX_QUE_LOCK_WAIT);
++#ifdef WITH_WSREP
++ if (!wsrep_is_BF_lock_timeout(trx)) {
++#endif /* WITH_WSREP */
+
+ lock_cancel_waiting_and_release(trx->lock.wait_lock);
++#ifdef WITH_WSREP
++ }
++#endif /* WITH_WSREP */
+ }
+
+ lock_mutex_exit();
+diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc
+index fb7e8ca..41372a8 100644
+--- a/storage/innobase/os/os0file.cc
++++ b/storage/innobase/os/os0file.cc
+@@ -87,6 +87,12 @@ UNIV_INTERN os_ib_mutex_t os_file_seek_mutexes[OS_FILE_N_SEEK_MUTEXES];
+ /* In simulated aio, merge at most this many consecutive i/os */
+ #define OS_AIO_MERGE_N_CONSECUTIVE 64
+
++#ifdef WITH_INNODB_DISALLOW_WRITES
++#define WAIT_ALLOW_WRITES() os_event_wait(srv_allow_writes_event)
++#else
++#define WAIT_ALLOW_WRITES() do { } while (0)
++#endif /* WITH_INNODB_DISALLOW_WRITES */
++
+ /**********************************************************************
+
+ InnoDB AIO Implementation:
+@@ -763,7 +769,11 @@ os_file_create_tmpfile(void)
+ /*========================*/
+ {
+ FILE* file = NULL;
+- int fd = innobase_mysql_tmpfile();
++ int fd;
++ WAIT_ALLOW_WRITES();
++ fd = innobase_mysql_tmpfile();
++
++ ut_ad(!srv_read_only_mode);
+
+ ut_ad(!srv_read_only_mode);
+
+@@ -1089,6 +1099,7 @@ os_file_create_directory(
+ return(TRUE);
+ #else
+ int rcode;
++ WAIT_ALLOW_WRITES();
+
+ rcode = mkdir(pathname, 0770);
+
+@@ -1215,6 +1226,8 @@ os_file_create_simple_func(
+
+ #else /* __WIN__ */
+ int create_flag;
++ if (create_mode != OS_FILE_OPEN && create_mode != OS_FILE_OPEN_RAW)
++ WAIT_ALLOW_WRITES();
+
+ ut_a(!(create_mode & OS_FILE_ON_ERROR_SILENT));
+ ut_a(!(create_mode & OS_FILE_ON_ERROR_NO_EXIT));
+@@ -1382,6 +1395,8 @@ os_file_create_simple_no_error_handling_func(
+ int create_flag;
+
+ ut_a(name);
++ if (create_mode != OS_FILE_OPEN && create_mode != OS_FILE_OPEN_RAW)
++ WAIT_ALLOW_WRITES();
+
+ ut_a(!(create_mode & OS_FILE_ON_ERROR_SILENT));
+ ut_a(!(create_mode & OS_FILE_ON_ERROR_NO_EXIT));
+@@ -1673,6 +1688,8 @@ os_file_create_func(
+ #else /* __WIN__ */
+ int create_flag;
+ const char* mode_str = NULL;
++ if (create_mode != OS_FILE_OPEN && create_mode != OS_FILE_OPEN_RAW)
++ WAIT_ALLOW_WRITES();
+
+ on_error_no_exit = create_mode & OS_FILE_ON_ERROR_NO_EXIT
+ ? TRUE : FALSE;
+@@ -1852,6 +1869,7 @@ loop:
+ goto loop;
+ #else
+ int ret;
++ WAIT_ALLOW_WRITES();
+
+ ret = unlink(name);
+
+@@ -1916,6 +1934,7 @@ loop:
+ goto loop;
+ #else
+ int ret;
++ WAIT_ALLOW_WRITES();
+
+ ret = unlink(name);
+
+@@ -1969,6 +1988,7 @@ os_file_rename_func(
+ return(FALSE);
+ #else
+ int ret;
++ WAIT_ALLOW_WRITES();
+
+ ret = rename(oldpath, newpath);
+
+@@ -2180,6 +2200,7 @@ os_file_set_eof(
+ HANDLE h = (HANDLE) _get_osfhandle(fileno(file));
+ return(SetEndOfFile(h));
+ #else /* __WIN__ */
++ WAIT_ALLOW_WRITES();
+ return(!ftruncate(fileno(file), ftell(file)));
+ #endif /* __WIN__ */
+ }
+@@ -2274,6 +2295,7 @@ os_file_flush_func(
+ return(FALSE);
+ #else
+ int ret;
++ WAIT_ALLOW_WRITES();
+
+ #if defined(HAVE_DARWIN_THREADS)
+ # ifndef F_FULLFSYNC
+@@ -2969,6 +2991,7 @@ retry:
+ return(FALSE);
+ #else
+ ssize_t ret;
++ WAIT_ALLOW_WRITES();
+
+ ret = os_file_pwrite(file, buf, n, offset);
+
+diff --git a/storage/innobase/rem/rem0rec.cc b/storage/innobase/rem/rem0rec.cc
+index 0d7b7c1..9540211 100644
+--- a/storage/innobase/rem/rem0rec.cc
++++ b/storage/innobase/rem/rem0rec.cc
+@@ -33,6 +33,9 @@ Created 5/30/1994 Heikki Tuuri
+ #include "mtr0mtr.h"
+ #include "mtr0log.h"
+ #include "fts0fts.h"
++#ifdef WITH_WSREP
++#include <ha_prototypes.h>
++#endif /* WITH_WSREP */
+
+ /* PHYSICAL RECORD (OLD STYLE)
+ ===========================
+@@ -1961,3 +1964,133 @@ rec_get_trx_id(
+ }
+ # endif /* UNIV_DEBUG */
+ #endif /* !UNIV_HOTBACKUP */
++#ifdef WITH_WSREP
++dberr_t
++wsrep_rec_get_foreign_key(
++ byte *buf, /* out: extracted key */
++ ulint *buf_len, /* in/out: length of buf */
++ const rec_t* rec, /* in: physical record */
++ dict_index_t* index_for, /* in: index in foreign table */
++ dict_index_t* index_ref, /* in: index in referenced table */
++ ibool new_protocol) /* in: protocol > 1 */
++{
++ const byte* data;
++ ulint len;
++ ulint key_len = 0;
++ ulint i;
++ uint key_parts;
++ mem_heap_t* heap = NULL;
++ ulint offsets_[REC_OFFS_NORMAL_SIZE];
++ const ulint* offsets;
++
++ ut_ad(index_for);
++ ut_ad(index_ref);
++
++ rec_offs_init(offsets_);
++ offsets = rec_get_offsets(rec, index_for, offsets_,
++ ULINT_UNDEFINED, &heap);
++
++ ut_ad(rec_offs_validate(rec, NULL, offsets));
++
++ ut_ad(rec);
++
++ key_parts = dict_index_get_n_unique_in_tree(index_for);
++ for (i = 0;
++ i < key_parts &&
++ (index_for->type & DICT_CLUSTERED || i < key_parts - 1);
++ i++) {
++ dict_field_t* field_f =
++ dict_index_get_nth_field(index_for, i);
++ const dict_col_t* col_f = dict_field_get_col(field_f);
++ dict_field_t* field_r =
++ dict_index_get_nth_field(index_ref, i);
++ const dict_col_t* col_r = dict_field_get_col(field_r);
++
++ data = rec_get_nth_field(rec, offsets, i, &len);
++ if (key_len + ((len != UNIV_SQL_NULL) ? len + 1 : 1) >
++ *buf_len) {
++ fprintf (stderr,
++ "WSREP: FK key len exceeded %lu %lu %lu\n",
++ key_len, len, *buf_len);
++ goto err_out;
++ }
++
++ if (len == UNIV_SQL_NULL) {
++ ut_a(!(col_f->prtype & DATA_NOT_NULL));
++ *buf++ = 1;
++ key_len++;
++ } else if (!new_protocol) {
++ if (!(col_r->prtype & DATA_NOT_NULL)) {
++ *buf++ = 0;
++ key_len++;
++ }
++ memcpy(buf, data, len);
++ *buf_len = wsrep_innobase_mysql_sort(
++ (int)(col_f->prtype & DATA_MYSQL_TYPE_MASK),
++ (uint)dtype_get_charset_coll(col_f->prtype),
++ buf, len, *buf_len);
++ } else { /* new protocol */
++ if (!(col_r->prtype & DATA_NOT_NULL)) {
++ *buf++ = 0;
++ key_len++;
++ }
++ switch (col_f->mtype) {
++ case DATA_INT: {
++ byte* ptr = buf+len;
++ for (;;) {
++ ptr--;
++ *ptr = *data;
++ if (ptr == buf) {
++ break;
++ }
++ data++;
++ }
++
++ if (!(col_f->prtype & DATA_UNSIGNED)) {
++ buf[len-1] = (byte) (buf[len-1] ^ 128);
++ }
++
++ break;
++ }
++ case DATA_VARCHAR:
++ case DATA_VARMYSQL:
++ case DATA_CHAR:
++ case DATA_MYSQL:
++ /* Copy the actual data */
++ ut_memcpy(buf, data, len);
++ len = wsrep_innobase_mysql_sort(
++ (int)
++ (col_f->prtype & DATA_MYSQL_TYPE_MASK),
++ (uint)
++ dtype_get_charset_coll(col_f->prtype),
++ buf, len, *buf_len);
++ break;
++ case DATA_BLOB:
++ case DATA_BINARY:
++ memcpy(buf, data, len);
++ break;
++ default:
++ break;
++ }
++
++ key_len += len;
++ buf += len;
++ }
++ }
++
++ rec_validate(rec, offsets);
++
++ if (UNIV_LIKELY_NULL(heap)) {
++ mem_heap_free(heap);
++ }
++
++ *buf_len = key_len;
++ return DB_SUCCESS;
++
++ err_out:
++ if (UNIV_LIKELY_NULL(heap)) {
++ mem_heap_free(heap);
++ }
++ return DB_ERROR;
++}
++#endif // WITH_WSREP
+diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
+index e31b447..3c3ccf4 100644
+--- a/storage/innobase/row/row0ins.cc
++++ b/storage/innobase/row/row0ins.cc
+@@ -920,6 +920,14 @@ row_ins_invalidate_query_cache(
+ innobase_invalidate_query_cache(thr_get_trx(thr), buf, len);
+ mem_free(buf);
+ }
++#ifdef WITH_WSREP
++dberr_t wsrep_append_foreign_key(trx_t *trx,
++ dict_foreign_t* foreign,
++ const rec_t* clust_rec,
++ dict_index_t* clust_index,
++ ibool referenced,
++ ibool shared);
++#endif /* WITH_WSREP */
+
+ /*********************************************************************//**
+ Perform referential actions or checks when a parent row is deleted or updated
+@@ -1271,6 +1279,18 @@ row_ins_foreign_check_on_constraint(
+
+ cascade->state = UPD_NODE_UPDATE_CLUSTERED;
+
++#ifdef WITH_WSREP
++ err = wsrep_append_foreign_key(
++ thr_get_trx(thr),
++ foreign,
++ clust_rec,
++ clust_index,
++ FALSE, FALSE);
++ if (err != DB_SUCCESS) {
++ fprintf(stderr,
++ "WSREP: foreign key append failed: %d\n", err);
++ } else
++#endif
+ err = row_update_cascade_for_mysql(thr, cascade,
+ foreign->foreign_table);
+
+@@ -1603,7 +1623,14 @@ run_again:
+
+ if (check_ref) {
+ err = DB_SUCCESS;
+-
++#ifdef WITH_WSREP
++ err = wsrep_append_foreign_key(
++ thr_get_trx(thr),
++ foreign,
++ rec,
++ check_index,
++ check_ref, TRUE);
++#endif /* WITH_WSREP */
+ goto end_scan;
+ } else if (foreign->type != 0) {
+ /* There is an ON UPDATE or ON DELETE
+diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc
+index fcd5433..5a8987a 100644
+--- a/storage/innobase/row/row0upd.cc
++++ b/storage/innobase/row/row0upd.cc
+@@ -51,6 +51,9 @@ Created 12/27/1996 Heikki Tuuri
+ #include "pars0sym.h"
+ #include "eval0eval.h"
+ #include "buf0lru.h"
++#ifdef WITH_WSREP
++extern my_bool wsrep_debug;
++#endif
+ #include <algorithm>
+
+ /* What kind of latch and lock can we assume when the control comes to
+@@ -162,6 +165,42 @@ row_upd_index_is_referenced(
+ return(is_referenced);
+ }
+
++#ifdef WITH_WSREP
++static
++ibool
++wsrep_row_upd_index_is_foreign(
++/*========================*/
++ dict_index_t* index, /*!< in: index */
++ trx_t* trx) /*!< in: transaction */
++{
++ dict_table_t* table = index->table;
++ ibool froze_data_dict = FALSE;
++ ibool is_referenced = FALSE;
++
++ if (table->foreign_set.empty()) {
++ return(FALSE);
++ }
++
++ if (trx->dict_operation_lock_mode == 0) {
++ row_mysql_freeze_data_dictionary(trx);
++ froze_data_dict = TRUE;
++ }
++
++ dict_foreign_set::iterator it
++ = std::find_if(table->foreign_set.begin(),
++ table->foreign_set.end(),
++ dict_foreign_with_foreign_index(index));
++
++ is_referenced = (it != table->foreign_set.end());
++
++ if (froze_data_dict) {
++ row_mysql_unfreeze_data_dictionary(trx);
++ }
++
++ return(is_referenced);
++}
++#endif /* WITH_WSREP */
++
+ /*********************************************************************//**
+ Checks if possible foreign key constraints hold after a delete of the record
+ under pcur.
+@@ -281,7 +320,6 @@ run_again:
+ }
+
+ err = DB_SUCCESS;
+-
+ func_exit:
+ if (got_s_lock) {
+ row_mysql_unfreeze_data_dictionary(trx);
+@@ -293,6 +331,127 @@ func_exit:
+
+ return(err);
+ }
++#ifdef WITH_WSREP
++static
++dberr_t
++wsrep_row_upd_check_foreign_constraints(
++/*=================================*/
++ upd_node_t* node, /*!< in: row update node */
++ btr_pcur_t* pcur, /*!< in: cursor positioned on a record; NOTE: the
++ cursor position is lost in this function! */
++ dict_table_t* table, /*!< in: table in question */
++ dict_index_t* index, /*!< in: index of the cursor */
++ ulint* offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */
++ que_thr_t* thr, /*!< in: query thread */
++ mtr_t* mtr) /*!< in: mtr */
++{
++ dict_foreign_t* foreign;
++ mem_heap_t* heap;
++ dtuple_t* entry;
++ trx_t* trx;
++ const rec_t* rec;
++ ulint n_ext;
++ dberr_t err;
++ ibool got_s_lock = FALSE;
++ ibool opened = FALSE;
++
++ if (table->foreign_set.empty()) {
++ return(DB_SUCCESS);
++ }
++
++ trx = thr_get_trx(thr);
++
++ /* TODO: make native slave thread bail out here */
++
++ rec = btr_pcur_get_rec(pcur);
++ ut_ad(rec_offs_validate(rec, index, offsets));
++
++ heap = mem_heap_create(500);
++
++ entry = row_rec_to_index_entry(rec, index, offsets,
++ &n_ext, heap);
++
++ mtr_commit(mtr);
++
++ mtr_start(mtr);
++
++ if (trx->dict_operation_lock_mode == 0) {
++ got_s_lock = TRUE;
++
++ row_mysql_freeze_data_dictionary(trx);
++ }
++
++run_again:
++
++ for (dict_foreign_set::iterator it = table->foreign_set.begin();
++ it != table->foreign_set.end();
++ ++it) {
++
++ foreign = *it;
++ /* Note that we may have an update which updates the index
++ record, but does NOT update the first fields which are
++ referenced in a foreign key constraint. Then the update does
++ NOT break the constraint. */
++
++ if (foreign->foreign_index == index
++ && (node->is_delete
++ || row_upd_changes_first_fields_binary(
++ entry, index, node->update,
++ foreign->n_fields))) {
++
++ if (foreign->referenced_table == NULL) {
++ foreign->referenced_table =
++ dict_table_open_on_name(
++ foreign->referenced_table_name_lookup,
++ FALSE, FALSE, DICT_ERR_IGNORE_NONE);
++ opened = TRUE;
++ }
++
++ if (foreign->referenced_table) {
++ os_inc_counter(dict_sys->mutex,
++ foreign->referenced_table
++ ->n_foreign_key_checks_running);
++ }
++
++ /* NOTE that if the thread ends up waiting for a lock
++ we will release dict_operation_lock temporarily!
++ But the counter on the table protects 'foreign' from
++ being dropped while the check is running. */
++
++ err = row_ins_check_foreign_constraint(
++ TRUE, foreign, table, entry, thr);
++
++ if (foreign->referenced_table) {
++ os_dec_counter(dict_sys->mutex,
++ foreign->referenced_table
++ ->n_foreign_key_checks_running);
++
++ if (opened == TRUE) {
++ dict_table_close(foreign->referenced_table, TRUE, FALSE);
++ opened = FALSE;
++ }
++ }
++
++ /* Some table foreign key dropped, try again */
++ if (err == DB_DICT_CHANGED) {
++ goto run_again;
++ } else if (err != DB_SUCCESS) {
++ goto func_exit;
++ }
++ }
++ }
++
++ err = DB_SUCCESS;
++func_exit:
++ if (got_s_lock) {
++ row_mysql_unfreeze_data_dictionary(trx);
++ }
++
++ mem_heap_free(heap);
++
++ return(err);
++}
++#endif /* WITH_WSREP */
+
+ /*********************************************************************//**
+ Creates an update node for a query graph.
+@@ -1667,6 +1826,9 @@ row_upd_sec_index_entry(
+ index = node->index;
+
+ referenced = row_upd_index_is_referenced(index, trx);
++#ifdef WITH_WSREP
++ ibool foreign = wsrep_row_upd_index_is_foreign(index, trx);
++#endif /* WITH_WSREP */
+
+ heap = mem_heap_create(1024);
+
+@@ -1794,6 +1956,9 @@ row_upd_sec_index_entry(
+ row_ins_sec_index_entry() below */
+ if (!rec_get_deleted_flag(
+ rec, dict_table_is_comp(index->table))) {
++#ifdef WITH_WSREP
++ que_node_t *parent = que_node_get_parent(node);
++#endif /* WITH_WSREP */
+ err = btr_cur_del_mark_set_sec_rec(
+ 0, btr_cur, TRUE, thr, &mtr);
+
+@@ -1811,6 +1976,37 @@ row_upd_sec_index_entry(
+ node, &pcur, index->table,
+ index, offsets, thr, &mtr);
+ }
++#ifdef WITH_WSREP
++ if (err == DB_SUCCESS && !referenced &&
++ !(parent && que_node_get_type(parent) ==
++ QUE_NODE_UPDATE &&
++ ((upd_node_t*)parent)->cascade_node == node) &&
++ foreign
++ ) {
++ ulint* offsets =
++ rec_get_offsets(
++ rec, index, NULL, ULINT_UNDEFINED,
++ &heap);
++ err = wsrep_row_upd_check_foreign_constraints(
++ node, &pcur, index->table,
++ index, offsets, thr, &mtr);
++ switch (err) {
++ case DB_SUCCESS:
++ case DB_NO_REFERENCED_ROW:
++ err = DB_SUCCESS;
++ break;
++ case DB_DEADLOCK:
++ if (wsrep_debug) fprintf (stderr,
++ "WSREP: sec index FK check fail for deadlock");
++ break;
++ default:
++ fprintf (stderr,
++ "WSREP: referenced FK check fail: %d",
++ (int)err);
++ break;
++ }
++ }
++#endif /* WITH_WSREP */
+ }
+ break;
+ }
+@@ -1965,6 +2161,9 @@ row_upd_clust_rec_by_insert(
+ que_thr_t* thr, /*!< in: query thread */
+ ibool referenced,/*!< in: TRUE if index may be referenced in
+ a foreign key constraint */
++#ifdef WITH_WSREP
++ ibool foreign, /*!< in: TRUE if index is foreign key index */
++#endif /* WITH_WSREP */
+ mtr_t* mtr) /*!< in/out: mtr; gets committed here */
+ {
+ mem_heap_t* heap;
+@@ -1978,6 +2177,9 @@ row_upd_clust_rec_by_insert(
+ rec_t* rec;
+ ulint* offsets = NULL;
+
++#ifdef WITH_WSREP
++ que_node_t *parent = que_node_get_parent(node);
++#endif /* WITH_WSREP */
+ ut_ad(node);
+ ut_ad(dict_index_is_clust(index));
+
+@@ -2060,6 +2262,34 @@ err_exit:
+ goto err_exit;
+ }
+ }
++#ifdef WITH_WSREP
++ if (!referenced &&
++ !(parent && que_node_get_type(parent) == QUE_NODE_UPDATE &&
++ ((upd_node_t*)parent)->cascade_node == node) &&
++ foreign
++ ) {
++ err = wsrep_row_upd_check_foreign_constraints(
++ node, pcur, table, index, offsets, thr, mtr);
++ switch (err) {
++ case DB_SUCCESS:
++ case DB_NO_REFERENCED_ROW:
++ err = DB_SUCCESS;
++ break;
++ case DB_DEADLOCK:
++ if (wsrep_debug) fprintf (stderr,
++ "WSREP: insert FK check fail for deadlock");
++ break;
++ default:
++ fprintf (stderr,
++ "WSREP: referenced FK check fail: %d",
++ (int)err);
++ break;
++ }
++ if (err != DB_SUCCESS) {
++ goto err_exit;
++ }
++ }
++#endif /* WITH_WSREP */
+ }
+
+ mtr_commit(mtr);
+@@ -2252,11 +2482,18 @@ row_upd_del_mark_clust_rec(
+ ibool referenced,
+ /*!< in: TRUE if index may be referenced in
+ a foreign key constraint */
++#ifdef WITH_WSREP
++ ibool foreign,/*!< in: TRUE if index is foreign key index */
++#endif /* WITH_WSREP */
+ mtr_t* mtr) /*!< in: mtr; gets committed here */
+ {
+ btr_pcur_t* pcur;
+ btr_cur_t* btr_cur;
+ dberr_t err;
++#ifdef WITH_WSREP
++ rec_t* rec;
++ que_node_t *parent = que_node_get_parent(node);
++#endif /* WITH_WSREP */
+
+ ut_ad(node);
+ ut_ad(dict_index_is_clust(index));
+@@ -2273,8 +2510,16 @@ row_upd_del_mark_clust_rec(
+ /* Mark the clustered index record deleted; we do not have to check
+ locks, because we assume that we have an x-lock on the record */
+
++#ifdef WITH_WSREP
++ rec = btr_cur_get_rec(btr_cur);
++#endif /* WITH_WSREP */
++
+ err = btr_cur_del_mark_set_clust_rec(
++#ifdef WITH_WSREP
++ btr_cur_get_block(btr_cur), rec,
++#else
+ btr_cur_get_block(btr_cur), btr_cur_get_rec(btr_cur),
++#endif /* WITH_WSREP */
+ index, offsets, thr, mtr);
+ if (err == DB_SUCCESS && referenced) {
+ /* NOTE that the following call loses the position of pcur ! */
+@@ -2282,6 +2527,32 @@ row_upd_del_mark_clust_rec(
+ err = row_upd_check_references_constraints(
+ node, pcur, index->table, index, offsets, thr, mtr);
+ }
++#ifdef WITH_WSREP
++ if (err == DB_SUCCESS && !referenced &&
++ !(parent && que_node_get_type(parent) == QUE_NODE_UPDATE &&
++ ((upd_node_t*)parent)->cascade_node == node) &&
++ thr_get_trx(thr) &&
++ foreign
++ ) {
++ err = wsrep_row_upd_check_foreign_constraints(
++ node, pcur, index->table, index, offsets, thr, mtr);
++ switch (err) {
++ case DB_SUCCESS:
++ case DB_NO_REFERENCED_ROW:
++ err = DB_SUCCESS;
++ break;
++ case DB_DEADLOCK:
++ if (wsrep_debug) fprintf (stderr,
++ "WSREP: clust rec FK check fail for deadlock");
++ break;
++ default:
++ fprintf (stderr,
++ "WSREP: clust rec referenced FK check fail: %d",
++ (int)err);
++ break;
++ }
++ }
++#endif /* WITH_WSREP */
+
+ mtr_commit(mtr);
+
+@@ -2314,6 +2585,10 @@ row_upd_clust_step(
+ index = dict_table_get_first_index(node->table);
+
+ referenced = row_upd_index_is_referenced(index, thr_get_trx(thr));
++#ifdef WITH_WSREP
++ ibool foreign = wsrep_row_upd_index_is_foreign(
++ index, thr_get_trx(thr));
++#endif /* WITH_WSREP */
+
+ pcur = node->pcur;
+
+@@ -2408,7 +2683,11 @@ row_upd_clust_step(
+
+ if (node->is_delete) {
+ err = row_upd_del_mark_clust_rec(
++#ifdef WITH_WSREP
++ node, index, offsets, thr, referenced, foreign, &mtr);
++#else
+ node, index, offsets, thr, referenced, &mtr);
++#endif /* WITH_WSREP */
+
+ if (err == DB_SUCCESS) {
+ node->state = UPD_NODE_UPDATE_ALL_SEC;
+@@ -2453,7 +2732,11 @@ row_upd_clust_step(
+ externally! */
+
+ err = row_upd_clust_rec_by_insert(
++#ifdef WITH_WSREP
++ node, index, thr, referenced, foreign, &mtr);
++#else
+ node, index, thr, referenced, &mtr);
++#endif /* WITH_WSREP */
+
+ if (err != DB_SUCCESS) {
+
+diff --git a/storage/innobase/srv/srv0conc.cc b/storage/innobase/srv/srv0conc.cc
+index dc3c0b1..fb7fad3 100644
+--- a/storage/innobase/srv/srv0conc.cc
++++ b/storage/innobase/srv/srv0conc.cc
+@@ -42,6 +42,10 @@ Created 2011/04/18 Sunny Bains
+ #include "trx0trx.h"
+
+ #include "mysql/plugin.h"
++#ifdef WITH_WSREP
++extern "C" int wsrep_trx_is_aborting(void *thd_ptr);
++extern my_bool wsrep_debug;
++#endif
+
+ /** Number of times a thread is allowed to enter InnoDB within the same
+ SQL query after it has once got the ticket. */
+@@ -86,6 +90,9 @@ struct srv_conc_slot_t{
+ reserved may still be TRUE at that
+ point */
+ srv_conc_node_t srv_conc_queue; /*!< queue node */
++#ifdef WITH_WSREP
++ void *thd; /*!< to see priority */
++#endif
+ };
+
+ /** Queue of threads waiting to get in */
+@@ -145,6 +152,9 @@ srv_conc_init(void)
+
+ conc_slot->event = os_event_create();
+ ut_a(conc_slot->event);
++#ifdef WITH_WSREP
++ conc_slot->thd = NULL;
++#endif /* WITH_WSREP */
+ }
+ #endif /* !HAVE_ATOMIC_BUILTINS */
+ }
+@@ -202,6 +212,16 @@ srv_conc_enter_innodb_with_atomics(
+
+ for (;;) {
+ ulint sleep_in_us;
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd) &&
++ wsrep_trx_is_aborting(trx->mysql_thd)) {
++ if (wsrep_debug)
++ fprintf(stderr,
++ "srv_conc_enter due to MUST_ABORT");
++ srv_conc_force_enter_innodb(trx);
++ return;
++ }
++#endif /* WITH_WSREP */
+
+ if (srv_conc.n_active < (lint) srv_thread_concurrency) {
+ ulint n_active;
+@@ -319,6 +339,9 @@ srv_conc_exit_innodb_without_atomics(
+ slot = NULL;
+
+ if (srv_conc.n_active < (lint) srv_thread_concurrency) {
++#ifdef WITH_WSREP
++ srv_conc_slot_t* wsrep_slot;
++#endif
+ /* Look for a slot where a thread is waiting and no other
+ thread has yet released the thread */
+
+@@ -329,6 +352,19 @@ srv_conc_exit_innodb_without_atomics(
+ /* No op */
+ }
+
++#ifdef WITH_WSREP
++ /* look for aborting trx, they must be released asap */
++ wsrep_slot= slot;
++ while (wsrep_slot && (wsrep_slot->wait_ended == TRUE ||
++ !wsrep_trx_is_aborting(wsrep_slot->thd))) {
++ wsrep_slot = UT_LIST_GET_NEXT(srv_conc_queue, wsrep_slot);
++ }
++ if (wsrep_slot) {
++ slot = wsrep_slot;
++ if (wsrep_debug)
++ fprintf(stderr, "WSREP: releasing aborting thd\n");
++ }
++#endif
+ if (slot != NULL) {
+ slot->wait_ended = TRUE;
+
+@@ -384,6 +420,13 @@ retry:
+
+ return;
+ }
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd) &&
++ wsrep_trx_is_aborting(trx->mysql_thd)) {
++ srv_conc_force_enter_innodb(trx);
++ return;
++ }
++#endif
+
+ /* If the transaction is not holding resources, let it sleep
+ for srv_thread_sleep_delay microseconds, and try again then */
+@@ -450,6 +493,9 @@ retry:
+ /* Add to the queue */
+ slot->reserved = TRUE;
+ slot->wait_ended = FALSE;
++#ifdef WITH_WSREP
++ slot->thd = trx->mysql_thd;
++#endif
+
+ UT_LIST_ADD_LAST(srv_conc_queue, srv_conc_queue, slot);
+
+@@ -457,6 +503,18 @@ retry:
+
+ srv_conc.n_waiting++;
+
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd) &&
++ wsrep_trx_is_aborting(trx->mysql_thd)) {
++ os_fast_mutex_unlock(&srv_conc_mutex);
++ if (wsrep_debug)
++ fprintf(stderr, "srv_conc_enter due to MUST_ABORT");
++ trx->declared_to_be_inside_innodb = TRUE;
++ trx->n_tickets_to_enter_innodb = srv_n_free_tickets_to_enter;
++ return;
++ }
++ trx->wsrep_event = slot->event;
++#endif /* WITH_WSREP */
+ os_fast_mutex_unlock(&srv_conc_mutex);
+
+ /* Go to wait for the event; when a thread leaves InnoDB it will
+@@ -471,6 +529,9 @@ retry:
+ thd_wait_begin(trx->mysql_thd, THD_WAIT_USER_LOCK);
+
+ os_event_wait(slot->event);
++#ifdef WITH_WSREP
++ trx->wsrep_event = NULL;
++#endif /* WITH_WSREP */
+ thd_wait_end(trx->mysql_thd);
+
+ trx->op_info = "";
+@@ -483,6 +544,9 @@ retry:
+ incremented the thread counter on behalf of this thread */
+
+ slot->reserved = FALSE;
++#ifdef WITH_WSREP
++ slot->thd = NULL;
++#endif
+
+ UT_LIST_REMOVE(srv_conc_queue, srv_conc_queue, slot);
+
+@@ -593,5 +657,32 @@ srv_conc_get_active_threads(void)
+ /*==============================*/
+ {
+ return(srv_conc.n_active);
+- }
++}
++
++#ifdef WITH_WSREP
++UNIV_INTERN
++void
++wsrep_srv_conc_cancel_wait(
++/*==================*/
++ trx_t* trx) /*!< in: transaction object associated with the
++ thread */
++{
++#ifdef HAVE_ATOMIC_BUILTINS
++ /* aborting transactions will enter innodb by force in
++ srv_conc_enter_innodb_with_atomics(). No need to cancel here,
++ thr will wake up after os_sleep and let to enter innodb
++ */
++ if (wsrep_debug)
++ fprintf(stderr, "WSREP: conc slot cancel, no atomics\n");
++#else
++ os_fast_mutex_lock(&srv_conc_mutex);
++ if (trx->wsrep_event) {
++ if (wsrep_debug)
++ fprintf(stderr, "WSREP: conc slot cancel\n");
++ os_event_set(trx->wsrep_event);
++ }
++ os_fast_mutex_unlock(&srv_conc_mutex);
++#endif
++}
++#endif /* WITH_WSREP */
+
+diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc
+index f4ea889..4404023 100644
+--- a/storage/innobase/srv/srv0srv.cc
++++ b/storage/innobase/srv/srv0srv.cc
+@@ -71,6 +71,10 @@ Created 10/8/1995 Heikki Tuuri
+ #include "mysql/plugin.h"
+ #include "mysql/service_thd_wait.h"
+
++#ifdef WITH_WSREP
++extern int wsrep_debug;
++extern int wsrep_trx_is_aborting(void *thd_ptr);
++#endif
+ /* The following is the maximum allowed duration of a lock wait. */
+ UNIV_INTERN ulint srv_fatal_semaphore_wait_threshold = 600;
+
+@@ -207,6 +211,10 @@ srv_printf_innodb_monitor() will request mutex acquisition
+ with mutex_enter(), which will wait until it gets the mutex. */
+ #define MUTEX_NOWAIT(mutex_skipped) ((mutex_skipped) < MAX_MUTEX_NOWAIT)
+
++#ifdef WITH_INNODB_DISALLOW_WRITES
++UNIV_INTERN os_event_t srv_allow_writes_event;
++#endif /* WITH_INNODB_DISALLOW_WRITES */
++
+ /** The sort order table of the MySQL latin1_swedish_ci character set
+ collation */
+ UNIV_INTERN const byte* srv_latin1_ordering;
+@@ -975,6 +983,14 @@ srv_init(void)
+
+ srv_conc_init();
+
++#ifdef WITH_INNODB_DISALLOW_WRITES
++ /* Writes have to be enabled on init or else we hang. Thus, we
++ always set the event here regardless of innobase_disallow_writes.
++ That flag will always be 0 at this point because it isn't settable
++ via my.cnf or command line arg. */
++ srv_allow_writes_event = os_event_create();
++ os_event_set(srv_allow_writes_event);
++#endif /* WITH_INNODB_DISALLOW_WRITES */
+ /* Initialize some INFORMATION SCHEMA internal structures */
+ trx_i_s_cache_init(trx_i_s_cache);
+
+@@ -1737,7 +1753,20 @@ loop:
+
+ if (sync_array_print_long_waits(&waiter, &sema)
+ && sema == old_sema && os_thread_eq(waiter, old_waiter)) {
++#if defined(WITH_WSREP) && defined(WITH_INNODB_DISALLOW_WRITES)
++ if (srv_allow_writes_event->is_set) {
++#endif /* WITH_WSREP */
+ fatal_cnt++;
++#if defined(WITH_WSREP) && defined(WITH_INNODB_DISALLOW_WRITES)
++ } else {
++ fprintf(stderr,
++ "WSREP: avoiding InnoDB self crash due to long "
++ "semaphore wait of > %lu seconds\n"
++ "Server is processing SST donor operation, "
++ "fatal_cnt now: %lu",
++ (ulong) srv_fatal_semaphore_wait_threshold, fatal_cnt);
++ }
++#endif /* WITH_WSREP */
+ if (fatal_cnt > 10) {
+
+ fprintf(stderr,
+diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc
+index bc11f1d..ae9095d 100644
+--- a/storage/innobase/trx/trx0roll.cc
++++ b/storage/innobase/trx/trx0roll.cc
+@@ -43,6 +43,9 @@ Created 3/26/1996 Heikki Tuuri
+ #include "row0mysql.h"
+ #include "lock0lock.h"
+ #include "pars0pars.h"
++#ifdef WITH_WSREP
++#include "ha_prototypes.h"
++#endif /* WITH_WSREP */
+ #include "srv0mon.h"
+ #include "trx0sys.h"
+
+@@ -374,6 +377,11 @@ trx_rollback_to_savepoint_for_mysql_low(
+
+ trx->op_info = "";
+
++#ifdef WITH_WSREP_OUT
++ if (wsrep_on(trx->mysql_thd)) {
++ trx->lock.was_chosen_as_deadlock_victim = FALSE;
++ }
++#endif /* WITH_WSREP */
+ return(err);
+ }
+
+@@ -1014,6 +1022,11 @@ trx_roll_try_truncate(
+ if (trx->update_undo) {
+ trx_undo_truncate_end(trx, trx->update_undo, limit);
+ }
++#ifdef WITH_WSREP_OUT
++ if (wsrep_on(trx->mysql_thd)) {
++ trx->lock.was_chosen_as_deadlock_victim = FALSE;
++ }
++#endif /* WITH_WSREP */
+ }
+
+ /***********************************************************************//**
+diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc
+index 52830a7..816de1c 100644
+--- a/storage/innobase/trx/trx0sys.cc
++++ b/storage/innobase/trx/trx0sys.cc
+@@ -44,6 +44,10 @@ Created 3/26/1996 Heikki Tuuri
+ #include "os0file.h"
+ #include "read0read.h"
+
++#ifdef WITH_WSREP
++#include "ha_prototypes.h" /* wsrep_is_wsrep_xid() */
++#endif /* */
++
+ /** The file format tag structure with id and name. */
+ struct file_format_t {
+ ulint id; /*!< id of the file format */
+@@ -174,7 +178,12 @@ trx_sys_flush_max_trx_id(void)
+ mtr_t mtr;
+ trx_sysf_t* sys_header;
+
++#ifndef WITH_WSREP
++ /* wsrep_fake_trx_id violates this assert
++ * Copied from trx_sys_get_new_trx_id
++ */
+ ut_ad(mutex_own(&trx_sys->mutex));
++#endif /* WITH_WSREP */
+
+ if (!srv_read_only_mode) {
+ mtr_start(&mtr);
+@@ -202,10 +211,14 @@ trx_sys_update_mysql_binlog_offset(
+ ib_int64_t offset, /*!< in: position in that log file */
+ ulint field, /*!< in: offset of the MySQL log info field in
+ the trx sys header */
++#ifdef WITH_WSREP
++ trx_sysf_t* sys_header, /*!< in: trx sys header */
++#endif /* WITH_WSREP */
+ mtr_t* mtr) /*!< in: mtr */
+ {
++#ifndef WITH_WSREP
+ trx_sysf_t* sys_header;
+-
++#endif /* !WITH_WSREP */
+ if (ut_strlen(file_name) >= TRX_SYS_MYSQL_LOG_NAME_LEN) {
+
+ /* We cannot fit the name to the 512 bytes we have reserved */
+@@ -213,7 +226,9 @@ trx_sys_update_mysql_binlog_offset(
+ return;
+ }
+
++#ifndef WITH_WSREP
+ sys_header = trx_sysf_get(mtr);
++#endif /* !WITH_WSREP */
+
+ if (mach_read_from_4(sys_header + field
+ + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
+@@ -300,6 +315,125 @@ trx_sys_print_mysql_binlog_offset(void)
+ mtr_commit(&mtr);
+ }
+
++#ifdef WITH_WSREP
++
++#ifdef UNIV_DEBUG
++static long long trx_sys_cur_xid_seqno = -1;
++static unsigned char trx_sys_cur_xid_uuid[16];
++
++long long read_wsrep_xid_seqno(const XID* xid)
++{
++ long long seqno;
++ memcpy(&seqno, xid->data + 24, sizeof(long long));
++ return seqno;
++}
++
++void read_wsrep_xid_uuid(const XID* xid, unsigned char* buf)
++{
++ memcpy(buf, xid->data + 8, 16);
++}
++
++#endif /* UNIV_DEBUG */
++
++void
++trx_sys_update_wsrep_checkpoint(
++ const XID* xid, /*!< in: transaction XID */
++ trx_sysf_t* sys_header, /*!< in: sys_header */
++ mtr_t* mtr) /*!< in: mtr */
++{
++
++#ifdef UNIV_DEBUG
++ {
++ /* Check that seqno is monotonically increasing */
++ unsigned char xid_uuid[16];
++ long long xid_seqno = read_wsrep_xid_seqno(xid);
++ read_wsrep_xid_uuid(xid, xid_uuid);
++ if (!memcmp(xid_uuid, trx_sys_cur_xid_uuid, 8))
++ {
++ ut_ad(xid_seqno > trx_sys_cur_xid_seqno);
++ trx_sys_cur_xid_seqno = xid_seqno;
++ }
++ else
++ {
++ memcpy(trx_sys_cur_xid_uuid, xid_uuid, 16);
++ }
++ trx_sys_cur_xid_seqno = xid_seqno;
++ }
++#endif /* UNIV_DEBUG */
++
++ ut_ad(xid && mtr && sys_header);
++ ut_a(xid->formatID == -1 || wsrep_is_wsrep_xid(xid));
++
++ if (mach_read_from_4(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_MAGIC_N_FLD)
++ != TRX_SYS_WSREP_XID_MAGIC_N) {
++ mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_MAGIC_N_FLD,
++ TRX_SYS_WSREP_XID_MAGIC_N,
++ MLOG_4BYTES, mtr);
++ }
++
++ mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_FORMAT,
++ (int)xid->formatID,
++ MLOG_4BYTES, mtr);
++ mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_GTRID_LEN,
++ (int)xid->gtrid_length,
++ MLOG_4BYTES, mtr);
++ mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_BQUAL_LEN,
++ (int)xid->bqual_length,
++ MLOG_4BYTES, mtr);
++ mlog_write_string(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_DATA,
++ (const unsigned char*) xid->data,
++ XIDDATASIZE, mtr);
++
++}
++
++void
++trx_sys_read_wsrep_checkpoint(XID* xid)
++/*===================================*/
++{
++ trx_sysf_t* sys_header;
++ mtr_t mtr;
++ ulint magic;
++
++ ut_ad(xid);
++
++ mtr_start(&mtr);
++
++ sys_header = trx_sysf_get(&mtr);
++
++ if ((magic = mach_read_from_4(sys_header + TRX_SYS_WSREP_XID_INFO
++ + TRX_SYS_WSREP_XID_MAGIC_N_FLD))
++ != TRX_SYS_WSREP_XID_MAGIC_N) {
++ memset(xid, 0, sizeof(*xid));
++ xid->formatID = -1;
++ trx_sys_update_wsrep_checkpoint(xid, sys_header, &mtr);
++ mtr_commit(&mtr);
++ return;
++ }
++
++ xid->formatID = (int)mach_read_from_4(
++ sys_header
++ + TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_FORMAT);
++ xid->gtrid_length = (int)mach_read_from_4(
++ sys_header
++ + TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_GTRID_LEN);
++ xid->bqual_length = (int)mach_read_from_4(
++ sys_header
++ + TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_BQUAL_LEN);
++ ut_memcpy(xid->data,
++ sys_header + TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_DATA,
++ XIDDATASIZE);
++
++ mtr_commit(&mtr);
++}
++
++#endif /* WITH_WSREP */
++
+ /*****************************************************************//**
+ Prints to stderr the MySQL master log offset info in the trx system header if
+ the magic number shows it valid. */
+diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
+index c2d5c1f..4c530dd 100644
+--- a/storage/innobase/trx/trx0trx.cc
++++ b/storage/innobase/trx/trx0trx.cc
+@@ -158,7 +158,10 @@ trx_create(void)
+ trx->lock.table_locks = ib_vector_create(
+ heap_alloc, sizeof(void**), 32);
+
+- return(trx);
++#ifdef WITH_WSREP
++ trx->wsrep_event = NULL;
++#endif /* WITH_WSREP */
++ return(trx);
+ }
+
+ /********************************************************************//**
+@@ -853,6 +856,11 @@ trx_start_low(
+ srv_undo_logs, srv_undo_tablespaces);
+ }
+
++#ifdef WITH_WSREP
++ memset(&trx->xid, 0, sizeof(trx->xid));
++ trx->xid.formatID = -1;
++#endif /* WITH_WSREP */
++
+ /* The initial value for trx->no: TRX_ID_MAX is used in
+ read_view_open_now: */
+
+@@ -967,6 +975,9 @@ trx_write_serialisation_history(
+ trx_t* trx, /*!< in/out: transaction */
+ mtr_t* mtr) /*!< in/out: mini-transaction */
+ {
++#ifdef WITH_WSREP
++ trx_sysf_t* sys_header;
++#endif /* WITH_WSREP */
+ trx_rseg_t* rseg;
+
+ rseg = trx->rseg;
+@@ -1013,6 +1024,15 @@ trx_write_serialisation_history(
+
+ MONITOR_INC(MONITOR_TRX_COMMIT_UNDO);
+
++#ifdef WITH_WSREP
++ sys_header = trx_sysf_get(mtr);
++ /* Update latest MySQL wsrep XID in trx sys header. */
++ if (wsrep_is_wsrep_xid(&trx->xid))
++ {
++ trx_sys_update_wsrep_checkpoint(&trx->xid, sys_header, mtr);
++ }
++#endif /* WITH_WSREP */
++
+ /* Update the latest MySQL binlog name and offset info
+ in trx sys header if MySQL binlogging is on or the database
+ server is a MySQL replication slave */
+@@ -1023,7 +1043,11 @@ trx_write_serialisation_history(
+ trx_sys_update_mysql_binlog_offset(
+ trx->mysql_log_file_name,
+ trx->mysql_log_offset,
++#ifdef WITH_WSREP
++ TRX_SYS_MYSQL_LOG_INFO, sys_header, mtr);
++#else
+ TRX_SYS_MYSQL_LOG_INFO, mtr);
++#endif /* WITH_WSREP */
+
+ trx->mysql_log_file_name = NULL;
+ }
+@@ -1318,6 +1342,11 @@ trx_commit_in_memory(
+ ut_ad(!trx->in_ro_trx_list);
+ ut_ad(!trx->in_rw_trx_list);
+
++#ifdef WITH_WSREP
++ if (wsrep_on(trx->mysql_thd)) {
++ trx->lock.was_chosen_as_deadlock_victim = FALSE;
++ }
++#endif
+ trx->dict_operation = TRX_DICT_OP_NONE;
+
+ trx->error_state = DB_SUCCESS;
+@@ -1502,6 +1531,10 @@ trx_commit_or_rollback_prepare(
+
+ switch (trx->state) {
+ case TRX_STATE_NOT_STARTED:
++#ifdef WITH_WSREP
++ ut_d(trx->start_file = __FILE__);
++ ut_d(trx->start_line = __LINE__);
++#endif /* WITH_WSREP */
+ trx_start_low(trx);
+ /* fall through */
+ case TRX_STATE_ACTIVE:
+@@ -2229,6 +2262,10 @@ trx_start_if_not_started_xa_low(
+ transaction, doesn't. */
+ trx->support_xa = thd_supports_xa(trx->mysql_thd);
+
++#ifdef WITH_WSREP
++ ut_d(trx->start_file = __FILE__);
++ ut_d(trx->start_line = __LINE__);
++#endif /* WITH_WSREP */
+ trx_start_low(trx);
+ /* fall through */
+ case TRX_STATE_ACTIVE:
+@@ -2251,6 +2288,10 @@ trx_start_if_not_started_low(
+ {
+ switch (trx->state) {
+ case TRX_STATE_NOT_STARTED:
++#ifdef WITH_WSREP
++ ut_d(trx->start_file = __FILE__);
++ ut_d(trx->start_line = __LINE__);
++#endif /* WITH_WSREP */
+ trx_start_low(trx);
+ /* fall through */
+ case TRX_STATE_ACTIVE:
+@@ -2284,6 +2325,10 @@ trx_start_for_ddl_low(
+ trx->will_lock = 1;
+
+ trx->ddl = true;
++#ifdef WITH_WSREP
++ ut_d(trx->start_file = __FILE__);
++ ut_d(trx->start_line = __LINE__);
++#endif /* WITH_WSREP */
+
+ trx_start_low(trx);
+ return;
+diff --git a/support-files/CMakeLists.txt b/support-files/CMakeLists.txt
+index 5afe261..74b1075 100644
+--- a/support-files/CMakeLists.txt
++++ b/support-files/CMakeLists.txt
+@@ -93,4 +93,12 @@ IF(UNIX)
+ DESTINATION ${inst_location} COMPONENT SupportFiles
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
+ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
++ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/wsrep_notify.sh
++ ${CMAKE_CURRENT_BINARY_DIR}/wsrep_notify @ONLY)
++ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/wsrep_notify
++ DESTINATION ${inst_location} COMPONENT SupportFiles
++ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
++ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
++ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/wsrep.cnf.sh
++ ${CMAKE_CURRENT_BINARY_DIR}/wsrep.cnf @ONLY)
+ ENDIF()
+diff --git a/support-files/build-tags b/support-files/build-tags
+index c37485e..635940a 100755
+--- a/support-files/build-tags
++++ b/support-files/build-tags
+@@ -10,3 +10,7 @@ $list |grep $filter |while read f;
+ do
+ etags -o TAGS --append $f
+ done
++(cd storage/galera && svn ls -R) | grep $filter | while read f;
++do
++ etags -o TAGS --append storage/galera/$f
++done
+diff --git a/support-files/cmake-no-wix.patch b/support-files/cmake-no-wix.patch
+new file mode 100644
+index 0000000..325d97e
+--- /dev/null
++++ b/support-files/cmake-no-wix.patch
+@@ -0,0 +1,14 @@
++diff --git a/CMakeLists.txt b/CMakeLists.txt
++index 9e235ba..15c3414 100644
++--- a/CMakeLists.txt
+++++ b/CMakeLists.txt
++@@ -569,7 +569,8 @@ IF(WIN32)
++ ELSE()
++ SET(CPACK_GENERATOR "TGZ")
++ ENDIF()
++-ADD_SUBDIRECTORY(packaging/WiX)
+++# Trouble on SLES 11 due to old cmake ...
+++# ADD_SUBDIRECTORY(packaging/WiX)
++ ADD_SUBDIRECTORY(packaging/solaris)
++
++ # Create a single package with "make package"
+diff --git a/support-files/mysql-rpmlintrc b/support-files/mysql-rpmlintrc
+new file mode 100644
+index 0000000..72dd6e3
+--- /dev/null
++++ b/support-files/mysql-rpmlintrc
+@@ -0,0 +1,64 @@
++# allow the traditional name "mysql-shared" even with a so-versioned lib
++setBadness('shlib-policy-name-error', 0)
++
++# Suppress several "false positive" messages which we cannot prevent:
++# Oracle text we must not change:
++addFilter("MySQL.* invalid-license")
++addFilter("mysql.* invalid-license")
++addFilter("MySQL.* name-repeated-in-summary")
++addFilter("mysql.* name-repeated-in-summary")
++addFilter("MySQL.* no-version-in-last-changelog")
++addFilter("mysql.* no-version-in-last-changelog")
++addFilter("MySQL.*src.* invalid-spec-name")
++addFilter("mysql.*src.* invalid-spec-name")
++# Oracle code we must not change:
++addFilter("MySQL.*shared.* shared-lib-calls-exit /usr/lib64/libmysqlclient")
++addFilter("mysql.*shared.* shared-lib-calls-exit /usr/lib64/libmysqlclient")
++addFilter("MySQL.*server.* binary-or-shlib-calls-gethostbyname /usr/bin/resolveip")
++addFilter("mysql.*server.* binary-or-shlib-calls-gethostbyname /usr/bin/resolveip")
++# "obsolete" the Oracle commercial RPMs:
++addFilter("MySQL.* obsolete-not-provided mysql[^ ]*advanced")
++addFilter("mysql.* obsolete-not-provided mysql[^ ]*advanced")
++addFilter("MySQL.* obsolete-not-provided MySQL[^ ]*advanced")
++addFilter("mysql.* obsolete-not-provided MySQL[^ ]*advanced")
++addFilter("MySQL.* obsolete-not-provided MySQL[^ ]*classic")
++addFilter("mysql.* obsolete-not-provided MySQL[^ ]*classic")
++addFilter("MySQL.* obsolete-not-provided MySQL[^ ]*community")
++addFilter("mysql.* obsolete-not-provided MySQL[^ ]*community")
++addFilter("MySQL.* obsolete-not-provided MySQL[^ ]*enterprise")
++addFilter("mysql.* obsolete-not-provided MySQL[^ ]*enterprise")
++addFilter("MySQL.* obsolete-not-provided MySQL[^ ]*pro")
++addFilter("mysql.* obsolete-not-provided MySQL[^ ]*pro")
++addFilter("MySQL.* obsolete-not-provided MySQL[^ ]*standard")
++addFilter("mysql.* obsolete-not-provided MySQL[^ ]*standard")
++# ... and the packages we dropped on intention:
++addFilter("MySQL.*devel.* obsolete-not-provided MySQL-embedded-devel")
++addFilter("mysql.*devel.* obsolete-not-provided MySQL-embedded-devel")
++addFilter("MySQL.*devel.* obsolete-not-provided mysql-embedded-devel")
++addFilter("mysql.*devel.* obsolete-not-provided mysql-embedded-devel")
++addFilter("MySQL.*test.* obsolete-not-provided MySQL-bench")
++addFilter("mysql.*test.* obsolete-not-provided MySQL-bench")
++addFilter("MySQL.*test.* obsolete-not-provided mysql-bench")
++addFilter("mysql.*test.* obsolete-not-provided mysql-bench")
++# Oracle does not strip binaries, and neither will we:
++addFilter("MySQL.* unstripped-binary-or-object /usr/s?bin/.*")
++addFilter("mysql.* unstripped-binary-or-object /usr/s?bin/.*")
++addFilter("MySQL.* unstripped-binary-or-object /usr/lib64/.*")
++addFilter("mysql.* unstripped-binary-or-object /usr/lib64/.*")
++addFilter("MySQL.*test.* unstripped-binary-or-object /usr/share/mysql-test/lib/.*")
++addFilter("mysql.*test.* unstripped-binary-or-object /usr/share/mysql-test/lib/.*")
++# Pre-defined certificates are needed for SSL tests, also a binary:
++addFilter("MySQL.*test.* pem-certificate")
++addFilter("mysql.*test.* pem-certificate")
++addFilter("MySQL.*test.* arch-dependent-file-in-usr-share /usr/share/mysql-test/lib/My/SafeProcess/my_safe_process")
++addFilter("mysql.*test.* arch-dependent-file-in-usr-share /usr/share/mysql-test/lib/My/SafeProcess/my_safe_process")
++# Oracle doesn't have a "check" section (yet):
++addFilter("MySQL.* make-check-outside-check-section")
++addFilter("mysql.* make-check-outside-check-section")
++# Documentation issues:
++addFilter("MySQL.*server.* no-manual-page-for-binary mysqld-debug")
++addFilter("mysql.*server.* no-manual-page-for-binary mysqld-debug")
++# File naming:
++addFilter("MySQL.*server.* incoherent-init-script-name mysql")
++addFilter("mysql.*server.* incoherent-init-script-name mysql")
++# End of the "false positive" messages which we cannot prevent.
+diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh
+index 7487d5a..5c3f2f9 100644
+--- a/support-files/mysql.server.sh
++++ b/support-files/mysql.server.sh
+@@ -24,6 +24,11 @@
+ # Short-Description: start and stop MySQL
+ # Description: MySQL is a very fast and reliable SQL database engine.
+ ### END INIT INFO
++
++# Prevent OpenSUSE's init scripts from calling systemd, so that
++# both 'bootstrap' and 'start' are handled entirely within this script
++
++SYSTEMD_NO_WRAP=1
+
+ # If you install MySQL on some other places than @prefix@, then you
+ # have to do one of the following things for this script to work:
+@@ -329,7 +334,10 @@ case "$mode" in
+ # Stop the service and regardless of whether it was
+ # running or not, start it again.
+ if $0 stop $other_args; then
+- $0 start $other_args
++ if ! $0 start $other_args; then
++ log_failure_msg "Failed to restart server."
++ exit 1
++ fi
+ else
+ log_failure_msg "Failed to stop running server, so refusing to try to start."
+ exit 1
+@@ -379,6 +387,12 @@ case "$mode" in
+ fi
+ fi
+ ;;
++ 'bootstrap')
++ # Bootstrap the cluster, start the first node
++ # that initiate the cluster
++ echo $echo_n "Bootstrapping the cluster"
++ $0 start $other_args --wsrep-new-cluster
++ ;;
+ *)
+ # usage
+ basename=`basename "$0"`
+diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh
+index c72ca39..9e8ce67 100644
+--- a/support-files/mysql.spec.sh
++++ b/support-files/mysql.spec.sh
+@@ -25,13 +25,15 @@
+ %global mysql_vendor Oracle and/or its affiliates
+
+ %global mysql_version @VERSION@
++%global wsrep_version @WSREP_VERSION@
++%global wsrep_revision @WSREP_REVISION@
+
+ %global mysqld_user mysql
+ %global mysqld_group mysql
+ %global mysqldatadir /var/lib/mysql
+
+-%global release 1
+-
++%global release %{wsrep_version}
++%global short_product_tag 5.6
+
+ #
+ # Macros we use which are not available in all supported versions of RPM
+@@ -79,9 +81,9 @@
+ # Source name
+ # ----------------------------------------------------------------------------
+ %if %{undefined src_base}
+-%define src_base mysql
++%define src_base mysql-wsrep
+ %endif
+-%define src_dir %{src_base}-%{mysql_version}
++%define src_dir %{src_base}-%{mysql_version}-%{wsrep_version}
+
+ # ----------------------------------------------------------------------------
+ # Feature set (storage engines, options). Default to community (everything)
+@@ -118,95 +120,48 @@
+ # ----------------------------------------------------------------------------
+ # Distribution support
+ # ----------------------------------------------------------------------------
+-%if %{undefined distro_specific}
+-%define distro_specific 0
++
++# Disable post build checks for time being.
++BuildConflicts: post-build-checks
++
++BuildRequires: gcc-c++ ncurses-devel perl zlib-devel cmake libaio-devel bison flex
++
++%if 0%{?rhel} == 6 || 0%{?rhel} == 7 || 0%{?fedora} == 20 || 0%{?fedora} == 21
++BuildRequires: time
+ %endif
+-%if %{distro_specific}
+- %if %(test -f /etc/enterprise-release && echo 1 || echo 0)
+- %define oelver %(rpm -qf --qf '%%{version}\\n' /etc/enterprise-release | sed -e 's/^\\([0-9]*\\).*/\\1/g')
+- %if "%oelver" == "4"
+- %define distro_description Oracle Enterprise Linux 4
+- %define distro_releasetag oel4
+- %define distro_buildreq gcc-c++ gperf ncurses-devel perl time zlib-devel cmake libaio-devel
+- %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools
+- %else
+- %if "%oelver" == "5"
+- %define distro_description Oracle Enterprise Linux 5
+- %define distro_releasetag oel5
+- %define distro_buildreq gcc-c++ gperf ncurses-devel perl time zlib-devel cmake libaio-devel
+- %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools
+- %else
+- %{error:Oracle Enterprise Linux %{oelver} is unsupported}
+- %endif
+- %endif
+- %else
+- %if %(test -f /etc/oracle-release && echo 1 || echo 0)
+- %define elver %(rpm -qf --qf '%%{version}\\n' /etc/oracle-release | sed -e 's/^\\([0-9]*\\).*/\\1/g')
+- %if "%elver" == "6" || "%elver" == "7"
+- %define distro_description Oracle Linux %elver
+- %define distro_releasetag el%elver
+- %define distro_buildreq gcc-c++ ncurses-devel perl time zlib-devel cmake libaio-devel
+- %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools
+- %else
+- %{error:Oracle Linux %{elver} is unsupported}
+- %endif
+- %else
+- %if %(test -f /etc/redhat-release && echo 1 || echo 0)
+- %define rhelver %(rpm -qf --qf '%%{version}\\n' /etc/redhat-release | sed -e 's/^\\([0-9]*\\).*/\\1/g')
+- %if "%rhelver" == "4"
+- %define distro_description Red Hat Enterprise Linux 4
+- %define distro_releasetag rhel4
+- %define distro_buildreq gcc-c++ gperf ncurses-devel perl time zlib-devel cmake libaio-devel
+- %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools
+- %else
+- %if "%rhelver" == "5"
+- %define distro_description Red Hat Enterprise Linux 5
+- %define distro_releasetag rhel5
+- %define distro_buildreq gcc-c++ gperf ncurses-devel perl time zlib-devel cmake libaio-devel
+- %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools
+- %else
+- %if "%rhelver" == "6"
+- %define distro_description Red Hat Enterprise Linux 6
+- %define distro_releasetag rhel6
+- %define distro_buildreq gcc-c++ ncurses-devel perl time zlib-devel cmake libaio-devel
+- %define distro_requires chkconfig coreutils grep procps shadow-utils net-tools
+- %else
+- %{error:Red Hat Enterprise Linux %{rhelver} is unsupported}
+- %endif
+- %endif
+- %endif
+- %else
+- %if %(test -f /etc/SuSE-release && echo 1 || echo 0)
+- %define susever %(rpm -qf --qf '%%{version}\\n' /etc/SuSE-release | cut -d. -f1)
+- %if "%susever" == "10"
+- %define distro_description SUSE Linux Enterprise Server 10
+- %define distro_releasetag sles10
+- %define distro_buildreq gcc-c++ gdbm-devel gperf ncurses-devel openldap2-client zlib-devel cmake libaio-devel
+- %define distro_requires aaa_base coreutils grep procps pwdutils
+- %else
+- %if "%susever" == "11"
+- %define distro_description SUSE Linux Enterprise Server 11
+- %define distro_releasetag sles11
+- %define distro_buildreq gcc-c++ gdbm-devel gperf ncurses-devel openldap2-client procps pwdutils zlib-devel cmake libaio-devel
+- %define distro_requires aaa_base coreutils grep procps pwdutils
+- %else
+- %{error:SuSE %{susever} is unsupported}
+- %endif
+- %endif
+- %else
+- %{error:Unsupported distribution}
+- %endif
+- %endif
+- %endif
++
++%if 0%{?suse_version}
++%if 0%{?suse_version} == 1110
++BuildRequires: gdbm-devel gperf openldap2-client procps pwdutils
++%endif
++%if 0%{?suse_version} == 1310 || 0%{?suse_version} == 1315 || 0%{?suse_version} == 1320
++BuildRequires: gperf procps time
++%endif
++%endif
++
++# Define dist tag if not given by platform
++%if %{undefined dist}
++ # For suse versions see:
++ # https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto
++ %if 0%{?suse_version} == 1110
++ %define dist .sle11
++ %endif
++ %if 0%{?suse_version} == 1310
++ %define dist .suse13.1
++ %endif
++ %if 0%{?suse_version} == 1315
++ %define dist .sle12
++ %endif
++ %if 0%{?suse_version} == 1320
++ %define dist .suse13.2
++ %endif
++ # Still missing?
++ %if %{undefined dist}
++ %define dist .DIST
+ %endif
+-%else
+- %define glibc_version %(/lib/libc.so.6 | grep stable | cut -d, -f1 | cut -c38-)
+- %define distro_description Generic Linux (glibc %{glibc_version})
+- %define distro_releasetag linux_glibc%{glibc_version}
+- %define distro_buildreq gcc-c++ gperf ncurses-devel perl time zlib-devel
+- %define distro_requires coreutils grep procps /sbin/chkconfig /usr/sbin/useradd /usr/sbin/groupadd
+ %endif
+
++
+ # Avoid debuginfo RPMs, leaves binaries unstripped
+ %define debug_package %{nil}
+
+@@ -231,7 +186,7 @@
+ %define license_files_server %{src_dir}/LICENSE.mysql
+ %define license_type Commercial
+ %else
+-%define license_files_server %{src_dir}/COPYING %{src_dir}/README
++%define license_files_server COPYING README
+ %define license_type GPL
+ %endif
+
+@@ -243,17 +198,20 @@ Name: MySQL%{product_suffix}
+ Summary: MySQL: a very fast and reliable SQL database server
+ Group: Applications/Databases
+ Version: @MYSQL_RPM_VERSION@
+-Release: %{release}%{?distro_releasetag:.%{distro_releasetag}}
+-Distribution: %{distro_description}
++Release: %{release}%{dist}
++# Distribution: %{distro_description}
+ License: Copyright (c) 2000, @MYSQL_COPYRIGHT_YEAR@, %{mysql_vendor}. All rights reserved. Under %{license_type} license as shown in the Description field.
+-Source: http://www.mysql.com/Downloads/MySQL-@MYSQL_BASE_VERSION@/%{src_dir}.tar.gz
++Source: %{src_dir}.tar.gz
++Source99: mysql-rpmlintrc
++Patch0: cmake-no-wix.patch
+ URL: http://www.mysql.com/
+-Packager: MySQL Release Engineering <mysql-build@oss.oracle.com>
++Packager: Codership Oy <info@galeracluster.com>
+ Vendor: %{mysql_vendor}
+-BuildRequires: %{distro_buildreq}
++# BuildRequires: %{distro_buildreq}
++#wsrep_patch_tag
+
+ # Regression tests may take a long time, override the default to skip them
+-%{!?runselftest:%global runselftest 1}
++%{!?runselftest:%global runselftest 0}
+
+ # Think about what you use here since the first step is to
+ # run a rm -rf
+@@ -282,10 +240,41 @@ documentation and the manual for more information.
+ # Sub package definition
+ ##############################################################################
+
+-%package -n MySQL-server%{product_suffix}
++%package -n mysql-wsrep%{product_suffix}
++Summary: MySQL: meta package for a server+client setup
++Group: Applications/Databases
++Requires: mysql-wsrep-server%{product_suffix}
++Requires: mysql-wsrep-client%{product_suffix}
++
++%description -n mysql-wsrep%{product_suffix}
++This meta package ensures the installation of a MySQL server and the necessary
++client programs for operation and administration. It does not itself
++contain those files but rather causes the installation of the subpackages
++"mysql-wsrep-server%{product_suffix}" and "mysql-wsrep-client%{product_suffix}".
++As indicated in the name, the server is built with the "wsrep" plugin so that
++it can be a node in a MySQL Galera Cluster.
++
++# ----------------------------------------------------------------------------
++
++%package -n mysql-wsrep-server%{product_suffix}
+ Summary: MySQL: a very fast and reliable SQL database server
+ Group: Applications/Databases
+-Requires: %{distro_requires}
++# Distro requirements
++# RedHat
++%if 0%{?fedora} || 0%{?rhel}
++Requires: chkconfig coreutils grep procps shadow-utils net-tools rsync lsof
++%if 0%{?rhel} == 7 || 0%{?fedora} >= 20
++Requires: perl-Data-Dumper
++%endif
++%endif
++# SUSE
++%if 0%{?suse_version}
++Requires: aaa_base coreutils grep procps rsync lsof
++%if 0%{suse_version} == 1110
++Requires: pwdutils
++%endif
++%endif
++
+ %if 0%{?commercial}
+ Obsoletes: MySQL-server
+ %else
+@@ -298,7 +287,7 @@ Obsoletes: MySQL-server-advanced-gpl MySQL-server-enterprise-gpl
+ Provides: mysql-server = %{version}-%{release}
+ Provides: mysql-server%{?_isa} = %{version}-%{release}
+
+-%description -n MySQL-server%{product_suffix}
++%description -n mysql-wsrep-server%{product_suffix}
+ The MySQL(TM) software delivers a very fast, multi-threaded, multi-user,
+ and robust SQL (Structured Query Language) database server. MySQL Server
+ is intended for mission-critical, heavy-load production systems as well
+@@ -319,11 +308,13 @@ and the manual for more information.
+ This package includes the MySQL server binary as well as related utilities
+ to run and administer a MySQL server.
+
++Built with wsrep patch %{wsrep_version}.
++
+ If you want to access and work with the database, you have to install
+-package "MySQL-client%{product_suffix}" as well!
++package "mysql-wsrep-client%{product_suffix}" as well!
+
+ # ----------------------------------------------------------------------------
+-%package -n MySQL-client%{product_suffix}
++%package -n mysql-wsrep-client%{product_suffix}
+ Summary: MySQL - Client
+ Group: Applications/Databases
+ %if 0%{?commercial}
+@@ -338,13 +329,13 @@ Obsoletes: MySQL-client-advanced-gpl MySQL-client-enterprise-gpl
+ Provides: mysql = %{version}-%{release}
+ Provides: mysql%{?_isa} = %{version}-%{release}
+
+-%description -n MySQL-client%{product_suffix}
++%description -n mysql-wsrep-client%{product_suffix}
+ This package contains the standard MySQL clients and administration tools.
+
+ For a description of MySQL see the base MySQL RPM or http://www.mysql.com/
+
+ # ----------------------------------------------------------------------------
+-%package -n MySQL-test%{product_suffix}
++%package -n mysql-wsrep-test%{product_suffix}
+ Summary: MySQL - Test suite
+ Group: Applications/Databases
+ %if 0%{?commercial}
+@@ -362,13 +353,13 @@ Provides: mysql-test = %{version}-%{release}
+ Provides: mysql-test%{?_isa} = %{version}-%{release}
+ AutoReqProv: no
+
+-%description -n MySQL-test%{product_suffix}
++%description -n mysql-wsrep-test%{product_suffix}
+ This package contains the MySQL regression test suite.
+
+ For a description of MySQL see the base MySQL RPM or http://www.mysql.com/
+
+ # ----------------------------------------------------------------------------
+-%package -n MySQL-devel%{product_suffix}
++%package -n mysql-wsrep-devel%{product_suffix}
+ Summary: MySQL - Development header files and libraries
+ Group: Applications/Databases
+ %if 0%{?commercial}
+@@ -383,14 +374,14 @@ Obsoletes: MySQL-devel-advanced-gpl MySQL-devel-enterprise-gpl
+ Provides: mysql-devel = %{version}-%{release}
+ Provides: mysql-devel%{?_isa} = %{version}-%{release}
+
+-%description -n MySQL-devel%{product_suffix}
++%description -n mysql-wsrep-devel%{product_suffix}
+ This package contains the development header files and libraries necessary
+ to develop MySQL client applications.
+
+ For a description of MySQL see the base MySQL RPM or http://www.mysql.com/
+
+ # ----------------------------------------------------------------------------
+-%package -n MySQL-shared%{product_suffix}
++%package -n mysql-wsrep-shared%{product_suffix}
+ Summary: MySQL - Shared libraries
+ Group: Applications/Databases
+ %if 0%{?commercial}
+@@ -404,44 +395,17 @@ Obsoletes: MySQL-shared-pro-gpl-cert
+ Obsoletes: MySQL-shared-classic MySQL-shared-community MySQL-shared-enterprise
+ Obsoletes: MySQL-shared-advanced-gpl MySQL-shared-enterprise-gpl
+
+-%description -n MySQL-shared%{product_suffix}
++%description -n mysql-wsrep-shared%{product_suffix}
+ This package contains the shared libraries (*.so*) which certain languages
+ and applications need to dynamically load and use MySQL.
+
+-# ----------------------------------------------------------------------------
+-%package -n MySQL-embedded%{product_suffix}
+-Summary: MySQL - Embedded library
+-Group: Applications/Databases
+-%if 0%{?commercial}
+-Requires: MySQL-devel-advanced
+-Obsoletes: MySQL-embedded
+-%else
+-Requires: MySQL-devel
+-Obsoletes: MySQL-embedded-advanced
+-%endif
+-Obsoletes: mysql-embedded < %{version}-%{release}
+-Obsoletes: mysql-embedded-advanced
+-Obsoletes: MySQL-embedded-pro
+-Obsoletes: MySQL-embedded-classic MySQL-embedded-community MySQL-embedded-enterprise
+-Obsoletes: MySQL-embedded-advanced-gpl MySQL-embedded-enterprise-gpl
+-Provides: mysql-embedded = %{version}-%{release}
+-Provides: mysql-emdedded%{?_isa} = %{version}-%{release}
+-
+-%description -n MySQL-embedded%{product_suffix}
+-This package contains the MySQL server as an embedded library.
+-
+-The embedded MySQL server library makes it possible to run a full-featured
+-MySQL server inside the client application. The main benefits are increased
+-speed and more simple management for embedded applications.
+-
+-The API is identical for the embedded MySQL version and the
+-client/server version.
+-
+-For a description of MySQL see the base MySQL RPM or http://www.mysql.com/
+-
+ ##############################################################################
+ %prep
+-%setup -T -a 0 -c -n %{src_dir}
++%setup -n %{src_dir}
++# That patch is needed with old cmake only, on SLES 11, but it won't do any harm
++# outside Windows, so it may be used in all RPM builds.
++%patch0 -p1
++#wsrep_apply_patch_tag
+ ##############################################################################
+ %build
+
+@@ -481,7 +445,7 @@ export CFLAGS=${MYSQL_BUILD_CFLAGS:-${CFLAGS:-$RPM_OPT_FLAGS}}
+ export CXXFLAGS=${MYSQL_BUILD_CXXFLAGS:-${CXXFLAGS:-$RPM_OPT_FLAGS -felide-constructors}}
+ export LDFLAGS=${MYSQL_BUILD_LDFLAGS:-${LDFLAGS:-}}
+ export CMAKE=${MYSQL_BUILD_CMAKE:-${CMAKE:-cmake}}
+-export MAKE_JFLAG=${MYSQL_BUILD_MAKE_JFLAG:-}
++export MAKE_JFLAG=${MYSQL_BUILD_MAKE_JFLAG:--j$(ncpu=$(cat /proc/cpuinfo | grep processor | wc -l) && echo $(($ncpu > 4 ? 4 : $ncpu)))}
+
+ # By default, a build will include the bundeled "yaSSL" library for SSL.
+ # However, there may be a need to override.
+@@ -500,6 +464,7 @@ mkdir debug
+ CFLAGS=`echo " ${CFLAGS} " | \
+ sed -e 's/ -O[0-9]* / /' \
+ -e 's/-Wp,-D_FORTIFY_SOURCE=2/ /' \
++ -e 's/-D_FORTIFY_SOURCE=2/ /' \
+ -e 's/ -unroll2 / /' \
+ -e 's/ -ip / /' \
+ -e 's/^ //' \
+@@ -507,19 +472,23 @@ mkdir debug
+ CXXFLAGS=`echo " ${CXXFLAGS} " | \
+ sed -e 's/ -O[0-9]* / /' \
+ -e 's/-Wp,-D_FORTIFY_SOURCE=2/ /' \
++ -e 's/-D_FORTIFY_SOURCE=2/ /' \
+ -e 's/ -unroll2 / /' \
+ -e 's/ -ip / /' \
+ -e 's/^ //' \
+ -e 's/ $//'`
+ # XXX: MYSQL_UNIX_ADDR should be in cmake/* but mysql_version is included before
+ # XXX: install_layout so we can't just set it based on INSTALL_LAYOUT=RPM
+- ${CMAKE} ../%{src_dir} -DBUILD_CONFIG=mysql_release -DINSTALL_LAYOUT=RPM \
++ ${CMAKE} ../ -DBUILD_CONFIG=mysql_release -DINSTALL_LAYOUT=RPM \
+ -DCMAKE_BUILD_TYPE=Debug \
+ -DMYSQL_UNIX_ADDR="%{mysqldatadir}/mysql.sock" \
+ -DFEATURE_SET="%{feature_set}" \
+ %{ssl_option} \
+ -DCOMPILATION_COMMENT="%{compilation_comment_debug}" \
+- -DMYSQL_SERVER_SUFFIX="%{server_suffix}"
++ -DMYSQL_SERVER_SUFFIX="%{server_suffix}" \
++ -DWITH_WSREP=1 \
++ -DWSREP_VERSION="%{wsrep_version}" \
++ -DWSREP_REVISION="%{wsrep_revision}"
+ echo BEGIN_DEBUG_CONFIG ; egrep '^#define' include/config.h ; echo END_DEBUG_CONFIG
+ make ${MAKE_JFLAG} VERBOSE=1
+ )
+@@ -529,13 +498,16 @@ mkdir release
+ cd release
+ # XXX: MYSQL_UNIX_ADDR should be in cmake/* but mysql_version is included before
+ # XXX: install_layout so we can't just set it based on INSTALL_LAYOUT=RPM
+- ${CMAKE} ../%{src_dir} -DBUILD_CONFIG=mysql_release -DINSTALL_LAYOUT=RPM \
++ ${CMAKE} ../ -DBUILD_CONFIG=mysql_release -DINSTALL_LAYOUT=RPM \
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+ -DMYSQL_UNIX_ADDR="%{mysqldatadir}/mysql.sock" \
+ -DFEATURE_SET="%{feature_set}" \
+ %{ssl_option} \
+ -DCOMPILATION_COMMENT="%{compilation_comment_release}" \
+- -DMYSQL_SERVER_SUFFIX="%{server_suffix}"
++ -DMYSQL_SERVER_SUFFIX="%{server_suffix}" \
++ -DWITH_WSREP=1 \
++ -DWSREP_VERSION="%{wsrep_version}" \
++ -DWSREP_REVISION="%{wsrep_revision}"
+ echo BEGIN_NORMAL_CONFIG ; egrep '^#define' include/config.h ; echo END_NORMAL_CONFIG
+ make ${MAKE_JFLAG} VERBOSE=1
+ )
+@@ -582,14 +554,20 @@ install -m 755 $MBD/release/support-files/mysql.server $RBR%{_sysconfdir}/init.d
+
+ # Create a symlink "rcmysql", pointing to the init.script. SuSE users
+ # will appreciate that, as all services usually offer this.
+-ln -s %{_sysconfdir}/init.d/mysql $RBR%{_sbindir}/rcmysql
++ln -sf %{_sysconfdir}/init.d/mysql $RBR%{_sbindir}/rcmysql
++
++# Create a wsrep_sst_rsync_wan symlink.
++install -d $RBR%{_bindir}
++ln -sf wsrep_sst_rsync $RBR%{_bindir}/wsrep_sst_rsync_wan
+
+ # Touch the place where the my.cnf config file might be located
+ # Just to make sure it's in the file list and marked as a config file
+ touch $RBR%{_sysconfdir}/my.cnf
++touch $RBR%{_sysconfdir}/wsrep.cnf
++
+
+ # Install SELinux files in datadir
+-install -m 600 $MBD/%{src_dir}/support-files/RHEL4-SElinux/mysql.{fc,te} \
++install -m 600 $MBD/support-files/RHEL4-SElinux/mysql.{fc,te} \
+ $RBR%{_datadir}/mysql/SELinux/RHEL4
+
+ %if %{WITH_TCMALLOC}
+@@ -608,7 +586,7 @@ install -m 644 "%{malloc_lib_source}" \
+ # Post processing actions, i.e. when installed
+ ##############################################################################
+
+-%pre -n MySQL-server%{product_suffix}
++%pre -n mysql-wsrep-server%{product_suffix}
+ # This is the code running at the beginning of a RPM upgrade action,
+ # before replacing the old files with the new ones.
+
+@@ -786,7 +764,7 @@ if [ -x %{_sysconfdir}/init.d/mysql ] ; then
+ sleep 5
+ fi
+
+-%post -n MySQL-server%{product_suffix}
++%post -n mysql-wsrep-server%{product_suffix}
+ # This is the code running at the end of a RPM install or upgrade action,
+ # after the (new) files have been written.
+
+@@ -946,7 +924,7 @@ mv -f $STATUS_FILE ${STATUS_FILE}-LAST # for "triggerpostun"
+ #scheduled service packs and more. Visit www.mysql.com/enterprise for more
+ #information."
+
+-%preun -n MySQL-server%{product_suffix}
++%preun -n mysql-wsrep-server%{product_suffix}
+
+ # Which '$1' does this refer to? Fedora docs have info:
+ # " ... a count of the number of versions of the package that are installed.
+@@ -975,7 +953,7 @@ fi
+ # We do not remove the mysql user since it may still own a lot of
+ # database files.
+
+-%triggerpostun -n MySQL-server%{product_suffix} --MySQL-server-community
++%triggerpostun -n mysql-wsrep-server%{product_suffix} --MySQL-server-community
+
+ # Setup: We renamed this package, so any existing "server-community"
+ # package will be removed when this "server" is installed.
+@@ -1046,15 +1024,22 @@ echo "=====" >> $STATUS_HISTORY
+ # Files section
+ ##############################################################################
+
+-%files -n MySQL-server%{product_suffix} -f release/support-files/plugins.files
++%files -n mysql-wsrep%{product_suffix}
++# Intentionally empty - this is a pure meta package.
++
++# ----------------------------------------------------------------------------
++%files -n mysql-wsrep-server%{product_suffix} -f release/support-files/plugins.files
+ %defattr(-,root,root,0755)
+ %if %{defined license_files_server}
+ %doc %{license_files_server}
+ %endif
+-%doc %{src_dir}/Docs/ChangeLog
+-%doc %{src_dir}/Docs/INFO_SRC*
++%doc Docs/ChangeLog
++%doc release/Docs/INFO_SRC*
+ %doc release/Docs/INFO_BIN*
+ %doc release/support-files/my-default.cnf
++%doc Docs/README-wsrep
++%doc release/support-files/wsrep.cnf
++%doc release/support-files/wsrep_notify
+
+ %if 0%{?commercial}
+ %doc %attr(644, root, root) %{_infodir}/mysql.info*
+@@ -1090,6 +1075,7 @@ echo "=====" >> $STATUS_HISTORY
+ %doc %attr(644, root, man) %{_mandir}/man1/resolveip.1*
+
+ %ghost %config(noreplace,missingok) %{_sysconfdir}/my.cnf
++%ghost %config(noreplace,missingok) %{_sysconfdir}/wsrep.cnf
+ %dir %{_sysconfdir}/my.cnf.d
+
+ %attr(755, root, root) %{_bindir}/innochecksum
+@@ -1117,10 +1103,18 @@ echo "=====" >> $STATUS_HISTORY
+ %attr(755, root, root) %{_bindir}/replace
+ %attr(755, root, root) %{_bindir}/resolve_stack_dump
+ %attr(755, root, root) %{_bindir}/resolveip
++%attr(755, root, root) %{_bindir}/wsrep_sst_common
++%attr(755, root, root) %{_bindir}/wsrep_sst_mysqldump
++%attr(755, root, root) %{_bindir}/wsrep_sst_rsync
++%attr(755, root, root) %{_bindir}/wsrep_sst_rsync_wan
++%attr(755, root, root) %{_bindir}/wsrep_sst_xtrabackup
++%attr(755, root, root) %{_bindir}/wsrep_sst_xtrabackup-v2
+
+ %attr(755, root, root) %{_sbindir}/mysqld
+ %attr(755, root, root) %{_sbindir}/mysqld-debug
+ %attr(755, root, root) %{_sbindir}/rcmysql
++%dir %{_libdir}/mysql/plugin
++%dir %{_libdir}/mysql/plugin/debug
+ %attr(755, root, root) %{_libdir}/mysql/plugin/daemon_example.ini
+
+ %if %{WITH_TCMALLOC}
+@@ -1133,7 +1127,8 @@ echo "=====" >> $STATUS_HISTORY
+ %dir %attr(755, mysql, mysql) /var/lib/mysql
+
+ # ----------------------------------------------------------------------------
+-%files -n MySQL-client%{product_suffix}
++%files -n mysql-wsrep-client%{product_suffix}
++
+ %defattr(-, root, root, 0755)
+ %if %{defined license_files_server}
+ %doc %{license_files_server}
+@@ -1169,7 +1164,7 @@ echo "=====" >> $STATUS_HISTORY
+ %doc %attr(644, root, man) %{_mandir}/man1/mysql_config_editor.1*
+
+ # ----------------------------------------------------------------------------
+-%files -n MySQL-devel%{product_suffix} -f optional-files-devel
++%files -n mysql-wsrep-devel%{product_suffix} -f optional-files-devel
+ %defattr(-, root, root, 0755)
+ %if %{defined license_files_server}
+ %doc %{license_files_server}
+@@ -1186,7 +1181,7 @@ echo "=====" >> $STATUS_HISTORY
+ %{_libdir}/mysql/libmysqlservices.a
+
+ # ----------------------------------------------------------------------------
+-%files -n MySQL-shared%{product_suffix}
++%files -n mysql-wsrep-shared%{product_suffix}
+ %defattr(-, root, root, 0755)
+ %if %{defined license_files_server}
+ %doc %{license_files_server}
+@@ -1194,44 +1189,47 @@ echo "=====" >> $STATUS_HISTORY
+ # Shared libraries (omit for architectures that don't support them)
+ %{_libdir}/libmysql*.so*
+
+-%post -n MySQL-shared%{product_suffix}
++%post -n mysql-wsrep-shared%{product_suffix}
+ /sbin/ldconfig
+
+-%postun -n MySQL-shared%{product_suffix}
++%postun -n mysql-wsrep-shared%{product_suffix}
+ /sbin/ldconfig
+
+ # ----------------------------------------------------------------------------
+-%files -n MySQL-test%{product_suffix}
++%files -n mysql-wsrep-test%{product_suffix}
+ %defattr(-, root, root, 0755)
+ %if %{defined license_files_server}
+ %doc %{license_files_server}
+ %endif
+ %attr(-, root, root) %{_datadir}/mysql-test
+ %attr(755, root, root) %{_bindir}/mysql_client_test
+-%attr(755, root, root) %{_bindir}/mysql_client_test_embedded
+-%attr(755, root, root) %{_bindir}/mysqltest_embedded
+ %doc %attr(644, root, man) %{_mandir}/man1/mysql_client_test.1*
+ %doc %attr(644, root, man) %{_mandir}/man1/mysql-stress-test.pl.1*
+ %doc %attr(644, root, man) %{_mandir}/man1/mysql-test-run.pl.1*
+ %doc %attr(644, root, man) %{_mandir}/man1/mysql_client_test_embedded.1*
+ %doc %attr(644, root, man) %{_mandir}/man1/mysqltest_embedded.1*
+
+-# ----------------------------------------------------------------------------
+-%files -n MySQL-embedded%{product_suffix}
+-%defattr(-, root, root, 0755)
+-%if %{defined license_files_server}
+-%doc %{license_files_server}
+-%endif
+-%attr(755, root, root) %{_bindir}/mysql_embedded
+-%attr(644, root, root) %{_libdir}/mysql/libmysqld.a
+-%attr(644, root, root) %{_libdir}/mysql/libmysqld-debug.a
+-
+ ##############################################################################
+ # The spec file changelog only includes changes made to the spec file
+ # itself - note that they must be ordered by date (important when
+ # merging BK trees)
+ ##############################################################################
+ %changelog
++* Thu Jan 29 2015 Joerg Bruehe <joerg.bruehe@fromdual.com>
++- Add a meta-package "mysql-wsrep" that requires both "server" and "client".
++- Fix the fall-back definition of "dist", it must start with a period.
++
++* Mon Jan 26 2015 Joerg Bruehe <joerg.bruehe@fromdual.com>
++- Allow "rpmlint", but suppress "post-build-checks" (fail on SuSE 12 + 13).
++- Improve handling of undefined "%%{dist}".
++- Fix wrong changelog dates, to get rid of warnings about "bogus date".
++- Escape percent signs in changelog, to get rid of "rpmlint" warnings.
++
++* Tue Jan 20 2015 Teemu Ollakka <teemu.ollakka@galeracluster.com>
++
++- Reworked to build wsrep patched packages exclusively
++- OBS compatible
++
+ * Mon Oct 06 2014 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com>
+ - Add license info in each subpackage
+
+@@ -1350,7 +1348,7 @@ echo "=====" >> $STATUS_HISTORY
+ not in an RPM upgrade.
+ This affects both the "mkdir" and the call of "mysql_install_db".
+
+-* Wed Feb 09 2011 Joerg Bruehe <joerg.bruehe@oracle.com>
++* Thu Feb 10 2011 Joerg Bruehe <joerg.bruehe@oracle.com>
+
+ - Fix bug#56581: If an installation deviates from the default file locations
+ ("datadir" and "pid-file"), the mechanism to detect a running server (on upgrade)
+@@ -1471,7 +1469,7 @@ echo "=====" >> $STATUS_HISTORY
+ - Fix some problems with the directives around "tcmalloc" (experimental),
+ remove erroneous traces of the InnoDB plugin (that is 5.1 only).
+
+-* Tue Oct 06 2009 Magnus Blaudd <mvensson@mysql.com>
++* Fri Oct 09 2009 Magnus Blaudd <mvensson@mysql.com>
+
+ - Removed mysql_fix_privilege_tables
+
+@@ -1668,7 +1666,7 @@ echo "=====" >> $STATUS_HISTORY
+
+ - Set $LDFLAGS from $MYSQL_BUILD_LDFLAGS
+
+-* Tue Mar 07 2006 Kent Boortz <kent@mysql.com>
++* Wed Mar 08 2006 Kent Boortz <kent@mysql.com>
+
+ - Changed product name from "Community Edition" to "Community Server"
+
+@@ -1763,7 +1761,7 @@ echo "=====" >> $STATUS_HISTORY
+
+ * Thu Sep 29 2005 Lenz Grimmer <lenz@mysql.com>
+
+-- fixed the removing of the RPM_BUILD_ROOT in the %clean section (the
++- fixed the removing of the RPM_BUILD_ROOT in the %%clean section (the
+ $RBR variable did not get expanded, thus leaving old build roots behind)
+
+ * Thu Aug 04 2005 Lenz Grimmer <lenz@mysql.com>
+@@ -1916,7 +1914,7 @@ echo "=====" >> $STATUS_HISTORY
+
+ - marked /etc/logrotate.d/mysql as a config file (BUG 2156)
+
+-* Sat Dec 13 2003 Lenz Grimmer <lenz@mysql.com>
++* Fri Dec 12 2003 Lenz Grimmer <lenz@mysql.com>
+
+ - fixed file permissions (BUG 1672)
+
+@@ -2058,7 +2056,7 @@ echo "=====" >> $STATUS_HISTORY
+ - Added separate libmysql_r directory; now both a threaded
+ and non-threaded library is shipped.
+
+-* Tue Sep 28 1999 David Axmark <davida@mysql.com>
++* Wed Sep 29 1999 David Axmark <davida@mysql.com>
+
+ - Added the support-files/my-example.cnf to the docs directory.
+
+diff --git a/support-files/wsrep.cnf b/support-files/wsrep.cnf
+new file mode 100644
+index 0000000..756d4f6
+--- /dev/null
++++ b/support-files/wsrep.cnf
+@@ -0,0 +1,129 @@
++# This file contains wsrep-related mysqld options. It should be included
++# in the main MySQL configuration file.
++#
++# Options that need to be customized:
++# - wsrep_provider
++# - wsrep_cluster_address
++# - wsrep_sst_auth
++# The rest of defaults should work out of the box.
++
++##
++## mysqld options _MANDATORY_ for correct opration of the cluster
++##
++[mysqld]
++
++# (This must be substituted by wsrep_format)
++binlog_format=ROW
++
++# Currently only InnoDB storage engine is supported
++default-storage-engine=innodb
++
++# to avoid issues with 'bulk mode inserts' using autoinc
++innodb_autoinc_lock_mode=2
++
++# This is a must for paralell applying
++innodb_locks_unsafe_for_binlog=1
++
++# Query Cache is not supported with wsrep
++query_cache_size=0
++query_cache_type=0
++
++# Override bind-address
++# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST
++# it will have (most likely) disastrous consequences on donor node
++bind-address=0.0.0.0
++
++##
++## WSREP options
++##
++
++# Full path to wsrep provider library or 'none'
++wsrep_provider=none
++
++# Provider specific configuration options
++#wsrep_provider_options=
++
++# Logical cluster name. Should be the same for all nodes.
++wsrep_cluster_name="my_wsrep_cluster"
++
++# Group communication system handle
++#wsrep_cluster_address="dummy://"
++
++# Human-readable node name (non-unique). Hostname by default.
++#wsrep_node_name=
++
++# Base replication <address|hostname>[:port] of the node.
++# The values supplied will be used as defaults for state transfer receiving,
++# listening ports and so on. Default: address of the first network interface.
++#wsrep_node_address=
++
++# Address for incoming client connections. Autodetect by default.
++#wsrep_node_incoming_address=
++
++# How many threads will process writesets from other nodes
++wsrep_slave_threads=1
++
++# DBUG options for wsrep provider
++#wsrep_dbug_option
++
++# Generate fake primary keys for non-PK tables (required for multi-master
++# and parallel applying operation)
++wsrep_certify_nonPK=1
++
++# Maximum number of rows in write set
++wsrep_max_ws_rows=131072
++
++# Maximum size of write set
++wsrep_max_ws_size=1073741824
++
++# to enable debug level logging, set this to 1
++wsrep_debug=0
++
++# convert locking sessions into transactions
++wsrep_convert_LOCK_to_trx=0
++
++# how many times to retry deadlocked autocommits
++wsrep_retry_autocommit=1
++
++# change auto_increment_increment and auto_increment_offset automatically
++wsrep_auto_increment_control=1
++
++# retry autoinc insert, which failed for duplicate key error
++wsrep_drupal_282555_workaround=0
++
++# enable "strictly synchronous" semantics for read operations
++wsrep_causal_reads=0
++
++# Command to call when node status or cluster membership changes.
++# Will be passed all or some of the following options:
++# --status - new status of this node
++# --uuid - UUID of the cluster
++# --primary - whether the component is primary or not ("yes"/"no")
++# --members - comma-separated list of members
++# --index - index of this node in the list
++wsrep_notify_cmd=
++
++##
++## WSREP State Transfer options
++##
++
++# State Snapshot Transfer method
++wsrep_sst_method=rsync
++
++# Address which donor should send State Snapshot to.
++# Should be the address of THIS node. DON'T SET IT TO DONOR ADDRESS!!!
++# (SST method dependent. Defaults to the first IP of the first interface)
++#wsrep_sst_receive_address=
++
++# SST authentication string. This will be used to send SST to joining nodes.
++# Depends on SST method. For mysqldump method it is root:<root password>
++wsrep_sst_auth=root:
++
++# Desired SST donor name.
++#wsrep_sst_donor=
++
++# Reject client queries when donating SST (false)
++#wsrep_sst_donor_rejects_queries=0
++
++# Protocol version to use
++# wsrep_protocol_version=
+diff --git a/support-files/wsrep.cnf.sh b/support-files/wsrep.cnf.sh
+new file mode 100644
+index 0000000..756d4f6
+--- /dev/null
++++ b/support-files/wsrep.cnf.sh
+@@ -0,0 +1,129 @@
++# This file contains wsrep-related mysqld options. It should be included
++# in the main MySQL configuration file.
++#
++# Options that need to be customized:
++# - wsrep_provider
++# - wsrep_cluster_address
++# - wsrep_sst_auth
++# The rest of defaults should work out of the box.
++
++##
++## mysqld options _MANDATORY_ for correct opration of the cluster
++##
++[mysqld]
++
++# (This must be substituted by wsrep_format)
++binlog_format=ROW
++
++# Currently only InnoDB storage engine is supported
++default-storage-engine=innodb
++
++# to avoid issues with 'bulk mode inserts' using autoinc
++innodb_autoinc_lock_mode=2
++
++# This is a must for paralell applying
++innodb_locks_unsafe_for_binlog=1
++
++# Query Cache is not supported with wsrep
++query_cache_size=0
++query_cache_type=0
++
++# Override bind-address
++# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST
++# it will have (most likely) disastrous consequences on donor node
++bind-address=0.0.0.0
++
++##
++## WSREP options
++##
++
++# Full path to wsrep provider library or 'none'
++wsrep_provider=none
++
++# Provider specific configuration options
++#wsrep_provider_options=
++
++# Logical cluster name. Should be the same for all nodes.
++wsrep_cluster_name="my_wsrep_cluster"
++
++# Group communication system handle
++#wsrep_cluster_address="dummy://"
++
++# Human-readable node name (non-unique). Hostname by default.
++#wsrep_node_name=
++
++# Base replication <address|hostname>[:port] of the node.
++# The values supplied will be used as defaults for state transfer receiving,
++# listening ports and so on. Default: address of the first network interface.
++#wsrep_node_address=
++
++# Address for incoming client connections. Autodetect by default.
++#wsrep_node_incoming_address=
++
++# How many threads will process writesets from other nodes
++wsrep_slave_threads=1
++
++# DBUG options for wsrep provider
++#wsrep_dbug_option
++
++# Generate fake primary keys for non-PK tables (required for multi-master
++# and parallel applying operation)
++wsrep_certify_nonPK=1
++
++# Maximum number of rows in write set
++wsrep_max_ws_rows=131072
++
++# Maximum size of write set
++wsrep_max_ws_size=1073741824
++
++# to enable debug level logging, set this to 1
++wsrep_debug=0
++
++# convert locking sessions into transactions
++wsrep_convert_LOCK_to_trx=0
++
++# how many times to retry deadlocked autocommits
++wsrep_retry_autocommit=1
++
++# change auto_increment_increment and auto_increment_offset automatically
++wsrep_auto_increment_control=1
++
++# retry autoinc insert, which failed for duplicate key error
++wsrep_drupal_282555_workaround=0
++
++# enable "strictly synchronous" semantics for read operations
++wsrep_causal_reads=0
++
++# Command to call when node status or cluster membership changes.
++# Will be passed all or some of the following options:
++# --status - new status of this node
++# --uuid - UUID of the cluster
++# --primary - whether the component is primary or not ("yes"/"no")
++# --members - comma-separated list of members
++# --index - index of this node in the list
++wsrep_notify_cmd=
++
++##
++## WSREP State Transfer options
++##
++
++# State Snapshot Transfer method
++wsrep_sst_method=rsync
++
++# Address which donor should send State Snapshot to.
++# Should be the address of THIS node. DON'T SET IT TO DONOR ADDRESS!!!
++# (SST method dependent. Defaults to the first IP of the first interface)
++#wsrep_sst_receive_address=
++
++# SST authentication string. This will be used to send SST to joining nodes.
++# Depends on SST method. For mysqldump method it is root:<root password>
++wsrep_sst_auth=root:
++
++# Desired SST donor name.
++#wsrep_sst_donor=
++
++# Reject client queries when donating SST (false)
++#wsrep_sst_donor_rejects_queries=0
++
++# Protocol version to use
++# wsrep_protocol_version=
+diff --git a/support-files/wsrep_notify.sh b/support-files/wsrep_notify.sh
+new file mode 100644
+index 0000000..bdbe3d1
+--- /dev/null
++++ b/support-files/wsrep_notify.sh
+@@ -0,0 +1,102 @@
++#!/bin/sh -eu
++
++# This is a simple example of wsrep notification script (wsrep_notify_cmd).
++# It will create 'wsrep' schema and two tables in it: 'membeship' and 'status'
++# and fill them on every membership or node status change.
++#
++# Edit parameters below to specify the address and login to server.
++
++USER=root
++PSWD=rootpass
++HOST=127.0.0.1
++PORT=3306
++
++SCHEMA="wsrep"
++MEMB_TABLE="$SCHEMA.membership"
++STATUS_TABLE="$SCHEMA.status"
++
++BEGIN="
++SET wsrep_on=0;
++DROP SCHEMA IF EXISTS $SCHEMA; CREATE SCHEMA $SCHEMA;
++CREATE TABLE $MEMB_TABLE (
++ idx INT UNIQUE PRIMARY KEY,
++ uuid CHAR(40) UNIQUE, /* node UUID */
++ name VARCHAR(32), /* node name */
++ addr VARCHAR(256) /* node address */
++) ENGINE=MEMORY;
++CREATE TABLE $STATUS_TABLE (
++ size INT, /* component size */
++ idx INT, /* this node index */
++ status CHAR(16), /* this node status */
++ uuid CHAR(40), /* cluster UUID */
++ prim BOOLEAN /* if component is primary */
++) ENGINE=MEMORY;
++BEGIN;
++DELETE FROM $MEMB_TABLE;
++DELETE FROM $STATUS_TABLE;
++"
++END="COMMIT;"
++
++configuration_change()
++{
++ echo "$BEGIN;"
++
++ local idx=0
++
++ for NODE in $(echo $MEMBERS | sed s/,/\ /g)
++ do
++ echo "INSERT INTO $MEMB_TABLE VALUES ( $idx, "
++ # Don't forget to properly quote string values
++ echo "'$NODE'" | sed s/\\//\',\'/g
++ echo ");"
++ idx=$(( $idx + 1 ))
++ done
++
++ echo "INSERT INTO $STATUS_TABLE VALUES($idx, $INDEX, '$STATUS', '$CLUSTER_UUID', $PRIMARY);"
++
++ echo "$END"
++}
++
++status_update()
++{
++ echo "SET wsrep_on=0; BEGIN; UPDATE $STATUS_TABLE SET status='$STATUS'; COMMIT;"
++}
++
++COM=status_update # not a configuration change by default
++
++while [ $# -gt 0 ]
++do
++ case $1 in
++ --status)
++ STATUS=$2
++ shift
++ ;;
++ --uuid)
++ CLUSTER_UUID=$2
++ shift
++ ;;
++ --primary)
++ [ "$2" = "yes" ] && PRIMARY="1" || PRIMARY="0"
++ COM=configuration_change
++ shift
++ ;;
++ --index)
++ INDEX=$2
++ shift
++ ;;
++ --members)
++ MEMBERS=$2
++ shift
++ ;;
++ esac
++ shift
++done
++
++# Undefined means node is shutting down
++if [ "$STATUS" != "Undefined" ]
++then
++ $COM | mysql -B -u$USER -p$PSWD -h$HOST -P$PORT
++fi
++
++exit 0
++#
+diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c
+index 4d5ff71..07e05e4 100644
+--- a/vio/viosslfactories.c
++++ b/vio/viosslfactories.c
+@@ -1,4 +1,4 @@
+-/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
++/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+diff --git a/wsrep/CMakeLists.txt b/wsrep/CMakeLists.txt
+new file mode 100644
+index 0000000..182e458
+--- /dev/null
++++ b/wsrep/CMakeLists.txt
+@@ -0,0 +1,24 @@
++# Copyright (c) 2012, Codership Oy. All rights reserved.
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 of the License.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++
++INCLUDE_DIRECTORIES( "." )
++
++SET(WSREP_SOURCES wsrep_gtid.c wsrep_uuid.c wsrep_loader.c wsrep_dummy.c)
++
++ADD_CONVENIENCE_LIBRARY(wsrep ${WSREP_SOURCES})
++DTRACE_INSTRUMENT(wsrep)
++
++#ADD_EXECUTABLE(listener wsrep_listener.c ${WSREP_SOURCES})
++#TARGET_LINK_LIBRARIES(listener ${LIBDL})
+diff --git a/wsrep/Makefile.am b/wsrep/Makefile.am
+new file mode 100644
+index 0000000..a748ece
+--- /dev/null
++++ b/wsrep/Makefile.am
+@@ -0,0 +1,7 @@
++noinst_LIBRARIES = libwsrep.a
++libwsrep_a_SOURCES = wsrep_api.h wsrep_loader.c wsrep_dummy.c wsrep_uuid.c wsrep_gtid.c
++noinst_PROGRAMS = wsrep_listener
++wsrep_listener_SOURCES = wsrep_listener.c
++wsrep_listener_LDADD = $(noinst_LIBRARIES)
++wsrep_listener_LDFLAGS = -static -ldl
++
+diff --git a/wsrep/wsrep_api.h b/wsrep/wsrep_api.h
+new file mode 100644
+index 0000000..c3304d7
+--- /dev/null
++++ b/wsrep/wsrep_api.h
+@@ -0,0 +1,1118 @@
++/* Copyright (C) 2009-2013 Codership Oy <info@codership.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++/*!
++ @file wsrep API declaration.
++
++ HOW TO READ THIS FILE.
++
++ Due to C language rules this header layout doesn't lend itself to intuitive
++ reading. So here's the scoop: in the end this header declares two main types:
++
++ * struct wsrep_init_args
++
++ and
++
++ * struct wsrep
++
++ wsrep_init_args contains initialization parameters for wsrep provider like
++ names, addresses, etc. and pointers to callbacks. The callbacks will be called
++ by provider when it needs to do something application-specific, like log a
++ message or apply a writeset. It should be passed to init() call from
++ wsrep API. It is an application part of wsrep API contract.
++
++ struct wsrep is the interface to wsrep provider. It contains all wsrep API
++ calls. It is a provider part of wsrep API contract.
++
++ Finally, wsrep_load() method loads (dlopens) wsrep provider library. It is
++ defined in wsrep_loader.c unit and is part of libwsrep.a (which is not a
++ wsrep provider, but a convenience library).
++
++ wsrep_unload() does the reverse.
++
++*/
++#ifndef WSREP_H
++#define WSREP_H
++
++#include <stdint.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <time.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/**************************************************************************
++ * *
++ * wsrep replication API *
++ * *
++ **************************************************************************/
++
++#define WSREP_INTERFACE_VERSION "25"
++
++/*! Empty backend spec */
++#define WSREP_NONE "none"
++
++
++/*!
++ * @brief log severity levels, passed as first argument to log handler
++ */
++typedef enum wsrep_log_level
++{
++ WSREP_LOG_FATAL, //!< Unrecoverable error, application must quit.
++ WSREP_LOG_ERROR, //!< Operation failed, must be repeated.
++ WSREP_LOG_WARN, //!< Unexpected condition, but no operational failure.
++ WSREP_LOG_INFO, //!< Informational message.
++ WSREP_LOG_DEBUG //!< Debug message. Shows only of compiled with debug.
++} wsrep_log_level_t;
++
++/*!
++ * @brief error log handler
++ *
++ * All messages from wsrep provider are directed to this
++ * handler, if present.
++ *
++ * @param level log level
++ * @param message log message
++ */
++typedef void (*wsrep_log_cb_t)(wsrep_log_level_t, const char *);
++
++
++/*!
++ * Certain provider capabilities application may want to know about
++ */
++#define WSREP_CAP_MULTI_MASTER ( 1ULL << 0 )
++#define WSREP_CAP_CERTIFICATION ( 1ULL << 1 )
++#define WSREP_CAP_PARALLEL_APPLYING ( 1ULL << 2 )
++#define WSREP_CAP_TRX_REPLAY ( 1ULL << 3 )
++#define WSREP_CAP_ISOLATION ( 1ULL << 4 )
++#define WSREP_CAP_PAUSE ( 1ULL << 5 )
++#define WSREP_CAP_CAUSAL_READS ( 1ULL << 6 )
++#define WSREP_CAP_CAUSAL_TRX ( 1ULL << 7 )
++#define WSREP_CAP_INCREMENTAL_WRITESET ( 1ULL << 8 )
++#define WSREP_CAP_SESSION_LOCKS ( 1ULL << 9 )
++#define WSREP_CAP_DISTRIBUTED_LOCKS ( 1ULL << 10 )
++#define WSREP_CAP_CONSISTENCY_CHECK ( 1ULL << 11 )
++#define WSREP_CAP_UNORDERED ( 1ULL << 12 )
++#define WSREP_CAP_ANNOTATION ( 1ULL << 13 )
++#define WSREP_CAP_PREORDERED ( 1ULL << 14 )
++
++
++/*!
++ * Writeset flags
++ *
++ * COMMIT the writeset and all preceding writesets must be committed
++ * ROLLBACK all preceding writesets in a transaction must be rolled back
++ * ISOLATION the writeset must be applied AND committed in isolation
++ * PA_UNSAFE the writeset cannot be applied in parallel
++ * COMMUTATIVE the order in which the writeset is applied does not matter
++ * NATIVE the writeset contains another writeset in this provider format
++ *
++ * Note that some of the flags are mutually exclusive (e.g. COMMIT and
++ * ROLLBACK).
++ */
++#define WSREP_FLAG_COMMIT ( 1ULL << 0 )
++#define WSREP_FLAG_ROLLBACK ( 1ULL << 1 )
++#define WSREP_FLAG_ISOLATION ( 1ULL << 2 )
++#define WSREP_FLAG_PA_UNSAFE ( 1ULL << 3 )
++#define WSREP_FLAG_COMMUTATIVE ( 1ULL << 4 )
++#define WSREP_FLAG_NATIVE ( 1ULL << 5 )
++
++
++typedef uint64_t wsrep_trx_id_t; //!< application transaction ID
++typedef uint64_t wsrep_conn_id_t; //!< application connection ID
++typedef int64_t wsrep_seqno_t; //!< sequence number of a writeset, etc.
++#ifdef __cplusplus
++typedef bool wsrep_bool_t;
++#else
++typedef _Bool wsrep_bool_t; //!< should be the same as standard (C99) bool
++#endif /* __cplusplus */
++
++/*! undefined seqno */
++#define WSREP_SEQNO_UNDEFINED (-1)
++
++
++/*! wsrep provider status codes */
++typedef enum wsrep_status
++{
++ WSREP_OK = 0, //!< success
++ WSREP_WARNING, //!< minor warning, error logged
++ WSREP_TRX_MISSING, //!< transaction is not known by wsrep
++ WSREP_TRX_FAIL, //!< transaction aborted, server can continue
++ WSREP_BF_ABORT, //!< trx was victim of brute force abort
++ WSREP_SIZE_EXCEEDED, //!< data exceeded maximum supported size
++ WSREP_CONN_FAIL, //!< error in client connection, must abort
++ WSREP_NODE_FAIL, //!< error in node state, wsrep must reinit
++ WSREP_FATAL, //!< fatal error, server must abort
++ WSREP_NOT_IMPLEMENTED //!< feature not implemented
++} wsrep_status_t;
++
++
++/*! wsrep callbacks status codes */
++typedef enum wsrep_cb_status
++{
++ WSREP_CB_SUCCESS = 0, //!< success (as in "not critical failure")
++ WSREP_CB_FAILURE //!< critical failure (consistency violation)
++ /* Technically, wsrep provider has no use for specific failure codes since
++ * there is nothing it can do about it but abort execution. Therefore any
++ * positive number shall indicate a critical failure. Optionally that value
++ * may be used by provider to come to a consensus about state consistency
++ * in a group of nodes. */
++} wsrep_cb_status_t;
++
++
++/*!
++ * UUID type - for all unique IDs
++ */
++typedef struct wsrep_uuid {
++ uint8_t data[16];
++} wsrep_uuid_t;
++
++/*! Undefined UUID */
++static const wsrep_uuid_t WSREP_UUID_UNDEFINED = {{0,}};
++
++/*! UUID string representation length, terminating '\0' not included */
++#define WSREP_UUID_STR_LEN 36
++
++/*!
++ * Scan UUID from string
++ * @return length of UUID string representation or negative error code
++ */
++extern int
++wsrep_uuid_scan (const char* str, size_t str_len, wsrep_uuid_t* uuid);
++
++/*!
++ * Print UUID to string
++ * @return length of UUID string representation or negative error code
++ */
++extern int
++wsrep_uuid_print (const wsrep_uuid_t* uuid, char* str, size_t str_len);
++
++#define WSREP_MEMBER_NAME_LEN 32 //!< maximum logical member name length
++#define WSREP_INCOMING_LEN 256 //!< max Domain Name length + 0x00
++
++
++/*!
++ * Global transaction identifier
++ */
++typedef struct wsrep_gtid
++{
++ wsrep_uuid_t uuid; /*!< History UUID */
++ wsrep_seqno_t seqno; /*!< Sequence number */
++} wsrep_gtid_t;
++
++/*! Undefined GTID */
++static const wsrep_gtid_t WSREP_GTID_UNDEFINED = {{{0, }}, -1};
++
++/*! Minimum number of bytes guaranteed to store GTID string representation,
++ * terminating '\0' not included (36 + 1 + 20) */
++#define WSREP_GTID_STR_LEN 57
++
++
++/*!
++ * Scan GTID from string
++ * @return length of GTID string representation or negative error code
++ */
++extern int
++wsrep_gtid_scan(const char* str, size_t str_len, wsrep_gtid_t* gtid);
++
++/*!
++ * Print GTID to string
++ * @return length of GTID string representation or negative error code
++ */
++extern int
++wsrep_gtid_print(const wsrep_gtid_t* gtid, char* str, size_t str_len);
++
++
++/*!
++ * Transaction meta data
++ */
++typedef struct wsrep_trx_meta
++{
++ wsrep_gtid_t gtid; /*!< Global transaction identifier */
++ wsrep_seqno_t depends_on; /*!< Sequence number part of the last transaction
++ this transaction depends on */
++} wsrep_trx_meta_t;
++
++
++/*!
++ * member status
++ */
++typedef enum wsrep_member_status {
++ WSREP_MEMBER_UNDEFINED, //!< undefined state
++ WSREP_MEMBER_JOINER, //!< incomplete state, requested state transfer
++ WSREP_MEMBER_DONOR, //!< complete state, donates state transfer
++ WSREP_MEMBER_JOINED, //!< complete state
++ WSREP_MEMBER_SYNCED, //!< complete state, synchronized with group
++ WSREP_MEMBER_ERROR, //!< this and above is provider-specific error code
++ WSREP_MEMBER_MAX
++} wsrep_member_status_t;
++
++/*!
++ * static information about a group member (some fields are tentative yet)
++ */
++typedef struct wsrep_member_info {
++ wsrep_uuid_t id; //!< group-wide unique member ID
++ char name[WSREP_MEMBER_NAME_LEN]; //!< human-readable name
++ char incoming[WSREP_INCOMING_LEN]; //!< address for client requests
++} wsrep_member_info_t;
++
++/*!
++ * group status
++ */
++typedef enum wsrep_view_status {
++ WSREP_VIEW_PRIMARY, //!< primary group configuration (quorum present)
++ WSREP_VIEW_NON_PRIMARY, //!< non-primary group configuration (quorum lost)
++ WSREP_VIEW_DISCONNECTED, //!< not connected to group, retrying.
++ WSREP_VIEW_MAX
++} wsrep_view_status_t;
++
++/*!
++ * view of the group
++ */
++typedef struct wsrep_view_info {
++ wsrep_gtid_t state_id; //!< global state ID
++ wsrep_seqno_t view; //!< global view number
++ wsrep_view_status_t status; //!< view status
++ wsrep_bool_t state_gap; //!< gap between global and local states
++ int my_idx; //!< index of this member in the view
++ int memb_num; //!< number of members in the view
++ int proto_ver; //!< application protocol agreed on the view
++ wsrep_member_info_t members[1];//!< array of member information
++} wsrep_view_info_t;
++
++/*!
++ * Magic string to tell provider to engage into trivial (empty) state transfer.
++ * No data will be passed, but the node shall be considered JOINED.
++ * Should be passed in sst_req parameter of wsrep_view_cb_t.
++ */
++#define WSREP_STATE_TRANSFER_TRIVIAL "trivial"
++
++/*!
++ * Magic string to tell provider not to engage in state transfer at all.
++ * The member will stay in WSREP_MEMBER_UNDEFINED state but will keep on
++ * receiving all writesets.
++ * Should be passed in sst_req parameter of wsrep_view_cb_t.
++ */
++#define WSREP_STATE_TRANSFER_NONE "none"
++
++/*!
++ * @brief group view handler
++ *
++ * This handler is called in total order corresponding to the group
++ * configuration change. It is to provide a vital information about
++ * new group view. If view info indicates existence of discontinuity
++ * between group and member states, state transfer request message
++ * should be filled in by the callback implementation.
++ *
++ * @note Currently it is assumed that sst_req is allocated using
++ * malloc()/calloc()/realloc() and it will be freed by
++ * wsrep implementation.
++ *
++ * @param app_ctx application context
++ * @param recv_ctx receiver context
++ * @param view new view on the group
++ * @param state current state
++ * @param state_len lenght of current state
++ * @param sst_req location to store SST request
++ * @param sst_req_len location to store SST request length or error code,
++ * value of 0 means no SST.
++ */
++typedef enum wsrep_cb_status (*wsrep_view_cb_t) (
++ void* app_ctx,
++ void* recv_ctx,
++ const wsrep_view_info_t* view,
++ const char* state,
++ size_t state_len,
++ void** sst_req,
++ size_t* sst_req_len
++);
++
++
++/*!
++ * @brief apply callback
++ *
++ * This handler is called from wsrep library to apply replicated writeset
++ * Must support brute force applying for multi-master operation
++ *
++ * @param recv_ctx receiver context pointer provided by the application
++ * @param data data buffer containing the writeset
++ * @param size data buffer size
++ * @param flags WSREP_FLAG_... flags
++ * @param meta transaction meta data of the writeset to be applied
++ *
++ * @return success code:
++ * @retval WSREP_OK
++ * @retval WSREP_NOT_IMPLEMENTED appl. does not support the writeset format
++ * @retval WSREP_ERROR failed to apply the writeset
++ */
++typedef enum wsrep_cb_status (*wsrep_apply_cb_t) (
++ void* recv_ctx,
++ const void* data,
++ size_t size,
++ uint32_t flags,
++ const wsrep_trx_meta_t* meta
++);
++
++
++/*!
++ * @brief commit callback
++ *
++ * This handler is called to commit the changes made by apply callback.
++ *
++ * @param recv_ctx receiver context pointer provided by the application
++ * @param flags WSREP_FLAG_... flags
++ * @param meta transaction meta data of the writeset to be committed
++ * @param exit set to true to exit recv loop
++ * @param commit true - commit writeset, false - rollback writeset
++ *
++ * @return success code:
++ * @retval WSREP_OK
++ * @retval WSREP_ERROR call failed
++ */
++typedef enum wsrep_cb_status (*wsrep_commit_cb_t) (
++ void* recv_ctx,
++ uint32_t flags,
++ const wsrep_trx_meta_t* meta,
++ wsrep_bool_t* exit,
++ wsrep_bool_t commit
++);
++
++
++/*!
++ * @brief unordered callback
++ *
++ * This handler is called to execute unordered actions (actions that need not
++ * to be executed in any particular order) attached to writeset.
++ *
++ * @param recv_ctx receiver context pointer provided by the application
++ * @param data data buffer containing the writeset
++ * @param size data buffer size
++ */
++typedef enum wsrep_cb_status (*wsrep_unordered_cb_t) (
++ void* recv_ctx,
++ const void* data,
++ size_t size
++);
++
++
++/*!
++ * @brief a callback to donate state snapshot
++ *
++ * This handler is called from wsrep library when it needs this node
++ * to deliver state to a new cluster member.
++ * No state changes will be committed for the duration of this call.
++ * Wsrep implementation may provide internal state to be transmitted
++ * to new cluster member for initial state.
++ *
++ * @param app_ctx application context
++ * @param recv_ctx receiver context
++ * @param msg state transfer request message
++ * @param msg_len state transfer request message length
++ * @param gtid current state ID on this node
++ * @param state current wsrep internal state buffer
++ * @param state_len current wsrep internal state buffer len
++ * @param bypass bypass snapshot transfer, only transfer uuid:seqno pair
++ */
++typedef enum wsrep_cb_status (*wsrep_sst_donate_cb_t) (
++ void* app_ctx,
++ void* recv_ctx,
++ const void* msg,
++ size_t msg_len,
++ const wsrep_gtid_t* state_id,
++ const char* state,
++ size_t state_len,
++ wsrep_bool_t bypass
++);
++
++
++/*!
++ * @brief a callback to signal application that wsrep state is synced
++ * with cluster
++ *
++ * This callback is called after wsrep library has got in sync with
++ * rest of the cluster.
++ *
++ * @param app_ctx application context
++ */
++typedef void (*wsrep_synced_cb_t) (void* app_ctx);
++
++
++/*!
++ * Initialization parameters for wsrep provider.
++ */
++struct wsrep_init_args
++{
++ void* app_ctx; //!< Application context for callbacks
++
++ /* Configuration parameters */
++ const char* node_name; //!< Symbolic name of this node (e.g. hostname)
++ const char* node_address; //!< Address to be used by wsrep provider
++ const char* node_incoming; //!< Address for incoming client connections
++ const char* data_dir; //!< Directory where wsrep files are kept if any
++ const char* options; //!< Provider-specific configuration string
++ int proto_ver; //!< Max supported application protocol version
++
++ /* Application initial state information. */
++ const wsrep_gtid_t* state_id; //!< Application state GTID
++ const char* state; //!< Initial state for wsrep provider
++ size_t state_len; //!< Length of state buffer
++
++ /* Application callbacks */
++ wsrep_log_cb_t logger_cb; //!< logging handler
++ wsrep_view_cb_t view_handler_cb; //!< group view change handler
++
++ /* Applier callbacks */
++ wsrep_apply_cb_t apply_cb; //!< apply callback
++ wsrep_commit_cb_t commit_cb; //!< commit callback
++ wsrep_unordered_cb_t unordered_cb; //!< callback for unordered actions
++
++ /* State Snapshot Transfer callbacks */
++ wsrep_sst_donate_cb_t sst_donate_cb; //!< starting to donate
++ wsrep_synced_cb_t synced_cb; //!< synced with group
++};
++
++
++/*! Type of the stats variable value in struct wsrep_status_var */
++typedef enum wsrep_var_type
++{
++ WSREP_VAR_STRING, //!< pointer to null-terminated string
++ WSREP_VAR_INT64, //!< int64_t
++ WSREP_VAR_DOUBLE //!< double
++}
++wsrep_var_type_t;
++
++/*! Generalized stats variable representation */
++struct wsrep_stats_var
++{
++ const char* name; //!< variable name
++ wsrep_var_type_t type; //!< variable value type
++ union {
++ int64_t _int64;
++ double _double;
++ const char* _string;
++ } value; //!< variable value
++};
++
++
++/*! Abstract data buffer structure */
++typedef struct wsrep_buf
++{
++ const void* ptr; /*!< Pointer to data buffer */
++ size_t len; /*!< Length of buffer */
++} wsrep_buf_t;
++
++/*! Key struct used to pass certification keys for transaction handling calls.
++ * A key consists of zero or more key parts. */
++typedef struct wsrep_key
++{
++ const wsrep_buf_t* key_parts; /*!< Array of key parts */
++ size_t key_parts_num; /*!< Number of key parts */
++} wsrep_key_t;
++
++/*! Key type:
++ * EXCLUSIVE conflicts with any key type
++ * SEMI reserved. If not supported, should be interpeted as EXCLUSIVE
++ * SHARED conflicts only with EXCLUSIVE keys */
++typedef enum wsrep_key_type
++{
++ WSREP_KEY_SHARED = 0,
++ WSREP_KEY_SEMI,
++ WSREP_KEY_EXCLUSIVE
++} wsrep_key_type_t;
++
++/*! Data type:
++ * ORDERED state modification event that should be applied and committed
++ * in order.
++ * UNORDERED some action that does not modify state and execution of which is
++ * optional and does not need to happen in order.
++ * ANNOTATION (human readable) writeset annotation. */
++typedef enum wsrep_data_type
++{
++ WSREP_DATA_ORDERED = 0,
++ WSREP_DATA_UNORDERED,
++ WSREP_DATA_ANNOTATION
++} wsrep_data_type_t;
++
++
++/*! Transaction handle struct passed for wsrep transaction handling calls */
++typedef struct wsrep_ws_handle
++{
++ wsrep_trx_id_t trx_id; //!< transaction ID
++ void* opaque; //!< opaque provider transaction context data
++} wsrep_ws_handle_t;
++
++/*!
++ * @brief Helper method to reset trx writeset handle state when trx id changes
++ *
++ * Instead of passing wsrep_ws_handle_t directly to wsrep calls,
++ * wrapping handle with this call offloads bookkeeping from
++ * application.
++ */
++static inline wsrep_ws_handle_t* wsrep_ws_handle_for_trx(
++ wsrep_ws_handle_t* ws_handle,
++ wsrep_trx_id_t trx_id)
++{
++ if (ws_handle->trx_id != trx_id)
++ {
++ ws_handle->trx_id = trx_id;
++ ws_handle->opaque = NULL;
++ }
++ return ws_handle;
++}
++
++
++/*!
++ * A handle for processing preordered actions.
++ * Must be initialized to WSREP_PO_INITIALIZER before use.
++ */
++typedef struct wsrep_po_handle { void* opaque; } wsrep_po_handle_t;
++
++static const wsrep_po_handle_t WSREP_PO_INITIALIZER = { NULL };
++
++
++typedef struct wsrep wsrep_t;
++/*!
++ * wsrep interface for dynamically loadable libraries
++ */
++struct wsrep {
++
++ const char *version; //!< interface version string
++
++ /*!
++ * @brief Initializes wsrep provider
++ *
++ * @param wsrep provider handle
++ * @param args wsrep initialization parameters
++ */
++ wsrep_status_t (*init) (wsrep_t* wsrep,
++ const struct wsrep_init_args* args);
++
++ /*!
++ * @brief Returns provider capabilities flag bitmap
++ *
++ * @param wsrep provider handle
++ */
++ uint64_t (*capabilities) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Passes provider-specific configuration string to provider.
++ *
++ * @param wsrep provider handle
++ * @param conf configuration string
++ *
++ * @retval WSREP_OK configuration string was parsed successfully
++ * @retval WSREP_WARNING could't not parse conf string, no action taken
++ */
++ wsrep_status_t (*options_set) (wsrep_t* wsrep, const char* conf);
++
++ /*!
++ * @brief Returns provider-specific string with current configuration values.
++ *
++ * @param wsrep provider handle
++ *
++ * @return a dynamically allocated string with current configuration
++ * parameter values
++ */
++ char* (*options_get) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Opens connection to cluster
++ *
++ * Returns when either node is ready to operate as a part of the clsuter
++ * or fails to reach operating status.
++ *
++ * @param wsrep provider handle
++ * @param cluster_name unique symbolic cluster name
++ * @param cluster_url URL-like cluster address (backend://address)
++ * @param state_donor name of the node to be asked for state transfer.
++ * @param bootstrap a flag to request initialization of a new wsrep
++ * service rather then a connection to the existing one.
++ * clister_url may still carry important initialization
++ * parameters, like backend spec and/or listen address.
++ */
++ wsrep_status_t (*connect) (wsrep_t* wsrep,
++ const char* cluster_name,
++ const char* cluster_url,
++ const char* state_donor,
++ wsrep_bool_t bootstrap);
++
++ /*!
++ * @brief Closes connection to cluster.
++ *
++ * If state_uuid and/or state_seqno is not NULL, will store final state
++ * in there.
++ *
++ * @param wsrep this wsrep handler
++ */
++ wsrep_status_t (*disconnect)(wsrep_t* wsrep);
++
++ /*!
++ * @brief start receiving replication events
++ *
++ * This function never returns
++ *
++ * @param wsrep provider handle
++ * @param recv_ctx receiver context
++ */
++ wsrep_status_t (*recv)(wsrep_t* wsrep, void* recv_ctx);
++
++ /*!
++ * @brief Replicates/logs result of transaction to other nodes and allocates
++ * required resources.
++ *
++ * Must be called before transaction commit. Returns success code, which
++ * caller must check.
++ * In case of WSREP_OK, starts commit critical section, transaction can
++ * commit. Otherwise transaction must rollback.
++ *
++ * @param wsrep provider handle
++ * @param ws_handle writeset of committing transaction
++ * @param conn_id connection ID
++ * @param flags fine tuning the replication WSREP_FLAG_*
++ * @param meta transaction meta data
++ *
++ * @retval WSREP_OK cluster-wide commit succeeded
++ * @retval WSREP_TRX_FAIL must rollback transaction
++ * @retval WSREP_CONN_FAIL must close client connection
++ * @retval WSREP_NODE_FAIL must close all connections and reinit
++ */
++ wsrep_status_t (*pre_commit)(wsrep_t* wsrep,
++ wsrep_conn_id_t conn_id,
++ wsrep_ws_handle_t* ws_handle,
++ uint32_t flags,
++ wsrep_trx_meta_t* meta);
++
++ /*!
++ * @brief Releases resources after transaction commit.
++ *
++ * Ends commit critical section.
++ *
++ * @param wsrep provider handle
++ * @param ws_handle writeset of committing transaction
++ * @retval WSREP_OK post_commit succeeded
++ */
++ wsrep_status_t (*post_commit) (wsrep_t* wsrep,
++ wsrep_ws_handle_t* ws_handle);
++
++ /*!
++ * @brief Releases resources after transaction rollback.
++ *
++ * @param wsrep provider handle
++ * @param ws_handle writeset of committing transaction
++ * @retval WSREP_OK post_rollback succeeded
++ */
++ wsrep_status_t (*post_rollback)(wsrep_t* wsrep,
++ wsrep_ws_handle_t* ws_handle);
++
++ /*!
++ * @brief Replay trx as a slave writeset
++ *
++ * If local trx has been aborted by brute force, and it has already
++ * replicated before this abort, we must try if we can apply it as
++ * slave trx. Note that slave nodes see only trx writesets and certification
++ * test based on write set content can be different to DBMS lock conflicts.
++ *
++ * @param wsrep provider handle
++ * @param ws_handle writeset of committing transaction
++ * @param trx_ctx transaction context
++ *
++ * @retval WSREP_OK cluster commit succeeded
++ * @retval WSREP_TRX_FAIL must rollback transaction
++ * @retval WSREP_BF_ABORT brute force abort happened after trx replicated
++ * must rollback transaction and try to replay
++ * @retval WSREP_CONN_FAIL must close client connection
++ * @retval WSREP_NODE_FAIL must close all connections and reinit
++ */
++ wsrep_status_t (*replay_trx)(wsrep_t* wsrep,
++ wsrep_ws_handle_t* ws_handle,
++ void* trx_ctx);
++
++ /*!
++ * @brief Abort pre_commit() call of another thread.
++ *
++ * It is possible, that some high-priority transaction needs to abort
++ * another transaction which is in pre_commit() call waiting for resources.
++ *
++ * The kill routine checks that abort is not attmpted against a transaction
++ * which is front of the caller (in total order).
++ *
++ * @param wsrep provider handle
++ * @param bf_seqno seqno of brute force trx, running this cancel
++ * @param victim_trx transaction to be aborted, and which is committing
++ *
++ * @retval WSREP_OK abort secceded
++ * @retval WSREP_WARNING abort failed
++ */
++ wsrep_status_t (*abort_pre_commit)(wsrep_t* wsrep,
++ wsrep_seqno_t bf_seqno,
++ wsrep_trx_id_t victim_trx);
++
++ /*!
++ * @brief Appends a row reference to transaction writeset
++ *
++ * Both copy flag and key_type can be ignored by provider (key type
++ * interpreted as WSREP_KEY_EXCLUSIVE).
++ *
++ * @param wsrep provider handle
++ * @param ws_handle writeset handle
++ * @param keys array of keys
++ * @param count length of the array of keys
++ * @param type type ot the key
++ * @param copy can be set to FALSE if keys persist through commit.
++ */
++ wsrep_status_t (*append_key)(wsrep_t* wsrep,
++ wsrep_ws_handle_t* ws_handle,
++ const wsrep_key_t* keys,
++ size_t count,
++ enum wsrep_key_type type,
++ wsrep_bool_t copy);
++
++ /*!
++ * @brief Appends data to transaction writeset
++ *
++ * This method can be called any time before commit and it
++ * appends a number of data buffers to transaction writeset.
++ *
++ * Both copy and unordered flags can be ignored by provider.
++ *
++ * @param wsrep provider handle
++ * @param ws_handle writeset handle
++ * @param data array of data buffers
++ * @param count buffer count
++ * @param type type of data
++ * @param copy can be set to FALSE if data persists through commit.
++ */
++ wsrep_status_t (*append_data)(wsrep_t* wsrep,
++ wsrep_ws_handle_t* ws_handle,
++ const struct wsrep_buf* data,
++ size_t count,
++ enum wsrep_data_type type,
++ wsrep_bool_t copy);
++
++ /*!
++ * @brief Get causal ordering for read operation
++ *
++ * This call will block until causal ordering with all possible
++ * preceding writes in the cluster is guaranteed. If pointer to
++ * gtid is non-null, the call stores the global transaction ID
++ * of the last transaction which is guaranteed to be ordered
++ * causally before this call.
++ *
++ * @param wsrep provider handle
++ * @param gtid location to store GTID
++ */
++ wsrep_status_t (*causal_read)(wsrep_t* wsrep, wsrep_gtid_t* gtid);
++
++ /*!
++ * @brief Clears allocated connection context.
++ *
++ * Whenever a new connection ID is passed to wsrep provider through
++ * any of the API calls, a connection context is allocated for this
++ * connection. This call is to explicitly notify provider fo connection
++ * closing.
++ *
++ * @param wsrep provider handle
++ * @param conn_id connection ID
++ * @param query the 'set database' query
++ * @param query_len length of query (does not end with 0)
++ */
++ wsrep_status_t (*free_connection)(wsrep_t* wsrep,
++ wsrep_conn_id_t conn_id);
++
++ /*!
++ * @brief Replicates a query and starts "total order isolation" section.
++ *
++ * Replicates the action spec and returns success code, which caller must
++ * check. Total order isolation continues until to_execute_end() is called.
++ *
++ * @param wsrep provider handle
++ * @param conn_id connection ID
++ * @param keys array of keys
++ * @param keys_num lenght of the array of keys
++ * @param action action buffer array to be executed
++ * @param count action buffer count
++ * @param meta transaction meta data
++ *
++ * @retval WSREP_OK cluster commit succeeded
++ * @retval WSREP_CONN_FAIL must close client connection
++ * @retval WSREP_NODE_FAIL must close all connections and reinit
++ */
++ wsrep_status_t (*to_execute_start)(wsrep_t* wsrep,
++ wsrep_conn_id_t conn_id,
++ const wsrep_key_t* keys,
++ size_t keys_num,
++ const struct wsrep_buf* action,
++ size_t count,
++ wsrep_trx_meta_t* meta);
++
++ /*!
++ * @brief Ends the total order isolation section.
++ *
++ * Marks the end of total order isolation. TO locks are freed
++ * and other transactions are free to commit from this point on.
++ *
++ * @param wsrep provider handle
++ * @param conn_id connection ID
++ *
++ * @retval WSREP_OK cluster commit succeeded
++ * @retval WSREP_CONN_FAIL must close client connection
++ * @retval WSREP_NODE_FAIL must close all connections and reinit
++ */
++ wsrep_status_t (*to_execute_end)(wsrep_t* wsrep, wsrep_conn_id_t conn_id);
++
++ /*!
++ * @brief Collects preordered replication events into a writeset.
++ *
++ * @param wsrep wsrep provider handle
++ * @param handle a handle associated with a given writeset
++ * @param data an array of data buffers.
++ * @param count length of data buffer array.
++ * @param copy whether provider needs to make a copy of events.
++ *
++ * @retval WSREP_OK cluster-wide commit succeeded
++ * @retval WSREP_TRX_FAIL operation failed (e.g. trx size exceeded limit)
++ * @retval WSREP_NODE_FAIL must close all connections and reinit
++ */
++ wsrep_status_t (*preordered_collect) (wsrep_t* wsrep,
++ wsrep_po_handle_t* handle,
++ const struct wsrep_buf* data,
++ size_t count,
++ wsrep_bool_t copy);
++
++ /*!
++ * @brief "Commits" preordered writeset to cluster.
++ *
++ * The contract is that the writeset will be committed in the same (partial)
++ * order this method was called. Frees resources associated with the writeset
++ * handle and reinitializes the handle.
++ *
++ * @param wsrep wsrep provider handle
++ * @param po_handle a handle associated with a given writeset
++ * @param source_id ID of the event producer, also serves as the partial order
++ * or stream ID - events with different source_ids won't be
++ * ordered with respect to each other.
++ * @param flags WSREP_FLAG_... flags
++ * @param pa_range the number of preceding events this event can be processed
++ * in parallel with. A value of 0 means strict serial
++ * processing. Note: commits always happen in wsrep order.
++ * @param commit 'true' to commit writeset to cluster (replicate) or
++ * 'false' to rollback (cancel) the writeset.
++ *
++ * @retval WSREP_OK cluster-wide commit succeeded
++ * @retval WSREP_TRX_FAIL operation failed (e.g. NON-PRIMARY component)
++ * @retval WSREP_NODE_FAIL must close all connections and reinit
++ */
++ wsrep_status_t (*preordered_commit) (wsrep_t* wsrep,
++ wsrep_po_handle_t* handle,
++ const wsrep_uuid_t* source_id,
++ uint32_t flags,
++ int pa_range,
++ wsrep_bool_t commit);
++
++ /*!
++ * @brief Signals to wsrep provider that state snapshot has been sent to
++ * joiner.
++ *
++ * @param wsrep provider handle
++ * @param state_id state ID
++ * @param rcode 0 or negative error code of the operation.
++ */
++ wsrep_status_t (*sst_sent)(wsrep_t* wsrep,
++ const wsrep_gtid_t* state_id,
++ int rcode);
++
++ /*!
++ * @brief Signals to wsrep provider that new state snapshot has been received.
++ * May deadlock if called from sst_prepare_cb.
++ *
++ * @param wsrep provider handle
++ * @param state_id state ID
++ * @param state initial state provided by SST donor
++ * @param state_len length of state buffer
++ * @param rcode 0 or negative error code of the operation.
++ */
++ wsrep_status_t (*sst_received)(wsrep_t* wsrep,
++ const wsrep_gtid_t* state_id,
++ const void* state,
++ size_t state_len,
++ int rcode);
++
++
++ /*!
++ * @brief Generate request for consistent snapshot.
++ *
++ * If successfull, this call will generate internally SST request
++ * which in turn triggers calling SST donate callback on the nodes
++ * specified in donor_spec. If donor_spec is null, callback is
++ * called only locally. This call will block until sst_sent is called
++ * from callback.
++ *
++ * @param wsrep provider handle
++ * @param msg context message for SST donate callback
++ * @param msg_len length of context message
++ * @param donor_spec list of snapshot donors
++ */
++ wsrep_status_t (*snapshot)(wsrep_t* wsrep,
++ const void* msg,
++ size_t msg_len,
++ const char* donor_spec);
++
++ /*!
++ * @brief Returns an array fo status variables.
++ * Array is terminated by Null variable name.
++ *
++ * @param wsrep provider handle
++ * @return array of struct wsrep_status_var.
++ */
++ struct wsrep_stats_var* (*stats_get) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Release resources that might be associated with the array.
++ *
++ * @param wsrep provider handle.
++ * @param var_array array returned by stats_get().
++ */
++ void (*stats_free) (wsrep_t* wsrep, struct wsrep_stats_var* var_array);
++
++ /*!
++ * @brief Reset some stats variables to inital value, provider-dependent.
++ *
++ * @param wsrep provider handle.
++ */
++ void (*stats_reset) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Pauses writeset applying/committing.
++ *
++ * @return global sequence number of the paused state or negative error code.
++ */
++ wsrep_seqno_t (*pause) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Resumes writeset applying/committing.
++ */
++ wsrep_status_t (*resume) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Desynchronize from cluster
++ *
++ * Effectively turns off flow control for this node, allowing it
++ * to fall behind the cluster.
++ */
++ wsrep_status_t (*desync) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Request to resynchronize with cluster.
++ *
++ * Effectively turns on flow control. Asynchronous - actual synchronization
++ * event to be deliverred via sync_cb.
++ */
++ wsrep_status_t (*resync) (wsrep_t* wsrep);
++
++ /*!
++ * @brief Acquire global named lock
++ *
++ * @param wsrep wsrep provider handle
++ * @param name lock name
++ * @param shared shared or exclusive lock
++ * @param owner 64-bit owner ID
++ * @param tout timeout in nanoseconds.
++ * 0 - return immediately, -1 wait forever.
++ * @return wsrep status or negative error code
++ * @retval -EDEADLK lock was already acquired by this thread
++ * @retval -EBUSY lock was busy
++ */
++ wsrep_status_t (*lock) (wsrep_t* wsrep,
++ const char* name, wsrep_bool_t shared,
++ uint64_t owner, int64_t tout);
++
++ /*!
++ * @brief Release global named lock
++ *
++ * @param wsrep wsrep provider handle
++ * @param name lock name
++ * @param owner 64-bit owner ID
++ * @return wsrep status or negative error code
++ * @retval -EPERM lock does not belong to this owner
++ */
++ wsrep_status_t (*unlock) (wsrep_t* wsrep, const char* name, uint64_t owner);
++
++ /*!
++ * @brief Check if global named lock is locked
++ *
++ * @param wsrep wsrep provider handle
++ * @param name lock name
++ * @param owner if not NULL will contain 64-bit owner ID
++ * @param node if not NULL will contain owner's node UUID
++ * @return true if lock is locked
++ */
++ wsrep_bool_t (*is_locked) (wsrep_t* wsrep, const char* name, uint64_t* conn,
++ wsrep_uuid_t* node);
++
++ /*!
++ * wsrep provider name
++ */
++ const char* provider_name;
++
++ /*!
++ * wsrep provider version
++ */
++ const char* provider_version;
++
++ /*!
++ * wsrep provider vendor name
++ */
++ const char* provider_vendor;
++
++ /*!
++ * @brief Frees allocated resources before unloading the library.
++ * @param wsrep provider handle
++ */
++ void (*free)(wsrep_t* wsrep);
++
++ void *dlh; //!< reserved for future use
++ void *ctx; //!< reserved for implemetation private context
++};
++
++
++/*!
++ *
++ * @brief Loads wsrep library
++ *
++ * @param spec path to wsrep library. If NULL or WSREP_NONE initialises dummy
++ * pass-through implementation.
++ * @param hptr wsrep handle
++ * @param log_cb callback to handle loader messages. Otherwise writes to stderr.
++ *
++ * @return zero on success, errno on failure
++ */
++int wsrep_load(const char* spec, wsrep_t** hptr, wsrep_log_cb_t log_cb);
++
++/*!
++ * @brief Unloads wsrep library and frees associated resources
++ *
++ * @param hptr wsrep handler pointer
++ */
++void wsrep_unload(wsrep_t* hptr);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* WSREP_H */
+diff --git a/wsrep/wsrep_dummy.c b/wsrep/wsrep_dummy.c
+new file mode 100644
+index 0000000..bab5329
+--- /dev/null
++++ b/wsrep/wsrep_dummy.c
+@@ -0,0 +1,407 @@
++/* Copyright (C) 2009-2010 Codership Oy <info@codersihp.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/*! @file Dummy wsrep API implementation. */
++
++#include "wsrep_api.h"
++
++#include <errno.h>
++#include <stdbool.h>
++#include <string.h>
++
++/*! Dummy backend context. */
++typedef struct wsrep_dummy
++{
++ wsrep_log_cb_t log_fn;
++ char* options;
++} wsrep_dummy_t;
++
++/* Get pointer to wsrep_dummy context from wsrep_t pointer */
++#define WSREP_DUMMY(_p) ((wsrep_dummy_t *) (_p)->ctx)
++
++/* Trace function usage a-la DBUG */
++#define WSREP_DBUG_ENTER(_w) do { \
++ if (WSREP_DUMMY(_w)) { \
++ if (WSREP_DUMMY(_w)->log_fn) \
++ WSREP_DUMMY(_w)->log_fn(WSREP_LOG_DEBUG, __FUNCTION__); \
++ } \
++ } while (0)
++
++
++static void dummy_free(wsrep_t *w)
++{
++ WSREP_DBUG_ENTER(w);
++ if (WSREP_DUMMY(w)->options) {
++ free(WSREP_DUMMY(w)->options);
++ WSREP_DUMMY(w)->options = NULL;
++ }
++ free(w->ctx);
++ w->ctx = NULL;
++}
++
++static wsrep_status_t dummy_init (wsrep_t* w,
++ const struct wsrep_init_args* args)
++{
++ WSREP_DUMMY(w)->log_fn = args->logger_cb;
++ WSREP_DBUG_ENTER(w);
++ if (args->options) {
++ WSREP_DUMMY(w)->options = strdup(args->options);
++ }
++ return WSREP_OK;
++}
++
++static uint64_t dummy_capabilities (wsrep_t* w __attribute__((unused)))
++{
++ return 0;
++}
++
++static wsrep_status_t dummy_options_set(
++ wsrep_t* w,
++ const char* conf)
++{
++ WSREP_DBUG_ENTER(w);
++ if (WSREP_DUMMY(w)->options) {
++ free(WSREP_DUMMY(w)->options);
++ WSREP_DUMMY(w)->options = NULL;
++ }
++ if (conf) {
++ WSREP_DUMMY(w)->options = strdup(conf);
++ }
++ return WSREP_OK;
++}
++
++static char* dummy_options_get (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_DUMMY(w)->options;
++}
++
++static wsrep_status_t dummy_connect(
++ wsrep_t* w,
++ const char* name __attribute__((unused)),
++ const char* url __attribute__((unused)),
++ const char* donor __attribute__((unused)),
++ wsrep_bool_t bootstrap __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_disconnect(wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_recv(wsrep_t* w,
++ void* recv_ctx __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_pre_commit(
++ wsrep_t* w,
++ const wsrep_conn_id_t conn_id __attribute__((unused)),
++ wsrep_ws_handle_t* ws_handle __attribute__((unused)),
++ uint32_t flags __attribute__((unused)),
++ wsrep_trx_meta_t* meta __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_post_commit(
++ wsrep_t* w,
++ wsrep_ws_handle_t* ws_handle __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_post_rollback(
++ wsrep_t* w,
++ wsrep_ws_handle_t* ws_handle __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_replay_trx(
++ wsrep_t* w,
++ wsrep_ws_handle_t* ws_handle __attribute__((unused)),
++ void* trx_ctx __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_abort_pre_commit(
++ wsrep_t* w,
++ const wsrep_seqno_t bf_seqno __attribute__((unused)),
++ const wsrep_trx_id_t trx_id __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_append_key(
++ wsrep_t* w,
++ wsrep_ws_handle_t* ws_handle __attribute__((unused)),
++ const wsrep_key_t* key __attribute__((unused)),
++ const size_t key_num __attribute__((unused)),
++ const wsrep_key_type_t key_type __attribute__((unused)),
++ const bool copy __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_append_data(
++ wsrep_t* w,
++ wsrep_ws_handle_t* ws_handle __attribute__((unused)),
++ const struct wsrep_buf* data __attribute__((unused)),
++ const size_t count __attribute__((unused)),
++ const wsrep_data_type_t type __attribute__((unused)),
++ const bool copy __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_causal_read(
++ wsrep_t* w,
++ wsrep_gtid_t* gtid __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_free_connection(
++ wsrep_t* w,
++ const wsrep_conn_id_t conn_id __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_to_execute_start(
++ wsrep_t* w,
++ const wsrep_conn_id_t conn_id __attribute__((unused)),
++ const wsrep_key_t* key __attribute__((unused)),
++ const size_t key_num __attribute__((unused)),
++ const struct wsrep_buf* data __attribute__((unused)),
++ const size_t count __attribute__((unused)),
++ wsrep_trx_meta_t* meta __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_to_execute_end(
++ wsrep_t* w,
++ const wsrep_conn_id_t conn_id __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_preordered_collect(
++ wsrep_t* w,
++ wsrep_po_handle_t* handle __attribute__((unused)),
++ const struct wsrep_buf* data __attribute__((unused)),
++ size_t count __attribute__((unused)),
++ wsrep_bool_t copy __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_preordered_commit(
++ wsrep_t* w,
++ wsrep_po_handle_t* handle __attribute__((unused)),
++ const wsrep_uuid_t* source_id __attribute__((unused)),
++ uint32_t flags __attribute__((unused)),
++ int pa_range __attribute__((unused)),
++ wsrep_bool_t commit __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_sst_sent(
++ wsrep_t* w,
++ const wsrep_gtid_t* state_id __attribute__((unused)),
++ const int rcode __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_sst_received(
++ wsrep_t* w,
++ const wsrep_gtid_t* state_id __attribute__((unused)),
++ const void* state __attribute__((unused)),
++ const size_t state_len __attribute__((unused)),
++ const int rcode __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_snapshot(
++ wsrep_t* w,
++ const void* msg __attribute__((unused)),
++ const size_t msg_len __attribute__((unused)),
++ const char* donor_spec __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static struct wsrep_stats_var dummy_stats[] = {
++ { NULL, WSREP_VAR_STRING, { 0 } }
++};
++
++static struct wsrep_stats_var* dummy_stats_get (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return dummy_stats;
++}
++
++static void dummy_stats_free (
++ wsrep_t* w,
++ struct wsrep_stats_var* stats __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++}
++
++static void dummy_stats_reset (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++}
++
++static wsrep_seqno_t dummy_pause (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return -ENOSYS;
++}
++
++static wsrep_status_t dummy_resume (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_desync (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_NOT_IMPLEMENTED;
++}
++
++static wsrep_status_t dummy_resync (wsrep_t* w)
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static wsrep_status_t dummy_lock (wsrep_t* w,
++ const char* s __attribute__((unused)),
++ bool r __attribute__((unused)),
++ uint64_t o __attribute__((unused)),
++ int64_t t __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_NOT_IMPLEMENTED;
++}
++
++static wsrep_status_t dummy_unlock (wsrep_t* w,
++ const char* s __attribute__((unused)),
++ uint64_t o __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return WSREP_OK;
++}
++
++static bool dummy_is_locked (wsrep_t* w,
++ const char* s __attribute__((unused)),
++ uint64_t* o __attribute__((unused)),
++ wsrep_uuid_t* t __attribute__((unused)))
++{
++ WSREP_DBUG_ENTER(w);
++ return false;
++}
++
++static wsrep_t dummy_iface = {
++ WSREP_INTERFACE_VERSION,
++ &dummy_init,
++ &dummy_capabilities,
++ &dummy_options_set,
++ &dummy_options_get,
++ &dummy_connect,
++ &dummy_disconnect,
++ &dummy_recv,
++ &dummy_pre_commit,
++ &dummy_post_commit,
++ &dummy_post_rollback,
++ &dummy_replay_trx,
++ &dummy_abort_pre_commit,
++ &dummy_append_key,
++ &dummy_append_data,
++ &dummy_causal_read,
++ &dummy_free_connection,
++ &dummy_to_execute_start,
++ &dummy_to_execute_end,
++ &dummy_preordered_collect,
++ &dummy_preordered_commit,
++ &dummy_sst_sent,
++ &dummy_sst_received,
++ &dummy_snapshot,
++ &dummy_stats_get,
++ &dummy_stats_free,
++ &dummy_stats_reset,
++ &dummy_pause,
++ &dummy_resume,
++ &dummy_desync,
++ &dummy_resync,
++ &dummy_lock,
++ &dummy_unlock,
++ &dummy_is_locked,
++ WSREP_NONE,
++ WSREP_INTERFACE_VERSION,
++ "Codership Oy <info@codership.com>",
++ &dummy_free,
++ NULL,
++ NULL
++};
++
++int wsrep_dummy_loader(wsrep_t* w)
++{
++ if (!w)
++ return EINVAL;
++
++ *w = dummy_iface;
++
++ // allocate private context
++ if (!(w->ctx = malloc(sizeof(wsrep_dummy_t))))
++ return ENOMEM;
++
++ // initialize private context
++ WSREP_DUMMY(w)->log_fn = NULL;
++ WSREP_DUMMY(w)->options = NULL;
++
++ return 0;
++}
+diff --git a/wsrep/wsrep_gtid.c b/wsrep/wsrep_gtid.c
+new file mode 100644
+index 0000000..e618c5a
+--- /dev/null
++++ b/wsrep/wsrep_gtid.c
+@@ -0,0 +1,74 @@
++/* Copyright (C) 2013 Codership Oy <info@codersihp.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/*! @file Helper functions to deal with GTID string representations */
++
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <inttypes.h>
++
++#include "wsrep_api.h"
++
++/*!
++ * Read GTID from string
++ * @return length of GTID string representation or -EINVAL in case of error
++ */
++int
++wsrep_gtid_scan(const char* str, size_t str_len, wsrep_gtid_t* gtid)
++{
++ unsigned int offset;
++ char* endptr;
++
++ if ((offset = wsrep_uuid_scan(str, str_len, >id->uuid)) > 0 &&
++ offset < str_len && str[offset] == ':') {
++ ++offset;
++ if (offset < str_len)
++ {
++ errno = 0;
++ gtid->seqno = strtoll(str + offset, &endptr, 0);
++
++ if (errno == 0) {
++ offset = endptr - str;
++ return offset;
++ }
++ }
++ }
++ *gtid = WSREP_GTID_UNDEFINED;
++ return -EINVAL;
++}
++
++/*!
++ * Write GTID to string
++ * @return length of GTID stirng representation of -EMSGSIZE if string is too
++ * short
++ */
++int
++wsrep_gtid_print(const wsrep_gtid_t* gtid, char* str, size_t str_len)
++{
++ unsigned int offset, ret;
++ if ((offset = wsrep_uuid_print(>id->uuid, str, str_len)) > 0)
++ {
++ ret = snprintf(str + offset, str_len - offset,
++ ":%" PRId64, gtid->seqno);
++ if (ret <= str_len - offset) {
++ return (offset + ret);
++ }
++
++ }
++
++ return -EMSGSIZE;
++}
+diff --git a/wsrep/wsrep_loader.c b/wsrep/wsrep_loader.c
+new file mode 100644
+index 0000000..c330c77
+--- /dev/null
++++ b/wsrep/wsrep_loader.c
+@@ -0,0 +1,203 @@
++/* Copyright (C) 2009-2011 Codership Oy <info@codersihp.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/*! @file wsrep implementation loader */
++
++#include <dlfcn.h>
++#include <errno.h>
++#include <string.h>
++#include <stdio.h>
++
++#include "wsrep_api.h"
++
++// Logging stuff for the loader
++static const char* log_levels[] = {"FATAL", "ERROR", "WARN", "INFO", "DEBUG"};
++
++static void default_logger (wsrep_log_level_t lvl, const char* msg)
++{
++ fprintf (stderr, "wsrep loader: [%s] %s\n", log_levels[lvl], msg);
++}
++
++static wsrep_log_cb_t logger = default_logger;
++
++/**************************************************************************
++ * Library loader
++ **************************************************************************/
++
++static int verify(const wsrep_t *wh, const char *iface_ver)
++{
++ const size_t msg_len = 128;
++ char msg[128];
++
++#define VERIFY(_p) if (!(_p)) { \
++ snprintf(msg, msg_len, "wsrep_load(): verify(): %s\n", # _p); \
++ logger (WSREP_LOG_ERROR, msg); \
++ return EINVAL; \
++ }
++
++ VERIFY(wh);
++ VERIFY(wh->version);
++
++ if (strcmp(wh->version, iface_ver)) {
++ snprintf (msg, msg_len,
++ "provider interface version mismatch: need '%s', found '%s'",
++ iface_ver, wh->version);
++ logger (WSREP_LOG_ERROR, msg);
++ return EINVAL;
++ }
++
++ VERIFY(wh->init);
++ VERIFY(wh->options_set);
++ VERIFY(wh->options_get);
++ VERIFY(wh->connect);
++ VERIFY(wh->disconnect);
++ VERIFY(wh->recv);
++ VERIFY(wh->pre_commit);
++ VERIFY(wh->post_commit);
++ VERIFY(wh->post_rollback);
++ VERIFY(wh->replay_trx);
++ VERIFY(wh->abort_pre_commit);
++ VERIFY(wh->append_key);
++ VERIFY(wh->append_data);
++ VERIFY(wh->free_connection);
++ VERIFY(wh->to_execute_start);
++ VERIFY(wh->to_execute_end);
++ VERIFY(wh->preordered_collect);
++ VERIFY(wh->preordered_commit);
++ VERIFY(wh->sst_sent);
++ VERIFY(wh->sst_received);
++ VERIFY(wh->stats_get);
++ VERIFY(wh->stats_free);
++ VERIFY(wh->stats_reset);
++ VERIFY(wh->pause);
++ VERIFY(wh->resume);
++ VERIFY(wh->desync);
++ VERIFY(wh->resync);
++ VERIFY(wh->lock);
++ VERIFY(wh->unlock);
++ VERIFY(wh->is_locked);
++ VERIFY(wh->provider_name);
++ VERIFY(wh->provider_version);
++ VERIFY(wh->provider_vendor);
++ VERIFY(wh->free);
++ return 0;
++}
++
++typedef int (*wsrep_loader_fun)(wsrep_t*);
++
++static wsrep_loader_fun wsrep_dlf(void *dlh, const char *sym)
++{
++ union {
++ wsrep_loader_fun dlfun;
++ void *obj;
++ } alias;
++ alias.obj = dlsym(dlh, sym);
++ return alias.dlfun;
++}
++
++extern int wsrep_dummy_loader(wsrep_t *w);
++
++int wsrep_load(const char *spec, wsrep_t **hptr, wsrep_log_cb_t log_cb)
++{
++ int ret = 0;
++ void *dlh = NULL;
++ wsrep_loader_fun dlfun;
++ const size_t msg_len = 1024;
++ char msg[1024 + 1];
++ msg[msg_len] = 0;
++
++ if (NULL != log_cb)
++ logger = log_cb;
++
++ if (!(spec && hptr))
++ return EINVAL;
++
++ snprintf (msg, msg_len,
++ "wsrep_load(): loading provider library '%s'", spec);
++ logger (WSREP_LOG_INFO, msg);
++
++ if (!(*hptr = malloc(sizeof(wsrep_t)))) {
++ logger (WSREP_LOG_FATAL, "wsrep_load(): out of memory");
++ return ENOMEM;
++ }
++
++ if (!spec || strcmp(spec, WSREP_NONE) == 0) {
++ if ((ret = wsrep_dummy_loader(*hptr)) != 0) {
++ free (*hptr);
++ *hptr = NULL;
++ }
++ return ret;
++ }
++
++ if (!(dlh = dlopen(spec, RTLD_NOW | RTLD_LOCAL))) {
++ snprintf(msg, msg_len, "wsrep_load(): dlopen(): %s", dlerror());
++ logger (WSREP_LOG_ERROR, msg);
++ ret = EINVAL;
++ goto out;
++ }
++
++ if (!(dlfun = wsrep_dlf(dlh, "wsrep_loader"))) {
++ ret = EINVAL;
++ goto out;
++ }
++
++ if ((ret = (*dlfun)(*hptr)) != 0) {
++ snprintf(msg, msg_len, "wsrep_load(): loader failed: %s",
++ strerror(ret));
++ logger (WSREP_LOG_ERROR, msg);
++ goto out;
++ }
++
++ if ((ret = verify(*hptr, WSREP_INTERFACE_VERSION)) != 0) {
++ snprintf (msg, msg_len,
++ "wsrep_load(): interface version mismatch: my version %s, "
++ "provider version %s", WSREP_INTERFACE_VERSION,
++ (*hptr)->version);
++ logger (WSREP_LOG_ERROR, msg);
++ goto out;
++ }
++
++ (*hptr)->dlh = dlh;
++
++out:
++ if (ret != 0) {
++ if (dlh) dlclose(dlh);
++ free(*hptr);
++ *hptr = NULL;
++ } else {
++ snprintf (msg, msg_len,
++ "wsrep_load(): %s %s by %s loaded successfully.",
++ (*hptr)->provider_name, (*hptr)->provider_version,
++ (*hptr)->provider_vendor);
++ logger (WSREP_LOG_INFO, msg);
++ }
++
++ return ret;
++}
++
++void wsrep_unload(wsrep_t *hptr)
++{
++ if (!hptr) {
++ logger (WSREP_LOG_WARN, "wsrep_unload(): null pointer.");
++ } else {
++ if (hptr->free)
++ hptr->free(hptr);
++ if (hptr->dlh)
++ dlclose(hptr->dlh);
++ free(hptr);
++ }
++}
++
+diff --git a/wsrep/wsrep_uuid.c b/wsrep/wsrep_uuid.c
+new file mode 100644
+index 0000000..baa95b2
+--- /dev/null
++++ b/wsrep/wsrep_uuid.c
+@@ -0,0 +1,83 @@
++/* Copyright (C) 2009 Codership Oy <info@codersihp.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 of the License.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/*! @file Helper functions to deal with history UUID string representations */
++
++#include <errno.h>
++#include <ctype.h>
++#include <stdio.h>
++
++#include "wsrep_api.h"
++
++/*!
++ * Read UUID from string
++ * @return length of UUID string representation or -EINVAL in case of error
++ */
++int
++wsrep_uuid_scan (const char* str, size_t str_len, wsrep_uuid_t* uuid)
++{
++ unsigned int uuid_len = 0;
++ unsigned int uuid_offt = 0;
++
++ while (uuid_len + 1 < str_len) {
++ /* We are skipping potential '-' after uuid_offt == 4, 6, 8, 10
++ * which means
++ * (uuid_offt >> 1) == 2, 3, 4, 5,
++ * which in turn means
++ * (uuid_offt >> 1) - 2 <= 3
++ * since it is always >= 0, because uuid_offt is unsigned */
++ if (((uuid_offt >> 1) - 2) <= 3 && str[uuid_len] == '-') {
++ // skip dashes after 4th, 6th, 8th and 10th positions
++ uuid_len += 1;
++ continue;
++ }
++
++ if (isxdigit(str[uuid_len]) && isxdigit(str[uuid_len + 1])) {
++ // got hex digit, scan another byte to uuid, increment uuid_offt
++ sscanf (str + uuid_len, "%2hhx", uuid->data + uuid_offt);
++ uuid_len += 2;
++ uuid_offt += 1;
++ if (sizeof (uuid->data) == uuid_offt)
++ return uuid_len;
++ }
++ else {
++ break;
++ }
++ }
++
++ *uuid = WSREP_UUID_UNDEFINED;
++ return -EINVAL;
++}
++
++/*!
++ * Write UUID to string
++ * @return length of UUID string representation or -EMSGSIZE if string is too
++ * short
++ */
++int
++wsrep_uuid_print (const wsrep_uuid_t* uuid, char* str, size_t str_len)
++{
++ if (str_len > 36) {
++ const unsigned char* u = uuid->data;
++ return snprintf(str, str_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
++ "%02x%02x-%02x%02x%02x%02x%02x%02x",
++ u[ 0], u[ 1], u[ 2], u[ 3], u[ 4], u[ 5], u[ 6], u[ 7],
++ u[ 8], u[ 9], u[10], u[11], u[12], u[13], u[14], u[15]);
++ }
++ else {
++ return -EMSGSIZE;
++ }
++}