From: Ivan Suzdal Date: Thu, 24 Sep 2015 17:42:16 +0000 (+0300) Subject: MySQL-wsrep package for centos7. X-Git-Tag: mos-9.0~7 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=e9624fb135fd2bc547d0cb8eb05c62d08045834a;p=packages%2Fcentos7%2FMySQL-wsrep.git MySQL-wsrep package for centos7. Rebuild from packages/centos6/MySQL-wsrep 7.0 branch. Change-Id: I3b099bba87772beb1007156150e48eed586cee69 Related-Bug: #1462998 --- diff --git a/disable_tests.patch b/disable_tests.patch new file mode 100644 index 0000000..7dff43b --- /dev/null +++ b/disable_tests.patch @@ -0,0 +1,7 @@ +--- a/mysql-test/t/disabled.def ++++ b/mysql-test/t/disabled.def +@@ -17,3 +17,4 @@ log_tables-big : Bug#11756699 + ds_mrr-big @solaris : Bug#14168107 2012-04-03 Hemant disabled new test added by Olav Sandstå,since this leads to timeout on Solaris on slow sparc servers + 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 diff --git a/fix-man-page-links.patch b/fix-man-page-links.patch new file mode 100644 index 0000000..4098907 --- /dev/null +++ b/fix-man-page-links.patch @@ -0,0 +1,10 @@ +--- a/man/mysql_client_test_embedded.1 2013-11-08 19:00:22.000000000 +0530 ++++ b/man/mysql_client_test_embedded.1 2013-11-14 19:29:56.768315219 +0530 +@@ -1 +1 @@ +-.so man/mysql_client_test.1 ++.so man1/mysql_client_test.1 +--- a/man/mysqltest_embedded.1 2013-11-08 19:00:22.000000000 +0530 ++++ b/man/mysqltest_embedded.1 2013-11-14 19:31:19.079280675 +0530 +@@ -1 +1 @@ +-.so man/mysqltest.1 ++.so man1/mysqltest.1 diff --git a/fix_standalone_tests.patch b/fix_standalone_tests.patch new file mode 100644 index 0000000..2ca95bf --- /dev/null +++ b/fix_standalone_tests.patch @@ -0,0 +1,12 @@ +--- mysql-5.5.orig/mysql-test/lib/mtr_cases.pm 2011-11-14 15:35:04.238715000 -0800 ++++ mysql-5.5/mysql-test/lib/mtr_cases.pm 2011-11-25 14:41:00.433887578 -0800 +@@ -287,7 +287,8 @@ + else + { + $suitedir= my_find_dir($::basedir, +- ["share/mysql-test/suite", ++ ["lib/mysql-testsuite/suite", ++ "share/mysql-test/suite", + "mysql-test/suite", + "internal/mysql-test/suite", + "mysql-test", diff --git a/mysql-5.6.23.tar.gz b/mysql-5.6.23.tar.gz new file mode 100644 index 0000000..8b3d006 Binary files /dev/null and b/mysql-5.6.23.tar.gz differ diff --git a/mysql-5.6.23_wsrep_25.10.patch b/mysql-5.6.23_wsrep_25.10.patch new file mode 100644 index 0000000..39ff8b9 --- /dev/null +++ b/mysql-5.6.23_wsrep_25.10.patch @@ -0,0 +1,43353 @@ +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 ") ++ LIST(APPEND CMAKE_C_LINK_EXECUTABLE "dsymutil ") ++ LIST(APPEND CMAKE_CXX_CREATE_SHARED_LIBRARY "dsymutil ") ++ LIST(APPEND CMAKE_C_CREATE_SHARED_LIBRARY "dsymutil ") ++ LIST(APPEND CMAKE_CXX_CREATE_SHARED_MODULE "dsymutil ") ++ LIST(APPEND CMAKE_C_CREATE_SHARED_MODULE "dsymutil ") ++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 . ++# ++# 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 ++ 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 + #include +@@ -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.] group to be ++# used for connecting to [mysqld.] ++# ++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.] ++ $self->post_check_client_group($config, ++ 'client', ++ $first_mysqld->name()); ++ ++ # Then generate [client.] for each [mysqld.] ++ 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.] ++# ++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.] section for each ++ # defined [cluster_config.] 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.] ++ $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= ':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= ':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= ':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= ':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= ':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= ':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= ':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= ':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 ++; ; ; 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.keep_pages_size = 0; gcache.mem_size = 0; ; 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.mcast_addr = ; gmcast.mcast_ttl = 1; gmcast.peer_timeout = PT3S; gmcast.segment = 0; gmcast.time_wait = PT5S; gmcast.version = 0; ; 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 ++--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 ++--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 ++--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 = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/base_host = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/base_port = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/gcache\.dir = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/gcache\.name = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/gmcast\.listen_addr = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/ist\.recv_addr = .*?;/;/sgio; ++ $wsrep_provider_options =~ s/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 *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 + #include + ++#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 + #include + #include // 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 ++#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 + #include + ++#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 *var_list); ++#endif + int sys_var_init(); + int sys_var_add_options(std::vector *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 + #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 &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 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) : "") + + #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 ++ 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 ++ ++ 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 ++ ++ 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 ++ ++/* 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 ++ ++ 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 ++ ++ 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 ++ ++ 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 ++#include ++//#include ++//#include ++ ++#include "wsrep_mysqld.h" ++ ++#include ++#include ++#include ++#include ++ ++/* 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(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(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 ++ ++ 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 ++#include "sql_base.h" ++#include "binlog.h" ++#include "rpl_filter.h" ++#include ++#include "wsrep_mysqld.h" ++#include "wsrep_binlog.h" ++#include ++#include ++ ++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 ++ ++ 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 ++#include ++#include ++#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 ++#include ++#include "log_event.h" ++#include ++ ++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(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(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 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 ++ ++ 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 ++#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 ++ ++ 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 ++#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 ++ ++ 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 ++#include ++#include ++ ++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 ++ ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include "wsrep_priv.h" ++#include "wsrep_utils.h" ++#include ++#include ++ ++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(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 ' 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(*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 ++ ++ 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 // 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 ++ ++ 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 ++ ++ 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 ++ ++ 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 ++ ++#include // posix_spawn() ++#include // pipe() ++#include // errno ++#include // strerror() ++#include // waitpid() ++#include ++#include ++#include // getaddrinfo() ++ ++#ifdef HAVE_GETIFADDRS ++#include ++#include ++#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(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(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(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(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 ++ ++ 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 ++ ++ 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 ++#include ++#include ++#include ++#include ++#include "wsrep_priv.h" ++#include "wsrep_thd.h" ++#include ++#include ++#include ++ ++#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 ++ ++ 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 ++#include ++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 ++#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, /*!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; jreferenced_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; itable_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; is->keys; ++i) { ++ KEY* key_info = table->key_info + i; ++ if (key_info->flags & HA_NOSAME) { ++ hasPK = true; ++ } ++ } ++ ++ for (i=0; is->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 ++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 // 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 + ++#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(mode), ++ block, heap_no, trx))) { ++#else + } else if (lock_rec_other_has_conflicting( + static_cast(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( ++ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION), ++ block, next_rec_heap_no, trx))) { ++#else + if (lock_rec_other_has_conflicting( + static_cast( + 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 ++#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 + + /* 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 ++Packager: Codership Oy + 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 ++- 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 ++- 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 ++ ++- Reworked to build wsrep patched packages exclusively ++- OBS compatible ++ + * Mon Oct 06 2014 Balasubramanian Kandasamy + - 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 ++* Thu Feb 10 2011 Joerg Bruehe + + - 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 ++* Fri Oct 09 2009 Magnus Blaudd + + - Removed mysql_fix_privilege_tables + +@@ -1668,7 +1666,7 @@ echo "=====" >> $STATUS_HISTORY + + - Set $LDFLAGS from $MYSQL_BUILD_LDFLAGS + +-* Tue Mar 07 2006 Kent Boortz ++* Wed Mar 08 2006 Kent Boortz + + - Changed product name from "Community Edition" to "Community Server" + +@@ -1763,7 +1761,7 @@ echo "=====" >> $STATUS_HISTORY + + * Thu Sep 29 2005 Lenz Grimmer + +-- 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 +@@ -1916,7 +1914,7 @@ echo "=====" >> $STATUS_HISTORY + + - marked /etc/logrotate.d/mysql as a config file (BUG 2156) + +-* Sat Dec 13 2003 Lenz Grimmer ++* Fri Dec 12 2003 Lenz Grimmer + + - 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 ++* Wed Sep 29 1999 David Axmark + + - 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 [: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: ++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 [: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: ++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 ++ ++ 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 ++#include ++#include ++#include ++#include ++ ++#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 ++ ++ 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 ++#include ++#include ++ ++/*! 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 ", ++ &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 ++ ++ 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 ++#include ++#include ++#include ++ ++#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 ++ ++ 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 ++#include ++#include ++#include ++ ++#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 ++ ++ 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 ++#include ++#include ++ ++#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; ++ } ++} diff --git a/mysql.spec b/mysql.spec new file mode 100644 index 0000000..c8ec694 --- /dev/null +++ b/mysql.spec @@ -0,0 +1,2117 @@ +# Copyright (c) 2000, 2014, 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 +# 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. + +############################################################################## +# Some common macro definitions +############################################################################## + +# NOTE: "vendor" is used in upgrade/downgrade check, so you can't +# change these, has to be exactly as is. +%global mysql_old_vendor MySQL AB +%global mysql_vendor_2 Sun Microsystems, Inc. +%global mysql_vendor Oracle and/or its affiliates + +%global mysql_version 5.6.23 +%global wsrep_version 25.10 +%global wsrep_revision XXXX + +%global mysqld_user mysql +%global mysqld_group mysql +%global mysqldatadir /var/lib/mysql + +%global release %{wsrep_version} +%global short_product_tag 5.6 + +# +# Macros we use which are not available in all supported versions of RPM +# +# - defined/undefined are missing on RHEL4 +# +%if %{expand:%{?defined:0}%{!?defined:1}} +%define defined() %{expand:%%{?%{1}:1}%%{!?%{1}:0}} +%endif +%if %{expand:%{?undefined:0}%{!?undefined:1}} +%define undefined() %{expand:%%{?%{1}:0}%%{!?%{1}:1}} +%endif + +# ---------------------------------------------------------------------------- +# RPM build tools now automatically detect Perl module dependencies. This +# detection causes problems as it is broken in some versions, and it also +# provides unwanted dependencies from mandatory scripts in our package. +# It might not be possible to disable this in all versions of RPM, but here we +# try anyway. We keep the "AutoReqProv: no" for the "test" sub package, as +# disabling here might fail, and that package has the most problems. +# See: +# http://fedoraproject.org/wiki/Packaging/Perl#Filtering_Requires:_and_Provides +# http://www.wideopen.com/archives/rpm-list/2002-October/msg00343.html +# ---------------------------------------------------------------------------- +%undefine __perl_provides +%undefine __perl_requires + +############################################################################## +# Command line handling +############################################################################## +# +# To set options: +# +# $ rpmbuild --define="option " ... +# + +# ---------------------------------------------------------------------------- +# Commercial builds +# ---------------------------------------------------------------------------- +%if %{undefined commercial} +%define commercial 0 +%endif + +# ---------------------------------------------------------------------------- +# Source name +# ---------------------------------------------------------------------------- +%if %{undefined src_base} +#%define src_base mysql-wsrep +%define src_base mysql +%endif +#%define src_dir %{src_base}-%{mysql_version}-%{wsrep_version} +%define src_dir %{src_base}-%{mysql_version} + +# ---------------------------------------------------------------------------- +# Feature set (storage engines, options). Default to community (everything) +# ---------------------------------------------------------------------------- +%if %{undefined feature_set} +%define feature_set community +%endif + +# ---------------------------------------------------------------------------- +# Server comment strings +# ---------------------------------------------------------------------------- +%if %{undefined compilation_comment_debug} +%define compilation_comment_debug MySQL Community Server - Debug (GPL) +%endif +%if %{undefined compilation_comment_release} +%define compilation_comment_release MySQL Community Server (GPL) +%endif + +# ---------------------------------------------------------------------------- +# Product and server suffixes +# ---------------------------------------------------------------------------- +%if %{undefined product_suffix} + %if %{defined short_product_tag} + %define product_suffix -%{short_product_tag} + %else + %define product_suffix %{nil} + %endif +%endif + +%if %{undefined server_suffix} +%define server_suffix %{nil} +%endif + +# ---------------------------------------------------------------------------- +# Distribution support +# ---------------------------------------------------------------------------- + +# 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 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 +%endif + + +# Avoid debuginfo RPMs, leaves binaries unstripped +%define debug_package %{nil} + +# Hack to work around bug in RHEL5 __os_install_post macro, wrong inverted +# test for __debug_package +%define __strip /bin/true + +# ---------------------------------------------------------------------------- +# Support optional "tcmalloc" library (experimental) +# ---------------------------------------------------------------------------- +%if %{defined malloc_lib_target} +%define WITH_TCMALLOC 1 +%else +%define WITH_TCMALLOC 0 +%endif + +############################################################################## +# Configuration based upon above user input, not to be set directly +############################################################################## + +%if 0%{?commercial} +%define license_files_server %{src_dir}/LICENSE.mysql +%define license_type Commercial +%else +%define license_files_server COPYING README +%define license_type GPL +%endif + +############################################################################## +# Main spec file section +############################################################################## + +Name: MySQL%{product_suffix} +Summary: MySQL: a very fast and reliable SQL database server +Group: Applications/Databases +Version: 5.6.23_wsrep_25.10 +Release: %{release}%{?dist}~mos8.0.1 +# Distribution: %{distro_description} +License: Copyright (c) 2000, 2015, %{mysql_vendor}. All rights reserved. Under %{license_type} license as shown in the Description field. +Source: mysql-5.6.23.tar.gz +URL: http://www.mysql.com/ +Packager: Codership Oy +Vendor: %{mysql_vendor} +# BuildRequires: %{distro_buildreq} +#wsrep_patch_tag +Patch0: fix-man-page-links.patch +Patch1: scripts__mysqld_safe.sh__signals.patch +Patch2: disable_tests.patch +Patch3: fix_standalone_tests.patch +Patch4: spelling.patch +Patch5: mysql-5.6.23_wsrep_25.10.patch +Patch6: wsrep_sst_mysqldump.patch + +# Regression tests may take a long time, override the default to skip them +%{!?runselftest:%global runselftest 0} + +# Think about what you use here since the first step is to +# run a rm -rf +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +# From the manual +%description +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 +as for embedding into mass-deployed software. MySQL is a trademark of +%{mysql_vendor} + +The MySQL software has Dual Licensing, which means you can use the MySQL +software free of charge under the GNU General Public License +(http://www.gnu.org/licenses/). You can also purchase commercial MySQL +licenses from %{mysql_vendor} if you do not wish to be bound by the terms of +the GPL. See the chapter "Licensing and Support" in the manual for +further info. + +The MySQL web site (http://www.mysql.com/) provides the latest +news and information about the MySQL software. Also please see the +documentation and the manual for more information. + +############################################################################## +# Sub package definition +############################################################################## + +%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 +# 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 +Obsoletes: MySQL-server-advanced +%endif +Obsoletes: mysql-server < %{version}-%{release} +Obsoletes: mysql-server-advanced +Obsoletes: MySQL-server-classic MySQL-server-community MySQL-server-enterprise +Obsoletes: MySQL-server-advanced-gpl MySQL-server-enterprise-gpl +Obsoletes: mariadb-libs mariadb-server +Provides: mysql-server = %{version}-%{release} +Provides: mysql-server%{?_isa} = %{version}-%{release} + +%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 +as for embedding into mass-deployed software. MySQL is a trademark of +%{mysql_vendor} + +The MySQL software has Dual Licensing, which means you can use the MySQL +software free of charge under the GNU General Public License +(http://www.gnu.org/licenses/). You can also purchase commercial MySQL +licenses from %{mysql_vendor} if you do not wish to be bound by the terms of +the GPL. See the chapter "Licensing and Support" in the manual for +further info. + +The MySQL web site (http://www.mysql.com/) provides the latest news and +information about the MySQL software. Also please see the documentation +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-wsrep-client%{product_suffix}" as well! + +# ---------------------------------------------------------------------------- +%package -n mysql-wsrep-client%{product_suffix} +Summary: MySQL - Client +Group: Applications/Databases +%if 0%{?commercial} +Obsoletes: MySQL-client +%else +Obsoletes: MySQL-client-advanced +%endif +Obsoletes: mysql < %{version}-%{release} +Obsoletes: mysql-advanced < %{version}-%{release} +Obsoletes: MySQL-client-classic MySQL-client-community MySQL-client-enterprise +Obsoletes: MySQL-client-advanced-gpl MySQL-client-enterprise-gpl +Provides: mysql = %{version}-%{release} +Provides: mysql%{?_isa} = %{version}-%{release} + +%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-wsrep-test%{product_suffix} +Summary: MySQL - Test suite +Group: Applications/Databases +%if 0%{?commercial} +#Requires: MySQL-client-advanced perl +Requires: mysql-wsrep-client%{product_suffix} perl +Obsoletes: MySQL-test +%else +#Requires: MySQL-client perl +Requires: mysql-wsrep-client%{product_suffix} perl +Obsoletes: MySQL-test-advanced +%endif +Obsoletes: mysql-test < %{version}-%{release} +Obsoletes: mysql-test-advanced +Obsoletes: MySQL-test-classic MySQL-test-community MySQL-test-enterprise +Obsoletes: MySQL-test-advanced-gpl MySQL-test-enterprise-gpl +Provides: mysql-test = %{version}-%{release} +Provides: mysql-test%{?_isa} = %{version}-%{release} +AutoReqProv: no + +%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-wsrep-devel%{product_suffix} +Summary: MySQL - Development header files and libraries +Group: Applications/Databases +%if 0%{?commercial} +Obsoletes: MySQL-devel +%else +Obsoletes: MySQL-devel-advanced +%endif +Obsoletes: mysql-devel < %{version}-%{release} +Obsoletes: mysql-embedded-devel mysql-devel-advanced mysql-embedded-devel-advanced +Obsoletes: MySQL-devel-classic MySQL-devel-community MySQL-devel-enterprise +Obsoletes: MySQL-devel-advanced-gpl MySQL-devel-enterprise-gpl +Provides: mysql-devel = %{version}-%{release} +Provides: mysql-devel%{?_isa} = %{version}-%{release} + +%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-wsrep-shared%{product_suffix} +Summary: MySQL - Shared libraries +Group: Applications/Databases +%if 0%{?commercial} +Obsoletes: MySQL-shared +%else +Obsoletes: MySQL-shared-advanced +%endif +Obsoletes: MySQL-shared-standard MySQL-shared-pro +Obsoletes: MySQL-shared-pro-cert MySQL-shared-pro-gpl +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-wsrep-shared%{product_suffix} +This package contains the shared libraries (*.so*) which certain languages +and applications need to dynamically load and use MySQL. + +############################################################################## +%prep +%setup -q -T -a 0 -c -n %{src_dir} +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +#wsrep_apply_patch_tag +############################################################################## +%build + +# Fail quickly and obviously if user tries to build as root +%if %runselftest + if [ x"`id -u`" = x0 ]; then + echo "The MySQL regression tests may fail if run as root." + echo "If you really need to build the RPM as root, use" + echo "--define='runselftest 0' to skip the regression tests." + exit 1 + fi +%endif + +# Be strict about variables, bail at earliest opportunity, etc. +set -eu + +# Optional package files +touch optional-files-devel + +# +# Set environment in order of preference, MYSQL_BUILD_* first, then variable +# name, finally a default. RPM_OPT_FLAGS is assumed to be a part of the +# default RPM build environment. +# + +# This is a hack, $RPM_OPT_FLAGS on ia64 hosts contains flags which break +# the compile in cmd-line-utils/libedit - needs investigation, but for now +# we simply unset it and use those specified directly in cmake. +%if "%{_arch}" == "ia64" +RPM_OPT_FLAGS= +%endif + +export PATH=${MYSQL_BUILD_PATH:-$PATH} +export CC=${MYSQL_BUILD_CC:-${CC:-gcc}} +export CXX=${MYSQL_BUILD_CXX:-${CXX:-g++}} +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:--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. +# Protect against undefined variables if there is no override option. +%if %{undefined with_ssl} +%define ssl_option %{nil} +%else +%define ssl_option -DWITH_SSL=%{with_ssl} +%endif + +# Build debug mysqld and libmysqld.a +mkdir debug +( + cd debug + # Attempt to remove any optimisation flags from the debug build + 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/^ //' \ + -e 's/ $//'` + 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} ../ -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}" \ + -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 +) +# Build full release +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} ../ -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}" \ + -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 +) + +%if %runselftest + MTR_BUILD_THREAD=auto + export MTR_BUILD_THREAD + + (cd release && make test-bt-fast || true) +%endif + +############################################################################## +%install + +RBR=$RPM_BUILD_ROOT +MBD=$RPM_BUILD_DIR/%{src_dir} + +# Ensure that needed directories exists +install -d $RBR%{_sysconfdir}/{logrotate.d,init.d} +install -d $RBR%{mysqldatadir}/mysql +install -d $RBR%{_datadir}/mysql-test +install -d $RBR%{_datadir}/mysql/SELinux/RHEL4 +install -d $RBR%{_includedir} +install -d $RBR%{_libdir} +install -d $RBR%{_mandir} +install -d $RBR%{_sbindir} + +mkdir -p $RBR%{_sysconfdir}/my.cnf.d + +# Install all binaries +( + cd $MBD/release + make DESTDIR=$RBR install +) + +# FIXME: at some point we should stop doing this and just install everything +# FIXME: directly into %{_libdir}/mysql - perhaps at the same time as renaming +# FIXME: the shared libraries to use libmysql*-$major.$minor.so syntax +mv -v $RBR/%{_libdir}/*.a $RBR/%{_libdir}/mysql/ + +# Install logrotate and autostart +install -m 644 $MBD/release/support-files/mysql-log-rotate $RBR%{_sysconfdir}/logrotate.d/mysql +install -m 755 $MBD/release/support-files/mysql.server $RBR%{_sysconfdir}/init.d/mysql + +# Create a symlink "rcmysql", pointing to the init.script. SuSE users +# will appreciate that, as all services usually offer this. +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/support-files/RHEL4-SElinux/mysql.{fc,te} \ + $RBR%{_datadir}/mysql/SELinux/RHEL4 + +%if %{WITH_TCMALLOC} +# Even though this is a shared library, put it under /usr/lib*/mysql, so it +# doesn't conflict with possible shared lib by the same name in /usr/lib*. See +# `mysql_config --variable=pkglibdir` and mysqld_safe for how this is used. +install -m 644 "%{malloc_lib_source}" \ + "$RBR%{_libdir}/mysql/%{malloc_lib_target}" +%endif + +# Remove man pages we explicitly do not want to package, avoids 'unpackaged +# files' warning. +# This has become obsolete: rm -f $RBR%{_mandir}/man1/make_win_bin_dist.1* + +############################################################################## +# Post processing actions, i.e. when installed +############################################################################## + +%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. + +# ATTENTION: Parts of this are duplicated in the "triggerpostun" ! + +# There are users who deviate from the default file system layout. +# Check local settings to support them. +if [ -x %{_bindir}/my_print_defaults ] +then + mysql_datadir=`%{_bindir}/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p'` + PID_FILE_PATT=`%{_bindir}/my_print_defaults server mysqld | grep '^--pid-file=' | sed -n 's/--pid-file=//p'` +fi +if [ -z "$mysql_datadir" ] +then + mysql_datadir=%{mysqldatadir} +fi +if [ -z "$PID_FILE_PATT" ] +then + PID_FILE_PATT="$mysql_datadir/*.pid" +fi + +# Check if we can safely upgrade. An upgrade is only safe if it's from one +# of our RPMs in the same version family. + +# Handle both ways of spelling the capability. +installed=`rpm -q --whatprovides mysql-server 2> /dev/null` +if [ $? -ne 0 -o -z "$installed" ]; then + installed=`rpm -q --whatprovides MySQL-server 2> /dev/null` +fi +if [ $? -eq 0 -a -n "$installed" ]; then + installed=`echo $installed | sed 's/\([^ ]*\) .*/\1/'` # Tests have shown duplicated package names + vendor=`rpm -q --queryformat='%{VENDOR}' "$installed" 2>&1` + version=`rpm -q --queryformat='%{VERSION}' "$installed" 2>&1` + myoldvendor='%{mysql_old_vendor}' + myvendor_2='%{mysql_vendor_2}' + myvendor='%{mysql_vendor}' + myversion='%{mysql_version}' + + old_family=`echo $version \ + | sed -n -e 's,^\([1-9][0-9]*\.[0-9][0-9]*\)\..*$,\1,p'` + new_family=`echo $myversion \ + | sed -n -e 's,^\([1-9][0-9]*\.[0-9][0-9]*\)\..*$,\1,p'` + + [ -z "$vendor" ] && vendor='' + [ -z "$old_family" ] && old_family="" + [ -z "$new_family" ] && new_family="" + + error_text= + if [ "$vendor" != "$myoldvendor" \ + -a "$vendor" != "$myvendor_2" \ + -a "$vendor" != "$myvendor" ]; then + error_text="$error_text +The current MySQL server package is provided by a different +vendor ($vendor) than $myoldvendor, $myvendor_2, or $myvendor. +Some files may be installed to different locations, including log +files and the service startup script in %{_sysconfdir}/init.d/. +" + fi + + if [ "$old_family" != "$new_family" ]; then + error_text="$error_text +Upgrading directly from MySQL $old_family to MySQL $new_family may not +be safe in all cases. A manual dump and restore using mysqldump is +recommended. It is important to review the MySQL manual's Upgrading +section for version-specific incompatibilities. +" + fi + + if [ -n "$error_text" ]; then + cat <&2 + +****************************************************************** +A MySQL server package ($installed) is installed. +$error_text +A manual upgrade is required. + +- Ensure that you have a complete, working backup of your data and my.cnf + files +- Shut down the MySQL server cleanly +- Remove the existing MySQL packages. Usually this command will + list the packages you should remove: + rpm -qa | grep -i '^mysql-' + + You may choose to use 'rpm --nodeps -ev ' to remove + the package which contains the mysqlclient shared library. The + library will be reinstalled by the MySQL-shared-compat package. +- Install the new MySQL packages supplied by $myvendor +- Ensure that the MySQL server is started +- Run the 'mysql_upgrade' program + +This is a brief description of the upgrade process. Important details +can be found in the MySQL manual, in the Upgrading section. +****************************************************************** +HERE + exit 1 + fi +fi + +# We assume that if there is exactly one ".pid" file, +# it contains the valid PID of a running MySQL server. +NR_PID_FILES=`ls -1 $PID_FILE_PATT 2>/dev/null | wc -l` +case $NR_PID_FILES in + 0 ) SERVER_TO_START='' ;; # No "*.pid" file == no running server + 1 ) SERVER_TO_START='true' ;; + * ) SERVER_TO_START='' # Situation not clear + SEVERAL_PID_FILES=true ;; +esac +# That logic may be debated: We might check whether it is non-empty, +# contains exactly one number (possibly a PID), and whether "ps" finds it. +# OTOH, if there is no such process, it means a crash without a cleanup - +# is that a reason not to start a new server after upgrade? + +STATUS_FILE=$mysql_datadir/RPM_UPGRADE_MARKER + +if [ -f $STATUS_FILE ]; then + echo "Some previous upgrade was not finished:" + ls -ld $STATUS_FILE + echo "Please check its status, then do" + echo " rm $STATUS_FILE" + echo "before repeating the MySQL upgrade." + exit 1 +elif [ -n "$SEVERAL_PID_FILES" ] ; then + echo "You have more than one PID file:" + ls -ld $PID_FILE_PATT + echo "Please check which one (if any) corresponds to a running server" + echo "and delete all others before repeating the MySQL upgrade." + exit 1 +fi + +NEW_VERSION=%{mysql_version}-%{release} + +# The "pre" section code is also run on a first installation, +# when there is no data directory yet. Protect against error messages. +# Check for the existence of subdirectory "mysql/", the database of system +# tables like "mysql.user". +if [ -d $mysql_datadir/mysql ] ; then + echo "MySQL RPM upgrade to version $NEW_VERSION" > $STATUS_FILE + echo "'pre' step running at `date`" >> $STATUS_FILE + echo >> $STATUS_FILE + fcount=`ls -ltr $mysql_datadir/*.err 2>/dev/null | wc -l` + if [ $fcount -gt 0 ] ; then + echo "ERR file(s):" >> $STATUS_FILE + ls -ltr $mysql_datadir/*.err >> $STATUS_FILE + echo >> $STATUS_FILE + echo "Latest 'Version' line in latest file:" >> $STATUS_FILE + grep '^Version' `ls -tr $mysql_datadir/*.err | tail -1` | \ + tail -1 >> $STATUS_FILE + echo >> $STATUS_FILE + fi + + if [ -n "$SERVER_TO_START" ] ; then + # There is only one PID file, race possibility ignored + echo "PID file:" >> $STATUS_FILE + ls -l $PID_FILE_PATT >> $STATUS_FILE + cat $PID_FILE_PATT >> $STATUS_FILE + echo >> $STATUS_FILE + echo "Server process:" >> $STATUS_FILE + ps -fp `cat $PID_FILE_PATT` >> $STATUS_FILE + echo >> $STATUS_FILE + echo "SERVER_TO_START=$SERVER_TO_START" >> $STATUS_FILE + else + # Take a note we checked it ... + echo "PID file:" >> $STATUS_FILE + ls -l $PID_FILE_PATT >> $STATUS_FILE 2>&1 + fi +fi + +# Shut down a previously installed server first +# Note we *could* make that depend on $SERVER_TO_START, but we rather don't, +# so a "stop" is attempted even if there is no PID file. +# (Maybe the "stop" doesn't work then, but we might fix that in itself.) +if [ -x %{_sysconfdir}/init.d/mysql ] ; then + %{_sysconfdir}/init.d/mysql stop > /dev/null 2>&1 + echo "Giving mysqld 5 seconds to exit nicely" + sleep 5 +fi + +%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. + +# ATTENTION: Parts of this are duplicated in the "triggerpostun" ! + +# There are users who deviate from the default file system layout. +# Check local settings to support them. +if [ -x %{_bindir}/my_print_defaults ] +then + mysql_datadir=`%{_bindir}/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p'` +fi +if [ -z "$mysql_datadir" ] +then + mysql_datadir=%{mysqldatadir} +fi + +NEW_VERSION=%{mysql_version}-%{release} +STATUS_FILE=$mysql_datadir/RPM_UPGRADE_MARKER + +# ---------------------------------------------------------------------- +# Create data directory if needed, check whether upgrade or install +# ---------------------------------------------------------------------- +if [ ! -d $mysql_datadir ] ; then mkdir -m 755 $mysql_datadir; fi +if [ -f $STATUS_FILE ] ; then + SERVER_TO_START=`grep '^SERVER_TO_START=' $STATUS_FILE | cut -c17-` +else + SERVER_TO_START='' +fi +# echo "Analyzed: SERVER_TO_START=$SERVER_TO_START" +if [ ! -d $mysql_datadir/mysql ] ; then + mkdir $mysql_datadir/mysql $mysql_datadir/test + echo "MySQL RPM installation of version $NEW_VERSION" >> $STATUS_FILE +else + # If the directory exists, we may assume it is an upgrade. + echo "MySQL RPM upgrade to version $NEW_VERSION" >> $STATUS_FILE +fi + +# ---------------------------------------------------------------------- +# Make MySQL start/shutdown automatically when the machine does it. +# ---------------------------------------------------------------------- +# NOTE: This still needs to be debated. Should we check whether these links +# for the other run levels exist(ed) before the upgrade? +# use chkconfig on Enterprise Linux and newer SuSE releases +if [ -x /sbin/chkconfig ] ; then + /sbin/chkconfig --add mysql +# use insserv for older SuSE Linux versions +elif [ -x /sbin/insserv ] ; then + /sbin/insserv %{_sysconfdir}/init.d/mysql +fi + +# ---------------------------------------------------------------------- +# Create a MySQL user and group. Do not report any problems if it already +# exists. +# ---------------------------------------------------------------------- +groupadd -r %{mysqld_group} 2> /dev/null || true +useradd -M -r -d $mysql_datadir -s /bin/bash -c "MySQL server" \ + -g %{mysqld_group} %{mysqld_user} 2> /dev/null || true +# The user may already exist, make sure it has the proper group nevertheless +# (BUG#12823) +usermod -g %{mysqld_group} %{mysqld_user} 2> /dev/null || true + +# ---------------------------------------------------------------------- +# Change permissions so that the user that will run the MySQL daemon +# owns all database files. +# ---------------------------------------------------------------------- +chown -R %{mysqld_user}:%{mysqld_group} $mysql_datadir + +# ---------------------------------------------------------------------- +# Initiate databases if needed +# ---------------------------------------------------------------------- +if ! grep '^MySQL RPM upgrade' $STATUS_FILE >/dev/null 2>&1 ; then + # Fix bug#45415: no "mysql_install_db" on an upgrade + # Do this as a negative to err towards more "install" runs + # rather than to miss one. + %{_bindir}/mysql_install_db --rpm --user=%{mysqld_user} --random-passwords + + # Attention: Now 'root' is the only database user, + # its password is a random value found in ~/.mysql_secret, + # and the "password expired" flag is set: + # Any client needs that password, and the first command + # executed must be a new "set password"! +fi + +# ---------------------------------------------------------------------- +# Upgrade databases if needed would go here - but it cannot be automated yet +# ---------------------------------------------------------------------- + +# ---------------------------------------------------------------------- +# Change permissions again to fix any new files. +# ---------------------------------------------------------------------- +chown -R %{mysqld_user}:%{mysqld_group} $mysql_datadir + +# ---------------------------------------------------------------------- +# Fix permissions for the permission database so that only the user +# can read them. +# ---------------------------------------------------------------------- +chmod -R og-rw $mysql_datadir/mysql + +# ---------------------------------------------------------------------- +# install SELinux files - but don't override existing ones +# ---------------------------------------------------------------------- +SETARGETDIR=/etc/selinux/targeted/src/policy +SEDOMPROG=$SETARGETDIR/domains/program +SECONPROG=$SETARGETDIR/file_contexts/program +if [ -f /etc/redhat-release ] \ + && (grep -q "Red Hat Enterprise Linux .. release 4" /etc/redhat-release \ + || grep -q "CentOS release 4" /etc/redhat-release) ; then + echo + echo + echo 'Notes regarding SELinux on this platform:' + echo '=========================================' + echo + echo 'The default policy might cause server startup to fail because it is' + echo 'not allowed to access critical files. In this case, please update' + echo 'your installation.' + echo + echo 'The default policy might also cause inavailability of SSL related' + echo 'features because the server is not allowed to access /dev/random' + echo 'and /dev/urandom. If this is a problem, please do the following:' + echo + echo ' 1) install selinux-policy-targeted-sources from your OS vendor' + echo ' 2) add the following two lines to '$SEDOMPROG/mysqld.te':' + echo ' allow mysqld_t random_device_t:chr_file read;' + echo ' allow mysqld_t urandom_device_t:chr_file read;' + echo ' 3) cd to '$SETARGETDIR' and issue the following command:' + echo ' make load' + echo + echo +fi + +if [ -x sbin/restorecon ] ; then + sbin/restorecon -R var/lib/mysql +fi + +# Was the server running before the upgrade? If so, restart the new one. +if [ "$SERVER_TO_START" = "true" ] ; then + # Restart in the same way that mysqld will be started normally. + if [ -x %{_sysconfdir}/init.d/mysql ] ; then + %{_sysconfdir}/init.d/mysql start + echo "Giving mysqld 5 seconds to start" + sleep 5 + fi +fi + +# Collect an upgrade history ... +echo "Upgrade/install finished at `date`" >> $STATUS_FILE +echo >> $STATUS_FILE +echo "=====" >> $STATUS_FILE +STATUS_HISTORY=$mysql_datadir/RPM_UPGRADE_HISTORY +cat $STATUS_FILE >> $STATUS_HISTORY +mv -f $STATUS_FILE ${STATUS_FILE}-LAST # for "triggerpostun" + + +#echo "Thank you for installing the MySQL Community Server! For Production +#systems, we recommend MySQL Enterprise, which contains enterprise-ready +#software, intelligent advisory services, and full production support with +#scheduled service packs and more. Visit www.mysql.com/enterprise for more +#information." + +%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. +# Action Count +# Install the first time 1 +# Upgrade 2 or higher (depending on the number of versions installed) +# Remove last version of package 0 " +# +# http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch09s04s05.html + +if [ $1 = 0 ] ; then + # Stop MySQL before uninstalling it + if [ -x %{_sysconfdir}/init.d/mysql ] ; then + %{_sysconfdir}/init.d/mysql stop > /dev/null + # Remove autostart of MySQL + # use chkconfig on Enterprise Linux and newer SuSE releases + if [ -x /sbin/chkconfig ] ; then + /sbin/chkconfig --del mysql + # For older SuSE Linux versions + elif [ -x /sbin/insserv ] ; then + /sbin/insserv -r %{_sysconfdir}/init.d/mysql + fi + fi +fi + +# We do not remove the mysql user since it may still own a lot of +# database files. + +%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. +# Problem: RPM will first run the "pre" and "post" sections of this script, +# and only then the "preun" of that old community server. +# But this "preun" includes stopping the server and uninstalling the service, +# "chkconfig --del mysql" which removes the symlinks to the start script. +# Solution: *After* the community server got removed, restart this server +# and re-install the service. +# +# For information about triggers in spec files, see the Fedora docs: +# http://docs.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch10s02.html +# For all details of this code, see the "pre" and "post" sections. + +# There are users who deviate from the default file system layout. +# Check local settings to support them. +if [ -x %{_bindir}/my_print_defaults ] +then + mysql_datadir=`%{_bindir}/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p'` +fi +if [ -z "$mysql_datadir" ] +then + mysql_datadir=%{mysqldatadir} +fi + +NEW_VERSION=%{mysql_version}-%{release} +STATUS_FILE=$mysql_datadir/RPM_UPGRADE_MARKER-LAST # Note the difference! +STATUS_HISTORY=$mysql_datadir/RPM_UPGRADE_HISTORY + +if [ -f $STATUS_FILE ] ; then + SERVER_TO_START=`grep '^SERVER_TO_START=' $STATUS_FILE | cut -c17-` +else + # This should never happen, but let's be prepared + SERVER_TO_START='' +fi +echo "Analyzed: SERVER_TO_START=$SERVER_TO_START" + +if [ -x /sbin/chkconfig ] ; then + /sbin/chkconfig --add mysql +# use insserv for older SuSE Linux versions +elif [ -x /sbin/insserv ] ; then + /sbin/insserv %{_sysconfdir}/init.d/mysql +fi + +# Was the server running before the upgrade? If so, restart the new one. +if [ "$SERVER_TO_START" = "true" ] ; then + # Restart in the same way that mysqld will be started normally. + if [ -x %{_sysconfdir}/init.d/mysql ] ; then + %{_sysconfdir}/init.d/mysql start + echo "Giving mysqld 5 seconds to start" + sleep 5 + fi +fi + +echo "Trigger 'postun --community' finished at `date`" >> $STATUS_HISTORY +echo >> $STATUS_HISTORY +echo "=====" >> $STATUS_HISTORY + + +# ---------------------------------------------------------------------- +# Clean up the BuildRoot after build is done +# ---------------------------------------------------------------------- +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && [ -d $RPM_BUILD_ROOT ] \ + && rm -rf $RPM_BUILD_ROOT; + +############################################################################## +# Files section +############################################################################## + +%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 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* +%endif + +%doc %attr(644, root, man) %{_mandir}/man1/innochecksum.1* +%doc %attr(644, root, man) %{_mandir}/man1/my_print_defaults.1* +%doc %attr(644, root, man) %{_mandir}/man1/myisam_ftdump.1* +%doc %attr(644, root, man) %{_mandir}/man1/myisamchk.1* +%doc %attr(644, root, man) %{_mandir}/man1/myisamlog.1* +%doc %attr(644, root, man) %{_mandir}/man1/myisampack.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_convert_table_format.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_fix_extensions.1* +%doc %attr(644, root, man) %{_mandir}/man8/mysqld.8* +%doc %attr(644, root, man) %{_mandir}/man1/mysqld_multi.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqld_safe.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqldumpslow.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_install_db.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_plugin.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_secure_installation.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_setpermission.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_upgrade.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlhotcopy.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlman.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql.server.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqltest.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_tzinfo_to_sql.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_zap.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlbug.1* +%doc %attr(644, root, man) %{_mandir}/man1/perror.1* +%doc %attr(644, root, man) %{_mandir}/man1/replace.1* +%doc %attr(644, root, man) %{_mandir}/man1/resolve_stack_dump.1* +%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 +%attr(755, root, root) %{_bindir}/my_print_defaults +%attr(755, root, root) %{_bindir}/myisam_ftdump +%attr(755, root, root) %{_bindir}/myisamchk +%attr(755, root, root) %{_bindir}/myisamlog +%attr(755, root, root) %{_bindir}/myisampack +%attr(755, root, root) %{_bindir}/mysql_convert_table_format +%attr(755, root, root) %{_bindir}/mysql_fix_extensions +%attr(755, root, root) %{_bindir}/mysql_install_db +%attr(755, root, root) %{_bindir}/mysql_plugin +%attr(755, root, root) %{_bindir}/mysql_secure_installation +%attr(755, root, root) %{_bindir}/mysql_setpermission +%attr(755, root, root) %{_bindir}/mysql_tzinfo_to_sql +%attr(755, root, root) %{_bindir}/mysql_upgrade +%attr(755, root, root) %{_bindir}/mysql_zap +%attr(755, root, root) %{_bindir}/mysqlbug +%attr(755, root, root) %{_bindir}/mysqld_multi +%attr(755, root, root) %{_bindir}/mysqld_safe +%attr(755, root, root) %{_bindir}/mysqldumpslow +%attr(755, root, root) %{_bindir}/mysqlhotcopy +%attr(755, root, root) %{_bindir}/mysqltest +%attr(755, root, root) %{_bindir}/perror +%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} +%attr(755, root, root) %{_libdir}/mysql/%{malloc_lib_target} +%endif + +%attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql +%attr(755, root, root) %{_sysconfdir}/init.d/mysql +%attr(755, root, root) %{_datadir}/mysql/ +%dir %attr(755, mysql, mysql) /var/lib/mysql + +# ---------------------------------------------------------------------------- +%files -n mysql-wsrep-client%{product_suffix} + +%defattr(-, root, root, 0755) +%if %{defined license_files_server} +%doc %{license_files_server} +%endif +%attr(755, root, root) %{_bindir}/msql2mysql +%attr(755, root, root) %{_bindir}/mysql +%attr(755, root, root) %{_bindir}/mysql_find_rows +%attr(755, root, root) %{_bindir}/mysql_waitpid +%attr(755, root, root) %{_bindir}/mysqlaccess +# XXX: This should be moved to %{_sysconfdir} +%attr(644, root, root) %{_bindir}/mysqlaccess.conf +%attr(755, root, root) %{_bindir}/mysqladmin +%attr(755, root, root) %{_bindir}/mysqlbinlog +%attr(755, root, root) %{_bindir}/mysqlcheck +%attr(755, root, root) %{_bindir}/mysqldump +%attr(755, root, root) %{_bindir}/mysqlimport +%attr(755, root, root) %{_bindir}/mysqlshow +%attr(755, root, root) %{_bindir}/mysqlslap +%attr(755, root, root) %{_bindir}/mysql_config_editor + +%doc %attr(644, root, man) %{_mandir}/man1/msql2mysql.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_find_rows.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_waitpid.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlaccess.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqladmin.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlbinlog.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlcheck.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqldump.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlimport.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlshow.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysqlslap.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_config_editor.1* + +# ---------------------------------------------------------------------------- +%files -n mysql-wsrep-devel%{product_suffix} -f optional-files-devel +%defattr(-, root, root, 0755) +%if %{defined license_files_server} +%doc %{license_files_server} +%endif +%doc %attr(644, root, man) %{_mandir}/man1/comp_err.1* +%doc %attr(644, root, man) %{_mandir}/man1/mysql_config.1* +%attr(755, root, root) %{_bindir}/mysql_config +%dir %attr(755, root, root) %{_includedir}/mysql +%dir %attr(755, root, root) %{_libdir}/mysql +%{_includedir}/mysql/* +%{_datadir}/aclocal/mysql.m4 +%{_libdir}/mysql/libmysqlclient.a +%{_libdir}/mysql/libmysqlclient_r.a +%{_libdir}/mysql/libmysqlservices.a + +# ---------------------------------------------------------------------------- +%files -n mysql-wsrep-shared%{product_suffix} +%defattr(-, root, root, 0755) +%if %{defined license_files_server} +%doc %{license_files_server} +%endif +# Shared libraries (omit for architectures that don't support them) +%{_libdir}/libmysql*.so* + +%post -n mysql-wsrep-shared%{product_suffix} +/sbin/ldconfig + +%postun -n mysql-wsrep-shared%{product_suffix} +/sbin/ldconfig + +# ---------------------------------------------------------------------------- +%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 +%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* + +############################################################################## +# 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 Sep 24 2015 Ivan Suzdal - 5.6.23-25.10%{?dist}~mos8.0.1 +- Bump release version + +* Thu Jan 29 2015 Joerg Bruehe +- 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 +- 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 + +- Reworked to build wsrep patched packages exclusively +- OBS compatible + +* Mon Oct 06 2014 Balasubramanian Kandasamy +- Add license info in each subpackage + +* Wed May 28 2014 Balasubramanian Kandasamy +- Updated usergroup to mysql on datadir + +* Wed Oct 30 2013 Balasubramanian Kandasamy +- Removed non gpl file docs/mysql.info from community packages + +* Mon Sep 09 2013 Balasubramanian Kandasamy +- Updated logic to get the correct count of PID files + +* Fri Aug 16 2013 Balasubramanian Kandasamy +- Added provides lowercase mysql tags + +* Wed Jun 26 2013 Balasubramanian Kandasamy +- Cleaned up spec file to resolve rpm dependencies. + +* Mon Nov 05 2012 Joerg Bruehe + +- Allow to override the default to use the bundled yaSSL by an option like + --define="with_ssl /path/to/ssl" + +* Wed Oct 10 2012 Bjorn Munch + +- Replace old my-*.cnf config file examples with template my-default.cnf + +* Fri Oct 05 2012 Joerg Bruehe + +- Let the installation use the new option "--random-passwords" of "mysql_install_db". + (Bug# 12794345 Ensure root password) +- Fix an inconsistency: "new install" vs "upgrade" are told from the (non)existence + of "$mysql_datadir/mysql" (holding table "mysql.user" and other system stuff). + +* Tue Jul 24 2012 Joerg Bruehe + +- Add a macro "runselftest": + if set to 1 (default), the test suite will be run during the RPM build; + this can be oveeridden via the command line by adding + --define "runselftest 0" + Failures of the test suite will NOT make the RPM build fail! + +* Mon Jul 16 2012 Joerg Bruehe + +- Add the man page for the "mysql_config_editor". + +* Mon Jun 11 2012 Joerg Bruehe + +- Make sure newly added "SPECIFIC-ULN/" directory does not disturb packaging. + +* Wed Feb 29 2012 Brajmohan Saxena + +- Removal all traces of the readline library from mysql (BUG 13738013) + +* Wed Sep 28 2011 Joerg Bruehe + +- Fix duplicate mentioning of "mysql_plugin" and its manual page, + it is better to keep alphabetic order in the files list (merging!). + +* Wed Sep 14 2011 Joerg Bruehe + +- Let the RPM capabilities ("obsoletes" etc) ensure that an upgrade may replace + the RPMs of any configuration (of the current or the preceding release series) + by the new ones. This is done by not using the implicitly generated capabilities + (which include the configuration name) and relying on more generic ones which + just list the function ("server", "client", ...). + The implicit generation cannot be prevented, so all these capabilities must be + explicitly listed in "Obsoletes:" + +* Tue Sep 13 2011 Jonathan Perkin + +- Add support for Oracle Linux 6 and Red Hat Enterprise Linux 6. Due to + changes in RPM behaviour ($RPM_BUILD_ROOT is removed prior to install) + this necessitated a move of the libmygcc.a installation to the install + phase, which is probably where it belonged in the first place. + +* Tue Sep 13 2011 Joerg Bruehe + +- "make_win_bin_dist" and its manual are dropped, cmake does it different. + +* Thu Sep 08 2011 Daniel Fischer + +- Add mysql_plugin man page. + +* Tue Aug 30 2011 Tor Didriksen + +- Set CXX=g++ by default to add a dependency on libgcc/libstdc++. + Also, remove the use of the -fno-exceptions and -fno-rtti flags. + TODO: update distro_buildreq/distro_requires + +* Tue Aug 30 2011 Joerg Bruehe + +- Add the manual page for "mysql_plugin" to the server package. + +* Fri Aug 19 2011 Joerg Bruehe + +- Null-upmerge the fix of bug#37165: This spec file is not affected. +- Replace "/var/lib/mysql" by the spec file variable "%%{mysqldatadir}". + +* Fri Aug 12 2011 Daniel Fischer + +- Source plugin library files list from cmake-generated file. + +* Mon Jul 25 2011 Chuck Bell + +- Added the mysql_plugin client - enables or disables plugins. + +* Thu Jul 21 2011 Sunanda Menon + +- Fix bug#12561297: Added the MySQL embedded binary + +* Thu Jul 07 2011 Joerg Bruehe + +- Fix bug#45415: "rpm upgrade recreates test database" + Let the creation of the "test" database happen only during a new installation, + not in an RPM upgrade. + This affects both the "mkdir" and the call of "mysql_install_db". + +* Thu Feb 10 2011 Joerg Bruehe + +- 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) + should still work, and use these locations. + The problem was that the fix for bug#27072 did not check for local settings. + +* Mon Jan 31 2011 Joerg Bruehe + +- Install the new "manifest" files: "INFO_SRC" and "INFO_BIN". + +* Tue Nov 23 2010 Jonathan Perkin + +- EXCEPTIONS-CLIENT has been deleted, remove it from here too +- Support MYSQL_BUILD_MAKE_JFLAG environment variable for passing + a '-j' argument to make. + +* Mon Nov 1 2010 Georgi Kodinov + +- Added test authentication (WL#1054) plugin binaries + +* Wed Oct 6 2010 Georgi Kodinov + +- Added example external authentication (WL#1054) plugin binaries + +* Wed Aug 11 2010 Joerg Bruehe + +- With a recent spec file cleanup, names have changed: A "-community" part was dropped. + Reflect that in the "Obsoletes" specifications. +- Add a "triggerpostun" to handle the uninstall of the "-community" server RPM. +- This fixes bug#55015 "MySQL server is not restarted properly after RPM upgrade". + +* Tue Jun 15 2010 Joerg Bruehe + +- Change the behaviour on installation and upgrade: + On installation, do not autostart the server. + *Iff* the server was stopped before the upgrade is started, this is taken as a + sign the administrator is handling that manually, and so the new server will + not be started automatically at the end of the upgrade. + The start/stop scripts will still be installed, so the server will be started + on the next machine boot. + This is the 5.5 version of fixing bug#27072 (RPM autostarting the server). + +* Tue Jun 1 2010 Jonathan Perkin + +- Implement SELinux checks from distribution-specific spec file. + +* Wed May 12 2010 Jonathan Perkin + +- Large number of changes to build using CMake +- Introduce distribution-specific RPMs +- Drop debuginfo, build all binaries with debug/symbols +- Remove __os_install_post, use native macro +- Remove _unpackaged_files_terminate_build, make it an error to have + unpackaged files +- Remove cluster RPMs + +* Wed Mar 24 2010 Joerg Bruehe + +- Add "--with-perfschema" to the configure options. + +* Mon Mar 22 2010 Joerg Bruehe + +- User "usr/lib*" to allow for both "usr/lib" and "usr/lib64", + mask "rmdir" return code 1. +- Remove "ha_example.*" files from the list, they aren't built. + +* Wed Mar 17 2010 Joerg Bruehe + +- Fix a wrong path name in handling the debug plugins. + +* Wed Mar 10 2010 Joerg Bruehe + +- Take the result of the debug plugin build and put it into the optimized tree, + so that it becomes part of the final installation; + include the files in the packlist. Part of the fixes for bug#49022. + +* Mon Mar 01 2010 Joerg Bruehe + +- Set "Oracle and/or its affiliates" as the vendor and copyright owner, + accept upgrading from packages showing MySQL or Sun as vendor. + +* Fri Feb 12 2010 Joerg Bruehe + +- Formatting changes: + Have a consistent structure of separator lines and of indentation + (8 leading blanks => tab). +- Introduce the variable "src_dir". +- Give the environment variables "MYSQL_BUILD_CC(CXX)" precedence + over "CC" ("CXX"). +- Drop the old "with_static" argument analysis, this is not supported + in 5.1 since ages. +- Introduce variables to control the handlers individually, as well + as other options. +- Use the new "--with-plugin" notation for the table handlers. +- Drop handling "/etc/rc.d/init.d/mysql", the switch to "/etc/init.d/mysql" + was done back in 2002 already. +- Make "--with-zlib-dir=bundled" the default, add an option to disable it. +- Add missing manual pages to the file list. +- Improve the runtime check for "libgcc.a", protect it against being tried + with the Intel compiler "icc". + +* Mon Jan 11 2010 Joerg Bruehe + +- Change RPM file naming: + - Suffix like "-m2", "-rc" becomes part of version as "_m2", "_rc". + - Release counts from 1, not 0. + +* Wed Dec 23 2009 Joerg Bruehe + +- The "semisync" plugin file name has lost its introductory "lib", + adapt the file lists for the subpackages. + This is a part missing from the fix for bug#48351. +- Remove the "fix_privilege_tables" manual, it does not exist in 5.5 + (and likely, the whole script will go, too). + +* Mon Nov 16 2009 Joerg Bruehe + +- Fix some problems with the directives around "tcmalloc" (experimental), + remove erroneous traces of the InnoDB plugin (that is 5.1 only). + +* Fri Oct 09 2009 Magnus Blaudd + +- Removed mysql_fix_privilege_tables + +* Fri Oct 02 2009 Alexander Nozdrin + +- "mysqlmanager" got removed from version 5.4, all references deleted. + +* Fri Aug 28 2009 Joerg Bruehe + +- Merge up from 5.1 to 5.4: Remove handling for the InnoDB plugin. + +* Thu Aug 27 2009 Joerg Bruehe + +- This version does not contain the "Instance manager", "mysqlmanager": + Remove it from the spec file so that packaging succeeds. + +* Mon Aug 24 2009 Jonathan Perkin + +- Add conditionals for bundled zlib and innodb plugin + +* Fri Aug 21 2009 Jonathan Perkin + +- Install plugin libraries in appropriate packages. +- Disable libdaemon_example and ftexample plugins. + +* Thu Aug 20 2009 Jonathan Perkin + +- Update variable used for mysql-test suite location to match source. + +* Fri Nov 07 2008 Joerg Bruehe + +- Correct yesterday's fix, so that it also works for the last flag, + and fix a wrong quoting: un-quoted quote marks must not be escaped. + +* Thu Nov 06 2008 Kent Boortz + +- Removed "mysql_upgrade_shell" +- Removed some copy/paste between debug and normal build + +* Thu Nov 06 2008 Joerg Bruehe + +- Modify CFLAGS and CXXFLAGS such that a debug build is not optimized. + This should cover both gcc and icc flags. Fixes bug#40546. + +* Fri Aug 29 2008 Kent Boortz + +- Removed the "Federated" storage engine option, and enabled in all + +* Tue Aug 26 2008 Joerg Bruehe + +- Get rid of the "warning: Installed (but unpackaged) file(s) found:" + Some generated files aren't needed in RPMs: + - the "sql-bench/" subdirectory + Some files were missing: + - /usr/share/aclocal/mysql.m4 ("devel" subpackage) + - Manual "mysqlbug" ("server" subpackage) + - Program "innochecksum" and its manual ("server" subpackage) + - Manual "mysql_find_rows" ("client" subpackage) + - Script "mysql_upgrade_shell" ("client" subpackage) + - Program "ndb_cpcd" and its manual ("ndb-extra" subpackage) + - Manuals "ndb_mgm" + "ndb_restore" ("ndb-tools" subpackage) + +* Mon Mar 31 2008 Kent Boortz + +- Made the "Federated" storage engine an option +- Made the "Cluster" storage engine and sub packages an option + +* Wed Mar 19 2008 Joerg Bruehe + +- Add the man pages for "ndbd" and "ndb_mgmd". + +* Mon Feb 18 2008 Timothy Smith + +- Require a manual upgrade if the alread-installed mysql-server is + from another vendor, or is of a different major version. + +* Wed May 02 2007 Joerg Bruehe + +- "ndb_size.tmpl" is not needed any more, + "man1/mysql_install_db.1" lacked the trailing '*'. + +* Sat Apr 07 2007 Kent Boortz + +- Removed man page for "mysql_create_system_tables" + +* Wed Mar 21 2007 Daniel Fischer + +- Add debug server. + +* Mon Mar 19 2007 Daniel Fischer + +- Remove Max RPMs; the server RPMs contain a mysqld compiled with all + features that previously only were built into Max. + +* Fri Mar 02 2007 Joerg Bruehe + +- Add several man pages for NDB which are now created. + +* Fri Jan 05 2007 Kent Boortz + +- Put back "libmygcc.a", found no real reason it was removed. + +- Add CFLAGS to gcc call with --print-libgcc-file, to make sure the + correct "libgcc.a" path is returned for the 32/64 bit architecture. + +* Mon Dec 18 2006 Joerg Bruehe + +- Fix the move of "mysqlmanager" to section 8: Directory name was wrong. + +* Thu Dec 14 2006 Joerg Bruehe + +- Include the new man pages for "my_print_defaults" and "mysql_tzinfo_to_sql" + in the server RPM. +- The "mysqlmanager" man page got moved from section 1 to 8. + +* Thu Nov 30 2006 Joerg Bruehe + +- Call "make install" using "benchdir_root=%%{_datadir}", + because that is affecting the regression test suite as well. + +* Thu Nov 16 2006 Joerg Bruehe + +- Explicitly note that the "MySQL-shared" RPMs (as built by MySQL AB) + replace "mysql-shared" (as distributed by SuSE) to allow easy upgrading + (bug#22081). + +* Mon Nov 13 2006 Joerg Bruehe + +- Add "--with-partition" to all server builds. + +- Use "--report-features" in one test run per server build. + +* Tue Aug 15 2006 Joerg Bruehe + +- The "max" server is removed from packages, effective from 5.1.12-beta. + Delete all steps to build, package, or install it. + +* Mon Jul 10 2006 Joerg Bruehe + +- Fix a typing error in the "make" target for the Perl script to run the tests. + +* Tue Jul 04 2006 Joerg Bruehe + +- Use the Perl script to run the tests, because it will automatically check + whether the server is configured with SSL. + +* Tue Jun 27 2006 Joerg Bruehe + +- move "mysqldumpslow" from the client RPM to the server RPM (bug#20216) + +- Revert all previous attempts to call "mysql_upgrade" during RPM upgrade, + there are some more aspects which need to be solved before this is possible. + For now, just ensure the binary "mysql_upgrade" is delivered and installed. + +* Thu Jun 22 2006 Joerg Bruehe + +- Close a gap of the previous version by explicitly using + a newly created temporary directory for the socket to be used + in the "mysql_upgrade" operation, overriding any local setting. + +* Tue Jun 20 2006 Joerg Bruehe + +- To run "mysql_upgrade", we need a running server; + start it in isolation and skip password checks. + +* Sat May 20 2006 Kent Boortz + +- Always compile for PIC, position independent code. + +* Wed May 10 2006 Kent Boortz + +- Use character set "all" when compiling with Cluster, to make Cluster + nodes independent on the character set directory, and the problem + that two RPM sub packages both wants to install this directory. + +* Mon May 01 2006 Kent Boortz + +- Use "./libtool --mode=execute" instead of searching for the + executable in current directory and ".libs". + +* Fri Apr 28 2006 Kent Boortz + +- Install and run "mysql_upgrade" + +* Wed Apr 12 2006 Jim Winstead + +- Remove sql-bench, and MySQL-bench RPM (will be built as an independent + project from the mysql-bench repository) + +* Tue Apr 11 2006 Jim Winstead + +- Remove old mysqltestmanager and related programs +* Sat Apr 01 2006 Kent Boortz + +- Set $LDFLAGS from $MYSQL_BUILD_LDFLAGS + +* Wed Mar 08 2006 Kent Boortz + +- Changed product name from "Community Edition" to "Community Server" + +* Mon Mar 06 2006 Kent Boortz + +- Fast mutexes is now disabled by default, but should be + used in Linux builds. + +* Mon Feb 20 2006 Kent Boortz + +- Reintroduced a max build +- Limited testing of 'debug' and 'max' servers +- Berkeley DB only in 'max' + +* Mon Feb 13 2006 Joerg Bruehe + +- Use "-i" on "make test-force"; + this is essential for later evaluation of this log file. + +* Thu Feb 09 2006 Kent Boortz + +- Pass '-static' to libtool, link static with our own libraries, dynamic + with system libraries. Link with the bundled zlib. + +* Wed Feb 08 2006 Kristian Nielsen + +- Modified RPM spec to match new 5.1 debug+max combined community packaging. + +* Sun Dec 18 2005 Kent Boortz + +- Added "client/mysqlslap" + +* Mon Dec 12 2005 Rodrigo Novo + +- Added zlib to the list of (static) libraries installed +- Added check against libtool wierdness (WRT: sql/mysqld || sql/.libs/mysqld) +- Compile MySQL with bundled zlib +- Fixed %%packager name to "MySQL Production Engineering Team" + +* Mon Dec 05 2005 Joerg Bruehe + +- Avoid using the "bundled" zlib on "shared" builds: + As it is not installed (on the build system), this gives dependency + problems with "libtool" causing the build to fail. + (Change was done on Nov 11, but left uncommented.) + +* Tue Nov 22 2005 Joerg Bruehe + +- Extend the file existence check for "init.d/mysql" on un-install + to also guard the call to "insserv"/"chkconfig". + +* Thu Oct 27 2005 Lenz Grimmer + +- added more man pages + +* Wed Oct 19 2005 Kent Boortz + +- Made yaSSL support an option (off by default) + +* Wed Oct 19 2005 Kent Boortz + +- Enabled yaSSL support + +* Sat Oct 15 2005 Kent Boortz + +- Give mode arguments the same way in all places +- Moved copy of mysqld.a to "standard" build, but + disabled it as we don't do embedded yet in 5.0 + +* Fri Oct 14 2005 Kent Boortz + +- For 5.x, always compile with --with-big-tables +- Copy the config.log file to location outside + the build tree + +* Fri Oct 14 2005 Kent Boortz + +- Removed unneeded/obsolete configure options +- Added archive engine to standard server +- Removed the embedded server from experimental server +- Changed suffix "-Max" => "-max" +- Changed comment string "Max" => "Experimental" + +* Thu Oct 13 2005 Lenz Grimmer + +- added a usermod call to assign a potential existing mysql user to the + correct user group (BUG#12823) +- Save the perror binary built during Max build so it supports the NDB + error codes (BUG#13740) +- added a separate macro "mysqld_group" to be able to define the + user group of the mysql user seperately, if desired. + +* Thu Sep 29 2005 Lenz Grimmer + +- 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 + +- Fixed the creation of the mysql user group account in the postinstall + section (BUG 12348) +- Fixed enabling the Archive storage engine in the Max binary + +* Tue Aug 02 2005 Lenz Grimmer + +- Fixed the Requires: tag for the server RPM (BUG 12233) + +* Fri Jul 15 2005 Lenz Grimmer + +- create a "mysql" user group and assign the mysql user account to that group + in the server postinstall section. (BUG 10984) + +* Tue Jun 14 2005 Lenz Grimmer + +- Do not build statically on i386 by default, only when adding either "--with + static" or "--define '_with_static 1'" to the RPM build options. Static + linking really only makes sense when linking against the specially patched + glibc 2.2.5. + +* Mon Jun 06 2005 Lenz Grimmer + +- added mysql_client_test to the "bench" subpackage (BUG 10676) +- added the libndbclient static and shared libraries (BUG 10676) + +* Wed Jun 01 2005 Lenz Grimmer + +- use "mysqldatadir" variable instead of hard-coding the path multiple times +- use the "mysqld_user" variable on all occasions a user name is referenced +- removed (incomplete) Brazilian translations +- removed redundant release tags from the subpackage descriptions + +* Wed May 25 2005 Joerg Bruehe + +- Added a "make clean" between separate calls to "BuildMySQL". + +* Thu May 12 2005 Guilhem Bichot + +- Removed the mysql_tableinfo script made obsolete by the information schema + +* Wed Apr 20 2005 Lenz Grimmer + +- Enabled the "blackhole" storage engine for the Max RPM + +* Wed Apr 13 2005 Lenz Grimmer + +- removed the MySQL manual files (html/ps/texi) - they have been removed + from the MySQL sources and are now available seperately. + +* Mon Apr 4 2005 Petr Chardin + +- old mysqlmanager, mysqlmanagerc and mysqlmanager-pwger renamed into + mysqltestmanager, mysqltestmanager and mysqltestmanager-pwgen respectively + +* Fri Mar 18 2005 Lenz Grimmer + +- Disabled RAID in the Max binaries once and for all (it has finally been + removed from the source tree) + +* Sun Feb 20 2005 Petr Chardin + +- Install MySQL Instance Manager together with mysqld, touch mysqlmanager + password file + +* Mon Feb 14 2005 Lenz Grimmer + +- Fixed the compilation comments and moved them into the separate build sections + for Max and Standard + +* Mon Feb 7 2005 Tomas Ulin + +- enabled the "Ndbcluster" storage engine for the max binary +- added extra make install in ndb subdir after Max build to get ndb binaries +- added packages for ndbcluster storage engine + +* Fri Jan 14 2005 Lenz Grimmer + +- replaced obsoleted "BuildPrereq" with "BuildRequires" instead + +* Thu Jan 13 2005 Lenz Grimmer + +- enabled the "Federated" storage engine for the max binary + +* Tue Jan 04 2005 Petr Chardin + +- ISAM and merge storage engines were purged. As well as appropriate + tools and manpages (isamchk and isamlog) + +* Fri Dec 31 2004 Lenz Grimmer + +- enabled the "Archive" storage engine for the max binary +- enabled the "CSV" storage engine for the max binary +- enabled the "Example" storage engine for the max binary + +* Thu Aug 26 2004 Lenz Grimmer + +- MySQL-Max now requires MySQL-server instead of MySQL (BUG 3860) + +* Fri Aug 20 2004 Lenz Grimmer + +- do not link statically on IA64/AMD64 as these systems do not have + a patched glibc installed + +* Tue Aug 10 2004 Lenz Grimmer + +- Added libmygcc.a to the devel subpackage (required to link applications + against the the embedded server libmysqld.a) (BUG 4921) + +* Mon Aug 09 2004 Lenz Grimmer + +- Added EXCEPTIONS-CLIENT to the "devel" package + +* Thu Jul 29 2004 Lenz Grimmer + +- disabled OpenSSL in the Max binaries again (the RPM packages were the + only exception to this anyway) (BUG 1043) + +* Wed Jun 30 2004 Lenz Grimmer + +- fixed server postinstall (mysql_install_db was called with the wrong + parameter) + +* Thu Jun 24 2004 Lenz Grimmer + +- added mysql_tzinfo_to_sql to the server subpackage +- run "make clean" instead of "make distclean" + +* Mon Apr 05 2004 Lenz Grimmer + +- added ncurses-devel to the build prerequisites (BUG 3377) + +* Thu Feb 12 2004 Lenz Grimmer + +- when using gcc, _always_ use CXX=gcc +- replaced Copyright with License field (Copyright is obsolete) + +* Tue Feb 03 2004 Lenz Grimmer + +- added myisam_ftdump to the Server package + +* Tue Jan 13 2004 Lenz Grimmer + +- link the mysql client against libreadline instead of libedit (BUG 2289) + +* Mon Dec 22 2003 Lenz Grimmer + +- marked /etc/logrotate.d/mysql as a config file (BUG 2156) + +* Fri Dec 12 2003 Lenz Grimmer + +- fixed file permissions (BUG 1672) + +* Thu Dec 11 2003 Lenz Grimmer + +- made testing for gcc3 a bit more robust + +* Fri Dec 05 2003 Lenz Grimmer + +- added missing file mysql_create_system_tables to the server subpackage + +* Fri Nov 21 2003 Lenz Grimmer + +- removed dependency on MySQL-client from the MySQL-devel subpackage + as it is not really required. (BUG 1610) + +* Fri Aug 29 2003 Lenz Grimmer + +- Fixed BUG 1162 (removed macro names from the changelog) +- Really fixed BUG 998 (disable the checking for installed but + unpackaged files) + +* Tue Aug 05 2003 Lenz Grimmer + +- Fixed BUG 959 (libmysqld not being compiled properly) +- Fixed BUG 998 (RPM build errors): added missing files to the + distribution (mysql_fix_extensions, mysql_tableinfo, mysqldumpslow, + mysql_fix_privilege_tables.1), removed "-n" from install section. + +* Wed Jul 09 2003 Lenz Grimmer + +- removed the GIF Icon (file was not included in the sources anyway) +- removed unused variable shared_lib_version +- do not run automake before building the standard binary + (should not be necessary) +- add server suffix '-standard' to standard binary (to be in line + with the binary tarball distributions) +- Use more RPM macros (_exec_prefix, _sbindir, _libdir, _sysconfdir, + _datadir, _includedir) throughout the spec file. +- allow overriding CC and CXX (required when building with other compilers) + +* Fri May 16 2003 Lenz Grimmer + +- re-enabled RAID again + +* Wed Apr 30 2003 Lenz Grimmer + +- disabled MyISAM RAID (--with-raid) - it throws an assertion which + needs to be investigated first. + +* Mon Mar 10 2003 Lenz Grimmer + +- added missing file mysql_secure_installation to server subpackage + (BUG 141) + +* Tue Feb 11 2003 Lenz Grimmer + +- re-added missing pre- and post(un)install scripts to server subpackage +- added config file /etc/my.cnf to the file list (just for completeness) +- make sure to create the datadir with 755 permissions + +* Mon Jan 27 2003 Lenz Grimmer + +- removed unused CC and CXX variables +- CFLAGS and CXXFLAGS should honor RPM_OPT_FLAGS + +* Fri Jan 24 2003 Lenz Grimmer + +- renamed package "MySQL" to "MySQL-server" +- fixed Copyright tag +- added mysql_waitpid to client subpackage (required for mysql-test-run) + +* Wed Nov 27 2002 Lenz Grimmer + +- moved init script from /etc/rc.d/init.d to /etc/init.d (the majority of + Linux distributions now support this scheme as proposed by the LSB either + directly or via a compatibility symlink) +- Use new "restart" init script action instead of starting and stopping + separately +- Be more flexible in activating the automatic bootup - use insserv (on + older SuSE versions) or chkconfig (Red Hat, newer SuSE versions and + others) to create the respective symlinks + +* Wed Sep 25 2002 Lenz Grimmer + +- MySQL-Max now requires MySQL >= 4.0 to avoid version mismatches + (mixing 3.23 and 4.0 packages) + +* Fri Aug 09 2002 Lenz Grimmer + +- Turn off OpenSSL in MySQL-Max for now until it works properly again +- enable RAID for the Max binary instead +- added compatibility link: safe_mysqld -> mysqld_safe to ease the + transition from 3.23 + +* Thu Jul 18 2002 Lenz Grimmer + +- Reworked the build steps a little bit: the Max binary is supposed + to include OpenSSL, which cannot be linked statically, thus trying + to statically link against a special glibc is futile anyway +- because of this, it is not required to make yet another build run + just to compile the shared libs (saves a lot of time) +- updated package description of the Max subpackage +- clean up the BuildRoot directory afterwards + +* Mon Jul 15 2002 Lenz Grimmer + +- Updated Packager information +- Fixed the build options: the regular package is supposed to + include InnoDB and linked statically, while the Max package + should include BDB and SSL support + +* Fri May 03 2002 Lenz Grimmer + +- Use more RPM macros (e.g. infodir, mandir) to make the spec + file more portable +- reorganized the installation of documentation files: let RPM + take care of this +- reorganized the file list: actually install man pages along + with the binaries of the respective subpackage +- do not include libmysqld.a in the devel subpackage as well, if we + have a special "embedded" subpackage +- reworked the package descriptions + +* Mon Oct 8 2001 Monty + +- Added embedded server as a separate RPM + +* Fri Apr 13 2001 Monty + +- Added mysqld-max to the distribution + +* Tue Jan 2 2001 Monty + +- Added mysql-test to the bench package + +* Fri Aug 18 2000 Tim Smith + +- Added separate libmysql_r directory; now both a threaded + and non-threaded library is shipped. + +* Wed Sep 29 1999 David Axmark + +- Added the support-files/my-example.cnf to the docs directory. + +- Removed devel dependency on base since it is about client + development. + +* Wed Sep 8 1999 David Axmark + +- Cleaned up some for 3.23. + +* Thu Jul 1 1999 David Axmark + +- Added support for shared libraries in a separate sub + package. Original fix by David Fox (dsfox@cogsci.ucsd.edu) + +- The --enable-assembler switch is now automatically disables on + platforms there assembler code is unavailable. This should allow + building this RPM on non i386 systems. + +* Mon Feb 22 1999 David Axmark + +- Removed unportable cc switches from the spec file. The defaults can + now be overridden with environment variables. This feature is used + to compile the official RPM with optimal (but compiler version + specific) switches. + +- Removed the repetitive description parts for the sub rpms. Maybe add + again if RPM gets a multiline macro capability. + +- Added support for a pt_BR translation. Translation contributed by + Jorge Godoy . + +* Wed Nov 4 1998 David Axmark + +- A lot of changes in all the rpm and install scripts. This may even + be a working RPM :-) + +* Sun Aug 16 1998 David Axmark + +- A developers changelog for MySQL is available in the source RPM. And + there is a history of major user visible changed in the Reference + Manual. Only RPM specific changes will be documented here. diff --git a/scripts__mysqld_safe.sh__signals.patch b/scripts__mysqld_safe.sh__signals.patch new file mode 100644 index 0000000..a3e9d14 --- /dev/null +++ b/scripts__mysqld_safe.sh__signals.patch @@ -0,0 +1,35 @@ +--- a/scripts/mysqld_safe.sh ++++ b/scripts/mysqld_safe.sh +@@ -29,9 +29,6 @@ err_log= + syslog_tag_mysqld=mysqld + syslog_tag_mysqld_safe=mysqld_safe + +-trap '' 1 2 3 15 # we shouldn't let anyone kill us +-trap '' 13 # not even SIGPIPE +- + # MySQL-specific environment variable. First off, it's not really a umask, + # it's the desired mode. Second, it follows umask(2), not umask(3) in that + # octal needs to be explicit. Our shell might be a proper sh without printf, +@@ -154,7 +151,7 @@ eval_log_error () { + # sed buffers output (only GNU sed supports a -u (unbuffered) option) + # which means that messages may not get sent to syslog until the + # mysqld process quits. +- cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error" ++ cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error & wait" + ;; + *) + echo "Internal program error (non-fatal):" \ +@@ -740,6 +737,13 @@ mysqld daemon not started" + fi + + # ++# From now on, we catch signals to do a proper shutdown of mysqld ++# when signalled to do so. ++# ++trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf refresh & wait' 1 # HUP ++trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf shutdown' 2 3 15 # INT QUIT and TERM ++ ++# + # Uncomment the following lines if you want all tables to be automatically + # checked and repaired during startup. You should add sensible key_buffer + # and sort_buffer values to my.cnf to improve check performance or require diff --git a/spelling.patch b/spelling.patch new file mode 100644 index 0000000..7978092 --- /dev/null +++ b/spelling.patch @@ -0,0 +1,244 @@ +--- a/libevent/event.3 ++++ b/libevent/event.3 +@@ -253,7 +253,7 @@ the type of event which will be either + or + .Va EV_WRITE . + Additionally, an event which has registered interest in more than one of the +-preceeding events, via bitwise-OR to ++preceding events, via bitwise-OR to + .Fn event_set , + can provide its callback function with a bitwise-OR of more than one triggered + event. +--- a/mysql-test/extra/rpl_tests/rpl_ddl.test ++++ b/mysql-test/extra/rpl_tests/rpl_ddl.test +@@ -98,8 +98,8 @@ + # --> less switching of AUTOCOMMIT mode on master side. + # + # 4. Never use a test object, which was direct or indirect affected by a +-# preceeding test sequence again. +-# If one preceeding test sequence hits a (sometimes not visible, ++# preceding test sequence again. ++# If one preceding test sequence hits a (sometimes not visible, + # because the sql error code of the statement might be 0) bug + # and these rules are ignored, a following test sequence might earn ugly + # effects like failing 'sync_slave_with_master', crashes of the slave or +--- a/mysql-test/extra/rpl_tests/rpl_row_basic.test ++++ b/mysql-test/extra/rpl_tests/rpl_row_basic.test +@@ -221,7 +221,7 @@ INSERT INTO t7 VALUES (1,3), (2,6), (3,9 + SELECT * FROM t7 ORDER BY C1; + + # since bug#31552/31609 idempotency is not default any longer. In order +-# the preceeding test INSERT INTO t7 to pass the mode is switched ++# the preceding test INSERT INTO t7 to pass the mode is switched + # temprorarily + set @@global.slave_exec_mode= 'IDEMPOTENT'; + +@@ -260,7 +260,7 @@ INSERT INTO t8 VALUES (1,2,3), (2,4,6), + SELECT * FROM t8 ORDER BY a; + + # since bug#31552/31609 idempotency is not default any longer. In order +-# the preceeding test INSERT INTO t8 to pass the mode is switched ++# the preceding test INSERT INTO t8 to pass the mode is switched + # temprorarily + set @@global.slave_exec_mode= 'IDEMPOTENT'; + +--- a/mysql-test/include/wait_until_count_sessions.inc ++++ b/mysql-test/include/wait_until_count_sessions.inc +@@ -10,7 +10,7 @@ + # 1. We wait for $current_sessions <= $count_sessions because in the use case + # with count_sessions.inc before and wait_until_count_sessions.inc after + # the core of the test it could happen that the disconnects of sessions +-# belonging to the preceeding test are not finished. ++# belonging to the preceding test are not finished. + # sessions at test begin($count_sessions) = m + n + # sessions of the previous test which will be soon disconnected = n (n >= 0) + # sessions at test end ($current sessions, assuming the test disconnects +--- a/mysql-test/suite/funcs_1/views/func_view.inc ++++ b/mysql-test/suite/funcs_1/views/func_view.inc +@@ -285,7 +285,7 @@ INSERT INTO t1_values SET + # other interesting value + # numbers -> 0 + # strings, blobs, binaries -> not full length of used data type, "exotic" +-# characters and preceeding and trailing spaces ++# characters and preceding and trailing spaces + # FIXME enum, set ?? + INSERT INTO t1_values SET + my_char_30 = ' ---äÖüß@µ*$-- ', +--- a/mysql-test/suite/funcs_1/views/views_master.inc ++++ b/mysql-test/suite/funcs_1/views/views_master.inc +@@ -545,7 +545,7 @@ let $message= Testcase 3.3.1.7 ; + # view names are accepted, at creation time, alteration time, + # and drop time. + ############################################################################### +-# Note(mleich): non-qualified view name means a view name without preceeding ++# Note(mleich): non-qualified view name means a view name without preceding + # database name + --disable_warnings + DROP VIEW IF EXISTS v1 ; +--- a/mysql-test/suite/rpl/t/rpl_ddl.test ++++ b/mysql-test/suite/rpl/t/rpl_ddl.test +@@ -13,10 +13,10 @@ + # sequences start. + # + # 2. Never use a test object, which was direct or indirect affected by a +-# preceeding test sequence again. ++# preceding test sequence again. + # Except table d1.t1 where ONLY DML is allowed. + # +-# If one preceeding test sequence hits a (sometimes not good visible, ++# If one preceding test sequence hits a (sometimes not good visible, + # because the sql error code of the statement might be 0) bug + # and these rules are ignored, a following test sequence might earn ugly + # effects like failing 'sync_slave_with_master', crashes of the slave or +--- a/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test ++++ b/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test +@@ -239,7 +239,7 @@ sync_slave_with_master; + UPDATE t1 SET a = 5, b = 'slave' WHERE a = 1; + SELECT * FROM t1 ORDER BY a; + # since bug#31552/31609 idempotency is not default any longer. In +-# order for the preceeding test UPDATE t1 to pass, the mode is switched ++# order for the preceding test UPDATE t1 to pass, the mode is switched + # temprorarily + set @@global.slave_exec_mode= 'IDEMPOTENT'; + --echo **** On Master **** +--- a/mysql-test/suite/rpl_ndb/t/rpl_ndb_ddl.test ++++ b/mysql-test/suite/rpl_ndb/t/rpl_ndb_ddl.test +@@ -13,10 +13,10 @@ + # sequences start. + # + # 2. Never use a test object, which was direct or indirect affected by a +-# preceeding test sequence again. ++# preceding test sequence again. + # Except table d1.t1 where ONLY DML is allowed. + # +-# If one preceeding test sequence hits a (sometimes not good visible, ++# If one preceding test sequence hits a (sometimes not good visible, + # because the sql error code of the statement might be 0) bug + # and these rules are ignored, a following test sequence might earn ugly + # effects like failing 'sync_slave_with_master', crashes of the slave or +--- a/sql/abstract_query_plan.cc ++++ b/sql/abstract_query_plan.cc +@@ -336,7 +336,7 @@ namespace AQP + { + /* + use_quick == 2 means that the decision on which access method to use +- will be taken late (as rows from the preceeding operation arrive). ++ will be taken late (as rows from the preceding operation arrive). + This operation is therefor not pushable. + */ + DBUG_PRINT("info", +--- a/sql/ha_ndbcluster_push.cc ++++ b/sql/ha_ndbcluster_push.cc +@@ -1271,7 +1271,7 @@ ndb_pushed_builder_ctx::build_key(const + if (m_join_scope.contain(referred_table_no)) + { + // Locate the parent operation for this 'join_items[]'. +- // May refer any of the preceeding parent tables ++ // May refer any of the preceding parent tables + const NdbQueryOperationDef* const parent_op= m_tables[referred_table_no].m_op; + DBUG_ASSERT(parent_op != NULL); + +--- a/sql/log_event.cc ++++ b/sql/log_event.cc +@@ -4607,7 +4607,7 @@ int Query_log_event::do_apply_event(Rela + if ((error= rows_event_stmt_cleanup(const_cast(rli), thd))) + { + const_cast(rli)->report(ERROR_LEVEL, error, +- "Error in cleaning up after an event preceeding the commit; " ++ "Error in cleaning up after an event preceding the commit; " + "the group log file/position: %s %s", + const_cast(rli)->get_group_master_log_name(), + llstr(const_cast(rli)->get_group_master_log_pos(), +--- a/sql/rpl_utility.cc ++++ b/sql/rpl_utility.cc +@@ -1564,7 +1564,7 @@ bool Deferred_log_events::execute(Relay_ + void Deferred_log_events::rewind() + { + /* +- Reset preceeding Query log event events which execution was ++ Reset preceding Query log event events which execution was + deferred because of slave side filtering. + */ + if (!is_empty()) +--- a/sql/sql_optimizer.cc ++++ b/sql/sql_optimizer.cc +@@ -971,7 +971,7 @@ JOIN::optimize() + } + } + } +- else if (order && // ORDER BY wo/ preceeding GROUP BY ++ else if (order && // ORDER BY wo/ preceding GROUP BY + (simple_order || skip_sort_order)) // which is possibly skippable + { + if (test_if_skip_sort_order(tab, order, m_select_limit, false, +--- a/sql/sql_rewrite.cc ++++ b/sql/sql_rewrite.cc +@@ -40,7 +40,7 @@ + + + /** +- Append a key/value pair to a string, with an optional preceeding comma. ++ Append a key/value pair to a string, with an optional preceding comma. + For numeric values. + + @param str The string to append to +@@ -72,7 +72,7 @@ bool append_int(String *str, bool comma, + + /** + Append a key/value pair to a string if the value is non-NULL, +- with an optional preceeding comma. ++ with an optional preceding comma. + + @param str The string to append to + @param comma Prepend a comma? +--- a/sql/sql_yacc.cc ++++ b/sql/sql_yacc.cc +@@ -39064,7 +39064,7 @@ yyreduce: + { + /* + Not in trigger assigning value to new row, +- and option_type preceeding local variable is illegal. ++ and option_type preceding local variable is illegal. + */ + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; +--- a/sql/sql_yacc.yy ++++ b/sql/sql_yacc.yy +@@ -14559,7 +14559,7 @@ opt_var_ident_type: + | SESSION_SYM '.' { $$=OPT_SESSION; } + ; + +-// Option values with preceeding option_type. ++// Option values with preceding option_type. + option_value_following_option_type: + internal_variable_name equal set_expr_or_default + { +@@ -14576,7 +14576,7 @@ option_value_following_option_type: + { + /* + Not in trigger assigning value to new row, +- and option_type preceeding local variable is illegal. ++ and option_type preceding local variable is illegal. + */ + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; +@@ -14584,7 +14584,7 @@ option_value_following_option_type: + } + ; + +-// Option values without preceeding option_type. ++// Option values without preceding option_type. + option_value_no_option_type: + internal_variable_name equal + { +--- a/storage/myisam/mi_rnext.c ++++ b/storage/myisam/mi_rnext.c +@@ -65,7 +65,7 @@ int mi_rnext(MI_INFO *info, uchar *buf, + Normally SQL layer would never request "search next" if + "search first" failed. But HANDLER may do anything. + +- As mi_rnext() without preceeding mi_rkey()/mi_rfirst() ++ As mi_rnext() without preceding mi_rkey()/mi_rfirst() + equals to mi_rfirst(), we must restore original state + as if failing mi_rfirst() was not called. + */ diff --git a/wsrep_sst_mysqldump.patch b/wsrep_sst_mysqldump.patch new file mode 100644 index 0000000..6b97076 --- /dev/null +++ b/wsrep_sst_mysqldump.patch @@ -0,0 +1,31 @@ +diff --git a/scripts/wsrep_sst_mysqldump.sh b/scripts/wsrep_sst_mysqldump.sh +index 89eb755..c20d9fa 100644 +--- a/scripts/wsrep_sst_mysqldump.sh ++++ b/scripts/wsrep_sst_mysqldump.sh +@@ -53,6 +53,8 @@ then + exit $EINVAL + fi + ++MYSQL_APPS_PREFIX="nice -n 15 ionice -c2 -n7" ++ + # Check client version + CLIENT_MINOR=$(mysql --version | cut -d ' ' -f 6 | cut -d '.' -f 2) + if [ $CLIENT_MINOR -lt "6" ] +@@ -73,7 +75,7 @@ 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 \ ++MYSQLDUMP="$MYSQL_APPS_PREFIX $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" +@@ -98,7 +100,7 @@ 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 "\ ++MYSQL="$MYSQL_APPS_PREFIX $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