From fe76fb69a03f53273602bbee5771d9a5fb339870 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 23 Oct 2009 13:04:55 +0200 Subject: [PATCH 001/466] Bug #48209 Confusing message "Test has succeeded" after test fails due to warnings There's no need for --verbose for the mysqltest in check-warnings, remove it --- mysql-test/mysql-test-run.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4d8d178e092..11ad4a56eb7 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3739,7 +3739,6 @@ sub start_check_warnings ($$) { mtr_add_arg($args, "--skip-safemalloc"); mtr_add_arg($args, "--test-file=%s", "include/check-warnings.test"); - mtr_add_arg($args, "--verbose"); if ( $opt_embedded_server ) { From 6c28505e6ed2b9f678165d439670958c03e801ff Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 26 Oct 2009 14:28:51 +0100 Subject: [PATCH 002/466] Bug #48240 "Test suite timeout" and "Too many tests failed" is masked from status page Prepend "Completed", "Timeout" or "Too many failed" to summary line --- mysql-test/lib/mtr_report.pm | 7 +++++-- mysql-test/mysql-test-run.pl | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm index 937e19111fb..81885c35a44 100644 --- a/mysql-test/lib/mtr_report.pm +++ b/mysql-test/lib/mtr_report.pm @@ -222,8 +222,8 @@ sub mtr_report_test ($) { } -sub mtr_report_stats ($;$) { - my ($tests, $dont_error)= @_; +sub mtr_report_stats ($$;$) { + my ($prefix, $tests, $dont_error)= @_; # ---------------------------------------------------------------------- # Find out how we where doing @@ -328,6 +328,9 @@ sub mtr_report_stats ($;$) { } } + # Print summary line prefix + print "$prefix: "; + # Print a list of testcases that failed if ( $tot_failed != 0 ) { diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4d8d178e092..762fa75ed93 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -411,7 +411,7 @@ sub main { $opt_gcov_msg, $opt_gcov_err); } - mtr_report_stats($completed); + mtr_report_stats("Completed", $completed); exit(0); } @@ -533,7 +533,7 @@ sub run_test_server ($$$) { $num_failed_test >= $opt_max_test_fail) { $suite_timeout_proc->kill(); push(@$completed, $result); - mtr_report_stats($completed, 1); + mtr_report_stats("Too many failed", $completed, 1); mtr_report("Too many tests($num_failed_test) failed!", "Terminating..."); return undef; @@ -665,7 +665,7 @@ sub run_test_server ($$$) { # ---------------------------------------------------- if ( ! $suite_timeout_proc->wait_one(0) ) { - mtr_report_stats($completed, 1); + mtr_report_stats("Timeout", $completed, 1); mtr_report("Test suite timeout! Terminating..."); return undef; } From dd1d0a464456b5d8c71efb675c715ad2e8d89151 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 4 Nov 2009 13:42:22 +0100 Subject: [PATCH 003/466] Bug #47663 mtr --parallel has weird output Some output is written, some is not Finally concluded it's a Perl bug: after running with parallel threads for a while, print suddenly ignores all but the first argument. Workaround: concatenate all the arguments into one, except in output that only comes before we start running tests --- mysql-test/lib/My/SafeProcess.pm | 6 +++--- mysql-test/lib/mtr_report.pm | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index 7e102b628ca..ee7ed2471c7 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -338,12 +338,12 @@ sub start_kill { $ret= system($safe_kill, $winpid) >> 8; if ($ret == 3){ - print "Couldn't open the winpid: $winpid ", + print "Couldn't open the winpid: $winpid ". "for pid: $pid, try one more time\n"; sleep(1); $winpid= _winpid($pid); $ret= system($safe_kill, $winpid) >> 8; - print "Couldn't open the winpid: $winpid ", + print "Couldn't open the winpid: $winpid ". "for pid: $pid, continue and see what happens...\n"; } } @@ -594,7 +594,7 @@ sub self2str { sub _verbose { return unless $_verbose; - print STDERR " ## ", @_, "\n"; + print STDERR " ## ". @_. "\n"; } diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm index 81885c35a44..1c4b940bbee 100644 --- a/mysql-test/lib/mtr_report.pm +++ b/mysql-test/lib/mtr_report.pm @@ -69,7 +69,7 @@ sub _mtr_report_test_name ($) { $tname.= " '$tinfo->{combination}'" if defined $tinfo->{combination}; - print _name(), _timestamp(); + print _name(). _timestamp(); printf "%-40s ", $tname; my $worker = $tinfo->{worker}; printf "w$worker " if $worker; @@ -390,13 +390,13 @@ sub mtr_report_stats ($$;$) { ############################################################################## sub mtr_print_line () { - print '-' x 60, "\n"; + print '-' x 60 . "\n"; } sub mtr_print_thick_line { my $char= shift || '='; - print $char x 78, "\n"; + print $char x 78 . "\n"; } @@ -454,7 +454,7 @@ sub _timestamp { # Always print message to screen sub mtr_print (@) { - print _name(), join(" ", @_), "\n"; + print _name(). join(" ", @_). "\n"; } @@ -462,22 +462,22 @@ sub mtr_print (@) { sub mtr_report (@) { if (defined $verbose) { - print _name(), join(" ", @_), "\n"; + print _name(). join(" ", @_). "\n"; } } # Print warning to screen sub mtr_warning (@) { - print STDERR _name(), _timestamp(), - "mysql-test-run: WARNING: ", join(" ", @_), "\n"; + print STDERR _name(). _timestamp(). + "mysql-test-run: WARNING: ". join(" ", @_). "\n"; } # Print error to screen and then exit sub mtr_error (@) { - print STDERR _name(), _timestamp(), - "mysql-test-run: *** ERROR: ", join(" ", @_), "\n"; + print STDERR _name(). _timestamp(). + "mysql-test-run: *** ERROR: ". join(" ", @_). "\n"; if (IS_WINDOWS) { POSIX::_exit(1); @@ -492,8 +492,8 @@ sub mtr_error (@) { sub mtr_debug (@) { if ( $verbose > 2 ) { - print STDERR _name(), - _timestamp(), "####: ", join(" ", @_), "\n"; + print STDERR _name(). + _timestamp(). "####: ". join(" ", @_). "\n"; } } @@ -501,8 +501,8 @@ sub mtr_debug (@) { sub mtr_verbose (@) { if ( $verbose ) { - print STDERR _name(), _timestamp(), - "> ",join(" ", @_),"\n"; + print STDERR _name(). _timestamp(). + "> ".join(" ", @_)."\n"; } } @@ -512,8 +512,8 @@ sub mtr_verbose_restart (@) { my $proc= $server->{proc}; if ( $verbose_restart ) { - print STDERR _name(),_timestamp(), - "> Restart $proc - ",join(" ", @args),"\n"; + print STDERR _name()._timestamp(). + "> Restart $proc - ".join(" ", @args)."\n"; } } From 13cd7170ccadbc596146175c0674956f49aed830 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 9 Nov 2009 12:32:48 +0100 Subject: [PATCH 004/466] WL#5161 : Cross-platform build with CMake --- BUILD-CMAKE | 243 +++ BUILD/Makefile.am | 1 + BUILD/autorun.sh | 7 + BUILD/choose_configure.sh | 14 + CMakeLists.txt | 459 +++--- Makefile.am | 9 +- client/CMakeLists.txt | 72 +- cmake/Makefile.am | 21 + cmake/abi_check.cmake | 60 + cmake/bison.cmake | 64 + cmake/cat.cmake | 27 + cmake/character_sets.cmake | 63 + cmake/check_minimal_version.cmake | 19 + cmake/configure.pl | 79 + cmake/create_initial_db.cmake.in | 84 ++ cmake/do_abi_check.cmake | 78 + cmake/dtrace.cmake | 92 ++ cmake/dtrace_prelink.cmake | 34 + cmake/install_macros.cmake | 69 + cmake/make_dist.cmake.in | 176 +++ cmake/merge_archives_unix.cmake.in | 62 + cmake/misc.cmake | 128 ++ cmake/mysql_version.cmake | 142 ++ cmake/plugin.cmake | 171 +++ cmake/readline.cmake | 197 +++ cmake/ssl.cmake | 88 ++ cmake/stack_direction.c | 31 + cmake/zlib.cmake | 73 + cmd-line-utils/libedit/CMakeLists.txt | 167 +++ cmd-line-utils/libedit/Makefile.am | 2 +- cmd-line-utils/readline/CMakeLists.txt | 60 + cmd-line-utils/readline/Makefile.am | 2 +- config.h.cmake | 618 ++++++++ configure.cmake | 1335 +++++++++++++++++ configure.in | 4 +- dbug/CMakeLists.txt | 16 +- extra/CMakeLists.txt | 76 +- extra/yassl/CMakeLists.txt | 24 +- extra/yassl/taocrypt/CMakeLists.txt | 11 +- include/CMakeLists.txt | 61 + include/Makefile.am | 3 +- include/keycache.h | 2 +- include/my_global.h | 111 +- include/my_pthread.h | 18 +- include/my_sys.h | 3 + include/mysql/plugin.h | 27 +- include/thr_alarm.h | 2 +- libmysql/CMakeLists.txt | 186 +-- libmysqld/CMakeLists.txt | 155 +- libmysqld/examples/CMakeLists.txt | 48 +- libmysqld/lib_sql.cc | 5 - man/CMakeLists.txt | 24 + man/Makefile.am | 2 +- mysql-test/CMakeLists.txt | 110 ++ mysql-test/Makefile.am | 5 +- mysql-test/lib/My/ConfigFactory.pm | 11 +- mysql-test/lib/My/Find.pm | 18 +- mysql-test/lib/My/SafeProcess.pm | 18 +- mysql-test/lib/My/SafeProcess/CMakeLists.txt | 14 +- .../lib/My/SafeProcess/safe_kill_win.cc | 1 + .../lib/My/SafeProcess/safe_process_win.cc | 5 + mysql-test/mtr.out-of-source | 5 + mysql-test/mysql-test-run.pl | 163 +- mysql-test/t/fulltext_plugin.test | 3 +- mysys/CMakeLists.txt | 44 +- mysys/my_create.c | 9 +- mysys/my_getsystime.c | 5 +- mysys/my_winthread.c | 18 + mysys/mysys_priv.h | 5 +- plugin/daemon_example/CMakeLists.txt | 18 + plugin/daemon_example/Makefile.am | 1 + plugin/fulltext/CMakeLists.txt | 18 + plugin/fulltext/Makefile.am | 1 + plugin/semisync/CMakeLists.txt | 33 +- regex/CMakeLists.txt | 5 +- regex/regex2.h | 4 +- scripts/CMakeLists.txt | 280 +++- sql-bench/CMakeLists.txt | 51 + sql-bench/Makefile.am | 2 +- sql/CMakeLists.txt | 258 +++- sql/nt_servc.cc | 1 + sql/share/CMakeLists.txt | 52 + sql/share/Makefile.am | 5 +- sql/sql_builtin.cc.in | 7 +- sql/sql_class.h | 4 +- storage/Makefile.am | 1 - storage/archive/CMakeLists.txt | 10 +- storage/blackhole/CMakeLists.txt | 5 +- storage/csv/CMakeLists.txt | 3 +- storage/example/CMakeLists.txt | 2 +- storage/federated/CMakeLists.txt | 8 +- storage/heap/CMakeLists.txt | 3 +- storage/ibmdb2i/CMakeLists.txt | 27 +- storage/innobase/CMakeLists.txt | 167 ++- storage/innobase/include/page0page.ic | 2 +- storage/myisam/CMakeLists.txt | 50 +- storage/myisammrg/CMakeLists.txt | 3 +- storage/mysql_storage_engine.cmake | 44 - strings/CMakeLists.txt | 8 +- support-files/CMakeLists.txt | 61 + support-files/Makefile.am | 3 +- tests/CMakeLists.txt | 12 +- unittest/mysys/CMakeLists.txt | 36 + unittest/mysys/Makefile.am | 2 + unittest/mytap/CMakeLists.txt | 18 + unittest/mytap/Makefile.am | 2 + unittest/mytap/tap.c | 8 +- vio/CMakeLists.txt | 13 +- win/create_def_file.js | 1 + zlib/CMakeLists.txt | 17 +- 110 files changed, 6220 insertions(+), 985 deletions(-) create mode 100644 BUILD-CMAKE create mode 100644 BUILD/choose_configure.sh create mode 100644 cmake/Makefile.am create mode 100644 cmake/abi_check.cmake create mode 100644 cmake/bison.cmake create mode 100644 cmake/cat.cmake create mode 100644 cmake/character_sets.cmake create mode 100644 cmake/check_minimal_version.cmake create mode 100644 cmake/configure.pl create mode 100644 cmake/create_initial_db.cmake.in create mode 100644 cmake/do_abi_check.cmake create mode 100644 cmake/dtrace.cmake create mode 100644 cmake/dtrace_prelink.cmake create mode 100644 cmake/install_macros.cmake create mode 100644 cmake/make_dist.cmake.in create mode 100644 cmake/merge_archives_unix.cmake.in create mode 100644 cmake/misc.cmake create mode 100644 cmake/mysql_version.cmake create mode 100644 cmake/plugin.cmake create mode 100644 cmake/readline.cmake create mode 100644 cmake/ssl.cmake create mode 100644 cmake/stack_direction.c create mode 100644 cmake/zlib.cmake create mode 100644 cmd-line-utils/libedit/CMakeLists.txt create mode 100644 cmd-line-utils/readline/CMakeLists.txt create mode 100644 config.h.cmake create mode 100644 configure.cmake create mode 100644 include/CMakeLists.txt create mode 100644 man/CMakeLists.txt create mode 100644 mysql-test/CMakeLists.txt create mode 100644 mysql-test/mtr.out-of-source create mode 100644 plugin/daemon_example/CMakeLists.txt create mode 100644 plugin/fulltext/CMakeLists.txt create mode 100644 sql-bench/CMakeLists.txt create mode 100644 sql/share/CMakeLists.txt delete mode 100644 storage/mysql_storage_engine.cmake create mode 100644 support-files/CMakeLists.txt create mode 100644 unittest/mysys/CMakeLists.txt create mode 100644 unittest/mytap/CMakeLists.txt diff --git a/BUILD-CMAKE b/BUILD-CMAKE new file mode 100644 index 00000000000..b5a01d52a5a --- /dev/null +++ b/BUILD-CMAKE @@ -0,0 +1,243 @@ +How to Build MySQL server with CMake + +WHAT YOU NEED +--------------------------------------------------------------- +CMake version 2.6 or later installed on your system. + +HOW TO INSTALL: + +Linux distributions: +shell> sudo apt-get install cmake + +The above works on do Debian/Ubuntu based distributions.On others, command +line needs to be modified to e.g "yum install" on Fedora or "zypper install" +on OpenSUSE. + +OpenSolaris: +shell> pfexec pkgadd install SUNWCMake + +Windows and Mac OSX: +Download and install the latest distribution from +http://www.cmake.org/cmake/resources/software.html.On Windows, download +installer exe file and run it. On Mac, download the .dmg image and open it. + +Other Unixes: +Precompiled packages for other Unix flavors (HPUX, AIX) are available from +http://www.cmake.org/cmake/resources/software.html + +Alternatively, you can build from source, source package is also available on +CMake download page. + + +Compiler Tools +-------------- +You will need a working compiler and make utility on your OS. +On Windows, install Visual Studio (Express editions will work too). +On Mac OSX, install Xcode tools. + + + +BUILD +--------------------------------------------------------------- +Ensure that compiler and cmake are in PATH. +The following description assumes that current working directory +is the source directory. + + +- Generic build on Unix, using "Unix Makefiles" generator + +shell>cmake . +shell>make + +Note: by default, cmake build is less verbose than automake build. Use +"make VERBOSE=1" if you want to see add command lines for each compiled source. + +- Windows, using "Visual Studio 9 2008" generator +shell>cmake . -G "Visual Studio 9 2008" +shell>devenv MySQL.sln /build /relwithdebinfo +(alternatively, open MySQL.sln and build using the IDE) + +- Windows, using "NMake Makefiles" generator +shell>cmake . -G "NMake Makefiles" +shell>nmake + +- Mac OSX build with Xcode +shell>cmake . -G Xcode +shell>xcodebuild -configuration Relwithdebinfo +(alternatively, open MySQL.xcodeproj and build using the IDE) + +Command line build with CMake 2.8 +After creating project with cmake -G as above, issue +cmake . --build +this works with any CMake generator. + +For Visual Studio and Xcode you might want to add an extra +configuration parameter, to avoid building all configurations. + +cmake . --build --config Relwithdebinfo + + +Building "out-of-source" +--------------------------------------------------------------- +Building out-of-source provides additional benefits. For example it allows to +build both Release and Debug configurations using the single source tree.Or +build the same source with different version of the same compiler or with +different compilers. Also you will prevent polluting the source tree with the +objects and binaries produced during the make. + +Here is an example on how to do it (generic Unix), assuming the source tree is +in directory named src and the current working directory is source root. + +shell>mkdir ../build # build directory is called build +shell>cd ../build +shell>cmake ../src + +Note: if a directory was used for in-source build, out-of-source will +not work. To reenable out-of-source build, remove /CMakeCache.txt +file. + + +CONFIGURATION PARAMETERS +--------------------------------------------------------------- +The procedure above will build with default configuration. + +Let's you want to change the configuration parameters and have archive +storage engine compiled into the server instead of building it as pluggable +module. + +1)You can provide parameters on the command line, like + +shell> cmake . -DWITH_ARCHIVE_STORAGE_ENGINE=1 + +This can be done during the initial configuration or any time later. + +Note, that parameters are "sticky", that is they are remebered in the CMake +cache (CMakeCache.txt file in the build directory) + +2) Configuration using cmake-gui (Windows, OSX, or Linux with cmake-gui +installed) + +From the build directory, issue +shell> cmake-gui . + +- Check the WITH_INNOBASE_STORAGE_ENGINE checkbox +- Click on "Configure" button +- Click on "Generate" button +- Close cmake-gui +shell> make + +3)Using ccmake (Unix) +ccmake is curses-based GUI application that provides the same functionality +as cmake-gui. It is less user-friendly compared to cmake-gui but works also +on exotic Unixes like HPUX, AIX or Solaris. + +Besides storage engines, probably the most important parameter from a +developer's point of view is WITH_DEBUG (this allows to build server with +dbug tracing library and with debug compile flags). + +After changing the configuration, recompile using +shell> make + + +Listing configuration parameters +--------------------------------------------------------------- +shell> cmake -L + +Gives a brief overview of important configuration parameters (dump to stdout) + +shell> cmake -LH + +Does the same but also provides a short help text for each parameter. + +shell> cmake -LAH + +Dumps all config parameters (including advanced) to the stdout. + +PACKAGING +--------------------------------------------------------------- +-- Binary distribution -- +Packaging in form of tar.gz archives (or .zip on Windows) is also supported +To create a tar.gz package, + +1)If you're using "generic" Unix build with makefiles + +shell> make package +this will create a tar.gz file in the top level build directory. + +2)On Windows, using "NMake Makefiles" generator + +shell> nmake package + +3)On Windows, using "Visual Studio" generator + +shell> devenv mysql.sln /build relwithdebinfo /project package + +Note On Windows, 7Zip or Winzip must be installed and 7z.exe rsp winzip.exe +need to be in the PATH. + + +Another way to build packages is calling cpack executable directly like +shell> cpack -G TGZ --config CPackConfig.cmake +(-G TGZ is for tar.gz generator, there is also -GZIP) + +-- Source distribution -- +"make dist" target is provided. + +ADDITIONAL MAKE TARGETS: "make install" AND "make test" +---------------------------------------------------------------- +install target also provided for Makefile based generators. Installation +directory can be controlled using configure-time parameter +CMAKE_INSTALL_PREFIX (default is /usr/local. It is also possible to install to +non-configured directory, using + +shell> make install DESTDIR="/some/absolute/path" + +"make test" runs unit tests (uses CTest for it) +"make test-force" runs mysql-test-run.pl tests with --test-force parameter + +FOR PROGRAMMERS: WRITING PLATFORM CHECKS +-------------------------------------------------------------- +If you modify MySQL source and want to add a new platform check,please read +http://www.vtk.org/Wiki/CMake_HowToDoPlatformChecks first. In MySQL, most of +the platform tests are implemented in config.cmake and the template header file +is config.h.cmake + +Bigger chunks of functionality, for example non-trivial macros are implemented +in files /cmake subdirectory. + +For people with autotools background, it is important to remember CMake does +not provide autoheader functionality. That is, when you add a check + +CHECK_FUNCTION_EXISTS(foo HAVE_FOO) +to config.cmake, then you will also need to add +#cmakedefine HAVE_FOO 1 +to config.h.cmake + +Troubleshooting platform checks +-------------------------------- +If you suspect that a platform check returned wrong result, examine +/CMakeFiles/CMakeError.log and +/CMakeFiles/CMakeOutput.log +These files they contain compiler command line, and exact error messages. + +Troubleshooting CMake code +---------------------------------- +While there are advanced flags for cmake like -debug-trycompile and --trace, +a simple and efficient way to debug to add +MESSAGE("interesting variable=${some_invariable}") +to the interesting places in CMakeLists.txt + + +Tips: +- When using Makefile generator it is easy to examine which compiler flags are +used to build. For example, compiler flags for mysqld are in +/sql/CMakeFiles/mysqld.dir/flags.make and the linker command line +is in /sql/CMakeFiles/mysqld.dir/link.txt + +- CMake caches results of platform checks in CMakeCache.txt. It is a nice +feature because tests do not rerun when reconfiguring (e.g when a new test was +added).The downside of caching is that when a platform test was wrong and was +later corrected, the cached result is still used. If you encounter this +situation, which should be a rare occation, you need either to remove the +offending entry from CMakeCache.txt (if test was for HAVE_FOO, remove lines +containing HAVE_FOO from CMakeCache.txt) or just remove the cache file. diff --git a/BUILD/Makefile.am b/BUILD/Makefile.am index cd4b00ea731..312340e7132 100644 --- a/BUILD/Makefile.am +++ b/BUILD/Makefile.am @@ -20,6 +20,7 @@ EXTRA_DIST = FINISH.sh \ SETUP.sh \ autorun.sh \ + choose_configure.sh \ build_mccge.sh \ check-cpu \ cleanup \ diff --git a/BUILD/autorun.sh b/BUILD/autorun.sh index 35a2e56cbdf..f45b1f7d08c 100755 --- a/BUILD/autorun.sh +++ b/BUILD/autorun.sh @@ -20,6 +20,7 @@ do done IFS="$save_ifs" +rm -rf configure aclocal || die "Can't execute aclocal" autoheader || die "Can't execute autoheader" # --force means overwrite ltmain.sh script if it already exists @@ -29,3 +30,9 @@ $LIBTOOLIZE --automake --force --copy || die "Can't execute libtoolize" # and --force to overwrite them if they already exist automake --add-missing --force --copy || die "Can't execute automake" autoconf || die "Can't execute autoconf" +# Do not use autotools generated configure directly. Instead, use a script +# that will either call CMake or original configure shell script at build +# time (CMake is preferred if installed). +mv configure configure.am +cp BUILD/choose_configure.sh configure +chmod a+x configure diff --git a/BUILD/choose_configure.sh b/BUILD/choose_configure.sh new file mode 100644 index 00000000000..71243ea09b6 --- /dev/null +++ b/BUILD/choose_configure.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Choose whether to use autoconf created configure +# of perl script that calls cmake. + +# Ensure cmake and perl are there +cmake -P cmake/check_minimal_version.cmake >/dev/null 2>&1 || HAVE_CMAKE=no +perl --version >/dev/null 2>&1 || HAVE_CMAKE=no +if test "$HAVE_CMAKE" = "no" +then + sh ./configure.am $@ +else + perl ./cmake/configure.pl $@ +fi + diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e53ff30159..3d55d115cff 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2006 MySQL AB +# Copyright (C) 2006-2008 MySQL AB, 2009 Sun Microsystems, 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 @@ -13,308 +13,217 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR) -IF(COMMAND cmake_policy) - cmake_policy(SET CMP0005 NEW) -ENDIF(COMMAND cmake_policy) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) -PROJECT(MySql) +# First, decide about build type (debug or release) +# If custom compiler flags are set or cmake is invoked with -DCMAKE_BUILD_TYPE, +# respect user wishes and do not (re)define CMAKE_BUILD_TYPE. If WITH_DEBUG{_FULL} +# is given, set CMAKE_BUILD_TYPE = Debug. Otherwise, use Relwithdebinfo. -# This reads user configuration, generated by configure.js. -INCLUDE(win/configure.data) -# Hardcode support for CSV storage engine -SET(WITH_CSV_STORAGE_ENGINE TRUE) +IF(CMAKE_BUILD_TYPE) + SET(HAVE_CMAKE_BUILD_TYPE TRUE) +ENDIF() +SET(CUSTOM_C_FLAGS $ENV{CFLAGS}) +IF(NOT CUSTOM_C_FLAGS) + SET(CUSTOM_C_FLAGS ${CMAKE_C_FLAGS}) +ENDIF() -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/include/mysql_version.h.in - ${CMAKE_SOURCE_DIR}/include/mysql_version.h @ONLY) +OPTION(WITH_DEBUG "Use dbug" OFF) +OPTION(WITH_DEBUG_FULL "Use dbug and safemalloc/safemutex. Slow" OFF) -# Set standard options -ADD_DEFINITIONS(-DHAVE_YASSL) -ADD_DEFINITIONS(-DCMAKE_CONFIGD) -ADD_DEFINITIONS(-DDEFAULT_MYSQL_HOME="c:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}/") -ADD_DEFINITIONS(-DDEFAULT_BASEDIR="c:/Program Files/MySQL/") -ADD_DEFINITIONS(-DMYSQL_DATADIR="c:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}/data") -ADD_DEFINITIONS(-DDEFAULT_CHARSET_HOME="c:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}/") -ADD_DEFINITIONS(-DPACKAGE=mysql) -ADD_DEFINITIONS(-DSHAREDIR="share") +IF(NOT HAVE_CMAKE_BUILD_TYPE AND NOT CUSTOM_C_FLAGS) + IF(WITH_DEBUG) + SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Debug build" FORCE) + ELSE() + SET(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "RelWithDebInfo build" FORCE) + ENDIF() +ENDIF() +IF(WITH_DEBUG_FULL) + SET(WITH_DEBUG ON CACHE BOOL "Use DBUG") +ENDIF() -# Set debug options -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFORCE_INIT_OF_VARS") +PROJECT(MySQL) -# Do not use SAFEMALLOC for Windows builds, as Debug CRT has the same functionality -# Neither SAFE_MUTEX works on Windows and it has been explicitely undefined in -# my_pthread.h -IF(NOT WIN32) - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -ENDIF(NOT WIN32) - -SET(localstatedir "C:\\mysql\\data") -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-huge.cnf.sh - ${CMAKE_SOURCE_DIR}/support-files/my-huge.ini @ONLY) -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-innodb-heavy-4G.cnf.sh - ${CMAKE_SOURCE_DIR}/support-files/my-innodb-heavy-4G.ini @ONLY) -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-large.cnf.sh - ${CMAKE_SOURCE_DIR}/support-files/my-large.ini @ONLY) -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-medium.cnf.sh - ${CMAKE_SOURCE_DIR}/support-files/my-medium.ini @ONLY) -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-small.cnf.sh - ${CMAKE_SOURCE_DIR}/support-files/my-small.ini @ONLY) - -IF(CYBOZU) - ADD_DEFINITIONS(-DCYBOZU) -ENDIF(CYBOZU) - -IF(EXTRA_DEBUG) - ADD_DEFINITIONS(-D EXTRA_DEBUG) -ENDIF(EXTRA_DEBUG) - -IF(ENABLED_DEBUG_SYNC) - ADD_DEFINITIONS(-D ENABLED_DEBUG_SYNC) -ENDIF(ENABLED_DEBUG_SYNC) -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DENABLED_DEBUG_SYNC") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DENABLED_DEBUG_SYNC") - -# in some places we use DBUG_OFF -SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDBUG_OFF") -SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") -SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DDBUG_OFF") -SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") - -#TODO: update the code and remove the disabled warnings -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800 /wd4805") -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4800 /wd4805") - -# Disable warnings in Visual Studio 8 and above -IF(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7") - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /wd4996") - SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4996") - SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /wd4996") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /wd4996") - SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /wd4996") - SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /wd4996") -ENDIF(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7") - -IF(CMAKE_GENERATOR MATCHES "Visual Studio 7") - # VS2003 has a bug that prevents linking mysqld with module definition file - # (/DEF option for linker). Linker would incorrectly complain about multiply - # defined symbols. Workaround is to disable dynamic plugins, so /DEF is not - # used. - MESSAGE("Warning: Building MySQL with Visual Studio 2003.NET is no more supported.") - MESSAGE("Please use a newer version of Visual Studio.") - SET(WITHOUT_DYNAMIC_PLUGINS TRUE) -ENDIF(CMAKE_GENERATOR MATCHES "Visual Studio 7") - -# Settings for Visual Studio 7 and above. -IF(MSVC) - # replace /MDd with /MTd - STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) - STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG_INIT ${CMAKE_C_FLAGS_DEBUG_INIT}) - - STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) - STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_CXX_FLAGS_DEBUG_INIT}) - - # generate map files, set stack size (see bug#20815) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MAP /MAPINFO:EXPORTS") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:1048576") - - # remove support for Exception handling - STRING(REPLACE "/GX" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS_INIT ${CMAKE_CXX_FLAGS_INIT}) - STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_CXX_FLAGS_DEBUG_INIT}) - - # Mark 32 bit executables large address aware so they can - # use > 2GB address space - IF(CMAKE_SIZEOF_VOID_P MATCHES 4) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") - ENDIF(CMAKE_SIZEOF_VOID_P MATCHES 4) - - # Disable automatic manifest generation. - STRING(REPLACE "/MANIFEST" "/MANIFEST:NO" CMAKE_EXE_LINKER_FLAGS - ${CMAKE_EXE_LINKER_FLAGS}) - # Explicitly disable it since it is the default for newer versions of VS - STRING(REGEX MATCH "MANIFEST:NO" tmp_manifest ${CMAKE_EXE_LINKER_FLAGS}) - IF(NOT tmp_manifest) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") - ENDIF(NOT tmp_manifest) -ENDIF(MSVC) +IF(CYGWIN) + SET(WIN32 0) +ENDIF() IF(WIN32) - ADD_DEFINITIONS("-D_WINDOWS -D__WIN__ -D_CRT_SECURE_NO_DEPRECATE") - ADD_DEFINITIONS("-D_WIN32_WINNT=0x0501") -ENDIF(WIN32) + SET(IF_WIN 0) +ELSE() + SET(IF_WIN 1) +ENDIF() -# default to x86 platform. We'll check for X64 in a bit -SET (PLATFORM X86) +# Add macros +INCLUDE(cmake/character_sets.cmake) +INCLUDE(cmake/zlib.cmake) +INCLUDE(cmake/ssl.cmake) +INCLUDE(cmake/readline.cmake) +INCLUDE(cmake/mysql_version.cmake) +INCLUDE(cmake/misc.cmake) +INCLUDE(cmake/dtrace.cmake) +INCLUDE(cmake/plugin.cmake) +INCLUDE(cmake/install_macros.cmake) -# This definition is necessary to work around a bug with Intellisense described -# here: http://tinyurl.com/2cb428. Syntax highlighting is important for proper -# debugger functionality. -IF(CMAKE_SIZEOF_VOID_P MATCHES 8) - MESSAGE(STATUS "Detected 64-bit platform.") - ADD_DEFINITIONS("-D_WIN64") - SET (PLATFORM X64) -ENDIF(CMAKE_SIZEOF_VOID_P MATCHES 8) +# Handle options +OPTION(ENABLED_PROFILING "Enable profiling" ON) +OPTION(CYBOZU "" OFF) +OPTION(BACKUP_TEST "" OFF) +OPTION(WITHOUT_SERVER OFF) +MARK_AS_ADVANCED(CYBOZU BACKUP_TEST WITHOUT_SERVER) -IF(EMBED_MANIFESTS) - # Search for the tools (mt, makecat, signtool) necessary for embedding - # manifests and signing executables with the MySQL AB authenticode cert. - # - # CMake will first search it's defaults (CMAKE_FRAMEWORK_PATH, - # CMAKE_APPBUNDLE_PATH, CMAKE_PROGRAM_PATH and the system PATH) followed - # by the listed paths which are the current possible defaults and should be - # updated when necessary. - # - # The custom manifests are designed to be compatible with all mt versions. - # The MySQL AB Authenticode certificate is available only internally. - # Others should store a single signing certificate in a local cryptographic - # service provider and alter the signtool command as necessary. - FIND_PROGRAM(HAVE_MANIFEST_TOOL NAMES mt - PATHS - "$ENV{PROGRAMFILES}/Microsoft Visual Studio 8/VC/bin" - "$ENV{PROGRAMFILES}/Microsoft Visual Studio 8/Common7/Tools/Bin" - "$ENV{PROGRAMFILES}/Microsoft Visual Studio 8/SDK/v2.0/Bin") - FIND_PROGRAM(HAVE_CATALOG_TOOL NAMES makecat - PATHS - "$ENV{PROGRAMFILES}/Microsoft Visual Studio 8/Common7/Tools/Bin") - FIND_PROGRAM(HAVE_SIGN_TOOL NAMES signtool - PATHS - "$ENV{PROGRAMFILES}/Microsoft Visual Studio 8/Common7/Tools/Bin" - "$ENV{PROGRAMFILES}/Microsoft Visual Studio 8/SDK/v2.0/Bin") + +OPTION(ENABLE_DEBUG_SYNC "Enable debug sync (debug builds only)" ON) +IF(ENABLE_DEBUG_SYNC) + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DENABLED_DEBUG_SYNC") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DENABLED_DEBUG_SYNC") +ENDIF() + +OPTION(WITH_ERROR_INJECT + "Enable error injection in MySQL Server (debug builds only)" OFF) +IF(WITH_ERROR_INJECT) + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DERROR_INJECT_SUPPORT") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DERROR_INJECT_SUPPORT") +ENDIF() - IF(HAVE_MANIFEST_TOOL) - MESSAGE(STATUS "Found Mainfest Tool.") - ELSE(HAVE_MANIFEST_TOOL) - MESSAGE(FATAL_ERROR "Manifest tool, mt.exe, can't be found.") - ENDIF(HAVE_MANIFEST_TOOL) - IF(HAVE_CATALOG_TOOL) - MESSAGE(STATUS "Found Catalog Tool.") - ELSE(HAVE_CATALOG_TOOL) - MESSAGE(FATAL_ERROR "Catalog tool, makecat.exe, can't be found.") - ENDIF(HAVE_CATALOG_TOOL) - IF(HAVE_SIGN_TOOL) - MESSAGE(STATUS "Found Sign Tool. Embedding custom manifests and signing executables.") - ELSE(HAVE_SIGN_TOOL) - MESSAGE(FATAL_ERROR "Sign tool, signtool.exe, can't be found.") - ENDIF(HAVE_SIGN_TOOL) - # Set the processor architecture. - IF(CMAKE_GENERATOR MATCHES "Visual Studio 8 2005 Win64") - SET(PROCESSOR_ARCH "amd64") - ELSE(CMAKE_GENERATOR MATCHES "Visual Studio 8 2005 Win64") - SET(PROCESSOR_ARCH "X86") - ENDIF(CMAKE_GENERATOR MATCHES "Visual Studio 8 2005 Win64") -ENDIF(EMBED_MANIFESTS) -# Figure out what engines to build and how (statically or dynamically), -# add preprocessor defines for storage engines. -IF(WITHOUT_DYNAMIC_PLUGINS) - MESSAGE("Dynamic plugins are disabled.") -ENDIF(WITHOUT_DYNAMIC_PLUGINS) +OPTION(ENABLE_LOCAL_INFILE + "If we should should enable LOAD DATA LOCAL by default" ${IF_WIN}) +MARK_AS_ADVANCED(ENABLE_LOCAL_INFILE) -FILE(GLOB STORAGE_SUBDIRS storage/*) -FOREACH(SUBDIR ${STORAGE_SUBDIRS}) - FILE(RELATIVE_PATH DIRNAME ${PROJECT_SOURCE_DIR}/storage ${SUBDIR}) - IF (EXISTS ${SUBDIR}/CMakeLists.txt) - # Check MYSQL_STORAGE_ENGINE macro is present - FILE(STRINGS ${SUBDIR}/CMakeLists.txt HAVE_STORAGE_ENGINE REGEX MYSQL_STORAGE_ENGINE) - IF(HAVE_STORAGE_ENGINE) - # Extract name of engine from HAVE_STORAGE_ENGINE - STRING(REGEX REPLACE ".*MYSQL_STORAGE_ENGINE\\((.*\)\\).*" - "\\1" ENGINE_NAME ${HAVE_STORAGE_ENGINE}) - STRING(TOUPPER ${ENGINE_NAME} ENGINE) - STRING(TOLOWER ${ENGINE_NAME} ENGINE_LOWER) - - SET(ENGINE_BUILD_TYPE "DYNAMIC") - # Read plug.in to find out if a plugin is mandatory and whether it supports - # build as shared library (dynamic). - IF(EXISTS ${SUBDIR}/plug.in) - FILE(READ ${SUBDIR}/plug.in PLUGIN_FILE_CONTENT) - STRING (REGEX MATCH "MYSQL_PLUGIN_DYNAMIC" MYSQL_PLUGIN_DYNAMIC ${PLUGIN_FILE_CONTENT}) - STRING (REGEX MATCH "MYSQL_PLUGIN_MANDATORY" MYSQL_PLUGIN_MANDATORY ${PLUGIN_FILE_CONTENT}) - STRING (REGEX MATCH "MYSQL_PLUGIN_STATIC" MYSQL_PLUGIN_STATIC ${PLUGIN_FILE_CONTENT}) +# Set DBUG_OFF for non-debug project types. +FOREACH(BUILD_TYPE RELEASE RELWITHDEBINFO MINSIZEREL) + FOREACH(LANG C CXX) + SET(CMAKE_${LANG}_FLAGS_${BUILD_TYPE} + "${CMAKE_${LANG}_FLAGS_${BUILD_TYPE}} -DDBUG_OFF") + ENDFOREACH() +ENDFOREACH() - IF(MYSQL_PLUGIN_MANDATORY) - SET(WITH_${ENGINE}_STORAGE_ENGINE TRUE) - ENDIF(MYSQL_PLUGIN_MANDATORY) +IF(NOT CMAKE_BUILD_TYPE + AND NOT CMAKE_GENERATOR MATCHES "Visual Studio" + AND NOT CMAKE_GENERATOR MATCHES "Xcode") + # This is the case of no CMAKE_BUILD_TYPE choosen, typical for VS and Xcode + # or if custom C flags are set. In VS and Xcode for non-Debug configurations + # DBUG_OFF is already correctly set. Use DBUG_OFF for Makefile based projects + # without build type too, unless user specifically requests DBUG. + IF(NOT CMAKE_C_FLAGS MATCHES "-DDBUG_ON") + ADD_DEFINITIONS(-DDBUG_OFF) + ENDIF() +ENDIF() - IF (WITH_${ENGINE}_STORAGE_ENGINE AND MYSQL_PLUGIN_STATIC) - SET(ENGINE_BUILD_TYPE "STATIC") - ELSEIF(NOT WITHOUT_${ENGINE}_STORAGE_ENGINE AND MYSQL_PLUGIN_DYNAMIC AND NOT WITHOUT_DYNAMIC_PLUGINS) - SET(ENGINE_BUILD_TYPE "DYNAMIC") - ELSE(WITH_${ENGINE}_STORAGE_ENGINE AND MYSQL_PLUGIN_STATIC) - SET(ENGINE_BUILD_TYPE "NONE") - ENDIF(WITH_${ENGINE}_STORAGE_ENGINE AND MYSQL_PLUGIN_STATIC) - IF (ENGINE_BUILD_TYPE STREQUAL "STATIC") - SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_${ENGINE_LOWER}_plugin") - SET (MYSQLD_STATIC_ENGINE_LIBS ${MYSQLD_STATIC_ENGINE_LIBS} ${ENGINE_LOWER}) - SET (STORAGE_ENGINE_DEFS "${STORAGE_ENGINE_DEFS} -DWITH_${ENGINE}_STORAGE_ENGINE") - SET (WITH_${ENGINE}_STORAGE_ENGINE TRUE) - SET (${ENGINE}_DIR ${DIRNAME}) - ENDIF (ENGINE_BUILD_TYPE STREQUAL "STATIC") - ENDIF(EXISTS ${SUBDIR}/plug.in) +# Add safemalloc and safemutex for debug condifurations, except on Windows +# (C runtime library provides safemalloc functionality and safemutex has never +# worked there) +IF(WITH_DEBUG OR WITH_DEBUG_FULL AND NOT WIN32) + FOREACH(LANG C CXX) + IF(WITH_DEBUG_FULL) + SET(CMAKE_${LANG}_FLAGS_DEBUG "-DSAFEMALLOC -DSAFE_MUTEX") + ELSE() + SET(CMAKE_${LANG}_FLAGS_DEBUG "-DSAFE_MUTEX") + ENDIF() + ENDFOREACH() +ENDIF() - IF(NOT ENGINE_BUILD_TYPE STREQUAL "NONE") - LIST(APPEND ${ENGINE_BUILD_TYPE}_ENGINE_DIRECTORIES ${SUBDIR}) - ENDIF(NOT ENGINE_BUILD_TYPE STREQUAL "NONE") - - ENDIF(HAVE_STORAGE_ENGINE) - ENDIF(EXISTS ${SUBDIR}/CMakeLists.txt) -ENDFOREACH(SUBDIR ${STORAGE_SUBDIRS}) -# Special handling for partition(not really pluggable) -IF(NOT WITHOUT_PARTITION_STORAGE_ENGINE) - SET (STORAGE_ENGINE_DEFS "${STORAGE_ENGINE_DEFS} -DWITH_PARTITION_STORAGE_ENGINE") - SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_partition_plugin") -ENDIF(NOT WITHOUT_PARTITION_STORAGE_ENGINE) -ADD_DEFINITIONS(${STORAGE_ENGINE_DEFS}) -# Now write out our mysql_plugin_defs struct -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc.in - ${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc @ONLY) +# Set commonly used variables +IF(WIN32) + SET(DEFAULT_MYSQL_HOME "C:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}/" ) + SET(SHAREDIR share) +ELSE() + SET(DEFAULT_MYSQL_HOME ${CMAKE_INSTALL_PREFIX}/mysql/) + SET(SHAREDIR ${DEFAULT_MYSQL_HOME}share) +ENDIF() -# Add subdirectories for storage engines -SET (ENGINE_BUILD_TYPE "STATIC") -FOREACH(DIR ${STATIC_ENGINE_DIRECTORIES}) - ADD_SUBDIRECTORY(${DIR}) -ENDFOREACH(DIR ${STATIC_ENGINE_DIRECTORIES}) +SET(DEFAULT_BASEDIR "${DEFAULT_MYSQL_HOME}") +SET(MYSQL_DATADIR "${DEFAULT_MYSQL_HOME}data") +SET(DEFAULT_CHARSET_HOME "${DEFAULT_MYSQL_HOME}") -SET (ENGINE_BUILD_TYPE "DYNAMIC") -FOREACH(DIR ${DYNAMIC_ENGINE_DIRECTORIES}) - ADD_SUBDIRECTORY(${DIR}) -ENDFOREACH(DIR ${DYNAMIC_ENGINE_DIRECTORIES}) -# Add subdirectories for semisync plugin -IF(NOT WITHOUT_DYNAMIC_PLUGINS) - ADD_SUBDIRECTORY(plugin/semisync) -ENDIF(NOT WITHOUT_DYNAMIC_PLUGINS) +# Optionally read user configuration, generated by configure.js. +# This is left for backward compatibility reasons only. +IF(WIN32) + INCLUDE(win/configure.data OPTIONAL) +ENDIF() -# FIXME "debug" only needed if build type is "Debug", but -# CMAKE_BUILD_TYPE is not set during configure time. -ADD_SUBDIRECTORY(vio) +# Run platform tests +INCLUDE(configure.cmake) + +# Common defines and includes +ADD_DEFINITIONS(-DHAVE_CONFIG_H) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include) + +# Add bundled or system zlib. +MYSQL_CHECK_ZLIB_WITH_COMPRESS() +# Optionally add bundled yassl/taocrypt or system openssl. +MYSQL_CHECK_SSL() +# Add readline or libedit. +MYSQL_CHECK_READLINE() + +IF(NOT WITHOUT_SERVER) + # Add storage engines and plugins. + CONFIGURE_PLUGINS() +ENDIF() + +ADD_SUBDIRECTORY(include) ADD_SUBDIRECTORY(dbug) ADD_SUBDIRECTORY(strings) +ADD_SUBDIRECTORY(vio) ADD_SUBDIRECTORY(regex) ADD_SUBDIRECTORY(mysys) -ADD_SUBDIRECTORY(scripts) -ADD_SUBDIRECTORY(zlib) -ADD_SUBDIRECTORY(extra/yassl) -ADD_SUBDIRECTORY(extra/yassl/taocrypt) -ADD_SUBDIRECTORY(extra) -ADD_SUBDIRECTORY(client) -ADD_SUBDIRECTORY(sql) ADD_SUBDIRECTORY(libmysql) -ADD_SUBDIRECTORY(libservices) -ADD_SUBDIRECTORY(tests) -IF(WITH_EMBEDDED_SERVER) - ADD_SUBDIRECTORY(libmysqld) - ADD_SUBDIRECTORY(libmysqld/examples) -ENDIF(WITH_EMBEDDED_SERVER) -ADD_SUBDIRECTORY(mysql-test/lib/My/SafeProcess) + +OPTION (WITH_UNIT_TESTS "Compile MySQL with unit tests" ON) +IF(WITH_UNIT_TESTS) + ENABLE_TESTING() +ENDIF() +IF(WITH_UNIT_TESTS) + ADD_SUBDIRECTORY(unittest/mytap) + ADD_SUBDIRECTORY(unittest/mysys) +ENDIF() + +IF(NOT WITHOUT_SERVER) + ADD_SUBDIRECTORY(tests) + ADD_SUBDIRECTORY(extra) + ADD_SUBDIRECTORY(client) + ADD_SUBDIRECTORY(sql) + ADD_SUBDIRECTORY(sql/share) + ADD_SUBDIRECTORY(libservices) + OPTION (WITH_EMBEDDED_SERVER "Compile MySQL with embedded server" OFF) + IF(WITH_EMBEDDED_SERVER) + ADD_SUBDIRECTORY(libmysqld) + ADD_SUBDIRECTORY(libmysqld/examples) + ENDIF(WITH_EMBEDDED_SERVER) + + ADD_SUBDIRECTORY(mysql-test) + ADD_SUBDIRECTORY(mysql-test/lib/My/SafeProcess) + ADD_SUBDIRECTORY(support-files) + ADD_SUBDIRECTORY(scripts) + ADD_SUBDIRECTORY(sql-bench) + IF(UNIX) + ADD_SUBDIRECTORY(man) + ENDIF() +ENDIF() + +INCLUDE(cmake/abi_check.cmake) + +CONFIGURE_FILE(config.h.cmake ${CMAKE_BINARY_DIR}/include/my_config.h) +CONFIGURE_FILE(config.h.cmake ${CMAKE_BINARY_DIR}/include/config.h) +CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/include/mysql_version.h.in + ${CMAKE_BINARY_DIR}/include/mysql_version.h ) + + +# Packaging +IF(WIN32) + SET(CPACK_GENERATOR "ZIP") +ELSE() + SET(CPACK_GENERATOR "TGZ") +ENDIF() +INCLUDE(CPack) +INSTALL(FILES COPYING EXCEPTIONS-CLIENT README DESTINATION .) +IF(UNIX) + INSTALL(FILES Docs/INSTALL-BINARY DESTINATION .) +ENDIF() diff --git a/Makefile.am b/Makefile.am index a4b02d23393..498184330ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,8 @@ AUTOMAKE_OPTIONS = foreign # These are built from source in the Docs directory EXTRA_DIST = INSTALL-SOURCE INSTALL-WIN-SOURCE \ - README COPYING EXCEPTIONS-CLIENT CMakeLists.txt + README COPYING EXCEPTIONS-CLIENT \ + CMakeLists.txt configure.cmake config.h.cmake BUILD-CMAKE SUBDIRS = . include @docs_dirs@ @zlib_dir@ \ @readline_topdir@ sql-common scripts \ @@ -28,8 +29,8 @@ SUBDIRS = . include @docs_dirs@ @zlib_dir@ \ @sql_server@ @man_dirs@ tests \ netware @libmysqld_dirs@ \ mysql-test support-files sql-bench \ - win - + win \ + cmake DIST_SUBDIRS = . include Docs zlib \ cmd-line-utils sql-common scripts \ pstack libservices \ @@ -38,6 +39,7 @@ DIST_SUBDIRS = . include Docs zlib \ netware libmysqld \ mysql-test support-files sql-bench \ win \ + cmake \ BUILD DISTCLEANFILES = ac_available_languages_fragment @@ -58,6 +60,7 @@ dist-hook: --datadir=$(distdir)/win/data \ --srcdir=$(top_srcdir) storage/myisam/myisamchk --silent --fast $(distdir)/win/data/mysql/*.MYI + test ! -f configure.am || $(INSTALL_DATA) configure.am $(distdir) all-local: @ABI_CHECK@ diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 28e4c354a69..1cda6592a6e 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -12,46 +12,49 @@ # 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("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/zlib - ${CMAKE_SOURCE_DIR}/extra/yassl/include - ${CMAKE_SOURCE_DIR}/libmysql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/strings) +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/include + ${ZLIB_INCLUDE_DIR} + ${SSL_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/libmysql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/strings + ${READLINE_INCLUDE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) -ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc ../mysys/my_conio.c) +ADD_DEFINITIONS(${READLINE_DEFINES}) +ADD_DEFINITIONS(${SSL_DEFINES}) +ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc) TARGET_LINK_LIBRARIES(mysql mysqlclient) +IF(UNIX) + TARGET_LINK_LIBRARIES(mysql ${READLINE_LIBRARY}) +ENDIF(UNIX) ADD_EXECUTABLE(mysqltest mysqltest.cc) SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") -TARGET_LINK_LIBRARIES(mysqltest mysqlclient mysys regex dbug) +TARGET_LINK_LIBRARIES(mysqltest mysqlclient regex) + ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient) -ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c ../mysys/mf_getdate.c) +ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c) TARGET_LINK_LIBRARIES(mysqldump mysqlclient) ADD_EXECUTABLE(mysqlimport mysqlimport.c) TARGET_LINK_LIBRARIES(mysqlimport mysqlclient) -ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c ../mysys/my_getpagesize.c) +ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c) TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient) ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs) ADD_EXECUTABLE(mysqlshow mysqlshow.c) TARGET_LINK_LIBRARIES(mysqlshow mysqlclient) -ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc - ../mysys/mf_tempdir.c - ../mysys/my_new.cc - ../mysys/my_bit.c - ../mysys/my_bitmap.c - ../mysys/my_vle.c - ../mysys/base64.c) +ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc) TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient) ADD_EXECUTABLE(mysqladmin mysqladmin.cc) @@ -59,20 +62,25 @@ TARGET_LINK_LIBRARIES(mysqladmin mysqlclient) ADD_EXECUTABLE(mysqlslap mysqlslap.c) SET_SOURCE_FILES_PROPERTIES(mysqlslap.c PROPERTIES COMPILE_FLAGS "-DTHREADS") -TARGET_LINK_LIBRARIES(mysqlslap mysqlclient mysys zlib dbug) +TARGET_LINK_LIBRARIES(mysqlslap mysqlclient) ADD_EXECUTABLE(echo echo.c) -IF(EMBED_MANIFESTS) - MYSQL_EMBED_MANIFEST("mysql" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqltest" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqlcheck" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqldump" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqlimport" "asInvoker") - MYSQL_EMBED_MANIFEST("mysql_upgrade" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqlshow" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqlbinlog" "asInvoker") - MYSQL_EMBED_MANIFEST("mysqladmin" "asInvoker") - MYSQL_EMBED_MANIFEST("echo" "asInvoker") -ENDIF(EMBED_MANIFESTS) +SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap +PROPERTIES HAS_CXX TRUE) + +SET(INSTALL_EXES + mysqlcheck + mysqldump + mysqlimport + mysql_upgrade + mysqlshow + mysqlslap + mysql + mysqladmin + mysqlbinlog + mysqltest) + +INSTALL(TARGETS ${INSTALL_EXES} DESTINATION bin) +INSTALL_DEBUG_SYMBOLS("${INSTALL_EXES}") diff --git a/cmake/Makefile.am b/cmake/Makefile.am new file mode 100644 index 00000000000..2326f787ab8 --- /dev/null +++ b/cmake/Makefile.am @@ -0,0 +1,21 @@ +EXTRA_DIST = \ + cat.cmake \ + check_minimal_version.cmake \ + create_initial_db.cmake.in \ + make_dist.cmake.in \ + dtrace.cmake \ + abi_check.cmake \ + bison.cmake \ + configure.pl \ + character_sets.cmake \ + misc.cmake \ + readline.cmake \ + mysql_version.cmake \ + install_macros.cmake \ + ssl.cmake \ + plugin.cmake \ + zlib.cmake \ + stack_direction.c \ + do_abi_check.cmake \ + merge_archives_unix.cmake.in \ + dtrace_prelink.cmake diff --git a/cmake/abi_check.cmake b/cmake/abi_check.cmake new file mode 100644 index 00000000000..61b0d9e7970 --- /dev/null +++ b/cmake/abi_check.cmake @@ -0,0 +1,60 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# +# Headers which need to be checked for abi/api compatibility. +# API_PREPROCESSOR_HEADER will be used until mysql_priv.h stablizes +# after which TEST_PREPROCESSOR_HEADER will be used. +# +# We use gcc specific preprocessing command and sed/diff, so it will +# only be run on Unix and only if gcc is used. +IF(CMAKE_COMPILER_IS_GNUCC AND UNIX) + IF(CMAKE_C_COMPILER MATCHES "ccache$") + SET(COMPILER ${CMAKE_C_COMPILER_ARG1}) + STRING(REGEX REPLACE "^ " "" COMPILER ${COMPILER}) + ELSE() + SET(COMPILER ${CMAKE_C_COMPILER}) + ENDIF() + SET(API_PREPROCESSOR_HEADER + ${CMAKE_SOURCE_DIR}/include/mysql/plugin.h + ${CMAKE_SOURCE_DIR}/include/mysql.h) + + SET(TEST_PREPROCESSOR_HEADER + ${CMAKE_SOURCE_DIR}/include/mysql/plugin.h + ${CMAKE_SOURCE_DIR}/sql/mysql_priv.h + ${CMAKE_SOURCE_DIR}/include/mysql.h) + + + ADD_CUSTOM_TARGET(abi_check ALL + COMMAND ${CMAKE_COMMAND} + -DCOMPILER=${COMPILER} + -DSOURCE_DIR=${CMAKE_SOURCE_DIR} + -DBINARY_DIR=${CMAKE_BINARY_DIR} + "-DABI_HEADERS=${API_PREPROCESSOR_HEADER}" + -P ${CMAKE_SOURCE_DIR}/cmake/do_abi_check.cmake + VERBATIM + ) + + ADD_CUSTOM_TARGET(abi_check_all + COMMAND ${CMAKE_COMMAND} + -DCMAKE_C_COMPILER=${COMPILER} + -DCMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR} + -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} + "-DABI_HEADERS=${TEST_PREPROCESSOR_HEADER}" + -P ${CMAKE_SOURCE_DIR}/cmake/scripts/do_abi_check.cmake + VERBATIM + ) +ENDIF() + diff --git a/cmake/bison.cmake b/cmake/bison.cmake new file mode 100644 index 00000000000..b1c2d7393fa --- /dev/null +++ b/cmake/bison.cmake @@ -0,0 +1,64 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +FIND_PROGRAM(BISON_EXECUTABLE bison DOC "path to the bison executable") +MARK_AS_ADVANCED(BISON_EXECUTABLE "") +IF(NOT BISON_EXECUTABLE) + MESSAGE("Warning: Bison executable not found in PATH") +ELSEIF(BISON_EXECUTABLE AND NOT BISON_USABLE) + # Check version as well + EXEC_PROGRAM(${BISON_EXECUTABLE} ARGS --version OUTPUT_VARIABLE BISON_VERSION_STR) + # Get first line in case it's multiline + STRING(REGEX REPLACE "([^\n]+).*" "\\1" FIRST_LINE "${BISON_VERSION_STR}") + # get version information + STRING(REGEX REPLACE ".* ([0-9]+)\\.([0-9]+)" "\\1" BISON_VERSION_MAJOR "${FIRST_LINE}") + STRING(REGEX REPLACE ".* ([0-9]+)\\.([0-9]+)" "\\2" BISON_VERSION_MINOR "${FIRST_LINE}") + IF (BISON_VERSION_MAJOR LESS 2) + MESSAGE("Warning: bison version is old. please update to version 2") + ELSE() + SET(BISON_USABLE 1 CACHE INTERNAL "Bison version 2 or higher") + ENDIF() +ENDIF() + +# Use bison to generate C++ and header file +MACRO (RUN_BISON input_yy output_cc output_h) + IF(BISON_TOO_OLD) + IF(EXISTS ${output_cc} AND EXISTS ${output_h}) + SET(BISON_USABLE FALSE) + ENDIF() + ENDIF() + IF(BISON_USABLE) + ADD_CUSTOM_COMMAND( + OUTPUT ${output_cc} + ${output_h} + COMMAND bison -y -p MYSQL + --output=${output_cc} + --defines=${output_h} + ${input_yy} + DEPENDS ${input_yy} + ) + ELSE() + # Bison is missing or not usable, e.g too old + IF(EXISTS ${output_cc} AND EXISTS ${output_h}) + IF(${input_yy} IS_NEWER_THAN ${output_cc} OR ${input_yy} IS_NEWER_THAN ${output_h}) + # Possibly timestamps are messed up in source distribution. + MESSAGE("Warning: no usable bison found, ${input_yy} will not be rebuilt.") + ENDIF() + ELSE() + # Output files are missing, bail out. + MESSAGE(FATAL_ERROR "Please install bison.") + ENDIF() + ENDIF() +ENDMACRO() diff --git a/cmake/cat.cmake b/cmake/cat.cmake new file mode 100644 index 00000000000..532f4115104 --- /dev/null +++ b/cmake/cat.cmake @@ -0,0 +1,27 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Concatenate files +# +# Parameters : +# IN - input files (list) +# OUT - output file +FILE(WRITE ${OUT} "") +FOREACH(FILENAME ${IN}) + FILE(READ ${FILENAME} CONTENTS) + FILE(APPEND ${OUT} "${CONTENTS}") +ENDFOREACH() + + diff --git a/cmake/character_sets.cmake b/cmake/character_sets.cmake new file mode 100644 index 00000000000..c21b6c60113 --- /dev/null +++ b/cmake/character_sets.cmake @@ -0,0 +1,63 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#Charsets and collations +IF(NOT DEFAULT_CHARSET) +SET(DEFAULT_CHARSET "latin1") +ENDIF() + +IF(NOT DEFAULT_COLLATIONS) +SET(DEFAULT_COLLATION "latin1_swedish_ci") +ENDIF() + +SET(CHARSETS ${DEFAULT_CHARSET} latin1 utf8 utf8mb3) +SET(CHARSETS_COMPLEX big5 cp1250 cp932 eucjpms euckr gb2312 gbk latin1 latin2 sjis tis620 ucs2 ujis utf8 utf8mb3 utf16 utf32) + +SET(CHARSETS_AVAILABLE +binary armscii8 ascii big5 cp1250 cp1251 cp1256 cp1257 +cp850 cp852 cp866 cp932 dec8 eucjpms euckr gb2312 gbk geostd8 +greek hebrew hp8 keybcs2 koi8r koi8u +latin1 latin2 latin5 latin7 macce macroman +sjis swe7 tis620 ucs2 ujis utf8 utf8mb3 utf16 utf32) + + +IF(WIN32) + SET (EXTRA_CHARSETS "all") +ELSE() + SET (EXTRA_CHARSETS "none") +ENDIF() +SET(WITH_EXTRA_CHARSETS ${EXTRA_CHARSETS} CACHE + STRING "Options are: none, complex,all") + + +IF(WITH_EXTRA_CHARSETS MATCHES "complex") + SET(CHARSETS ${CHARSETS} ${CHARSETS_COMPLEX}) +ELSEIF(WITH_EXTRA_CHARSETS MATCHES "all") + SET(CHARSETS ${CHARSETS} ${CHARSETS_AVAILABLE}) +ENDIF() + +SET(MYSQL_DEFAULT_CHARSET_NAME "${DEFAULT_CHARSET}") +SET(MYSQL_DEFAULT_COLLATION_NAME "${DEFAULT_COLLATION}") + +FOREACH(cs in ${CHARSETS}) + SET(HAVE_CHARSET_${cs} 1) +ENDFOREACH() + +SET(HAVE_UCA_COLLATIONS 1) + +SET(HAVE_UTF8_GENERAL_CS 1) +SET(USE_MB 1) +SET(USE_MB_IDENT 1) + diff --git a/cmake/check_minimal_version.cmake b/cmake/check_minimal_version.cmake new file mode 100644 index 00000000000..4f134712fae --- /dev/null +++ b/cmake/check_minimal_version.cmake @@ -0,0 +1,19 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This is a helper script is used to check for the minimal required version +# It helps to decide whether to use autoconf based configure or cmake's +# configure +CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) diff --git a/cmake/configure.pl b/cmake/configure.pl new file mode 100644 index 00000000000..ee7846b9c36 --- /dev/null +++ b/cmake/configure.pl @@ -0,0 +1,79 @@ +#!/usr/bin/perl +use Switch; + +my $cmakeargs=""; +foreach $option (@ARGV) +{ + if (substr ($option, 0, 2) == "--") + { + $option = substr($option, 2); + } + if($option =~ /help/) + { + system("cmake -LH"); + exit(0); + } + if($option =~ /with-plugins=/) + { + my @plugins= split(/,/, substr($option,13)); + foreach $p (@plugins) + { + $p =~ s/-/_/g; + $cmakeargs = $cmakeargs." -DWITH_".uc($p)."=1"; + } + next; + } + if($option =~ /with-extra-charsets=/) + { + my $charsets= substr($option,20); + $cmakeargs = $cmakeargs." -DWITH_EXTRA_CHARSETS=".$charsets; + next; + } + if($option =~ /without-plugin=/) + { + $cmakeargs = $cmakeargs." -DWITHOUT_".uc(substr($option,15))."=1"; + next; + } + if($option =~ /with-zlib-dir=bundled/) + { + $cmakeargs = $cmakeargs." -DWITH_ZLIB=bundled"; + next; + } + if($option =~ /with-zlib-dir=/) + { + $cmakeargs = $cmakeargs." -DWITH_ZLIB=system"; + next; + } + if($option =~ /with-ssl=/) + { + $cmakeargs = $cmakeargs." -DWITH_SSL=bundled"; + next; + } + if($option =~ /with-ssl/) + { + $cmakeargs = $cmakeargs." -DWITH_SSL=yes"; + next; + } + if($option =~ /prefix=/) + { + my $cmake_install_prefix= substr($option, 7); + $cmakeargs = $cmakeargs." -DCMAKE_INSTALL_PREFIX=".$cmake_install_prefix; + next; + } + if ($options =~ /extra-charsets=all/) + { + $cmakeargs = $cmakeargs." -DWITH_CHARSETS=all"; + next; + } + if ($options =~ /extra-charsets=complex/) + { + $cmakeargs = $cmakeargs." -DWITH_CHARSETS=complex"; + next; + } + $option = uc($option); + $option =~ s/-/_/g; + $cmakeargs = $cmakeargs." -D".$option."=1"; +} +print("configure.pl : calling cmake . $cmakeargs\n"); +my $rc = system("cmake . $cmakeargs"); +exit($rc); diff --git a/cmake/create_initial_db.cmake.in b/cmake/create_initial_db.cmake.in new file mode 100644 index 00000000000..92ebc72d425 --- /dev/null +++ b/cmake/create_initial_db.cmake.in @@ -0,0 +1,84 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This script creates initial database for packaging on Windows +SET(CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@") +SET(CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@") +SET(MYSQLD_EXECUTABLE "@MYSQLD_EXECUTABLE@") +SET(CMAKE_CFG_INTDIR "@CMAKE_CFG_INTDIR@") +SET(WIN32 "@WIN32@") +SET ($ENV{VS_UNICODE_OUTPUT}) +IF(CMAKE_CFG_INTDIR) + IF(CONFIG) + MESSAGE(${CONFIG}) + #Resolve build configuration variables + STRING(REPLACE "${CMAKE_CFG_INTDIR}" ${CONFIG} MYSQLD_EXECUTABLE + "${MYSQLD_EXECUTABLE}") + ELSE() + MESSAGE(FATAL_ERROR "Please provide CONFIG parameter to the script") + ENDIF() +ENDIF() + +# Create bootstrapper SQL script +FILE(WRITE bootstrap.sql "use mysql;\n" ) +FOREACH(FILENAME mysql_system_tables.sql mysql_system_tables_data.sql + fill_help_tables.sql) + FILE(STRINGS ${CMAKE_SOURCE_DIR}/scripts/${FILENAME} CONTENTS) + FOREACH(STR ${CONTENTS}) + IF(NOT STR MATCHES "@current_hostname") + FILE(APPEND bootstrap.sql "${STR}\n") + ENDIF() + ENDFOREACH() +ENDFOREACH() + + +MAKE_DIRECTORY(data) +FILE(REMOVE_RECURSE data/mysql) +MAKE_DIRECTORY(data/mysql) +IF(WIN32) + SET(CONSOLE --console) +ENDIF() + +SET(BOOTSTRAP_COMMAND + ${MYSQLD_EXECUTABLE} + --no-defaults + ${CONSOLE} + --bootstrap + --lc-messages-dir=${CMAKE_CURRENT_BINARY_DIR}/share + --basedir=. + --datadir=. + --loose-skip-innodb + --loose-skip-ndbcluster + --max_allowed_packet=8M + --net_buffer_length=16K +) + +GET_FILENAME_COMPONENT(CWD . ABSOLUTE) +EXECUTE_PROCESS( + COMMAND "@CMAKE_COMMAND@" -E echo Executing ${BOOTSTRAP_COMMAND} +) +EXECUTE_PROCESS ( + COMMAND "@CMAKE_COMMAND@" -E echo input file bootstrap.sql, current directory ${CWD} +) +EXECUTE_PROCESS ( + COMMAND ${BOOTSTRAP_COMMAND} INPUT_FILE bootstrap.sql OUTPUT_VARIABLE OUT + ERROR_VARIABLE ERR + RESULT_VARIABLE RESULT + ) + +IF(NOT RESULT EQUAL 0) + MESSAGE(FATAL_ERROR "Could not create initial database \n ${OUT} \n ${ERR}") +ENDIF() + \ No newline at end of file diff --git a/cmake/do_abi_check.cmake b/cmake/do_abi_check.cmake new file mode 100644 index 00000000000..aa7784e7372 --- /dev/null +++ b/cmake/do_abi_check.cmake @@ -0,0 +1,78 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# +# Rules for checking that the abi/api has not changed. +# +# The following steps are followed in the do_abi_check rule below +# +# 1) Generate preprocessor output for the files that need to +# be tested for abi/api changes. use -nostdinc to prevent +# generation of preprocessor output for system headers. This +# results in messages in stderr saying that these headers +# were not found. Redirect the stderr output to /dev/null +# to prevent seeing these messages. +# 2) sed the output to +# 2.1) remove blank lines and lines that begin with "# " +# 2.2) When gcc -E is run on the Mac OS and solaris sparc platforms it +# introduces a line of output that shows up as a difference between +# the .pp and .out files. Remove these OS specific preprocessor text +# inserted by the preprocessor. +# 3) diff the generated file and the canons (.pp files already in +# the repository). +# 4) delete the .out file that is generated. +# +# If the diff fails, the generated file is not removed. This will +# be useful for analysis of ABI differences (e.g. using a visual +# diff tool). +# +# A ABI change that causes a build to fail will always be accompanied +# by new canons (.out files). The .out files that are not removed will +# be replaced as the new .pp files. +# +# e.g. If include/mysql/plugin.h has an ABI change then this rule would +# leave a /abi_check.out file. +# +# A developer with a justified API change will then do a +# mv /abi_check.out include/mysql/plugin.pp +# to replace the old canons with the new ones. +# + +SET(abi_check_out ${BINARY_DIR}/abi_check.out) + +FOREACH(file ${ABI_HEADERS}) + SET(tmpfile ${file}.pp.tmp) + EXECUTE_PROCESS( + COMMAND ${COMPILER} + -E -nostdinc -dI -I${SOURCE_DIR}/include -I${BINARY_DIR}/include + -I${SOURCE_DIR}/include/mysql -I${SOURCE_DIR}/sql ${file} + ERROR_QUIET OUTPUT_FILE ${tmpfile}) + EXECUTE_PROCESS( + COMMAND sed -e + "/^# /d" -e "/^[ ]*$/d" -e "/^#pragma GCC set_debug_pwd/d" -e "/^#ident/d" + RESULT_VARIABLE result OUTPUT_FILE ${abi_check_out} INPUT_FILE ${tmpfile}) + IF(NOT ${result} EQUAL 0) + MESSAGE(FATAL_ERROR "sed returned error ${result}") + ENDIF() + FILE(REMOVE ${tmpfile}) + EXECUTE_PROCESS(COMMAND diff -w ${file}.pp ${abi_check_out} RESULT_VARIABLE + result) + IF(NOT ${result} EQUAL 0) + MESSAGE(FATAL_ERROR + "ABI check found difference between ${file}.pp and ${abi_check_out}") + ENDIF() + FILE(REMOVE ${abi_check_out}) +ENDFOREACH() + diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake new file mode 100644 index 00000000000..5c031e04841 --- /dev/null +++ b/cmake/dtrace.cmake @@ -0,0 +1,92 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Check if OS supports DTrace +MACRO(CHECK_DTRACE) + FIND_PROGRAM(DTRACE dtrace) + MARK_AS_ADVANCED(DTRACE) + + # On FreeBSD, dtrace does not handle userland tracing yet + IF(DTRACE AND NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + SET(ENABLE_DTRACE ON CACHE BOOL "Enable dtrace") + ENDIF() + SET(HAVE_DTRACE ${ENABLE_DTRACE}) + IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + IF(CMAKE_SIZEOF_VOID_P EQUAL 4) + SET(DTRACE_FLAGS -32 CACHE INTERNAL "DTrace architecture flags") + ELSE() + SET(DTRACE_FLAGS -64 CACHE INTERNAL "DTrace architecture flags") + ENDIF() + ENDIF() +ENDMACRO() + +CHECK_DTRACE() + +# Produce a header file with +# DTrace macros +MACRO (DTRACE_HEADER provider header header_no_dtrace) + IF(ENABLE_DTRACE) + ADD_CUSTOM_COMMAND( + OUTPUT ${header} ${header_no_dtrace} + COMMAND ${DTRACE} -h -s ${provider} -o ${header} + COMMAND perl ${CMAKE_SOURCE_DIR}/scripts/dheadgen.pl -f ${provider} > ${header_no_dtrace} + DEPENDS ${provider} + ) + ENDIF() +ENDMACRO() + + +# Create provider headers +IF(ENABLE_DTRACE) + CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/include/probes_mysql.d.base + ${CMAKE_BINARY_DIR}/include/probes_mysql.d COPYONLY) + DTRACE_HEADER( + ${CMAKE_BINARY_DIR}/include/probes_mysql.d + ${CMAKE_BINARY_DIR}/include/probes_mysql_dtrace.h + ${CMAKE_BINARY_DIR}/include/probes_mysql_nodtrace.h + ) + ADD_CUSTOM_TARGET(gen_dtrace_header + DEPENDS + ${CMAKE_BINARY_DIR}/include/probes_mysql.d + ${CMAKE_BINARY_DIR}/include/probes_mysql_dtrace.h + ${CMAKE_BINARY_DIR}/include/probes_mysql_nodtrace.h + ) +ENDIF() + + +MACRO (DTRACE_INSTRUMENT target) + IF(ENABLE_DTRACE) + ADD_DEPENDENCIES(${target} gen_dtrace_header) + + # On Solaris, invoke dtrace -G to generate object file and + # link it together with target. + IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET(objdir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir) + SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/${target}_dtrace.o) + + ADD_CUSTOM_COMMAND( + TARGET ${target} PRE_LINK + COMMAND ${CMAKE_COMMAND} + -DDTRACE=${DTRACE} + -DOUTFILE=${outfile} + -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d + -DDTRACE_FLAGS=${DTRACE_FLAGS} + -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake + WORKING_DIRECTORY ${OBJDIR} + ) + SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS "${outfile}") + ENDIF() + ENDIF() +ENDMACRO() diff --git a/cmake/dtrace_prelink.cmake b/cmake/dtrace_prelink.cmake new file mode 100644 index 00000000000..53e12407cb9 --- /dev/null +++ b/cmake/dtrace_prelink.cmake @@ -0,0 +1,34 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Generates an ELF object file with dtrace entry points. +# This object that must to be linked with together with +# the target. This script needs to run on Solaris only + +# Do not follow symlinks in GLOB_RECURSE +CMAKE_POLICY(SET CMP0009 NEW) +FILE(GLOB_RECURSE OBJECTS *.o) + +# Use relative paths to generate shorter command line +GET_FILENAME_COMPONENT(CURRENT_ABS_DIR . ABSOLUTE) +FOREACH(OBJ ${OBJECTS}) + FILE(RELATIVE_PATH REL ${CURRENT_ABS_DIR} ${OBJ}) + LIST(APPEND REL_OBJECTS ${REL}) +ENDFOREACH() + +EXECUTE_PROCESS( + COMMAND ${DTRACE} ${DTRACE_FLAGS} -o ${OUTFILE} -G -s ${DFILE} ${REL_OBJECTS} +) + diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake new file mode 100644 index 00000000000..4fc6db6b9b7 --- /dev/null +++ b/cmake/install_macros.cmake @@ -0,0 +1,69 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +MACRO (INSTALL_DEBUG_SYMBOLS targets) + IF(MSVC) + FOREACH(target ${targets}) + GET_TARGET_PROPERTY(location ${target} LOCATION) + GET_TARGET_PROPERTY(type ${target} TYPE) + IF(NOT INSTALL_LOCATION) + IF(type MATCHES "STATIC_LIBRARY" OR type MATCHES "MODULE_LIBRARY" OR type MATCHES "SHARED_LIBRARY") + SET(INSTALL_LOCATION "lib") + ELSEIF(type MATCHES "EXECUTABLE") + SET(INSTALL_LOCATION "bin") + ELSE() + MESSAGE(FATAL_ERROR "cannot determine type of ${target}. Don't now where to install") + ENDIF() + ENDIF() + STRING(REPLACE ".exe" ".pdb" pdb_location ${location}) + STRING(REPLACE ".dll" ".pdb" pdb_location ${pdb_location}) + STRING(REPLACE ".lib" ".pdb" pdb_location ${pdb_location}) + STRING(REPLACE "$(OutDir)" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) + STRING(REPLACE "$(ConfigurationName)" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) + INSTALL(FILES ${pdb_location} DESTINATION ${INSTALL_LOCATION}) + ENDFOREACH() + ENDIF() +ENDMACRO() + +# Install symbolic link to CMake target. +# the link is created in the same directory as target +# and extension will be the same as for target file. +MACRO(INSTALL_SYMLINK linkbasename target destination) +IF(UNIX) + GET_TARGET_PROPERTY(location ${target} LOCATION) + GET_FILENAME_COMPONENT(path ${location} PATH) + GET_FILENAME_COMPONENT(name_we ${location} NAME_WE) + GET_FILENAME_COMPONENT(ext ${location} EXT) + SET(output ${path}/${linkbasename}${ext}) + ADD_CUSTOM_COMMAND( + OUTPUT ${output} + COMMAND ${CMAKE_COMMAND} ARGS -E remove -f ${output} + COMMAND ${CMAKE_COMMAND} ARGS -E create_symlink + ${name_we}${ext} + ${linkbasename}${ext} + WORKING_DIRECTORY ${path} + DEPENDS ${target} + ) + + ADD_CUSTOM_TARGET(symlink_${linkbasename}${ext} + ALL + DEPENDS ${output}) + SET_TARGET_PROPERTIES(symlink_${linkbasename}${ext} PROPERTIES CLEAN_DIRECT_OUTPUT 1) + # For Xcode, replace project config with install config + STRING(REPLACE "$(CONFIGURATION)" "\${CMAKE_INSTALL_CONFIG_NAME}" output ${output}) + INSTALL(FILES ${output} DESTINATION ${destination}) +ENDIF() +ENDMACRO() + diff --git a/cmake/make_dist.cmake.in b/cmake/make_dist.cmake.in new file mode 100644 index 00000000000..aae5bdbc666 --- /dev/null +++ b/cmake/make_dist.cmake.in @@ -0,0 +1,176 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Make source distribution +# If bzr is present, run bzr export, add output of BUILD/autorun.sh +# if autotools are present, also pack bison output into it. +# Otherwise, just run cpack with source configuration. + +SET(CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@") +SET(CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@") +SET(CPACK_SOURCE_PACKAGE_FILE_NAME "@CPACK_SOURCE_PACKAGE_FILE_NAME@") +SET(GLIBTOOLIZE_EXECUTABLE "@GLIBTOOLIZE_EXECUTABLE@") +SET(LIBTOOLIZE_EXECUTABLE "@LIBTOOLIZE_EXECUTABLE@") +SET(ACLOCAL_EXECUTABLE "@ACLOCAL_EXECUTABLE@") +SET(AUTOCONF_EXECUTABLE "@AUTOCONF_EXECUTABLE@") +SET(AUTOHEADER_EXECUTABLE "@AUTOHEADER_EXECUTABLE@") +SET(AUTOMAKE_EXECUTABLE "@AUTOMAKE_EXECUTABLE@") +SET(CMAKE_CPACK_COMMAND "@CMAKE_CPACK_COMMAND@") +SET(CMAKE_COMMAND "@CMAKE_COMMAND@") +SET(BZR_EXECUTABLE "@BZR_EXECUTABLE@") +SET(GTAR_EXECUTABLE "@GTAR_EXECUTABLE@") +SET(TAR_EXECUTABLE "@TAR_EXECUTABLE@") +SET(CMAKE_GENERATOR "@CMAKE_GENERATOR@") +SET(CMAKE_MAKE_PROGRAM "@CMAKE_MAKE_PROGRAM@") +SET(CMAKE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@") + + +SET(PACKAGE_DIR ${CMAKE_BINARY_DIR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}) + +FILE(REMOVE_RECURSE ${PACKAGE_DIR}) +FILE(REMOVE ${PACKAGE_DIR}.tar.gz ) + +IF(BZR_EXECUTABLE) + MESSAGE(STATUS "Running bzr export") + EXECUTE_PROCESS( + COMMAND "${BZR_EXECUTABLE}" export + ${PACKAGE_DIR} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE RESULT + ) + + IF(NOT RESULT EQUAL 0) + SET(BZR_EXECUTABLE) + ENDIF() +ENDIF() + +IF(NOT BZR_EXECUTABLE) + MESSAGE(STATUS "bzr not found or source dir is not a repo, use CPack") + + IF(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + # In-source build is the worst option, we have to cleanup source tree. + + # Save bison output first. + CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql/sql_yacc.cc + ${CMAKE_BINARY_DIR}/sql_yacc.cc COPY_ONLY) + CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql/sql_yacc.h + ${CMAKE_BINARY_DIR}/sql_yacc.h COPY_ONLY) + + IF(CMAKE_GENERATOR MATCHES "Makefiles") + # make clean + EXECUTE_PROCESS( + COMMAND ${CMAKE_MAKE_PROGRAM} clean + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + ENDIF() + + # Restore bison output + CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql_yacc.cc + ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc COPY_ONLY) + CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql_yacc.h + ${CMAKE_BINARY_DIR}/sql/sql_yacc.h COPY_ONLY) + FILE(REMOVE ${CMAKE_BINARY_DIR}/sql_yacc.cc) + FILE(REMOVE ${CMAKE_BINARY_DIR}/sql_yacc.h) + ENDIF() + + EXECUTE_PROCESS( + COMMAND ${CMAKE_CPACK_COMMAND} -G TGZ --config ./CPackSourceConfig.cmake + ${CMAKE_BINARY_DIR}/CPackSourceConfig.cmake + + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} -E tar xzf + ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz + ${PACK_SOURCE_PACKAGE_FILE_NAME} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ENDIF() + +# Try to pack output of BUILD/autorun, if autotools are present +IF(GLIBTOOLIZE_EXECUTABLE OR LIBTOOLIZE_EXECUTABLE) + IF(ACLOCAL_EXECUTABLE AND AUTOMAKE_EXECUTABLE AND AUTOCONF_EXECUTABLE + AND AUTOHEADER_EXECUTABLE) + SET(HAVE_AUTOTOOLS 1) + ENDIF() +ENDIF() + +IF(HAVE_AUTOTOOLS) + EXECUTE_PROCESS(COMMAND BUILD/autorun.sh + WORKING_DIRECTORY ${PACKAGE_DIR}) +ELSE() + MESSAGE( "Autotools not found, resulting source package can only be built" + " with cmake") + CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/configure.pl + ${PACKAGE_DIR}/configure + COPYONLY) + IF(UNIX) + EXECUTE_PROCESS(COMMAND chmod +x ${PACKAGE_DIR}/configure) + ENDIF() +ENDIF() + +# Copy bison output +CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql/sql_yacc.h + ${PACKAGE_DIR}/sql/sql_yacc.h COPYONLY) +CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql/sql_yacc.cc + ${PACKAGE_DIR}/sql/sql_yacc.cc COPYONLY) + + +# In case we used CPack, it could have copied some +# extra files that are not usable on different machines. +FILE(REMOVE ${PACKAGE_DIR}/CMakeCache.txt) + +# When packing source, prefer gnu tar to "cmake -P tar" +# cmake does not preserve timestamps.gnuwin32 tar is broken, cygwin is ok + +IF(CMAKE_SYSTEM_NAME MATCHES "Windows") + IF (EXISTS C:/cygwin/bin/tar.exe) + SET(TAR_EXECUTABLE C:/cygwin/bin/tar.exe) + ENDIF() +ENDIF() + +IF(GTAR_EXECUTABLE) + SET(GNUTAR ${GTAR_EXECUTABLE}) +ELSEIF(TAR_EXECUTABLE) + EXECUTE_PROCESS( + COMMAND "${TAR_EXECUTABLE}" --version + RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR + ) + IF(RESULT EQUAL 0 AND OUT MATCHES "GNU") + SET(GNUTAR ${TAR_EXECUTABLE}) + ENDIF() +ENDIF() + +SET($ENV{GZIP} "--best") + +IF(GNUTAR) + SET(PACK_COMMAND + ${GNUTAR} cfz + ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz + ${CPACK_SOURCE_PACKAGE_FILE_NAME} + ) +ELSE() + SET(PACK_COMMAND ${CMAKE_COMMAND} -E tar cfz + ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz + ${CPACK_SOURCE_PACKAGE_FILE_NAME} +) +ENDIF() + +MESSAGE(STATUS "Creating source package") + +EXECUTE_PROCESS( + COMMAND ${PACK_COMMAND} +) +MESSAGE(STATUS "Source package ${PACKAGE_DIR}.tar.gz created") diff --git a/cmake/merge_archives_unix.cmake.in b/cmake/merge_archives_unix.cmake.in new file mode 100644 index 00000000000..a9a060ab49e --- /dev/null +++ b/cmake/merge_archives_unix.cmake.in @@ -0,0 +1,62 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This script merges many static libraries into +# one big library on Unix. +SET(TARGET_LOCATION "@TARGET_LOCATION@") +SET(TARGET "@TARGET@") +SET(STATIC_LIBS "@STATIC_LIBS@") +SET(CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@") +SET(CMAKE_AR "@CMAKE_AR@") +SET(CMAKE_RANLIB "@CMAKE_RANLIB@") + + +SET(TEMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}) +MAKE_DIRECTORY(${TEMP_DIR}) +# Extract each archive to its own subdirectory(avoid object filename clashes) +FOREACH(LIB ${STATIC_LIBS}) + GET_FILENAME_COMPONENT(NAME_NO_EXT ${LIB} NAME_WE) + SET(TEMP_SUBDIR ${TEMP_DIR}/${NAME_NO_EXT}) + MAKE_DIRECTORY(${TEMP_SUBDIR}) + EXECUTE_PROCESS( + COMMAND ${CMAKE_AR} -x ${LIB} + WORKING_DIRECTORY ${TEMP_SUBDIR} + ) + + FILE(GLOB_RECURSE LIB_OBJECTS "${TEMP_SUBDIR}/*") + SET(OBJECTS ${OBJECTS} ${LIB_OBJECTS}) +ENDFOREACH() + +# Use relative paths, makes command line shorter. +GET_FILENAME_COMPONENT(ABS_TEMP_DIR ${TEMP_DIR} ABSOLUTE) +FOREACH(OBJ ${OBJECTS}) + FILE(RELATIVE_PATH OBJ ${ABS_TEMP_DIR} ${OBJ}) + FILE(TO_NATIVE_PATH ${OBJ} OBJ) + SET(ALL_OBJECTS ${ALL_OBJECTS} ${OBJ}) +ENDFOREACH() + +FILE(TO_NATIVE_PATH ${TARGET_LOCATION} ${TARGET_LOCATION}) +# Now pack the objects into library with ar. +EXECUTE_PROCESS( + COMMAND ${CMAKE_AR} -r ${TARGET_LOCATION} ${ALL_OBJECTS} + WORKING_DIRECTORY ${TEMP_DIR} +) +EXECUTE_PROCESS( + COMMAND ${CMAKE_RANLIB} ${TARGET_LOCATION} + WORKING_DIRECTORY ${TEMP_DIR} +) + +# Cleanup +FILE(REMOVE_RECURSE ${TEMP_DIR}) diff --git a/cmake/misc.cmake b/cmake/misc.cmake new file mode 100644 index 00000000000..c73e66ef3d7 --- /dev/null +++ b/cmake/misc.cmake @@ -0,0 +1,128 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Merge static libraries. +MACRO(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE) + # To produce a library we need at least one source file. + # It is created by ADD_CUSTOM_COMMAND below and will helps + # also help to track dependencies. + SET(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_depends.c) + ADD_LIBRARY(${TARGET} STATIC ${SOURCE_FILE}) + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${OUTPUT_NAME}) + + FOREACH(LIB ${LIBS_TO_MERGE}) + GET_TARGET_PROPERTY(LIB_LOCATION ${LIB} LOCATION) + GET_TARGET_PROPERTY(LIB_TYPE ${LIB} TYPE) + IF(NOT LIB_LOCATION) + # 3rd party library like libz.so. Make sure that everything + # that links to our library links to this one as well. + TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) + ELSE() + # This is a target in current project + # (can be a static or shared lib) + IF(LIB_TYPE STREQUAL "STATIC_LIBRARY") + SET(STATIC_LIBS ${STATIC_LIBS} ${LIB_LOCATION}) + ADD_DEPENDENCIES(${TARGET} ${LIB}) + ELSE() + # This is a shared library our static lib depends on. + TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) + ENDIF() + ENDIF() + ENDFOREACH() + + # Make the generated dummy source file depended on all static input + # libs. If input lib changes,the source file is touched + # which causes the desired effect (relink). + ADD_CUSTOM_COMMAND( + OUTPUT ${SOURCE_FILE} + COMMAND ${CMAKE_COMMAND} -E touch ${SOURCE_FILE} + DEPENDS ${STATIC_LIBS}) + + IF(MSVC) + # To merge libs, just pass them to lib.exe command line. + SET(LINKER_EXTRA_FLAGS "") + FOREACH(LIB ${STATIC_LIBS}) + SET(LINKER_EXTRA_FLAGS "${LINKER_EXTRA_FLAGS} ${LIB}") + ENDFOREACH() + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES STATIC_LIBRARY_FLAGS + "${LINKER_EXTRA_FLAGS}") + ELSE() + GET_TARGET_PROPERTY(TARGET_LOCATION ${TARGET} LOCATION) + IF(APPLE) + # Use OSX's libtool to merge archives (ihandles universal + # binaries properly) + ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD + COMMAND rm ${TARGET_LOCATION} + COMMAND /usr/bin/libtool -static -o ${TARGET_LOCATION} + ${STATIC_LIBS} + ) + ELSE() + # Generic Unix, Cygwin or MinGW. In post-build step, call + # script, that extracts objects from archives with "ar x" + # and repacks them with "ar r" + SET(TARGET ${TARGET}) + CONFIGURE_FILE( + ${CMAKE_SOURCE_DIR}/cmake/merge_archives_unix.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}.cmake + @ONLY + ) + ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD + COMMAND rm ${TARGET_LOCATION} + COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}.cmake + ) + ENDIF() + ENDIF() +ENDMACRO() + +# Convert static library to shared +MACRO(STATIC_TO_SHARED STATIC_LIB SHARED_LIB EXPORTS_FILE) + IF(NOT MSVC) + MESSAGE(FATAL_ERROR + "Cannot convert static ${STATIC_LIB} to shared ${TARGET} library." + ) + ENDIF() + + # Need one source file. + SET(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${SHARED_LIB}_dummy.c) + ADD_CUSTOM_COMMAND( + OUTPUT ${SOURCE_FILE} + COMMAND ${CMAKE_COMMAND} -E touch ${SOURCE_FILE} + ) + + ADD_LIBRARY(${SHARED_LIB} SHARED ${SOURCE_FILE} ${EXPORTS_FILE}) + TARGET_LINK_LIBRARIES(${SHARED_LIB} ${STATIC_LIB}) +ENDMACRO() + +MACRO(SET_TARGET_SOURCEDIR TARGET) + SET(${TARGET}_SOURCEDIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "source directory for a target") +ENDMACRO() + +# Handy macro to use when source projects maybe used somewhere else +# For example, embedded or client library may recompile mysys sources +# In such cases, using absolute names in ADD_LIBRARY has the advantage that +# GET_TARGET_PROPERTY(xxx SOURCES) also returns absolute names, so there is +# no need to know the base directory of a target. +MACRO(USE_ABSOLUTE_FILENAMES FILELIST) + # Use absolute file paths for sources + # It helps when building embedded where we need to + # sources files for the plugin to recompile. + SET(RESOLVED_PATHS) + FOREACH(FILE ${${FILELIST}}) + GET_FILENAME_COMPONENT(ABSOLUTE_PATH ${FILE} ABSOLUTE) + LIST(APPEND RESOLVED_PATHS ${ABSOLUTE_PATH}) + ENDFOREACH() + SET(${FILELIST} ${RESOLVED_PATHS}) +ENDMACRO() diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake new file mode 100644 index 00000000000..8b0e8f4efab --- /dev/null +++ b/cmake/mysql_version.cmake @@ -0,0 +1,142 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Read value for a variable from configure.in + +MACRO(MYSQL_GET_CONFIG_VALUE keyword var) + IF(NOT ${var}) + IF (EXISTS ${CMAKE_SOURCE_DIR}/configure.in) + FILE (STRINGS ${CMAKE_SOURCE_DIR}/configure.in str REGEX "^[ ]*${keyword}=") + IF(str) + STRING(REPLACE "${keyword}=" "" str ${str}) + STRING(REGEX REPLACE "[ ].*" "" str ${str}) + SET(${var} ${str} CACHE INTERNAL "Config variable") + ENDIF() + ENDIF() + ENDIF() +ENDMACRO() + + +# Read mysql version for configure script + +MACRO(GET_MYSQL_VERSION) + + IF(NOT VERSION_STRING) + IF(EXISTS ${CMAKE_SOURCE_DIR}/configure.in) + FILE(STRINGS ${CMAKE_SOURCE_DIR}/configure.in str REGEX "AM_INIT_AUTOMAKE") + STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-][^ \\)]+" VERSION_STRING "${str}") + IF(NOT VERSION_STRING) + FILE(STRINGS configure.in str REGEX "AC_INIT\\(") + STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-][^ \\]]+" VERSION_STRING "${str}") + ENDIF() + ENDIF() + ENDIF() + + IF(NOT VERSION_STRING) + MESSAGE(FATAL_ERROR + "VERSION_STRING cannot be parsed, please specify -DVERSION_STRING=major.minor.patch-extra" + "when calling cmake") + ENDIF() + + SET(VERSION ${VERSION_STRING}) + + STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+[^ ]+" "\\1" MAJOR_VERSION "${VERSION_STRING}") + STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+[^ ]+" "\\1" MINOR_VERSION "${VERSION_STRING}") + STRING(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)[^ ]+" "\\1" PATCH "${VERSION_STRING}") + SET(MYSQL_BASE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}" CACHE INTERNAL "MySQL Base version") + SET(MYSQL_NO_DASH_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH}") + MATH(EXPR MYSQL_VERSION_ID "10000*${MAJOR_VERSION} + 100*${MINOR_VERSION} + ${PATCH}") + MARK_AS_ADVANCED(VERSION MYSQL_VERSION_ID MYSQL_BASE_VERSION) + SET(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION}) + SET(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION}) + SET(CPACK_PACKAGE_VERSION_PATCH ${PATCH}) +ENDMACRO() + +# Get mysql version and other interesting variables +GET_MYSQL_VERSION() + +MYSQL_GET_CONFIG_VALUE("PROTOCOL_VERSION" PROTOCOL_VERSION) +MYSQL_GET_CONFIG_VALUE("DOT_FRM_VERSION" DOT_FRM_VERSION) +MYSQL_GET_CONFIG_VALUE("MYSQL_TCP_PORT_DEFAULT" MYSQL_TCP_PORT_DEFAULT) +MYSQL_GET_CONFIG_VALUE("MYSQL_UNIX_ADDR_DEFAULT" MYSQL_UNIX_ADDR_DEFAULT) +MYSQL_GET_CONFIG_VALUE("SHARED_LIB_MAJOR_VERSION" SHARED_LIB_MAJOR_VERSION) +IF(NOT MYSQL_TCP_PORT_DEFAULT) + SET(MYSQL_TCP_PORT_DEFAULT "3306") +ENDIF() +IF(NOT MYSQL_TCP_PORT) + SET(MYSQL_TCP_PORT ${MYSQL_TCP_PORT_DEFAULT}) + SET(MYSQL_TCP_PORT_DEFAULT "0") +ENDIF() +IF(NOT MYSQL_UNIX_ADDR) + SET(MYSQL_UNIX_ADDR "/tmp/mysql.sock") +ENDIF() +IF(NOT COMPILATION_COMMENT) + SET(COMPILATION_COMMENT "Source distribution") +ENDIF() + + + +# Use meaningful package name for the binary package +IF(NOT CPACK_PACKAGE_FILE_NAME) + IF( NOT SYSTEM_NAME_AND_PROCESSOR) + IF(WIN32) + # CMake does not set CMAKE_SYSTEM_PROCESSOR correctly on Win64 + # (uses x86). Besides, we try to be compatible with existing naming + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(SYSTEM_NAME_AND_PROCESSOR "winx64") + ELSE() + SET(SYSTEM_NAME_AND_PROCESSOR "win32") + ENDIF() + ELSE() + IF(NOT PLATFORM) + SET(PLATFORM ${CMAKE_SYSTEM_NAME}) + ENDIF() + IF(NOT MACHINE) + SET(MACHINE ${CMAKE_SYSTEM_PROCESSOR}) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT ${MACHINE} MATCHES "ia64") + # On almost every 64 bit machine (except IA64) it is possible + # to build 32 bit packages. Add -64bit suffix to differentiate + # between 32 and 64 bit packages. + SET(MACHINE ${MACHINE}-64bit) + ENDIF() + ENDIF() + SET(SYSTEM_NAME_AND_PROCESSOR "${PLATFORM}-${MACHINE}") + ENDIF() + ENDIF() + + SET(package_name "mysql-${VERSION}-${SYSTEM_NAME_AND_PROCESSOR}" ) + # Sometimes package suffix is added (something like icc-glibc23) + IF(PACKAGE_SUFFIX) + SET(package_name "${package_name}-${PACKAGE_SUFFIX}") + ENDIF() + STRING(TOLOWER ${package_name} package_name) + SET(CPACK_PACKAGE_FILE_NAME ${package_name}) +ENDIF() + +IF(NOT CPACK_SOURCE_PACKAGE_FILE_NAME) + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mysql-${VERSION}") +ENDIF() +SET(CPACK_PACKAGE_VENDOR "Sun Microsystems") +SET(CPACK_SOURCE_GENERATOR "TGZ") +SET(CPACK_SOURCE_IGNORE_FILES + \\\\.bzr/ + \\\\.bzr-mysql + .bzrignore + CMakeCache.txt + /CMakeFiles/ + /_CPack_Packages/ + $.gz + $.zip +) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake new file mode 100644 index 00000000000..58a3dafa2f4 --- /dev/null +++ b/cmake/plugin.cmake @@ -0,0 +1,171 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Creates a project to build plugin either as static or shared library +# Parameters: +# plugin - storage engine name. +# variable BUILD_TYPE should be set to "STATIC" or "DYNAMIC" +# Remarks: +# ${PLUGIN}_SOURCES variable containing source files to produce the +# library must set before calling this macro + +MACRO(MYSQL_PLUGIN plugin) + # Add common include directories + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${SSL_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR}) + + STRING(TOUPPER ${plugin} plugin) + STRING(TOLOWER ${plugin} target) + + IF(NOT ${plugin}_PLUGIN_STATIC AND NOT ${plugin}_PLUGIN_DYNAMIC) + MESSAGE(FATAL_ERROR + "Neither ${plugin}_PLUGIN_STATIC nor ${plugin}_PLUGIN_DYNAMIC is defined. + Please set at least one of these variables to the name of the output + library in CMakeLists.txt prior to calling MYSQL_PLUGIN" + ) + ENDIF() + + IF(WITH_PLUGIN_${plugin}) + SET(WITH_${plugin} 1) + ENDIF() + + IF(WITH_${plugin}_STORAGE_ENGINE OR WITH_{$plugin} OR WITH_ALL + OR WITH_MAX AND NOT WITHOUT_${plugin}_STORAGE_ENGINE AND NOT + WITHOUT_${plugin}) + SET(WITH_${plugin} 1) + ELSEIF(WITHOUT_${plugin}_STORAGE_ENGINE OR WITH_NONE OR ${plugin}_DISABLED) + SET(WITHOUT_${plugin} 1) + SET(WITH_${plugin}_STORAGE_ENGINE 0) + SET(WITH_${plugin} 0) + ENDIF() + + IF(${plugin}_PLUGIN_MANDATORY) + SET(WITH_${plugin} 1) + ENDIF() + + IF(${plugin} MATCHES NDBCLUSTER AND WITH_MAX_NO_NDB) + SET(WITH_${plugin} 0) + SET(WITH_${plugin}_STORAGE_ENGINE 0) + SET(WITHOUT_${plugin} 1) + SET(WITHOUT_${plugin}_STORAGE_ENGINE 0) + ENDIF() + + IF(STORAGE_ENGINE) + SET(with_var "WITH_${plugin}_STORAGE_ENGINE" ) + ELSE() + SET(with_var "WITH_${plugin}") + ENDIF() + + + USE_ABSOLUTE_FILENAMES(${plugin}_SOURCES) + + IF (WITH_${plugin} AND ${plugin}_PLUGIN_STATIC) + ADD_DEFINITIONS(-DMYSQL_SERVER) + #Create static library. + ADD_LIBRARY(${target} ${${plugin}_SOURCES}) + DTRACE_INSTRUMENT(${target}) + ADD_DEPENDENCIES(${target} GenError) + IF(${plugin}_LIBS) + TARGET_LINK_LIBRARIES(${target} ${${plugin}_LIBS}) + ENDIF() + SET_TARGET_PROPERTIES(${target} PROPERTIES + OUTPUT_NAME "${${plugin}_PLUGIN_STATIC}") + # Update mysqld dependencies + SET (MYSQLD_STATIC_PLUGIN_LIBS ${MYSQLD_STATIC_PLUGIN_LIBS} + ${target} PARENT_SCOPE) + SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_${target}_plugin" + PARENT_SCOPE) + SET(${with_var} ON CACHE BOOL "Link ${plugin} statically to the server" + FORCE) + ELSEIF(NOT WITHOUT_${plugin} AND ${plugin}_PLUGIN_DYNAMIC + AND NOT WITHOUT_DYNAMIC_PLUGINS) + + # Create a shared module. + ADD_DEFINITIONS(-DMYSQL_DYNAMIC_PLUGIN) + ADD_LIBRARY(${target} MODULE ${${plugin}_SOURCES}) + IF(${plugin}_LIBS) + TARGET_LINK_LIBRARIES(${target} ${${plugin}_LIBS}) + ENDIF() + DTRACE_INSTRUMENT(${target}) + SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "") + TARGET_LINK_LIBRARIES (${target} mysqlservices) + + # Plugin uses symbols defined in mysqld executable. + # Some operating systems like Windows and OSX and are pretty strict about + # unresolved symbols. Others are less strict and allow unresolved symbols + # in shared libraries. On Linux for example, CMake does not even add + # executable to the linker command line (it would result into link error). + # Thus we skip TARGET_LINK_LIBRARIES on Linux, as it would only generate + # an additional dependency. + IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + TARGET_LINK_LIBRARIES (${target} mysqld) + ENDIF() + + ADD_DEPENDENCIES(${target} GenError) + + IF(${plugin}_PLUGIN_DYNAMIC) + SET_TARGET_PROPERTIES(${target} PROPERTIES + OUTPUT_NAME "${${plugin}_PLUGIN_DYNAMIC}") + ENDIF() + + # Update cache "WITH" variable for plugins that support static linking + IF(${plugin}_PLUGIN_STATIC) + SET(${with_var} OFF CACHE BOOL "Link ${plugin} statically to the server" + FORCE) + ENDIF() + + # Install dynamic library + SET(INSTALL_LOCATION lib/plugin) + INSTALL(TARGETS ${target} DESTINATION ${INSTALL_LOCATION}) + INSTALL_DEBUG_SYMBOLS(${target}) + ELSE() + IF(STORAGE_ENGINE) + SET(without_var "WITHOUT_${plugin}_STORAGE_ENGINE") + ELSE() + SET(without_var "WITHOUT_${plugin}") + ENDIF() + SET(${without_var} ON CACHE BOOL "Link ${plugin} statically to the server" + FORCE) + MARK_AS_ADVANCED(${without_var}) + ENDIF() +ENDMACRO() + +MACRO (MYSQL_STORAGE_ENGINE engine) + SET(STORAGE_ENGINE 1) + MYSQL_PLUGIN(${engine}) +ENDMACRO() + +# Add all CMake projects under storage and plugin +# subdirectories, configure sql_builtins.cc +MACRO(CONFIGURE_PLUGINS) + FILE(GLOB dirs_storage ${CMAKE_SOURCE_DIR}/storage/*) + FILE(GLOB dirs_plugin ${CMAKE_SOURCE_DIR}/plugin/*) + FOREACH(dir ${dirs_storage} ${dirs_plugin}) + IF (EXISTS ${dir}/CMakeLists.txt) + ADD_SUBDIRECTORY(${dir}) + ENDIF() + ENDFOREACH() + # Special handling for partition(not really pluggable) + IF(NOT WITHOUT_PARTITION_STORAGE_ENGINE) + SET (WITH_PARTITION_STORAGE_ENGINE 1) + SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_partition_plugin") + ENDIF(NOT WITHOUT_PARTITION_STORAGE_ENGINE) + ADD_DEFINITIONS(${STORAGE_ENGINE_DEFS}) + CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc.in + ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc) +ENDMACRO() diff --git a/cmake/readline.cmake b/cmake/readline.cmake new file mode 100644 index 00000000000..7661f12cce2 --- /dev/null +++ b/cmake/readline.cmake @@ -0,0 +1,197 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +MACRO(SET_WITH_BUNDLED_READLINE option) + IF(option) + SET(not_option OFF) + ELSE() + SET(not_option ON) + ENDIF() + SET(WITH_READLINE ${option} CACHE BOOL "Use bundled readline") + SET(WITH_LIBEDIT ${not_option} CACHE BOOL "Use bundled libedit") +ENDMACRO() + +MACRO (MYSQL_CHECK_MULTIBYTE) + CHECK_INCLUDE_FILE(wctype.h HAVE_WCTYPE_H) + CHECK_INCLUDE_FILE(wchar.h HAVE_WCHAR_H) + IF(HAVE_WCHAR_H) + SET(CMAKE_EXTRA_INCLUDE_FILES wchar.h) + CHECK_TYPE_SIZE(mbstate_t SIZEOF_MBSTATE_T) + SET(CMAKE_EXTRA_INCLUDE_FILES) + IF(SIZEOF_MBSTATE_T) + SET(HAVE_MBSTATE_T 1) + ENDIF() + ENDIF() + + CHECK_C_SOURCE_COMPILES(" + #include + int main(int ac, char **av) + { + char *cs = nl_langinfo(CODESET); + return 0; + }" + HAVE_LANGINFO_CODESET) + + CHECK_FUNCTION_EXISTS(mbrlen HAVE_MBRLEN) + CHECK_FUNCTION_EXISTS(mbscmp HAVE_MBSCMP) + CHECK_FUNCTION_EXISTS(mbsrtowcs HAVE_MBSRTOWCS) + CHECK_FUNCTION_EXISTS(wcrtomb HAVE_WCRTOMB) + CHECK_FUNCTION_EXISTS(mbrtowc HAVE_MBRTOWC) + CHECK_FUNCTION_EXISTS(wcscoll HAVE_WCSCOLL) + CHECK_FUNCTION_EXISTS(wcsdup HAVE_WCSDUP) + CHECK_FUNCTION_EXISTS(wcwidth HAVE_WCWIDTH) + CHECK_FUNCTION_EXISTS(wctype HAVE_WCTYPE) + CHECK_FUNCTION_EXISTS(iswlower HAVE_ISWLOWER) + CHECK_FUNCTION_EXISTS(iswupper HAVE_ISWUPPER) + CHECK_FUNCTION_EXISTS(towlower HAVE_TOWLOWER) + CHECK_FUNCTION_EXISTS(towupper HAVE_TOWUPPER) + CHECK_FUNCTION_EXISTS(iswctype HAVE_ISWCTYPE) + + SET(CMAKE_EXTRA_INCLUDE_FILES wchar.h) + CHECK_TYPE_SIZE(wchar_t SIZEOF_WCHAR_T) + IF(SIZEOF_WCHAR_T) + SET(HAVE_WCHAR_T 1) + ENDIF() + + SET(CMAKE_EXTRA_INCLUDE_FILES wctype.h) + CHECK_TYPE_SIZE(wctype_t SIZEOF_WCTYPE_T) + IF(SIZEOF_WCTYPE_T) + SET(HAVE_WCTYPE_T 1) + ENDIF() + CHECK_TYPE_SIZE(wint_t SIZEOF_WINT_T) + IF(SIZEOF_WINT_T) + SET(HAVE_WINT_T 1) + ENDIF() + SET(CMAKE_EXTRA_INCLUDE_FILES) + +ENDMACRO() + +MACRO (FIND_CURSES) + INCLUDE (FindCurses) + MARK_AS_ADVANCED(CURSES_CURSES_H_PATH CURSES_FORM_LIBRARY CURSES_HAVE_CURSES_H) + IF(NOT CURSES_FOUND) + MESSAGE(FATAL_ERROR "curses library not found") + ENDIF() + + IF(CURSES_HAVE_CURSES_H) + SET(HAVE_CURSES_H 1 CACHE INTERNAL "") + ELSEIF(CURSES_HAVE_NCURSES_H) + SET(HAVE_NCURSES_H 1 CACHE INTERNAL "") + ENDIF() + IF(CMAKE_SYSTEM_NAME MATCHES "HP") + # CMake uses full path to library /lib/libcurses.sl + # On Itanium, it results into architecture mismatch+ + # the library is for PA-RISC + SET(CURSES_LIBRARY "curses" CACHE INTERNAL "" FORCE) + SET(CURSES_CURSES_LIBRARY "curses" CACHE INTERNAL "" FORCE) + ENDIF() +ENDMACRO() + +MACRO (MYSQL_USE_BUNDLED_READLINE) + SET_WITH_BUNDLED_READLINE(ON) + SET(USE_NEW_READLINE_INTERFACE 1) + SET(READLINE_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/cmd-line-utils) + SET(READLINE_LIBRARY readline) + FIND_CURSES() + ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/cmd-line-utils/readline) +ENDMACRO() + +MACRO (MYSQL_USE_BUNDLED_LIBEDIT) + SET_WITH_BUNDLED_READLINE(OFF) + SET(USE_LIBEDIT_INTERFACE 1 CACHE INTERNAL "") + SET(HAVE_HIST_ENTRY 1 CACHE INTERNAL "") + SET(READLINE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/cmd-line-utils/libedit") + SET(READLINE_LIBRARY edit) + FIND_CURSES() + ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/cmd-line-utils/libedit) +ENDMACRO() + +MACRO (MYSQL_FIND_SYSTEM_READLINE name) + FIND_PATH(SYSTEM_READLINE_INCLUDE_DIR readline/readline.h ) + FIND_LIBRARY(SYSTEM_READLINE_LIBRARY NAMES ${name}) + MARK_AS_ADVANCED(SYSTEM_READLINE_INCLUDE_DIR SYSTEM_READLINE_LIBRARY) + + INCLUDE(CheckCXXSourceCompiles) + SET(CMAKE_REQUIRES_LIBRARIES ${SYSTEM_READLINE_LIBRARY}) + + IF(SYSTEM_READLINE_LIBRARY AND SYSTEM_READLINE_INCLUDE_DIR) + SET(SYSTEM_READLINE_FOUND 1) + SET(CMAKE_REQUIRED_LIBRARIES ${SYSTEM_READLINE_LIBRARY}) + CHECK_CXX_SOURCE_COMPILES(" + #include + #include + int main(int argc, char **argv) + { + HIST_ENTRY entry; + return 0; + }" + HAVE_HIST_ENTRY) + + CHECK_CXX_SOURCE_COMPILES(" + #include + #include + int main(int argc, char **argv) + { + char res= *(*rl_completion_entry_function)(0,0); + completion_matches(0,0); + }" + USE_LIBEDIT_INTERFACE) + + + CHECK_CXX_SOURCE_COMPILES(" + #include + #include + int main(int argc, char **argv) + { + rl_completion_func_t *func1= (rl_completion_func_t*)0; + rl_compentry_func_t *func2= (rl_compentry_func_t*)0; + }" + USE_NEW_READLINE_INTERFACE) + + IF(USE_LIBEDIT_INTERFACE OR USE_NEW_READLINE_INTERFACE) + SET(READLINE_LIBRARY ${SYSTEM_READLINE_LIBRARY}) + SET(READLINE_INCLUDE_DIR ${SYSTEM_READLINE_INCLUDE_DIR}) + SET(READLINE_FOUND 1) + ENDIF() + ENDIF() +ENDMACRO() + +MACRO (MYSQL_CHECK_READLINE) + IF (NOT WIN32) + MYSQL_CHECK_MULTIBYTE() + OPTION(WITH_READLINE "Use bundled readline" OFF) + IF(NOT CYGWIN) + # Bundled libedit does not compile on cygwin + OPTION(WITH_LIBEDIT "Use bundled libedit" ON) + ELSE() + OPTION(WITH_LIBEDIT "Use bundled libedit" OFF) + ENDIF() + + IF(WITH_READLINE) + MYSQL_USE_BUNDLED_READLINE() + ELSEIF(WITH_LIBEDIT) + MYSQL_USE_BUNDLED_LIBEDIT() + ELSE() + MYSQL_FIND_SYSTEM_READLINE(readline) + IF(NOT READLINE_FOUND) + MYSQL_FIND_SYSTEM_READLINE(edit) + IF(NOT READLINE_FOUND) + MESSAGE(FATAL_ERROR "Cannot find system readline or libedit libraries.Use WITH_READLINE or WITH_LIBEDIT") + ENDIF() + ENDIF() + ENDIF() + ENDIF(NOT WIN32) +ENDMACRO() + diff --git a/cmake/ssl.cmake b/cmake/ssl.cmake new file mode 100644 index 00000000000..44f57899784 --- /dev/null +++ b/cmake/ssl.cmake @@ -0,0 +1,88 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +MACRO (CHANGE_SSL_SETTINGS string) + SET(WITH_SSL ${string} CACHE STRING "Options are : no, bundled, yes (prefer os library if present otherwise use bundled), system (use os library)" FORCE) +ENDMACRO() + +MACRO (MYSQL_USE_BUNDLED_SSL) + SET(INC_DIRS + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/include + ) + SET(SSL_LIBRARIES yassl taocrypt) + SET(SSL_INCLUDE_DIRS ${INC_DIRS}) + SET(SSL_INTERNAL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/mySTL) + SET(SSL_DEFINES "-DHAVE_YASSL -DYASSL_PURE_C -DYASSL_PREFIX -DHAVE_OPENSSL") + CHANGE_SSL_SETTINGS("bundled") + #Remove -fno-implicit-templates + #(yassl sources cannot be compiled with it) + SET(SAVE_CXX_FLAGS ${CXX_FLAGS}) + STRING(REPLACE "-fno-implicit-templates" "" CMAKE_CXX_FLAGS + ${CMAKE_CXX_FLAGS}) + ADD_SUBDIRECTORY(extra/yassl) + ADD_SUBDIRECTORY(extra/yassl/taocrypt) + SET(CXX_FLAGS ${SAVE_CXX_FLAGS}) + GET_TARGET_PROPERTY(src yassl SOURCES) + FOREACH(file ${src}) + SET(SSL_SOURCES ${SSL_SOURCES} ${CMAKE_SOURCE_DIR}/extra/yassl/${file}) + ENDFOREACH() + GET_TARGET_PROPERTY(src taocrypt SOURCES) + FOREACH(file ${src}) + SET(SSL_SOURCES ${SSL_SOURCES} ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/${file}) + ENDFOREACH() +ENDMACRO() + +# MYSQL_CHECK_SSL +# +# Provides the following configure options: +# WITH_SSL=[yes|no|bundled] +MACRO (MYSQL_CHECK_SSL) + IF(NOT WITH_SSL) + IF(WIN32) + CHANGE_SSL_SETTINGS("bundled") + ELSE() + CHANGE_SSL_SETTINGS("no") + ENDIF() + ENDIF() + + IF(WITH_SSL STREQUAL "bundled") + MYSQL_USE_BUNDLED_SSL() + ELSEIF(WITH_SSL STREQUAL "system" OR WITH_SSL STREQUAL "yes") + # Check for system library + SET(OPENSSL_FIND_QUIETLY TRUE) + INCLUDE(FindOpenSSL) + FIND_LIBRARY(CRYPTO_LIBRARY crypto) + MARK_AS_ADVANCED(CRYPTO_LIBRARY) + INCLUDE(CheckSymbolExists) + CHECK_SYMBOL_EXISTS(SHA512_DIGEST_LENGTH "openssl/sha.h" + HAVE_SHA512_DIGEST_LENGTH) + IF(OPENSSL_FOUND AND CRYPTO_LIBRARY AND HAVE_SHA512_DIGEST_LENGTH) + SET(SSL_SOURCES "") + SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARY}) + SET(SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) + SET(SSL_INTERNAL_INCLUDE_DIRS "") + SET(SSL_DEFINES "-DHAVE_OPENSSL") + CHANGE_SSL_SETTINGS("system") + ELSE() + IF(WITH_SSL STREQUAL "system") + MESSAGE(SEND_ERROR "Cannot find appropriate system libraries for SSL. Use WITH_SSL=bundled to enable SSL support") + ENDIF() + MYSQL_USE_BUNDLED_SSL() + ENDIF() + ELSEIF(NOT WITH_SSL STREQUAL "no") + MESSAGE(SEND_ERROR "Wrong option for WITH_SSL. Valid values are : yes, no, bundled") + ENDIF() +ENDMACRO() diff --git a/cmake/stack_direction.c b/cmake/stack_direction.c new file mode 100644 index 00000000000..11bcf803bfa --- /dev/null +++ b/cmake/stack_direction.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* Check stack direction (0-down, 1-up) */ +int f(int *a) +{ + int b; + return(&b > a)?1:0; +} +/* + Prevent compiler optimizations by calling function + through pointer. +*/ +volatile int (*ptr_f)(int *) = f; +int main() +{ + int a; + return ptr_f(&a); +} \ No newline at end of file diff --git a/cmake/zlib.cmake b/cmake/zlib.cmake new file mode 100644 index 00000000000..59f1f7ba68e --- /dev/null +++ b/cmake/zlib.cmake @@ -0,0 +1,73 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +MACRO (MYSQL_USE_BUNDLED_ZLIB) + SET(ZLIB_LIBRARY zlib) + SET(ZLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/zlib) + SET(ZLIB_FOUND TRUE) + SET(WITH_ZLIB "bundled" CACHE STRING "Use bundled zlib") + ADD_SUBDIRECTORY(zlib) + GET_TARGET_PROPERTY(src zlib SOURCES) + FOREACH(file ${src}) + SET(ZLIB_SOURCES ${ZLIB_SOURCES} ${CMAKE_SOURCE_DIR}/zlib/${file}) + ENDFOREACH() +ENDMACRO() + +# MYSQL_CHECK_ZLIB_WITH_COMPRESS +# +# Provides the following configure options: +# WITH_ZLIB_BUNDLED +# If this is set,we use bindled zlib +# If this is not set,search for system zlib. +# if system zlib is not found, use bundled copy +# ZLIB_LIBRARIES, ZLIB_INCLUDE_DIR and ZLIB_SOURCES +# are set after this macro has run + +MACRO (MYSQL_CHECK_ZLIB_WITH_COMPRESS) + + IF(CMAKE_SYSTEM_NAME STREQUAL "OS400" OR + CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + CMAKE_SYSTEM_NAME STREQUAL "Windows") + # Use bundled zlib on some platforms by default (system one is too + # old or not existent) + IF (NOT WITH_ZLIB) + SET(WITH_ZLIB "bundled" CACHE STRING "By default use bundled zlib on this platform") + ENDIF() + ENDIF() + + IF(WITH_ZLIB STREQUAL "bundled") + MYSQL_USE_BUNDLED_ZLIB() + ELSE() + SET(ZLIB_FIND_QUIETLY TRUE) + INCLUDE(FindZLIB) + IF(ZLIB_FOUND) + INCLUDE(CheckFunctionExists) + SET(CMAKE_REQUIRED_LIBRARIES z) + CHECK_FUNCTION_EXISTS(crc32 HAVE_CRC32) + SET(CMAKE_REQUIRED_LIBRARIES) + IF(HAVE_CRC32) + SET(ZLIB_LIBRARY z CACHE INTERNAL "System zlib library") + SET(WITH_ZLIB "system" CACHE STRING "Which zlib to use (possible values are 'bundled' or 'system')") + SET(ZLIB_SOURCES "") + ELSE() + SET(ZLIB_FOUND FALSE CACHE INTERNAL "Zlib found but not usable") + ENDIF() + ENDIF() + IF(NOT ZLIB_FOUND) + MYSQL_USE_BUNDLED_ZLIB() + ENDIF() + ENDIF() + SET(HAVE_COMPRESS 1) +ENDMACRO() diff --git a/cmd-line-utils/libedit/CMakeLists.txt b/cmd-line-utils/libedit/CMakeLists.txt new file mode 100644 index 00000000000..0c58fe9546f --- /dev/null +++ b/cmd-line-utils/libedit/CMakeLists.txt @@ -0,0 +1,167 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ) +INCLUDE(CheckIncludeFile) +CHECK_INCLUDE_FILES(term.h HAVE_TERM_H) + +SET(CMAKE_REQUIRED_LIBRARIES ${CURSES_CURSES_LIBRARY}) +CHECK_CXX_SOURCE_COMPILES(" +#include +int main() +{ + tgoto(0,0,0); + return 0; +}" HAVE_DECL_TGOTO) +SET(CMAKE_REQUIRED_LIBRARIES) + + + +IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + #On Solaris, default awk is next to unusable while the xpg4 one is ok. + IF(EXISTS /usr/xpg4/bin/awk) + SET(AWK_EXECUTABLE /usr/xpg4/bin/awk) + ENDIF() +ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "OS400") + #Workaround for cases, where /usr/bin/gawk is not executable + IF(EXISTS /QOpenSys/usr/bin/awk) + SET(AWK_EXECUTABLE /QOpenSys/usr/bin/awk) + ENDIF() +ENDIF() + +IF(NOT AWK_EXECUTABLE) + FIND_PROGRAM(AWK_EXECUTABLE NAMES gawk awk DOC "path to the awk executable") +ENDIF() + +MARK_AS_ADVANCED(AWK_EXECUTABLE) +SET(AWK ${AWK_EXECUTABLE}) +CONFIGURE_FILE(makelist.sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist @ONLY) + + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CURSES_INCLUDE_PATH} + } +) + +SET(ASRC vi.c emacs.c common.c) +SET(AHDR vi.h emacs.h common.h) + + +SET(LIBEDIT_SOURCES + chared.c el.c history.c map.c prompt.c readline.c + search.c tokenizer.c vi.c common.c emacs.c + hist.c key.c parse.c read.c refresh.c sig.c term.c + tty.c help.c fcns.c filecomplete.c + ${AHDR} + ) + +include(CheckFunctionExists) +include(CheckIncludeFile) +CHECK_INCLUDE_FILE(vis.h HAVE_VIS_H) + +IF(HAVE_VIS_H) + CHECK_FUNCTION_EXISTS(strvis HAVE_STRVIS) + IF(NOT HAVE_STRVIS) + SET(HAVE_VIS_H FALSE CACHE INTERNAL "" FORCE) + ENDIF() +ENDIF() + +CHECK_FUNCTION_EXISTS(strvis HAVE_STRVIS) +IF(NOT HAVE_STRVIS) + SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/vis.c) +ENDIF() + + +CHECK_FUNCTION_EXISTS(strunvis HAVE_STRUNVIS) +IF(NOT HAVE_STRUNVIS) + SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/unvis.c) +ENDIF() + +CHECK_FUNCTION_EXISTS(strlcpy HAVE_STRLCPY) +IF(NOT HAVE_STRLCPY) + SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/strlcpy.c) +ENDIF() + + +CHECK_FUNCTION_EXISTS(strlcat HAVE_STRLCAT) +IF(NOT HAVE_STRLCAT) + SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/strlcat.c) +ENDIF() + + +CHECK_FUNCTION_EXISTS(fgetln HAVE_FGETLN) +IF(NOT HAVE_FGETLN) + SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/fgetln.c) +ENDIF() + + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/vi.h + COMMAND sh ./makelist -h vi.c > vi.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS vi.c) + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/emacs.h + COMMAND sh ./makelist -h emacs.c > emacs.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS emacs.c) + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/common.h + COMMAND sh ./makelist -h common.c > common.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS common.c) + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/help.c + COMMAND sh ./makelist -bc ${ASRC} > help.c + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${ASRC} + ) + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/help.h + COMMAND sh ./makelist -bh ${ASRC} > help.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${ASRC} + ) + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/fcns.h + COMMAND sh ./makelist -fh ${AHDR} > fcns.h + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${AHDR} + ) + +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/fcns.c + COMMAND sh ./makelist -fc ${AHDR} > fcns.c + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${AHDR} + ) + + +ADD_CUSTOM_TARGET( + GenLibeditSource + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/vi.h + ${CMAKE_CURRENT_SOURCE_DIR}/emacs.h + ${CMAKE_CURRENT_SOURCE_DIR}/common.h + ${CMAKE_CURRENT_SOURCE_DIR}/help.c + ${CMAKE_CURRENT_SOURCE_DIR}/help.h + ${CMAKE_CURRENT_SOURCE_DIR}/fcns.c + ${CMAKE_CURRENT_SOURCE_DIR}/fcns.h +) + +ADD_LIBRARY(edit ${LIBEDIT_SOURCES}) +ADD_DEPENDENCIES(edit GenLibeditSource) +TARGET_LINK_LIBRARIES(edit ${CURSES_LIBRARY}) + diff --git a/cmd-line-utils/libedit/Makefile.am b/cmd-line-utils/libedit/Makefile.am index ddafa4aab44..88ea97afffd 100644 --- a/cmd-line-utils/libedit/Makefile.am +++ b/cmd-line-utils/libedit/Makefile.am @@ -23,7 +23,7 @@ noinst_HEADERS = chared.h el.h el_term.h histedit.h key.h parse.h refresh.h sig. sys.h config.h hist.h map.h prompt.h read.h \ search.h tty.h filecomplete.h np/vis.h -EXTRA_DIST = makelist.sh +EXTRA_DIST = makelist.sh CMakeLists.txt CLEANFILES = makelist common.h emacs.h vi.h fcns.h help.h fcns.c help.c diff --git a/cmd-line-utils/readline/CMakeLists.txt b/cmd-line-utils/readline/CMakeLists.txt new file mode 100644 index 00000000000..506dc1a0ef6 --- /dev/null +++ b/cmd-line-utils/readline/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (C) 2007 MySQL AB +# +# 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(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/cmd-line-utils) + +ADD_DEFINITIONS(-DHAVE_CONFIG_H -DNO_KILL_INTR -DMYSQL_CLIENT_NO_THREADS) + +INCLUDE_DIRECTORIES(${CURSES_INCLUDE_PATH}) + +ADD_LIBRARY(readline + readline.c + funmap.c + keymaps.c + vi_mode.c + parens.c + rltty.c + complete.c + bind.c + isearch.c + display.c + signals.c + util.c + kill.c + undo.c + macro.c + input.c + callback.c + terminal.c + xmalloc.c + history.c + histsearch.c + histexpand.c + histfile.c + nls.c + search.c + shell.c + tilde.c + misc.c + text.c + mbutil.c + compat.c + savestring.c +) + +# Declare dependency +# so every executable that links with readline links with curses as well +TARGET_LINK_LIBRARIES(readline ${CURSES_LIBRARY}) diff --git a/cmd-line-utils/readline/Makefile.am b/cmd-line-utils/readline/Makefile.am index e5f5717858d..d58ce0f1de1 100644 --- a/cmd-line-utils/readline/Makefile.am +++ b/cmd-line-utils/readline/Makefile.am @@ -29,7 +29,7 @@ noinst_HEADERS = readline.h chardefs.h keymaps.h \ tilde.h rlconf.h rltty.h ansi_stdlib.h \ tcap.h rlstdc.h -EXTRA_DIST= emacs_keymap.c vi_keymap.c +EXTRA_DIST= emacs_keymap.c vi_keymap.c CMakeLists.txt DEFS = -DMYSQL_CLIENT_NO_THREADS -DHAVE_CONFIG_H -DNO_KILL_INTR diff --git a/config.h.cmake b/config.h.cmake new file mode 100644 index 00000000000..054777e9f43 --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,618 @@ +/* Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef MY_CONFIG_H +#define MY_CONFIG_H +#cmakedefine DOT_FRM_VERSION @DOT_FRM_VERSION@ +/* Headers we may want to use. */ +#cmakedefine STDC_HEADERS 1 +#cmakedefine HAVE_ALLOCA_H 1 +#cmakedefine HAVE_AIO_H 1 +#cmakedefine HAVE_ARPA_INET_H 1 +#cmakedefine HAVE_BSEARCH 1 +#cmakedefine HAVE_CRYPT_H 1 +#cmakedefine HAVE_CURSES_H 1 +#cmakedefine HAVE_CXXABI_H 1 +#cmakedefine HAVE_NCURSES_H 1 +#cmakedefine HAVE_DIRENT_H 1 +#cmakedefine HAVE_DLFCN_H 1 +#cmakedefine HAVE_EXECINFO_H 1 +#cmakedefine HAVE_FCNTL_H 1 +#cmakedefine HAVE_FENV_H 1 +#cmakedefine HAVE_FLOAT_H 1 +#cmakedefine HAVE_FLOATINGPOINT_H 1 +#cmakedefine HAVE_FNMATCH_H 1 +#cmakedefine HAVE_FPU_CONTROL_H 1 +#cmakedefine HAVE_GRP_H 1 +#cmakedefine HAVE_EXPLICIT_TEMPLATE_INSTANTIATION 1 +#cmakedefine HAVE_IEEEFP_H 1 +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_LIMITS_H 1 +#cmakedefine HAVE_LOCALE_H 1 +#cmakedefine HAVE_MALLOC_H 1 +#cmakedefine HAVE_MEMORY_H 1 +#cmakedefine HAVE_NETINET_IN_H 1 +#cmakedefine HAVE_PATHS_H 1 +#cmakedefine HAVE_PORT_H 1 +#cmakedefine HAVE_PWD_H 1 +#cmakedefine HAVE_SCHED_H 1 +#cmakedefine HAVE_SELECT_H 1 +#cmakedefine HAVE_SOLARIS_LARGE_PAGES 1 +#cmakedefine HAVE_STDDEF_H 1 +#cmakedefine HAVE_STDLIB_H 1 +#cmakedefine HAVE_STDARG_H 1 +#cmakedefine HAVE_STRINGS_H 1 +#cmakedefine HAVE_STRING_H 1 +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_SEMAPHORE_H 1 +#cmakedefine HAVE_SYNCH_H 1 +#cmakedefine HAVE_SYSENT_H 1 +#cmakedefine HAVE_SYS_DIR_H 1 +#cmakedefine HAVE_SYS_CDEFS_H 1 +#cmakedefine HAVE_SYS_FILE_H 1 +#cmakedefine HAVE_SYS_FPU_H 1 +#cmakedefine HAVE_SYS_IOCTL_H 1 +#cmakedefine HAVE_SYS_IPC_H 1 +#cmakedefine HAVE_SYS_MALLOC_H 1 +#cmakedefine HAVE_SYS_MMAN_H 1 +#cmakedefine HAVE_SYS_PTE_H 1 +#cmakedefine HAVE_SYS_PTEM_H 1 +#cmakedefine HAVE_SYS_PRCTL_H 1 +#cmakedefine HAVE_SYS_RESOURCE_H 1 +#cmakedefine HAVE_SYS_SELECT_H 1 +#cmakedefine HAVE_SYS_SHM_H 1 +#cmakedefine HAVE_SYS_SOCKET_H 1 +#cmakedefine HAVE_SYS_STAT_H 1 +#cmakedefine HAVE_SYS_STREAM_H 1 +#cmakedefine HAVE_SYS_TERMCAP_H 1 +#cmakedefine HAVE_SYS_TIMEB_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 +#cmakedefine HAVE_SYS_UN_H 1 +#cmakedefine HAVE_SYS_VADVISE_H 1 +#cmakedefine HAVE_TERM_H 1 +#cmakedefine HAVE_TERMIOS_H 1 +#cmakedefine HAVE_TERMIO_H 1 +#cmakedefine HAVE_TERMCAP_H 1 +#cmakedefine HAVE_UNISTD_H 1 +#cmakedefine HAVE_UTIME_H 1 +#cmakedefine HAVE_VARARGS_H 1 +#cmakedefine HAVE_VIS_H 1 +#cmakedefine HAVE_SYS_UTIME_H 1 +#cmakedefine HAVE_SYS_WAIT_H 1 +#cmakedefine HAVE_SYS_PARAM_H 1 + +/* Libraries */ +#cmakedefine HAVE_LIBPTHREAD 1 +#cmakedefine HAVE_LIBM 1 +#cmakedefine HAVE_LIBDL 1 +#cmakedefine HAVE_LIBRT 1 +#cmakedefine HAVE_LIBSOCKET 1 +#cmakedefine HAVE_LIBNSL 1 +#cmakedefine HAVE_LIBCRYPT 1 +#cmakedefine HAVE_LIBMTMALLOC 1 +#cmakedefine HAVE_LIBWRAP 1 +/* Does "struct timespec" have a "sec" and "nsec" field? */ +#cmakedefine HAVE_TIMESPEC_TS_SEC 1 + +/* Readline */ +#cmakedefine HAVE_HIST_ENTRY 1 +#cmakedefine USE_LIBEDIT_INTERFACE 1 +#cmakedefine USE_NEW_READLINE_INTERFACE 1 + +#cmakedefine FIONREAD_IN_SYS_IOCTL 1 +#cmakedefine GWINSZ_IN_SYS_IOCTL 1 +#cmakedefine TIOCSTAT_IN_SYS_IOCTL 1 + +/* Functions we may want to use. */ +#cmakedefine HAVE_AIOWAIT 1 +#cmakedefine HAVE_ALARM 1 +#cmakedefine HAVE_ALLOCA 1 +#cmakedefine HAVE_BCMP 1 +#cmakedefine HAVE_BFILL 1 +#cmakedefine HAVE_BMOVE 1 +#cmakedefine HAVE_BZERO 1 +#cmakedefine HAVE_INDEX 1 +#cmakedefine HAVE_CLOCK_GETTIME 1 +#cmakedefine HAVE_CRYPT 1 +#cmakedefine HAVE_CUSERID 1 +#cmakedefine HAVE_DIRECTIO 1 +#cmakedefine HAVE_DLERROR 1 +#cmakedefine HAVE_DLOPEN 1 +#cmakedefine HAVE_DOPRNT 1 +#cmakedefine HAVE_FCHMOD 1 +#cmakedefine HAVE_FCNTL 1 +#cmakedefine HAVE_FCONVERT 1 +#cmakedefine HAVE_FDATASYNC 1 +#cmakedefine HAVE_FESETROUND 1 +#cmakedefine HAVE_FINITE 1 +#cmakedefine HAVE_FP_EXCEPT 1 +#cmakedefine HAVE_FPSETMASK 1 +#cmakedefine HAVE_FSEEKO 1 +#cmakedefine HAVE_FSYNC 1 +#cmakedefine HAVE_GETADDRINFO 1 +#cmakedefine HAVE_GETCWD 1 +#cmakedefine HAVE_GETHOSTBYADDR_R 1 +#cmakedefine HAVE_GETHOSTBYNAME_R 1 +#cmakedefine HAVE_GETHRTIME 1 +#cmakedefine HAVE_GETLINE 1 +#cmakedefine HAVE_GETNAMEINFO 1 +#cmakedefine HAVE_GETPAGESIZE 1 +#cmakedefine HAVE_GETPASS 1 +#cmakedefine HAVE_GETPASSPHRASE 1 +#cmakedefine HAVE_GETPWNAM 1 +#cmakedefine HAVE_GETPWUID 1 +#cmakedefine HAVE_GETRLIMIT 1 +#cmakedefine HAVE_GETRUSAGE 1 +#cmakedefine HAVE_GETTIMEOFDAY 1 +#cmakedefine HAVE_GETWD 1 +#cmakedefine HAVE_GMTIME_R 1 +#cmakedefine gmtime_r @gmtime_r@ +#cmakedefine HAVE_INITGROUPS 1 +#cmakedefine HAVE_ISSETUGID 1 +#cmakedefine HAVE_ISNAN 1 +#cmakedefine HAVE_ISINF 1 +#cmakedefine HAVE_LARGE_PAGE_OPTION 1 +#cmakedefine HAVE_LDIV 1 +#cmakedefine HAVE_LRAND48 1 +#cmakedefine HAVE_LOCALTIME_R 1 +#cmakedefine HAVE_LOG2 1 +#cmakedefine HAVE_LONGJMP 1 +#cmakedefine HAVE_LSTAT 1 +#cmakedefine HAVE_NPTL 1 +#cmakedefine HAVE_NL_LANGINFO 1 +#cmakedefine HAVE_MADVISE 1 +#cmakedefine HAVE_DECL_MADVISE 1 +#cmakedefine HAVE_DECL_TGOTO 1 +#cmakedefine HAVE_DECL_MHA_MAPSIZE_VA +#cmakedefine HAVE_MALLINFO 1 +#cmakedefine HAVE_MEMCPY 1 +#cmakedefine HAVE_MEMMOVE 1 +#cmakedefine HAVE_MKSTEMP 1 +#cmakedefine HAVE_MLOCKALL 1 +#cmakedefine HAVE_MMAP 1 +#cmakedefine HAVE_MMAP64 1 +#cmakedefine HAVE_PERROR 1 +#cmakedefine HAVE_POLL 1 +#cmakedefine HAVE_PORT_CREATE 1 +#cmakedefine HAVE_POSIX_FALLOCATE 1 +#cmakedefine HAVE_PREAD 1 +#cmakedefine HAVE_PAUSE_INSTRUCTION 1 +#cmakedefine HAVE_FAKE_PAUSE_INSTRUCTION 1 +#cmakedefine HAVE_PTHREAD_ATTR_CREATE 1 +#cmakedefine HAVE_PTHREAD_ATTR_GETSTACKSIZE 1 +#cmakedefine HAVE_PTHREAD_ATTR_SETPRIO 1 +#cmakedefine HAVE_PTHREAD_ATTR_SETSCHEDPARAM 1 +#cmakedefine HAVE_PTHREAD_ATTR_SETSCOPE 1 +#cmakedefine HAVE_PTHREAD_ATTR_SETSTACKSIZE 1 +#cmakedefine HAVE_PTHREAD_CONDATTR_CREATE 1 +#cmakedefine HAVE_PTHREAD_CONDATTR_SETCLOCK 1 +#cmakedefine HAVE_PTHREAD_INIT 1 +#cmakedefine HAVE_PTHREAD_KEY_DELETE 1 +#cmakedefine HAVE_PTHREAD_KEY_DELETE 1 +#cmakedefine HAVE_PTHREAD_KILL 1 +#cmakedefine HAVE_PTHREAD_RWLOCK_RDLOCK 1 +#cmakedefine HAVE_PTHREAD_SETPRIO_NP 1 +#cmakedefine HAVE_PTHREAD_SETSCHEDPARAM 1 +#cmakedefine HAVE_PTHREAD_SIGMASK 1 +#cmakedefine HAVE_PTHREAD_THREADMASK 1 +#cmakedefine HAVE_PTHREAD_YIELD_NP 1 +#cmakedefine HAVE_PTHREAD_YIELD_ZERO_ARG 1 +#cmakedefine HAVE_PUTENV 1 +#cmakedefine HAVE_RE_COMP 1 +#cmakedefine HAVE_REGCOMP 1 +#cmakedefine HAVE_READDIR_R 1 +#cmakedefine HAVE_READLINK 1 +#cmakedefine HAVE_REALPATH 1 +#cmakedefine HAVE_RENAME 1 +#cmakedefine HAVE_RINT 1 +#cmakedefine HAVE_RWLOCK_INIT 1 +#cmakedefine HAVE_SCHED_YIELD 1 +#cmakedefine HAVE_SELECT 1 +#cmakedefine HAVE_SETFD 1 +#cmakedefine HAVE_SETENV 1 +#cmakedefine HAVE_SETLOCALE 1 +#cmakedefine HAVE_SIGADDSET 1 +#cmakedefine HAVE_SIGEMPTYSET 1 +#cmakedefine HAVE_SIGHOLD 1 +#cmakedefine HAVE_SIGSET 1 +#cmakedefine HAVE_SIGSET_T 1 +#cmakedefine HAVE_SIGACTION 1 +#cmakedefine HAVE_SIGTHREADMASK 1 +#cmakedefine HAVE_SIGWAIT 1 +#cmakedefine HAVE_SLEEP 1 +#cmakedefine HAVE_SNPRINTF 1 +#cmakedefine HAVE_STPCPY 1 +#cmakedefine HAVE_STRERROR 1 +#cmakedefine HAVE_STRCOLL 1 +#cmakedefine HAVE_STRSIGNAL 1 +#cmakedefine HAVE_STRLCPY 1 +#cmakedefine HAVE_STRLCAT 1 +#cmakedefine HAVE_FGETLN 1 +#cmakedefine HAVE_STRNLEN 1 +#cmakedefine HAVE_STRPBRK 1 +#cmakedefine HAVE_STRSEP 1 +#cmakedefine HAVE_STRSTR 1 +#cmakedefine HAVE_STRTOK_R 1 +#cmakedefine HAVE_STRTOL 1 +#cmakedefine HAVE_STRTOLL 1 +#cmakedefine HAVE_STRTOUL 1 +#cmakedefine HAVE_STRTOULL 1 +#cmakedefine HAVE_SHMAT 1 +#cmakedefine HAVE_SHMCTL 1 +#cmakedefine HAVE_SHMDT 1 +#cmakedefine HAVE_SHMGET 1 +#cmakedefine HAVE_TELL 1 +#cmakedefine HAVE_TEMPNAM 1 +#cmakedefine HAVE_THR_SETCONCURRENCY 1 +#cmakedefine HAVE_THR_YIELD 1 +#cmakedefine HAVE_VALLOC 1 +#define HAVE_VIO_READ_BUFF 1 +#cmakedefine HAVE_VASPRINTF 1 +#cmakedefine HAVE_VPRINTF 1 +#cmakedefine HAVE_VSNPRINTF 1 +#cmakedefine HAVE_FTRUNCATE 1 +#cmakedefine HAVE_TZNAME 1 +#cmakedefine HAVE_AIO_READ 1 +/* Symbols we may use */ +#cmakedefine HAVE_SYS_ERRLIST 1 +/* used by stacktrace functions */ +#cmakedefine HAVE_BSS_START 1 +#cmakedefine HAVE_BACKTRACE 1 +#cmakedefine HAVE_BACKTRACE_SYMBOLS 1 +#cmakedefine HAVE_BACKTRACE_SYMBOLS_FD 1 +#cmakedefine HAVE_STRUCT_SOCKADDR_IN6 1 +#cmakedefine HAVE_STRUCT_IN6_ADDR 1 +#cmakedefine HAVE_NETINET_IN6_H 1 +#cmakedefine HAVE_IPV6 1 +#cmakedefine ss_family @ss_family@ +#cmakedefine HAVE_TIMESPEC_TS_SEC 1 +#cmakedefine STRUCT_DIRENT_HAS_D_INO 1 +#cmakedefine STRUCT_DIRENT_HAS_D_NAMLEN 1 +#cmakedefine SPRINTF_RETURNS_INT 1 + +#define USE_MB 1 +#define USE_MB_IDENT 1 + + + +/* Types we may use */ +#cmakedefine SIZEOF_CHAR @SIZEOF_CHAR@ +#if SIZEOF_CHAR +# define HAVE_CHAR 1 +#endif + +#cmakedefine SIZEOF_CHARP @SIZEOF_CHARP@ +#if SIZEOF_CHARP +#define HAVE_CHARP 1 +#define SIZEOF_VOIDP SIZEOF_CHARP +#endif + +#cmakedefine SIZEOF_SHORT @SIZEOF_SHORT@ +#if SIZEOF_SHORT +# define HAVE_SHORT 1 +#endif + +#cmakedefine SIZEOF_INT @SIZEOF_INT@ +#if SIZEOF_INT +# define HAVE_INT 1 +#endif + +#cmakedefine SIZEOF_LONG @SIZEOF_LONG@ +#if SIZEOF_LONG +# define HAVE_LONG 1 +#endif + +#cmakedefine SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@ +#if SIZEOF_LONG_LONG +# define HAVE_LONG_LONG 1 +#endif + +#cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@ +#if SIZEOF_OFF_T +#define HAVE_OFF_T 1 +#endif + +#cmakedefine SIZEOF_SIGSET_T @SIZEOF_SIGSET_T@ +#if SIZEOF_SIGSET_T +#define HAVE_SIGSET_T 1 +#endif + +#cmakedefine SIZEOF_SIZE_T @SIZEOF_SIZE_T@ +#if SIZEOF_SIZE_T +#define HAVE_SIZE_T 1 +#endif + +#cmakedefine SIZEOF_UCHAR @SIZEOF_UCHAR@ +#if SIZEOF_UCHAR +#define HAVE_UCHAR 1 +#endif + +#cmakedefine SIZEOF_UINT @SIZEOF_UINT@ +#if SIZEOF_UINT +#define HAVE_UINT 1 +#endif + +#cmakedefine SIZEOF_ULONG @SIZEOF_ULONG@ +#if SIZEOF_ULONG +#define HAVE_ULONG 1 +#endif + +#cmakedefine SIZEOF_INT8 @SIZEOF_INT8@ +#if SIZEOF_INT8 +#define HAVE_INT8 1 +#endif +#cmakedefine SIZEOF_UINT8 @SIZEOF_UINT8@ +#if SIZEOF_UINT8 +#define HAVE_UINT8 1 +#endif + +#cmakedefine SIZEOF_INT16 @SIZEOF_INT16@ +#if SIZEOF_INT16 +# define HAVE_INT16 1 +#endif +#cmakedefine SIZEOF_UINT16 @SIZEOF_UINT16@ +#if SIZEOF_UINT16 +#define HAVE_UINT16 1 +#endif + +#cmakedefine SIZEOF_INT32 @SIZEOF_INT32@ +#if SIZEOF_INT32 +#define HAVE_INT32 1 +#endif +#cmakedefine SIZEOF_UINT32 @SIZEOF_UINT32@ +#if SIZEOF_UINT32 +#define HAVE_UINT32 1 +#endif +#cmakedefine SIZEOF_U_INT32_T @SIZEOF_U_INT32_T@ +#if SIZEOF_U_INT32_T +#define HAVE_U_INT32_T 1 +#endif + +#cmakedefine SIZEOF_INT64 @SIZEOF_INT64@ +#if SIZEOF_INT64 +#define HAVE_INT64 1 +#endif +#cmakedefine SIZEOF_UINT64 @SIZEOF_UINT64@ +#if SIZEOF_UINT64 +#define HAVE_UINT64 1 +#endif + +#cmakedefine SOCKET_SIZE_TYPE @SOCKET_SIZE_TYPE@ + +#cmakedefine SIZEOF_BOOL @SIZEOF_BOOL@ +#if SIZEOF_BOOL +#define HAVE_BOOL 1 +#endif +#cmakedefine HAVE_MBSTATE_T + +#define MAX_INDEXES 64 + +#cmakedefine QSORT_TYPE_IS_VOID 1 +#define RETQSORTTYPE void + +#cmakedefine SIGNAL_RETURN_TYPE_IS_VOID 1 +#define RETSIGTYPE void +#if SIGNAL_RETURN_TYPE_IS_VOID +#define VOID_SIGHANDLER 1 +#endif +#define STRUCT_RLIMIT struct rlimit + +#cmakedefine WORDS_BIGENDIAN 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler calls + it, or to nothing if 'inline' is not supported under any name. */ +#cmakedefine C_HAS_inline 1 +#if !(C_HAS_inline) +#ifndef __cplusplus +# define inline @C_INLINE@ +#endif +#endif + + +#cmakedefine TARGET_OS_LINUX 1 +#cmakedefine TARGET_OS_SOLARIS 1 + +#cmakedefine HAVE_WCTYPE_H 1 +#cmakedefine HAVE_WCHAR_H 1 +#cmakedefine HAVE_LANGINFO_H 1 +#cmakedefine HAVE_MBRLEN +#cmakedefine HAVE_MBSCMP +#cmakedefine HAVE_MBSRTOWCS +#cmakedefine HAVE_WCRTOMB +#cmakedefine HAVE_MBRTOWC +#cmakedefine HAVE_WCSCOLL +#cmakedefine HAVE_WCSDUP +#cmakedefine HAVE_WCWIDTH +#cmakedefine HAVE_WCTYPE +#cmakedefine HAVE_ISWLOWER 1 +#cmakedefine HAVE_ISWUPPER 1 +#cmakedefine HAVE_TOWLOWER 1 +#cmakedefine HAVE_TOWUPPER 1 +#cmakedefine HAVE_ISWCTYPE 1 +#cmakedefine HAVE_WCHAR_T 1 +#cmakedefine HAVE_WCTYPE_T 1 +#cmakedefine HAVE_WINT_T 1 + + +#cmakedefine HAVE_STRCASECMP 1 +#cmakedefine HAVE_STRNCASECMP 1 +#cmakedefine HAVE_STRDUP 1 +#cmakedefine HAVE_LANGINFO_CODESET +#cmakedefine HAVE_TCGETATTR 1 +#cmakedefine HAVE_FLOCKFILE 1 + +#cmakedefine HAVE_WEAK_SYMBOL 1 +#cmakedefine HAVE_ABI_CXA_DEMANGLE 1 + + +#cmakedefine HAVE_POSIX_SIGNALS 1 +#cmakedefine HAVE_BSD_SIGNALS 1 +#cmakedefine HAVE_SVR3_SIGNALS 1 +#cmakedefine HAVE_V7_SIGNALS 1 + + +#cmakedefine HAVE_SOLARIS_STYLE_GETHOST 1 +#cmakedefine HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE 1 +#cmakedefine HAVE_GETHOSTBYNAME_R_RETURN_INT 1 + +#cmakedefine MY_ATOMIC_MODE_DUMMY 1 +#cmakedefine MY_ATOMIC_MODE_RWLOCKS 1 +#cmakedefine HAVE_GCC_ATOMIC_BUILTINS 1 +#cmakedefine HAVE_SOLARIS_ATOMIC 1 +#cmakedefine HAVE_DECL_SHM_HUGETLB 1 +#cmakedefine HAVE_LARGE_PAGES 1 +#cmakedefine HUGETLB_USE_PROC_MEMINFO 1 +#cmakedefine NO_FCNTL_NONBLOCK 1 + +#cmakedefine _LARGE_FILES 1 +#cmakedefine _LARGEFILE_SOURCE 1 +#cmakedefine _LARGEFILE64_SOURCE 1 +#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ + +#cmakedefine TIME_WITH_SYS_TIME 1 + +#cmakedefine STACK_DIRECTION @STACK_DIRECTION@ + +#define THREAD 1 +#define THREAD_SAFE_CLIENT 1 + +#define SYSTEM_TYPE "@SYSTEM_TYPE@" +#define MACHINE_TYPE "@CMAKE_SYSTEM_PROCESSOR@" +#cmakedefine HAVE_DTRACE 1 + +#cmakedefine SIGNAL_WITH_VIO_CLOSE 1 + +/* Windows stuff, mostly functions, that have Posix analogs but named differently */ +#cmakedefine S_IROTH @S_IROTH@ +#cmakedefine S_IFIFO @S_IFIFO@ +#cmakedefine IPPROTO_IPV6 @IPPROTO_IPV6@ +#cmakedefine IPV6_V6ONLY @IPV6_V6ONLY@ +#cmakedefine sigset_t @sigset_t@ +#cmakedefine mode_t @mode_t@ +#cmakedefine SIGQUIT @SIGQUIT@ +#cmakedefine SIGPIPE @SIGPIPE@ +#cmakedefine isnan @isnan@ +#cmakedefine finite @finite@ +#cmakedefine popen @popen@ +#cmakedefine pclose @pclose@ +#cmakedefine ssize_t @ssize_t@ +#cmakedefine strcasecmp @strcasecmp@ +#cmakedefine strncasecmp @strncasecmp@ +#cmakedefine snprintf @snprintf@ +#cmakedefine strtok_r @strtok_r@ +#cmakedefine strtoll @strtoll@ +#cmakedefine strtoull @strtoull@ + + + +/* + MySQL features +*/ +#cmakedefine ENABLED_LOCAL_INFILE 1 +#cmakedefine ENABLED_PROFILING 1 +#cmakedefine EXTRA_DEBUG 1 +#cmakedefine BACKUP_TEST 1 +#cmakedefine CYBOZU 1 + +/* Character sets and collations */ +#cmakedefine MYSQL_DEFAULT_CHARSET_NAME "latin1" +#cmakedefine MYSQL_DEFAULT_COLLATION_NAME "latin1_swedish_ci" + +#cmakedefine USE_MB 1 +#cmakedefine USE_MB_IDENT 1 +#cmakedefine USE_STRCOLL 1 + +#cmakedefine HAVE_CHARSET_armscii8 1 +#cmakedefine HAVE_CHARSET_ascii +#cmakedefine HAVE_CHARSET_big5 1 +#cmakedefine HAVE_CHARSET_cp1250 1 +#cmakedefine HAVE_CHARSET_cp1251 1 +#cmakedefine HAVE_CHARSET_cp1256 1 +#cmakedefine HAVE_CHARSET_cp1257 1 +#cmakedefine HAVE_CHARSET_cp850 1 +#cmakedefine HAVE_CHARSET_cp852 1 +#cmakedefine HAVE_CHARSET_cp866 1 +#cmakedefine HAVE_CHARSET_cp932 1 +#cmakedefine HAVE_CHARSET_dec8 1 +#cmakedefine HAVE_CHARSET_eucjpms 1 +#cmakedefine HAVE_CHARSET_euckr 1 +#cmakedefine HAVE_CHARSET_gb2312 1 +#cmakedefine HAVE_CHARSET_gbk 1 +#cmakedefine HAVE_CHARSET_geostd8 1 +#cmakedefine HAVE_CHARSET_greek 1 +#cmakedefine HAVE_CHARSET_hebrew 1 +#cmakedefine HAVE_CHARSET_hp8 1 +#cmakedefine HAVE_CHARSET_keybcs2 1 +#cmakedefine HAVE_CHARSET_koi8r 1 +#cmakedefine HAVE_CHARSET_koi8u 1 +#cmakedefine HAVE_CHARSET_latin1 1 +#cmakedefine HAVE_CHARSET_latin2 1 +#cmakedefine HAVE_CHARSET_latin5 1 +#cmakedefine HAVE_CHARSET_latin7 1 +#cmakedefine HAVE_CHARSET_macce 1 +#cmakedefine HAVE_CHARSET_macroman 1 +#cmakedefine HAVE_CHARSET_sjis 1 +#cmakedefine HAVE_CHARSET_swe7 1 +#cmakedefine HAVE_CHARSET_tis620 1 +#cmakedefine HAVE_CHARSET_ucs2 1 +#cmakedefine HAVE_CHARSET_ujis 1 +#cmakedefine HAVE_CHARSET_utf8mb4 1 +#cmakedefine HAVE_CHARSET_utf8mb3 1 +#cmakedefine HAVE_CHARSET_utf8 1 +#cmakedefine HAVE_CHARSET_utf16 1 +#cmakedefine HAVE_CHARSET_utf32 1 +#cmakedefine HAVE_UCA_COLLATIONS 1 +#cmakedefine HAVE_COMPRESS 1 + + +/* + Stuff that always need to be defined (compile breaks without it) +*/ +#define HAVE_SPATIAL 1 +#define HAVE_RTREE_KEYS 1 +#define HAVE_QUERY_CACHE 1 + +/* + Important storage engines (those that really need define + WITH__STORAGE_ENGINE for the whole server) +*/ +#cmakedefine WITH_MYISAM_STORAGE_ENGINE 1 +#cmakedefine WITH_MYISAMMRG_STORAGE_ENGINE 1 +#cmakedefine WITH_HEAP_STORAGE_ENGINE 1 +#cmakedefine WITH_CSV_STORAGE_ENGINE 1 +#cmakedefine WITH_PARTITION_STORAGE_ENGINE 1 +#cmakedefine WITH_PERFSCHEMA_STORAGE_ENGINE 1 +#cmakedefine WITH_NDBCLUSTER_STORAGE_ENGINE 1 +#if (WITH_NDBCLUSTER_STORAGE_ENGINE) && !defined(EMBEDDED_LIBRARY) +#define HAVE_NDB_BINLOG 1 +#endif + +#cmakedefine DEFAULT_MYSQL_HOME "@DEFAULT_MYSQL_HOME@" +#cmakedefine SHAREDIR "@SHAREDIR@" +#cmakedefine DEFAULT_BASEDIR "@DEFAULT_BASEDIR@" +#cmakedefine MYSQL_DATADIR "@MYSQL_DATADIR@" +#cmakedefine DEFAULT_CHARSET_HOME "@DEFAULT_CHARSET_HOME@" + +#define PACKAGE "mysql" +#define PACKAGE_BUGREPORT "" +#define PACKAGE_NAME "MySQL Server" +#define PACKAGE_STRING "MySQL Server @VERSION@" +#define PACKAGE_TARNAME "mysql" +#define PACKAGE_VERSION "@VERSION@" +#define VERSION "@VERSION@" +#define PROTOCOL_VERSION 10 + + +#endif diff --git a/configure.cmake b/configure.cmake new file mode 100644 index 00000000000..aff4c1403f9 --- /dev/null +++ b/configure.cmake @@ -0,0 +1,1335 @@ +# Copyright (C) 2009 Sun Microsystems,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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +INCLUDE (CheckCSourceCompiles) +INCLUDE (CheckCXXSourceCompiles) +INCLUDE (CheckStructHasMember) +INCLUDE (CheckLibraryExists) +INCLUDE (CheckFunctionExists) +INCLUDE (CheckCCompilerFlag) +INCLUDE (CheckCSourceRuns) + + +# Sometimes it is handy to know if PIC option +# is set, to avoid recompilation of the same source +# for shared libs. We also allow it as an option for +# fast compile. +IF(UNIX) + IF(APPLE) + # OSX executable are always PIC + SET(WITH_PIC ON) + ELSE() + OPTION(WITH_PIC "Generate PIC objects" OFF) + IF(WITH_PIC) + SET(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} ${CMAKE_SHARED_LIBRARY_C_FLAGS}") + SET(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") + ENDIF() + ENDIF() +ENDIF() + + +# +# Tests for OS +IF (CMAKE_SYSTEM_NAME MATCHES "Linux") + SET(TARGET_OS_LINUX 1) + SET(HAVE_NPTL 1) +ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET(TARGET_OS_SOLARIS 1) +ENDIF() + + +# OS display name (version_compile_os etc). +# Used by the test suite to ignore bugs on some platforms, +# typically on Windows. +IF(WIN32) + IF(CMAKE_SIZEOF_VOID_P MATCHES 8) + SET(SYSTEM_TYPE "Win64") + ELSE() + SET(SYSTEM_TYPE "Win32") + ENDIF() +ELSE() + IF(PLATFORM) + SET(SYSTEM_TYPE ${PLATFORM}) + ELSE() + SET(SYSTEM_TYPE ${CMAKE_SYSTEM_NAME}) + ENDIF() +ENDIF() + + +# Intel compiler is almost Visual C++ +# (same compile flags etc). Set MSVC flag +IF(WIN32 AND CMAKE_C_COMPILER MATCHES "icl") + SET(MSVC TRUE) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCXX) + SET(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fno-implicit-templates -fno-exceptions -fno-rtti") + IF(CMAKE_CXX_FLAGS) + STRING(REGEX MATCH "fno-implicit-templates" NO_IMPLICIT_TEMPLATES + ${CMAKE_CXX_FLAGS}) + IF (NO_IMPLICIT_TEMPLATES) + SET(HAVE_EXPLICIT_TEMPLATE_INSTANTIATION TRUE) + ENDIF() + ENDIF() + IF(MINGW AND CMAKE_SIZEOF_VOIDP EQUAL 4) + # mininal architecture flags, i486 enables GCC atomics + ADD_DEFINITIONS(-march=i486) + ENDIF() +ENDIF(CMAKE_COMPILER_IS_GNUCXX) + +IF(WIN32) + SET(CAN_CONVERT_STATIC_TO_SHARED_LIB 1) +ENDIF() + +# Large files +SET(_LARGEFILE_SOURCE 1) +IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") + SET(_LARGEFILE64_SOURCE 1) + SET(_FILE_OFFSET_BITS 64) +ENDIF() +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS" ) + SET(_FILE_OFFSET_BITS 64) +ENDIF() +IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") + SET(_LARGE_FILES 1) +ENDIF() + + + + +IF(MSVC) +# Enable debug info also in Release build, and create PDB to be able to analyze +# crashes + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi") + SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /debug") +ENDIF() + +IF(CMAKE_GENERATOR MATCHES "Visual Studio 7") + # VS2003 has a bug that prevents linking mysqld with module definition file + # (/DEF option for linker). Linker would incorrectly complain about multiply + # defined symbols. Workaround is to disable dynamic plugins, so /DEF is not + # used. + MESSAGE( + "Warning: Building MySQL with Visual Studio 2003.NET is no more supported.") + MESSAGE("Please use a newer version of Visual Studio.") + SET(WITHOUT_DYNAMIC_PLUGINS TRUE) + + # VS2003 needs the /Op compiler option to disable floating point + # optimizations + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Op") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Op") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Op") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Op") + SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Op") + SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /Op") +ENDIF(CMAKE_GENERATOR MATCHES "Visual Studio 7") + + +IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) + IF(CMAKE_SIZEOF_VOID_P EQUAL 4) + # HPUX linker crashes building plugins + SET(WITHOUT_DYNAMIC_PLUGINS TRUE) + ENDIF() + # If not PA-RISC make shared library suffix .so + # OS understands both .sl and .so. CMake would + # use .sl, however MySQL prefers .so + IF(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "9000") + SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so" CACHE INTERNAL "" FORCE) + SET(CMAKE_SHARED_MODULE_SUFFIX ".so" CACHE INTERNAL "" FORCE) + ENDIF() +ENDIF() + +#Some OS specific hacks +IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + ADD_DEFINITIONS(-DNET_RETRY_COUNT=1000000) +ELSEIF(CMAKE_SYSTEM MATCHES "HP-UX" AND CMAKE_SYSTEM MATCHES "11") + ADD_DEFINITIONS(-DHPUX11) +ENDIF() + +# Disable warnings in Visual Studio 8 and above +IF(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7") + #TODO: update the code and remove the disabled warnings + ADD_DEFINITIONS(/wd4800 /wd4805) + ADD_DEFINITIONS(/wd4996) +ENDIF() + + +# Settings for Visual Studio 7 and above. +IF(MSVC) + # replace /MDd with /MTd + STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) + STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) + STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) + STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG_INIT ${CMAKE_C_FLAGS_DEBUG_INIT}) + + STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) + STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) + STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_CXX_FLAGS_DEBUG_INIT}) + + # generate map files, set stack size (see bug#20815) + SET(thread_stack_size 1048576) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:${thread_stack_size}") + ADD_DEFINITIONS(-DPTHREAD_STACK_MIN=${thread_stack_size}) + + # remove support for Exception handling + STRING(REPLACE "/GX" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS_INIT ${CMAKE_CXX_FLAGS_INIT}) + STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_CXX_FLAGS_DEBUG_INIT}) + + # Mark 32 bit executables large address aware so they can + # use > 2GB address space + IF(CMAKE_SIZEOF_VOID_P MATCHES 4) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") + ENDIF(CMAKE_SIZEOF_VOID_P MATCHES 4) +ENDIF(MSVC) + +IF(WIN32) + ADD_DEFINITIONS("-D_WINDOWS -D__WIN__ -D_CRT_SECURE_NO_DEPRECATE") + ADD_DEFINITIONS("-D_WIN32_WINNT=0x0501") + # Speed up build process excluding unused header files + ADD_DEFINITIONS("-DWIN32_LEAN_AND_MEAN") + IF (MSVC_VERSION GREATER 1400) + # Speed up multiprocessor build + ADD_DEFINITIONS("/MP") + ENDIF() + + # default to x86 platform. We'll check for X64 in a bit + SET (PLATFORM X86) + IF(MSVC AND CMAKE_SIZEOF_VOID_P MATCHES 8) + # _WIN64 is defined by the compiler itself. + # Yet, we define it here again to work around a bug with Intellisense + # described here: http://tinyurl.com/2cb428. + # Syntax highlighting is important for proper debugger functionality. + ADD_DEFINITIONS("-D_WIN64") + SET (PLATFORM X64) + ENDIF() +ENDIF() + + + +# Figure out what engines to build and how (statically or dynamically), +# add preprocessor defines for storage engines. +IF(WITHOUT_DYNAMIC_PLUGINS) + MESSAGE("Dynamic plugins are disabled.") +ENDIF(WITHOUT_DYNAMIC_PLUGINS) + + +# Perform machine tests on posix platforms only +IF(WIN32) + SET(SYSTEM_LIBS ws2_32) + SET(CMAKE_REQUIRED_INCLUDES "winsock2.h;ws2tcpip.h") + SET(CMAKE_REQUIRED_DEFINITONS "-D_WIN32_WINNT=0x0501") + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ws2_32) + LINK_LIBRARIES(ws2_32) +ENDIF() + + + +MACRO(MY_CHECK_LIB func lib found) + SET(${found} 0) + CHECK_FUNCTION_EXISTS(${func} HAVE_${func}_IN_LIBC) + CHECK_LIBRARY_EXISTS(${lib} ${func} "" HAVE_${func}_IN_${lib}) + IF (HAVE_${func}_IN_${lib}) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${lib}) + LINK_LIBRARIES(${lib}) + STRING(TOUPPER ${lib} upper_lib) + SET(HAVE_LIB${upper_lib} 1 CACHE INTERNAL "Library check") + SET(${found} 1) + ENDIF() +ENDMACRO() + +MACRO(MY_SEARCH_LIBS func lib found) + SET(${found} 0) + CHECK_FUNCTION_EXISTS(${func} HAVE_${func}_IN_LIBC) + IF(NOT HAVE_${func}_IN_LIBC) + MY_CHECK_LIB(${func} ${lib} ${found}) + ELSE() + SET(${found} 1) + ENDIF() +ENDMACRO() + +IF(UNIX) + MY_CHECK_LIB(floor m found) + IF(NOT found) + MY_CHECK_LIB( __infinity m found) + ENDIF() + MY_CHECK_LIB(gethostbyname_r nsl_r found) + IF (NOT found) + MY_CHECK_LIB(gethostbyname_r nsl found) + ENDIF() + MY_SEARCH_LIBS(bind bind found) + MY_SEARCH_LIBS(crypt crypt found) + MY_SEARCH_LIBS(setsockopt socket found) + MY_SEARCH_LIBS(aio_read rt found) + MY_SEARCH_LIBS(sched_yield posix4 found) + MY_CHECK_LIB(pthread_create pthread found) + MY_SEARCH_LIBS(dlopen dl found) + + OPTION(WITH_LIBWRAP "Compile with tcp wrappers support" OFF) + IF(WITH_LIBWRAP) + SET(SAVE_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} wrap) + CHECK_C_SOURCE_COMPILES( + " + #include + int allow_severity = 0; + int deny_severity = 0; + int main() + { + hosts_access(0); + }" + HAVE_LIBWRAP) + SET(CMAKE_REQUIRED_LIBRARIES ${SAVE_CMAKE_REQUIRED_LIBRARIES}) + IF(HAVE_LIBWRAP) + SET(MYSYS_LIBWRAP_SOURCE ${CMAKE_SOURCE_DIR}/mysys/my_libwrap.c) + SET(LIBWRAP_LIBRARY "wrap") + ENDIF() + ENDIF() +ENDIF() + +IF (CMAKE_SYSTEM_NAME MATCHES "SunOS") + INCLUDE(CheckLibraryExists) + CHECK_LIBRARY_EXISTS(mtmalloc malloc "" HAVE_LIBMTMALLOC) + IF(HAVE_LIBMTMALLOC) + LINK_LIBRARIES(mtmalloc) + ENDIF() +ENDIF() + + +# Workaround for CMake bug#9051 +IF(CMAKE_OSX_SYSROOT) + SET(ENV{CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}) +ENDIF() +IF(CMAKE_OSX_SYSROOT) + SET(ENV{MACOSX_DEPLOYMENT_TARGET} ${OSX_DEPLOYMENT_TARGET}) +ENDIF() + +# This macro is used only on Windows at the moment +# Some standard functions exist there under different +# names (e.g popen is _popen or strok_r is _strtok_s) +# If a replacement function exists, HAVE_FUNCTION is +# defined to 1. CMake variable will also +# be defined to the replacement name. +# So for example, CHECK_FUNCTION_REPLACEMENT(popen _popen) +# will define HAVE_POPEN to 1 and set variable named popen +# to _popen. If the header template, one needs to have +# cmakedefine popen @popen@ which will expand to +# define popen _popen after CONFIGURE_FILE + +MACRO(CHECK_FUNCTION_REPLACEMENT function replacement) + STRING(TOUPPER ${function} function_upper) + CHECK_FUNCTION_EXISTS(${function} HAVE_${function_upper}) + IF(NOT HAVE_${function_upper}) + CHECK_FUNCTION_EXISTS(${replacement} HAVE_${replacement}) + IF(HAVE_${replacement}) + SET(HAVE_${function_upper} 1 ) + SET(${function} ${replacement}) + ENDIF() + ENDIF() +ENDMACRO() + +MACRO(CHECK_SYMBOL_REPLACEMENT symbol replacement header) + STRING(TOUPPER ${symbol} symbol_upper) + CHECK_SYMBOL_EXISTS(${symbol} ${header} HAVE_${symbol_upper}) + IF(NOT HAVE_${symbol_upper}) + CHECK_SYMBOL_EXISTS(${replacement} ${header} HAVE_${replacement}) + IF(HAVE_${replacement}) + SET(HAVE_${symbol_upper} 1) + SET(${symbol} ${replacement}) + ENDIF() + ENDIF() +ENDMACRO() + + +MACRO(CHECK_INCLUDE_FILES_UNIX INCLUDES VAR) +IF(UNIX) + CHECK_INCLUDE_FILES ("${INCLUDES}" ${VAR}) +ENDIF() +ENDMACRO() + +MACRO(CHECK_C_SOURCE_COMPILES_UNIX SRC VAR) +IF(UNIX) + CHECK_C_SOURCE_COMPILES("${SRC}" ${VAR}) +ENDIF() +ENDMACRO() + +MACRO(CHECK_CXX_SOURCE_COMPILES_UNIX SRC VAR) +IF(UNIX) + CHECK_CXX_SOURCE_COMPILES("${SRC}" ${VAR}) +ENDIF() +ENDMACRO() + +MACRO(CHECK_FUNCTION_EXISTS_UNIX FUNC VAR) +IF(UNIX) + CHECK_FUNCTION_EXISTS("${FUNC}" ${VAR}) +ENDIF() +ENDMACRO() + +MACRO (CHECK_SYMBOL_EXISTS_UNIX SYM HEADER VAR) +IF(UNIX) + CHECK_SYMBOL_EXISTS("${SYM}" "${HEADER}" ${VAR}) +ENDIF() +ENDMACRO() + +# +# Tests for header files +# +INCLUDE (CheckIncludeFiles) + +CHECK_INCLUDE_FILES ("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) +CHECK_INCLUDE_FILES (sys/types.h HAVE_SYS_TYPES_H) +CHECK_INCLUDE_FILES_UNIX (alloca.h HAVE_ALLOCA_H) +CHECK_INCLUDE_FILES_UNIX (aio.h HAVE_AIO_H) +CHECK_INCLUDE_FILES_UNIX (arpa/inet.h HAVE_ARPA_INET_H) +CHECK_INCLUDE_FILES_UNIX (crypt.h HAVE_CRYPT_H) +CHECK_INCLUDE_FILES (dirent.h HAVE_DIRENT_H) +CHECK_INCLUDE_FILES_UNIX (dlfcn.h HAVE_DLFCN_H) +CHECK_INCLUDE_FILES_UNIX (execinfo.h HAVE_EXECINFO_H) +CHECK_INCLUDE_FILES (fcntl.h HAVE_FCNTL_H) +CHECK_INCLUDE_FILES (fenv.h HAVE_FENV_H) +CHECK_INCLUDE_FILES (float.h HAVE_FLOAT_H) +CHECK_INCLUDE_FILES_UNIX (floatingpoint.h HAVE_FLOATINGPOINT_H) +CHECK_INCLUDE_FILES_UNIX (fpu_control.h HAVE_FPU_CONTROL_H) +CHECK_INCLUDE_FILES_UNIX (grp.h HAVE_GRP_H) +CHECK_INCLUDE_FILES_UNIX (ieeefp.h HAVE_IEEEFP_H) +CHECK_INCLUDE_FILES (inttypes.h HAVE_INTTYPES_H) +CHECK_INCLUDE_FILES_UNIX (langinfo.h HAVE_LANGINFO_H) +CHECK_INCLUDE_FILES (limits.h HAVE_LIMITS_H) +CHECK_INCLUDE_FILES (locale.h HAVE_LOCALE_H) +CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H) +CHECK_INCLUDE_FILES (memory.h HAVE_MEMORY_H) +CHECK_INCLUDE_FILES_UNIX (netinet/in.h HAVE_NETINET_IN_H) +CHECK_INCLUDE_FILES_UNIX (paths.h HAVE_PATHS_H) +CHECK_INCLUDE_FILES_UNIX (port.h HAVE_PORT_H) +CHECK_INCLUDE_FILES_UNIX (pwd.h HAVE_PWD_H) +CHECK_INCLUDE_FILES_UNIX (sched.h HAVE_SCHED_H) +CHECK_INCLUDE_FILES_UNIX (select.h HAVE_SELECT_H) +CHECK_INCLUDE_FILES_UNIX (semaphore.h HAVE_SEMAPHORE_H) +CHECK_INCLUDE_FILES_UNIX (sys/dir.h HAVE_SYS_DIR_H) +CHECK_INCLUDE_FILES_UNIX (sys/pte.h HAVE_SYS_PTE_H) +CHECK_INCLUDE_FILES_UNIX (sys/ptem.h HAVE_SYS_PTEM_H) +CHECK_INCLUDE_FILES (stddef.h HAVE_STDDEF_H) +CHECK_INCLUDE_FILES (stdint.h HAVE_STDINT_H) +CHECK_INCLUDE_FILES (stdlib.h HAVE_STDLIB_H) +CHECK_INCLUDE_FILES (strings.h HAVE_STRINGS_H) +CHECK_INCLUDE_FILES (string.h HAVE_STRING_H) +CHECK_INCLUDE_FILES_UNIX (synch.h HAVE_SYNCH_H) +CHECK_INCLUDE_FILES_UNIX (sysent.h HAVE_SYSENT_H) +CHECK_INCLUDE_FILES_UNIX (sys/cdefs.h HAVE_SYS_CDEFS_H) +CHECK_INCLUDE_FILES_UNIX (sys/file.h HAVE_SYS_FILE_H) +CHECK_INCLUDE_FILES_UNIX (sys/fpu.h HAVE_SYS_FPU_H) +CHECK_INCLUDE_FILES_UNIX (sys/ioctl.h HAVE_SYS_IOCTL_H) +CHECK_INCLUDE_FILES_UNIX (sys/ipc.h HAVE_SYS_IPC_H) +CHECK_INCLUDE_FILES_UNIX (sys/malloc.h HAVE_SYS_MALLOC_H) +CHECK_INCLUDE_FILES_UNIX (sys/mman.h HAVE_SYS_MMAN_H) +CHECK_INCLUDE_FILES_UNIX (sys/prctl.h HAVE_SYS_PRCTL_H) +CHECK_INCLUDE_FILES_UNIX (sys/resource.h HAVE_SYS_RESOURCE_H) +CHECK_INCLUDE_FILES_UNIX (sys/select.h HAVE_SYS_SELECT_H) +CHECK_INCLUDE_FILES_UNIX (sys/shm.h HAVE_SYS_SHM_H) +CHECK_INCLUDE_FILES_UNIX (sys/socket.h HAVE_SYS_SOCKET_H) +CHECK_INCLUDE_FILES (sys/stat.h HAVE_SYS_STAT_H) +CHECK_INCLUDE_FILES_UNIX (sys/stream.h HAVE_SYS_STREAM_H) +CHECK_INCLUDE_FILES_UNIX (sys/termcap.h HAVE_SYS_TERMCAP_H) +CHECK_INCLUDE_FILES ("time.h;sys/timeb.h" HAVE_SYS_TIMEB_H) +CHECK_INCLUDE_FILES_UNIX ("curses.h;term.h" HAVE_TERM_H) +CHECK_INCLUDE_FILES_UNIX (termios.h HAVE_TERMIOS_H) +CHECK_INCLUDE_FILES_UNIX (termio.h HAVE_TERMIO_H) +CHECK_INCLUDE_FILES_UNIX (termcap.h HAVE_TERMCAP_H) +CHECK_INCLUDE_FILES_UNIX (unistd.h HAVE_UNISTD_H) +CHECK_INCLUDE_FILES (utime.h HAVE_UTIME_H) +CHECK_INCLUDE_FILES (varargs.h HAVE_VARARGS_H) +CHECK_INCLUDE_FILES (sys/time.h HAVE_SYS_TIME_H) +CHECK_INCLUDE_FILES (sys/utime.h HAVE_SYS_UTIME_H) +CHECK_INCLUDE_FILES_UNIX (sys/wait.h HAVE_SYS_WAIT_H) +CHECK_INCLUDE_FILES_UNIX (sys/param.h HAVE_SYS_PARAM_H) +CHECK_INCLUDE_FILES_UNIX (sys/vadvise.h HAVE_SYS_VADVISE_H) +CHECK_INCLUDE_FILES_UNIX (fnmatch.h HAVE_FNMATCH_H) +CHECK_INCLUDE_FILES (stdarg.h HAVE_STDARG_H) + +# sys/un.h is broken on Linux and Solaris +# It cannot be included alone ( it references types +# defined in stdlib.h, i.e size_t) +CHECK_C_SOURCE_COMPILES_UNIX(" +#include +#include +int main() +{ + return 0; +} +" +HAVE_SYS_UN_H) + +# +# Figure out threading library +# +FIND_PACKAGE (Threads) + +# +# Tests for functions +# +#CHECK_FUNCTION_EXISTS (aiowait HAVE_AIOWAIT) +CHECK_FUNCTION_EXISTS_UNIX (aio_read HAVE_AIO_READ) +CHECK_FUNCTION_EXISTS_UNIX (alarm HAVE_ALARM) +SET(HAVE_ALLOCA 1) +CHECK_FUNCTION_EXISTS_UNIX (backtrace HAVE_BACKTRACE) +CHECK_FUNCTION_EXISTS_UNIX (backtrace_symbols HAVE_BACKTRACE_SYMBOLS) +CHECK_FUNCTION_EXISTS_UNIX (backtrace_symbols_fd HAVE_BACKTRACE_SYMBOLS_FD) +CHECK_FUNCTION_EXISTS_UNIX (bcmp HAVE_BCMP) +CHECK_FUNCTION_EXISTS_UNIX (bfill HAVE_BFILL) +CHECK_FUNCTION_EXISTS_UNIX (bmove HAVE_BMOVE) +CHECK_FUNCTION_EXISTS (bsearch HAVE_BSEARCH) +CHECK_FUNCTION_EXISTS (index HAVE_INDEX) +CHECK_FUNCTION_EXISTS_UNIX (bzero HAVE_BZERO) +CHECK_FUNCTION_EXISTS_UNIX (clock_gettime HAVE_CLOCK_GETTIME) +CHECK_FUNCTION_EXISTS_UNIX (cuserid HAVE_CUSERID) +CHECK_FUNCTION_EXISTS_UNIX (directio HAVE_DIRECTIO) +CHECK_FUNCTION_EXISTS_UNIX (_doprnt HAVE_DOPRNT) +CHECK_FUNCTION_EXISTS_UNIX (flockfile HAVE_FLOCKFILE) +CHECK_FUNCTION_EXISTS_UNIX (ftruncate HAVE_FTRUNCATE) +CHECK_FUNCTION_EXISTS_UNIX (getline HAVE_GETLINE) +CHECK_FUNCTION_EXISTS_UNIX (compress HAVE_COMPRESS) +CHECK_FUNCTION_EXISTS_UNIX (crypt HAVE_CRYPT) +CHECK_FUNCTION_EXISTS_UNIX (dlerror HAVE_DLERROR) +CHECK_FUNCTION_EXISTS_UNIX (dlopen HAVE_DLOPEN) +IF (CMAKE_COMPILER_IS_GNUCC) + IF (CMAKE_EXE_LINKER_FLAGS MATCHES " -static " + OR CMAKE_EXE_LINKER_FLAGS MATCHES " -static$") + SET(HAVE_DLOPEN FALSE CACHE "Disable dlopen due to -static flag" FORCE) + SET(WITHOUT_DYNAMIC_PLUGINS TRUE) + ENDIF() +ENDIF() +CHECK_FUNCTION_EXISTS_UNIX (fchmod HAVE_FCHMOD) +CHECK_FUNCTION_EXISTS_UNIX (fcntl HAVE_FCNTL) +CHECK_FUNCTION_EXISTS_UNIX (fconvert HAVE_FCONVERT) +CHECK_FUNCTION_EXISTS_UNIX (fdatasync HAVE_FDATASYNC) +CHECK_FUNCTION_EXISTS_UNIX (fesetround HAVE_FESETROUND) +CHECK_FUNCTION_EXISTS_UNIX (fpsetmask HAVE_FPSETMASK) +CHECK_FUNCTION_EXISTS_UNIX (fseeko HAVE_FSEEKO) +CHECK_FUNCTION_EXISTS_UNIX (fsync HAVE_FSYNC) +CHECK_FUNCTION_EXISTS (getcwd HAVE_GETCWD) +CHECK_FUNCTION_EXISTS_UNIX (gethostbyaddr_r HAVE_GETHOSTBYADDR_R) +CHECK_FUNCTION_EXISTS_UNIX (gethostbyname_r HAVE_GETHOSTBYNAME_R) +CHECK_FUNCTION_EXISTS_UNIX (gethrtime HAVE_GETHRTIME) +CHECK_FUNCTION_EXISTS (getnameinfo HAVE_GETNAMEINFO) +CHECK_FUNCTION_EXISTS_UNIX (getpass HAVE_GETPASS) +CHECK_FUNCTION_EXISTS_UNIX (getpassphrase HAVE_GETPASSPHRASE) +CHECK_FUNCTION_EXISTS_UNIX (getpwnam HAVE_GETPWNAM) +CHECK_FUNCTION_EXISTS_UNIX (getpwuid HAVE_GETPWUID) +CHECK_FUNCTION_EXISTS_UNIX (getrlimit HAVE_GETRLIMIT) +CHECK_FUNCTION_EXISTS_UNIX (getrusage HAVE_GETRUSAGE) +CHECK_FUNCTION_EXISTS_UNIX (getwd HAVE_GETWD) +CHECK_FUNCTION_EXISTS_UNIX (gmtime_r HAVE_GMTIME_R) +CHECK_FUNCTION_EXISTS_UNIX (initgroups HAVE_INITGROUPS) +CHECK_FUNCTION_EXISTS_UNIX (issetugid HAVE_ISSETUGID) +CHECK_FUNCTION_EXISTS (ldiv HAVE_LDIV) +CHECK_FUNCTION_EXISTS_UNIX (localtime_r HAVE_LOCALTIME_R) +CHECK_FUNCTION_EXISTS (longjmp HAVE_LONGJMP) +CHECK_FUNCTION_EXISTS (lstat HAVE_LSTAT) +CHECK_FUNCTION_EXISTS_UNIX (madvise HAVE_MADVISE) +CHECK_FUNCTION_EXISTS_UNIX (mallinfo HAVE_MALLINFO) +CHECK_FUNCTION_EXISTS (memcpy HAVE_MEMCPY) +CHECK_FUNCTION_EXISTS (memmove HAVE_MEMMOVE) +CHECK_FUNCTION_EXISTS (mkstemp HAVE_MKSTEMP) +CHECK_FUNCTION_EXISTS_UNIX (mlock HAVE_MLOCK) +CHECK_FUNCTION_EXISTS_UNIX (mlockall HAVE_MLOCKALL) +CHECK_FUNCTION_EXISTS_UNIX (mmap HAVE_MMAP) +CHECK_FUNCTION_EXISTS_UNIX (mmap64 HAVE_MMAP64) +CHECK_FUNCTION_EXISTS (perror HAVE_PERROR) +CHECK_FUNCTION_EXISTS_UNIX (poll HAVE_POLL) +CHECK_FUNCTION_EXISTS_UNIX (port_create HAVE_PORT_CREATE) +CHECK_FUNCTION_EXISTS_UNIX (posix_fallocate HAVE_POSIX_FALLOCATE) +CHECK_FUNCTION_EXISTS_UNIX (pread HAVE_PREAD) +CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_create HAVE_PTHREAD_ATTR_CREATE) +CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_getstacksize HAVE_PTHREAD_ATTR_GETSTACKSIZE) +CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_setprio HAVE_PTHREAD_ATTR_SETPRIO) +CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_setschedparam + HAVE_PTHREAD_ATTR_SETSCHEDPARAM) +CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_setscope HAVE_PTHREAD_ATTR_SETSCOPE) +CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_setstacksize HAVE_PTHREAD_ATTR_SETSTACKSIZE) +CHECK_FUNCTION_EXISTS_UNIX (pthread_condattr_create HAVE_PTHREAD_CONDATTR_CREATE) +CHECK_FUNCTION_EXISTS_UNIX (pthread_condattr_setclock HAVE_PTHREAD_CONDATTR_SETCLOCK) +CHECK_FUNCTION_EXISTS_UNIX (pthread_init HAVE_PTHREAD_INIT) +CHECK_FUNCTION_EXISTS_UNIX (pthread_key_delete HAVE_PTHREAD_KEY_DELETE) +CHECK_FUNCTION_EXISTS_UNIX (pthread_rwlock_rdlock HAVE_PTHREAD_RWLOCK_RDLOCK) +CHECK_FUNCTION_EXISTS_UNIX (pthread_setprio_np HAVE_PTHREAD_SETPRIO_NP) +CHECK_FUNCTION_EXISTS_UNIX (pthread_setschedparam HAVE_PTHREAD_SETSCHEDPARAM) +CHECK_FUNCTION_EXISTS_UNIX (pthread_sigmask HAVE_PTHREAD_SIGMASK) +CHECK_FUNCTION_EXISTS_UNIX (pthread_threadmask HAVE_PTHREAD_THREADMASK) +CHECK_FUNCTION_EXISTS_UNIX (pthread_yield_np HAVE_PTHREAD_YIELD_NP) +CHECK_FUNCTION_EXISTS (putenv HAVE_PUTENV) +CHECK_FUNCTION_EXISTS_UNIX (readdir_r HAVE_READDIR_R) +CHECK_FUNCTION_EXISTS_UNIX (readlink HAVE_READLINK) +CHECK_FUNCTION_EXISTS_UNIX (re_comp HAVE_RE_COMP) +CHECK_FUNCTION_EXISTS_UNIX (regcomp HAVE_REGCOMP) +CHECK_FUNCTION_EXISTS_UNIX (realpath HAVE_REALPATH) +CHECK_FUNCTION_EXISTS (rename HAVE_RENAME) +CHECK_FUNCTION_EXISTS_UNIX (rwlock_init HAVE_RWLOCK_INIT) +CHECK_FUNCTION_EXISTS_UNIX (sched_yield HAVE_SCHED_YIELD) +CHECK_FUNCTION_EXISTS_UNIX (setenv HAVE_SETENV) +CHECK_FUNCTION_EXISTS (setlocale HAVE_SETLOCALE) +CHECK_FUNCTION_EXISTS_UNIX (setfd HAVE_SETFD) +CHECK_FUNCTION_EXISTS_UNIX (sigaction HAVE_SIGACTION) +CHECK_FUNCTION_EXISTS_UNIX (sigthreadmask HAVE_SIGTHREADMASK) +CHECK_FUNCTION_EXISTS_UNIX (sigwait HAVE_SIGWAIT) +CHECK_FUNCTION_EXISTS_UNIX (sigaddset HAVE_SIGADDSET) +CHECK_FUNCTION_EXISTS_UNIX (sigemptyset HAVE_SIGEMPTYSET) +CHECK_FUNCTION_EXISTS_UNIX (sighold HAVE_SIGHOLD) +CHECK_FUNCTION_EXISTS_UNIX (sigset HAVE_SIGSET) +CHECK_FUNCTION_EXISTS_UNIX (sleep HAVE_SLEEP) +CHECK_FUNCTION_EXISTS (snprintf HAVE_SNPRINTF) +CHECK_FUNCTION_EXISTS_UNIX (stpcpy HAVE_STPCPY) +CHECK_FUNCTION_EXISTS (strcoll HAVE_STRCOLL) +CHECK_FUNCTION_EXISTS (strerror HAVE_STRERROR) +CHECK_FUNCTION_EXISTS_UNIX (strlcpy HAVE_STRLCPY) +CHECK_FUNCTION_EXISTS (strnlen HAVE_STRNLEN) +CHECK_FUNCTION_EXISTS_UNIX (strlcat HAVE_STRLCAT) +CHECK_FUNCTION_EXISTS_UNIX (strsignal HAVE_STRSIGNAL) +CHECK_FUNCTION_EXISTS_UNIX (fgetln HAVE_FGETLN) +CHECK_FUNCTION_EXISTS (strpbrk HAVE_STRPBRK) +CHECK_FUNCTION_EXISTS (strsep HAVE_STRSEP) +CHECK_FUNCTION_EXISTS (strstr HAVE_STRSTR) +CHECK_FUNCTION_EXISTS_UNIX (strtok_r HAVE_STRTOK_R) +CHECK_FUNCTION_EXISTS (strtol HAVE_STRTOL) +CHECK_FUNCTION_EXISTS (strtoll HAVE_STRTOLL) +CHECK_FUNCTION_EXISTS (strtoul HAVE_STRTOUL) +CHECK_FUNCTION_EXISTS (strtoull HAVE_STRTOULL) +CHECK_FUNCTION_EXISTS (strcasecmp HAVE_STRCASECMP) +CHECK_FUNCTION_EXISTS (strncasecmp HAVE_STRNCASECMP) +CHECK_FUNCTION_EXISTS (strdup HAVE_STRDUP) +CHECK_FUNCTION_EXISTS_UNIX (shmat HAVE_SHMAT) +CHECK_FUNCTION_EXISTS_UNIX (shmctl HAVE_SHMCTL) +CHECK_FUNCTION_EXISTS_UNIX (shmdt HAVE_SHMDT) +CHECK_FUNCTION_EXISTS_UNIX (shmget HAVE_SHMGET) +CHECK_FUNCTION_EXISTS (tell HAVE_TELL) +CHECK_FUNCTION_EXISTS (tempnam HAVE_TEMPNAM) +CHECK_FUNCTION_EXISTS_UNIX (thr_setconcurrency HAVE_THR_SETCONCURRENCY) +CHECK_FUNCTION_EXISTS_UNIX (thr_yield HAVE_THR_YIELD) +CHECK_FUNCTION_EXISTS_UNIX (vasprintf HAVE_VASPRINTF) +CHECK_FUNCTION_EXISTS (vsnprintf HAVE_VSNPRINTF) +CHECK_FUNCTION_EXISTS_UNIX (vprintf HAVE_VPRINTF) +CHECK_FUNCTION_EXISTS_UNIX (valloc HAVE_VALLOC) +CHECK_FUNCTION_EXISTS_UNIX (memalign HAVE_MEMALIGN) +CHECK_FUNCTION_EXISTS_UNIX (chown HAVE_CHOWN) +CHECK_FUNCTION_EXISTS_UNIX (nl_langinfo HAVE_NL_LANGINFO) + + +# +# Tests for symbols +# +INCLUDE (CheckSymbolExists) +CHECK_SYMBOL_EXISTS_UNIX(sys_errlist "stdio.h" HAVE_SYS_ERRLIST) +CHECK_SYMBOL_EXISTS_UNIX(madvise "sys/mman.h" HAVE_DECL_MADVISE) +CHECK_SYMBOL_EXISTS_UNIX(tzname "time.h" HAVE_TZNAME) +CHECK_SYMBOL_EXISTS(lrand48 "stdlib.h" HAVE_LRAND48) +CHECK_SYMBOL_EXISTS_UNIX(getpagesize "unistd.h" HAVE_GETPAGESIZE) +CHECK_SYMBOL_EXISTS_UNIX(TIOCGWINSZ "sys/ioctl.h" GWINSZ_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS_UNIX(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS_UNIX(TIOCSTAT "sys/ioctl.h" TIOCSTAT_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY) + +CHECK_SYMBOL_EXISTS(finite "math.h" HAVE_FINITE_IN_MATH_H) +IF(HAVE_FINITE_IN_MATH_H) + SET(HAVE_FINITE TRUE CACHE INTERNAL "") +ELSE() + CHECK_SYMBOL_EXISTS(finite "ieeefp.h" HAVE_FINITE) +ENDIF() +CHECK_SYMBOL_EXISTS(log2 "math.h" HAVE_LOG2) + + + +# +# Test for endianess +# +INCLUDE(TestBigEndian) +IF(APPLE) + # Care for universal binary format + SET(WORDS_BIGENDIAN __BIG_ENDIAN CACHE INTERNAL "big endian test") +ELSE() + TEST_BIG_ENDIAN(WORDS_BIGENDIAN) +ENDIF() + +# +# Tests for type sizes (and presence) +# +INCLUDE (CheckTypeSize) +set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + -D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 + -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) +SET(CMAKE_EXTRA_INCLUDE_FILES signal.h) +CHECK_TYPE_SIZE(sigset_t SIZEOF_SIGSET_T) +IF(SIZEOF_SIGSET_T) + SET(HAVE_SIGSET_T 1) +ENDIF() +IF(NOT SIZEOF_SIGSET_T) + SET(sigset_t int) +ENDIF() +CHECK_TYPE_SIZE(mode_t SIZEOF_MODE_T) +IF(NOT SIZEOF_MODE_T) + SET(mode_t int) +ENDIF() + + +IF(HAVE_STDINT_H) + SET(CMAKE_EXTRA_INCLUDE_FILES stdint.h) +ENDIF(HAVE_STDINT_H) +CHECK_TYPE_SIZE(char SIZEOF_CHAR) +CHECK_TYPE_SIZE("char *" SIZEOF_CHARP) +CHECK_TYPE_SIZE(short SIZEOF_SHORT) +CHECK_TYPE_SIZE(int SIZEOF_INT) +CHECK_TYPE_SIZE(long SIZEOF_LONG) +CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG) +SET(CMAKE_EXTRA_INCLUDE_FILES stdio.h) +CHECK_TYPE_SIZE(size_t SIZEOF_SIZE_T) +SET(CMAKE_EXTRA_INCLUDE_FILES sys/types.h) +CHECK_TYPE_SIZE(off_t SIZEOF_OFF_T) +CHECK_TYPE_SIZE(uchar SIZEOF_UCHAR) +CHECK_TYPE_SIZE(uint SIZEOF_UINT) +CHECK_TYPE_SIZE(ulong SIZEOF_ULONG) +CHECK_TYPE_SIZE(int8 SIZEOF_INT8) +CHECK_TYPE_SIZE(uint8 SIZEOF_UINT8) +CHECK_TYPE_SIZE(int16 SIZEOF_INT16) +CHECK_TYPE_SIZE(uint16 SIZEOF_UINT16) +CHECK_TYPE_SIZE(int32 SIZEOF_INT32) +CHECK_TYPE_SIZE(uint32 SIZEOF_UINT32) +CHECK_TYPE_SIZE(u_int32_t SIZEOF_U_INT32_T) +CHECK_TYPE_SIZE(int64 SIZEOF_INT64) +CHECK_TYPE_SIZE(uint64 SIZEOF_UINT64) +SET (CMAKE_EXTRA_INCLUDE_FILES sys/types.h) +CHECK_TYPE_SIZE(bool SIZEOF_BOOL) +SET(CMAKE_EXTRA_INCLUDE_FILES) +IF(HAVE_SYS_SOCKET_H) + SET(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) +ENDIF(HAVE_SYS_SOCKET_H) +CHECK_TYPE_SIZE(socklen_t SIZEOF_SOCKLEN_T) +SET(CMAKE_EXTRA_INCLUDE_FILES) + +IF(HAVE_IEEEFP_H) + SET(CMAKE_EXTRA_INCLUDE_FILES ieeefp.h) + CHECK_TYPE_SIZE(fp_except SIZEOF_FP_EXCEPT) + IF(SIZEOF_FP_EXCEPT) + SET(HAVE_FP_EXCEPT TRUE) + ENDIF() +ENDIF() + + +# +# Code tests +# + +CHECK_C_SOURCE_COMPILES(" +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif +int main() +{ + getaddrinfo( 0, 0, 0, 0); + return 0; +}" +HAVE_GETADDRINFO) + +CHECK_C_SOURCE_COMPILES(" +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif +int main() +{ + select(0,0,0,0,0); + return 0; +}" +HAVE_SELECT) + +# +# Check if timespec has ts_sec and ts_nsec fields +# + +CHECK_C_SOURCE_COMPILES_UNIX(" +#include + +int main(int ac, char **av) +{ + struct timespec abstime; + abstime.ts_sec = time(NULL)+1; + abstime.ts_nsec = 0; +} +" HAVE_TIMESPEC_TS_SEC) + + +# +# Check return type of qsort() +# +CHECK_C_SOURCE_COMPILES(" +#include +#ifdef __cplusplus +extern \"C\" +#endif +void qsort(void *base, size_t nel, size_t width, + int (*compar) (const void *, const void *)); +int main(int ac, char **av) {} +" QSORT_TYPE_IS_VOID) +IF(QSORT_TYPE_IS_VOID) + SET(RETQSORTTYPE "void") +ELSE(QSORT_TYPE_IS_VOID) + SET(RETQSORTTYPE "int") +ENDIF(QSORT_TYPE_IS_VOID) + +IF(WIN32) +SET(SOCKET_SIZE_TYPE int) +ELSE() +CHECK_CXX_SOURCE_COMPILES(" +#include +int main(int argc, char **argv) +{ + getsockname(0,0,(socklen_t *) 0); + return 0; +}" +HAVE_SOCKET_SIZE_T_AS_socklen_t) + +IF(HAVE_SOCKET_SIZE_T_AS_socklen_t) + SET(SOCKET_SIZE_TYPE socklen_t) +ELSE() + CHECK_CXX_SOURCE_COMPILES(" + #include + int main(int argc, char **argv) + { + getsockname(0,0,(int *) 0); + return 0; + }" + HAVE_SOCKET_SIZE_T_AS_int) + IF(HAVE_SOCKET_SIZE_T_AS_int) + SET(SOCKET_SIZE_TYPE int) + ELSE() + CHECK_CXX_SOURCE_COMPILES(" + #include + int main(int argc, char **argv) + { + getsockname(0,0,(size_t *) 0); + return 0; + }" + HAVE_SOCKET_SIZE_T_AS_size_t) + IF(HAVE_SOCKET_SIZE_T_AS_size_t) + SET(SOCKET_SIZE_TYPE size_t) + ELSE() + SET(SOCKET_SIZE_TYPE int) + ENDIF() + ENDIF() +ENDIF() +ENDIF() + +CHECK_CXX_SOURCE_COMPILES_UNIX(" +#include +int main() +{ + pthread_yield(); + return 0; +} +" HAVE_PTHREAD_YIELD_ZERO_ARG) + +IF(NOT STACK_DIRECTION) + IF(CMAKE_CROSSCOMPILING) + MESSAGE(FATAL_ERROR + "STACK_DIRECTION is not defined. Please specify -DSTACK_DIRECTION=1 " + "or -DSTACK_DIRECTION=-1 when calling cmake.") + ELSE() + TRY_RUN(STACKDIR_RUN_RESULT STACKDIR_COMPILE_RESULT + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/cmake/stack_direction.c + ) + # Test program returns 0 (down) or 1 (up). + # Convert to -1 or 1 + IF(STACKDIR_RUN_RESULT EQUAL 0) + SET(STACK_DIRECTION -1 CACHE INTERNAL "Stack grows direction") + ELSE() + SET(STACK_DIRECTION 1 CACHE INTERNAL "Stack grows direction") + ENDIF() + MESSAGE(STATUS "Checking stack grows direction : ${STACK_DIRECTION}") + ENDIF() +ENDIF() + +# +# Check return type of signal handlers +# +CHECK_C_SOURCE_COMPILES(" +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern \"C\" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif +int main(int ac, char **av) {} +" SIGNAL_RETURN_TYPE_IS_VOID) +IF(SIGNAL_RETURN_TYPE_IS_VOID) + SET(RETSIGTYPE void) +ELSE(SIGNAL_RETURN_TYPE_IS_VOID) + SET(RETSIGTYPE int) +ENDIF(SIGNAL_RETURN_TYPE_IS_VOID) + +# +# isnan() is trickier than the usual function test -- it may be a macro +# +CHECK_CXX_SOURCE_COMPILES("#include + int main(int ac, char **av) + { + isnan(0.0); + return 0; + }" + HAVE_ISNAN) + +# +# isinf() is trickier than the usual function test -- it may be a macro +# +CHECK_CXX_SOURCE_COMPILES("#include + int main(int ac, char **av) + { + isinf(0.0); + return 0; + }" + HAVE_ISINF) +# +# rint() is trickier than the usual function test -- it may be a macro +# +CHECK_CXX_SOURCE_COMPILES("#include + int main(int ac, char **av) + { + rint(0.0); + return 0; + }" + HAVE_RINT) + +CHECK_CXX_SOURCE_COMPILES(" + #include + #include + int main() + { + return 0; + }" + TIME_WITH_SYS_TIME) + +IF(UNIX) +CHECK_C_SOURCE_COMPILES(" +#include +#include +int main() +{ + fcntl(0, F_SETFL, O_NONBLOCK); + return 0; +} +" +HAVE_FCNTL_NONBLOCK) +ENDIF() + +IF(NOT HAVE_FCNTL_NONBLOCK) + SET(NO_FCNTL_NONBLOCK 1) +ENDIF() + +# +# Test for how the C compiler does inline, if at all +# +CHECK_C_SOURCE_COMPILES(" +static inline int foo(){return 0;} +int main(int argc, char *argv[]){return 0;}" + C_HAS_inline) +IF(NOT C_HAS_inline) + CHECK_C_SOURCE_COMPILES(" + static __inline int foo(){return 0;} + int main(int argc, char *argv[]){return 0;}" + C_HAS___inline) + SET(C_INLINE __inline) +ENDIF() + +IF(NOT CMAKE_CROSSCOMPILING AND NOT MSVC) + STRING(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} processor) + IF(processor MATCHES "86" OR processor MATCHES "amd64" OR processor MATCHES "x64") + #Check for x86 PAUSE instruction + # We have to actually try running the test program, because of a bug + # in Solaris on x86_64, where it wrongly reports that PAUSE is not + # supported when trying to run an application. See + # http://bugs.opensolaris.org/bugdatabase/printableBug.do?bug_id=6478684 + CHECK_C_SOURCE_RUNS(" + int main() + { + __asm__ __volatile__ (\"pause\"); + return 0; + }" HAVE_PAUSE_INSTRUCTION) + ENDIF() + IF (NOT HAVE_PAUSE_INSTRUCTION) + CHECK_C_SOURCE_COMPILES(" + int main() + { + __asm__ __volatile__ (\"rep; nop\"); + return 0; + } + " HAVE_FAKE_PAUSE_INSTRUCTION) + ENDIF() +ENDIF() + + + + +CHECK_SYMBOL_EXISTS_UNIX(tcgetattr "termios.h" HAVE_TCGETATTR 1) +CHECK_INCLUDE_FILES_UNIX(sys/ioctl.h HAVE_SYS_IOCTL 1) + + +# +# Check type of signal routines (posix, 4.2bsd, 4.1bsd or v7) +# +CHECK_C_SOURCE_COMPILES_UNIX(" + #include + int main(int ac, char **av) + { + sigset_t ss; + struct sigaction sa; + sigemptyset(&ss); sigsuspend(&ss); + sigaction(SIGINT, &sa, (struct sigaction *) 0); + sigprocmask(SIG_BLOCK, &ss, (sigset_t *) 0); + }" + HAVE_POSIX_SIGNALS) + +IF(NOT HAVE_POSIX_SIGNALS) + CHECK_C_SOURCE_COMPILES_UNIX(" + #include + int main(int ac, char **av) + { + int mask = sigmask(SIGINT); + sigsetmask(mask); sigblock(mask); sigpause(mask); + }" + HAVE_BSD_SIGNALS) + IF (NOT HAVE_BSD_SIGNALS) + CHECK_C_SOURCE_COMPILES_UNIX(" + #include + void foo() { } + int main(int ac, char **av) + { + int mask = sigmask(SIGINT); + sigset(SIGINT, foo); sigrelse(SIGINT); + sighold(SIGINT); sigpause(SIGINT); + }" + HAVE_SVR3_SIGNALS) + IF (NOT HAVE_SVR3_SIGNALS) + SET(HAVE_V7_SIGNALS 1) + ENDIF(NOT HAVE_SVR3_SIGNALS) + ENDIF(NOT HAVE_BSD_SIGNALS) +ENDIF(NOT HAVE_POSIX_SIGNALS) + +# Assume regular sprintf +SET(SPRINTFS_RETURNS_INT 1) + +IF(CMAKE_COMPILER_IS_GNUXX) +CHECK_CXX_SOURCE_COMPILES(" + #include + int main(int argc, char **argv) + { + char *foo= 0; int bar= 0; + foo= abi::__cxa_demangle(foo, foo, 0, &bar); + return 0; + }" + HAVE_ABI_CXA_DEMANGLE) +IF(HAVE_ABI_CXA_DEMANGLE) + SET(HAVE_CXXABI_H 1) +ENDIF() +ENDIF() + +CHECK_C_SOURCE_COMPILES_UNIX(" + int main(int argc, char **argv) + { + extern char *__bss_start; + return __bss_start ? 1 : 0; + }" +HAVE_BSS_START) + +CHECK_C_SOURCE_COMPILES_UNIX(" + int main() + { + extern void __attribute__((weak)) foo(void); + return 0; + }" + HAVE_WEAK_SYMBOL +) + + +CHECK_CXX_SOURCE_COMPILES(" + #include + int main() + { + char *c = new char; + return 0; + }" + HAVE_CXX_NEW +) + +CHECK_CXX_SOURCE_COMPILES_UNIX(" + #undef inline + #if !defined(SCO) && !defined(__osf__) && !defined(_REENTRANT) + #define _REENTRANT + #endif + #include + #include + #include + #include + #include + #include + int main() + { + + struct hostent *foo = + gethostbyaddr_r((const char *) 0, + 0, 0, (struct hostent *) 0, (char *) NULL, 0, (int *)0); + return 0; + } + " + HAVE_SOLARIS_STYLE_GETHOST) + +CHECK_CXX_SOURCE_COMPILES_UNIX(" + #undef inline + #if !defined(SCO) && !defined(__osf__) && !defined(_REENTRANT) + #define _REENTRANT + #endif + #include + #include + #include + #include + #include + #include + int main() + { + int ret = gethostbyname_r((const char *) 0, + (struct hostent*) 0, (char*) 0, 0, (struct hostent **) 0, (int *) 0); + return 0; + }" + HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE) + +CHECK_CXX_SOURCE_COMPILES_UNIX(" + #undef inline + #if !defined(SCO) && !defined(__osf__) && !defined(_REENTRANT) + #define _REENTRANT + #endif + #include + #include + #include + #include + #include + #include + int main() + { + int ret = gethostbyname_r((const char *) 0, (struct hostent*) 0, (struct hostent_data*) 0); + return 0; + }" + HAVE_GETHOSTBYNAME_R_RETURN_INT) + +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + CHECK_SYMBOL_EXISTS(SHM_HUGETLB sys/shm.h HAVE_DECL_SHM_HUGETLB) + IF(HAVE_DECL_SHM_HUGETLB) + SET(HAVE_LARGE_PAGES 1) + SET(HUGETLB_USE_PROC_MEMINFO 1) + SET(HAVE_LARGE_PAGE_OPTION 1) + ENDIF() +ENDIF() + +IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + CHECK_SYMBOL_EXISTS(MHA_MAPSIZE_VA sys/mman.h HAVE_DECL_MHA_MAPSIZE_VA) + IF(HAVE_DECL_MHA_MAPSIZE_VA) + SET(HAVE_SOLARIS_LARGE_PAGES 1) + SET(HAVE_LARGE_PAGE_OPTION 1) + ENDIF() +ENDIF() + +IF(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") + # xlC oddity - it complains about same inline function defined multiple times + # in different compilation units + INCLUDE(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-qstaticinline" HAVE_QSTATICINLINE) + IF(HAVE_QSTATICINLINE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qstaticinline") + ENDIF() + + # The following is required to export all symbols (also with leading underscore + # from libraries and mysqld + STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS + ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}) + STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS + ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCXX) +IF(WITH_ATOMIC_OPS STREQUAL "up") + SET(MY_ATOMIC_MODE_DUMMY 1 CACHE BOOL "Assume single-CPU mode, no concurrency") +ELSEIF(WITH_ATOMIC_OPS STREQUAL "rwlocks") + SET(MY_ATOMIC_MODE_RWLOCK 1 CACHE BOOL "Use pthread rwlocks for atomic ops") +ELSEIF(WITH_ATOMIC_OPS STREQUAL "smp") +ELSEIF(NOT WITH_ATOMIC_OPS) + CHECK_CXX_SOURCE_COMPILES(" + int main() + { + int foo= -10; + int bar= 10; + if (!__sync_fetch_and_add(&foo, bar) || foo) + return -1; + bar= __sync_lock_test_and_set(&foo, bar); + if (bar || foo != 10) + return -1; + bar= __sync_val_compare_and_swap(&bar, foo, 15); + if (bar) + return -1; + return 0; + }" + HAVE_GCC_ATOMIC_BUILTINS) +ELSE() + MESSAGE(FATAL_ERROR "${WITH_ATOMIC_OPS} is not a valid value for WITH_ATOMIC_OPS!") +ENDIF() +ENDIF() + +SET(WITH_ATOMIC_LOCKS "${WITH_ATOMIC_LOCKS}" CACHE STRING +"Implement atomic operations using pthread rwlocks or atomic CPU +instructions for multi-processor or uniprocessor +configuration. By default gcc built-in sync functions are used, +if available and 'smp' configuration otherwise.") +MARK_AS_ADVANCED(WITH_ATOMIC_LOCKS MY_ATOMIC_MODE_RWLOCK MY_ATOMIC_MODE_DUMMY) + +IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + CHECK_C_SOURCE_RUNS( + " + #include + int main() + { + int foo = -10; int bar = 10; + if (atomic_add_int_nv((uint_t *)&foo, bar) || foo) + return -1; + bar = atomic_swap_uint((uint_t *)&foo, (uint_t)bar); + if (bar || foo != 10) + return -1; + bar = atomic_cas_uint((uint_t *)&bar, (uint_t)foo, 15); + if (bar) + return -1; + return 0; + } +" HAVE_SOLARIS_ATOMIC) +ENDIF() + +#-------------------------------------------------------------------- +# Check for IPv6 support +#-------------------------------------------------------------------- +CHECK_INCLUDE_FILE(netinet/in6.h HAVE_NETINET_IN6_H) + +IF(UNIX) + SET(CMAKE_EXTRA_INCLUDE_FILES sys/types.h netinet/in.h sys/socket.h) + IF(HAVE_NETINET_IN6_H) + SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} netinet/in6.h) + ENDIF() +ELSEIF(WIN32) + SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} winsock2.h ws2ipdef.h) +ENDIF() + +CHECK_TYPE_SIZE("struct sockaddr_in6" SIZEOF_SOCKADDR_IN6) +CHECK_TYPE_SIZE("struct in6_addr" SIZEOF_IN6_ADDR) +IF(SIZEOF_SOCKADDR_IN6) + SET(HAVE_STRUCT_SOCKADDR_IN6 1) +ENDIF() +IF(SIZEOF_IN6_ADDR) + SET(HAVE_STRUCT_IN6_ADDR 1) +ENDIF() + +IF(HAVE_STRUCT_SOCKADDR_IN6 OR HAVE_STRUCT_IN6_ADDR) + SET(HAVE_IPV6 TRUE CACHE INTERNAL "") +ENDIF() + + +# Check for sockaddr_storage.ss_family +# It is called differently under OS400 and older AIX + +CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" + ss_family "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SOCKADDR_STORAGE_SS_FAMILY) +IF(NOT HAVE_SOCKADDR_STORAGE_SS_FAMILY) + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" + __ss_family "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_SOCKADDR_STORAGE___SS_FAMILY) + IF(HAVE_SOCKADDR_STORAGE___SS_FAMILY) + SET(ss_family __ss_family) + ENDIF() +ENDIF() +SET(CMAKE_EXTRA_INCLUDE_FILES) + +CHECK_STRUCT_HAS_MEMBER("struct dirent" d_ino "dirent.h" STRUCT_DIRENT_HAS_D_INO) +CHECK_STRUCT_HAS_MEMBER("struct dirent" d_namlen "dirent.h" STRUCT_DIRENT_HAS_D_NAMLEN) +SET(SPRINTF_RETURNS_INT 1) + +IF(WIN32) + SET(SIGNAL_WITH_VIO_CLOSE 1) + CHECK_SYMBOL_REPLACEMENT(S_IROTH _S_IREAD sys/stat.h) + CHECK_SYMBOL_REPLACEMENT(S_IFIFO _S_IFIFO sys/stat.h) + CHECK_SYMBOL_REPLACEMENT(SIGQUIT SIGTERM signal.h) + CHECK_SYMBOL_REPLACEMENT(SIGPIPE SIGINT signal.h) + CHECK_SYMBOL_REPLACEMENT(isnan _isnan float.h) + CHECK_SYMBOL_REPLACEMENT(finite _finite float.h) + CHECK_FUNCTION_REPLACEMENT(popen _popen) + CHECK_FUNCTION_REPLACEMENT(pclose _pclose) + CHECK_FUNCTION_REPLACEMENT(access _access) + CHECK_FUNCTION_REPLACEMENT(strcasecmp _stricmp) + CHECK_FUNCTION_REPLACEMENT(strncasecmp _strnicmp) + CHECK_FUNCTION_REPLACEMENT(snprintf _snprintf) + CHECK_FUNCTION_REPLACEMENT(strtok_r strtok_s) + CHECK_FUNCTION_REPLACEMENT(strtoll _strtoi64) + CHECK_FUNCTION_REPLACEMENT(strtoull _strtoui64) + CHECK_TYPE_SIZE(ssize_t SIZE_OF_SSIZE_T) + IF(NOT SIZE_OF_SSIZE_T) + SET(ssize_t SSIZE_T) + ENDIF() + + + # IPv6 definition (appeared in Vista SDK first) + CHECK_C_SOURCE_COMPILES(" + #include + int main() + { + return IPPROTO_IPV6; + }" + HAVE_IPPROTO_IPV6) + + CHECK_C_SOURCE_COMPILES(" + #include + #include + int main() + { + return IPV6_V6ONLY; + }" + HAVE_IPV6_V6ONLY) + + IF(NOT HAVE_IPPROTO_IPV6) + SET(HAVE_IPPROTO_IPV6 41) + ENDIF() + IF(NOT HAVE_IPV6_V6ONLY) + SET(IPV6_V6ONLY 27) + ENDIF() + +ENDIF(WIN32) + + diff --git a/configure.in b/configure.in index 9fe5c741a03..24c44358c3c 100644 --- a/configure.in +++ b/configure.in @@ -2929,7 +2929,9 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl libmysqld/Makefile libmysqld/examples/Makefile dnl mysql-test/Makefile mysql-test/lib/My/SafeProcess/Makefile dnl netware/Makefile sql-bench/Makefile dnl - include/mysql_version.h plugin/Makefile win/Makefile) + include/mysql_version.h plugin/Makefile win/Makefile + cmake/Makefile + ) AC_CONFIG_COMMANDS([default], , test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h) diff --git a/dbug/CMakeLists.txt b/dbug/CMakeLists.txt index fabb592dccc..00de8ba11ff 100755 --- a/dbug/CMakeLists.txt +++ b/dbug/CMakeLists.txt @@ -13,11 +13,11 @@ # 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(${CMAKE_SOURCE_DIR}/dbug) - -SET(DBUG_SOURCES dbug.c factorial.c sanity.c) - -IF(NOT SOURCE_SUBLIBS) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) - ADD_LIBRARY(dbug ${DBUG_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/dbug + ${CMAKE_SOURCE_DIR}/include +) +SET(DBUG_SOURCES dbug.c sanity.c) +USE_ABSOLUTE_FILENAMES(DBUG_SOURCES) +ADD_LIBRARY(dbug ${DBUG_SOURCES}) +TARGET_LINK_LIBRARIES(dbug mysys) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index b67fdfa601f..c1487149acd 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -12,42 +12,78 @@ # 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("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/include +${ZLIB_INCLUDE_DIR} +# Following is for perror, in case NDB is compiled in. +${CMAKE_SOURCE_DIR}/storage/ndb/include +${CMAKE_SOURCE_DIR}/storage/ndb/include/util +${CMAKE_SOURCE_DIR}/storage/ndb/include/ndbapi +${CMAKE_SOURCE_DIR}/storage/ndb/include/portlib +${CMAKE_SOURCE_DIR}/storage/ndb/include/mgmapi) -ADD_EXECUTABLE(comp_err comp_err.c) -TARGET_LINK_LIBRARIES(comp_err dbug mysys strings zlib) -GET_TARGET_PROPERTY(COMP_ERR_EXE comp_err LOCATION) +IF(NOT CMAKE_CROSSCOMPILING) + ADD_EXECUTABLE(comp_err comp_err.c) + TARGET_LINK_LIBRARIES(comp_err ${ZLIB_LIBRARY} mysys) +ENDIF() -ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_SOURCE_DIR}/include/mysqld_error.h - COMMAND ${COMP_ERR_EXE} +ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_BINARY_DIR}/include/mysqld_error.h + ${PROJECT_BINARY_DIR}/sql/share/english/errmsg.sys + COMMAND comp_err --charset=${PROJECT_SOURCE_DIR}/sql/share/charsets - --out-dir=${PROJECT_SOURCE_DIR}/sql/share/ - --header_file=${PROJECT_SOURCE_DIR}/include/mysqld_error.h - --name_file=${PROJECT_SOURCE_DIR}/include/mysqld_ername.h - --state_file=${PROJECT_SOURCE_DIR}/include/sql_state.h + --out-dir=${PROJECT_BINARY_DIR}/sql/share/ + --header_file=${PROJECT_BINARY_DIR}/include/mysqld_error.h + --name_file=${PROJECT_BINARY_DIR}/include/mysqld_ername.h + --state_file=${PROJECT_BINARY_DIR}/include/sql_state.h --in_file=${PROJECT_SOURCE_DIR}/sql/share/errmsg-utf8.txt - DEPENDS comp_err ${PROJECT_SOURCE_DIR}/sql/share/errmsg-utf8.txt) + DEPENDS ${PROJECT_SOURCE_DIR}/sql/share/errmsg-utf8.txt + ${CMAKE_CURRENT_SOURCE_DIR}/comp_err.c) ADD_CUSTOM_TARGET(GenError ALL - DEPENDS ${PROJECT_SOURCE_DIR}/include/mysqld_error.h) + DEPENDS + ${PROJECT_BINARY_DIR}/include/mysqld_error.h + ${PROJECT_BINARY_DIR}/sql/share/english/errmsg.sys + ${PROJECT_SOURCE_DIR}/sql/share/errmsg-utf8.txt) ADD_EXECUTABLE(my_print_defaults my_print_defaults.c) -TARGET_LINK_LIBRARIES(my_print_defaults strings mysys dbug taocrypt) +TARGET_LINK_LIBRARIES(my_print_defaults mysys) ADD_EXECUTABLE(perror perror.c) -TARGET_LINK_LIBRARIES(perror strings mysys dbug) +ADD_DEPENDENCIES(perror GenError) +TARGET_LINK_LIBRARIES(perror mysys) ADD_EXECUTABLE(resolveip resolveip.c) -TARGET_LINK_LIBRARIES(resolveip strings mysys dbug) +TARGET_LINK_LIBRARIES(resolveip mysys) +# On Solaris, inet_aton() function used by resolveip could be in resolv library +IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + INCLUDE(CheckFunctionExists) + INCLUDE(CheckLibraryExists) + CHECK_FUNCTION_EXISTS(inet_aton HAVE_INET_ATON) + IF(NOT HAVE_INET_ATON) + CHECK_LIBRARY_EXISTS(resolv inet_aton "" HAVE_INET_ATON_IN_RESOLV) + IF(HAVE_INET_ATON_IN_RESOLV) + TARGET_LINK_LIBRARIES(resolveip resolv) + ENDIF() + ENDIF() +ENDIF() + ADD_EXECUTABLE(replace replace.c) -TARGET_LINK_LIBRARIES(replace strings mysys dbug) +TARGET_LINK_LIBRARIES(replace mysys) +IF(UNIX) + ADD_EXECUTABLE(innochecksum innochecksum.c) + + ADD_EXECUTABLE(resolve_stack_dump resolve_stack_dump.c) + TARGET_LINK_LIBRARIES(resolve_stack_dump mysys) + + ADD_EXECUTABLE(mysql_waitpid mysql_waitpid.c) + TARGET_LINK_LIBRARIES(mysql_waitpid mysys) + + INSTALL(TARGETS innochecksum mysql_waitpid resolve_stack_dump DESTINATION bin) +ENDIF() -IF(EMBED_MANIFESTS) - MYSQL_EMBED_MANIFEST("myTest" "asInvoker") -ENDIF(EMBED_MANIFESTS) +INSTALL(TARGETS perror resolveip my_print_defaults replace DESTINATION bin) diff --git a/extra/yassl/CMakeLists.txt b/extra/yassl/CMakeLists.txt index ffb72b831af..a815a46a323 100755 --- a/extra/yassl/CMakeLists.txt +++ b/extra/yassl/CMakeLists.txt @@ -13,16 +13,22 @@ # 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(${CMAKE_SOURCE_DIR}/extra/yassl/include - ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/include - ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/mySTL) - -ADD_DEFINITIONS("-D_LIB -DYASSL_PREFIX") +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/include + ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/mySTL) +ADD_DEFINITIONS(${SSL_DEFINES}) +IF(CMAKE_COMPILER_IS_GNUXX) + #Remove -fno-implicit-templates + #(yassl sources cannot be compiled with it) + STRING(REPLACE "-fno-implicit-templates" "" CMAKE_CXX_FLAGS +${CMAKE_CXX_FLAGS}) +ENDIF() SET(YASSL_SOURCES src/buffer.cpp src/cert_wrapper.cpp src/crypto_wrapper.cpp src/handshake.cpp src/lock.cpp src/log.cpp src/socket_wrapper.cpp src/ssl.cpp src/timer.cpp src/yassl_error.cpp src/yassl_imp.cpp src/yassl_int.cpp) -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(yassl ${YASSL_SOURCES}) - ADD_DEPENDENCIES(yassl GenError) -ENDIF(NOT SOURCE_SUBLIBS) +USE_ABSOLUTE_FILENAMES(YASSL_SOURCES) +ADD_LIBRARY(yassl ${YASSL_SOURCES}) + diff --git a/extra/yassl/taocrypt/CMakeLists.txt b/extra/yassl/taocrypt/CMakeLists.txt index e91fa021de5..58de3989b73 100755 --- a/extra/yassl/taocrypt/CMakeLists.txt +++ b/extra/yassl/taocrypt/CMakeLists.txt @@ -16,6 +16,11 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/mySTL ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/include) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) +ADD_DEFINITIONS(${SSL_DEFINES}) +IF(PREVENT_CPP_RUNTIME) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PREVENT_CPP_RUNTIME}") +ENDIF() SET(TAOCRYPT_SOURCES src/aes.cpp src/aestables.cpp src/algebra.cpp src/arc4.cpp src/asn.cpp src/coding.cpp src/des.cpp src/dh.cpp src/dsa.cpp src/file.cpp src/hash.cpp src/integer.cpp src/md2.cpp src/md4.cpp src/md5.cpp src/misc.cpp src/random.cpp src/ripemd.cpp src/rsa.cpp src/sha.cpp @@ -24,6 +29,6 @@ SET(TAOCRYPT_SOURCES src/aes.cpp src/aestables.cpp src/algebra.cpp src/arc4.cpp include/error.hpp include/file.hpp include/hash.hpp include/hmac.hpp include/integer.hpp include/md2.hpp include/md5.hpp include/misc.hpp include/modarith.hpp include/modes.hpp include/random.hpp include/ripemd.hpp include/rsa.hpp include/sha.hpp) -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(taocrypt ${TAOCRYPT_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) +USE_ABSOLUTE_FILENAMES(TAOCRYPT_SOURCES) +ADD_LIBRARY(taocrypt ${TAOCRYPT_SOURCES}) + diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 00000000000..c16f8ea1ff8 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(HEADERS_GEN_CONFIGURE +${CMAKE_CURRENT_BINARY_DIR}/mysql_version.h +${CMAKE_CURRENT_BINARY_DIR}/my_config.h +${CMAKE_CURRENT_BINARY_DIR}/mysqld_ername.h +${CMAKE_CURRENT_BINARY_DIR}/mysqld_error.h +${CMAKE_CURRENT_BINARY_DIR}/sql_state.h +) +SET(HEADERS_ABI + mysql.h + mysql_com.h + mysql_time.h + my_list.h + my_alloc.h + typelib.h +) + +SET(HEADERS + ${HEADERS_ABI} + my_dbug.h + m_string.h + my_sys.h + my_xml.h + mysql_embed.h + my_pthread.h + my_no_pthread.h + decimal.h + errmsg.h + my_global.h + my_net.h + my_getopt.h + sslopt-longopts.h + my_dir.h + sslopt-vars.h + sslopt-case.h + sql_common.h + keycache.h + m_ctype.h + my_attribute.h + ${HEADERS_GEN_CONFIGURE} +) + +INSTALL(FILES ${HEADERS} DESTINATION include) +INSTALL(DIRECTORY mysql/ DESTINATION include + FILES_MATCHING PATTERN "*.h") + + diff --git a/include/Makefile.am b/include/Makefile.am index 0a7a35bd6fa..f9a069bc4b5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -43,7 +43,8 @@ noinst_HEADERS = config-win.h config-netware.h my_bit.h \ atomic/solaris.h \ atomic/gcc_builtins.h my_libwrap.h my_stacktrace.h -EXTRA_DIST = mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base +EXTRA_DIST = mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base \ + CMakeLists.txt # Remove built files and the symlinked directories CLEANFILES = $(BUILT_SOURCES) readline openssl probes_mysql.d probes_mysql_nodtrace.h diff --git a/include/keycache.h b/include/keycache.h index a6005bae878..1ffb1dbb71f 100644 --- a/include/keycache.h +++ b/include/keycache.h @@ -67,7 +67,7 @@ typedef struct st_key_cache HASH_LINK *free_hash_list; /* list of free hash links */ BLOCK_LINK *free_block_list; /* list of free blocks */ BLOCK_LINK *block_root; /* memory for block links */ - uchar HUGE_PTR *block_mem; /* memory for block buffers */ + uchar *block_mem; /* memory for block buffers */ BLOCK_LINK *used_last; /* ptr to the last block of the LRU chain */ BLOCK_LINK *used_ins; /* ptr to the insertion block in LRU chain */ pthread_mutex_t cache_lock; /* to lock access to the cache structure */ diff --git a/include/my_global.h b/include/my_global.h index f6d1592fc6f..c95861204d7 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -68,8 +68,8 @@ #define C_MODE_END #endif -#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) -#include +#if defined(_WIN32) +#include #elif defined(__NETWARE__) #include #include @@ -107,6 +107,49 @@ #define NETWARE_SET_SCREEN_MODE(A) #endif +#if defined (_WIN32) +/* + off_t is 32 bit long. We do not use C runtime functions + with off_t but native Win32 file IO APIs, that work with + 64 bit offsets. +*/ +#undef SIZEOF_OFF_T +#define SIZEOF_OFF_T 8 + +/* + Prevent inclusion of Windows GDI headers - they define symbol + ERROR that conflicts with mysql headers. +*/ +#ifndef NOGDI +#define NOGDI +#endif + +/* Include common headers.*/ +#include +#include /* SOCKET */ +#include /* access(), chmod() */ +#include /* getpid() */ + +#define sleep(a) Sleep((a)*1000) + +/* Define missing access() modes. */ +#define F_OK 0 +#define W_OK 2 + +/* Define missing file locking constants. */ +#define F_RDLCK 1 +#define F_WRLCK 2 +#define F_UNLCK 3 +#define F_TO_EOF 0x3FFFFFFF + +/* Shared memory and named pipe connections are supported. */ +#define HAVE_SMEM 1 +#define HAVE_NAMED_PIPE 1 +#define shared_memory_buffer_length 16000 +#define default_shared_memory_base_name "MYSQL" +#endif /* _WIN32*/ + + /* Workaround for _LARGE_FILES and _LARGE_FILE_API incompatibility on AIX */ #if defined(_AIX) && defined(_LARGE_FILE_API) #undef _LARGE_FILE_API @@ -486,8 +529,11 @@ C_MODE_END /* Go around some bugs in different OS and compilers */ #if defined (HPUX11) && defined(_LARGEFILE_SOURCE) +#ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif +#endif + #if defined(_HPUX_SOURCE) && defined(HAVE_SYS_STREAM_H) #include /* HPUX 10.20 defines ulong here. UGLY !!! */ #define HAVE_ULONG @@ -656,7 +702,9 @@ C_MODE_END /* Some types that is different between systems */ typedef int File; /* File descriptor */ -#ifndef Socket_defined +#ifdef _WIN32 +typedef SOCKET my_socket; +#else typedef int my_socket; /* File descriptor for sockets */ #define INVALID_SOCKET -1 #endif @@ -744,7 +792,13 @@ typedef SOCKET_SIZE_TYPE size_socket; #define FN_CURLIB '.' /* ./ is used as abbrev for current dir */ #define FN_PARENTDIR ".." /* Parent directory; Must be a string */ -#ifndef FN_LIBCHAR +#ifdef _WIN32 +#define FN_LIBCHAR '\\' +#define FN_ROOTDIR "\\" +#define FN_DEVCHAR ':' +#define FN_NETWORK_DRIVES /* Uses \\ to indicate network drives */ +#define FN_NO_CASE_SENCE /* Files are not case-sensitive */ +#else #define FN_LIBCHAR '/' #define FN_ROOTDIR "/" #endif @@ -828,6 +882,31 @@ typedef SOCKET_SIZE_TYPE size_socket; #undef remove /* Crashes MySQL on SCO 5.0.0 */ #ifndef __WIN__ #define closesocket(A) close(A) +#endif + +#if (_MSC_VER) +#if !defined(_WIN64) +inline double my_ulonglong2double(unsigned long long value) +{ + long long nr=(long long) value; + if (nr >= 0) + return (double) nr; + return (18446744073709551616.0 + (double) nr); +} +#define ulonglong2double my_ulonglong2double +#define my_off_t2double my_ulonglong2double +#endif /* _WIN64 */ +inline unsigned long long my_double2ulonglong(double d) +{ + double t= d - (double) 0x8000000000000000ULL; + + if (t >= 0) + return ((unsigned long long) t) + 0x8000000000000000ULL; + return (unsigned long long) d; +} +#define double2ulonglong my_double2ulonglong +#endif + #ifndef ulonglong2double #define ulonglong2double(A) ((double) (ulonglong) (A)) #define my_off_t2double(A) ((double) (my_off_t) (A)) @@ -835,7 +914,6 @@ typedef SOCKET_SIZE_TYPE size_socket; #ifndef double2ulonglong #define double2ulonglong(A) ((ulonglong) (double) (A)) #endif -#endif #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) @@ -1092,15 +1170,18 @@ typedef long long intptr; #define SYSTEM_SIZEOF_OFF_T SIZEOF_OFF_T #endif /* USE_RAID */ +#if defined(_WIN32) +typedef unsigned long long my_off_t; +typedef unsigned long long os_off_t; +#else +typedef off_t os_off_t; #if SIZEOF_OFF_T > 4 typedef ulonglong my_off_t; #else typedef unsigned long my_off_t; #endif +#endif /*_WIN32*/ #define MY_FILEPOS_ERROR (~(my_off_t) 0) -#if !defined(__WIN__) -typedef off_t os_off_t; -#endif #if defined(__WIN__) #define socket_errno WSAGetLastError() @@ -1127,9 +1208,6 @@ typedef uint8 int7; /* Most effective integer 0 <= x <= 127 */ typedef short int15; /* Most effective integer 0 <= x <= 32767 */ typedef int myf; /* Type of MyFlags in my_funcs */ typedef char my_bool; /* Small bool */ -#if !defined(bool) && (!defined(HAVE_BOOL) || !defined(__cplusplus)) -typedef char bool; /* Ordinary boolean values 0 1 */ -#endif /* Macros for converting *constants* to the right type */ #define INT8(v) (int8) (v) #define INT16(v) (int16) (v) @@ -1522,12 +1600,15 @@ do { doubleget_union _tmp; \ #define NO_EMBEDDED_ACCESS_CHECKS #endif -#ifdef HAVE_DLOPEN -#if defined(__WIN__) -#define dlsym(lib, name) GetProcAddress((HMODULE)lib, name) +#if defined(_WIN32) +#define dlsym(lib, name) (void*)GetProcAddress((HMODULE)lib, name) #define dlopen(libname, unused) LoadLibraryEx(libname, NULL, 0) #define dlclose(lib) FreeLibrary((HMODULE)lib) -#elif defined(HAVE_DLFCN_H) +#define HAVE_DLOPEN +#endif + +#ifdef HAVE_DLOPEN +#if defined(HAVE_DLFCN_H) #include #endif #endif diff --git a/include/my_pthread.h b/include/my_pthread.h index b6d9feae067..9f46d1f76c1 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -117,9 +117,11 @@ struct tm *gmtime_r(const time_t *timep,struct tm *tmp); void pthread_exit(void *a); int pthread_join(pthread_t thread, void **value_ptr); +int pthread_cancel(pthread_t thread); - +#ifndef ETIMEDOUT #define ETIMEDOUT 145 /* Win32 doesn't have this */ +#endif #define HAVE_LOCALTIME_R 1 #define _REENTRANT 1 #define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1 @@ -154,6 +156,7 @@ int pthread_join(pthread_t thread, void **value_ptr); #define pthread_condattr_destroy(A) #define my_pthread_getprio(thread_id) pthread_dummy(0) +#define my_sigset(A,B) signal(A,B) #else /* Normal threads */ @@ -699,25 +702,32 @@ extern uint thd_lib_detected; Warning: When compiling without threads, this file is not included. See the *other* declarations of thread_safe_xxx in include/my_global.h - - Second warning: - See include/config-win.h, for yet another implementation. */ #ifdef THREAD #ifndef thread_safe_increment +#ifdef _WIN32 +#define thread_safe_increment(V,L) InterlockedIncrement((long*) &(V)) +#define thread_safe_decrement(V,L) InterlockedDecrement((long*) &(V)) +#else #define thread_safe_increment(V,L) \ (pthread_mutex_lock((L)), (V)++, pthread_mutex_unlock((L))) #define thread_safe_decrement(V,L) \ (pthread_mutex_lock((L)), (V)--, pthread_mutex_unlock((L))) #endif +#endif #ifndef thread_safe_add +#ifdef _WIN32 +#define thread_safe_add(V,C,L) InterlockedExchangeAdd((long*) &(V),(C)) +#define thread_safe_sub(V,C,L) InterlockedExchangeAdd((long*) &(V),-(long) (C)) +#else #define thread_safe_add(V,C,L) \ (pthread_mutex_lock((L)), (V)+=(C), pthread_mutex_unlock((L))) #define thread_safe_sub(V,C,L) \ (pthread_mutex_lock((L)), (V)-=(C), pthread_mutex_unlock((L))) #endif #endif +#endif /* statistics_xxx functions are for non critical statistic, diff --git a/include/my_sys.h b/include/my_sys.h index 9121f0f249e..ea466653372 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -34,6 +34,9 @@ extern int NEAR my_errno; /* Last error in mysys */ #include /* for CHARSET_INFO */ #include #include +#ifdef _WIN32 +#include /*for alloca*/ +#endif #define MYSYS_PROGRAM_USES_CURSES() { error_handler_hook = my_message_curses; mysys_uses_curses=1; } #define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;} diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index f158dc20999..289d1f06fbc 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -18,10 +18,25 @@ /* On Windows, exports from DLL need to be declared + Also, plugin needs to be declared as extern "C" because MSVC + unlike other compilers, uses C++ mangling for variables not only + for functions. */ -#if (defined(_WIN32) && defined(MYSQL_DYNAMIC_PLUGIN)) -#define MYSQL_PLUGIN_EXPORT extern "C" __declspec(dllexport) -#else +#if defined(_MSC_VER) +#if defined(MYSQL_DYNAMIC_PLUGIN) + #ifdef __cplusplus + #define MYSQL_PLUGIN_EXPORT extern "C" __declspec(dllexport) + #else + #define MYSQL_PLUGIN_EXPORT __declspec(dllexport) + #endif +#else /* MYSQL_DYNAMIC_PLUGIN */ + #ifdef __cplusplus + #define MYSQL_PLUGIN_EXPORT extern "C" + #else + #define MYSQL_PLUGIN_EXPORT + #endif +#endif /*MYSQL_DYNAMIC_PLUGIN */ +#else /*_MSC_VER */ #define MYSQL_PLUGIN_EXPORT #endif @@ -87,9 +102,9 @@ typedef struct st_mysql_xid MYSQL_XID; #ifndef MYSQL_DYNAMIC_PLUGIN #define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS) \ -int VERSION= MYSQL_PLUGIN_INTERFACE_VERSION; \ -int PSIZE= sizeof(struct st_mysql_plugin); \ -struct st_mysql_plugin DECLS[]= { +MYSQL_PLUGIN_EXPORT int VERSION= MYSQL_PLUGIN_INTERFACE_VERSION; \ +MYSQL_PLUGIN_EXPORT int PSIZE= sizeof(struct st_mysql_plugin); \ +MYSQL_PLUGIN_EXPORT struct st_mysql_plugin DECLS[]= { #else #define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS) \ MYSQL_PLUGIN_EXPORT int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; \ diff --git a/include/thr_alarm.h b/include/thr_alarm.h index fb906039269..8d7f5bcdee0 100644 --- a/include/thr_alarm.h +++ b/include/thr_alarm.h @@ -64,7 +64,7 @@ typedef my_bool ALARM; #if defined(__WIN__) typedef struct st_thr_alarm_entry { - rf_SetTimer crono; + UINT_PTR crono; } thr_alarm_entry; #else /* System with posix threads */ diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 84ad50e03e3..916aeae491a 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -12,102 +12,106 @@ # 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("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") + +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/libmysql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/strings + ${SSL_INCLUDE_DIRS} + ${SSL_INTERNAL_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR}) +ADD_DEFINITIONS(${SSL_DEFINES}) -# Note that we don't link with the libraries "strings" or "mysys" -# here, instead we recompile the files needed and include them -# directly. This means we don't have to worry here about if these -# libraries are compiled defining USE_TLS or not. Not that it *should* -# have been a problem anyway, they don't use thread local storage. - -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/libmysql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/strings) - -# We include the source file listing instead of referencing the -# libraries. At least with CMake 2.4 and Visual Studio 2005 a static -# library created from other static libraries would not be complete, -# i.e. the libraries listed in TARGET_LINK_LIBRARIES() were just -# ignored. - - -# Include and add the directory path -SET(SOURCE_SUBLIBS TRUE) -SET(LIB_SOURCES "") - -INCLUDE(${CMAKE_SOURCE_DIR}/zlib/CMakeLists.txt) -FOREACH(rpath ${ZLIB_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../zlib/${rpath}) -ENDFOREACH(rpath) - -# FIXME only needed if build type is "Debug", but CMAKE_BUILD_TYPE is -# not set during configure time. -INCLUDE(${CMAKE_SOURCE_DIR}/dbug/CMakeLists.txt) -FOREACH(rpath ${DBUG_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../dbug/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/CMakeLists.txt) -FOREACH(rpath ${TAOCRYPT_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../extra/yassl/taocrypt/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/extra/yassl/CMakeLists.txt) -FOREACH(rpath ${YASSL_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../extra/yassl/${rpath}) -ENDFOREACH(rpath) - -SET(CLIENT_SOURCES ../mysys/array.c ../strings/bchange.c ../strings/bmove.c - ../strings/bmove_upp.c ../mysys/charset-def.c ../mysys/charset.c - ../sql-common/client.c ../strings/ctype-big5.c ../strings/ctype-bin.c - ../strings/ctype-cp932.c ../strings/ctype-czech.c ../strings/ctype-euc_kr.c - ../strings/ctype-eucjpms.c ../strings/ctype-extra.c ../strings/ctype-gb2312.c - ../strings/ctype-gbk.c ../strings/ctype-latin1.c ../strings/ctype-mb.c - ../strings/ctype-simple.c ../strings/ctype-sjis.c ../strings/ctype-tis620.c - ../strings/ctype-uca.c ../strings/ctype-ucs2.c ../strings/ctype-ujis.c - ../strings/ctype-utf8.c ../strings/ctype-win1250ch.c ../strings/ctype.c - ../mysys/default.c errmsg.c ../mysys/errors.c - ../mysys/hash.c ../mysys/my_sleep.c ../mysys/default_modify.c - get_password.c ../strings/int2str.c ../strings/is_prefix.c - libmysql.c ../mysys/list.c ../strings/llstr.c - ../strings/longlong2str.c ../mysys/mf_arr_appstr.c ../mysys/mf_cache.c - ../mysys/mf_dirname.c ../mysys/mf_fn_ext.c ../mysys/mf_format.c - ../mysys/mf_iocache.c ../mysys/mf_iocache2.c ../mysys/mf_loadpath.c - ../mysys/mf_pack.c ../mysys/mf_path.c ../mysys/mf_tempfile.c ../mysys/mf_unixpath.c - ../mysys/mf_wcomp.c ../mysys/mulalloc.c ../mysys/my_access.c ../mysys/my_alloc.c - ../mysys/my_chsize.c ../mysys/my_compress.c ../mysys/my_create.c - ../mysys/my_delete.c ../mysys/my_div.c ../mysys/my_error.c ../mysys/my_file.c - ../mysys/my_fopen.c ../mysys/my_fstream.c ../mysys/my_gethostbyname.c - ../mysys/my_getopt.c ../mysys/my_getwd.c ../mysys/my_init.c ../mysys/my_lib.c - ../mysys/my_malloc.c ../mysys/my_messnc.c ../mysys/my_net.c ../mysys/my_once.c - ../mysys/my_open.c ../mysys/my_pread.c ../mysys/my_pthread.c ../mysys/my_read.c - ../mysys/my_realloc.c ../mysys/my_rename.c ../mysys/my_seek.c - ../mysys/my_static.c ../strings/my_strtoll10.c ../mysys/my_symlink.c - ../mysys/my_symlink2.c ../mysys/my_thr_init.c ../sql-common/my_time.c - ../strings/my_vsnprintf.c ../mysys/my_wincond.c ../mysys/my_winthread.c - ../mysys/my_write.c ../sql/net_serv.cc ../sql-common/pack.c ../sql/password.c - ../mysys/safemalloc.c ../mysys/sha1.c ../strings/str2int.c - ../strings/str_alloc.c ../strings/strcend.c ../strings/strcont.c ../strings/strend.c - ../strings/strfill.c ../mysys/string.c ../strings/strinstr.c ../strings/strmake.c - ../strings/strmov.c ../strings/strnlen.c ../strings/strnmov.c ../strings/strtod.c - ../strings/strtoll.c ../strings/strtoull.c ../strings/strxmov.c ../strings/strxnmov.c - ../mysys/thr_mutex.c ../mysys/typelib.c ../vio/vio.c ../vio/viosocket.c - ../vio/viossl.c ../vio/viosslfactories.c ../strings/xml.c ../mysys/mf_qsort.c - ../mysys/my_getsystime.c ../mysys/my_sync.c ../mysys/my_winerr.c ../mysys/my_winfile.c ${LIB_SOURCES}) +#Remove -fno-implicit-templates +#(yassl sources cannot be compiled with it) +STRING(REPLACE "-fno-implicit-templates" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +ADD_DEFINITIONS(-DDISABLE_DTRACE) -ADD_LIBRARY(mysqlclient STATIC ${CLIENT_SOURCES}) +SET(CLIENT_SOURCES + get_password.c + libmysql.c + errmsg.c + ../sql-common/client.c + ../sql-common/my_time.c + ../sql/net_serv.cc + ../sql-common/pack.c + ../sql/password.c +) + +ADD_LIBRARY(clientlib STATIC ${CLIENT_SOURCES}) +ADD_DEPENDENCIES(clientlib GenError) + +# Merge several static libraries into one big mysqlclient. +SET(LIBS dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) +MERGE_STATIC_LIBS(mysqlclient mysqlclient "${LIBS};clientlib") + ADD_DEPENDENCIES(mysqlclient GenError) -TARGET_LINK_LIBRARIES(mysqlclient) +SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) -ADD_LIBRARY(libmysql SHARED ${CLIENT_SOURCES} dll.c libmysql.def) -ADD_DEPENDENCIES(libmysql GenError) -TARGET_LINK_LIBRARIES(libmysql) -IF(EMBED_MANIFESTS) - MYSQL_EMBED_MANIFEST("myTest" "asInvoker") -ENDIF(EMBED_MANIFESTS) +# Make shared client library +IF(WIN32) + SET(SHARED_OUTPUT_NAME libmysql) +ELSE() + SET(SHARED_OUTPUT_NAME mysqlclient) +ENDIF() + +# On Windows, we can make a shared library out of static. +# On Unix, we need to recompile all sources, unless we compiled with -fPIC, in +# which case we can link static libraries to shared. +IF(MSVC) + STATIC_TO_SHARED(mysqlclient libmysql libmysql.def) +ELSE() + SET(LIBMYSQL_SOURCES ${CLIENT_SOURCES}) + + IF(NOT WITH_PIC) + # Add all sources that come into common static libs. + FOREACH(LIB ${LIBS}) + GET_TARGET_PROPERTY(SRC ${LIB} SOURCES) + IF (NOT SRC) + # This must be system shared lib (zlib or openssl) + # Users of libmysql must link with it too. + LIST(APPEND OS_LIBS ${LIB}) + ELSE() + LIST(APPEND LIBMYSQL_SOURCES ${SRC}) + ENDIF() + ENDFOREACH() + + # Some extra flags as in mysys + IF(CMAKE_COMPILER_IS_GNUCC AND NOT HAVE_CXX_NEW) + SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/mysys/my_new.cc + PROPERTIES COMPILE_FLAGS "-DUSE_MYSYS_NEW") + ENDIF() + ENDIF() + + + ADD_LIBRARY(libmysql SHARED ${LIBMYSQL_SOURCES}) + ADD_DEPENDENCIES(libmysql GenError) + SET_TARGET_PROPERTIES(libmysql PROPERTIES OUTPUT_NAME ${SHARED_OUTPUT_NAME} + SOVERSION "${SHARED_LIB_MAJOR_VERSION}.0") + SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) + + IF(WITH_PIC) + TARGET_LINK_LIBRARIES(libmysql ${LIBS}) + ENDIF() + + IF(OS_LIBS) + TARGET_LINK_LIBRARIES(libmysql ${OS_LIBS}) + ENDIF() +ENDIF() + + +IF(UNIX) + # Install links to shared and static libraries + # (append _r to base name) + INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) + INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient lib) +ENDIF() + +INSTALL(TARGETS mysqlclient libmysql DESTINATION lib) +INSTALL_DEBUG_SYMBOLS( "mysqlclient;libmysql") diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 65b8e12bc26..80f45a4491d 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -13,84 +13,45 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -ADD_DEFINITIONS(-DMYSQL_SERVER -DEMBEDDED_LIBRARY -DHAVE_DLOPEN) +ADD_DEFINITIONS(-DMYSQL_SERVER -DEMBEDDED_LIBRARY -DDISABLE_DTRACE + ${SSL_DEFINES}) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/libmysqld - ${CMAKE_SOURCE_DIR}/libmysql - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/extra/yassl/include - ${CMAKE_SOURCE_DIR}/zlib) +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/include +${CMAKE_SOURCE_DIR}/libmysql +${CMAKE_SOURCE_DIR}/libmysqld +${CMAKE_SOURCE_DIR}/sql +${CMAKE_BINARY_DIR}/sql +${CMAKE_SOURCE_DIR}/regex +${ZLIB_INCLUDE_DIR} +${SSL_INCLUDE_DIRS} +${SSL_INTERNAL_INCLUDE_DIRS} +${NDB_CLUSTER_INCLUDES} +${CMAKE_SOURCE_DIR}/sql/backup +) + +SET(GEN_SOURCES +${CMAKE_BINARY_DIR}/sql/sql_yacc.h +${CMAKE_BINARY_DIR}/sql/sql_yacc.cc +${CMAKE_BINARY_DIR}/sql/lex_hash.h +) -SET(GEN_SOURCES ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc - ${CMAKE_SOURCE_DIR}/sql/sql_yacc.h - ${CMAKE_SOURCE_DIR}/sql/message.h - ${CMAKE_SOURCE_DIR}/sql/message.rc - ${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc - ${CMAKE_SOURCE_DIR}/sql/lex_hash.h) +SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED TRUE) +SET(LIBS dbug strings regex mysys vio ${ZLIB_LIBRARY} ${SSL_LIBRARIES} +${MYSQLD_STATIC_PLUGIN_LIBS} ${NDB_CLIENT_LIBS}) -SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1) - -# Include and add the directory path -SET(SOURCE_SUBLIBS TRUE) -SET(LIB_SOURCES "") - -INCLUDE(${CMAKE_SOURCE_DIR}/zlib/CMakeLists.txt) -FOREACH(rpath ${ZLIB_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../zlib/${rpath}) -ENDFOREACH(rpath) - -# FIXME only needed if build type is "Debug", but CMAKE_BUILD_TYPE is -# not set during configure time. -INCLUDE(${CMAKE_SOURCE_DIR}/dbug/CMakeLists.txt) -FOREACH(rpath ${DBUG_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../dbug/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/CMakeLists.txt) -FOREACH(rpath ${TAOCRYPT_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../extra/yassl/taocrypt/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/extra/yassl/CMakeLists.txt) -FOREACH(rpath ${YASSL_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../extra/yassl/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/strings/CMakeLists.txt) -FOREACH(rpath ${STRINGS_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../strings/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/regex/CMakeLists.txt) -FOREACH(rpath ${REGEX_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../regex/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/mysys/CMakeLists.txt) -FOREACH(rpath ${MYSYS_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../mysys/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/vio/CMakeLists.txt) -FOREACH(rpath ${VIO_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../vio/${rpath}) -ENDFOREACH(rpath) +# Quirk: recompile selected storage engines with -DEMBEDDED_LIBRARY +# They depend on internal structures like THD that is different in embedded. +SET(RECOMPILE_ENGINES myisam myisammrg heap ndbcluster) +FOREACH(ENGINE ${RECOMPILE_ENGINES}) + LIST(REMOVE_ITEM LIBS ${ENGINE}) + GET_TARGET_PROPERTY(SRC ${ENGINE} SOURCES) + IF(SRC) + LIST(APPEND ENGINE_SOURCES ${SRC}) + ENDIF() +ENDFOREACH() - -FOREACH (ENGINE_LIB ${MYSQLD_STATIC_ENGINE_LIBS}) - STRING(TOUPPER ${ENGINE_LIB} ENGINE_LIB_UPPER) - SET(ENGINE_DIR ${${ENGINE_LIB_UPPER}_DIR}) - INCLUDE(${CMAKE_SOURCE_DIR}/storage/${ENGINE_DIR}/CMakeLists.txt) - FOREACH(rpath ${${ENGINE_LIB_UPPER}_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ${CMAKE_SOURCE_DIR}/storage/${ENGINE_DIR}/${rpath}) - ENDFOREACH(rpath) -ENDFOREACH(ENGINE_LIB) - - -SET(SOURCE_SUBLIBS FALSE) SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../libmysql/libmysql.c ../libmysql/errmsg.c ../client/get_password.c @@ -133,29 +94,35 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/partition_info.cc ../sql/sql_connect.cc ../sql/scheduler.cc ../sql/event_parse_data.cc ../sql/sql_signal.cc ../sql/rpl_handler.cc + ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc ${GEN_SOURCES} - ${LIB_SOURCES}) + ${ENGINE_SOURCES} + ${MYSYS_LIBWRAP_SOURCE} +) -# Seems we cannot make a library without at least one source file. So use a -# dummy empty file -FILE(WRITE cmake_dummy.c " ") -# Tried use the correct ${GEN_SOURCES} as dependency, worked on Unix -# but not on Windows and Visual Studio generators. Likely because they -# are no real targets from the Visual Studio project files view. Added -# custom targets to "sql/CMakeLists.txt" and reference them here. -ADD_LIBRARY(mysqlserver STATIC ${LIBMYSQLD_SOURCES}) -ADD_DEPENDENCIES(mysqlserver GenServerSource GenError) -TARGET_LINK_LIBRARIES(mysqlserver) +ADD_LIBRARY(mysqlserver_int STATIC ${LIBMYSQLD_SOURCES}) +ADD_DEPENDENCIES(mysqlserver_int GenError GenServerSource) -# Add any additional libraries requested by engine(s) -FOREACH (ENGINE_LIB ${MYSQLD_STATIC_ENGINE_LIBS}) - STRING(TOUPPER ${ENGINE_LIB} ENGINE_LIB_UPPER) - IF(${ENGINE_LIB_UPPER}_LIBS) - TARGET_LINK_LIBRARIES(mysqlserver ${${ENGINE_LIB_UPPER}_LIBS}) - ENDIF(${ENGINE_LIB_UPPER}_LIBS) -ENDFOREACH(ENGINE_LIB) +# On Windows, static embedded server library is called mysqlserver.lib +# On Unix, it is libmysqld.a +IF(WIN32) + SET(MYSQLSERVER_OUTPUT_NAME mysqlserver) +ELSE() + SET(MYSQLSERVER_OUTPUT_NAME mysqld) +ENDIF() -ADD_LIBRARY(libmysqld SHARED cmake_dummy.c libmysqld.def) -ADD_DEPENDENCIES(libmysqld mysqlserver) -TARGET_LINK_LIBRARIES(libmysqld mysqlserver wsock32) +# Merge slim mysqlserver_int with other libraries like mysys to create a big +# static library that contains everything. +MERGE_STATIC_LIBS(mysqlserver ${MYSQLSERVER_OUTPUT_NAME} + "mysqlserver_int;${LIBS}") + +IF(LIBWRAP_LIBRARY) + TARGET_LINK_LIBRARIES(mysqlserver ${LIBWRAP_LIBRARY}) +ENDIF() +INSTALL(TARGETS mysqlserver DESTINATION lib) + +IF(MSVC) + STATIC_TO_SHARED(mysqlserver libmysqld libmysqld.def) + INSTALL(TARGETS libmysqld DESTINATION lib) +ENDIF() diff --git a/libmysqld/examples/CMakeLists.txt b/libmysqld/examples/CMakeLists.txt index e4b6533f8a2..400ddd8b729 100644 --- a/libmysqld/examples/CMakeLists.txt +++ b/libmysqld/examples/CMakeLists.txt @@ -15,24 +15,44 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/libmysqld/include - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/zlib - ${CMAKE_SOURCE_DIR}/extra/yassl/include) + ${CMAKE_SOURCE_DIR}/regex + ${READLINE_INCLUDE_DIR} + ) -# Currently does not work with DBUG, there are missing symbols reported. -ADD_DEFINITIONS(-DEMBEDDED_LIBRARY) +ADD_DEFINITIONS(-DEMBEDDED_LIBRARY -UMYSQL_CLIENT) + ADD_EXECUTABLE(mysql_embedded ../../client/completion_hash.cc - ../../client/mysql.cc ../../client/readline.cc - ../../client/sql_string.cc) -TARGET_LINK_LIBRARIES(mysql_embedded debug dbug strings mysys vio yassl taocrypt regex ws2_32) -TARGET_LINK_LIBRARIES(mysql_embedded libmysqld) + ../../client/mysql.cc ../../client/readline.cc) +TARGET_LINK_LIBRARIES(mysql_embedded mysqlserver) +IF(UNIX) + ADD_DEFINITIONS(${READLINE_DEFINES}) + TARGET_LINK_LIBRARIES(mysql_embedded ${READLINE_LIBRARY}) +ENDIF(UNIX) ADD_EXECUTABLE(mysqltest_embedded ../../client/mysqltest.cc) -TARGET_LINK_LIBRARIES(mysqltest_embedded debug dbug strings mysys vio yassl taocrypt regex ws2_32) -TARGET_LINK_LIBRARIES(mysqltest_embedded libmysqld) +TARGET_LINK_LIBRARIES(mysqltest_embedded mysqlserver) -ADD_EXECUTABLE(mysql_client_test_embedded ../../tests/mysql_client_test.c) -TARGET_LINK_LIBRARIES(mysql_client_test_embedded debug dbug strings mysys vio yassl taocrypt regex ws2_32) -TARGET_LINK_LIBRARIES(mysql_client_test_embedded libmysqld) + +IF(CMAKE_GENERATOR MATCHES "Xcode") +# It does not seem possible to tell Xcode the resulting target might need +# to be linked with C++ runtime. The project needs to have at least one C++ +# file. Add a dummy one. + MYSQL_CREATE_EMPTY_FILE(mysql_client_test_embedded_dummy.cc) + ADD_EXECUTABLE(mysql_client_test_embedded + mysql_client_test_embedded_dummy.cc + ../../tests/mysql_client_test.c) +ELSE() + ADD_EXECUTABLE(mysql_client_test_embedded ../../tests/mysql_client_test.c) + SET_TARGET_PROPERTIES(mysql_client_test_embedded PROPERTIES HAS_CXX TRUE) +ENDIF() +TARGET_LINK_LIBRARIES(mysql_client_test_embedded mysqlserver) + +IF(UNIX) +SET_TARGET_PROPERTIES(mysql_embedded PROPERTIES ENABLE_EXPORTS TRUE) +SET_TARGET_PROPERTIES(mysqltest_embedded PROPERTIES ENABLE_EXPORTS TRUE) +SET_TARGET_PROPERTIES(mysql_client_test_embedded PROPERTIES ENABLE_EXPORTS TRUE) +ENDIF() + +INSTALL(TARGETS mysql_embedded mysqltest_embedded mysql_client_test_embedded DESTINATION bin) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 1a078d0d0c6..92b64d98fa0 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -731,11 +731,6 @@ void THD::clear_data_list() cur_data= 0; } -void THD::clear_error() -{ - if (stmt_da->is_error()) - stmt_da->reset_diagnostics_area(); -} static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt new file mode 100644 index 00000000000..35da3411805 --- /dev/null +++ b/man/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Copy man pages +FILE(GLOB MAN1_FILES *.1) +FILE(GLOB MAN8_FILES *.8) +IF(MAN1_FILES) + INSTALL(FILES ${MAN1_FILES} DESTINATION man/man1) +ENDIF() +IF(MAN8_FILES) + INSTALL(FILES ${MAN8_FILES} DESTINATION man/man8) +ENDIF() diff --git a/man/Makefile.am b/man/Makefile.am index 72e67f7445f..8ccada67828 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -19,7 +19,7 @@ man1_MANS = @man1_files@ man8_MANS = @man8_files@ -EXTRA_DIST = $(man1_MANS) $(man8_MANS) +EXTRA_DIST = $(man1_MANS) $(man8_MANS) CMakeLists.txt # "make_win_*" are not needed in Unix binary packages, install-data-hook: diff --git a/mysql-test/CMakeLists.txt b/mysql-test/CMakeLists.txt new file mode 100644 index 00000000000..b3afa4f8f98 --- /dev/null +++ b/mysql-test/CMakeLists.txt @@ -0,0 +1,110 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INSTALL( + DIRECTORY . + DESTINATION mysql-test + PATTERN "var/" EXCLUDE + PATTERN "lib/My/SafeProcess" EXCLUDE + PATTERN "CPack" EXCLUDE + PATTERN "CMake" EXCLUDE + PATTERN "mtr.out" EXCLUDE + PATTERN ".cvsignore" EXCLUDE +) + + + +IF(NOT ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + # Enable running mtr from build directory + CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/mtr.out-of-source + ${CMAKE_CURRENT_BINARY_DIR}/mysql-test-run.pl + @ONLY + ) +ENDIF() +IF(UNIX) + EXECUTE_PROCESS( + COMMAND chmod +x mysql-test-run.pl + COMMAND ${CMAKE_COMMAND} -E create_symlink + ./mysql-test-run.pl mtr + COMMAND ${CMAKE_COMMAND} -E create_symlink + ./mysql-test-run.pl mysql-test-run + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mtr + ${CMAKE_CURRENT_BINARY_DIR}/mysql-test-run DESTINATION mysql-test) +ENDIF() + +IF(CMAKE_GENERATOR MATCHES "Visual Studio") + SET(SETCONFIG_COMMAND set MTR_VS_CONFIG=$(OutDir)) +ELSEIF(CMAKE_GENERATOR MATCHES "Xcode") + SET(SETCONFIG_COMMAND export MTR_VS_CONFIG=$(CONFIGURATION)) +ELSE() + SET(SETCONFIG_COMMAND echo Running tests) +ENDIF() +IF(CYGWIN) + # On cygwin, pretend to be "Unix" system + SET(SETOS_COMMAND export MTR_CYGWIN_IS_UNIX=1) +ELSE() + SET(SETOS_COMMAND echo OS=${CMAKE_SYSTEM_NAME}) +ENDIF() + + +ADD_CUSTOM_TARGET(test-force + COMMAND ${SETCONFIG_COMMAND} + COMMAND ${SETOS_COMMAND} + COMMAND perl mysql-test-run.pl --force + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +SET(EXP --experimental=collections/default.experimental) +IF(WIN32) + SET(SET_ENV set) +ELSE() + SET(SET_ENV export) +ENDIF() + + +SET(MTR_FORCE perl ./mysql-test-run.pl --force) +IF(EXISTS ${CMAKE_SOURCE_DIR}/mysql-test/suite/nist) + SET(TEST_NIST ${MTR_FORCE} --comment=nist suite=nist ${EXP} && + ${MTR_FORCE} --comment=nist --force --suite=nist+ps ${EXP}) +ELSE() + SET(TEST_NIST echo "NIST tests not found") +ENDIF() + +IF(WITH_EMBEDDED_SERVER) + SET(TEST_EMBEDDED ${MTR_FORCE} --comment=embedded --timer --embedded-server + --skip-rpl --skip-ndbcluster $(EXP)) +ELSE() + SET(TEST_EMBEDDED echo "Can not test embedded, not compiled in") +ENDIF() + +ADD_CUSTOM_TARGET(test-bt + COMMAND ${SETCONFIG_COMMAND} + COMMAND ${SETOS_COMMAND} + COMMAND ${SET_ENV} MTR_BUILD_THREAD=auto + COMMAND ${MTR_FORCE} --comment=normal --timer --skip-ndbcluster --report-features ${EXP} + COMMAND ${MTR_FORCE} --comment=ps --timer --skip-ndbcluster --ps-protocol ${EXP} + COMMAND ${MTR_FORCE} --comment=funcs1+ps --ps-protocol --reorder --suite=funcs_1 ${EXP} + COMMAND ${MTR_FORCE} --comment=funcs2 --suite=funcs_2 ${EXP} + COMMAND ${MTR_FORCE} --comment=partitions --suite=parts ${EXP} + COMMAND ${MTR_FORCE} --comment=stress --suite=stress ${EXP} + COMMAND ${MTR_FORCE} --force --comment=jp --suite=jp ${EXP} + COMMAND ${TEST_NIST} + COMMAND ${TEST_EMBEDDED} +) + + diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am index b1d0e85c70e..a040688596f 100644 --- a/mysql-test/Makefile.am +++ b/mysql-test/Makefile.am @@ -71,7 +71,10 @@ SUBDIRS = lib/My/SafeProcess EXTRA_DIST = README \ $(test_SCRIPTS) \ - $(nobase_test_DATA) + $(nobase_test_DATA) \ + CMakeLists.txt \ + mtr.out-of-source + # List of directories containing test + result files and the # related test data files that should be copied diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index 505f93f1fec..1d2c606a6f7 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -29,6 +29,15 @@ sub get_basedir { return $basedir; } +# 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)= @_; @@ -38,7 +47,7 @@ sub fix_charset_dir { sub fix_language { my ($self, $config, $group_name, $group)= @_; - return my_find_dir($self->get_basedir($group), + return my_find_dir($self->get_bindir($group), \@share_locations); } diff --git a/mysql-test/lib/My/Find.pm b/mysql-test/lib/My/Find.pm index 8557584bbc8..b2fec0d77b8 100644 --- a/mysql-test/lib/My/Find.pm +++ b/mysql-test/lib/My/Find.pm @@ -167,16 +167,16 @@ sub my_find_paths { } # ------------------------------------------------------- - # Windows specific + # CMake generator specific (Visual Studio and Xcode have multimode builds) # ------------------------------------------------------- - if (IS_WINDOWS) { - # Add the default extra build dirs unless a specific one has - # already been selected - push(@extra_dirs, - ("release", - "relwithdebinfo", - "debug")) if @extra_dirs == 0; - } + + # Add the default extra build dirs unless a specific one has + # already been selected + push(@extra_dirs, + ("Release", + "Relwithdebinfo", + "Debug")) if @extra_dirs == 0; + #print "extra_build_dir: @extra_dirs\n"; diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index 7e102b628ca..0fac25b814b 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -84,19 +84,31 @@ sub is_child { # Find the safe process binary or script my @safe_process_cmd; my $safe_kill; +my $bindir; +if(defined $ENV{MTR_BINDIR}) +{ + # This is an out-of-source build. Build directory + # is given in MTR_BINDIR env.variable + $bindir = $ENV{MTR_BINDIR}."/mysql-test"; +} +else +{ + $bindir = "."; +} + if (IS_WIN32PERL or IS_CYGWIN){ # Use my_safe_process.exe - my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], + my $exe= my_find_bin($bindir, ["lib/My/SafeProcess", "My/SafeProcess"], "my_safe_process"); push(@safe_process_cmd, $exe); # Use my_safe_kill.exe - $safe_kill= my_find_bin(".", "lib/My/SafeProcess", "my_safe_kill"); + $safe_kill= my_find_bin($bindir, "lib/My/SafeProcess", "my_safe_kill"); } else { # Use my_safe_process - my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], + my $exe= my_find_bin($bindir, ["lib/My/SafeProcess", "My/SafeProcess"], "my_safe_process"); push(@safe_process_cmd, $exe); } diff --git a/mysql-test/lib/My/SafeProcess/CMakeLists.txt b/mysql-test/lib/My/SafeProcess/CMakeLists.txt index 97fab820f95..d22d4fddddf 100644 --- a/mysql-test/lib/My/SafeProcess/CMakeLists.txt +++ b/mysql-test/lib/My/SafeProcess/CMakeLists.txt @@ -13,5 +13,15 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -ADD_EXECUTABLE(my_safe_process safe_process_win.cc) -ADD_EXECUTABLE(my_safe_kill safe_kill_win.cc) +IF (WIN32) + ADD_EXECUTABLE(my_safe_process safe_process_win.cc) + ADD_EXECUTABLE(my_safe_kill safe_kill_win.cc) +ELSE() + ADD_EXECUTABLE(my_safe_process safe_process.cc) +ENDIF() + +INSTALL(TARGETS my_safe_process DESTINATION "mysql-test/lib/My/SafeProcess") +IF(WIN32) + INSTALL(TARGETS my_safe_kill DESTINATION "mysql-test/lib/My/SafeProcess") +ENDIF() +INSTALL(FILES safe_process.pl Base.pm DESTINATION "mysql-test/lib/My/SafeProcess") diff --git a/mysql-test/lib/My/SafeProcess/safe_kill_win.cc b/mysql-test/lib/My/SafeProcess/safe_kill_win.cc index 963a02c8099..f72b851d0b6 100755 --- a/mysql-test/lib/My/SafeProcess/safe_kill_win.cc +++ b/mysql-test/lib/My/SafeProcess/safe_kill_win.cc @@ -24,6 +24,7 @@ #include #include #include +#include int main(int argc, const char** argv ) { diff --git a/mysql-test/lib/My/SafeProcess/safe_process_win.cc b/mysql-test/lib/My/SafeProcess/safe_process_win.cc index 455262b29f5..ce5deb4d82a 100755 --- a/mysql-test/lib/My/SafeProcess/safe_process_win.cc +++ b/mysql-test/lib/My/SafeProcess/safe_process_win.cc @@ -57,6 +57,7 @@ #include #include #include +#include static int verbose= 0; static char safe_process_name[32]= {0}; @@ -248,6 +249,10 @@ int main(int argc, const char** argv ) Make all processes associated with the job terminate when the last handle to the job is closed. */ +#ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE +#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000 +#endif + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; if (SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)) == 0) diff --git a/mysql-test/mtr.out-of-source b/mysql-test/mtr.out-of-source new file mode 100644 index 00000000000..c2809ede136 --- /dev/null +++ b/mysql-test/mtr.out-of-source @@ -0,0 +1,5 @@ +#!/usr/bin/perl +# Call mtr in out-of-source build +$ENV{MTR_BINDIR} = "@CMAKE_BINARY_DIR@"; +chdir("@CMAKE_SOURCE_DIR@/mysql-test"); +exit(system($^X, "@CMAKE_SOURCE_DIR@/mysql-test/mysql-test-run.pl", @ARGV) >> 8); \ No newline at end of file diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f76cb008c3c..9a9fa85aa79 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -94,6 +94,7 @@ $SIG{INT}= sub { mtr_error("Got ^C signal"); }; our $mysql_version_id; our $glob_mysql_test_dir; our $basedir; +our $bindir; our $path_charsetsdir; our $path_client_bindir; @@ -943,7 +944,11 @@ sub command_line_setup { { $basedir= dirname($basedir); } - + + # Respect MTR_BINDIR variable, which is typically set in to the + # build directory in out-of-source builds. + $bindir=$ENV{MTR_BINDIR}||$basedir; + # Look for the client binaries directory if ($path_client_bindir) { @@ -952,21 +957,21 @@ sub command_line_setup { } else { - $path_client_bindir= mtr_path_exists("$basedir/client_release", - "$basedir/client_debug", + $path_client_bindir= mtr_path_exists("$bindir/client_release", + "$bindir/client_debug", vs_config_dirs('client', ''), - "$basedir/client", - "$basedir/bin"); + "$bindir/client", + "$bindir/bin"); } # Look for language files and charsetsdir, use same share - $path_language= mtr_path_exists("$basedir/share/mysql", - "$basedir/sql/share", - "$basedir/share"); - - + $path_language= mtr_path_exists("$bindir/share/mysql", + "$bindir/sql/share", + "$bindir/share"); my $path_share= $path_language; - $path_charsetsdir= mtr_path_exists("$path_share/charsets"); + $path_charsetsdir = mtr_path_exists("$basedir/share/mysql/charsets", + "$basedir/sql/share/charsets", + "$basedir/share/charsets"); if (using_extern()) { @@ -1104,7 +1109,14 @@ sub command_line_setup { # -------------------------------------------------------------------------- # Set the "var/" directory, the base for everything else # -------------------------------------------------------------------------- - $default_vardir= "$glob_mysql_test_dir/var"; + if(defined $ENV{MTR_BINDIR}) + { + $default_vardir= "$ENV{MTR_BINDIR}/mysql-test/var"; + } + else + { + $default_vardir= "$glob_mysql_test_dir/var"; + } if ( ! $opt_vardir ) { $opt_vardir= $default_vardir; @@ -1558,7 +1570,8 @@ sub collect_mysqld_features_from_running_server () } sub find_mysqld { - my ($mysqld_basedir)= @_; + + my ($mysqld_basedir)= $ENV{MTR_BINDIR}|| @_; my @mysqld_names= ("mysqld", "mysqld-max-nt", "mysqld-max", "mysqld-nt"); @@ -1644,7 +1657,7 @@ sub client_debug_arg($$) { sub mysql_fix_arguments () { - return "" if ( IS_WINDOWS ); + return "" ; my $exe= mtr_script_exists("$basedir/scripts/mysql_fix_privilege_tables", @@ -1744,6 +1757,30 @@ sub mysql_client_test_arguments(){ # Set environment to be used by childs of this process for # things that are constant during the whole lifetime of mysql-test-run # + +sub find_plugin($$) +{ + my ($plugin, $location) = @_; + my $plugin_filename; + + if (IS_WINDOWS) + { + $plugin_filename = $plugin.".dll"; + } + else + { + $plugin_filename = $plugin.".so"; + } + + my $lib_example_plugin= + mtr_file_exists(vs_config_dirs($location,$plugin_filename), + "$basedir/lib/plugin/".$plugin_filename, + "$basedir/$location/.libs/".$plugin_filename, + "$basedir/lib/mysql/plugin/".$plugin_filename, + ); + return $lib_example_plugin; +} + sub environment_setup { umask(022); @@ -1782,9 +1819,18 @@ sub environment_setup { # -------------------------------------------------------------------------- # Add the path where mysqld will find udf_example.so # -------------------------------------------------------------------------- + my $udf_example_filename; + if (IS_WINDOWS) + { + $udf_example_filename = "udf_example.dll"; + } + else + { + $udf_example_filename = "udf_example.so"; + } my $lib_udf_example= - mtr_file_exists(vs_config_dirs('sql', 'udf_example.dll'), - "$basedir/sql/.libs/udf_example.so",); + mtr_file_exists(vs_config_dirs('sql', $udf_example_filename), + "$basedir/sql/.libs/$udf_example_filename",); if ( $lib_udf_example ) { @@ -1800,60 +1846,46 @@ sub environment_setup { # Add the path where mysqld will find ha_example.so # -------------------------------------------------------------------------- if ($mysql_version_id >= 50100) { - my $plugin_filename; - if (IS_WINDOWS) - { - $plugin_filename = "ha_example.dll"; - } - else - { - $plugin_filename = "ha_example.so"; - } - my $lib_example_plugin= - mtr_file_exists(vs_config_dirs('storage/example',$plugin_filename), - "$basedir/storage/example/.libs/".$plugin_filename, - "$basedir/lib/mysql/plugin/".$plugin_filename); - $ENV{'EXAMPLE_PLUGIN'}= - ($lib_example_plugin ? basename($lib_example_plugin) : ""); - $ENV{'EXAMPLE_PLUGIN_OPT'}= "--plugin-dir=". + my ($lib_example_plugin) = find_plugin("ha_example", "storage/example"); + + if($lib_example_plugin) + { + $ENV{'EXAMPLE_PLUGIN'}= + ($lib_example_plugin ? basename($lib_example_plugin) : ""); + $ENV{'EXAMPLE_PLUGIN_OPT'}= "--plugin-dir=". ($lib_example_plugin ? dirname($lib_example_plugin) : ""); - $ENV{'HA_EXAMPLE_SO'}="'".$plugin_filename."'"; - $ENV{'EXAMPLE_PLUGIN_LOAD'}="--plugin_load=EXAMPLE=".$plugin_filename; - } - else - { - # Some ".opt" files use some of these variables, so they must be defined - $ENV{'EXAMPLE_PLUGIN'}= ""; - $ENV{'EXAMPLE_PLUGIN_OPT'}= ""; - $ENV{'HA_EXAMPLE_SO'}= ""; - $ENV{'EXAMPLE_PLUGIN_LOAD'}= ""; + $ENV{'HA_EXAMPLE_SO'}="'".basename($lib_example_plugin)."'"; + $ENV{'EXAMPLE_PLUGIN_LOAD'}="--plugin_load=EXAMPLE=".basename($lib_example_plugin); + } + else + { + # Some ".opt" files use some of these variables, so they must be defined + $ENV{'EXAMPLE_PLUGIN'}= ""; + $ENV{'EXAMPLE_PLUGIN_OPT'}= ""; + $ENV{'HA_EXAMPLE_SO'}= ""; + $ENV{'EXAMPLE_PLUGIN_LOAD'}= ""; + } } + # -------------------------------------------------------------------------- # Add the path where mysqld will find semisync plugins # -------------------------------------------------------------------------- if (!$opt_embedded_server) { - my $semisync_master_filename; - my $semisync_slave_filename; + my $semisync_lib_prefix; if (IS_WINDOWS) { - $semisync_master_filename = "semisync_master.dll"; - $semisync_slave_filename = "semisync_slave.dll"; + $semisync_lib_prefix = ""; } else { - $semisync_master_filename = "libsemisync_master.so"; - $semisync_slave_filename = "libsemisync_slave.so"; + $semisync_lib_prefix= "lib"; } - my $lib_semisync_master_plugin= - mtr_file_exists(vs_config_dirs('plugin/semisync',$semisync_master_filename), - "$basedir/plugin/semisync/.libs/" . $semisync_master_filename, - "$basedir/lib/mysql/plugin/" . $semisync_master_filename); - my $lib_semisync_slave_plugin= - mtr_file_exists(vs_config_dirs('plugin/semisync',$semisync_slave_filename), - "$basedir/plugin/semisync/.libs/" . $semisync_slave_filename, - "$basedir/lib/mysql/plugin/" . $semisync_slave_filename); + + my ($lib_semisync_master_plugin) = find_plugin($semisync_lib_prefix."semisync_master", "plugin/semisync"); + my ($lib_semisync_slave_plugin) = find_plugin($semisync_lib_prefix."semisync_slave", "plugin/semisync"); + if ($lib_semisync_master_plugin && $lib_semisync_slave_plugin) { $ENV{'SEMISYNC_MASTER_PLUGIN'}= basename($lib_semisync_master_plugin); @@ -1871,10 +1903,10 @@ sub environment_setup { # ---------------------------------------------------- # Add the path where mysqld will find mypluglib.so # ---------------------------------------------------- - my $lib_simple_parser= - mtr_file_exists(vs_config_dirs('plugin/fulltext', 'mypluglib.dll'), - "$basedir/plugin/fulltext/.libs/mypluglib.so",); + my ($lib_simple_parser) = find_plugin("mypluglib", "plugin/fulltext"); + + $ENV{'MYPLUGLIB_SO'}="'".basename($lib_simple_parser)."'"; $ENV{'SIMPLE_PARSER'}= ($lib_simple_parser ? basename($lib_simple_parser) : ""); $ENV{'SIMPLE_PARSER_OPT'}= "--plugin-dir=". @@ -2303,18 +2335,15 @@ sub vs_config_dirs ($$) { my ($path_part, $exe) = @_; $exe = "" if not defined $exe; - - # Don't look in these dirs when not on windows - return () unless IS_WINDOWS; - if ($opt_vs_config) { - return ("$basedir/$path_part/$opt_vs_config/$exe"); + return ("$bindir/$path_part/$opt_vs_config/$exe"); } - return ("$basedir/$path_part/release/$exe", - "$basedir/$path_part/relwithdebinfo/$exe", - "$basedir/$path_part/debug/$exe"); + return ("$bindir/$path_part/Release/$exe", + "$bindir/$path_part/RelWithDebinfo/$exe", + "$bindir/$path_part/Debug/$exe", + "$bindir/$path_part/$exe"); } diff --git a/mysql-test/t/fulltext_plugin.test b/mysql-test/t/fulltext_plugin.test index 31978dadc51..25e4302ef0d 100644 --- a/mysql-test/t/fulltext_plugin.test +++ b/mysql-test/t/fulltext_plugin.test @@ -3,7 +3,8 @@ # # BUG#39746 - Debug flag breaks struct definition (server crash) # -INSTALL PLUGIN simple_parser SONAME 'mypluglib.so'; +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN simple_parser SONAME $MYPLUGLIB_SO; CREATE TABLE t1(a TEXT, b TEXT, FULLTEXT(a) WITH PARSER simple_parser); ALTER TABLE t1 ADD FULLTEXT(b) WITH PARSER simple_parser; DROP TABLE t1; diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 21b04e7c968..5c0cebf5938 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -13,15 +13,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# Only the server link with this library, the client libraries and the client -# executables all link with recompiles of source found in the "mysys" directory. -# So we only need to create one version of this library, with the "static" -# Thread Local Storage model. -# -# Exception is the embedded server that needs this library compiled with -# dynamic TLS, i.e. define USE_TLS -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/mysys) + +INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/mysys) SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c default_modify.c errors.c hash.c list.c md5.c mf_brkhant.c mf_cache.c mf_dirname.c mf_fn_ext.c @@ -30,17 +24,39 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c default_ mf_radix.c mf_same.c mf_sort.c mf_soundex.c mf_arr_appstr.c mf_tempdir.c mf_tempfile.c mf_unixpath.c mf_wcomp.c mf_wfile.c mulalloc.c my_access.c my_aes.c my_alarm.c my_alloc.c my_append.c my_bit.c my_bitmap.c my_chsize.c - my_clock.c my_compress.c my_conio.c my_copy.c my_crc32.c my_create.c my_delete.c + my_clock.c my_compress.c my_copy.c my_crc32.c my_create.c my_delete.c my_div.c my_error.c my_file.c my_fopen.c my_fstream.c my_gethostbyname.c my_gethwaddr.c my_getopt.c my_getsystime.c my_getwd.c my_handler.c my_init.c my_lib.c my_lock.c my_lockmem.c my_malloc.c my_messnc.c my_mkdir.c my_mmap.c my_net.c my_once.c my_open.c my_pread.c my_pthread.c my_quick.c my_read.c my_realloc.c my_redel.c my_rename.c my_seek.c my_sleep.c - my_static.c my_symlink.c my_symlink2.c my_sync.c my_thr_init.c my_wincond.c - my_winerr.c my_winfile.c my_windac.c my_winthread.c my_write.c ptr_cmp.c queues.c stacktrace.c + my_static.c my_symlink.c my_symlink2.c my_sync.c my_thr_init.c + my_write.c ptr_cmp.c queues.c stacktrace.c rijndael.c safemalloc.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c my_vle.c base64.c my_memmem.c my_getpagesize.c) -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(mysys ${MYSYS_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) +IF (WIN32) + SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_winthread.c my_wincond.c my_winerr.c my_winfile.c my_windac.c my_conio.c) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCC AND NOT HAVE_CXX_NEW) + # gcc as C++ compiler does not have new/delete + SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_new.cc) + ADD_DEFINITIONS( -DUSE_MYSYS_NEW) +ENDIF() + +IF(HAVE_LARGE_PAGES) + SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_largepage.c) +ENDIF() + +IF(UNIX) + # some workarounds + SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_port.c) + # Some stuff not ported to windows + SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_atomic.c) + SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_getncpus.c) +ENDIF() +USE_ABSOLUTE_FILENAMES(MYSYS_SOURCES) +ADD_LIBRARY(mysys ${MYSYS_SOURCES}) +TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY}) +DTRACE_INSTRUMENT(mysys) diff --git a/mysys/my_create.c b/mysys/my_create.c index d0436276d03..49529f9b7b5 100644 --- a/mysys/my_create.c +++ b/mysys/my_create.c @@ -39,14 +39,11 @@ File my_create(const char *FileName, int CreateFlags, int access_flags, DBUG_ENTER("my_create"); DBUG_PRINT("my",("Name: '%s' CreateFlags: %d AccessFlags: %d MyFlags: %d", FileName, CreateFlags, access_flags, MyFlags)); - -#if !defined(NO_OPEN_3) - fd= open((char *) FileName, access_flags | O_CREAT, - CreateFlags ? CreateFlags : my_umask); -#elif defined(_WIN32) +#if defined(_WIN32) fd= my_win_open(FileName, access_flags | O_CREAT); #else - fd= open(FileName, access_flags); + fd= open((char *) FileName, access_flags | O_CREAT, + CreateFlags ? CreateFlags : my_umask); #endif if ((MyFlags & MY_SYNC_DIR) && (fd >=0) && diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c index b692b18bfc7..b9f385d83a9 100644 --- a/mysys/my_getsystime.c +++ b/mysys/my_getsystime.c @@ -150,7 +150,10 @@ ulonglong my_micro_time() Value in microseconds from some undefined point in time */ -#define DELTA_FOR_SECONDS LL(500000000) /* Half a second */ +#define DELTA_FOR_SECONDS 500000000LL /* Half a second */ + +/* Difference between GetSystemTimeAsFileTime() and now() */ +#define OFFSET_TO_EPOCH 116444736000000000ULL ulonglong my_micro_time_and_time(time_t *time_arg) { diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index 9e8458b0799..35715832fc6 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -129,4 +129,22 @@ error_return: return -1; } +int pthread_cancel(pthread_t thread) +{ + + HANDLE handle= 0; + BOOL ok= FALSE; + + handle= OpenThread(THREAD_TERMINATE, FALSE, thread); + if (handle) + { + ok= TerminateThread(handle,0); + CloseHandle(handle); + } + if (ok) + return 0; + + errno= EINVAL; + return -1; +} #endif diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index 4c4d6ea3598..8f125a2320e 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -44,6 +44,7 @@ extern pthread_mutex_t THR_LOCK_charset, THR_LOCK_time; void my_error_unregister_all(void); #ifdef _WIN32 +#include /* my_winfile.c exports, should not be used outside mysys */ extern File my_win_open(const char *path, int oflag); extern int my_win_close(File fd); @@ -59,8 +60,8 @@ extern FILE* my_win_fopen(const char *filename, const char *type); extern File my_win_fclose(FILE *file); extern File my_win_fileno(FILE *file); extern FILE* my_win_fdopen(File Filedes, const char *type); -extern int my_win_stat(const char *path, struct _stat64 *buf); -extern int my_win_fstat(File fd, struct _stat64 *buf); +extern int my_win_stat(const char *path, struct _stati64 *buf); +extern int my_win_fstat(File fd, struct _stati64 *buf); extern int my_win_fsync(File fd); extern File my_win_dup(File fd); extern File my_win_sopen(const char *path, int oflag, int shflag, int perm); diff --git a/plugin/daemon_example/CMakeLists.txt b/plugin/daemon_example/CMakeLists.txt new file mode 100644 index 00000000000..97ada05f935 --- /dev/null +++ b/plugin/daemon_example/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(DAEMON_EXAMPLE_PLUGIN_DYNAMIC daemon_example) +SET(DAEMON_EXAMPLE_SOURCES daemon_example.cc) +MYSQL_PLUGIN(DAEMON_EXAMPLE) diff --git a/plugin/daemon_example/Makefile.am b/plugin/daemon_example/Makefile.am index fce67285a5f..d1f2555594c 100644 --- a/plugin/daemon_example/Makefile.am +++ b/plugin/daemon_example/Makefile.am @@ -38,6 +38,7 @@ noinst_LIBRARIES = @plugin_daemon_example_static_target@ libdaemon_example_a_CXXFLAGS = $(AM_CFLAGS) libdaemon_example_a_CFLAGS = $(AM_CFLAGS) libdaemon_example_a_SOURCES= daemon_example.cc +EXTRA_DIST = CMakeLists.txt # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/plugin/fulltext/CMakeLists.txt b/plugin/fulltext/CMakeLists.txt new file mode 100644 index 00000000000..e0dd2ac510c --- /dev/null +++ b/plugin/fulltext/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(FTEXAMPLE_PLUGIN_DYNAMIC mypluglib) +SET(FTEXAMPLE_SOURCES plugin_example.c) +MYSQL_PLUGIN(FTEXAMPLE) diff --git a/plugin/fulltext/Makefile.am b/plugin/fulltext/Makefile.am index 343416072dd..0747e6d0193 100644 --- a/plugin/fulltext/Makefile.am +++ b/plugin/fulltext/Makefile.am @@ -22,6 +22,7 @@ pkgplugin_LTLIBRARIES= mypluglib.la mypluglib_la_SOURCES= plugin_example.c mypluglib_la_LDFLAGS= -module -rpath $(pkgplugindir) mypluglib_la_CFLAGS= -DMYSQL_DYNAMIC_PLUGIN +EXTRA_DIST= CMakeLists.txt # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/plugin/semisync/CMakeLists.txt b/plugin/semisync/CMakeLists.txt index d42510fab18..7681894207d 100644 --- a/plugin/semisync/CMakeLists.txt +++ b/plugin/semisync/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2006 MySQL AB +# Copyright (C) 2009 Sun Microsystems,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 @@ -11,25 +11,22 @@ # # 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 +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02 -# This is CMakeLists.txt for semi-sync replication plugins +IF(WIN32) + SET(LIBPREFIX "") +ELSE() + SET(LIBPREFIX "lib") +ENDIF() -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(SEMISYNC_MASTER_SOURCES semisync.cc semisync_master.cc semisync_master_plugin.cc + semisync.h semisync_master.h) -# Add common include directories -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/extra/yassl/include) +SET(SEMISYNC_MASTER_PLUGIN_DYNAMIC ${LIBPREFIX}semisync_master) +MYSQL_PLUGIN(SEMISYNC_MASTER) -SET(SEMISYNC_MASTER_SOURCES semisync.cc semisync_master.cc semisync_master_plugin.cc) -SET(SEMISYNC_SLAVE_SOURCES semisync.cc semisync_slave.cc semisync_slave_plugin.cc) +SET(SEMISYNC_SLAVE_SOURCES semisync.cc semisync_slave.cc semisync_slave_plugin.cc +semisync.h semisync_slave.h ) +SET(SEMISYNC_SLAVE_PLUGIN_DYNAMIC ${LIBPREFIX}semisync_slave) +MYSQL_PLUGIN(SEMISYNC_SLAVE) -ADD_DEFINITIONS(-DMYSQL_DYNAMIC_PLUGIN) -ADD_LIBRARY(semisync_master SHARED ${SEMISYNC_MASTER_SOURCES}) -TARGET_LINK_LIBRARIES(semisync_master mysqld) -ADD_LIBRARY(semisync_slave SHARED ${SEMISYNC_SLAVE_SOURCES}) -TARGET_LINK_LIBRARIES(semisync_slave mysqld) -MESSAGE("build SEMISYNC as DLL") diff --git a/regex/CMakeLists.txt b/regex/CMakeLists.txt index 440be4d2f12..f02c5745ef3 100755 --- a/regex/CMakeLists.txt +++ b/regex/CMakeLists.txt @@ -17,7 +17,6 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) SET(REGEX_SOURCES regcomp.c regerror.c regexec.c regfree.c reginit.c) +USE_ABSOLUTE_FILENAMES(REGEX_SOURCES) +ADD_LIBRARY(regex ${REGEX_SOURCES}) -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(regex ${REGEX_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) diff --git a/regex/regex2.h b/regex/regex2.h index bba54ea2054..2ab32c62da8 100644 --- a/regex/regex2.h +++ b/regex/regex2.h @@ -83,9 +83,7 @@ typedef long sopno; * a string of multi-character elements, and decide the size of the * vectors at run time. */ -#ifdef __WIN__ -typedef unsigned char uch ; -#endif + typedef struct { uch *ptr; /* -> uch [csetsize] */ diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 25cdae2b522..de3e4c661f6 100755 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -13,65 +13,257 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# Build mysql_fix_privilege_tables.sql -ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_SOURCE_DIR}/scripts/mysql_fix_privilege_tables.sql - COMMAND copy /b - mysql_system_tables.sql + mysql_system_tables_fix.sql - mysql_fix_privilege_tables.sql - DEPENDS - ${PROJECT_SOURCE_DIR}/scripts/mysql_system_tables.sql - ${PROJECT_SOURCE_DIR}/scripts/mysql_system_tables_fix.sql) +IF(UNIX) + # FIND_PROC and CHECK_PID are used by mysqld_safe +IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + SET (FIND_PROC + "ps wwwp $PID | grep -v \" grep\" | grep -v mysqld_safe | grep -- \"$MYSQLD\" > /dev/null") +ENDIF() +IF(NOT FIND_PROC AND CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET (FIND_PROC + "ps -p $PID | grep -v \" grep\" | grep -v mysqld_safe | grep -- \"$MYSQLD\" > /dev/null") +ENDIF() + +IF(NOT FIND_PROC) + # BSD style + EXECUTE_PROCESS(COMMAND ps -uaxww OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result) + IF(result MATCHES 0) + SET( FIND_PROC + "ps -uaxww | grep -v \" grep\" | grep -v mysqld_safe | grep -- "\$MYSQLD\" | grep \" $PID \" > /dev/null") + ENDIF() +ENDIF() + +IF(NOT FIND_PROC) + # SysV style + EXECUTE_PROCESS(COMMAND ps -ef OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result) + IF(result MATCHES 0) + SET( FIND_PROC "ps -ef | grep -v \" grep\" | grep -v mysqld_safe | grep -- "\$MYSQLD\" | grep \" $PID \" > /dev/null") + ENDIF() +ENDIF() + +EXECUTE_PROCESS(COMMAND sh -c "kill -0 $$" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result2) +IF(result3 MATCHES 0) + SET(CHECK_PID "kill -0 $PID > /dev/null 2> /dev/null") +ELSE() + EXECUTE_PROCESS(COMMAND sh -c "kill -s 0 $$" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result3) + IF(result4 MATCHES 0) + SET(CHECK_PID "kill -s 0 $PID > /dev/null 2> /dev/null") + ELSE() + SET(CHECK_PID "kill -s SIGCONT $PID > /dev/null 2> /dev/null") + ENDIF() +ENDIF() + +ENDIF(UNIX) + # Build comp_sql - used for embedding SQL in C or C++ programs -ADD_EXECUTABLE(comp_sql comp_sql.c) -TARGET_LINK_LIBRARIES(comp_sql dbug mysys strings) +IF(NOT CMAKE_CROSSCOMPILING) + ADD_EXECUTABLE(comp_sql comp_sql.c) + TARGET_LINK_LIBRARIES(comp_sql) +ENDIF() -# Use comp_sql to build mysql_fix_privilege_tables_sql.c -GET_TARGET_PROPERTY(COMP_SQL_EXE comp_sql LOCATION) +SET(FIX_PRIVS_IN + ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables.sql + ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables_fix.sql +) +SET(FIX_PRIVILEGES_SQL + ${CMAKE_CURRENT_BINARY_DIR}/mysql_fix_privilege_tables.sql +) -ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_SOURCE_DIR}/scripts/mysql_fix_privilege_tables_sql.c - COMMAND ${COMP_SQL_EXE} - mysql_fix_privilege_tables - mysql_fix_privilege_tables.sql - mysql_fix_privilege_tables_sql.c - DEPENDS comp_sql ${PROJECT_SOURCE_DIR}/scripts/mysql_fix_privilege_tables.sql) +# Build mysql_fix_privilege_tables.sql (concatenate 2 sql scripts) +ADD_CUSTOM_COMMAND( + OUTPUT ${FIX_PRIVILEGES_SQL} + COMMAND ${CMAKE_COMMAND} "-DIN=${FIX_PRIVS_IN}" "-DOUT=${FIX_PRIVILEGES_SQL}" + -P "${CMAKE_SOURCE_DIR}/cmake/cat.cmake" + VERBATIM + DEPENDS ${FIX_PRIVS_IN} +) -# Add dummy target for the above to be built +# Build mysql_fix_privilege_tables.c +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mysql_fix_privilege_tables_sql.c + COMMAND comp_sql + mysql_fix_privilege_tables + mysql_fix_privilege_tables.sql + mysql_fix_privilege_tables_sql.c + DEPENDS comp_sql ${CMAKE_CURRENT_BINARY_DIR}/mysql_fix_privilege_tables.sql + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +# Add target for the above to be built ADD_CUSTOM_TARGET(GenFixPrivs - ALL - DEPENDS ${PROJECT_SOURCE_DIR}/scripts/mysql_fix_privilege_tables_sql.c) + ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/mysql_fix_privilege_tables_sql.c +) # ---------------------------------------------------------------------- # Replace some variables @foo@ in the .in/.sh file, and write the new script # ---------------------------------------------------------------------- -SET(CFLAGS "-D_WINDOWS ${CMAKE_C_FLAGS_RELWITHDEBINFO}") -SET(prefix "${CMAKE_INSTALL_PREFIX}/MySQL Server ${MYSQL_BASE_VERSION}") -SET(sysconfdir ${prefix}) -SET(bindir ${prefix}/bin) -SET(libexecdir ${prefix}/bin) -SET(scriptdir ${prefix}/bin) -SET(datadir ${prefix}/share) -SET(pkgdatadir ${prefix}/share) -SET(localstatedir ${prefix}/data) +SET(CFLAGS "${CMAKE_C_FLAGS_RELWITHDEBINFO}") +SET(CXXFLAGS "${CMAKE_C_FLAGS_RELWITHDEBINFO}") +SET(LDFLAGS "${CMAKE_SHARED_LIBRARY_LINK_FLAGS}") -CONFIGURE_FILE(mysql_config.pl.in - scripts/mysql_config.pl ESCAPE_QUOTES @ONLY) +IF(WIN32) + SET(prefix "${CMAKE_INSTALL_PREFIX}/MySQL Server ${MYSQL_BASE_VERSION}") +ELSE() + set(prefix "${CMAKE_INSTALL_PREFIX}/mysql") +ENDIF() -CONFIGURE_FILE(mysql_convert_table_format.sh - scripts/mysql_convert_table_format.pl ESCAPE_QUOTES @ONLY) +SET(sysconfdir ${prefix}) +SET(bindir ${prefix}/bin) +SET(libexecdir ${prefix}/bin) +SET(scriptdir ${prefix}/bin) +SET(datadir ${prefix}/share) +SET(pkgdatadir ${prefix}/share) +SET(pkgincludedir ${prefix}/include) +SET(pkglibdir ${prefix}/lib) +SET(pkgplugindir ${prefix}/lib/plugin) +SET(localstatedir ${prefix}/data) -CONFIGURE_FILE(mysql_install_db.pl.in - scripts/mysql_install_db.pl ESCAPE_QUOTES @ONLY) +# Use cmake variables to inspect dependencies for +# mysqlclient library +SET(CLIENT_LIBS "") +SET(LIBS "") +FOREACH(lib ${mysqlclient_LIB_DEPENDS}) + # Filter out general, it is CMake hint + # not real + IF(NOT lib STREQUAL "general" AND NOT CLIENT_LIBS MATCHES "-l${lib} ") + SET(CLIENT_LIBS "${CLIENT_LIBS}-l${lib} " ) + ENDIF() +ENDFOREACH() +FOREACH(lib ${mysqlserver_LIB_DEPENDS}) + IF(NOT lib STREQUAL "general" AND NOT LIBS MATCHES "-l${lib} ") + SET(LIBS "${LIBS}-l${lib} " ) + ENDIF() +ENDFOREACH() +IF(MSVC) + STRING(REPLACE "-l" "" CLIENT_LIBS "${CLIENT_LIBS}") + STRING(REPLACE "-l" "" LIBS "${LIBS}" ) +ENDIF() -CONFIGURE_FILE(mysql_secure_installation.pl.in - scripts/mysql_secure_installation.pl ESCAPE_QUOTES @ONLY) +SET(NON_THREADED_LIBS ${CLIENT_LIBS}) -CONFIGURE_FILE(mysqld_multi.sh - scripts/mysqld_multi.pl ESCAPE_QUOTES @ONLY) +IF(WIN32) + # On Windows, some .sh and some .pl.in files are configured + # The resulting files will have .pl extension (those are perl scripts) -CONFIGURE_FILE(mysqldumpslow.sh - scripts/mysqldumpslow.pl ESCAPE_QUOTES @ONLY) + # Input files with pl.in extension + SET(PLIN_FILES mysql_config mysql_secure_installation) + # Input files with .sh extension + SET(SH_FILES mysql_convert_table_format mysqld_multi) -CONFIGURE_FILE(mysqlhotcopy.sh - scripts/mysqlhotcopy.pl ESCAPE_QUOTES @ONLY) + FOREACH(file ${PLIN_FILES}) + CONFIGURE_FILE(${file}.pl.in + ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) + ENDFOREACH() + + FOREACH(file ${SH_FILES}) + CONFIGURE_FILE(${file}.sh + ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) + ENDFOREACH() +ELSE() + # On Unix, most of the files end up in the bin directory + SET(BIN_SCRIPTS + msql2mysql + mysql_config + mysql_fix_extensions + mysql_setpermission + mysql_secure_installation + mysql_zap + mysqlaccess + mysqlbug + mysql_convert_table_format + mysql_find_rows + mysqlhotcopy + mysqldumpslow + mysqld_multi + mysqlaccess + mysqlaccess.conf + mysql_install_db + ) + FOREACH(file ${BIN_SCRIPTS}) + IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh + ${CMAKE_CURRENT_BINARY_DIR}/${file} ESCAPE_QUOTES @ONLY) + ELSEIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file} + ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) + ELSE() + MESSAGE(FATAL_ERROR "Can not find ${file}.sh or ${file} in " + "${CMAKE_CURRENT_SOURCE_DIR}" ) + ENDIF() + + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file} DESTINATION bin + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ + GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + ENDFOREACH() + + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysqld_safe.sh + ${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe @ONLY) + + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe DESTINATION bin + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ + GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + + # For some reason, mysqld_safe needs to be also in scripts directory + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mysql_install_db + DESTINATION scripts + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ + GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) +ENDIF() + +INSTALL(FILES mysql_test_data_timezone.sql DESTINATION share) + +IF(UNIX) + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/make_binary_distribution + "cd ${CMAKE_BINARY_DIR} && ${CMAKE_CPACK_COMMAND} -G TGZ --config CPackConfig.cmake" ) + EXECUTE_PROCESS( + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/make_binary_distribution +) +ENDIF() + +# Install libgcc as mylibgcc.a +IF(CMAKE_COMPILER_IS_GNUCXX) + IF(NOT LIBGCC_LOCATION) + EXECUTE_PROCESS ( + COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} + ${CMAKE_CXX_FLAGS} --print-libgcc + OUTPUT_VARIABLE LIBGCC_LOCATION + RESULT_VARIABLE RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + IF(${RESULT} EQUAL 0 AND EXISTS ${LIBGCC_LOCATION}) + SET(LIBGCC_LOCATION "${LIBGCC_LOCATION}" CACHE INTERNAL + "location of libgcc" ) + ENDIF() + ENDIF() + + IF(LIBGCC_LOCATION) + INSTALL (CODE "CONFIGURE_FILE (${LIBGCC_LOCATION} + ${CMAKE_CURRENT_BINARY_DIR}/libmygcc.a COPYONLY)") + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmygcc.a DESTINATION lib + OPTIONAL) + ENDIF() +ENDIF() + + +INSTALL (FILES + ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables.sql + ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables_data.sql + ${CMAKE_CURRENT_SOURCE_DIR}/fill_help_tables.sql + DESTINATION share +) + +# TCMalloc hacks +IF($ENV{MALLOC_LIB}) + SET(MALLOC_LIB $ENV{MALLOC_LIB} CACHE STRING "malloc library") +ENDIF() + +IF(MALLOC_LIB) + INSTALL(FILES ${MALLOC_LIB} DESTINATION lib OPTIONAL) +ENDIF() diff --git a/sql-bench/CMakeLists.txt b/sql-bench/CMakeLists.txt new file mode 100644 index 00000000000..e69fa9fa18f --- /dev/null +++ b/sql-bench/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Install sql-bench files +FILE(GLOB all_files +${CMAKE_SOURCE_DIR}/sql-bench/* +${CMAKE_SOURCE_DIR}/sql-bench/Data/ATIS/* +${CMAKE_SOURCE_DIR}/sql-bench/Data/Wisconsin/* +${CMAKE_SOURCE_DIR}/sql-bench/Comments/* +${CMAKE_SOURCE_DIR}/sql-bench/limits/* +) + +GET_FILENAME_COMPONENT(basedir ${CMAKE_SOURCE_DIR} ABSOLUTE) +FOREACH(file ${all_files}) + IF(NOT IS_DIRECTORY ${file} AND NOT ${file} MATCHES "Make" ) + FILE(RELATIVE_PATH relpath ${basedir} ${file}) + SET(target_relpath ${relpath}) + GET_FILENAME_COMPONENT(ext ${file} EXT) + GET_FILENAME_COMPONENT(dir ${relpath} PATH) + IF(ext MATCHES ".sh$") + # Those are perl files actually + STRING(REPLACE ".sh" "" target_relpath ${target_relpath} ) + IF(WIN32) + IF(NOT ext MATCHES ".pl") + SET(target_relpath "${target_relpath}.pl") + ENDIF() + ENDIF() + ENDIF() + SET(target "${CMAKE_BINARY_DIR}/${target_relpath}") + CONFIGURE_FILE(${file} ${target} COPYONLY) + IF (ext MATCHES ".bat") + IF(WIN32) + INSTALL(FILES ${target} DESTINATION ${dir}) + ENDIF() + ELSE() + INSTALL(FILES ${target} DESTINATION ${dir}) + ENDIF() + ENDIF() +ENDFOREACH() diff --git a/sql-bench/Makefile.am b/sql-bench/Makefile.am index a7aff83e7aa..ff726ff561e 100644 --- a/sql-bench/Makefile.am +++ b/sql-bench/Makefile.am @@ -37,7 +37,7 @@ EXTRA_SCRIPTS = test-ATIS.sh test-connect.sh test-create.sh \ graph-compare-results.sh innotest1.sh innotest1a.sh \ innotest1b.sh innotest2.sh innotest2a.sh innotest2b.sh \ bench-count-distinct.sh -EXTRA_DIST = $(EXTRA_SCRIPTS) +EXTRA_DIST = $(EXTRA_SCRIPTS) CMakeLists.txt dist-hook: mkdir -p $(distdir)/Data/ATIS $(distdir)/Data/Wisconsin \ diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 15c2d950ff9..881772bfc5a 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -12,32 +12,36 @@ # 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("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -SET(CMAKE_CXX_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} -DUSE_SYMDIR /Zi") -SET(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} -DUSE_SYMDIR /Zi") -SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /MAP /MAPINFO:EXPORTS") +IF(MSVC) + #Innodb plugin needs linker-generated map file to locate server exports + #see bug#42001 + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MAP /MAPINFO:EXPORTS") +ENDIF(MSVC) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/extra/yassl/include - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/zlib +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/include +${CMAKE_SOURCE_DIR}/sql +${CMAKE_SOURCE_DIR}/regex +${ZLIB_INCLUDE_DIR} +${SSL_INCLUDE_DIRS} +${CMAKE_BINARY_DIR}/sql ) -SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/sql_yacc.h - ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc - ${CMAKE_SOURCE_DIR}/include/mysql_version.h - ${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc - ${CMAKE_SOURCE_DIR}/sql/lex_hash.h - ${PROJECT_SOURCE_DIR}/include/mysqld_error.h - ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h - ${PROJECT_SOURCE_DIR}/include/sql_state.h - PROPERTIES GENERATED 1) +SET(GEN_SOURCES +${CMAKE_BINARY_DIR}/sql/sql_yacc.h +${CMAKE_BINARY_DIR}/sql/sql_yacc.cc +${CMAKE_BINARY_DIR}/sql/sql_builtin.cc +${CMAKE_BINARY_DIR}/sql/lex_hash.h +) + +SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1) + +ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER) +IF(SSL_DEFINES) + ADD_DEFINITIONS(${SSL_DEFINES}) +ENDIF() -ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN -DHAVE_EVENT_SCHEDULER) SET (SQL_SOURCE @@ -54,7 +58,7 @@ SET (SQL_SOURCE log_event_old.cc rpl_record_old.cc message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c mysqld.cc net_serv.cc - nt_servc.cc nt_servc.h opt_range.cc opt_range.h opt_sum.cc + opt_range.cc opt_range.h opt_sum.cc ../sql-common/pack.c parse_file.cc password.c procedure.cc protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc @@ -77,79 +81,169 @@ SET (SQL_SOURCE sql_connect.cc scheduler.cc sql_profile.cc event_parse_data.cc sql_signal.cc rpl_handler.cc - ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc - ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h - ${PROJECT_SOURCE_DIR}/include/mysqld_error.h - ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h - ${PROJECT_SOURCE_DIR}/include/sql_state.h - ${PROJECT_SOURCE_DIR}/include/mysql_version.h - ${PROJECT_SOURCE_DIR}/sql/sql_builtin.cc - ${PROJECT_SOURCE_DIR}/sql/lex_hash.h) -ADD_LIBRARY(sql ${SQL_SOURCE}) - -IF (NOT EXISTS cmake_dummy.cc) - FILE (WRITE cmake_dummy.cc "") -ENDIF (NOT EXISTS cmake_dummy.cc) -ADD_EXECUTABLE(mysqld cmake_dummy.cc) - -SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) -SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) - -SET (MYSQLD_CORE_LIBS mysys zlib dbug strings yassl taocrypt vio regex sql) -TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_CORE_LIBS} ${MYSQLD_STATIC_ENGINE_LIBS}) + ${GEN_SOURCES} + ${MYSYS_LIBWRAP_SOURCE}) -IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) - # Set module definition file. Also use non-incremental linker, - # incremental appears to crash from time to time,if used with /DEF option - SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "/DEF:mysqld.def /INCREMENTAL:NO") +SET (MYSQLD_CORE_LIBS mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} vio regex ) +IF(WIN32) + SET(SQL_SOURCE ${SQL_SOURCE} nt_servc.cc nt_servc.h) +ENDIF() +IF(MSVC) + ADD_LIBRARY(sql ${SQL_SOURCE}) + ADD_EXECUTABLE(mysqld mysqld_dummy.cc) + ADD_CUSTOM_COMMAND( + OUTPUT mysqld_dummy.cc + COMMAND cmake ARGS -E touch mysqld_dummy.cc + VERBATIM) + SET(MYSQLD_CORE_LIBS ${MYSQLD_CORE_LIBS} sql) + +ELSE() + ADD_EXECUTABLE(mysqld ${SQL_SOURCE}) + DTRACE_INSTRUMENT(mysqld) +ENDIF() - FOREACH (CORELIB ${MYSQLD_CORE_LIBS}) - GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION) - FILE(TO_NATIVE_PATH ${LOC} LOC) - SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC}) - ENDFOREACH (CORELIB ${MYSQLD_CORE_LIBS}) +IF(NOT WITHOUT_DYNAMIC_PLUGINS) + SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) + IF (MINGW OR CYGWIN) + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") + ENDIF() + IF(MSVC) + # Set module definition file. Also use non-incremental linker, + # incremental appears to crash from time to time,if used with /DEF option + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "/DEF:mysqld.def /INCREMENTAL:NO") + + FOREACH (CORELIB ${MYSQLD_CORE_LIBS} dbug strings) + GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION) + FILE(TO_NATIVE_PATH ${LOC} LOC) + SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC}) + ENDFOREACH (CORELIB ${MYSQLD_CORE_LIBS}) - ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK - COMMAND cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js + ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK + COMMAND echo ${PLATFORM} && cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js ${PLATFORM} ${LIB_LOCATIONS} > mysqld.def - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/sql) -ENDIF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + ADD_DEPENDENCIES(sql GenError) + ENDIF() +ENDIF() -ADD_DEPENDENCIES(sql GenError) +SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) +SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) +TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} ${MYSQLD_CORE_LIBS} ${LIBWRAP_LIBRARY}) +INSTALL(TARGETS mysqld DESTINATION bin) +INSTALL_DEBUG_SYMBOLS(mysqld) + -# Sql Parser custom command -ADD_CUSTOM_COMMAND( - OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h - ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc - COMMAND bison ARGS -y -p MYSQL --defines=sql_yacc.h - --output=sql_yacc.cc sql_yacc.yy - DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy) +FIND_PROGRAM(BISON_EXECUTABLE bison DOC "path to the bison executable") +MARK_AS_ADVANCED(BISON_EXECUTABLE "") +# Handle out-of-source build from source package with possibly broken +# bison. Copy bison output to from source to build directory, if not already +# there +IF (NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.cc) + IF(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.cc + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc COPYONLY) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.h + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h COPYONLY) + ENDIF() + ENDIF() +ENDIF() + + +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/bison.cmake) +RUN_BISON( + ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h +) + # Gen_lex_hash ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc) -TARGET_LINK_LIBRARIES(gen_lex_hash dbug mysqlclient) -GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION) +TARGET_LINK_LIBRARIES(gen_lex_hash mysys) + ADD_CUSTOM_COMMAND( - OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h - COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h - DEPENDS ${GEN_LEX_HASH_EXE}) + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h + COMMAND gen_lex_hash ARGS > lex_hash.h + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gen_lex_hash.cc) + +ADD_CUSTOM_TARGET( + GenServerSource + DEPENDS ${GEN_SOURCES} +) -ADD_CUSTOM_TARGET( - GenServerSource ALL - DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h - ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc - ${PROJECT_SOURCE_DIR}/sql/message.h - ${PROJECT_SOURCE_DIR}/sql/message.rc - ${PROJECT_SOURCE_DIR}/sql/lex_hash.h) +#Need this only for embedded +SET_TARGET_PROPERTIES(GenServerSource PROPERTIES EXCLUDE_FROM_ALL TRUE) -ADD_DEPENDENCIES(mysqld GenServerSource) +IF(WIN32 OR HAVE_DLOPEN) + ADD_LIBRARY(udf_example MODULE udf_example.c) + SET_TARGET_PROPERTIES(udf_example PROPERTIES PREFIX "") + # udf_example depends on strings + IF(WIN32) + IF(MSVC) + SET_TARGET_PROPERTIES(udf_example PROPERTIES LINK_FLAGS "/DEF:${CMAKE_CURRENT_SOURCE_DIR}/udf_example.def") + ENDIF() + TARGET_LINK_LIBRARIES(udf_example strings) + ENDIF() +ENDIF() -# Remove the auto-generated files as part of 'Clean Solution' -SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES - "lex_hash.h;sql_yacc.h;sql_yacc.cc;mysqld.def") +FOREACH(tool glibtoolize libtoolize aclocal autoconf autoheader automake gtar + tar bzr) + STRING(TOUPPER ${tool} TOOL) + FIND_PROGRAM(${TOOL}_EXECUTABLE ${tool} DOC "path to the executable") + MARK_AS_ADVANCED(${TOOL}_EXECUTABLE) +ENDFOREACH() -ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def) -ADD_DEPENDENCIES(udf_example strings GenError) -TARGET_LINK_LIBRARIES(udf_example strings) +CONFIGURE_FILE( + ${CMAKE_SOURCE_DIR}/cmake/make_dist.cmake.in ${CMAKE_BINARY_DIR}/make_dist.cmake @ONLY) + +ADD_CUSTOM_TARGET(dist + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/make_dist.cmake + DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc.h + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) + + + + + +# We need to create empty directories (data/test) the installation. +# This does not work with current CPack due to http://www.cmake.org/Bug/view.php?id=8767 +# Avoid completely empty directories and install dummy file instead. + +SET(DUMMY_FILE ${CMAKE_CURRENT_BINARY_DIR}/.empty ) +FILE(WRITE ${DUMMY_FILE} "") +INSTALL(FILES ${DUMMY_FILE} DESTINATION data/test) + +# Install initial database on windows +IF(NOT CMAKE_CROSSCOMPILING) + GET_TARGET_PROPERTY(MYSQLD_EXECUTABLE mysqld LOCATION) +ENDIF() +IF(WIN32 AND MYSQLD_EXECUTABLE) + CONFIGURE_FILE( + ${CMAKE_SOURCE_DIR}/cmake/create_initial_db.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake + @ONLY + ) + + MAKE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR}/data) + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data/mysql/user.frm + COMMAND ${CMAKE_COMMAND} + -DCONFIG=${CMAKE_CFG_INTDIR} + -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data + DEPENDS mysqld + ) + ADD_CUSTOM_TARGET(initial_database + ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/data/mysql/user.frm + ) + INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data/mysql DESTINATION data) +ELSE() + # Not windows or cross compiling, just install an empty directory + INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql) +ENDIF() \ No newline at end of file diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index f41fa08f828..76dc2846ed0 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include "nt_servc.h" diff --git a/sql/share/CMakeLists.txt b/sql/share/CMakeLists.txt new file mode 100644 index 00000000000..7a67833f9e3 --- /dev/null +++ b/sql/share/CMakeLists.txt @@ -0,0 +1,52 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +SET (dirs +danish +german +slovak +dutch +greek +norwegian +spanish +english +hungarian +norwegian-ny +swedish +italian +polish +ukrainian +japanese +portuguese +romanian +estonian +korean +russian +czech +french +serbian +) + +SET(files + errmsg-utf8.txt +) + +FOREACH (dir ${dirs}) + INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${dir} + DESTINATION share) +ENDFOREACH() +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION share) + +INSTALL(FILES ${files} DESTINATION share) diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index 06b349d5de2..c4078440abf 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -15,8 +15,9 @@ ## Process this file with automake to create Makefile.in -EXTRA_DIST= errmsg-utf8.txt - +EXTRA_DIST= errmsg-utf8.txt \ + CMakeLists.txt + dist-hook: for dir in charsets @AVAILABLE_LANGUAGES@; do \ test -d $(distdir)/$$dir || mkdir $(distdir)/$$dir; \ diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in index 3becdbaccfe..1b9b152247f 100644 --- a/sql/sql_builtin.cc.in +++ b/sql/sql_builtin.cc.in @@ -17,7 +17,12 @@ typedef struct st_mysql_plugin builtin_plugin[]; -extern builtin_plugin +#if defined(_MSC_VER) +extern "C" +#else +extern +#endif +builtin_plugin builtin_binlog_plugin@mysql_plugin_defs@; struct st_mysql_plugin *mysqld_builtins[]= diff --git a/sql/sql_class.h b/sql/sql_class.h index 1a5cceaca90..e876b2a6787 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1960,7 +1960,7 @@ public: void add_changed_table(const char *key, long key_length); CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); int send_explain_fields(select_result *result); -#ifndef EMBEDDED_LIBRARY + /** Clear the current error, if any. We do not clear is_fatal_error or is_fatal_sub_stmt_error since we @@ -1976,9 +1976,9 @@ public: is_slave_error= 0; DBUG_VOID_RETURN; } +#ifndef EMBEDDED_LIBRARY inline bool vio_ok() const { return net.vio != 0; } #else - void clear_error(); inline bool vio_ok() const { return true; } #endif /** diff --git a/storage/Makefile.am b/storage/Makefile.am index 8aa1e4f7dc6..5b4e32374ae 100644 --- a/storage/Makefile.am +++ b/storage/Makefile.am @@ -18,7 +18,6 @@ AUTOMAKE_OPTIONS = foreign # These are built from source in the Docs directory -EXTRA_DIST = mysql_storage_engine.cmake SUBDIRS = @mysql_se_dirs@ DIST_SUBDIRS = @mysql_se_distdirs@ diff --git a/storage/archive/CMakeLists.txt b/storage/archive/CMakeLists.txt index ce4d92d3f99..458f21bfb6c 100644 --- a/storage/archive/CMakeLists.txt +++ b/storage/archive/CMakeLists.txt @@ -13,6 +13,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") +SET(ARCHIVE_PLUGIN_STATIC "archive") +SET(ARCHIVE_PLUGIN_DYNAMIC "ha_archive") + SET(ARCHIVE_SOURCES azio.c ha_archive.cc ha_archive.h) MYSQL_STORAGE_ENGINE(ARCHIVE) +IF(NOT WITH_ARCHIVE_STORAGE_ENGINE AND NOT WITHOUT_ARCHIVE_STORAGE_ENGINE + AND NOT WITH_ZLIB STREQUAL "bundled") + TARGET_LINK_LIBRARIES(archive ${ZLIB_LIBRARY}) +ENDIF() + + diff --git a/storage/blackhole/CMakeLists.txt b/storage/blackhole/CMakeLists.txt index bed282ef21d..ec28208c311 100644 --- a/storage/blackhole/CMakeLists.txt +++ b/storage/blackhole/CMakeLists.txt @@ -13,7 +13,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") -SET(BLACKHOLE_SOURCES ha_blackhole.cc ha_blackhole.h) +SET(BLACKHOLE_PLUGIN_STATIC "blackhole") +SET(BLACKHOLE_PLUGIN_DYNAMIC "ha_blackhole") +SET(BLACKHOLE_SOURCES ha_blackhole.cc ha_blackhole.h) MYSQL_STORAGE_ENGINE(BLACKHOLE) diff --git a/storage/csv/CMakeLists.txt b/storage/csv/CMakeLists.txt index 37760588897..b9d31fc372f 100644 --- a/storage/csv/CMakeLists.txt +++ b/storage/csv/CMakeLists.txt @@ -13,7 +13,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +SET(CSV_PLUGIN_STATIC "csv") +SET(CSV_PLUGIN_MANDATORY TRUE) -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") SET(CSV_SOURCES ha_tina.cc ha_tina.h transparent_file.cc transparent_file.h) MYSQL_STORAGE_ENGINE(CSV) \ No newline at end of file diff --git a/storage/example/CMakeLists.txt b/storage/example/CMakeLists.txt index f0b1343ab9c..47722dfd53b 100644 --- a/storage/example/CMakeLists.txt +++ b/storage/example/CMakeLists.txt @@ -13,6 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") +SET(EXAMPLE_PLUGIN_DYNAMIC "ha_example") SET(EXAMPLE_SOURCES ha_example.cc) MYSQL_STORAGE_ENGINE(EXAMPLE) diff --git a/storage/federated/CMakeLists.txt b/storage/federated/CMakeLists.txt index fa54d36481a..f47c7240682 100644 --- a/storage/federated/CMakeLists.txt +++ b/storage/federated/CMakeLists.txt @@ -13,6 +13,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") +SET(FEDERATED_PLUGIN_STATIC "federated") +SET(FEDERATED_PLUGIN_DYNAMIC "ha_federated") SET(FEDERATED_SOURCES ha_federated.cc) +IF(NOT WITH_FEDERATED AND NOT WITH_FEDERATED_STORAGE_ENGINE) + # Bug#45488- federated uses symbols that are not used anywhere in + # mysqld and are optimized away by the linker. + SET(FEDERATED_SOURCES ${FEDERATED_SOURCES} ${CMAKE_SOURCE_DIR}/mysys/string.c) +ENDIF() MYSQL_STORAGE_ENGINE(FEDERATED) diff --git a/storage/heap/CMakeLists.txt b/storage/heap/CMakeLists.txt index 4a0fa22c8f1..1e88ecd503f 100755 --- a/storage/heap/CMakeLists.txt +++ b/storage/heap/CMakeLists.txt @@ -13,8 +13,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +SET(HEAP_PLUGIN_STATIC "heap") +SET(HEAP_PLUGIN_MANDATORY TRUE) -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") SET(HEAP_SOURCES _check.c _rectest.c hp_block.c hp_clear.c hp_close.c hp_create.c ha_heap.cc hp_delete.c hp_extra.c hp_hash.c hp_info.c hp_open.c hp_panic.c diff --git a/storage/ibmdb2i/CMakeLists.txt b/storage/ibmdb2i/CMakeLists.txt index 11cc4300569..76ef3b2662d 100644 --- a/storage/ibmdb2i/CMakeLists.txt +++ b/storage/ibmdb2i/CMakeLists.txt @@ -13,13 +13,28 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(IBMDB2I_PLUGIN_DYNAMIC "ha_ibmdb2i") +CHECK_INCLUDE_FILES(qlgusr.h HAVE_PASE_ENVIRONMENT) +IF(HAVE_PASE_ENVIRONMENT) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/extra/yassl/include) -ADD_LIBRARY(ibmdb2i ha_ibmdb2i.cc db2i_ileBridge.cc db2i_conversion.cc +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_BINARY_DIR}/include + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_CURRENT_SOURCE_DIR} + /afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0f.xpf/cur/cmvc/base.pgm/my.xpf/apis + /afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0.xpf/bld/cmvc/base.pgm/lg.xpf + /afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0.xpf/bld/cmvc/base.pgm/tq.xpf +) + + +SET (IBMDB2I_SOURCES ha_ibmdb2i.cc db2i_ileBridge.cc db2i_conversion.cc db2i_blobCollection.cc db2i_file.cc db2i_charsetSupport.cc db2i_collationSupport.cc db2i_errors.cc db2i_constraints.cc db2i_rir.cc db2i_sqlStatementStream.cc db2i_ioBuffers.cc db2i_myconv.cc) + +SET(IBMDB2I_LIBS iconv) +MYSQL_STORAGE_ENGINE(IBMDB2I) + +ENDIF(HAVE_PASE_ENVIRONMENT) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index d67b518642c..c8ec56ee5c9 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -15,40 +15,127 @@ # This is the CMakeLists for InnoDB Plugin +INCLUDE(CheckFunctionExists) +INCLUDE(CheckCSourceCompiles) +INCLUDE(CheckCSourceRuns) -# TODO: remove the two FLAGS_DEBUG settings when merging into -# 6.0-based trees, like is already the case for other engines in -# those trees. -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +# OS tests +IF(UNIX) + IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + ADD_DEFINITIONS("-DUNIV_LINUX") + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "HP*") + ADD_DEFINITIONS("-DUNIV_HPUX -DUNIV_MUST_NOT_INLINE") + ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "AIX") + ADD_DEFINITIONS("-DUNIV_AIX -DUNIX_MUST_NOT_INLINE") + ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + ADD_DEFINITIONS("-DUNIV_SOLARIS") + ELSE() + ADD_DEFINITIONS("-DUNIV_MUST_NOT_INLINE") + ENDIF() +ENDIF() -# Starting at 5.1.38, MySQL CMake files are simplified. But the plugin -# CMakeLists.txt still needs to work with previous versions of MySQL. -IF (MYSQL_VERSION_ID GREATER "50137") - INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") -ENDIF (MYSQL_VERSION_ID GREATER "50137") +# Solaris atomics +IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + CHECK_FUNCTION_EXISTS(atomic_cas_ulong HAVE_ATOMIC_CAS_ULONG) + CHECK_FUNCTION_EXISTS(atomic_cas_32 HAVE_ATOMIC_CAS_32) + CHECK_FUNCTION_EXISTS(atomic_cas_64 HAVE_ATOMIC_CAS_64) + CHECK_FUNCTION_EXISTS(atomic_add_long HAVE_ATOMIC_ADD_LONG) + IF(HAVE_ATOMIC_CAS_ULONG AND HAVE_ATOMIC_CAS_32 AND + HAVE_ATOMIC_CAS_64 AND HAVE_ATOMIC_ADD_LONG) + SET(HAVE_SOLARIS_ATOMICS 1) + ENDIF() +ENDIF() + +IF(HAVE_GCC_ATOMIC_BUILTINS) + CHECK_C_SOURCE_COMPILES(" + #include + #include + int main() + { + pthread_t x1; + pthread_t x2; + pthread_t x3; + __sync_bool_compare_and_swap(&x1, x2, x3); + return 0; + }" HAVE_ATOMIC_PTHREAD_T_GCC + ) +ENDIF() + +IF(NOT HAVE_ATOMIC_PTHREAD_T_GCC AND HAVE_SOLARIS_ATOMICS) + CHECK_C_SOURCE_COMPILES(" + #include + int main() + { + pthread_t x = 0; + return(0); + }" HAVE_ATOMIC_PTHREAD_T_SOLARIS + ) + IF(HAVE_ATOMIC_PTHREAD_T_SOLARIS) + SET(CMAKE_EXTRA_INCLUDE_FILES pthread.h) + CHECK_TYPE_SIZE(pthread_t SIZEOF_PTHREAD_T) + SET(CMAKE_EXTRA_INCLUDE_FILES) + ENDIF() +ENDIF() + +IF(NOT CMAKE_CROSSCOMPILING) + STRING(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} processor) + IF(NOT WIN32 AND processor MATCHES "86" OR processor MATCHES "amd64" + OR processor MATCHES "x64") + # Check for x86 PAUSE instruction + # We have to actually try running the test program, because of a bug + # in Solaris on x86_64, where it wrongly reports that PAUSE is not + # supported when trying to run an application. See + # http://bugs.opensolaris.org/bugdatabase/printableBug.do?bug_id=6478684 + CHECK_C_SOURCE_RUNS(" + int main() + { + __asm__ __volatile__ (\"pause\"); + return 0; + }" IB_HAVE_PAUSE_INSTRUCTION) + ENDIF() +ENDIF() + +IF(HAVE_ATOMIC_PTHREAD_T_SOLARIS OR HAVE_ATOMIC_PTHREAD_T_GCC) + ADD_DEFINITIONS(-DHAVE_ATOMIC_PTHREAD_T=1) +ENDIF() + +IF(HAVE_SOLARIS_ATOMICS) + ADD_DEFINITIONS(-DHAVE_SOLARIS_ATOMICS=1) +ENDIF() + +IF(IB_HAVE_PAUSE_INSTRUCTIONS) + ADD_DEFINITIONS(-DIB_HAVE_PAUSE_INSTRUCTIONS=1) +ENDIF() + +IF(SIZEOF_PTHREAD_T) + ADD_DEFINITIONS(-DSIZEOF_PTHREAD_T=${SIZEOF_PTHREAD_T}) +ENDIF() + +IF(MSVC) + ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS -DIB_HAVE_PAUSE_INSTRUCTION) +ENDIF() -IF (CMAKE_SIZEOF_VOID_P MATCHES 8) - SET(WIN64 TRUE) -ENDIF (CMAKE_SIZEOF_VOID_P MATCHES 8) # Include directories under innobase INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/storage/innobase/include ${CMAKE_SOURCE_DIR}/storage/innobase/handler) -# Include directories under mysql -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/zlib - ${CMAKE_SOURCE_DIR}/extra/yassl/include) +# Sun Studio bug with -xO2 +IF(CMAKE_C_COMPILER_ID MATCHES "SunPro" + AND CMAKE_C_FLAGS_RELEASE MATCHES "O2" + AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + # Sun Studio 12 crashes with -xO2 flag, but not with higher optimization + # -xO3 + SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/rem/rem0rec.c + PROPERTIES COMPILE_FLAGS -xO3) +ENDIF() # Removing compiler optimizations for innodb/mem/* files on 64-bit Windows # due to 64-bit compiler error, See MySQL Bug #19424, #36366, #34297 -IF (MSVC AND $(WIN64)) +IF (MSVC AND CMAKE_SIZEOF_VOIDP EQUAL 8) SET_SOURCE_FILES_PROPERTIES(mem/mem0mem.c mem/mem0pool.c PROPERTIES COMPILE_FLAGS -Od) -ENDIF (MSVC AND $(WIN64)) +ENDIF() SET(INNOBASE_SOURCES btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c btr/btr0sea.c buf/buf0buddy.c buf/buf0buf.c buf/buf0flu.c buf/buf0lru.c buf/buf0rea.c @@ -83,20 +170,42 @@ SET(INNOBASE_SOURCES btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c btr/btr0sea.c usr/usr0sess.c ut/ut0byte.c ut/ut0dbg.c ut/ut0mem.c ut/ut0rnd.c ut/ut0ut.c ut/ut0vec.c ut/ut0list.c ut/ut0wqueue.c) -ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS -DIB_HAVE_PAUSE_INSTRUCTION) -IF (MYSQL_VERSION_ID GREATER "50137") +IF(WITH_INNODB) + # Legacy option + SET(WITH_INNOBASE_STORAGE_ENGINE TRUE) +ENDIF() + +SET(INNOBASE_PLUGIN_STATIC "innobase") +SET(INNOBASE_PLUGIN_DYNAMIC "ha_innodb") + +# Innobase depends on zlib. If server links with system +# zlib shared library, and innobase builds as shared library, +# innobase need to link with it too, to avoid unresolved symbols. +IF(ZLIB_FOUND AND NOT WITH_ZLIB STREQUAL "bundled") + SET(INNOBASE_LIBS ${ZLIB_LIBRARY}) +ENDIF() + +#The plugin's CMakeLists.txt still needs to work with previous versions of MySQL. +IF(EXISTS ${SOURCE_DIR}/storage/mysql_storage_engine.cmake) + # Old plugin support on Windows only, + # use tricks to force ha_innodb.dll name for DLL + INCLUDE(${SOURCE_DIR}/storage/mysql_storage_engine.cmake) MYSQL_STORAGE_ENGINE(INNOBASE) - # Use ha_innodb for plugin name, if plugin is built GET_TARGET_PROPERTY(LIB_LOCATION ha_innobase LOCATION) IF(LIB_LOCATION) SET_TARGET_PROPERTIES(ha_innobase PROPERTIES OUTPUT_NAME ha_innodb) - ENDIF(LIB_LOCATION) -ELSE (MYSQL_VERSION_ID GREATER "50137") + ENDIF() +ELSEIF (MYSQL_VERSION_ID LESS "50137") + # Windows only, no plugin support IF (NOT SOURCE_SUBLIBS) - ADD_DEFINITIONS(-D_WIN32 -DMYSQL_SERVER) + ADD_DEFINITIONS(-DMYSQL_SERVER) ADD_LIBRARY(innobase STATIC ${INNOBASE_SOURCES}) # Require mysqld_error.h, which is built as part of the GenError ADD_DEPENDENCIES(innobase GenError) - ENDIF (NOT SOURCE_SUBLIBS) -ENDIF (MYSQL_VERSION_ID GREATER "50137") + ENDIF() +ELSE() + # New plugin support, cross-platform , name for shared library + # is given in INNOBASE_PLUGIN_STATIC and INNOBASE_PLUGIN_DYNAMIC + MYSQL_STORAGE_ENGINE(INNOBASE) +ENDIF() diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic index 318ec1cc1f2..8f794410f20 100644 --- a/storage/innobase/include/page0page.ic +++ b/storage/innobase/include/page0page.ic @@ -907,7 +907,7 @@ page_get_data_size( /************************************************************//** Allocates a block of memory from the free list of an index page. */ -UNIV_INTERN +UNIV_INLINE void page_mem_alloc_free( /*================*/ diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt index 829d89a798a..080448eb3f6 100755 --- a/storage/myisam/CMakeLists.txt +++ b/storage/myisam/CMakeLists.txt @@ -12,9 +12,9 @@ # 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("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") -INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") +SET(MYISAM_PLUGIN_STATIC "myisam") +SET(MYISAM_PLUGIN_MANDATORY TRUE) SET(MYISAM_SOURCES ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c ft_stem.c ha_myisam.cc @@ -30,26 +30,38 @@ SET(MYISAM_SOURCES ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c MYSQL_STORAGE_ENGINE(MYISAM) -IF(NOT SOURCE_SUBLIBS) - ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c) - TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys dbug strings zlib) +ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c) +TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys) - ADD_EXECUTABLE(myisamchk myisamchk.c) - TARGET_LINK_LIBRARIES(myisamchk myisam mysys dbug strings zlib) +ADD_EXECUTABLE(myisamchk myisamchk.c) +TARGET_LINK_LIBRARIES(myisamchk myisam mysys) - ADD_EXECUTABLE(myisamlog myisamlog.c) - TARGET_LINK_LIBRARIES(myisamlog myisam mysys dbug strings zlib) +ADD_EXECUTABLE(myisamlog myisamlog.c) +TARGET_LINK_LIBRARIES(myisamlog myisam mysys) - ADD_EXECUTABLE(myisampack myisampack.c) - TARGET_LINK_LIBRARIES(myisampack myisam mysys dbug strings zlib) +ADD_EXECUTABLE(myisampack myisampack.c) +TARGET_LINK_LIBRARIES(myisampack myisam mysys) + +IF(WITH_UNIT_TESTS AND FALSE) + ADD_EXECUTABLE(mi_test1 mi_test1.c) + TARGET_LINK_LIBRARIES(mi_test1 myisam mysys) + + ADD_EXECUTABLE(mi_test2 mi_test2.c) + TARGET_LINK_LIBRARIES(mi_test2 myisam mysys) + + ADD_EXECUTABLE(mi_test3 mi_test3.c) + TARGET_LINK_LIBRARIES(mi_test3 myisam mysys) + + ADD_EXECUTABLE(sp_test sp_test.c) + TARGET_LINK_LIBRARIES(sp_test myisam mysys) + + ADD_EXECUTABLE(rt_test rt_test.c) + TARGET_LINK_LIBRARIES(rt_test myisam mysys) +ENDIF() + +IF (MSVC) SET_TARGET_PROPERTIES(myisamchk myisampack PROPERTIES LINK_FLAGS "setargv.obj") +ENDIF() - IF(EMBED_MANIFESTS) - MYSQL_EMBED_MANIFEST("myisam_ftdump" "asInvoker") - MYSQL_EMBED_MANIFEST("myisamchk" "asInvoker") - MYSQL_EMBED_MANIFEST("myisamlog" "asInvoker") - MYSQL_EMBED_MANIFEST("myisampack" "asInvoker") - ENDIF(EMBED_MANIFESTS) - -ENDIF(NOT SOURCE_SUBLIBS) +INSTALL(TARGETS myisamchk myisamlog myisampack myisam_ftdump DESTINATION bin) diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt index c545d04a780..206848b1715 100755 --- a/storage/myisammrg/CMakeLists.txt +++ b/storage/myisammrg/CMakeLists.txt @@ -13,7 +13,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") +SET(MYISAMMRG_PLUGIN_STATIC "myisammrg") +SET(MYISAMMRG_PLUGIN_MANDATORY 1) SET(MYISAMMRG_SOURCES myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myrg_info.c ha_myisammrg.cc diff --git a/storage/mysql_storage_engine.cmake b/storage/mysql_storage_engine.cmake deleted file mode 100644 index b920f16452b..00000000000 --- a/storage/mysql_storage_engine.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# MYSQL_STORAGE_ENGINE Macro creates a project to build storage engine -# library. -# -# Parameters: -# engine - storage engine name. -# variable ENGINE_BUILD_TYPE should be set to "STATIC" or "DYNAMIC" -# Remarks: -# ${engine}_SOURCES variable containing source files to produce the library must set before -# calling this macro -# ${engine}_LIBS variable containing extra libraries to link with may be set - - -MACRO(MYSQL_STORAGE_ENGINE engine) -IF(NOT SOURCE_SUBLIBS) - # Add common include directories - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/extra/yassl/include) - STRING(TOUPPER ${engine} engine) - STRING(TOLOWER ${engine} libname) - IF(${ENGINE_BUILD_TYPE} STREQUAL "STATIC") - ADD_DEFINITIONS(-DWITH_${engine}_STORAGE_ENGINE -DMYSQL_SERVER) - #Create static library. The name of the library is .lib - ADD_LIBRARY(${libname} ${${engine}_SOURCES}) - ADD_DEPENDENCIES(${libname} GenError) - IF(${engine}_LIBS) - TARGET_LINK_LIBRARIES(${libname} ${${engine}_LIBS}) - ENDIF(${engine}_LIBS) - MESSAGE("build ${engine} as static library") - ELSEIF(${ENGINE_BUILD_TYPE} STREQUAL "DYNAMIC") - ADD_DEFINITIONS(-DMYSQL_DYNAMIC_PLUGIN) - #Create a DLL.The name of the dll is ha_.dll - #The dll is linked to the mysqld executable - SET(dyn_libname ha_${libname}) - ADD_LIBRARY(${dyn_libname} SHARED ${${engine}_SOURCES}) - TARGET_LINK_LIBRARIES (${dyn_libname} mysqlservices mysqld) - IF(${engine}_LIBS) - TARGET_LINK_LIBRARIES(${dyn_libname} ${${engine}_LIBS}) - ENDIF(${engine}_LIBS) - MESSAGE("build ${engine} as DLL") - ENDIF(${ENGINE_BUILD_TYPE} STREQUAL "STATIC") -ENDIF(NOT SOURCE_SUBLIBS) -ENDMACRO(MYSQL_STORAGE_ENGINE) diff --git a/strings/CMakeLists.txt b/strings/CMakeLists.txt index 294a129fc1b..79fe03f019a 100755 --- a/strings/CMakeLists.txt +++ b/strings/CMakeLists.txt @@ -22,8 +22,8 @@ SET(STRINGS_SOURCES bchange.c bcmp.c bfill.c bmove512.c bmove_upp.c ctype-big5.c is_prefix.c llstr.c longlong2str.c my_strtoll10.c my_vsnprintf.c r_strinstr.c str2int.c str_alloc.c strcend.c strend.c strfill.c strmake.c strmov.c strnmov.c strtod.c strtol.c strtoll.c strtoul.c strtoull.c strxmov.c strxnmov.c xml.c - my_strchr.c strcont.c strinstr.c strnlen.c) + my_strchr.c strcont.c strinstr.c strnlen.c strappend.c) + +USE_ABSOLUTE_FILENAMES(STRINGS_SOURCES) +ADD_LIBRARY(strings ${STRINGS_SOURCES}) -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(strings ${STRINGS_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) diff --git a/support-files/CMakeLists.txt b/support-files/CMakeLists.txt new file mode 100644 index 00000000000..f8aa1f329f5 --- /dev/null +++ b/support-files/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +IF(WIN32) + SET(localstatedir "C:\\mysql\\data") + SET(install_destination .) + SET(ini_file_extension "ini") +ELSE() + SET(localstatedir "/usr/local/mysql/data") + SET(prefix "/usr/local") + SET(libexedir "/usr/local/mysql/bin") + SET(bindir "/usr/local/mysql/bin" ) + SET(sbindir "/usr/local/mysql/bin") + SET(datadir "/usr/local/mysql/data") + SET(CC ${CMAKE_C_COMPILER}) + SET(CXX ${CMAKE_CXX_COMPILER}) + SET(CFLAGS ${CMAKE_C_COMPILE_FLAGS}) + SET(CXXFLAGS ${CMAKE_COMPILE_CXX_FLAGS}) + SET(MYSQLD_USER "mysql") + SET(install_destination "support-files") + SET(ini_file_extension "cnf") +ENDIF() + +FOREACH(inifile my-huge my-innodb-heavy-4G my-large my-medium my-small) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${inifile}.cnf.sh + ${CMAKE_CURRENT_BINARY_DIR}/${inifile}.${ini_file_extension} @ONLY) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${inifile}.${ini_file_extension} DESTINATION ${install_destination}) + +ENDFOREACH() + +IF(UNIX) + FILE(GLOB ndb_ini_files ${CMAKE_CURRENT_SOURCE_DIR}/*.ini) + INSTALL(FILES ${ndb_ini_files} DESTINATION ${install_destination}) + + FOREACH(script mysql.server mysqld_multi.server mysql-log-rotate) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${script}.sh + ${CMAKE_CURRENT_BINARY_DIR}/${script} @ONLY ) + + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${script} DESTINATION support-files + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + ENDFOREACH() + INSTALL(FILES magic DESTINATION support-files) + INSTALL(FILES mysql.m4 DESTINATION share/aclocal) + CONFIGURE_FILE(MySQL-shared-compat.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/MySQL-shared-compat.spec @ONLY) + CONFIGURE_FILE(mysql.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/mysql.spec @ONLY) + CONFIGURE_FILE(mysql.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/mysql.${VERSION}.spec @ONLY) + CONFIGURE_FILE(MySQL-shared-compat.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/MySQL-shared-compat.spec @ONLY) +ENDIF() + diff --git a/support-files/Makefile.am b/support-files/Makefile.am index 47e8c395b31..9718c6e7ebf 100644 --- a/support-files/Makefile.am +++ b/support-files/Makefile.am @@ -34,7 +34,8 @@ EXTRA_DIST = mysql.spec.sh \ MySQL-shared-compat.spec.sh \ ndb-config-2-node.ini.sh \ compiler_warnings.supp \ - dtrace + dtrace \ + CMakeLists.txt SUBDIRS = MacOSX RHEL4-SElinux diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0ee8769cd23..ea2f41abb73 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,8 +17,14 @@ ADD_DEFINITIONS("-DMYSQL_CLIENT") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) -ADD_EXECUTABLE(mysql_client_test mysql_client_test.c ../mysys/my_memmem.c) +ADD_EXECUTABLE(mysql_client_test mysql_client_test.c) TARGET_LINK_LIBRARIES(mysql_client_test mysqlclient) +SET_TARGET_PROPERTIES(mysql_client_test PROPERTIES LINKER_LANGUAGE CXX) -ADD_EXECUTABLE(bug25714 bug25714.c) -TARGET_LINK_LIBRARIES(bug25714 mysqlclient) +IF(WITH_UNIT_TESTS) + ADD_EXECUTABLE(bug25714 bug25714.c) + TARGET_LINK_LIBRARIES(bug25714 mysqlclient) + SET_TARGET_PROPERTIES(bug25714 PROPERTIES LINKER_LANGUAGE CXX) +ENDIF() + +INSTALL(TARGETS mysql_client_test DESTINATION bin) diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt new file mode 100644 index 00000000000..94eb3159693 --- /dev/null +++ b/unittest/mysys/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2007 MySQL AB, 2009 Sun Microsystems,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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/unittest/mytap) + + +MACRO (MY_ADD_TEST name) + ADD_EXECUTABLE(${name}-t ${name}-t.c) + TARGET_LINK_LIBRARIES(${name}-t mytap mysys) + ADD_TEST(${name} ${name}-t) +ENDMACRO() + + +FOREACH(testname bitmap base64 my_vsnprintf) + MY_ADD_TEST(${testname}) +ENDFOREACH() +IF(NOT WIN32) + # does not work on Windows in 5.x codebase + MY_ADD_TEST(my_atomic) +ENDIF() diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index 56c65d71396..2dfa6d7698a 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -22,6 +22,8 @@ LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/strings/libmystrings.a noinst_PROGRAMS = bitmap-t base64-t my_vsnprintf-t +EXTRA_DIST = CMakeLists.txt + if NEED_THREAD # my_atomic-t is used to check thread functions, so it is safe to diff --git a/unittest/mytap/CMakeLists.txt b/unittest/mytap/CMakeLists.txt new file mode 100644 index 00000000000..8a2f6c9639a --- /dev/null +++ b/unittest/mytap/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2007 MySQL AB, 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) +ADD_LIBRARY(mytap tap.c) diff --git a/unittest/mytap/Makefile.am b/unittest/mytap/Makefile.am index c02bcd3b49d..8feefb134bb 100644 --- a/unittest/mytap/Makefile.am +++ b/unittest/mytap/Makefile.am @@ -20,6 +20,8 @@ noinst_HEADERS = tap.h libmytap_a_SOURCES = tap.c +EXTRA_DIST = CMakeLists.txt + SUBDIRS = . t # Don't update the files from bitkeeper diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 4e053e3e745..34a3b933eba 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -151,7 +151,9 @@ static signal_entry install_signal[]= { { SIGABRT, handle_core_signal }, { SIGFPE, handle_core_signal }, { SIGSEGV, handle_core_signal }, +#ifdef SIGBUS { SIGBUS, handle_core_signal } +#endif #ifdef SIGXCPU , { SIGXCPU, handle_core_signal } #endif @@ -167,7 +169,7 @@ static signal_entry install_signal[]= { }; void -plan(int const count) +plan(int count) { /* Install signal handler @@ -201,7 +203,7 @@ skip_all(char const *reason, ...) } void -ok(int const pass, char const *fmt, ...) +ok(int pass, char const *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -218,7 +220,7 @@ ok(int const pass, char const *fmt, ...) void -skip(int how_many, char const *const fmt, ...) +skip(int how_many, char const *fmt, ...) { char reason[80]; if (fmt && *fmt) diff --git a/vio/CMakeLists.txt b/vio/CMakeLists.txt index e1bd57d150f..6f44ecadb21 100755 --- a/vio/CMakeLists.txt +++ b/vio/CMakeLists.txt @@ -14,13 +14,12 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -ADD_DEFINITIONS(-DUSE_SYMDIR) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/extra/yassl/include) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include +${SSL_INCLUDE_DIRS}) +ADD_DEFINITIONS(${SSL_DEFINES}) SET(VIO_SOURCES vio.c viosocket.c viossl.c viosslfactories.c) - -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(vio ${VIO_SOURCES}) - ADD_DEPENDENCIES(vio GenError) -ENDIF(NOT SOURCE_SUBLIBS) +USE_ABSOLUTE_FILENAMES(VIO_SOURCES) +ADD_LIBRARY(vio ${VIO_SOURCES}) +ADD_DEPENDENCIES(vio GenError) diff --git a/win/create_def_file.js b/win/create_def_file.js index aaaf4659736..21f884417e3 100644 --- a/win/create_def_file.js +++ b/win/create_def_file.js @@ -173,6 +173,7 @@ function IsCompilerDefinedSymbol(symbol) (symbol.indexOf("??_R") != -1) || (symbol.indexOf("??_7") != -1) || (symbol.indexOf("?_G") != -1) || // scalar deleting destructor + (symbol.indexOf("_VInfreq_?") != -1) || // special label (exception handler?) for Intel compiler (symbol.indexOf("?_E") != -1)); // vector deleting destructor } diff --git a/zlib/CMakeLists.txt b/zlib/CMakeLists.txt index 43235b631f6..2d0706d024e 100755 --- a/zlib/CMakeLists.txt +++ b/zlib/CMakeLists.txt @@ -13,18 +13,15 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# Note that this library is not using any "Thread Local Storage" (TLS), -# i.e. no data declared "__declspec(thread)" or allocated with TlsAlloc(). -# Not directly and indirectly using any of the macros for creating and -# using the storage, pthread_key*(), {,my_}{set,get}_specific*() .... -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib) - -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/include +${CMAKE_SOURCE_DIR}/zlib +) SET(ZLIB_SOURCES adler32.c compress.c crc32.c crc32.h deflate.c deflate.h gzio.c infback.c inffast.c inffast.h inffixed.h inflate.c inflate.h inftrees.c inftrees.h trees.c trees.h uncompr.c zconf.h zlib.h zutil.c zutil.h) -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(zlib ${ZLIB_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) +USE_ABSOLUTE_FILENAMES(ZLIB_SOURCES) +ADD_LIBRARY(zlib ${ZLIB_SOURCES}) + From 360ee02d5dfdd4f102d69d8db617966a605e1f51 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 9 Nov 2009 12:42:29 +0100 Subject: [PATCH 005/466] fix branch name --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index e9ae26ca793..38fc10fed70 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-5.5-next-mr" +tree_name = "mysql-5.5-next-mr-cmake" From d47ac4d7a6ea54116793301aa324cac477c4c3de Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 9 Nov 2009 19:14:33 +0100 Subject: [PATCH 006/466] Fix problems found by Joro (Xcode generator specific) --- libmysqld/examples/CMakeLists.txt | 8 ++++++-- sql/CMakeLists.txt | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libmysqld/examples/CMakeLists.txt b/libmysqld/examples/CMakeLists.txt index 400ddd8b729..ed0e5a361a8 100644 --- a/libmysqld/examples/CMakeLists.txt +++ b/libmysqld/examples/CMakeLists.txt @@ -39,9 +39,13 @@ IF(CMAKE_GENERATOR MATCHES "Xcode") # It does not seem possible to tell Xcode the resulting target might need # to be linked with C++ runtime. The project needs to have at least one C++ # file. Add a dummy one. - MYSQL_CREATE_EMPTY_FILE(mysql_client_test_embedded_dummy.cc) + ADD_CUSTOM_COMMAND(OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/mysql_client_test_embedded_dummy.cc + COMMAND ${CMAKE_COMMAND} -E touch + ${CMAKE_CURRENT_BINARY_DIR}/mysql_client_test_embedded_dummy.cc + ) ADD_EXECUTABLE(mysql_client_test_embedded - mysql_client_test_embedded_dummy.cc + ${CMAKE_CURRENT_BINARY_DIR}/mysql_client_test_embedded_dummy.cc ../../tests/mysql_client_test.c) ELSE() ADD_EXECUTABLE(mysql_client_test_embedded ../../tests/mysql_client_test.c) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 881772bfc5a..336f94c0718 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -187,6 +187,9 @@ IF(WIN32 OR HAVE_DLOPEN) SET_TARGET_PROPERTIES(udf_example PROPERTIES LINK_FLAGS "/DEF:${CMAKE_CURRENT_SOURCE_DIR}/udf_example.def") ENDIF() TARGET_LINK_LIBRARIES(udf_example strings) + ELSE() + # udf_example is using safemutex exported by mysqld + TARGET_LINK_LIBRARIES(udf_example mysqld) ENDIF() ENDIF() @@ -246,4 +249,4 @@ IF(WIN32 AND MYSQLD_EXECUTABLE) ELSE() # Not windows or cross compiling, just install an empty directory INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql) -ENDIF() \ No newline at end of file +ENDIF() From c69715f36bf539a3c903c57b403019b307651cea Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 10 Nov 2009 02:19:58 +0100 Subject: [PATCH 007/466] refactor libedit, generate files in binary tree, not in source tree --- cmd-line-utils/libedit/CMakeLists.txt | 161 +++++++++++++------------- 1 file changed, 81 insertions(+), 80 deletions(-) diff --git a/cmd-line-utils/libedit/CMakeLists.txt b/cmd-line-utils/libedit/CMakeLists.txt index 0c58fe9546f..3d04f1c0ce2 100644 --- a/cmd-line-utils/libedit/CMakeLists.txt +++ b/cmd-line-utils/libedit/CMakeLists.txt @@ -15,6 +15,7 @@ INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ) INCLUDE(CheckIncludeFile) +include(CheckFunctionExists) CHECK_INCLUDE_FILES(term.h HAVE_TERM_H) SET(CMAKE_REQUIRED_LIBRARIES ${CURSES_CURSES_LIBRARY}) @@ -47,30 +48,11 @@ ENDIF() MARK_AS_ADVANCED(AWK_EXECUTABLE) SET(AWK ${AWK_EXECUTABLE}) -CONFIGURE_FILE(makelist.sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist @ONLY) +CONFIGURE_FILE(makelist.sh ${CMAKE_CURRENT_BINARY_DIR}/makelist @ONLY) - -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CURSES_INCLUDE_PATH} - } -) - -SET(ASRC vi.c emacs.c common.c) -SET(AHDR vi.h emacs.h common.h) - - -SET(LIBEDIT_SOURCES - chared.c el.c history.c map.c prompt.c readline.c - search.c tokenizer.c vi.c common.c emacs.c - hist.c key.c parse.c read.c refresh.c sig.c term.c - tty.c help.c fcns.c filecomplete.c - ${AHDR} - ) - -include(CheckFunctionExists) include(CheckIncludeFile) -CHECK_INCLUDE_FILE(vis.h HAVE_VIS_H) +CHECK_INCLUDE_FILE(vis.h HAVE_VIS_H) IF(HAVE_VIS_H) CHECK_FUNCTION_EXISTS(strvis HAVE_STRVIS) IF(NOT HAVE_STRVIS) @@ -80,88 +62,107 @@ ENDIF() CHECK_FUNCTION_EXISTS(strvis HAVE_STRVIS) IF(NOT HAVE_STRVIS) - SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/vis.c) + SET(LIBEDIT_EXTRA_SOURCES ${LIBEDIT_EXTRA_SOURCES} np/vis.c) ENDIF() - CHECK_FUNCTION_EXISTS(strunvis HAVE_STRUNVIS) IF(NOT HAVE_STRUNVIS) - SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/unvis.c) + SET(LIBEDIT_EXTRA_SOURCES ${LIBEDIT_EXTRA_SOURCES} np/unvis.c) ENDIF() CHECK_FUNCTION_EXISTS(strlcpy HAVE_STRLCPY) IF(NOT HAVE_STRLCPY) - SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/strlcpy.c) + SET(LIBEDIT_EXTRA_SOURCES ${LIBEDIT_EXTRA_SOURCES} np/strlcpy.c) ENDIF() - CHECK_FUNCTION_EXISTS(strlcat HAVE_STRLCAT) IF(NOT HAVE_STRLCAT) - SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/strlcat.c) + SET(LIBEDIT_EXTRA_SOURCES ${LIBEDIT_EXTRA_SOURCES} np/strlcat.c) ENDIF() - CHECK_FUNCTION_EXISTS(fgetln HAVE_FGETLN) IF(NOT HAVE_FGETLN) - SET(LIBEDIT_SOURCES ${LIBEDIT_SOURCES} np/fgetln.c) + SET(LIBEDIT_EXTRA_SOURCES ${LIBEDIT_EXTRA_SOURCES} np/fgetln.c) ENDIF() +# Generate headers +FOREACH(SRCBASENAME vi emacs common) + SET(SRC ${CMAKE_CURRENT_SOURCE_DIR}/${SRCBASENAME}.c) + SET(HDR ${CMAKE_CURRENT_BINARY_DIR}/${SRCBASENAME}.h) -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/vi.h - COMMAND sh ./makelist -h vi.c > vi.h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS vi.c) + ADD_CUSTOM_COMMAND( + OUTPUT ${HDR} + COMMAND sh ./makelist -h ${SRC} > ${HDR} + DEPENDS ${SRC}) -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/emacs.h - COMMAND sh ./makelist -h emacs.c > emacs.h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS emacs.c) + SET(AHDR ${AHDR} ${HDR}) + SET(ASRC ${ASRC} ${SRC}) +ENDFOREACH() -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/common.h - COMMAND sh ./makelist -h common.c > common.h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS common.c) - -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/help.c - COMMAND sh ./makelist -bc ${ASRC} > help.c - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${ASRC} - ) - -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/help.h - COMMAND sh ./makelist -bh ${ASRC} > help.h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${ASRC} - ) - -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/fcns.h - COMMAND sh ./makelist -fh ${AHDR} > fcns.h - VERBATIM - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${AHDR} - ) - -ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/fcns.c - COMMAND sh ./makelist -fc ${AHDR} > fcns.c - VERBATIM - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${AHDR} - ) - - -ADD_CUSTOM_TARGET( - GenLibeditSource - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/vi.h - ${CMAKE_CURRENT_SOURCE_DIR}/emacs.h - ${CMAKE_CURRENT_SOURCE_DIR}/common.h - ${CMAKE_CURRENT_SOURCE_DIR}/help.c - ${CMAKE_CURRENT_SOURCE_DIR}/help.h - ${CMAKE_CURRENT_SOURCE_DIR}/fcns.c - ${CMAKE_CURRENT_SOURCE_DIR}/fcns.h +# Generate source files +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/help.c + COMMAND sh ./makelist -bc ${ASRC} > help.c + DEPENDS ${ASRC} ) +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/help.h + COMMAND sh ./makelist -bh ${ASRC} > help.h + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${ASRC} +) + +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fcns.h + COMMAND sh ./makelist -fh ${AHDR} > fcns.h + VERBATIM + DEPENDS ${AHDR} +) + +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fcns.c + COMMAND sh ./makelist -fc ${AHDR} > fcns.c + VERBATIM + DEPENDS ${AHDR} +) + + +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/include +${CMAKE_CURRENT_BINARY_DIR} +${CURSES_INCLUDE_PATH} +) + +SET(LIBEDIT_SOURCES + chared.c + el.c + history.c + map.c + prompt.c + readline.c + search.c + tokenizer.c + vi.c + common.c + emacs.c + hist.c + key.c + parse.c + read.c + refresh.c + sig.c + term.c + tty.c + filecomplete.c + ${CMAKE_CURRENT_BINARY_DIR}/help.c + ${CMAKE_CURRENT_BINARY_DIR}/help.h + ${CMAKE_CURRENT_BINARY_DIR}/fcns.c + ${CMAKE_CURRENT_SOURCE_DIR}/fcns.h + ${AHDR} + ${LIBEDIT_EXTRA_SOURCES} +) +MESSAGE(${LIBEDIT_EXTRA_SOURCES}) ADD_LIBRARY(edit ${LIBEDIT_SOURCES}) -ADD_DEPENDENCIES(edit GenLibeditSource) TARGET_LINK_LIBRARIES(edit ${CURSES_LIBRARY}) From 2dced72b27d1c0887cf76cb64e97985156b7c4c3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 10 Nov 2009 02:43:23 +0100 Subject: [PATCH 008/466] remove debug output --- cmd-line-utils/libedit/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-line-utils/libedit/CMakeLists.txt b/cmd-line-utils/libedit/CMakeLists.txt index 3d04f1c0ce2..52b47585b6b 100644 --- a/cmd-line-utils/libedit/CMakeLists.txt +++ b/cmd-line-utils/libedit/CMakeLists.txt @@ -162,7 +162,6 @@ SET(LIBEDIT_SOURCES ${AHDR} ${LIBEDIT_EXTRA_SOURCES} ) -MESSAGE(${LIBEDIT_EXTRA_SOURCES}) ADD_LIBRARY(edit ${LIBEDIT_SOURCES}) TARGET_LINK_LIBRARIES(edit ${CURSES_LIBRARY}) From 3d6465d98732118ebea7913fc1e4aee4d733b961 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 10 Nov 2009 18:24:59 +0100 Subject: [PATCH 009/466] Fix libedit out-of-source build --- cmd-line-utils/libedit/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-line-utils/libedit/CMakeLists.txt b/cmd-line-utils/libedit/CMakeLists.txt index 52b47585b6b..763da8a4e04 100644 --- a/cmd-line-utils/libedit/CMakeLists.txt +++ b/cmd-line-utils/libedit/CMakeLists.txt @@ -158,7 +158,7 @@ SET(LIBEDIT_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/help.c ${CMAKE_CURRENT_BINARY_DIR}/help.h ${CMAKE_CURRENT_BINARY_DIR}/fcns.c - ${CMAKE_CURRENT_SOURCE_DIR}/fcns.h + ${CMAKE_CURRENT_BINARY_DIR}/fcns.h ${AHDR} ${LIBEDIT_EXTRA_SOURCES} ) From ffdb6be7551fc3c0b6e265d9a1f55fe0e78a9ccc Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 11 Nov 2009 12:46:19 +0100 Subject: [PATCH 010/466] Bug #48671 mysqltest fails on 'perl' in file sourced inside 'while' Actually, fails on 'perl' in any while Fixed essentially the same way as for append_file --- client/mysqltest.cc | 87 +++++++++++++++++++---------------- mysql-test/r/mysqltest.result | 2 + mysql-test/t/mysqltest.test | 14 ++++++ 3 files changed, 64 insertions(+), 39 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 8de2f0c79b0..cfcabb5a5ab 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -3632,49 +3632,58 @@ void do_perl(struct st_command *command) sizeof(perl_args)/sizeof(struct command_arg), ' '); - /* If no delimiter was provided, use EOF */ - if (ds_delimiter.length == 0) - dynstr_set(&ds_delimiter, "EOF"); - - init_dynamic_string(&ds_script, "", 1024, 1024); - read_until_delimiter(&ds_script, &ds_delimiter); - - DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); - - /* Create temporary file name */ - if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), - "tmp", O_CREAT | O_SHARE | O_RDWR, - MYF(MY_WME))) < 0) - die("Failed to create temporary file for perl command"); - my_close(fd, MYF(0)); - - str_to_file(temp_file_path, ds_script.str, ds_script.length); - - /* Format the "perl " command */ - my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path); - - if (!(res_file= popen(buf, "r")) && command->abort_on_error) - die("popen(\"%s\", \"r\") failed", buf); - - while (fgets(buf, sizeof(buf), res_file)) + ds_script= command->content; + /* If it hasn't been done already by a loop iteration, fill it in */ + if (! ds_script.str) { - if (disable_result_log) - { - buf[strlen(buf)-1]=0; - DBUG_PRINT("exec_result",("%s", buf)); - } - else - { - replace_dynstr_append(&ds_res, buf); - } + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + + init_dynamic_string(&ds_script, "", 1024, 1024); + read_until_delimiter(&ds_script, &ds_delimiter); + command->content= ds_script; } - error= pclose(res_file); - /* Remove the temporary file */ - my_delete(temp_file_path, MYF(0)); + /* This function could be called even if "false", so check before doing */ + if (cur_block->ok) + { + DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); - handle_command_error(command, WEXITSTATUS(error)); - dynstr_free(&ds_script); + /* Create temporary file name */ + if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), + "tmp", O_CREAT | O_SHARE | O_RDWR, + MYF(MY_WME))) < 0) + die("Failed to create temporary file for perl command"); + my_close(fd, MYF(0)); + + str_to_file(temp_file_path, ds_script.str, ds_script.length); + + /* Format the "perl " command */ + my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path); + + if (!(res_file= popen(buf, "r")) && command->abort_on_error) + die("popen(\"%s\", \"r\") failed", buf); + + while (fgets(buf, sizeof(buf), res_file)) + { + if (disable_result_log) + { + buf[strlen(buf)-1]=0; + DBUG_PRINT("exec_result",("%s", buf)); + } + else + { + replace_dynstr_append(&ds_res, buf); + } + } + error= pclose(res_file); + + /* Remove the temporary file */ + my_delete(temp_file_path, MYF(0)); + + handle_command_error(command, WEXITSTATUS(error)); + } dynstr_free(&ds_delimiter); DBUG_VOID_RETURN; } diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 2e3a9489593..671f88cb00a 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -553,6 +553,8 @@ hello mysqltest: At line 1: Max delimiter length(16) exceeded hello hello +val is 5 +val is 5 mysqltest: At line 1: test of die Some output create table t1( a int, b char(255), c timestamp); diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index bcf33aa8c27..b4ea9202df7 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1905,6 +1905,20 @@ perl; print "hello\n"; EOF +# Test perl within while, also with if being false first iteration +let $outer= 3; +let $ifval= 0; +while ($outer) { + if ($ifval) { + perl UNTIL; + my $val= 5; + print "val is $val\n"; +UNTIL + } + inc $ifval; + dec $outer; +} + # ---------------------------------------------------------------------------- # test for die # ---------------------------------------------------------------------------- From 12c7945b5bf3a574fd7784c29b32bca279e139cb Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 11 Nov 2009 12:51:44 +0000 Subject: [PATCH 011/466] correctly specify working directory for dtrace_prelink. Also, generate _dtrace.o in the object directory --- cmake/dtrace.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index 5c031e04841..c0f66e4c46c 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -74,7 +74,7 @@ MACRO (DTRACE_INSTRUMENT target) # link it together with target. IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") SET(objdir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir) - SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/${target}_dtrace.o) + SET(outfile ${objdir}/${target}_dtrace.o) ADD_CUSTOM_COMMAND( TARGET ${target} PRE_LINK @@ -84,7 +84,7 @@ MACRO (DTRACE_INSTRUMENT target) -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d -DDTRACE_FLAGS=${DTRACE_FLAGS} -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake - WORKING_DIRECTORY ${OBJDIR} + WORKING_DIRECTORY ${objdir} ) SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS "${outfile}") ENDIF() From 59940cdcd2049ff92a434ff758d06f99e5e45d72 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 12 Nov 2009 11:04:01 +0100 Subject: [PATCH 012/466] Bug #43418 MTR2: does not notice a memory leak occuring at shutdown of mysqld w/ --valgrind Checking is done before server is terminated Adds post processing of server logs if --valgrind Also had to remove --quiet option to valgrind --- mysql-test/include/mtr_warnings.sql | 8 ++++ mysql-test/mysql-test-run.pl | 71 +++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index 57e7cb97d48..0176c1800e8 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -175,6 +175,14 @@ INSERT INTO global_suppressions VALUES ("Can't find file: '.\\\\test\\\\\\?{8}.frm'"), ("Slave: Unknown table 't1' Error_code: 1051"), + /* Messages from valgrind */ + ("==[0-9]*== Memcheck,"), + ("==[0-9]*== Copyright"), + ("==[0-9]*== Using"), + ("==[0-9]*== For more details"), + /* This comes with innodb plugin tests */ + ("==[0-9]*== Warning: set address range perms: large range .* .defined."), + ("THE_LAST_SUPPRESSION")|| diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c91a74570bd..4ac3beff0a6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -226,6 +226,7 @@ my @default_valgrind_args= ("--show-reachable=yes"); my @valgrind_args; my $opt_valgrind_path; my $opt_callgrind; +my %mysqld_logs; my $opt_debug_sync_timeout= 300; # Default timeout for WAIT_FOR actions. our $opt_warnings= 1; @@ -741,6 +742,9 @@ sub run_worker ($) { elsif ($line eq 'BYE'){ mtr_report("Server said BYE"); stop_all_servers($opt_shutdown_timeout); + if ($opt_valgrind_mysqld) { + valgrind_exit_reports(); + } exit(0); } else { @@ -1340,8 +1344,7 @@ sub command_line_setup { push(@valgrind_args, @default_valgrind_args) unless @valgrind_args; - # Make valgrind run in quiet mode so it only print errors - push(@valgrind_args, "--quiet" ); + # Don't add --quiet; you will loose the summary reports. mtr_report("Running valgrind with options \"", join(" ", @valgrind_args), "\""); @@ -3690,7 +3693,7 @@ sub extract_warning_lines ($$) { ( qr/^Warning:|mysqld: Warning|\[Warning\]/, qr/^Error:|\[ERROR\]/, - qr/^==\d*==/, # valgrind errors + qr/^==\d+==\s+\S/, # valgrind errors qr/InnoDB: Warning|InnoDB: Error/, qr/^safe_mutex:|allocated at line/, qr/missing DBUG_RETURN/, @@ -4296,6 +4299,8 @@ sub mysqld_start ($$) { # see the exact location where valgrind complains $output= "$opt_vardir/log/".$mysqld->name().".trace"; } + # Remember this log file for valgrind error report search + $mysqld_logs{$output}= 1 if $opt_valgrind; if ( defined $exe ) { @@ -5148,6 +5153,66 @@ sub valgrind_arguments { } } +# +# Search server logs for valgrind reports printed at mysqld termination +# + +sub valgrind_exit_reports() { + foreach my $log_file (keys %mysqld_logs) + { + my @culprits= (); + my $valgrind_rep= ""; + my $found_report= 0; + my $err_in_report= 0; + + my $LOGF = IO::File->new($log_file) + or mtr_error("Could not open file '$log_file' for reading: $!"); + + while ( my $line = <$LOGF> ) + { + if ($line =~ /^CURRENT_TEST: (.+)$/) + { + my $testname= $1; + # If we have a report, report it if needed and start new list of tests + if ($found_report) + { + if ($err_in_report) + { + mtr_print ("Valgrind report from $log_file after tests:\n", + @culprits); + mtr_print_line(); + print ("$valgrind_rep\n"); + $err_in_report= 0; + } + # Make ready to collect new report + @culprits= (); + $found_report= 0; + $valgrind_rep= ""; + } + push (@culprits, $testname); + next; + } + # This line marks the start of a valgrind report + $found_report= 1 if $line =~ /ERROR SUMMARY:/; + + if ($found_report) { + $line=~ s/^==\d+== //; + $valgrind_rep .= $line; + $err_in_report= 1 if $line =~ /ERROR SUMMARY: [1-9]/; + $err_in_report= 1 if $line =~ /definitely lost: [1-9]/; + $err_in_report= 1 if $line =~ /possibly lost: [1-9]/; + } + } + + $LOGF= undef; + + if ($err_in_report) { + mtr_print ("Valgrind report from $log_file after tests:\n", @culprits); + mtr_print_line(); + print ("$valgrind_rep\n"); + } + } +} # # Usage From 1b2fe5f257d8505463a8631fb1e76962f30a89b1 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 13 Nov 2009 13:37:13 +0100 Subject: [PATCH 013/466] fix tree name in bzr config file --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 38fc10fed70..61347f93817 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-5.5-next-mr-cmake" +tree_name = "mysql-next-mr-cmake" From 9edb94c69e4f77d29dddc9fde49c4b5edf7e129f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 14 Nov 2009 14:29:14 +0100 Subject: [PATCH 014/466] fix make install/ mysqld_safe --- CMakeLists.txt | 2 +- scripts/CMakeLists.txt | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d55d115cff..4ee7aa414e3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,7 +134,7 @@ IF(WIN32) SET(DEFAULT_MYSQL_HOME "C:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}/" ) SET(SHAREDIR share) ELSE() - SET(DEFAULT_MYSQL_HOME ${CMAKE_INSTALL_PREFIX}/mysql/) + SET(DEFAULT_MYSQL_HOME ${CMAKE_INSTALL_PREFIX}) SET(SHAREDIR ${DEFAULT_MYSQL_HOME}share) ENDIF() diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index de3e4c661f6..0403a1bb5ae 100755 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -55,7 +55,12 @@ ENDIF() ENDIF(UNIX) - +# some scripts use @TARGET_LINUX@ +IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + SET(TARGET_LINUX 1) +ELSE() + SET(TARGET_LINUX 0) +ENDIF() # Build comp_sql - used for embedding SQL in C or C++ programs IF(NOT CMAKE_CROSSCOMPILING) ADD_EXECUTABLE(comp_sql comp_sql.c) @@ -107,7 +112,7 @@ SET(LDFLAGS "${CMAKE_SHARED_LIBRARY_LINK_FLAGS}") IF(WIN32) SET(prefix "${CMAKE_INSTALL_PREFIX}/MySQL Server ${MYSQL_BASE_VERSION}") ELSE() - set(prefix "${CMAKE_INSTALL_PREFIX}/mysql") + set(prefix "${CMAKE_INSTALL_PREFIX}") ENDIF() SET(sysconfdir ${prefix}) From 6edaebbf57bb2722b925883e705c167b09beceef Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 16 Nov 2009 11:18:16 +0100 Subject: [PATCH 015/466] Bug #48795 Valgrind summary from tests where server has been restarted cause test failure Seen in a few tests after 43418 Add code in extract_warning_lines() to skip this part --- mysql-test/mysql-test-run.pl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4ac3beff0a6..4501afa8ee7 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3700,9 +3700,17 @@ sub extract_warning_lines ($$) { qr/Attempting backtrace/, qr/Assertion .* failed/, ); + my $skip_valgrind= 0; foreach my $line ( @lines ) { + if ($opt_valgrind_mysqld) { + # Skip valgrind summary from tests where server has been restarted + # Should this contain memory leaks, the final report will find it + $skip_valgrind= 1 if $line =~ /^==\d+== ERROR SUMMARY:/; + $skip_valgrind= 0 unless $line =~ /^==\d+==/; + next if $skip_valgrind; + } foreach my $pat ( @patterns ) { if ( $line =~ /$pat/ ) From fbd542013fc72aa6b681d656a782615514f9a40a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 16 Nov 2009 14:45:06 +0100 Subject: [PATCH 016/466] Fixes for cmake2.8 --- cmake/create_initial_db.cmake.in | 23 ++++++++++------------- configure.cmake | 3 ++- scripts/CMakeLists.txt | 15 +++++++-------- sql/CMakeLists.txt | 7 +++++-- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/cmake/create_initial_db.cmake.in b/cmake/create_initial_db.cmake.in index 92ebc72d425..649d96a6627 100644 --- a/cmake/create_initial_db.cmake.in +++ b/cmake/create_initial_db.cmake.in @@ -19,16 +19,14 @@ SET(CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@") SET(MYSQLD_EXECUTABLE "@MYSQLD_EXECUTABLE@") SET(CMAKE_CFG_INTDIR "@CMAKE_CFG_INTDIR@") SET(WIN32 "@WIN32@") -SET ($ENV{VS_UNICODE_OUTPUT}) -IF(CMAKE_CFG_INTDIR) - IF(CONFIG) - MESSAGE(${CONFIG}) - #Resolve build configuration variables - STRING(REPLACE "${CMAKE_CFG_INTDIR}" ${CONFIG} MYSQLD_EXECUTABLE - "${MYSQLD_EXECUTABLE}") - ELSE() - MESSAGE(FATAL_ERROR "Please provide CONFIG parameter to the script") - ENDIF() +# Force Visual Studio to output to stdout +IF(ENV{VS_UNICODE_OUTPUT}) + SET ($ENV{VS_UNICODE_OUTPUT}) +ENDIF() +IF(CMAKE_CFG_INTDIR AND CONFIG) + #Resolve build configuration variables + STRING(REPLACE "${CMAKE_CFG_INTDIR}" ${CONFIG} MYSQLD_EXECUTABLE + "${MYSQLD_EXECUTABLE}") ENDIF() # Create bootstrapper SQL script @@ -44,9 +42,8 @@ FOREACH(FILENAME mysql_system_tables.sql mysql_system_tables_data.sql ENDFOREACH() -MAKE_DIRECTORY(data) -FILE(REMOVE_RECURSE data/mysql) -MAKE_DIRECTORY(data/mysql) +FILE(REMOVE_RECURSE mysql) +MAKE_DIRECTORY(mysql) IF(WIN32) SET(CONSOLE --console) ENDIF() diff --git a/configure.cmake b/configure.cmake index aff4c1403f9..b35548b2e2f 100644 --- a/configure.cmake +++ b/configure.cmake @@ -209,7 +209,8 @@ IF(WIN32) ADD_DEFINITIONS("-DWIN32_LEAN_AND_MEAN") IF (MSVC_VERSION GREATER 1400) # Speed up multiprocessor build - ADD_DEFINITIONS("/MP") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") ENDIF() # default to x86 platform. We'll check for X64 in a bit diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 0403a1bb5ae..8d246e6a38a 100755 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -76,13 +76,12 @@ SET(FIX_PRIVILEGES_SQL ) # Build mysql_fix_privilege_tables.sql (concatenate 2 sql scripts) -ADD_CUSTOM_COMMAND( - OUTPUT ${FIX_PRIVILEGES_SQL} - COMMAND ${CMAKE_COMMAND} "-DIN=${FIX_PRIVS_IN}" "-DOUT=${FIX_PRIVILEGES_SQL}" - -P "${CMAKE_SOURCE_DIR}/cmake/cat.cmake" - VERBATIM - DEPENDS ${FIX_PRIVS_IN} -) +FILE(WRITE ${FIX_PRIVILEGES_SQL} "") +FOREACH(FILENAME ${FIX_PRIVS_IN}) + FILE(READ "${FILENAME}" CONTENTS) + FILE(APPEND ${FIX_PRIVILEGES_SQL} "${CONTENTS}") +ENDFOREACH() + # Build mysql_fix_privilege_tables.c ADD_CUSTOM_COMMAND( @@ -91,7 +90,7 @@ ADD_CUSTOM_COMMAND( mysql_fix_privilege_tables mysql_fix_privilege_tables.sql mysql_fix_privilege_tables_sql.c - DEPENDS comp_sql ${CMAKE_CURRENT_BINARY_DIR}/mysql_fix_privilege_tables.sql + DEPENDS comp_sql WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 336f94c0718..c3aadc8def5 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -232,11 +232,14 @@ IF(WIN32 AND MYSQLD_EXECUTABLE) @ONLY ) + IF(MSVC_IDE OR CMAKE_GENERATOR MATCHES "Xcode") + SET (CONFIG_PARAM -DCONFIG=${CMAKE_CFG_INTDIR}) + ENDIF() MAKE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR}/data) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data/mysql/user.frm - COMMAND ${CMAKE_COMMAND} - -DCONFIG=${CMAKE_CFG_INTDIR} + COMMAND ${CMAKE_COMMAND} + ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data DEPENDS mysqld From f57dda3babf5b8e821b09f34f5146080ff32f9eb Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 16 Nov 2009 14:46:33 +0100 Subject: [PATCH 017/466] minor fix of a valgrind suppress pattern --- mysql-test/include/mtr_warnings.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index 0176c1800e8..7502470979e 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -181,7 +181,7 @@ INSERT INTO global_suppressions VALUES ("==[0-9]*== Using"), ("==[0-9]*== For more details"), /* This comes with innodb plugin tests */ - ("==[0-9]*== Warning: set address range perms: large range .* .defined."), + ("==[0-9]*== Warning: set address range perms: large range"), ("THE_LAST_SUPPRESSION")|| From c982aaa38c3ebbc63f641bafc34a9229a6aae36a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 17 Nov 2009 04:45:11 +0100 Subject: [PATCH 018/466] Support out of source build with the wrapper configure.pl script --- cmake/configure.pl | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/cmake/configure.pl b/cmake/configure.pl index ee7846b9c36..3768b046530 100644 --- a/cmake/configure.pl +++ b/cmake/configure.pl @@ -1,22 +1,35 @@ #!/usr/bin/perl -use Switch; +use strict; +use Cwd 'abs_path'; +use File::Basename; -my $cmakeargs=""; -foreach $option (@ARGV) +my $cmakeargs = ""; + +# Find source root directory +# Assume this script is in /cmake +my $srcdir = dirname(dirname(abs_path($0))); + +foreach my $option (@ARGV) { + if (substr ($option, 0, 2) == "--") { $option = substr($option, 2); } + if($option =~ /srcdir/) + { + $srcdir = substr($option,7); + next; + } if($option =~ /help/) { - system("cmake -LH"); + system("cmake ${srcdir} -LH"); exit(0); } if($option =~ /with-plugins=/) { my @plugins= split(/,/, substr($option,13)); - foreach $p (@plugins) + foreach my $p (@plugins) { $p =~ s/-/_/g; $cmakeargs = $cmakeargs." -DWITH_".uc($p)."=1"; @@ -60,12 +73,12 @@ foreach $option (@ARGV) $cmakeargs = $cmakeargs." -DCMAKE_INSTALL_PREFIX=".$cmake_install_prefix; next; } - if ($options =~ /extra-charsets=all/) + if ($option =~ /extra-charsets=all/) { $cmakeargs = $cmakeargs." -DWITH_CHARSETS=all"; next; } - if ($options =~ /extra-charsets=complex/) + if ($option =~ /extra-charsets=complex/) { $cmakeargs = $cmakeargs." -DWITH_CHARSETS=complex"; next; @@ -74,6 +87,7 @@ foreach $option (@ARGV) $option =~ s/-/_/g; $cmakeargs = $cmakeargs." -D".$option."=1"; } -print("configure.pl : calling cmake . $cmakeargs\n"); -my $rc = system("cmake . $cmakeargs"); + +print("configure.pl : calling cmake $srcdir $cmakeargs\n"); +my $rc = system("cmake $srcdir $cmakeargs"); exit($rc); From 7516158138346f53348a112c84f5f8d52345f4c8 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 17 Nov 2009 09:36:09 +0100 Subject: [PATCH 019/466] mtr ignoring of skip-im incorrectly removed by backport of WL#4085 --- mysql-test/mysql-test-run.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 6a6be3b6f1e..aeda46e6c5b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -824,6 +824,7 @@ sub command_line_setup { 'combination=s' => \@opt_combinations, 'skip-combinations' => \&collect_option, 'experimental=s' => \$opt_experimental, + 'skip-im' => \&ignore_option, # Specify ports 'build-thread|mtr-build-thread=i' => \$opt_build_thread, From 2b0be38266d15515ed602148d05f400e41a7553a Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 17 Nov 2009 12:13:22 +0100 Subject: [PATCH 020/466] Bug #48806 mysql-test-run.pl --help should work even in the absence of binaries Searches for my_safe_process binary too early Put this into a sub() and call it after examining options (incl. --help) --- mysql-test/lib/My/SafeProcess.pm | 34 ++++++++++++++++++-------------- mysql-test/mysql-test-run.pl | 3 +++ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index ee7ed2471c7..a620a7c6c72 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -81,24 +81,28 @@ sub is_child { } -# Find the safe process binary or script my @safe_process_cmd; my $safe_kill; -if (IS_WIN32PERL or IS_CYGWIN){ - # Use my_safe_process.exe - my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], - "my_safe_process"); - push(@safe_process_cmd, $exe); - # Use my_safe_kill.exe - $safe_kill= my_find_bin(".", "lib/My/SafeProcess", "my_safe_kill"); -} -else -{ - # Use my_safe_process - my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], - "my_safe_process"); - push(@safe_process_cmd, $exe); +# Find the safe process binary or script +sub find_bin { + if (IS_WIN32PERL or IS_CYGWIN) + { + # Use my_safe_process.exe + my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], + "my_safe_process"); + push(@safe_process_cmd, $exe); + + # Use my_safe_kill.exe + $safe_kill= my_find_bin(".", "lib/My/SafeProcess", "my_safe_kill"); + } + else + { + # Use my_safe_process + my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], + "my_safe_process"); + push(@safe_process_cmd, $exe); + } } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4501afa8ee7..1184d994627 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -266,6 +266,9 @@ sub main { command_line_setup(); + # --help will not reach here, so now it's safe to assume we have binaries + My::SafeProcess::find_bin(); + if ( $opt_gcov ) { gcov_prepare($basedir); } From 54bdfab91e9e34a14d07c8d110dbb7d43f8a8d73 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 18 Nov 2009 10:27:43 +0100 Subject: [PATCH 021/466] Bug #48808 mysql-test-run.pl --debugger=devenv does not work Wrong argument order, fixed --- mysql-test/mysql-test-run.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4501afa8ee7..d07b976a26d 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -5086,9 +5086,9 @@ sub debugger_arguments { { # vc[express] /debugexe exe arg1 .. argn - # Add /debugexe and name of the exe before args - unshift(@$$args, "/debugexe"); + # Add name of the exe and /debugexe before args unshift(@$$args, "$$exe"); + unshift(@$$args, "/debugexe"); # Set exe to debuggername $$exe= $debugger; From 9d69adc9d61120ec786f266540eac6d2f025641a Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 19 Nov 2009 09:58:50 +0100 Subject: [PATCH 022/466] Bug #48367 MTR should react to server dying, rather than to resulting failure of mysqltest For some reason it usually picks up mysqltest Wait .1s and then see if a server has died Change from first commit: label on separate line --- mysql-test/mysql-test-run.pl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4501afa8ee7..e8da2c9b5c2 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3495,6 +3495,14 @@ sub run_testcase ($) { run_on_all($tinfo, "analyze-$analyze"); } + # Wait a bit and see if a server died, if so report that instead + mtr_milli_sleep(100); + my $srvproc= My::SafeProcess::check_any(); + if ($srvproc && grep($srvproc eq $_, started(all_servers()))) { + $proc= $srvproc; + goto SRVDIED; + } + # Test case failure reported by mysqltest report_failure_and_restart($tinfo); } @@ -3520,6 +3528,7 @@ sub run_testcase ($) { # ---------------------------------------------------- # Check if it was an expected crash # ---------------------------------------------------- + SRVDIED: my $check_crash = check_expected_crash_and_restart($proc); if ($check_crash) { From 91bb19f44ecf803c8411741a100bf1af80e7172f Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 19 Nov 2009 10:10:21 +0100 Subject: [PATCH 023/466] Bug #35543 mysqlbinlog.cc does not properly work with tmp files mtr patch ported to v2 --- mysql-test/mysql-test-run.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4501afa8ee7..bd11ffde5fe 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2511,6 +2511,7 @@ sub create_config_file_for_extern { # binlog reads from [client] and [mysqlbinlog] [mysqlbinlog] character-sets-dir= $path_charsetsdir +local-load= $opt_tmpdir # mysql_fix_privilege_tables.sh don't read from [client] [mysql_fix_privilege_tables] From 94f138eb5b42eeda7a732eb78319085a9fd6853f Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 19 Nov 2009 13:19:11 +0100 Subject: [PATCH 024/466] Bug #48683 mysql-stress-test fails with "Value ... invalid for option abort-on-error" Add =1 to --abort-on-error argument --- mysql-test/lib/mtr_stress.pl | 2 +- mysql-test/lib/v1/mtr_stress.pl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/lib/mtr_stress.pl b/mysql-test/lib/mtr_stress.pl index cd5c7b0dbb7..702bc178ae5 100644 --- a/mysql-test/lib/mtr_stress.pl +++ b/mysql-test/lib/mtr_stress.pl @@ -150,7 +150,7 @@ sub run_stress_test () mtr_add_arg($args, "--verbose"); mtr_add_arg($args, "--cleanup"); mtr_add_arg($args, "--log-error-details"); - mtr_add_arg($args, "--abort-on-error"); + mtr_add_arg($args, "--abort-on-error=1"); if ( $::opt_stress_init_file ) { diff --git a/mysql-test/lib/v1/mtr_stress.pl b/mysql-test/lib/v1/mtr_stress.pl index 93b06b32c5f..40800c9729b 100644 --- a/mysql-test/lib/v1/mtr_stress.pl +++ b/mysql-test/lib/v1/mtr_stress.pl @@ -150,7 +150,7 @@ sub run_stress_test () mtr_add_arg($args, "--verbose"); mtr_add_arg($args, "--cleanup"); mtr_add_arg($args, "--log-error-details"); - mtr_add_arg($args, "--abort-on-error"); + mtr_add_arg($args, "--abort-on-error=1"); if ( $::opt_stress_init_file ) { From eb53bc5a2af3963a4b4cfe6e569e8ad074469d01 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 21 Nov 2009 01:56:58 +0100 Subject: [PATCH 025/466] Write some helpful messages to assist user, when prerequisite software is not found (ncurses-devel or bison) --- cmake/bison.cmake | 16 +++++++++++++--- cmake/readline.cmake | 9 ++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/cmake/bison.cmake b/cmake/bison.cmake index b1c2d7393fa..9e01ef3d246 100644 --- a/cmake/bison.cmake +++ b/cmake/bison.cmake @@ -37,13 +37,13 @@ MACRO (RUN_BISON input_yy output_cc output_h) IF(BISON_TOO_OLD) IF(EXISTS ${output_cc} AND EXISTS ${output_h}) SET(BISON_USABLE FALSE) - ENDIF() + ENDIF() ENDIF() IF(BISON_USABLE) ADD_CUSTOM_COMMAND( OUTPUT ${output_cc} ${output_h} - COMMAND bison -y -p MYSQL + COMMAND ${BISON_EXECUTABLE} -y -p MYSQL --output=${output_cc} --defines=${output_h} ${input_yy} @@ -58,7 +58,17 @@ MACRO (RUN_BISON input_yy output_cc output_h) ENDIF() ELSE() # Output files are missing, bail out. - MESSAGE(FATAL_ERROR "Please install bison.") + SET(ERRMSG + "Bison (GNU parser generator) is required to build MySQL." + "Please install bison." + ) + IF(WIN32) + SET(ERRMSG ${ERRMSG} + "You can download bison from http://gnuwin32.sourceforge.net/packages/bison.htm " + "Choose 'Complete package, except sources' installation. We recommend to " + "install bison into a directory without spaces, e.g C:\\GnuWin32.") + ENDIF() + MESSAGE(FATAL_ERROR ${ERRMSG}) ENDIF() ENDIF() ENDMACRO() diff --git a/cmake/readline.cmake b/cmake/readline.cmake index 7661f12cce2..44be0c2196e 100644 --- a/cmake/readline.cmake +++ b/cmake/readline.cmake @@ -82,7 +82,14 @@ MACRO (FIND_CURSES) INCLUDE (FindCurses) MARK_AS_ADVANCED(CURSES_CURSES_H_PATH CURSES_FORM_LIBRARY CURSES_HAVE_CURSES_H) IF(NOT CURSES_FOUND) - MESSAGE(FATAL_ERROR "curses library not found") + SET(ERRORMSG "Curses library not found. Please install appropriate package, + remove CMakeCache.txt and rerun cmake.") + IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + SET(ERRORMSG ${ERRORMSG} + "On Debian/Ubuntu, package name is libncurses5-dev, on Redhat and derivates " + "it is ncurses-devel.") + ENDIF() + MESSAGE(FATAL_ERROR ${ERRORMSG}) ENDIF() IF(CURSES_HAVE_CURSES_H) From cd0b755c625bfff265af1b8977424a988f945122 Mon Sep 17 00:00:00 2001 From: vvaintroub Date: Sat, 21 Nov 2009 18:40:03 +0100 Subject: [PATCH 026/466] fix test-force target on VS2010 --- mysql-test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/CMakeLists.txt b/mysql-test/CMakeLists.txt index b3afa4f8f98..c91940523b4 100644 --- a/mysql-test/CMakeLists.txt +++ b/mysql-test/CMakeLists.txt @@ -48,9 +48,9 @@ IF(UNIX) ENDIF() IF(CMAKE_GENERATOR MATCHES "Visual Studio") - SET(SETCONFIG_COMMAND set MTR_VS_CONFIG=$(OutDir)) + SET(SETCONFIG_COMMAND set MTR_VS_CONFIG=${CMAKE_CFG_INTDIR}) ELSEIF(CMAKE_GENERATOR MATCHES "Xcode") - SET(SETCONFIG_COMMAND export MTR_VS_CONFIG=$(CONFIGURATION)) + SET(SETCONFIG_COMMAND export MTR_VS_CONFIG=${CMAKE_CFG_INTDIR}) ELSE() SET(SETCONFIG_COMMAND echo Running tests) ENDIF() From 8e04f4c73ca7938e97b24f6f3a60dc7c22d29352 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 24 Nov 2009 09:12:48 +0100 Subject: [PATCH 027/466] Bug #47978 timer : expired after 90 seconds Problems occur after killing threads on Windows Get rid of the timeout threads, implement simple timer in wait_any_timeout() --- mysql-test/lib/My/SafeProcess.pm | 91 ++++++++++++-------------------- mysql-test/lib/mtr_misc.pl | 13 ++++- mysql-test/mysql-test-run.pl | 79 ++++++++++----------------- 3 files changed, 72 insertions(+), 111 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index a620a7c6c72..bfcad910a16 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -187,63 +187,6 @@ sub run { return $proc->exit_status(); } -# -# Start a process that returns after "duration" seconds -# or when it's parent process does not exist anymore -# -sub timer { - my $class= shift; - my $duration= shift or croak "duration required"; - my $parent_pid= $$; - - my $pid= My::SafeProcess::Base::_safe_fork(); - if ($pid){ - # Parent - my $proc= bless - ({ - SAFE_PID => $pid, - SAFE_NAME => "timer", - PARENT => $$, - }, $class); - - # Put the new process in list of running - $running{$pid}= $proc; - return $proc; - } - - # Child, install signal handlers and sleep for "duration" - $SIG{INT}= 'IGNORE'; - - $SIG{TERM}= sub { - #print STDERR "timer $$: woken up, exiting!\n"; - exit(0); - }; - - $0= "safe_timer($duration)"; - - if (IS_WIN32PERL){ - # Just a thread in same process - sleep($duration); - print STDERR "timer $$: expired after $duration seconds\n"; - exit(0); - } - - my $count_down= $duration; - while($count_down--){ - - # Check that parent is still alive - if (kill(0, $parent_pid) == 0){ - #print STDERR "timer $$: parent gone, exiting!\n"; - exit(0); - } - - sleep(1); - } - print STDERR "timer $$: expired after $duration seconds\n"; - exit(0); -} - - # # Shutdown process nicely, and wait for shutdown_timeout seconds # If processes hasn't shutdown, kill them hard and wait for return @@ -541,6 +484,40 @@ sub wait_any { } +# +# Wait for any process to exit, or a timeout +# +# Returns a reference to the SafeProcess that +# exited or a pseudo-process with $proc->{timeout} == 1 +# + +sub wait_any_timeout { + my $class= shift; + my $timeout= shift; + my $proc; + my $millis=10; + + do { + ::mtr_milli_sleep($millis); + # Slowly increse interval up to max. 1 second + $millis++ if $millis < 1000; + # Return a "fake" process for timeout + if (::has_expired($timeout)) { + $proc= bless + ({ + SAFE_PID => 0, + SAFE_NAME => "timer", + timeout => 1, + }, $class); + } else { + $proc= check_any(); + } + } while (! $proc); + + return $proc; +} + + # # Wait for all processes to exit # diff --git a/mysql-test/lib/mtr_misc.pl b/mysql-test/lib/mtr_misc.pl index 658eb270535..97eb693b52e 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -30,7 +30,9 @@ sub mtr_script_exists(@); sub mtr_file_exists(@); sub mtr_exe_exists(@); sub mtr_exe_maybe_exists(@); - +sub mtr_milli_sleep($); +sub start_timer($); +sub has_expired($); ############################################################################## # @@ -167,11 +169,18 @@ sub mtr_exe_exists (@) { } -sub mtr_milli_sleep { +sub mtr_milli_sleep ($) { die "usage: mtr_milli_sleep(milliseconds)" unless @_ == 1; my ($millis)= @_; select(undef, undef, undef, ($millis/1000)); } +# Simple functions to start and check timers (have to be actively polled) +# Timer can be "killed" by setting it to 0 + +sub start_timer ($) { return time + $_[0]; } + +sub has_expired ($) { return $_[0] && time gt $_[0]; } + 1; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index ae9674bbde5..2147b4ae7a7 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -439,7 +439,7 @@ sub run_test_server ($$$) { my $result; my $exe_mysqld= find_mysqld($basedir) || ""; # Used as hint to CoreDump - my $suite_timeout_proc= My::SafeProcess->timer(suite_timeout()); + my $suite_timeout= start_timer(suite_timeout()); my $s= IO::Select->new(); $s->add($server); @@ -460,7 +460,6 @@ sub run_test_server ($$$) { mtr_verbose("Child closed socket"); $s->remove($sock); if (--$childs == 0){ - $suite_timeout_proc->kill(); return $completed; } next; @@ -529,13 +528,11 @@ sub run_test_server ($$$) { if ( !$opt_force ) { # Test has failed, force is off - $suite_timeout_proc->kill(); push(@$completed, $result); return $completed; } elsif ($opt_max_test_fail > 0 and $num_failed_test >= $opt_max_test_fail) { - $suite_timeout_proc->kill(); push(@$completed, $result); mtr_report_stats("Too many failed", $completed, 1); mtr_report("Too many tests($num_failed_test) failed!", @@ -667,7 +664,7 @@ sub run_test_server ($$$) { # ---------------------------------------------------- # Check if test suite timer expired # ---------------------------------------------------- - if ( ! $suite_timeout_proc->wait_one(0) ) + if ( has_expired($suite_timeout) ) { mtr_report_stats("Timeout", $completed, 1); mtr_report("Test suite timeout! Terminating..."); @@ -2944,11 +2941,11 @@ sub check_testcase($$) # Return immediately if no check proceess was started return 0 unless ( keys %started ); - my $timeout_proc= My::SafeProcess->timer(check_timeout()); + my $timeout= start_timer(check_timeout()); while (1){ my $result; - my $proc= My::SafeProcess->wait_any(); + my $proc= My::SafeProcess->wait_any_timeout($timeout); mtr_report("Got $proc"); if ( delete $started{$proc->pid()} ) { @@ -2972,9 +2969,6 @@ sub check_testcase($$) if ( keys(%started) == 0){ # All checks completed - - $timeout_proc->kill(); - return 0; } # Wait for next process to exit @@ -3015,10 +3009,9 @@ test case was executed:\n"; } } - elsif ( $proc eq $timeout_proc ) { - $tinfo->{comment}.= "Timeout $timeout_proc for ". - "'check-testcase' expired after ".check_timeout(). - " seconds"; + elsif ( $proc->{timeout} ) { + $tinfo->{comment}.= "Timeout for 'check-testcase' expired after " + .check_timeout()." seconds"; $result= 4; } else { @@ -3033,8 +3026,6 @@ test case was executed:\n"; # Kill any check processes still running map($_->kill(), values(%started)); - $timeout_proc->kill(); - return $result; } @@ -3106,11 +3097,11 @@ sub run_on_all($$) # Return immediately if no check proceess was started return 0 unless ( keys %started ); - my $timeout_proc= My::SafeProcess->timer(check_timeout()); + my $timeout= start_timer(check_timeout()); while (1){ my $result; - my $proc= My::SafeProcess->wait_any(); + my $proc= My::SafeProcess->wait_any_timeout($timeout); mtr_report("Got $proc"); if ( delete $started{$proc->pid()} ) { @@ -3129,17 +3120,15 @@ sub run_on_all($$) if ( keys(%started) == 0){ # All completed - $timeout_proc->kill(); return 0; } # Wait for next process to exit next; } - elsif ( $proc eq $timeout_proc ) { - $tinfo->{comment}.= "Timeout $timeout_proc for '$run' ". - "expired after ". check_timeout(). - " seconds"; + elsif ($proc->{timeout}) { + $tinfo->{comment}.= "Timeout for '$run' expired after " + .check_timeout()." seconds"; } else { # Unknown process returned, most likley a crash, abort everything @@ -3151,8 +3140,6 @@ sub run_on_all($$) # Kill any check processes still running map($_->kill(), values(%started)); - $timeout_proc->kill(); - return 1; } mtr_error("INTERNAL_ERROR: run_on_all"); @@ -3382,7 +3369,7 @@ sub run_testcase ($) { } } - my $test_timeout_proc= My::SafeProcess->timer(testcase_timeout()); + my $test_timeout= start_timer(testcase_timeout()); do_before_run_mysqltest($tinfo); @@ -3390,9 +3377,6 @@ sub run_testcase ($) { # Failed to record state of server or server crashed report_failure_and_restart($tinfo); - # Stop the test case timer - $test_timeout_proc->kill(); - return 1; } @@ -3410,20 +3394,20 @@ sub run_testcase ($) { if ($proc) { mtr_verbose ("Found exited process $proc"); - # If that was the timeout, cancel waiting - if ( $proc eq $test_timeout_proc ) - { - $keep_waiting_proc = 0; - } } else { $proc = $keep_waiting_proc; + # Also check if timer has expired, if so cancel waiting + if ( has_expired($test_timeout) ) + { + $keep_waiting_proc = 0; + } } } - else + if (! $keep_waiting_proc) { - $proc= My::SafeProcess->wait_any(); + $proc= My::SafeProcess->wait_any_timeout($test_timeout); } # Will be restored if we need to keep waiting @@ -3440,9 +3424,6 @@ sub run_testcase ($) { # ---------------------------------------------------- if ($proc eq $test) { - # Stop the test case timer - $test_timeout_proc->kill(); - my $res= $test->exit_status(); if ($res == 0 and $opt_warnings and check_warnings($tinfo) ) @@ -3545,7 +3526,7 @@ sub run_testcase ($) { # ---------------------------------------------------- # Stop the test case timer # ---------------------------------------------------- - $test_timeout_proc->kill(); + $test_timeout= 0; # ---------------------------------------------------- # Check if it was a server that died @@ -3584,7 +3565,7 @@ sub run_testcase ($) { # ---------------------------------------------------- # Check if testcase timer expired # ---------------------------------------------------- - if ( $proc eq $test_timeout_proc ) + if ( $proc->{timeout} ) { my $log_file_name= $opt_vardir."/log/".$tinfo->{shortname}.".log"; $tinfo->{comment}= @@ -3825,11 +3806,11 @@ sub check_warnings ($) { # Return immediately if no check proceess was started return 0 unless ( keys %started ); - my $timeout_proc= My::SafeProcess->timer(check_timeout()); + my $timeout= start_timer(check_timeout()); while (1){ my $result= 0; - my $proc= My::SafeProcess->wait_any(); + my $proc= My::SafeProcess->wait_any_timeout($timeout); mtr_report("Got $proc"); if ( delete $started{$proc->pid()} ) { @@ -3858,9 +3839,6 @@ sub check_warnings ($) { if ( keys(%started) == 0){ # All checks completed - - $timeout_proc->kill(); - return $result; } # Wait for next process to exit @@ -3877,10 +3855,9 @@ sub check_warnings ($) { $result= 2; } } - elsif ( $proc eq $timeout_proc ) { - $tinfo->{comment}.= "Timeout $timeout_proc for ". - "'check warnings' expired after ".check_timeout(). - " seconds"; + elsif ( $proc->{timeout} ) { + $tinfo->{comment}.= "Timeout for 'check warnings' expired after " + .check_timeout()." seconds"; $result= 4; } else { @@ -3894,8 +3871,6 @@ sub check_warnings ($) { # Kill any check processes still running map($_->kill(), values(%started)); - $timeout_proc->kill(); - return $result; } From afe4ee1e17edd8edbdc2d142da8ada747e759ea9 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 24 Nov 2009 23:15:47 +0000 Subject: [PATCH 028/466] implement convenience libraries --- CMakeLists.txt | 2 +- cmake/Makefile.am | 3 +- cmake/character_sets.cmake | 8 +- cmake/configurable_file_content.in | 1 + cmake/install_macros.cmake | 8 +- cmake/libutils.cmake | 291 ++++++++++++++++++++++++++++ cmake/misc.cmake | 128 ------------ cmake/plugin.cmake | 27 ++- dbug/CMakeLists.txt | 3 +- extra/CMakeLists.txt | 2 +- extra/yassl/CMakeLists.txt | 4 +- extra/yassl/taocrypt/CMakeLists.txt | 6 +- libmysql/CMakeLists.txt | 187 +++++++++++------- libmysql/libmysql.def | 38 ---- libmysqld/CMakeLists.txt | 57 +++--- mysys/CMakeLists.txt | 3 +- regex/CMakeLists.txt | 4 +- strings/CMakeLists.txt | 4 +- vio/CMakeLists.txt | 5 +- zlib/CMakeLists.txt | 4 +- 20 files changed, 476 insertions(+), 309 deletions(-) create mode 100644 cmake/configurable_file_content.in create mode 100644 cmake/libutils.cmake delete mode 100644 cmake/misc.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ee7aa414e3..18c1fca68b7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ INCLUDE(cmake/zlib.cmake) INCLUDE(cmake/ssl.cmake) INCLUDE(cmake/readline.cmake) INCLUDE(cmake/mysql_version.cmake) -INCLUDE(cmake/misc.cmake) +INCLUDE(cmake/libutils.cmake) INCLUDE(cmake/dtrace.cmake) INCLUDE(cmake/plugin.cmake) INCLUDE(cmake/install_macros.cmake) diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 2326f787ab8..9148334e140 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -1,5 +1,6 @@ EXTRA_DIST = \ cat.cmake \ + ConfigurableFileContent.in \ check_minimal_version.cmake \ create_initial_db.cmake.in \ make_dist.cmake.in \ @@ -8,7 +9,7 @@ EXTRA_DIST = \ bison.cmake \ configure.pl \ character_sets.cmake \ - misc.cmake \ + libutils.cmake \ readline.cmake \ mysql_version.cmake \ install_macros.cmake \ diff --git a/cmake/character_sets.cmake b/cmake/character_sets.cmake index c21b6c60113..82de1f27d96 100644 --- a/cmake/character_sets.cmake +++ b/cmake/character_sets.cmake @@ -33,13 +33,9 @@ latin1 latin2 latin5 latin7 macce macroman sjis swe7 tis620 ucs2 ujis utf8 utf8mb3 utf16 utf32) -IF(WIN32) - SET (EXTRA_CHARSETS "all") -ELSE() - SET (EXTRA_CHARSETS "none") -ENDIF() +SET (EXTRA_CHARSETS "all") SET(WITH_EXTRA_CHARSETS ${EXTRA_CHARSETS} CACHE - STRING "Options are: none, complex,all") + STRING "Options are: none, complex, all") IF(WITH_EXTRA_CHARSETS MATCHES "complex") diff --git a/cmake/configurable_file_content.in b/cmake/configurable_file_content.in new file mode 100644 index 00000000000..df2c382e9b3 --- /dev/null +++ b/cmake/configurable_file_content.in @@ -0,0 +1 @@ +@CMAKE_CONFIGURABLE_FILE_CONTENT@ diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 4fc6db6b9b7..81fc44c6a15 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -19,13 +19,13 @@ MACRO (INSTALL_DEBUG_SYMBOLS targets) GET_TARGET_PROPERTY(location ${target} LOCATION) GET_TARGET_PROPERTY(type ${target} TYPE) IF(NOT INSTALL_LOCATION) - IF(type MATCHES "STATIC_LIBRARY" OR type MATCHES "MODULE_LIBRARY" OR type MATCHES "SHARED_LIBRARY") + IF(type MATCHES "STATIC_LIBRARY" OR type MATCHES "MODULE_LIBRARY" OR type MATCHES "SHARED_LIBRARY") SET(INSTALL_LOCATION "lib") - ELSEIF(type MATCHES "EXECUTABLE") + ELSEIF(type MATCHES "EXECUTABLE") SET(INSTALL_LOCATION "bin") - ELSE() + ELSE() MESSAGE(FATAL_ERROR "cannot determine type of ${target}. Don't now where to install") - ENDIF() + ENDIF() ENDIF() STRING(REPLACE ".exe" ".pdb" pdb_location ${location}) STRING(REPLACE ".dll" ".pdb" pdb_location ${pdb_location}) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake new file mode 100644 index 00000000000..ea8f809de9f --- /dev/null +++ b/cmake/libutils.cmake @@ -0,0 +1,291 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +# This file exports macros that emulate some functionality found in GNU libtool +# on Unix systems. One such feature is convenience libraries. In this context, +# convenience library is a static library that can be linked to shared library +# On systems that force position-independent code, linking into shared library +# normally requires compilation with a special flag (often -fPIC). To enable +# linking static libraries to shared, we compile source files that come into +# static library with the PIC flag (${CMAKE_SHARED_LIBRARY_C_FLAGS} in CMake) +# Some systems, like Windows or OSX do not need special compilation (Windows +# never uses PIC and OSX always uses it). +# +# The intention behind convenience libraries is simplify the build and to reduce +# excessive recompiles. + +# Except for convenience libraries, this file provides macros to merge static +# libraries (we need it for mysqlclient) and to create shared library out of +# convenience libraries(again, for mysqlclient) + +# Following macros are exported +# - ADD_CONVENIENCE_LIBRARY(target source1...sourceN) +# This macro creates convenience library. The functionality is similar to +# ADD_LIBRARY(target STATIC source1...sourceN), the difference is that resulting +# library can always be linked to shared library +# +# - MERGE_LIBRARIES(target [STATIC|SHARED|MODULE] [linklib1 .... linklibN] +# [EXPORTS exported_func1 .... exported_func_N] +# [OUTPUT_NAME output_name] +# This macro merges several static libraries into a single one or creates a shared +# library from several convenience libraries + +# Important global flags +# - WITH_PIC : If set, it is assumed that everything is compiled as position +# independent code (that is CFLAGS/CMAKE_C_FLAGS contain -fPIC or equivalent) +# If defined, ADD_CONVENIENCE_LIBRARY does not add PIC flag to compile flags +# +# - DISABLE_SHARED: If set, it is assumed that shared libraries are not produced +# during the build. ADD_CONVENIENCE_LIBRARY does not add anything to compile flags + + +GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +IF(NOT WIN32 AND NOT CYGWIN AND NOT APPLE AND NOT WITH_PIC AND NOT DISABLE_SHARED + AND CMAKE_SHARED_LIBRARY_C_FLAGS) + SET(_SKIP_PIC 1) +ENDIF() + +# CREATE_EXPORT_FILE (VAR target api_functions) +# Internal macro, used to create source file for shared libraries that +# otherwise consists entirely of "convenience" libraries. On Windows, +# also exports API functions as dllexport. On unix, creates a dummy file +# that references all exports and this prevents linker from creating an +# empty library(there are unportable alternatives, --whole-archive) +MACRO(CREATE_EXPORT_FILE VAR TARGET API_FUNCTIONS) + IF(WIN32) + SET(DUMMY ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_dummy.c) + SET(EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_exports.def) + CONFIGURE_FILE_CONTENT("" ${DUMMY}) + SET(CONTENT "EXPORTS\n") + FOREACH(FUNC ${API_FUNCTIONS}) + SET(CONTENT "${CONTENT} ${FUNC}\n") + ENDFOREACH() + CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS}) + SET(${VAR} ${DUMMY} ${EXPORTS}) + ELSE() + SET(EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${target}_exports_file.cc) + SET(CONTENT) + FOREACH(FUNC ${API_FUNCTIONS}) + SET(CONTENT "${CONTENT} extern void* ${FUNC}\;\n") + ENDFOREACH() + SET(CONTENT "${CONTENT} void *${TARGET}_api_funcs[] = {\n") + FOREACH(FUNC ${API_FUNCTIONS}) + SET(CONTENT "${CONTENT} &${FUNC},\n") + ENDFOREACH() + SET(CONTENT "${CONTENT} (void *)0\n}\;") + CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS}) + SET(${VAR} ${EXPORTS}) + ENDIF() +ENDMACRO() + + +# MYSQL_ADD_CONVENIENCE_LIBRARY(name source1...sourceN) +# Create static library that can be linked to shared library. +# On systems that force position-independent code, adds -fPIC or +# equivalent flag to compile flags. +MACRO(ADD_CONVENIENCE_LIBRARY) + SET(TARGET ${ARGV0}) + SET(SOURCES ${ARGN}) + LIST(REMOVE_AT SOURCES 0) + ADD_LIBRARY(${TARGET} STATIC ${SOURCES}) + IF(NOT _SKIP_PIC) + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS + "${CMAKE_SHARED_LIBRARY_C_FLAGS}") + ENDIF() +ENDMACRO() + + +# Handy macro to parse macro arguments +MACRO(CMAKE_PARSE_ARGUMENTS prefix arg_names option_names) + SET(DEFAULT_ARGS) + FOREACH(arg_name ${arg_names}) + SET(${prefix}_${arg_name}) + ENDFOREACH(arg_name) + FOREACH(option ${option_names}) + SET(${prefix}_${option} FALSE) + ENDFOREACH(option) + + SET(current_arg_name DEFAULT_ARGS) + SET(current_arg_list) + FOREACH(arg ${ARGN}) + SET(larg_names ${arg_names}) + LIST(FIND larg_names "${arg}" is_arg_name) + IF (is_arg_name GREATER -1) + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name ${arg}) + SET(current_arg_list) + ELSE (is_arg_name GREATER -1) + SET(loption_names ${option_names}) + LIST(FIND loption_names "${arg}" is_option) + IF (is_option GREATER -1) + SET(${prefix}_${arg} TRUE) + ELSE (is_option GREATER -1) + SET(current_arg_list ${current_arg_list} ${arg}) + ENDIF (is_option GREATER -1) + ENDIF (is_arg_name GREATER -1) + ENDFOREACH(arg) + SET(${prefix}_${current_arg_name} ${current_arg_list}) +ENDMACRO() + +# Write content to file, using CONFIGURE_FILE +# The advantage compared to FILE(WRITE) is that timestamp +# does not change if file already has the same content +MACRO(CONFIGURE_FILE_CONTENT content file) + SET(CMAKE_CONFIGURABLE_FILE_CONTENT + "${content}\n") + CONFIGURE_FILE( + ${MYSQL_CMAKE_SCRIPT_DIR}/configurable_file_content.in + ${file} + @ONLY) +ENDMACRO() + +# Merge static libraries into a big static lib. The resulting library +# should not not have dependencies on other static libraries. +# We use it in MySQL to merge mysys,dbug,vio etc into mysqlclient + +MACRO(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE) + # To produce a library we need at least one source file. + # It is created by ADD_CUSTOM_COMMAND below and will helps + # also help to track dependencies. + SET(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_depends.c) + ADD_LIBRARY(${TARGET} STATIC ${SOURCE_FILE}) + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${OUTPUT_NAME}) + + FOREACH(LIB ${LIBS_TO_MERGE}) + GET_TARGET_PROPERTY(LIB_LOCATION ${LIB} LOCATION) + GET_TARGET_PROPERTY(LIB_TYPE ${LIB} TYPE) + IF(NOT LIB_LOCATION) + # 3rd party library like libz.so. Make sure that everything + # that links to our library links to this one as well. + TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) + ELSE() + # This is a target in current project + # (can be a static or shared lib) + IF(LIB_TYPE STREQUAL "STATIC_LIBRARY") + SET(STATIC_LIBS ${STATIC_LIBS} ${LIB_LOCATION}) + ADD_DEPENDENCIES(${TARGET} ${LIB}) + ELSE() + # This is a shared library our static lib depends on. + TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) + ENDIF() + ENDIF() + ENDFOREACH() + + # Make the generated dummy source file depended on all static input + # libs. If input lib changes,the source file is touched + # which causes the desired effect (relink). + ADD_CUSTOM_COMMAND( + OUTPUT ${SOURCE_FILE} + COMMAND ${CMAKE_COMMAND} -E touch ${SOURCE_FILE} + DEPENDS ${STATIC_LIBS}) + + IF(MSVC) + # To merge libs, just pass them to lib.exe command line. + SET(LINKER_EXTRA_FLAGS "") + FOREACH(LIB ${STATIC_LIBS}) + SET(LINKER_EXTRA_FLAGS "${LINKER_EXTRA_FLAGS} ${LIB}") + ENDFOREACH() + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES STATIC_LIBRARY_FLAGS + "${LINKER_EXTRA_FLAGS}") + ELSE() + GET_TARGET_PROPERTY(TARGET_LOCATION ${TARGET} LOCATION) + IF(APPLE) + # Use OSX's libtool to merge archives (ihandles universal + # binaries properly) + ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD + COMMAND rm ${TARGET_LOCATION} + COMMAND /usr/bin/libtool -static -o ${TARGET_LOCATION} + ${STATIC_LIBS} + ) + ELSE() + # Generic Unix, Cygwin or MinGW. In post-build step, call + # script, that extracts objects from archives with "ar x" + # and repacks them with "ar r" + SET(TARGET ${TARGET}) + CONFIGURE_FILE( + ${MYSQL_CMAKE_SCRIPT_DIR}/merge_archives_unix.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}.cmake + @ONLY + ) + ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD + COMMAND rm ${TARGET_LOCATION} + COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}.cmake + ) + ENDIF() + ENDIF() +ENDMACRO() + +# Create libs from libs. +# Merges static libraries, creates shared libraries out of convenience libraries. +# MYSQL_MERGE_LIBRARIES(target [STATIC|SHARED|MODULE] +# [linklib1 .... linklibN] +# [EXPORTS exported_func1 .... exportedFuncN] +# [OUTPUT_NAME output_name] +#) +MACRO(MERGE_LIBRARIES) + CMAKE_PARSE_ARGUMENTS(ARG + "EXPORTS;OUTPUT_NAME" + "STATIC;SHARED;MODULE" + ${ARGN} + ) + LIST(GET ARG_DEFAULT_ARGS 0 TARGET) + SET(LIBS ${ARG_DEFAULT_ARGS}) + LIST(REMOVE_AT LIBS 0) + IF(ARG_STATIC) + IF (NOT "${ARG_OUTPUT_NAME}") + SET(ARG_OUTPUT_NAME ${TARGET}) + ENDIF() + MERGE_STATIC_LIBS(${TARGET} ${ARG_OUTPUT_NAME} "${LIBS}") + ELSEIF(ARG_SHARED OR ARG_MODULE) + IF(ARG_SHARED) + SET(LIBTYPE SHARED) + ELSE() + SET(LIBTYPE MODULE) + ENDIF() + # check for non-PIC libraries + IF(NOT _SKIP_PIC) + FOREACH(LIB ${LIBS}) + GET_TARGET_PROPERTY(${LIB} TYPE LIBTYPE) + IF(LIBTYPE STREQUAL "STATIC_LIBRARY") + GET_TARGET_PROPERTY(LIB COMPILE_FLAGS LIB_COMPILE_FLAGS) + STRING(REPLACE "${CMAKE_SHARED_LIBRARY_C_FLAGS}" + "" LIB_COMPILE_FLAGS ${LIB_COMPILE_FLAG}) + IF(NOT LIB_COMPILE_FLAGS MATCHES "") + MESSAGE(FATAL_ERROR + "Attempted to link non-PIC static library ${LIB} to shared library ${TARGET}\n" + "Please use ADD_CONVENIENCE_LIBRARY, instead of ADD_LIBRARY for ${LIB}" + ) + ENDIF() + ENDIF() + ENDFOREACH() + ENDIF() + CREATE_EXPORT_FILE(SRC ${TARGET} "${EXPORTS}") + ADD_LIBRARY(${TARGET} SHARED ${SRC}) + TARGET_LINK_LIBRARIES(${TARGET} ${LIBS}) + IF(ARG_OUTPUT_NAME) + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME "${ARG_OUTPUT_NAME}") + ENDIF() + # Disallow undefined symbols in shared libraries, but allow for modules + # (they export from loading executable) + IF(LIBTYPE MATCHES "SHARED" AND CMAKE_SYSTEM_TYPE MATCHES "Linux") + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES LINK_FLAGS "-Wl,--no-undefined") + ENDIF() + ELSE() + MESSAGE(FATAL_ERROR "Unknown library type") + ENDIF() +ENDMACRO() + diff --git a/cmake/misc.cmake b/cmake/misc.cmake deleted file mode 100644 index c73e66ef3d7..00000000000 --- a/cmake/misc.cmake +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Merge static libraries. -MACRO(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE) - # To produce a library we need at least one source file. - # It is created by ADD_CUSTOM_COMMAND below and will helps - # also help to track dependencies. - SET(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_depends.c) - ADD_LIBRARY(${TARGET} STATIC ${SOURCE_FILE}) - SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${OUTPUT_NAME}) - - FOREACH(LIB ${LIBS_TO_MERGE}) - GET_TARGET_PROPERTY(LIB_LOCATION ${LIB} LOCATION) - GET_TARGET_PROPERTY(LIB_TYPE ${LIB} TYPE) - IF(NOT LIB_LOCATION) - # 3rd party library like libz.so. Make sure that everything - # that links to our library links to this one as well. - TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) - ELSE() - # This is a target in current project - # (can be a static or shared lib) - IF(LIB_TYPE STREQUAL "STATIC_LIBRARY") - SET(STATIC_LIBS ${STATIC_LIBS} ${LIB_LOCATION}) - ADD_DEPENDENCIES(${TARGET} ${LIB}) - ELSE() - # This is a shared library our static lib depends on. - TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) - ENDIF() - ENDIF() - ENDFOREACH() - - # Make the generated dummy source file depended on all static input - # libs. If input lib changes,the source file is touched - # which causes the desired effect (relink). - ADD_CUSTOM_COMMAND( - OUTPUT ${SOURCE_FILE} - COMMAND ${CMAKE_COMMAND} -E touch ${SOURCE_FILE} - DEPENDS ${STATIC_LIBS}) - - IF(MSVC) - # To merge libs, just pass them to lib.exe command line. - SET(LINKER_EXTRA_FLAGS "") - FOREACH(LIB ${STATIC_LIBS}) - SET(LINKER_EXTRA_FLAGS "${LINKER_EXTRA_FLAGS} ${LIB}") - ENDFOREACH() - SET_TARGET_PROPERTIES(${TARGET} PROPERTIES STATIC_LIBRARY_FLAGS - "${LINKER_EXTRA_FLAGS}") - ELSE() - GET_TARGET_PROPERTY(TARGET_LOCATION ${TARGET} LOCATION) - IF(APPLE) - # Use OSX's libtool to merge archives (ihandles universal - # binaries properly) - ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD - COMMAND rm ${TARGET_LOCATION} - COMMAND /usr/bin/libtool -static -o ${TARGET_LOCATION} - ${STATIC_LIBS} - ) - ELSE() - # Generic Unix, Cygwin or MinGW. In post-build step, call - # script, that extracts objects from archives with "ar x" - # and repacks them with "ar r" - SET(TARGET ${TARGET}) - CONFIGURE_FILE( - ${CMAKE_SOURCE_DIR}/cmake/merge_archives_unix.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}.cmake - @ONLY - ) - ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD - COMMAND rm ${TARGET_LOCATION} - COMMAND ${CMAKE_COMMAND} -P - ${CMAKE_CURRENT_BINARY_DIR}/merge_archives_${TARGET}.cmake - ) - ENDIF() - ENDIF() -ENDMACRO() - -# Convert static library to shared -MACRO(STATIC_TO_SHARED STATIC_LIB SHARED_LIB EXPORTS_FILE) - IF(NOT MSVC) - MESSAGE(FATAL_ERROR - "Cannot convert static ${STATIC_LIB} to shared ${TARGET} library." - ) - ENDIF() - - # Need one source file. - SET(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${SHARED_LIB}_dummy.c) - ADD_CUSTOM_COMMAND( - OUTPUT ${SOURCE_FILE} - COMMAND ${CMAKE_COMMAND} -E touch ${SOURCE_FILE} - ) - - ADD_LIBRARY(${SHARED_LIB} SHARED ${SOURCE_FILE} ${EXPORTS_FILE}) - TARGET_LINK_LIBRARIES(${SHARED_LIB} ${STATIC_LIB}) -ENDMACRO() - -MACRO(SET_TARGET_SOURCEDIR TARGET) - SET(${TARGET}_SOURCEDIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "source directory for a target") -ENDMACRO() - -# Handy macro to use when source projects maybe used somewhere else -# For example, embedded or client library may recompile mysys sources -# In such cases, using absolute names in ADD_LIBRARY has the advantage that -# GET_TARGET_PROPERTY(xxx SOURCES) also returns absolute names, so there is -# no need to know the base directory of a target. -MACRO(USE_ABSOLUTE_FILENAMES FILELIST) - # Use absolute file paths for sources - # It helps when building embedded where we need to - # sources files for the plugin to recompile. - SET(RESOLVED_PATHS) - FOREACH(FILE ${${FILELIST}}) - GET_FILENAME_COMPONENT(ABSOLUTE_PATH ${FILE} ABSOLUTE) - LIST(APPEND RESOLVED_PATHS ${ABSOLUTE_PATH}) - ENDFOREACH() - SET(${FILELIST} ${RESOLVED_PATHS}) -ENDMACRO() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 58a3dafa2f4..40e31aeea02 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -72,19 +72,26 @@ MACRO(MYSQL_PLUGIN plugin) ENDIF() - USE_ABSOLUTE_FILENAMES(${plugin}_SOURCES) - IF (WITH_${plugin} AND ${plugin}_PLUGIN_STATIC) ADD_DEFINITIONS(-DMYSQL_SERVER) #Create static library. ADD_LIBRARY(${target} ${${plugin}_SOURCES}) DTRACE_INSTRUMENT(${target}) ADD_DEPENDENCIES(${target} GenError) + IF(WITH_EMBEDDED_SERVER AND NOT ${plugin}_PLUGIN_DYNAMIC) + # Recompile couple of plugins for embedded + ADD_LIBRARY(${target}_embedded ${${plugin}_SOURCES}) + DTRACE_INSTRUMENT(${target}_embedded) + SET_TARGET_PROPERTIES(${target}_embedded + PROPERTIES COMPILE_DEFINITIONS "EMBEDDED_LIBRARY") + ADD_DEPENDENCIES(${target}_embedded GenError) + ENDIF() IF(${plugin}_LIBS) TARGET_LINK_LIBRARIES(${target} ${${plugin}_LIBS}) - ENDIF() + ENDIF() + SET_TARGET_PROPERTIES(${target} PROPERTIES - OUTPUT_NAME "${${plugin}_PLUGIN_STATIC}") + OUTPUT_NAME "${${plugin}_PLUGIN_STATIC}") # Update mysqld dependencies SET (MYSQLD_STATIC_PLUGIN_LIBS ${MYSQLD_STATIC_PLUGIN_LIBS} ${target} PARENT_SCOPE) @@ -92,6 +99,7 @@ MACRO(MYSQL_PLUGIN plugin) PARENT_SCOPE) SET(${with_var} ON CACHE BOOL "Link ${plugin} statically to the server" FORCE) + ELSEIF(NOT WITHOUT_${plugin} AND ${plugin}_PLUGIN_DYNAMIC AND NOT WITHOUT_DYNAMIC_PLUGINS) @@ -105,22 +113,21 @@ MACRO(MYSQL_PLUGIN plugin) SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "") TARGET_LINK_LIBRARIES (${target} mysqlservices) - # Plugin uses symbols defined in mysqld executable. + # Plugin uses symbols defined in mysqld executable. # Some operating systems like Windows and OSX and are pretty strict about - # unresolved symbols. Others are less strict and allow unresolved symbols + # unresolved symbols. Others are less strict and allow unresolved symbols # in shared libraries. On Linux for example, CMake does not even add # executable to the linker command line (it would result into link error). # Thus we skip TARGET_LINK_LIBRARIES on Linux, as it would only generate # an additional dependency. - IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") TARGET_LINK_LIBRARIES (${target} mysqld) - ENDIF() - + ENDIF() ADD_DEPENDENCIES(${target} GenError) IF(${plugin}_PLUGIN_DYNAMIC) SET_TARGET_PROPERTIES(${target} PROPERTIES - OUTPUT_NAME "${${plugin}_PLUGIN_DYNAMIC}") + OUTPUT_NAME "${${plugin}_PLUGIN_DYNAMIC}") ENDIF() # Update cache "WITH" variable for plugins that support static linking diff --git a/dbug/CMakeLists.txt b/dbug/CMakeLists.txt index 00de8ba11ff..16e130fa28a 100755 --- a/dbug/CMakeLists.txt +++ b/dbug/CMakeLists.txt @@ -18,6 +18,5 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ) SET(DBUG_SOURCES dbug.c sanity.c) -USE_ABSOLUTE_FILENAMES(DBUG_SOURCES) -ADD_LIBRARY(dbug ${DBUG_SOURCES}) +ADD_CONVENIENCE_LIBRARY(dbug ${DBUG_SOURCES}) TARGET_LINK_LIBRARIES(dbug mysys) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index c1487149acd..bf7232e4b0a 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -27,7 +27,7 @@ ${CMAKE_SOURCE_DIR}/storage/ndb/include/mgmapi) IF(NOT CMAKE_CROSSCOMPILING) ADD_EXECUTABLE(comp_err comp_err.c) - TARGET_LINK_LIBRARIES(comp_err ${ZLIB_LIBRARY} mysys) + TARGET_LINK_LIBRARIES(comp_err mysys) ENDIF() ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_BINARY_DIR}/include/mysqld_error.h diff --git a/extra/yassl/CMakeLists.txt b/extra/yassl/CMakeLists.txt index a815a46a323..1bc75956221 100755 --- a/extra/yassl/CMakeLists.txt +++ b/extra/yassl/CMakeLists.txt @@ -29,6 +29,6 @@ ENDIF() SET(YASSL_SOURCES src/buffer.cpp src/cert_wrapper.cpp src/crypto_wrapper.cpp src/handshake.cpp src/lock.cpp src/log.cpp src/socket_wrapper.cpp src/ssl.cpp src/timer.cpp src/yassl_error.cpp src/yassl_imp.cpp src/yassl_int.cpp) -USE_ABSOLUTE_FILENAMES(YASSL_SOURCES) -ADD_LIBRARY(yassl ${YASSL_SOURCES}) +ADD_CONVENIENCE_LIBRARY(yassl ${YASSL_SOURCES}) + diff --git a/extra/yassl/taocrypt/CMakeLists.txt b/extra/yassl/taocrypt/CMakeLists.txt index 58de3989b73..34ff2323390 100755 --- a/extra/yassl/taocrypt/CMakeLists.txt +++ b/extra/yassl/taocrypt/CMakeLists.txt @@ -18,9 +18,6 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/mySTL INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) ADD_DEFINITIONS(${SSL_DEFINES}) -IF(PREVENT_CPP_RUNTIME) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PREVENT_CPP_RUNTIME}") -ENDIF() SET(TAOCRYPT_SOURCES src/aes.cpp src/aestables.cpp src/algebra.cpp src/arc4.cpp src/asn.cpp src/coding.cpp src/des.cpp src/dh.cpp src/dsa.cpp src/file.cpp src/hash.cpp src/integer.cpp src/md2.cpp src/md4.cpp src/md5.cpp src/misc.cpp src/random.cpp src/ripemd.cpp src/rsa.cpp src/sha.cpp @@ -29,6 +26,5 @@ SET(TAOCRYPT_SOURCES src/aes.cpp src/aestables.cpp src/algebra.cpp src/arc4.cpp include/error.hpp include/file.hpp include/hash.hpp include/hmac.hpp include/integer.hpp include/md2.hpp include/md5.hpp include/misc.hpp include/modarith.hpp include/modes.hpp include/random.hpp include/ripemd.hpp include/rsa.hpp include/sha.hpp) -USE_ABSOLUTE_FILENAMES(TAOCRYPT_SOURCES) -ADD_LIBRARY(taocrypt ${TAOCRYPT_SOURCES}) +ADD_CONVENIENCE_LIBRARY(taocrypt ${TAOCRYPT_SOURCES}) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 916aeae491a..c6047c0c4c5 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -24,13 +24,115 @@ INCLUDE_DIRECTORIES( ${ZLIB_INCLUDE_DIR}) ADD_DEFINITIONS(${SSL_DEFINES}) +SET(CLIENT_API_FUNCTIONS +load_defaults +mysql_thread_end +mysql_thread_init +myodbc_remove_escape +mysql_affected_rows +mysql_autocommit +mysql_stmt_bind_param +mysql_stmt_bind_result +mysql_change_user +mysql_character_set_name +mysql_close +mysql_commit +mysql_data_seek +mysql_debug +mysql_dump_debug_info +mysql_eof +mysql_errno +mysql_error +mysql_escape_string +mysql_hex_string +mysql_stmt_execute +mysql_stmt_fetch +mysql_stmt_fetch_column +mysql_fetch_field +mysql_fetch_field_direct +mysql_fetch_fields +mysql_fetch_lengths +mysql_fetch_row +mysql_field_count +mysql_field_seek +mysql_field_tell +mysql_free_result +mysql_get_client_info +mysql_get_host_info +mysql_get_proto_info +mysql_get_server_info +mysql_get_client_version +mysql_get_ssl_cipher +mysql_info +mysql_init +mysql_insert_id +mysql_kill +mysql_set_server_option +mysql_list_dbs +mysql_list_fields +mysql_list_processes +mysql_list_tables +mysql_more_results +mysql_next_result +mysql_num_fields +mysql_num_rows +mysql_odbc_escape_string +mysql_options +mysql_stmt_param_count +mysql_stmt_param_metadata +mysql_ping +mysql_stmt_result_metadata +mysql_query +mysql_read_query_result +mysql_real_connect +mysql_real_escape_string +mysql_real_query +mysql_refresh +mysql_rollback +mysql_row_seek +mysql_row_tell +mysql_select_db +mysql_stmt_send_long_data +mysql_send_query +mysql_shutdown +mysql_ssl_set +mysql_stat +mysql_stmt_affected_rows +mysql_stmt_close +mysql_stmt_reset +mysql_stmt_data_seek +mysql_stmt_errno +mysql_stmt_error +mysql_stmt_free_result +mysql_stmt_num_rows +mysql_stmt_row_seek +mysql_stmt_row_tell +mysql_stmt_store_result +mysql_store_result +mysql_thread_id +mysql_thread_safe +mysql_use_result +mysql_warning_count +mysql_stmt_sqlstate +mysql_sqlstate +mysql_get_server_version +mysql_stmt_prepare +mysql_stmt_init +mysql_stmt_insert_id +mysql_stmt_attr_get +mysql_stmt_attr_set +mysql_stmt_field_count +mysql_set_local_infile_default +mysql_set_local_infile_handler +mysql_embedded +mysql_server_init +mysql_server_end +mysql_set_character_set +mysql_get_character_set_info -#Remove -fno-implicit-templates -#(yassl sources cannot be compiled with it) -STRING(REPLACE "-fno-implicit-templates" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -ADD_DEFINITIONS(-DDISABLE_DTRACE) - +CACHE INTERNAL "Functions exported by client API" +) SET(CLIENT_SOURCES get_password.c @@ -42,71 +144,22 @@ SET(CLIENT_SOURCES ../sql-common/pack.c ../sql/password.c ) - -ADD_LIBRARY(clientlib STATIC ${CLIENT_SOURCES}) +ADD_CONVENIENCE_LIBRARY(clientlib ${CLIENT_SOURCES}) +DTRACE_INSTRUMENT(clientlib) ADD_DEPENDENCIES(clientlib GenError) -# Merge several static libraries into one big mysqlclient. -SET(LIBS dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) -MERGE_STATIC_LIBS(mysqlclient mysqlclient "${LIBS};clientlib") - -ADD_DEPENDENCIES(mysqlclient GenError) -SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) - - -# Make shared client library -IF(WIN32) - SET(SHARED_OUTPUT_NAME libmysql) -ELSE() - SET(SHARED_OUTPUT_NAME mysqlclient) -ENDIF() - -# On Windows, we can make a shared library out of static. -# On Unix, we need to recompile all sources, unless we compiled with -fPIC, in -# which case we can link static libraries to shared. -IF(MSVC) - STATIC_TO_SHARED(mysqlclient libmysql libmysql.def) -ELSE() - SET(LIBMYSQL_SOURCES ${CLIENT_SOURCES}) - - IF(NOT WITH_PIC) - # Add all sources that come into common static libs. - FOREACH(LIB ${LIBS}) - GET_TARGET_PROPERTY(SRC ${LIB} SOURCES) - IF (NOT SRC) - # This must be system shared lib (zlib or openssl) - # Users of libmysql must link with it too. - LIST(APPEND OS_LIBS ${LIB}) - ELSE() - LIST(APPEND LIBMYSQL_SOURCES ${SRC}) - ENDIF() - ENDFOREACH() - - # Some extra flags as in mysys - IF(CMAKE_COMPILER_IS_GNUCC AND NOT HAVE_CXX_NEW) - SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/mysys/my_new.cc - PROPERTIES COMPILE_FLAGS "-DUSE_MYSYS_NEW") - ENDIF() - ENDIF() - - - ADD_LIBRARY(libmysql SHARED ${LIBMYSQL_SOURCES}) - ADD_DEPENDENCIES(libmysql GenError) - SET_TARGET_PROPERTIES(libmysql PROPERTIES OUTPUT_NAME ${SHARED_OUTPUT_NAME} - SOVERSION "${SHARED_LIB_MAJOR_VERSION}.0") - SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) - - IF(WITH_PIC) - TARGET_LINK_LIBRARIES(libmysql ${LIBS}) - ENDIF() - - IF(OS_LIBS) - TARGET_LINK_LIBRARIES(libmysql ${OS_LIBS}) - ENDIF() -ENDIF() - +SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) +# Merge several convenience libraries into one big mysqlclient +# and link them together into shared library. +MERGE_LIBRARIES(mysqlclient STATIC ${LIBS}) +MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) IF(UNIX) + # Name of shared library is mysqlclient on Unix + SET_TARGET_PROPERTIES(libmysql PROPERTIES OUTPUT_NAME mysqlclient) + # clean direct output needs to be set several targets have the same name + #(mysqlclient in this case) + SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) # Install links to shared and static libraries # (append _r to base name) INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) @@ -114,4 +167,4 @@ IF(UNIX) ENDIF() INSTALL(TARGETS mysqlclient libmysql DESTINATION lib) -INSTALL_DEBUG_SYMBOLS( "mysqlclient;libmysql") +INSTALL_DEBUG_SYMBOLS("mysqlclient;libmysql") diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index e435a14b005..a32f501d756 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -1,33 +1,7 @@ LIBRARY LIBMYSQL VERSION 6.0 EXPORTS - _dig_vec_lower - _dig_vec_upper - bmove_upp - delete_dynamic - free_defaults - getopt_compare_strings - getopt_ull_limit_value - handle_options - init_dynamic_array - insert_dynamic - int2str - is_prefix - list_add - list_delete load_defaults - my_end - my_getopt_print_errors - my_init - my_malloc - my_memdup - my_no_flags_free - my_path - mysql_get_parameters - my_print_help - my_print_variables - my_realloc - my_strdup mysql_thread_end mysql_thread_init myodbc_remove_escape @@ -118,22 +92,12 @@ EXPORTS mysql_stmt_sqlstate mysql_sqlstate mysql_get_server_version - set_dynamic - strcend - strcont - strdup_root - strfill - strinstr - strmake - strmov - strxmov mysql_stmt_prepare mysql_stmt_init mysql_stmt_insert_id mysql_stmt_attr_get mysql_stmt_attr_set mysql_stmt_field_count - client_errors mysql_set_local_infile_default mysql_set_local_infile_handler mysql_embedded @@ -141,5 +105,3 @@ EXPORTS mysql_server_end mysql_set_character_set mysql_get_character_set_info - get_defaults_options - modify_defaults_file diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 80f45a4491d..d02cf2e6149 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -13,7 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -ADD_DEFINITIONS(-DMYSQL_SERVER -DEMBEDDED_LIBRARY -DDISABLE_DTRACE +ADD_DEFINITIONS(-DMYSQL_SERVER -DEMBEDDED_LIBRARY ${SSL_DEFINES}) INCLUDE_DIRECTORIES( @@ -29,7 +29,7 @@ ${SSL_INTERNAL_INCLUDE_DIRS} ${NDB_CLUSTER_INCLUDES} ${CMAKE_SOURCE_DIR}/sql/backup ) - + SET(GEN_SOURCES ${CMAKE_BINARY_DIR}/sql/sql_yacc.h ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc @@ -37,23 +37,8 @@ ${CMAKE_BINARY_DIR}/sql/lex_hash.h ) SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED TRUE) -SET(LIBS dbug strings regex mysys vio ${ZLIB_LIBRARY} ${SSL_LIBRARIES} -${MYSQLD_STATIC_PLUGIN_LIBS} ${NDB_CLIENT_LIBS}) -# Quirk: recompile selected storage engines with -DEMBEDDED_LIBRARY -# They depend on internal structures like THD that is different in embedded. -SET(RECOMPILE_ENGINES myisam myisammrg heap ndbcluster) -FOREACH(ENGINE ${RECOMPILE_ENGINES}) - LIST(REMOVE_ITEM LIBS ${ENGINE}) - GET_TARGET_PROPERTY(SRC ${ENGINE} SOURCES) - IF(SRC) - LIST(APPEND ENGINE_SOURCES ${SRC}) - ENDIF() -ENDFOREACH() - - - -SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc +SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../libmysql/libmysql.c ../libmysql/errmsg.c ../client/get_password.c ../sql-common/client.c ../sql-common/my_time.c ../sql-common/my_user.c ../sql-common/pack.c @@ -96,13 +81,13 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/sql_signal.cc ../sql/rpl_handler.cc ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc ${GEN_SOURCES} - ${ENGINE_SOURCES} ${MYSYS_LIBWRAP_SOURCE} ) -ADD_LIBRARY(mysqlserver_int STATIC ${LIBMYSQLD_SOURCES}) -ADD_DEPENDENCIES(mysqlserver_int GenError GenServerSource) +ADD_LIBRARY(sql_embedded STATIC ${SQL_EMBEDDED_SOURCES}) +DTRACE_INSTRUMENT(sql_embedded) +ADD_DEPENDENCIES(sql_embedded GenError GenServerSource) # On Windows, static embedded server library is called mysqlserver.lib # On Unix, it is libmysqld.a @@ -112,17 +97,31 @@ ELSE() SET(MYSQLSERVER_OUTPUT_NAME mysqld) ENDIF() -# Merge slim mysqlserver_int with other libraries like mysys to create a big -# static library that contains everything. -MERGE_STATIC_LIBS(mysqlserver ${MYSQLSERVER_OUTPUT_NAME} - "mysqlserver_int;${LIBS}") -IF(LIBWRAP_LIBRARY) - TARGET_LINK_LIBRARIES(mysqlserver ${LIBWRAP_LIBRARY}) -ENDIF() +SET(LIBS + dbug strings regex mysys vio + ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBWRAP_LIBRARY} + ${MYSQLD_STATIC_PLUGIN_LIBS} ${NDB_CLIENT_LIBS} + sql_embedded +) + +# Some storage engine were compiled for embedded specifically +# (with corresponding target ${engine}_embedded) +SET(EMBEDDED_LIBS) +FOREACH(LIB ${LIBS}) + GET_TARGET_PROPERTY(EMBEDDED_LOCATION ${LIB}_embedded LOCATION) + IF(EMBEDDED_LOCATION) + LIST(APPEND EMBEDDED_LIBS ${LIB}_embedded) + ELSE() + LIST(APPEND EMBEDDED_LIBS ${LIB}) + ENDIF() +ENDFOREACH() + +MERGE_LIBRARIES(mysqlserver STATIC ${EMBEDDED_LIBS} + OUTPUT_NAME ${MYSQLSERVER_OUTPUT_NAME}) INSTALL(TARGETS mysqlserver DESTINATION lib) IF(MSVC) - STATIC_TO_SHARED(mysqlserver libmysqld libmysqld.def) + MERGE_LIBRARIES(libmysqld SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) INSTALL(TARGETS libmysqld DESTINATION lib) ENDIF() diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 5c0cebf5938..07ca44244fc 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -56,7 +56,6 @@ IF(UNIX) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_atomic.c) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_getncpus.c) ENDIF() -USE_ABSOLUTE_FILENAMES(MYSYS_SOURCES) -ADD_LIBRARY(mysys ${MYSYS_SOURCES}) +ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES}) TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY}) DTRACE_INSTRUMENT(mysys) diff --git a/regex/CMakeLists.txt b/regex/CMakeLists.txt index f02c5745ef3..46a36ae0758 100755 --- a/regex/CMakeLists.txt +++ b/regex/CMakeLists.txt @@ -17,6 +17,4 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) SET(REGEX_SOURCES regcomp.c regerror.c regexec.c regfree.c reginit.c) -USE_ABSOLUTE_FILENAMES(REGEX_SOURCES) -ADD_LIBRARY(regex ${REGEX_SOURCES}) - +ADD_CONVENIENCE_LIBRARY(regex STATIC ${REGEX_SOURCES}) diff --git a/strings/CMakeLists.txt b/strings/CMakeLists.txt index 79fe03f019a..5b2ab978157 100755 --- a/strings/CMakeLists.txt +++ b/strings/CMakeLists.txt @@ -24,6 +24,4 @@ SET(STRINGS_SOURCES bchange.c bcmp.c bfill.c bmove512.c bmove_upp.c ctype-big5.c strtod.c strtol.c strtoll.c strtoul.c strtoull.c strxmov.c strxnmov.c xml.c my_strchr.c strcont.c strinstr.c strnlen.c strappend.c) -USE_ABSOLUTE_FILENAMES(STRINGS_SOURCES) -ADD_LIBRARY(strings ${STRINGS_SOURCES}) - +ADD_CONVENIENCE_LIBRARY(strings STATIC ${STRINGS_SOURCES}) diff --git a/vio/CMakeLists.txt b/vio/CMakeLists.txt index 6f44ecadb21..3470db61c60 100755 --- a/vio/CMakeLists.txt +++ b/vio/CMakeLists.txt @@ -19,7 +19,4 @@ ${SSL_INCLUDE_DIRS}) ADD_DEFINITIONS(${SSL_DEFINES}) SET(VIO_SOURCES vio.c viosocket.c viossl.c viosslfactories.c) -USE_ABSOLUTE_FILENAMES(VIO_SOURCES) -ADD_LIBRARY(vio ${VIO_SOURCES}) -ADD_DEPENDENCIES(vio GenError) - +ADD_CONVENIENCE_LIBRARY(vio ${VIO_SOURCES}) diff --git a/zlib/CMakeLists.txt b/zlib/CMakeLists.txt index 2d0706d024e..8aae6bdd287 100755 --- a/zlib/CMakeLists.txt +++ b/zlib/CMakeLists.txt @@ -18,10 +18,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib ) - SET(ZLIB_SOURCES adler32.c compress.c crc32.c crc32.h deflate.c deflate.h gzio.c infback.c inffast.c inffast.h inffixed.h inflate.c inflate.h inftrees.c inftrees.h trees.c trees.h uncompr.c zconf.h zlib.h zutil.c zutil.h) -USE_ABSOLUTE_FILENAMES(ZLIB_SOURCES) -ADD_LIBRARY(zlib ${ZLIB_SOURCES}) +ADD_CONVENIENCE_LIBRARY(zlib ${ZLIB_SOURCES}) From 1e6f71a006bea920a4354cd6b74a48ec312a0838 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 25 Nov 2009 01:55:47 +0100 Subject: [PATCH 029/466] fix EXTRA_DIST crap --- cmake/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 9148334e140..c8cde52ca2d 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -1,6 +1,6 @@ EXTRA_DIST = \ cat.cmake \ - ConfigurableFileContent.in \ + configurable_file_content.in \ check_minimal_version.cmake \ create_initial_db.cmake.in \ make_dist.cmake.in \ From b224e24a1e560c991c5d6391607a904a636cb4ed Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 25 Nov 2009 02:11:01 +0100 Subject: [PATCH 030/466] Remove STATIC from ADD_CONVENIENCE_LIBRARY on 2 places --- regex/CMakeLists.txt | 2 +- strings/CMakeLists.txt | 2 +- unittest/mysys/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/regex/CMakeLists.txt b/regex/CMakeLists.txt index 46a36ae0758..02132eefaad 100755 --- a/regex/CMakeLists.txt +++ b/regex/CMakeLists.txt @@ -17,4 +17,4 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) SET(REGEX_SOURCES regcomp.c regerror.c regexec.c regfree.c reginit.c) -ADD_CONVENIENCE_LIBRARY(regex STATIC ${REGEX_SOURCES}) +ADD_CONVENIENCE_LIBRARY(regex ${REGEX_SOURCES}) diff --git a/strings/CMakeLists.txt b/strings/CMakeLists.txt index 5b2ab978157..06b8384a957 100755 --- a/strings/CMakeLists.txt +++ b/strings/CMakeLists.txt @@ -24,4 +24,4 @@ SET(STRINGS_SOURCES bchange.c bcmp.c bfill.c bmove512.c bmove_upp.c ctype-big5.c strtod.c strtol.c strtoll.c strtoul.c strtoull.c strxmov.c strxnmov.c xml.c my_strchr.c strcont.c strinstr.c strnlen.c strappend.c) -ADD_CONVENIENCE_LIBRARY(strings STATIC ${STRINGS_SOURCES}) +ADD_CONVENIENCE_LIBRARY(strings ${STRINGS_SOURCES}) diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt index bc07ce1c851..6377ec1ab74 100644 --- a/unittest/mysys/CMakeLists.txt +++ b/unittest/mysys/CMakeLists.txt @@ -27,6 +27,6 @@ MACRO (MY_ADD_TEST name) ENDMACRO() -FOREACH(testname bitmap base64 my_vsnprintf my_atomic) +FOREACH(testname bitmap base64 my_vsnprintf my_atomic lf) MY_ADD_TEST(${testname}) ENDFOREACH() From 2f3e777797a643284ffdd0f31c820497097ab451 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 25 Nov 2009 05:13:51 +0100 Subject: [PATCH 031/466] add --disable-shared equivalent fix mtr (empty argument passed to mysqld) --- CMakeLists.txt | 7 ++++++- libmysql/CMakeLists.txt | 28 ++++++++++++++++------------ libmysqld/CMakeLists.txt | 4 +++- mysql-test/mysql-test-run.pl | 5 +++++ mysys/CMakeLists.txt | 3 --- sql/CMakeLists.txt | 2 +- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18c1fca68b7..93d2f0ab457 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,11 +67,16 @@ INCLUDE(cmake/plugin.cmake) INCLUDE(cmake/install_macros.cmake) # Handle options +OPTION(DISABLE_SHARED + "Don't build shared libraries, compile code as position-dependent" OFF) +IF(DISABLE_SHARED) + SET(WITHOUT_DYNAMIC_PLUGINS 1) +ENDIF() OPTION(ENABLED_PROFILING "Enable profiling" ON) OPTION(CYBOZU "" OFF) OPTION(BACKUP_TEST "" OFF) OPTION(WITHOUT_SERVER OFF) -MARK_AS_ADVANCED(CYBOZU BACKUP_TEST WITHOUT_SERVER) +MARK_AS_ADVANCED(CYBOZU BACKUP_TEST WITHOUT_SERVER DISABLE_SHARED) OPTION(ENABLE_DEBUG_SYNC "Enable debug sync (debug builds only)" ON) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index c6047c0c4c5..4286ea2bb0b 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -153,18 +153,22 @@ SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. MERGE_LIBRARIES(mysqlclient STATIC ${LIBS}) -MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) +INSTALL(TARGETS mysqlclient DESTINATION lib) IF(UNIX) - # Name of shared library is mysqlclient on Unix - SET_TARGET_PROPERTIES(libmysql PROPERTIES OUTPUT_NAME mysqlclient) - # clean direct output needs to be set several targets have the same name - #(mysqlclient in this case) - SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) - # Install links to shared and static libraries - # (append _r to base name) - INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) - INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient lib) + INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient lib) ENDIF() +INSTALL_DEBUG_SYMBOLS(mysqlclient) -INSTALL(TARGETS mysqlclient libmysql DESTINATION lib) -INSTALL_DEBUG_SYMBOLS("mysqlclient;libmysql") +IF(NOT DISABLE_SHARED) + MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) + IF(UNIX) + # Name of shared library is mysqlclient on Unix + SET_TARGET_PROPERTIES(libmysql PROPERTIES OUTPUT_NAME mysqlclient) + # clean direct output needs to be set several targets have the same name + #(mysqlclient in this case) + SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) + SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) + INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) + ENDIF() + INSTALL_DEBUG_SYMBOLS(libmysql) +ENDIF() diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index d02cf2e6149..7378d23790d 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -120,8 +120,10 @@ ENDFOREACH() MERGE_LIBRARIES(mysqlserver STATIC ${EMBEDDED_LIBS} OUTPUT_NAME ${MYSQLSERVER_OUTPUT_NAME}) INSTALL(TARGETS mysqlserver DESTINATION lib) +INSTALL_DEBUG_SYMBOLS(mysqlserver) -IF(MSVC) +IF(MSVC AND NOT DISABLE_SHARED) MERGE_LIBRARIES(libmysqld SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) INSTALL(TARGETS libmysqld DESTINATION lib) + INSTALL_DEBUG_SYMBOLS(libmysqld) ENDIF() diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 9a9fa85aa79..3764cff2d9a 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4275,6 +4275,11 @@ sub mysqld_arguments ($$$) { { ; # Dont add --skip-log-bin when mysqld have --log-slave-updates in config } + elsif ($arg eq "") + { + # We can get an empty argument when we set environment variables to "" + # (e.g plugin not found). Just skip it. + } else { mtr_add_arg($args, "%s", $arg); diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 5f271d0a4a3..b9ecd4bf83a 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -54,9 +54,6 @@ ENDIF() IF(UNIX) # some workarounds SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_port.c) - # Some stuff not ported to windows - SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_atomic.c) - SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_getncpus.c) ENDIF() ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES}) TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY}) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index c3aadc8def5..82ad781c73b 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -178,7 +178,7 @@ ADD_CUSTOM_TARGET( #Need this only for embedded SET_TARGET_PROPERTIES(GenServerSource PROPERTIES EXCLUDE_FROM_ALL TRUE) -IF(WIN32 OR HAVE_DLOPEN) +IF(WIN32 OR HAVE_DLOPEN AND NOT DISABLE_SHARED) ADD_LIBRARY(udf_example MODULE udf_example.c) SET_TARGET_PROPERTIES(udf_example PROPERTIES PREFIX "") # udf_example depends on strings From ff57321f8ce404a298f898a03f80bf31a0620e94 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 25 Nov 2009 07:28:45 +0100 Subject: [PATCH 032/466] - move ADD_SUBDIRECTORY(extra) outside of WITHOUT_SERVER - add INSTALL to shared client library --- CMakeLists.txt | 2 +- libmysql/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93d2f0ab457..2b5e5464001 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,9 +190,9 @@ IF(WITH_UNIT_TESTS) ADD_SUBDIRECTORY(unittest/mysys) ENDIF() +ADD_SUBDIRECTORY(extra) IF(NOT WITHOUT_SERVER) ADD_SUBDIRECTORY(tests) - ADD_SUBDIRECTORY(extra) ADD_SUBDIRECTORY(client) ADD_SUBDIRECTORY(sql) ADD_SUBDIRECTORY(sql/share) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 4286ea2bb0b..4fb6e10409d 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -168,6 +168,7 @@ IF(NOT DISABLE_SHARED) #(mysqlclient in this case) SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) + INSTALL(TARGETS libmysql DESTINATION lib) INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) ENDIF() INSTALL_DEBUG_SYMBOLS(libmysql) From a710e76783d9d0d31d2d279967d717589e026fb3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 26 Nov 2009 00:47:38 +0100 Subject: [PATCH 033/466] Fix universal binaries build on OSX, in case both 32 and 64 bit architectures are used --- config.h.cmake | 29 +++++++++++++++++++++++------ configure.cmake | 9 ++++++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/config.h.cmake b/config.h.cmake index 054777e9f43..e4dd2541f92 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -293,7 +293,29 @@ # define HAVE_CHAR 1 #endif -#cmakedefine SIZEOF_CHARP @SIZEOF_CHARP@ +#ifdef __APPLE__ + /* + Special handling required for OSX to support universal binaries that + mix 32 and 64 bit architectures. + */ + #if(__LP64__) + #define SIZEOF_LONG 8 + #else + #define SIZEOF_LONG 4 + #endif + #define SIZEOF_CHARP SIZEOF_LONG + #define SIZEOF_SIZE_T SIZEOF_LONG +#else + #cmakedefine SIZEOF_LONG @SIZEOF_LONG@ + #cmakedefine SIZEOF_CHARP @SIZEOF_CHARP@ + #cmakedefine SIZEOF_SIZE_T @SIZEOF_CHARP@ +#endif + +#if SIZEOF_LONG +# define HAVE_LONG 1 +#endif + + #if SIZEOF_CHARP #define HAVE_CHARP 1 #define SIZEOF_VOIDP SIZEOF_CHARP @@ -309,10 +331,6 @@ # define HAVE_INT 1 #endif -#cmakedefine SIZEOF_LONG @SIZEOF_LONG@ -#if SIZEOF_LONG -# define HAVE_LONG 1 -#endif #cmakedefine SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@ #if SIZEOF_LONG_LONG @@ -329,7 +347,6 @@ #define HAVE_SIGSET_T 1 #endif -#cmakedefine SIZEOF_SIZE_T @SIZEOF_SIZE_T@ #if SIZEOF_SIZE_T #define HAVE_SIZE_T 1 #endif diff --git a/configure.cmake b/configure.cmake index 6c105c25f4a..efc94f09244 100644 --- a/configure.cmake +++ b/configure.cmake @@ -687,14 +687,17 @@ ENDIF() IF(HAVE_STDINT_H) SET(CMAKE_EXTRA_INCLUDE_FILES stdint.h) ENDIF(HAVE_STDINT_H) + +IF(NOT APPLE) + CHECK_TYPE_SIZE("char *" SIZEOF_CHARP) + CHECK_TYPE_SIZE(long SIZEOF_LONG) + CHECK_TYPE_SIZE(size_t SIZEOF_SIZE_T) +ENDIF() CHECK_TYPE_SIZE(char SIZEOF_CHAR) -CHECK_TYPE_SIZE("char *" SIZEOF_CHARP) CHECK_TYPE_SIZE(short SIZEOF_SHORT) CHECK_TYPE_SIZE(int SIZEOF_INT) -CHECK_TYPE_SIZE(long SIZEOF_LONG) CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG) SET(CMAKE_EXTRA_INCLUDE_FILES stdio.h) -CHECK_TYPE_SIZE(size_t SIZEOF_SIZE_T) SET(CMAKE_EXTRA_INCLUDE_FILES sys/types.h) CHECK_TYPE_SIZE(off_t SIZEOF_OFF_T) CHECK_TYPE_SIZE(uchar SIZEOF_UCHAR) From ae3e5e3da0b1f00964420f0f04e9cb68e37f5189 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 26 Nov 2009 11:16:06 +0100 Subject: [PATCH 034/466] Bug #48250 mysqtest_embedded can lock destroyed mutex As suggested, replaced relevant uses of my_fopen with fopen (and close) Tested on HPUX where it was reproducable with test innodb_bug30919 --- client/mysqltest.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index cfcabb5a5ab..3cd87bd3236 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -1119,7 +1119,7 @@ void close_files() if (cur_file->file && cur_file->file != stdin) { DBUG_PRINT("info", ("closing file: %s", cur_file->file_name)); - my_fclose(cur_file->file, MYF(0)); + fclose(cur_file->file); } my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); cur_file->file_name= 0; @@ -2441,7 +2441,7 @@ int open_file(const char *name) if (cur_file == file_stack_end) die("Source directives are nesting too deep"); cur_file++; - if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) + if (!(cur_file->file = fopen(buff, "rb"))) { cur_file--; die("Could not open '%s' for reading, errno: %d", buff, errno); @@ -5286,7 +5286,7 @@ int read_line(char *buf, int size) found_eof: if (cur_file->file != stdin) { - my_fclose(cur_file->file, MYF(0)); + fclose(cur_file->file); cur_file->file= 0; } my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); @@ -5865,7 +5865,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); if (!(cur_file->file= - my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) + fopen(buff, "rb"))) die("Could not open '%s' for reading, errno: %d", buff, errno); cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); cur_file->lineno= 1; From 4deb53a56dd6a912efbbbfe0c6499d79f512fec9 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 26 Nov 2009 11:23:30 +0100 Subject: [PATCH 035/466] Bug #49096 mtr: --mem option should be ignored on Windows Ignored w/message in v1 and v2 --- mysql-test/lib/v1/mysql-test-run.pl | 5 +++++ mysql-test/mysql-test-run.pl | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/mysql-test/lib/v1/mysql-test-run.pl b/mysql-test/lib/v1/mysql-test-run.pl index 86ad5c485c1..9630c65ade4 100755 --- a/mysql-test/lib/v1/mysql-test-run.pl +++ b/mysql-test/lib/v1/mysql-test-run.pl @@ -905,6 +905,11 @@ sub command_line_setup () { mtr_report("Using default engine '$used_default_engine'") if defined $used_default_engine; + if ($glob_win32 and defined $opt_mem) { + mtr_report("--mem not supported on Windows, ignored"); + $opt_mem= undef; + } + # -------------------------------------------------------------------------- # Check if we should speed up tests by trying to run on tmpfs # -------------------------------------------------------------------------- diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2147b4ae7a7..e7f8e90dc44 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1080,6 +1080,11 @@ sub command_line_setup { } } + if (IS_WINDOWS and defined $opt_mem) { + mtr_report("--mem not supported on Windows, ignored"); + $opt_mem= undef; + } + # -------------------------------------------------------------------------- # Check if we should speed up tests by trying to run on tmpfs # -------------------------------------------------------------------------- From 15d5390010f46717b90125a55480720dd1edebb9 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 26 Nov 2009 11:34:16 +0100 Subject: [PATCH 036/466] Bug #48918 MTR uses an un-initialized value in comparison mysqld->{proc} not defined for an embedded server Check only if {proc} defined --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index e7f8e90dc44..7d2426459d0 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3893,7 +3893,7 @@ sub check_expected_crash_and_restart { foreach my $mysqld ( mysqlds() ) { - next unless ( $mysqld->{proc} eq $proc ); + next unless ( $mysqld->{proc} and $mysqld->{proc} eq $proc ); # Check if crash expected by looking at the .expect file # in var/tmp From dba1a7ccd91621432f51d4663ce450e5dc1af1ff Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 26 Nov 2009 18:40:45 +0100 Subject: [PATCH 037/466] Delay configuring scripts like mysql_install_db until CPack runs (to handle DESTDIR correctly) --- scripts/CMakeLists.txt | 204 ++----------------------------- scripts/Makefile.am | 3 +- scripts/install_scripts.cmake.in | 196 +++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 197 deletions(-) create mode 100644 scripts/install_scripts.cmake.in diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 8d246e6a38a..113948dc3d4 100755 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -13,54 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -IF(UNIX) - # FIND_PROC and CHECK_PID are used by mysqld_safe -IF(CMAKE_SYSTEM_NAME MATCHES "Linux") - SET (FIND_PROC - "ps wwwp $PID | grep -v \" grep\" | grep -v mysqld_safe | grep -- \"$MYSQLD\" > /dev/null") -ENDIF() -IF(NOT FIND_PROC AND CMAKE_SYSTEM_NAME MATCHES "SunOS") - SET (FIND_PROC - "ps -p $PID | grep -v \" grep\" | grep -v mysqld_safe | grep -- \"$MYSQLD\" > /dev/null") -ENDIF() -IF(NOT FIND_PROC) - # BSD style - EXECUTE_PROCESS(COMMAND ps -uaxww OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result) - IF(result MATCHES 0) - SET( FIND_PROC - "ps -uaxww | grep -v \" grep\" | grep -v mysqld_safe | grep -- "\$MYSQLD\" | grep \" $PID \" > /dev/null") - ENDIF() -ENDIF() - -IF(NOT FIND_PROC) - # SysV style - EXECUTE_PROCESS(COMMAND ps -ef OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result) - IF(result MATCHES 0) - SET( FIND_PROC "ps -ef | grep -v \" grep\" | grep -v mysqld_safe | grep -- "\$MYSQLD\" | grep \" $PID \" > /dev/null") - ENDIF() -ENDIF() - -EXECUTE_PROCESS(COMMAND sh -c "kill -0 $$" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result2) -IF(result3 MATCHES 0) - SET(CHECK_PID "kill -0 $PID > /dev/null 2> /dev/null") -ELSE() - EXECUTE_PROCESS(COMMAND sh -c "kill -s 0 $$" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result3) - IF(result4 MATCHES 0) - SET(CHECK_PID "kill -s 0 $PID > /dev/null 2> /dev/null") - ELSE() - SET(CHECK_PID "kill -s SIGCONT $PID > /dev/null 2> /dev/null") - ENDIF() -ENDIF() - -ENDIF(UNIX) - -# some scripts use @TARGET_LINUX@ -IF(CMAKE_SYSTEM_NAME MATCHES "Linux") - SET(TARGET_LINUX 1) -ELSE() - SET(TARGET_LINUX 0) -ENDIF() # Build comp_sql - used for embedding SQL in C or C++ programs IF(NOT CMAKE_CROSSCOMPILING) ADD_EXECUTABLE(comp_sql comp_sql.c) @@ -100,128 +53,6 @@ ADD_CUSTOM_TARGET(GenFixPrivs DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/mysql_fix_privilege_tables_sql.c ) -# ---------------------------------------------------------------------- -# Replace some variables @foo@ in the .in/.sh file, and write the new script -# ---------------------------------------------------------------------- - -SET(CFLAGS "${CMAKE_C_FLAGS_RELWITHDEBINFO}") -SET(CXXFLAGS "${CMAKE_C_FLAGS_RELWITHDEBINFO}") -SET(LDFLAGS "${CMAKE_SHARED_LIBRARY_LINK_FLAGS}") - -IF(WIN32) - SET(prefix "${CMAKE_INSTALL_PREFIX}/MySQL Server ${MYSQL_BASE_VERSION}") -ELSE() - set(prefix "${CMAKE_INSTALL_PREFIX}") -ENDIF() - -SET(sysconfdir ${prefix}) -SET(bindir ${prefix}/bin) -SET(libexecdir ${prefix}/bin) -SET(scriptdir ${prefix}/bin) -SET(datadir ${prefix}/share) -SET(pkgdatadir ${prefix}/share) -SET(pkgincludedir ${prefix}/include) -SET(pkglibdir ${prefix}/lib) -SET(pkgplugindir ${prefix}/lib/plugin) -SET(localstatedir ${prefix}/data) - -# Use cmake variables to inspect dependencies for -# mysqlclient library -SET(CLIENT_LIBS "") -SET(LIBS "") -FOREACH(lib ${mysqlclient_LIB_DEPENDS}) - # Filter out general, it is CMake hint - # not real - IF(NOT lib STREQUAL "general" AND NOT CLIENT_LIBS MATCHES "-l${lib} ") - SET(CLIENT_LIBS "${CLIENT_LIBS}-l${lib} " ) - ENDIF() -ENDFOREACH() -FOREACH(lib ${mysqlserver_LIB_DEPENDS}) - IF(NOT lib STREQUAL "general" AND NOT LIBS MATCHES "-l${lib} ") - SET(LIBS "${LIBS}-l${lib} " ) - ENDIF() -ENDFOREACH() -IF(MSVC) - STRING(REPLACE "-l" "" CLIENT_LIBS "${CLIENT_LIBS}") - STRING(REPLACE "-l" "" LIBS "${LIBS}" ) -ENDIF() - -SET(NON_THREADED_LIBS ${CLIENT_LIBS}) - -IF(WIN32) - # On Windows, some .sh and some .pl.in files are configured - # The resulting files will have .pl extension (those are perl scripts) - - # Input files with pl.in extension - SET(PLIN_FILES mysql_config mysql_secure_installation) - # Input files with .sh extension - SET(SH_FILES mysql_convert_table_format mysqld_multi) - - FOREACH(file ${PLIN_FILES}) - CONFIGURE_FILE(${file}.pl.in - ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) - ENDFOREACH() - - FOREACH(file ${SH_FILES}) - CONFIGURE_FILE(${file}.sh - ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) - ENDFOREACH() -ELSE() - # On Unix, most of the files end up in the bin directory - SET(BIN_SCRIPTS - msql2mysql - mysql_config - mysql_fix_extensions - mysql_setpermission - mysql_secure_installation - mysql_zap - mysqlaccess - mysqlbug - mysql_convert_table_format - mysql_find_rows - mysqlhotcopy - mysqldumpslow - mysqld_multi - mysqlaccess - mysqlaccess.conf - mysql_install_db - ) - FOREACH(file ${BIN_SCRIPTS}) - IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh - ${CMAKE_CURRENT_BINARY_DIR}/${file} ESCAPE_QUOTES @ONLY) - ELSEIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file} - ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) - ELSE() - MESSAGE(FATAL_ERROR "Can not find ${file}.sh or ${file} in " - "${CMAKE_CURRENT_SOURCE_DIR}" ) - ENDIF() - - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file} DESTINATION bin - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ - GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - ENDFOREACH() - - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysqld_safe.sh - ${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe @ONLY) - - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe DESTINATION bin - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ - GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - - # For some reason, mysqld_safe needs to be also in scripts directory - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mysql_install_db - DESTINATION scripts - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ - GROUP_EXECUTE WORLD_READ WORLD_EXECUTE - ) -ENDIF() - -INSTALL(FILES mysql_test_data_timezone.sql DESTINATION share) - IF(UNIX) FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/make_binary_distribution "cd ${CMAKE_BINARY_DIR} && ${CMAKE_CPACK_COMMAND} -G TGZ --config CPackConfig.cmake" ) @@ -230,36 +61,11 @@ IF(UNIX) ) ENDIF() -# Install libgcc as mylibgcc.a -IF(CMAKE_COMPILER_IS_GNUCXX) - IF(NOT LIBGCC_LOCATION) - EXECUTE_PROCESS ( - COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} - ${CMAKE_CXX_FLAGS} --print-libgcc - OUTPUT_VARIABLE LIBGCC_LOCATION - RESULT_VARIABLE RESULT - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET - ) - IF(${RESULT} EQUAL 0 AND EXISTS ${LIBGCC_LOCATION}) - SET(LIBGCC_LOCATION "${LIBGCC_LOCATION}" CACHE INTERNAL - "location of libgcc" ) - ENDIF() - ENDIF() - - IF(LIBGCC_LOCATION) - INSTALL (CODE "CONFIGURE_FILE (${LIBGCC_LOCATION} - ${CMAKE_CURRENT_BINARY_DIR}/libmygcc.a COPYONLY)") - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmygcc.a DESTINATION lib - OPTIONAL) - ENDIF() -ENDIF() - - -INSTALL (FILES +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables.sql ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables_data.sql ${CMAKE_CURRENT_SOURCE_DIR}/fill_help_tables.sql + ${CMAKE_CURRENT_SOURCE_DIR}/mysql_test_data_timezone.sql DESTINATION share ) @@ -271,3 +77,9 @@ ENDIF() IF(MALLOC_LIB) INSTALL(FILES ${MALLOC_LIB} DESTINATION lib OPTIONAL) ENDIF() + +# install_scripts.cmake delays configuring scripts (e.g mysql_install_db) +# until cpack runs (necessary to handle DESTDIR correctly) +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/install_scripts.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/install_scripts.cmake @ONLY) +INSTALL(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/install_scripts.cmake) diff --git a/scripts/Makefile.am b/scripts/Makefile.am index cd758370388..88b1e5f92a2 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -69,7 +69,8 @@ EXTRA_DIST = $(EXTRA_SCRIPTS) \ make_win_bin_dist \ mysql_fix_privilege_tables_sql.c \ mysql_system_tables_fix.sql \ - CMakeLists.txt + CMakeLists.txt \ + install_scripts.cmake.in dist_pkgdata_DATA = fill_help_tables.sql \ mysql_fix_privilege_tables.sql \ diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in new file mode 100644 index 00000000000..145c5405de3 --- /dev/null +++ b/scripts/install_scripts.cmake.in @@ -0,0 +1,196 @@ +SET(CMAKE_SYSTEM_NAME @CMAKE_SYSTEM_NAME@) +SET(UNIX @UNIX@) +SET(WIN32 @WIN32@) +SET(MSVC @MSVC@) +SET(CFLAGS "@CMAKE_C_FLAGS_RELWITHDEBINFO@") +SET(CXXFLAGS "@CMAKE_CXX_FLAGS_RELWITHDEBINFO@") +SET(LDFLAGS "@CMAKE_SHARED_LIBRARY_LINK_FLAGS@") +SET(mysqlclient_LIB_DEPENDS "@mysqlclient_LIB_DEPENDS@") +SET(mysqlclient_LIB_DEPENDS "@mysqlserver_LIB_DEPENDS@") +SET(CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@") +SET(CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@") +SET(CMAKE_COMPILER_IS_GNUCXX "@CMAKE_COMPILER_IS_GNUCXX@") +SET(CMAKE_CXX_COMPILER "@CMAKE_CXX_COMPILER@") +SET(CMAKE_CXX_COMPILER_ARG1 "@CMAKE_CXX_COMPILER_ARG1@") + +IF(UNIX) + # FIND_PROC and CHECK_PID are used by mysqld_safe +IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + SET (FIND_PROC + "ps wwwp $PID | grep -v \" grep\" | grep -v mysqld_safe | grep -- \"$MYSQLD\" > /dev/null") +ENDIF() +IF(NOT FIND_PROC AND CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET (FIND_PROC + "ps -p $PID | grep -v \" grep\" | grep -v mysqld_safe | grep -- \"$MYSQLD\" > /dev/null") +ENDIF() + +IF(NOT FIND_PROC) + # BSD style + EXECUTE_PROCESS(COMMAND ps -uaxww OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result) + IF(result MATCHES 0) + SET( FIND_PROC + "ps -uaxww | grep -v \" grep\" | grep -v mysqld_safe | grep -- "\$MYSQLD\" | grep \" $PID \" > /dev/null") + ENDIF() +ENDIF() + +IF(NOT FIND_PROC) + # SysV style + EXECUTE_PROCESS(COMMAND ps -ef OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result) + IF(result MATCHES 0) + SET( FIND_PROC "ps -ef | grep -v \" grep\" | grep -v mysqld_safe | grep -- "\$MYSQLD\" | grep \" $PID \" > /dev/null") + ENDIF() +ENDIF() + +EXECUTE_PROCESS(COMMAND sh -c "kill -0 $$" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result2) +IF(result3 MATCHES 0) + SET(CHECK_PID "kill -0 $PID > /dev/null 2> /dev/null") +ELSE() + EXECUTE_PROCESS(COMMAND sh -c "kill -s 0 $$" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE result3) + IF(result4 MATCHES 0) + SET(CHECK_PID "kill -s 0 $PID > /dev/null 2> /dev/null") + ELSE() + SET(CHECK_PID "kill -s SIGCONT $PID > /dev/null 2> /dev/null") + ENDIF() +ENDIF() + +ENDIF(UNIX) + + + + +set(prefix "$ENV{DESTDIR}/${CMAKE_INSTALL_PREFIX}") + +SET(sysconfdir ${prefix}) +SET(bindir ${prefix}/bin) +SET(libexecdir ${prefix}/bin) +SET(scriptdir ${prefix}/bin) +SET(datadir ${prefix}/share) +SET(pkgdatadir ${prefix}/share) +SET(pkgincludedir ${prefix}/include) +SET(pkglibdir ${prefix}/lib) +SET(pkgplugindir ${prefix}/lib/plugin) +SET(localstatedir ${prefix}/data) +# some scripts use @TARGET_LINUX@ +IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + SET(TARGET_LINUX 1) +ELSE() + SET(TARGET_LINUX 0) +ENDIF() + +# Use cmake variables to inspect dependencies for +# mysqlclient library (add -l stuff) +SET(CLIENT_LIBS "") +SET(LIBS "") +FOREACH(lib ${mysqlclient_LIB_DEPENDS}) + # Filter out "general", it is not a library, just CMake hint + IF(NOT lib STREQUAL "general" AND NOT CLIENT_LIBS MATCHES "-l${lib} ") + SET(CLIENT_LIBS "${CLIENT_LIBS}-l${lib} " ) + ENDIF() +ENDFOREACH() +FOREACH(lib ${mysqlserver_LIB_DEPENDS}) + IF(NOT lib STREQUAL "general" AND NOT LIBS MATCHES "-l${lib} ") + SET(LIBS "${LIBS}-l${lib} " ) + ENDIF() +ENDFOREACH() +IF(MSVC) + STRING(REPLACE "-l" "" CLIENT_LIBS "${CLIENT_LIBS}") + STRING(REPLACE "-l" "" LIBS "${LIBS}" ) +ENDIF() + +SET(NON_THREADED_LIBS ${CLIENT_LIBS}) + +IF(WIN32) + # On Windows, some .sh and some .pl.in files are configured + # The resulting files will have .pl extension (those are perl scripts) + + # Input files with pl.in extension + SET(PLIN_FILES mysql_config mysql_secure_installation) + # Input files with .sh extension + SET(SH_FILES mysql_convert_table_format mysqld_multi) + + FOREACH(file ${PLIN_FILES}) + CONFIGURE_FILE(${file}.pl.in + ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) + ENDFOREACH() + + FOREACH(file ${SH_FILES}) + CONFIGURE_FILE(${file}.sh + ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) + ENDFOREACH() +ELSE() + # On Unix, most of the files end up in the bin directory + SET(BIN_SCRIPTS + msql2mysql + mysql_config + mysql_fix_extensions + mysql_setpermission + mysql_secure_installation + mysql_zap + mysqlaccess + mysqlbug + mysql_convert_table_format + mysql_find_rows + mysqlhotcopy + mysqldumpslow + mysqld_multi + mysqlaccess + mysqlaccess.conf + mysql_install_db + ) + FOREACH(file ${BIN_SCRIPTS}) + IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh + ${CMAKE_CURRENT_BINARY_DIR}/${file} ESCAPE_QUOTES @ONLY) + ELSEIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file} + ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) + ELSE() + MESSAGE(FATAL_ERROR "Can not find ${file}.sh or ${file} in " + "${CMAKE_CURRENT_SOURCE_DIR}" ) + ENDIF() + + FILE(INSTALL DESTINATION + "${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE PERMISSIONS OWNER_READ OWNER_WRITE + OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE FILES + "${CMAKE_CURRENT_BINARY_DIR}/${file}") + ENDFOREACH() + + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysqld_safe.sh + ${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe @ONLY) + + FILE(INSTALL DESTINATION + "${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE + PERMISSIONS OWNER_READ OWNER_WRITE + OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE FILES + "${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe" + ) + + # For some reason, mysqld_safe needs to be also in scripts directory + FILE(INSTALL DESTINATION + "${CMAKE_INSTALL_PREFIX}/scripts" TYPE FILE + PERMISSIONS OWNER_READ OWNER_WRITE + OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE FILES + "${CMAKE_CURRENT_BINARY_DIR}/mysql_install_db") + +ENDIF() + +# Install libgcc as mylibgcc.a +IF(CMAKE_COMPILER_IS_GNUCXX) + EXECUTE_PROCESS ( + COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} + ${CMAKE_CXX_FLAGS} --print-libgcc + OUTPUT_VARIABLE LIBGCC_LOCATION + RESULT_VARIABLE RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + IF(${RESULT} EQUAL 0 AND EXISTS ${LIBGCC_LOCATION}) + FILE(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" + TYPE FILE OPTIONAL FILES "${LIBGCC_LOCATION}") + ENDIF() +ENDIF() + From 69b9761f2913dfa9fa2989ef7f3e01b8d2d3e334 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 18:55:03 +0300 Subject: [PATCH 038/466] Initial import of WL#3726 "DDL locking for all metadata objects". Backport of: ------------------------------------------------------------ revno: 2630.4.1 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Fri 2008-05-23 17:54:03 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. ------------------------------------------------------------ This is the first patch in series. It transforms the metadata locking subsystem to use a dedicated module (mdl.h,cc). No significant changes in the locking protocol. The import passes the test suite with the exception of deprecated/removed 6.0 features, and MERGE tables. The latter are subject to a fix by WL#4144. Unfortunately, the original changeset comments got lost in a merge, thus this import has its own (largely insufficient) comments. This patch fixes Bug#25144 "replication / binlog with view breaks". Warning: this patch introduces an incompatible change: Under LOCK TABLES, it's no longer possible to FLUSH a table that was not locked for WRITE. Under LOCK TABLES, it's no longer possible to DROP a table or VIEW that was not locked for WRITE. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.2 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 14:03:45 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 14:08:51 +0400 message: WL#3726 "DDL locking for all metadata objects" Fixed failing Windows builds by adding mdl.cc to the lists of files needed to build server/libmysqld on Windows. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.4 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 21:57:58 +0400 message: WL#3726 "DDL locking for all metadata objects". Fix for assert failures in kill.test which occured when one tried to kill ALTER TABLE statement on merge table while it was waiting in wait_while_table_is_used() for other connections to close this table. These assert failures stemmed from the fact that cleanup code in this case assumed that temporary table representing new version of table was open with adding to THD::temporary_tables list while code which were opening this temporary table wasn't always fulfilling this. This patch changes code that opens new version of table to always do this linking in. It also streamlines cleanup process for cases when error occurs while we have new version of table open. ****** WL#3726 "DDL locking for all metadata objects" Add libmysqld/mdl.cc to .bzrignore. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.6 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sun 2008-05-25 00:33:22 +0400 message: WL#3726 "DDL locking for all metadata objects". Addition to the fix of assert failures in kill.test caused by changes for this worklog. Make sure we close the new table only once. --- .bzrignore | 1 + libmysqld/CMakeLists.txt | 1 + libmysqld/Makefile.am | 2 +- mysql-test/include/handler.inc | 33 +- mysql-test/r/create.result | 7 +- mysql-test/r/flush.result | 5 + mysql-test/r/flush_table.result | 17 +- mysql-test/r/handler_innodb.result | 2 + mysql-test/r/handler_myisam.result | 2 + mysql-test/r/information_schema.result | 77 +- mysql-test/r/kill.result | 103 + mysql-test/r/lock.result | 3 +- mysql-test/r/partition_column_prune.result | 6 +- mysql-test/r/partition_pruning.result | 2 +- mysql-test/r/ps_ddl.result | 4 +- mysql-test/r/sp.result | 6 +- mysql-test/r/view.result | 4 +- mysql-test/r/view_grant.result | 4 +- mysql-test/r/view_multi.result | 48 + mysql-test/suite/rpl/t/disabled.def | 3 + mysql-test/t/create.test | 8 +- mysql-test/t/disabled.def | 1 + mysql-test/t/flush.test | 14 +- mysql-test/t/flush_table.test | 29 +- mysql-test/t/information_schema.test | 99 +- mysql-test/t/kill.test | 226 ++ mysql-test/t/lock.test | 3 +- mysql-test/t/lock_multi.test | 34 +- mysql-test/t/ps_ddl.test | 4 +- mysql-test/t/sp.test | 4 +- mysql-test/t/trigger_notembedded.test | 2 +- mysql-test/t/view.test | 4 +- mysql-test/t/view_grant.test | 4 +- mysql-test/t/view_multi.test | 110 + sql/CMakeLists.txt | 2 +- sql/Makefile.am | 5 +- sql/event_db_repository.cc | 4 + sql/ha_ndbcluster.cc | 23 +- sql/ha_ndbcluster_binlog.cc | 33 +- sql/handler.cc | 9 +- sql/lock.cc | 382 +-- sql/log_event.cc | 22 +- sql/log_event_old.cc | 36 +- sql/mdl.cc | 1342 +++++++++++ sql/mdl.h | 260 ++ sql/mysql_priv.h | 53 +- sql/mysqld.cc | 5 +- sql/rpl_rli.cc | 21 +- sql/rpl_rli.h | 1 + sql/set_var.cc | 2 +- sql/sp_head.cc | 7 +- sql/sql_acl.cc | 16 +- sql/sql_base.cc | 2491 +++++++++++--------- sql/sql_binlog.cc | 2 +- sql/sql_class.cc | 20 +- sql/sql_class.h | 13 +- sql/sql_db.cc | 4 - sql/sql_delete.cc | 27 +- sql/sql_handler.cc | 136 +- sql/sql_insert.cc | 13 +- sql/sql_parse.cc | 118 +- sql/sql_partition.cc | 10 +- sql/sql_plist.h | 125 + sql/sql_plugin.cc | 4 + sql/sql_prepare.cc | 2 +- sql/sql_rename.cc | 17 +- sql/sql_servers.cc | 11 +- sql/sql_show.cc | 52 +- sql/sql_table.cc | 508 ++-- sql/sql_test.cc | 40 +- sql/sql_trigger.cc | 46 +- sql/sql_udf.cc | 3 + sql/sql_update.cc | 4 +- sql/sql_view.cc | 92 +- sql/table.cc | 20 + sql/table.h | 80 +- storage/myisammrg/ha_myisammrg.cc | 6 + 77 files changed, 4804 insertions(+), 2135 deletions(-) create mode 100644 mysql-test/r/view_multi.result create mode 100644 mysql-test/t/view_multi.test create mode 100644 sql/mdl.cc create mode 100644 sql/mdl.h create mode 100644 sql/sql_plist.h diff --git a/.bzrignore b/.bzrignore index 351417a4353..bc3ca7b0c24 100644 --- a/.bzrignore +++ b/.bzrignore @@ -3070,3 +3070,4 @@ libmysqld/rpl_handler.cc libmysqld/debug_sync.cc libmysqld/rpl_handler.cc dbug/tests +libmysqld/mdl.cc diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 65b8e12bc26..655082f0304 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -133,6 +133,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/partition_info.cc ../sql/sql_connect.cc ../sql/scheduler.cc ../sql/event_parse_data.cc ../sql/sql_signal.cc ../sql/rpl_handler.cc + ../sql/mdl.cc ${GEN_SOURCES} ${LIB_SOURCES}) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index ec73741eaaf..3a7fa934778 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -79,7 +79,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ sql_tablespace.cc \ rpl_injector.cc my_user.c partition_info.cc \ sql_servers.cc event_parse_data.cc sql_signal.cc \ - rpl_handler.cc + rpl_handler.cc mdl.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources) diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 6e7f53ba9b2..8ff38c7e7a1 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -518,12 +518,15 @@ connect (flush,localhost,root,,); connection flush; --echo connection: flush --send flush tables; -connection default; ---echo connection: default +connect (waiter,localhost,root,,); +connection waiter; +--echo connection: waiter let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Flushing tables"; --source include/wait_condition.inc +connection default; +--echo connection: default handler t2 open; handler t2 read first; handler t1 read next; @@ -550,12 +553,14 @@ connect (flush,localhost,root,,); connection flush; --echo connection: flush --send rename table t1 to t2; -connection default; ---echo connection: default +connection waiter; +--echo connection: waiter let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "rename table t1 to t2"; --source include/wait_condition.inc +connection default; +--echo connection: default handler t2 open; handler t2 read first; --error ER_NO_SUCH_TABLE @@ -566,7 +571,13 @@ connection flush; reap; connection default; drop table t2; +connection flush; disconnect flush; +--source include/wait_until_disconnected.inc +connection waiter; +disconnect waiter; +--source include/wait_until_disconnected.inc +connection default; # # Bug#30882 Dropping a temporary table inside a stored function may cause a server crash @@ -699,19 +710,24 @@ handler t1 read a next; # Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table # +connect(con1,localhost,root,,); +connect(con2,localhost,root,,); + +connection default; --disable_warnings drop table if exists t1; --enable_warnings create table t1 (a int); insert into t1 values (1); handler t1 open; -connect(con1,localhost,root,,); +connection con1; send alter table t1 engine=memory; -connection default; +connection con2; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "rename result table" and info = "alter table t1 engine=memory"; + where state = "Waiting for table" and info = "alter table t1 engine=memory"; --source include/wait_condition.inc +connection default; --error ER_ILLEGAL_HA handler t1 read a next; handler t1 close; @@ -720,6 +736,9 @@ connection con1; drop table t1; disconnect con1; --source include/wait_until_disconnected.inc +connection con2; +disconnect con2; +--source include/wait_until_disconnected.inc connection default; # diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 471cc6e9a3d..cf424b8b058 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -785,7 +785,7 @@ drop table t1; create table t1 select * from t2; ERROR 42S02: Table 'test.t2' doesn't exist create table t1 select * from t1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR 42S02: Table 'test.t1' doesn't exist create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin); ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce' create table t1 (primary key(a)) select "b" as b; @@ -805,6 +805,11 @@ Note 1050 Table 't1' already exists select * from t1; i 1 +create table if not exists t1 select * from t1; +ERROR HY000: You can't specify target table 't1' for update in FROM clause +select * from t1; +i +1 create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin); ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce' select * from t1; diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index b978304f59d..2be426d3a4a 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -33,6 +33,9 @@ flush tables with read lock; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction lock table t1 read; flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +flush tables with read lock; lock table t1 write; ERROR HY000: Can't execute the query because you have a conflicting read lock lock table t1 read; @@ -46,6 +49,7 @@ flush tables with read lock; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction lock table t1 read, t2 read, t3 read; flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; drop table t1, t2, t3; create table t1 (c1 int); @@ -69,6 +73,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl unlock tables; lock tables t1 read; flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; drop table t1, t2; set session low_priority_updates=default; diff --git a/mysql-test/r/flush_table.result b/mysql-test/r/flush_table.result index 8821bade6b4..2b0ee1cb205 100644 --- a/mysql-test/r/flush_table.result +++ b/mysql-test/r/flush_table.result @@ -3,21 +3,14 @@ create table t1 (a int not null auto_increment primary key); insert into t1 values(0); lock table t1 read; flush table t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +unlock tables; +lock table t1 write; +flush table t1; check table t1; Table Op Msg_type Msg_text test.t1 check status OK unlock tables; -lock table t1 read; -lock table t1 read; -flush table t1; -select * from t1; -a -1 -unlock tables; -select * from t1; -a -1 -unlock tables; lock table t1 write; lock table t1 read; flush table t1; @@ -26,7 +19,7 @@ a 1 unlock tables; unlock tables; -lock table t1 read; +lock table t1 write; lock table t1 write; flush table t1; select * from t1; diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index 957fc30acef..5990b19062b 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -548,6 +548,7 @@ c1 1 connection: flush flush tables;; +connection: waiter connection: default handler t2 open; handler t2 read first; @@ -567,6 +568,7 @@ handler t1 read first; c1 connection: flush rename table t1 to t2;; +connection: waiter connection: default handler t2 open; handler t2 read first; diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index 90a1bdfe6be..f7b0ff2e04e 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -547,6 +547,7 @@ c1 1 connection: flush flush tables;; +connection: waiter connection: default handler t2 open; handler t2 read first; @@ -566,6 +567,7 @@ handler t1 read first; c1 connection: flush rename table t1 to t2;; +connection: waiter connection: default handler t2 open; handler t2 read first; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 04234eb3cc4..a66e494dcd2 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1644,6 +1644,57 @@ TEST_RESULT OK SET TIMESTAMP=DEFAULT; End of 5.1 tests. +# +# Additional test for WL#3726 "DDL locking for all metadata objects" +# To avoid possible deadlocks process of filling of I_S tables should +# use high-priority metadata lock requests when opening tables. +# Below we just test that we really use high-priority lock request +# since reproducing a deadlock will require much more complex test. +# +drop tables if exists t1, t2, t3; +create table t1 (i int); +create table t2 (j int primary key auto_increment); +# Switching to connection 'con3726_1' +lock table t2 read; +# Switching to connection 'con3726_2' +# RENAME below will be blocked by 'lock table t2 read' above but +# will add two pending requests for exclusive metadata locks. +rename table t2 to t3; +# Switching to connection 'default' +# These statements should not be blocked by pending lock requests +select table_name, column_name, data_type from information_schema.columns +where table_schema = 'test' and table_name in ('t1', 't2'); +table_name column_name data_type +t1 i int +t2 j int +select table_name, auto_increment from information_schema.tables +where table_schema = 'test' and table_name in ('t1', 't2'); +table_name auto_increment +t1 NULL +t2 1 +# Switching to connection 'con3726_1' +unlock tables; +# Switching to connection 'con3726_2' +# Switching to connection 'default' +drop tables t1, t3; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE KEY_COLUMN_USAGE ALL NULL NULL NULL NULL NULL Open_full_table; Scanned all databases +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE PARTITIONS ALL NULL TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 1 database +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS +WHERE CONSTRAINT_SCHEMA='test'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE REFERENTIAL_CONSTRAINTS ALL NULL CONSTRAINT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS +WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE TABLE_CONSTRAINTS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 0 databases +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS +WHERE EVENT_OBJECT_SCHEMA='test'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE TRIGGERS ALL NULL EVENT_OBJECT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database create table information_schema.t1 (f1 INT); ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' drop table information_schema.t1; @@ -1682,28 +1733,10 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_ LOCK TABLES t1 READ, information_schema.tables READ; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' DROP TABLE t1; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE KEY_COLUMN_USAGE ALL NULL NULL NULL NULL NULL Open_full_table; Scanned all databases -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE PARTITIONS ALL NULL TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 1 database -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS -WHERE CONSTRAINT_SCHEMA='test'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE REFERENTIAL_CONSTRAINTS ALL NULL CONSTRAINT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE TABLE_CONSTRAINTS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 0 databases -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS -WHERE EVENT_OBJECT_SCHEMA='test'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE TRIGGERS ALL NULL EVENT_OBJECT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database SELECT * -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -LEFT JOIN INFORMATION_SCHEMA.COLUMNS -USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) -WHERE COLUMNS.TABLE_SCHEMA = 'test' +FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +LEFT JOIN INFORMATION_SCHEMA.COLUMNS +USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) +WHERE COLUMNS.TABLE_SCHEMA = 'test' AND COLUMNS.TABLE_NAME = 't1'; TABLE_SCHEMA TABLE_NAME COLUMN_NAME CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME TABLE_CATALOG ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index 8b6830d4798..1f4f4bb32eb 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -138,4 +138,107 @@ KILL CONNECTION_ID(); # of close of the connection socket SELECT 1; Got one of the listed errors +# +# Additional test for WL#3726 "DDL locking for all metadata objects" +# Check that DDL and DML statements waiting for metadata locks can +# be killed. Note that we don't cover all situations here since it +# can be tricky to write test case for some of them (e.g. REPAIR or +# ALTER and other statements under LOCK TABLES). +# +drop tables if exists t1, t2, t3; +create table t1 (i int primary key); +# Test for RENAME TABLE +# Switching to connection 'blocker' +lock table t1 read; +# Switching to connection 'ddl' +rename table t1 to t2; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Test for DROP TABLE +drop table t1; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Test for CREATE TRIGGER +create trigger t1_bi before insert on t1 for each row set @a:=1; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# +# Tests for various kinds of ALTER TABLE +# +# Full-blown ALTER which should copy table +alter table t1 add column j int; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Two kinds of simple ALTER +alter table t1 rename to t2; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +alter table t1 disable keys; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Fast ALTER +alter table t1 alter column i set default 100; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Special case which is triggered only for MERGE tables. +# Switching to connection 'blocker' +unlock tables; +create table t2 (i int primary key) engine=merge union=(t1); +lock tables t2 read; +# Switching to connection 'ddl' +alter table t2 alter column i set default 100; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Test for DML waiting for meta-data lock +# Switching to connection 'blocker' +unlock tables; +drop table t2; +create table t2 (k int); +lock tables t1 read; +# Switching to connection 'ddl' +rename tables t1 to t3, t2 to t1; +# Switching to connection 'dml' +insert into t2 values (1); +# Switching to connection 'default' +kill query ID2; +# Switching to connection 'dml' +ERROR 70100: Query execution was interrupted +# Switching to connection 'blocker' +unlock tables; +# Switching to connection 'ddl' +# Test for DML waiting for tables to be flushed +# Switching to connection 'blocker' +lock tables t1 read; +# Switching to connection 'ddl' +# Let us mark locked table t1 as old +flush tables; +# Switching to connection 'dml' +select * from t1; +# Switching to connection 'default' +kill query ID2; +# Switching to connection 'dml' +ERROR 70100: Query execution was interrupted +# Switching to connection 'blocker' +unlock tables; +# Switching to connection 'ddl' +# Cleanup. +# Switching to connection 'default' +drop table t3; +drop table t1; set @@global.concurrent_insert= @old_concurrent_insert; diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 1f8f6aa04ae..a60c5c8383f 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -128,13 +128,14 @@ select * from v_bug5719; 1 1 drop view v_bug5719; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction sic: did not left LOCK TABLES mode automatically select * from t1; ERROR HY000: Table 't1' was not locked with LOCK TABLES unlock tables; -create view v_bug5719 as select * from t1; +create or replace view v_bug5719 as select * from t1; lock tables v_bug5719 write; select * from v_bug5719; a diff --git a/mysql-test/r/partition_column_prune.result b/mysql-test/r/partition_column_prune.result index 82c49453d43..844429d24a6 100644 --- a/mysql-test/r/partition_column_prune.result +++ b/mysql-test/r/partition_column_prune.result @@ -56,11 +56,11 @@ insert into t1 values (2,5), (2,15), (2,25), insert into t1 select * from t1; explain partitions select * from t1 where a=2; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 p01,p02,p03,p11 ALL NULL NULL NULL NULL 13 Using where +1 SIMPLE t1 p01,p02,p03,p11 ALL NULL NULL NULL NULL 8 Using where explain partitions select * from t1 where a=4; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 p11,p12,p13,p21 ALL NULL NULL NULL NULL 16 Using where +1 SIMPLE t1 p11,p12,p13,p21 ALL NULL NULL NULL NULL 14 Using where explain partitions select * from t1 where a=2 and b < 22; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 p01,p02,p03 ALL NULL NULL NULL NULL 16 Using where +1 SIMPLE t1 p01,p02,p03 ALL NULL NULL NULL NULL 14 Using where drop table t1; diff --git a/mysql-test/r/partition_pruning.result b/mysql-test/r/partition_pruning.result index d7790cd8075..4c22c25ddb9 100644 --- a/mysql-test/r/partition_pruning.result +++ b/mysql-test/r/partition_pruning.result @@ -1960,7 +1960,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra explain partitions select * from t1 X, t1 Y where X.a = Y.a and (X.a=1 or X.a=2); id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE X p1,p2 ALL a NULL NULL NULL 4 Using where +1 SIMPLE X p1,p2 ALL a NULL NULL NULL 2 Using where 1 SIMPLE Y p1,p2 ref a a 4 test.X.a 2 drop table t1; create table t1 (a int) partition by hash(a) partitions 20; diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index c7e8812320c..af0ffaf4ae5 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -850,7 +850,7 @@ flush table t1; execute stmt; f1() 6 -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); SUCCESS execute stmt; @@ -1706,7 +1706,7 @@ SUCCESS drop temporary table t2; execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS drop table t2; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a73abf787d8..47d441ee182 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1083,11 +1083,9 @@ select f0()| f0() 100 select * from v0| -f0() -100 +ERROR HY000: Table 'v0' was not locked with LOCK TABLES select *, f0() from v0, (select 123) as d1| -f0() 123 f0() -100 123 100 +ERROR HY000: Table 'v0' was not locked with LOCK TABLES select id, f3() from t1| ERROR HY000: Table 't1' was not locked with LOCK TABLES select f4()| diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 2df2b0bafa6..5f16d88a0dc 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1102,10 +1102,8 @@ select * from v1; a select * from t2; ERROR HY000: Table 't2' was not locked with LOCK TABLES -drop view v1; -drop table t1, t2; -ERROR HY000: Table 't1' was locked with a READ lock and can't be updated unlock tables; +drop view v1; drop table t1, t2; create table t1 (a int); create view v1 as select * from t1 where a < 2 with check option; diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 65fbf2d87b6..0c74d8ed91b 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -779,9 +779,9 @@ GRANT CREATE VIEW ON db26813.v2 TO u26813@localhost; GRANT DROP, CREATE VIEW ON db26813.v3 TO u26813@localhost; GRANT SELECT ON db26813.t1 TO u26813@localhost; ALTER VIEW v1 AS SELECT f2 FROM t1; -ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +ERROR 42000: CREATE VIEW command denied to user 'u26813'@'localhost' for table 'v1' ALTER VIEW v2 AS SELECT f2 FROM t1; -ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +ERROR 42000: DROP command denied to user 'u26813'@'localhost' for table 'v2' ALTER VIEW v3 AS SELECT f2 FROM t1; ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation SHOW CREATE VIEW v3; diff --git a/mysql-test/r/view_multi.result b/mysql-test/r/view_multi.result new file mode 100644 index 00000000000..95a8d572be4 --- /dev/null +++ b/mysql-test/r/view_multi.result @@ -0,0 +1,48 @@ +reset master; +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +insert into v1 values (get_lock("lock_bg25144", 100));; +drop view v1;; +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select * from t1; +i +1 +create view v1 as select * from t1; +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +insert into v1 values (get_lock("lock_bg25144", 100));; +alter view v1 as select * from t2;; +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select * from t1; +i +1 +1 +select * from t2; +i +show binlog events in 'master-bin.000001' from 107; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t1 (i int) +master-bin.000001 # Query 1 # use `test`; create table t2 (i int) +master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) +master-bin.000001 # Query 1 # use `test`; drop view v1 +master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) +master-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t2 +drop table t1, t2; +drop view v1; diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index bed019f9c79..da962a7fb9d 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -13,3 +13,6 @@ rpl_get_master_version_and_clock: # Bug#46931 2009-10-17 joro rpl.rpl_get_master_version_and_clock fails rpl_cross_version : BUG#43913 2009-10-22 luis rpl_cross_version fails with symptom in described in bug report rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux +rpl_empty_master_crash : BUG#48048 +rpl_load_from_master : BUG#48048 +rpl_load_table_from_master : BUG#48048 diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 9f3c3a88151..c07014bfc19 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -683,8 +683,8 @@ drop table t1; # Error during open_and_lock_tables() of tables --error ER_NO_SUCH_TABLE create table t1 select * from t2; -# Rather special error which also caught during open tables pahse ---error ER_UPDATE_TABLE_USED +# A special case which is also caught during open tables pahse +--error ER_NO_SUCH_TABLE create table t1 select * from t1; # Error which happens before select_create::prepare() --error ER_CANT_AGGREGATE_2COLLATIONS @@ -706,6 +706,10 @@ create table t1 (i int); create table t1 select 1 as i; create table if not exists t1 select 1 as i; select * from t1; +# Error which is detected after successfull table open. +--error ER_UPDATE_TABLE_USED +create table if not exists t1 select * from t1; +select * from t1; # Error before select_create::prepare() --error ER_CANT_AGGREGATE_2COLLATIONS create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin); diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index ad7617b9403..bb2749e902b 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -15,3 +15,4 @@ partition_innodb_builtin : Bug#32430 2009-09-25 mattiasj Waiting for push of Inn partition_innodb_plugin : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes innodb-autoinc : Bug#48482 2009-11-02 svoj innodb-autoinc.test fails with results difference rpl_killed_ddl : Bug#45520: rpl_killed_ddl fails sporadically in pb2 +merge : WL#4144 diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index f27d4cf2fad..4172230a54d 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -68,10 +68,13 @@ drop table t1; create table t1 (c1 int); lock table t1 write; # Cannot get the global read lock with write locked tables. ---error 1192 +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; lock table t1 read; -# Can get the global read lock with read locked tables. +# Cannot get the global read lock with read locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +flush tables with read lock; +unlock tables; flush tables with read lock; --error 1223 lock table t1 write; @@ -84,12 +87,12 @@ create table t2 (c1 int); create table t3 (c1 int); lock table t1 read, t2 read, t3 write; # Cannot get the global read lock with write locked tables. ---error 1192 +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; lock table t1 read, t2 read, t3 read; -# Can get the global read lock with read locked tables. +# Cannot get the global read lock with read locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; -# Release all table locks and the global read lock. unlock tables; drop table t1, t2, t3; @@ -157,6 +160,7 @@ flush tables with read lock; unlock tables; lock tables t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; unlock tables; diff --git a/mysql-test/t/flush_table.test b/mysql-test/t/flush_table.test index 50e7e91419a..e4fc1b0c39f 100644 --- a/mysql-test/t/flush_table.test +++ b/mysql-test/t/flush_table.test @@ -15,30 +15,21 @@ insert into t1 values(0); # Test for with read lock + flush lock table t1 read; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +flush table t1; +unlock tables; + +# Test for with write lock + flush + +lock table t1 write; flush table t1; check table t1; unlock tables; -# Test for with 2 read lock in different thread + flush - -lock table t1 read; -connect (locker,localhost,root,,test); -connection locker; -lock table t1 read; -connection default; -send flush table t1; -connection locker; ---sleep 2 -select * from t1; -unlock tables; -connection default; -reap; -select * from t1; -unlock tables; - # Test for with a write lock and a waiting read lock + flush lock table t1 write; +connect (locker,localhost,root,,test); connection locker; send lock table t1 read; connection default; @@ -51,9 +42,9 @@ reap; unlock tables; connection default; -# Test for with a read lock and a waiting write lock + flush +# Test for with a write lock and a waiting write lock + flush -lock table t1 read; +lock table t1 write; connection locker; send lock table t1 write; connection default; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 9da7cc1042d..c6cb28a4a30 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -544,6 +544,7 @@ AND table_name not like 'ndb%' AND table_name not like 'innodb_%' GROUP BY TABLE_SCHEMA; + # # TRIGGERS table test # @@ -914,8 +915,8 @@ DROP PROCEDURE p1; DROP USER mysql_bug20230@localhost; # -# Bug#2123 query with a simple non-correlated subquery over -# INFORMARTION_SCHEMA.TABLES +# Bug#21231 query with a simple non-correlated subquery over +# INFORMARTION_SCHEMA.TABLES # SELECT MAX(table_name) FROM information_schema.tables WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test'); @@ -1391,9 +1392,66 @@ SET TIMESTAMP=DEFAULT; --echo End of 5.1 tests. +--echo # +--echo # Additional test for WL#3726 "DDL locking for all metadata objects" +--echo # To avoid possible deadlocks process of filling of I_S tables should +--echo # use high-priority metadata lock requests when opening tables. +--echo # Below we just test that we really use high-priority lock request +--echo # since reproducing a deadlock will require much more complex test. +--echo # +--disable_warnings +drop tables if exists t1, t2, t3; +--enable_warnings +create table t1 (i int); +create table t2 (j int primary key auto_increment); +connect (con3726_1,localhost,root,,test); +--echo # Switching to connection 'con3726_1' +connection con3726_1; +lock table t2 read; +connect (con3726_2,localhost,root,,test); +--echo # Switching to connection 'con3726_2' +connection con3726_2; +--echo # RENAME below will be blocked by 'lock table t2 read' above but +--echo # will add two pending requests for exclusive metadata locks. +--send rename table t2 to t3 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info like "rename table t2 to t3"; +--source include/wait_condition.inc +--echo # These statements should not be blocked by pending lock requests +select table_name, column_name, data_type from information_schema.columns + where table_schema = 'test' and table_name in ('t1', 't2'); +select table_name, auto_increment from information_schema.tables + where table_schema = 'test' and table_name in ('t1', 't2'); +--echo # Switching to connection 'con3726_1' +connection con3726_1; +unlock tables; +--echo # Switching to connection 'con3726_2' +connection con3726_2; +--reap +--echo # Switching to connection 'default' +connection default; +disconnect con3726_1; +disconnect con3726_2; +drop tables t1, t3; + +# +# Bug#39270 I_S optimization algorithm does not work properly in some cases +# +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS + WHERE CONSTRAINT_SCHEMA='test'; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS + WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS + WHERE EVENT_OBJECT_SCHEMA='test'; + # # Bug#24062 Incorrect error msg after execute DROP TABLE IF EXISTS on information_schema -# +# --error ER_DBACCESS_DENIED_ERROR create table information_schema.t1 (f1 INT); --error ER_DBACCESS_DENIED_ERROR @@ -1429,27 +1487,18 @@ DROP TABLE t1, information_schema.tables; LOCK TABLES t1 READ, information_schema.tables READ; DROP TABLE t1; -# -# Bug#39270 I_S optimization algorithm does not work properly in some cases -# -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS - WHERE CONSTRAINT_SCHEMA='test'; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS - WHERE EVENT_OBJECT_SCHEMA='test'; - -# -# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query -# -SELECT * -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -LEFT JOIN INFORMATION_SCHEMA.COLUMNS -USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) -WHERE COLUMNS.TABLE_SCHEMA = 'test' -AND COLUMNS.TABLE_NAME = 't1'; - # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc + +# +# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query +# + +SELECT * +FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +LEFT JOIN INFORMATION_SCHEMA.COLUMNS +USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) +WHERE COLUMNS.TABLE_SCHEMA = 'test' +AND COLUMNS.TABLE_NAME = 't1'; + + diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index 02b033df2e5..98cb9f64d98 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -329,6 +329,232 @@ KILL CONNECTION_ID(); SELECT 1; --connection default +--echo # +--echo # Additional test for WL#3726 "DDL locking for all metadata objects" +--echo # Check that DDL and DML statements waiting for metadata locks can +--echo # be killed. Note that we don't cover all situations here since it +--echo # can be tricky to write test case for some of them (e.g. REPAIR or +--echo # ALTER and other statements under LOCK TABLES). +--echo # +--disable_warnings +drop tables if exists t1, t2, t3; +--enable_warnings + +create table t1 (i int primary key); +connect (blocker, localhost, root, , ); +connect (dml, localhost, root, , ); +connect (ddl, localhost, root, , ); + +--echo # Test for RENAME TABLE +--echo # Switching to connection 'blocker' +connection blocker; +lock table t1 read; +--echo # Switching to connection 'ddl' +connection ddl; +let $ID= `select connection_id()`; +--send rename table t1 to t2 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t1 to t2"; +--source include/wait_condition.inc +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Test for DROP TABLE +--send drop table t1 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "drop table t1"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Test for CREATE TRIGGER +--send create trigger t1_bi before insert on t1 for each row set @a:=1 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "create trigger t1_bi before insert on t1 for each row set @a:=1"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # +--echo # Tests for various kinds of ALTER TABLE +--echo # +--echo # Full-blown ALTER which should copy table +--send alter table t1 add column j int +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 add column j int"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Two kinds of simple ALTER +--send alter table t1 rename to t2 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 rename to t2"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap +--send alter table t1 disable keys +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 disable keys"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Fast ALTER +--send alter table t1 alter column i set default 100 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 alter column i set default 100"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Special case which is triggered only for MERGE tables. +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +create table t2 (i int primary key) engine=merge union=(t1); +lock tables t2 read; +--echo # Switching to connection 'ddl' +connection ddl; +--send alter table t2 alter column i set default 100 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t2 alter column i set default 100"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Test for DML waiting for meta-data lock +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +drop table t2; +create table t2 (k int); +lock tables t1 read; +--echo # Switching to connection 'ddl' +connection ddl; +# Let us add pending exclusive metadata lock on t2 +--send rename tables t1 to t3, t2 to t1 +--echo # Switching to connection 'dml' +connection dml; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "rename tables t1 to t3, t2 to t1"; +let $ID2= `select connection_id()`; +--send insert into t2 values (1) +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "insert into t2 values (1)"; +--replace_result $ID2 ID2 +eval kill query $ID2; +--echo # Switching to connection 'dml' +connection dml; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +--echo # Switching to connection 'ddl' +connection ddl; +--reap + +--echo # Test for DML waiting for tables to be flushed +--echo # Switching to connection 'blocker' +connection blocker; +lock tables t1 read; +--echo # Switching to connection 'ddl' +connection ddl; +--echo # Let us mark locked table t1 as old +--send flush tables +--echo # Switching to connection 'dml' +connection dml; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Flushing tables" and + info = "flush tables"; +--send select * from t1 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "select * from t1"; +--replace_result $ID2 ID2 +eval kill query $ID2; +--echo # Switching to connection 'dml' +connection dml; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +--echo # Switching to connection 'ddl' +connection ddl; +--reap + +--echo # Cleanup. +--echo # Switching to connection 'default' +connection default; +drop table t3; +drop table t1; + ########################################################################### # Restore global concurrent_insert value. Keep in the end of the test file. diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 04994e3e48f..856ae020492 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -178,6 +178,7 @@ select * from t2; --error ER_TABLE_NOT_LOCKED select * from t3; select * from v_bug5719; +--error ER_LOCK_OR_ACTIVE_TRANSACTION drop view v_bug5719; --echo --echo sic: did not left LOCK TABLES mode automatically @@ -185,7 +186,7 @@ drop view v_bug5719; --error ER_TABLE_NOT_LOCKED select * from t1; unlock tables; -create view v_bug5719 as select * from t1; +create or replace view v_bug5719 as select * from t1; lock tables v_bug5719 write; select * from v_bug5719; --echo diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 75ee6d07723..58f46941920 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -164,7 +164,7 @@ connection locker; # Sleep a bit till the select of connection reader is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and info = + where state = "Locked" and info = "SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1"; --source include/wait_condition.inc # Make test case independent from earlier grants. @@ -196,7 +196,7 @@ connection writer; # Sleep a bit till the flush of connection locker is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables" and info = "FLUSH TABLES WITH READ LOCK"; + where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; --source include/wait_condition.inc # This must not block. CREATE TABLE t2 (c1 int); @@ -226,7 +226,7 @@ connection writer; # Sleep a bit till the flush of connection locker is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables" and info = "FLUSH TABLES WITH READ LOCK"; + where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; --source include/wait_condition.inc --error ER_TABLE_NOT_LOCKED CREATE TABLE t2 AS SELECT * FROM t1; @@ -337,10 +337,10 @@ connection con2; send flush tables with read lock; connection con5; --echo # con5 -let $show_statement= SHOW PROCESSLIST; -let $field= State; -let $condition= = 'Flushing tables'; ---source include/wait_show_condition.inc +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "flush tables with read lock"; +--source include/wait_condition.inc --echo # global read lock is taken connection con3; --echo # con3 @@ -528,7 +528,7 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc alter table t1 add column j int; connect (insert,localhost,root,,test,,); @@ -536,7 +536,7 @@ connection insert; --echo connection: insert let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc --send insert into t1 values (1,2); --echo connection: default @@ -586,18 +586,14 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc flush tables; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc unlock tables; -let $wait_condition= - select count(*) = 0 from information_schema.processlist - where state = "Flushing tables"; ---source include/wait_condition.inc connection flush; --reap connection default; @@ -656,18 +652,14 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc flush tables; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc drop table t1; -let $wait_condition= - select count(*) = 0 from information_schema.processlist - where state = "Flushing tables"; ---source include/wait_condition.inc connection flush; --reap connection default; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index fee235cd36c..6e771d44b3f 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -745,7 +745,7 @@ execute stmt; call p_verify_reprepare_count(1); flush table t1; execute stmt; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); execute stmt; --echo # Test 18-c: dependent VIEW has changed @@ -1453,7 +1453,7 @@ execute stmt; call p_verify_reprepare_count(0); drop temporary table t2; execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); drop table t2; execute stmt; call p_verify_reprepare_count(0); diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index b0342491a34..b477b5a3e24 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1308,9 +1308,11 @@ select f3()| select id, f3() from t1 as t11 order by id| # Degenerate cases work too :) select f0()| +# But these should not (particularly views should be locked explicitly). +--error ER_TABLE_NOT_LOCKED select * from v0| +--error ER_TABLE_NOT_LOCKED select *, f0() from v0, (select 123) as d1| -# But these should not ! --error ER_TABLE_NOT_LOCKED select id, f3() from t1| --error ER_TABLE_NOT_LOCKED diff --git a/mysql-test/t/trigger_notembedded.test b/mysql-test/t/trigger_notembedded.test index 7a7e6c6bc85..8a570a7e87d 100644 --- a/mysql-test/t/trigger_notembedded.test +++ b/mysql-test/t/trigger_notembedded.test @@ -896,7 +896,7 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc create trigger t1_bi before insert on t1 for each row begin end; unlock tables; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index abf8dac2870..c3ff58880c9 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1016,10 +1016,8 @@ lock tables t1 read, v1 read; select * from v1; -- error ER_TABLE_NOT_LOCKED select * from t2; -drop view v1; ---error ER_TABLE_NOT_LOCKED_FOR_WRITE -drop table t1, t2; unlock tables; +drop view v1; drop table t1, t2; # diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index f01edb1e499..d94bcfc29ed 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -1047,9 +1047,9 @@ GRANT SELECT ON db26813.t1 TO u26813@localhost; connect (u1,localhost,u26813,,db26813); connection u1; ---error ER_SPECIFIC_ACCESS_DENIED_ERROR +--error ER_TABLEACCESS_DENIED_ERROR ALTER VIEW v1 AS SELECT f2 FROM t1; ---error ER_SPECIFIC_ACCESS_DENIED_ERROR +--error ER_TABLEACCESS_DENIED_ERROR ALTER VIEW v2 AS SELECT f2 FROM t1; --error ER_SPECIFIC_ACCESS_DENIED_ERROR ALTER VIEW v3 AS SELECT f2 FROM t1; diff --git a/mysql-test/t/view_multi.test b/mysql-test/t/view_multi.test new file mode 100644 index 00000000000..a61e7738095 --- /dev/null +++ b/mysql-test/t/view_multi.test @@ -0,0 +1,110 @@ +# +# QQ: Should we find a better place for this test? +# May be binlog or rpl suites ? +# +--source include/have_log_bin.inc +--source include/have_binlog_format_mixed_or_statement.inc + +# +# Bug #25144 "replication / binlog with view breaks". +# Statements that used views didn't ensure that view were not modified +# during their execution. Indeed this led to incorrect binary log with +# statement based logging. +# +--disable_parsing +drop table if not exists t1, t2; +drop view if exists v1; +--enable_parsing + +# We are going to use binary log later to check that statements are +# logged in proper order, so it is good idea to reset it here. +reset master; + +connect (addconn1,localhost,root,,); +connect (addconn2,localhost,root,,); +connection default; + +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; + +# First we try to concurrently execute statement that uses view +# and statement that drops it. We use "user" locks as means to +# suspend execution of first statement once it opens our view. +select get_lock("lock_bg25144", 1); + +connection addconn1; +--send insert into v1 values (get_lock("lock_bg25144", 100)); + +connection addconn2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send drop view v1; + +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "drop view v1"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +connection addconn1; +--reap +select release_lock("lock_bg25144"); + +connection addconn2; +--reap + +connection default; +# Check that insertion through view did happen. +select * from t1; +# At the end of test we will check that statements were +# logged in proper order. + +# Now we will repeat the test by trying concurrently execute +# statement that uses a view and statement that alters it. +create view v1 as select * from t1; + +select get_lock("lock_bg25144", 1); + +connection addconn1; +--send insert into v1 values (get_lock("lock_bg25144", 100)); + +connection addconn2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send alter view v1 as select * from t2; + +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter view v1 as select * from t2"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +connection addconn1; +--reap +select release_lock("lock_bg25144"); + +connection addconn2; +--reap + +connection default; + +# Second insertion should go to t1 as well. +select * from t1; +select * from t2; + +# Now let us check that statements were logged in proper order +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 107; + +drop table t1, t2; +drop view v1; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 15c2d950ff9..a40cf04f37c 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -76,7 +76,7 @@ SET (SQL_SOURCE rpl_rli.cc rpl_mi.cc sql_servers.cc sql_connect.cc scheduler.cc sql_profile.cc event_parse_data.cc - sql_signal.cc rpl_handler.cc + sql_signal.cc rpl_handler.cc mdl.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h diff --git a/sql/Makefile.am b/sql/Makefile.am index 15ee0d588c4..95864dfbb1b 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -112,7 +112,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ event_data_objects.h event_scheduler.h \ sql_partition.h partition_info.h partition_element.h \ contributors.h sql_servers.h sql_signal.h records.h \ - sql_prepare.h rpl_handler.h replication.h + sql_prepare.h rpl_handler.h replication.h mdl.h \ + sql_plist.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -158,7 +159,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_servers.cc event_parse_data.cc sql_signal.cc \ - rpl_handler.cc + rpl_handler.cc mdl.cc nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 8faab5023da..0cf16e3a8a4 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -555,6 +555,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, DBUG_ENTER("Event_db_repository::open_event_table"); tables.init_one_table("mysql", "event", lock_type); + alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1109,6 +1110,7 @@ Event_db_repository::check_system_tables(THD *thd) /* Check mysql.db */ tables.init_one_table("mysql", "db", TL_READ); + alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1126,6 +1128,7 @@ Event_db_repository::check_system_tables(THD *thd) } /* Check mysql.user */ tables.init_one_table("mysql", "user", TL_READ); + alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1146,6 +1149,7 @@ Event_db_repository::check_system_tables(THD *thd) } /* Check mysql.event */ tables.init_one_table("mysql", "event", TL_READ); + alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index b71a7f602ab..a7c4cbf5d20 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -565,7 +565,7 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans) bzero((char*) &table_list,sizeof(table_list)); table_list.db= m_dbname; table_list.alias= table_list.table_name= m_tabname; - close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE); + close_cached_tables(thd, &table_list, FALSE, FALSE); break; } default: @@ -7276,6 +7276,20 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, } } + /* + ndbcluster_find_files() may be called from I_S code and ndbcluster_binlog + thread in situations when some tables are already open. This means that + code below will try to obtain exclusive metadata lock on some table + while holding shared meta-data lock on other tables. This might lead to + a deadlock, and therefore is disallowed by assertions of the metadata + locking subsystem. In order to temporarily make the code work, we must + reset and backup the open tables state, thus hide the existing locks + from MDL asserts. But in the essence this is violation of metadata + locking protocol which has to be closed ASAP. + */ + Open_tables_state open_tables_state_backup; + thd->reset_n_backup_open_tables_state(&open_tables_state_backup); + if (!global_read_lock) { // Delete old files @@ -7299,8 +7313,11 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, } } + thd->restore_backup_open_tables_state(&open_tables_state_backup); + + /* Lock mutex before creating .FRM files. */ pthread_mutex_lock(&LOCK_open); - // Create new files + /* Create new files. */ List_iterator_fast it2(create_list); while ((file_name_str=it2++)) { @@ -8215,7 +8232,7 @@ int handle_trailing_share(NDB_SHARE *share) table_list.db= share->db; table_list.alias= table_list.table_name= share->table_name; safe_mutex_assert_owner(&LOCK_open); - close_cached_tables(thd, &table_list, TRUE, FALSE, FALSE); + close_cached_tables(thd, &table_list, TRUE, FALSE); pthread_mutex_lock(&ndbcluster_mutex); /* ndb_share reference temporary free */ diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index e34a22cf9f4..bf76960201d 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -140,6 +140,8 @@ static Uint64 *p_latest_trans_gci= 0; */ static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; +static MDL_LOCK binlog_mdl_lock; +static char binlog_mdlkey[MAX_DBKEY_LENGTH]; /* Helper functions @@ -282,7 +284,13 @@ static void run_query(THD *thd, char *buf, char *end, thd_ndb->m_error_code, (int) thd->is_error(), thd->is_slave_error); } + + /* + After executing statement we should unlock and close tables open + by it as well as release meta-data locks obtained by it. + */ close_thread_tables(thd); + /* XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command() can not be called from within a statement, and @@ -921,7 +929,7 @@ int ndbcluster_setup_binlog_table_shares(THD *thd) ndb_binlog_tables_inited= TRUE; if (ndb_extra_logging) sql_print_information("NDB Binlog: ndb tables writable"); - close_cached_tables(NULL, NULL, TRUE, FALSE, FALSE); + close_cached_tables(NULL, NULL, TRUE, FALSE); pthread_mutex_unlock(&LOCK_open); /* Signal injector thread that all is setup */ pthread_cond_signal(&injector_cond); @@ -1735,7 +1743,7 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, bzero((char*) &table_list,sizeof(table_list)); table_list.db= (char *)dbname; table_list.alias= table_list.table_name= (char *)tabname; - close_cached_tables(thd, &table_list, TRUE, FALSE, FALSE); + close_cached_tables(thd, &table_list, TRUE, FALSE); if ((error= ndbcluster_binlog_open_table(thd, share, table_share, table, 1))) @@ -1841,7 +1849,7 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, bzero((char*) &table_list,sizeof(table_list)); table_list.db= (char *)dbname; table_list.alias= table_list.table_name= (char *)tabname; - close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE); + close_cached_tables(thd, &table_list, FALSE, FALSE); /* ndb_share reference create free */ DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u", share->key, share->use_count)); @@ -1962,7 +1970,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, bzero((char*) &table_list,sizeof(table_list)); table_list.db= schema->db; table_list.alias= table_list.table_name= schema->name; - close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE); + close_cached_tables(thd, &table_list, FALSE, FALSE); } /* ndb_share reference temporary free */ if (share) @@ -2079,7 +2087,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, pthread_mutex_unlock(&ndb_schema_share_mutex); /* end protect ndb_schema_share */ - close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE); + close_cached_tables(NULL, NULL, FALSE, FALSE); // fall through case NDBEVENT::TE_ALTER: ndb_handle_schema_change(thd, ndb, pOp, tmp_share); @@ -2236,7 +2244,7 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, bzero((char*) &table_list,sizeof(table_list)); table_list.db= schema->db; table_list.alias= table_list.table_name= schema->name; - close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE); + close_cached_tables(thd, &table_list, FALSE, FALSE); } if (schema_type != SOT_ALTER_TABLE) break; @@ -2323,18 +2331,21 @@ struct ndb_binlog_index_row { /* Open the ndb_binlog_index table */ -static int open_ndb_binlog_index(THD *thd, TABLE_LIST *tables, - TABLE **ndb_binlog_index) +static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) { static char repdb[]= NDB_REP_DB; static char reptable[]= NDB_REP_TABLE; const char *save_proc_info= thd->proc_info; + TABLE_LIST *tables= &binlog_tables; bzero((char*) tables, sizeof(*tables)); tables->db= repdb; tables->alias= tables->table_name= reptable; tables->lock_type= TL_WRITE; thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE; + mdl_init_lock(&binlog_mdl_lock, binlog_mdlkey, 0, tables->db, + tables->table_name); + tables->mdl_lock= &binlog_mdl_lock; tables->required_type= FRMTYPE_TABLE; uint counter; thd->clear_error(); @@ -2374,7 +2385,7 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) for ( ; ; ) /* loop for need_reopen */ { - if (!ndb_binlog_index && open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index)) + if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index)) { error= -1; goto add_ndb_binlog_index_err; @@ -2385,7 +2396,7 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) if (need_reopen) { TABLE_LIST *p_binlog_tables= &binlog_tables; - close_tables_for_reopen(thd, &p_binlog_tables); + close_tables_for_reopen(thd, &p_binlog_tables, FALSE); ndb_binlog_index= 0; continue; } @@ -3893,7 +3904,7 @@ restart: static char db[]= ""; thd->db= db; if (ndb_binlog_running) - open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index); + open_ndb_binlog_index(thd, &ndb_binlog_index); thd->db= db; } do_ndbcluster_binlog_close_connection= BCCC_running; diff --git a/sql/handler.cc b/sql/handler.cc index 17a92b00b4f..9c32171eefd 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2997,20 +2997,13 @@ static bool update_frm_version(TABLE *table) if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0) { uchar version[4]; - char *key= table->s->table_cache_key.str; - uint key_length= table->s->table_cache_key.length; - TABLE *entry; - HASH_SEARCH_STATE state; int4store(version, MYSQL_VERSION_ID); if ((result= my_pwrite(file,(uchar*) version,4,51L,MYF_RW))) goto err; - for (entry=(TABLE*) my_hash_first(&open_cache,(uchar*) key,key_length, &state); - entry; - entry= (TABLE*) my_hash_next(&open_cache,(uchar*) key,key_length, &state)) - entry->s->mysql_version= MYSQL_VERSION_ID; + table->s->mysql_version= MYSQL_VERSION_ID; } err: if (file >= 0) diff --git a/sql/lock.cc b/sql/lock.cc index 56ae94ddc39..0c8c3095844 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -955,361 +955,56 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, *****************************************************************************/ /** - Lock and wait for the named lock. + Obtain exclusive metadata locks on the list of tables. - @param thd Thread handler - @param table_list Lock first table in this list + @param thd Thread handle + @param table_list List of tables to lock + @note This function assumes that no metadata locks were acquired + before calling it. Also it cannot be called while holding + LOCK_open mutex. Both these invariants are enforced by asserts + in mdl_acquire_exclusive_locks() functions. - @note - Works together with global read lock. - - @retval - 0 ok - @retval - 1 error -*/ - -int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list) -{ - int lock_retcode; - int error= -1; - DBUG_ENTER("lock_and_wait_for_table_name"); - - if (wait_if_global_read_lock(thd, 0, 1)) - DBUG_RETURN(1); - pthread_mutex_lock(&LOCK_open); - if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0) - goto end; - if (lock_retcode && wait_for_locked_table_names(thd, table_list)) - { - unlock_table_name(thd, table_list); - goto end; - } - error=0; - -end: - pthread_mutex_unlock(&LOCK_open); - start_waiting_global_read_lock(thd); - DBUG_RETURN(error); -} - - -/** - Put a not open table with an old refresh version in the table cache. - - @param thd Thread handler - @param table_list Lock first table in this list - @param check_in_use Do we need to check if table already in use by us - - @note - One must have a lock on LOCK_open! - - @warning - If you are going to update the table, you should use - lock_and_wait_for_table_name instead of this function as this works - together with 'FLUSH TABLES WITH READ LOCK' - - @note - This will force any other threads that uses the table to release it - as soon as possible. - - @return - < 0 error - @return - == 0 table locked - @return - > 0 table locked, but someone is using it -*/ - -int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) -{ - TABLE *table; - char key[MAX_DBKEY_LENGTH]; - char *db= table_list->db; - uint key_length; - bool found_locked_table= FALSE; - HASH_SEARCH_STATE state; - DBUG_ENTER("lock_table_name"); - DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name)); - - key_length= create_table_def_key(thd, key, table_list, 0); - - if (check_in_use) - { - /* Only insert the table if we haven't insert it already */ - for (table=(TABLE*) my_hash_first(&open_cache, (uchar*)key, - key_length, &state); - table ; - table = (TABLE*) my_hash_next(&open_cache,(uchar*) key, - key_length, &state)) - { - if (table->reginfo.lock_type < TL_WRITE) - { - if (table->in_use == thd) - found_locked_table= TRUE; - continue; - } - - if (table->in_use == thd) - { - DBUG_PRINT("info", ("Table is in use")); - table->s->version= 0; // Ensure no one can use this - table->locked_by_name= 1; - DBUG_RETURN(0); - } - } - } - - if (thd->locked_tables && thd->locked_tables->table_count && - ! find_temporary_table(thd, table_list->db, table_list->table_name)) - { - if (found_locked_table) - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias); - else - my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_list->alias); - - DBUG_RETURN(-1); - } - - if (!(table= table_cache_insert_placeholder(thd, key, key_length))) - DBUG_RETURN(-1); - - table_list->table=table; - - /* Return 1 if table is in use */ - DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name, - check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG))); -} - - -void unlock_table_name(THD *thd, TABLE_LIST *table_list) -{ - if (table_list->table) - { - my_hash_delete(&open_cache, (uchar*) table_list->table); - broadcast_refresh(); - } -} - - -static bool locked_named_table(THD *thd, TABLE_LIST *table_list) -{ - for (; table_list ; table_list=table_list->next_local) - { - TABLE *table= table_list->table; - if (table) - { - TABLE *save_next= table->next; - bool result; - table->next= 0; - result= table_is_used(table_list->table, 0); - table->next= save_next; - if (result) - return 1; - } - } - return 0; // All tables are locked -} - - -bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list) -{ - bool result=0; - DBUG_ENTER("wait_for_locked_table_names"); - - safe_mutex_assert_owner(&LOCK_open); - - while (locked_named_table(thd,table_list)) - { - if (thd->killed) - { - result=1; - break; - } - wait_for_condition(thd, &LOCK_open, &COND_refresh); - pthread_mutex_lock(&LOCK_open); - } - DBUG_RETURN(result); -} - - -/** - Lock all tables in list with a name lock. - - REQUIREMENTS - - One must have a lock on LOCK_open when calling this - - @param thd Thread handle - @param table_list Names of tables to lock - - @note - If you are just locking one table, you should use - lock_and_wait_for_table_name(). - - @retval - 0 ok - @retval - 1 Fatal error (end of memory ?) + @retval FALSE Success. + @retval TRUE Failure (OOM or thread was killed). */ bool lock_table_names(THD *thd, TABLE_LIST *table_list) { - bool got_all_locks=1; TABLE_LIST *lock_table; + MDL_LOCK *mdl_lock; for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - int got_lock; - if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0) - goto end; // Fatal error - if (got_lock) - got_all_locks=0; // Someone is using table + if (!(mdl_lock= mdl_alloc_lock(0, lock_table->db, lock_table->table_name, + thd->mem_root))) + goto end; + mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock); } - - /* If some table was in use, wait until we got the lock */ - if (!got_all_locks && wait_for_locked_table_names(thd, table_list)) - goto end; + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + return 1; return 0; end: - unlock_table_names(thd, table_list, lock_table); + mdl_remove_all_locks(&thd->mdl_context); return 1; } /** - Unlock all tables in list with a name lock. + Release all metadata locks previously obtained by lock_table_names(). - @param thd Thread handle. - @param table_list Names of tables to lock. + @param thd Thread handle. - @note - This function needs to be protected by LOCK_open. If we're - under LOCK TABLES, this function does not work as advertised. Namely, - it does not exclude other threads from using this table and does not - put an exclusive name lock on this table into the table cache. - - @see lock_table_names - @see unlock_table_names - - @retval TRUE An error occured. - @retval FALSE Name lock successfully acquired. + @note Cannot be called while holding LOCK_open mutex. */ -bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list) -{ - if (lock_table_names(thd, table_list)) - return TRUE; - - /* - Upgrade the table name locks from semi-exclusive to exclusive locks. - */ - for (TABLE_LIST *table= table_list; table; table= table->next_global) - { - if (table->table) - table->table->open_placeholder= 1; - } - return FALSE; -} - - -/** - Test is 'table' is protected by an exclusive name lock. - - @param[in] thd The current thread handler - @param[in] table_list Table container containing the single table to be - tested - - @note Needs to be protected by LOCK_open mutex. - - @return Error status code - @retval TRUE Table is protected - @retval FALSE Table is not protected -*/ - -bool -is_table_name_exclusively_locked_by_this_thread(THD *thd, - TABLE_LIST *table_list) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length; - - key_length= create_table_def_key(thd, key, table_list, 0); - - return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key, - key_length); -} - - -/** - Test is 'table key' is protected by an exclusive name lock. - - @param[in] thd The current thread handler. - @param[in] key - @param[in] key_length - - @note Needs to be protected by LOCK_open mutex - - @retval TRUE Table is protected - @retval FALSE Table is not protected - */ - -bool -is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key, - int key_length) -{ - HASH_SEARCH_STATE state; - TABLE *table; - - for (table= (TABLE*) my_hash_first(&open_cache, key, - key_length, &state); - table ; - table= (TABLE*) my_hash_next(&open_cache, key, - key_length, &state)) - { - if (table->in_use == thd && - table->open_placeholder == 1 && - table->s->version == 0) - return TRUE; - } - - return FALSE; -} - -/** - Unlock all tables in list with a name lock. - - @param - thd Thread handle - @param - table_list Names of tables to unlock - @param - last_table Don't unlock any tables after this one. - (default 0, which will unlock all tables) - - @note - One must have a lock on LOCK_open when calling this. - - @note - This function will broadcast refresh signals to inform other threads - that the name locks are removed. - - @retval - 0 ok - @retval - 1 Fatal error (end of memory ?) -*/ - -void unlock_table_names(THD *thd, TABLE_LIST *table_list, - TABLE_LIST *last_table) +void unlock_table_names(THD *thd) { DBUG_ENTER("unlock_table_names"); - for (TABLE_LIST *table= table_list; - table != last_table; - table= table->next_local) - unlock_table_name(thd,table); - broadcast_refresh(); + mdl_release_locks(&thd->mdl_context); + mdl_remove_all_locks(&thd->mdl_context); DBUG_VOID_RETURN; } @@ -1455,6 +1150,33 @@ bool lock_global_read_lock(THD *thd) thd->global_read_lock= GOT_GLOBAL_READ_LOCK; global_read_lock++; thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock + /* + When we perform FLUSH TABLES or ALTER TABLE under LOCK TABLES, + tables being reopened are protected only by meta-data locks at + some point. To avoid sneaking in with our global read lock at + this moment we have to take global shared meta data lock. + + TODO: We should change this code to acquire global shared metadata + lock before acquiring global read lock. But in order to do + this we have to get rid of all those places in which + wait_if_global_read_lock() is called before acquiring + metadata locks first. Also long-term we should get rid of + redundancy between metadata locks, global read lock and DDL + blocker (see WL#4399 and WL#4400). + */ + if (mdl_acquire_global_shared_lock(&thd->mdl_context)) + { + /* Our thread was killed -- return back to initial state. */ + pthread_mutex_lock(&LOCK_global_read_lock); + if (!(--global_read_lock)) + { + DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock")); + pthread_cond_broadcast(&COND_global_read_lock); + } + pthread_mutex_unlock(&LOCK_global_read_lock); + thd->global_read_lock= 0; + DBUG_RETURN(1); + } } /* We DON'T set global_read_lock_blocks_commit now, it will be set after @@ -1476,6 +1198,8 @@ void unlock_global_read_lock(THD *thd) ("global_read_lock: %u global_read_lock_blocks_commit: %u", global_read_lock, global_read_lock_blocks_commit)); + mdl_release_global_shared_lock(&thd->mdl_context); + pthread_mutex_lock(&LOCK_global_read_lock); tmp= --global_read_lock; if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT) diff --git a/sql/log_event.cc b/sql/log_event.cc index 3d35dec4fb0..fd0e20d690d 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3036,7 +3036,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, } else { - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); } /* @@ -7238,8 +7238,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) */ DBUG_ASSERT(get_flags(STMT_END_F)); - const_cast(rli)->clear_tables_to_lock(); - close_thread_tables(thd); + const_cast(rli)->slave_close_thread_tables(thd); thd->clear_error(); DBUG_RETURN(0); } @@ -7322,7 +7321,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) "unexpected success or fatal error")); thd->is_slave_error= 1; } - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(actual_error); } @@ -7347,7 +7346,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) mysql_unlock_tables(thd, thd->lock); thd->lock= 0; thd->is_slave_error= 1; - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(ERR_BAD_TABLE_DEF); } } @@ -7530,12 +7529,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) } } // if (table) - /* - We need to delay this clear until here bacause unpack_current_row() uses - master-side table definitions stored in rli. - */ - if (rli->tables_to_lock && get_flags(STMT_END_F)) - const_cast(rli)->clear_tables_to_lock(); if (error) { @@ -8064,7 +8057,8 @@ Table_map_log_event::~Table_map_log_event() int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; - char *db_mem, *tname_mem; + char *db_mem, *tname_mem, *mdlkey; + MDL_LOCK *mdl_lock; size_t dummy_len; void *memory; DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); @@ -8079,6 +8073,8 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) &table_list, (uint) sizeof(RPL_TABLE_LIST), &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, + &mdl_lock, sizeof(MDL_LOCK), + &mdlkey, MAX_DBKEY_LENGTH, NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -8091,6 +8087,8 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) table_list->updating= 1; strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); strmov(table_list->table_name, m_tblnam); + mdl_init_lock(mdl_lock, mdlkey, 0, table_list->db, table_list->table_name); + table_list->mdl_lock= mdl_lock; int error= 0; diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index fbcbb388236..030d51b3618 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -32,8 +32,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info */ DBUG_ASSERT(ev->get_flags(Old_rows_log_event::STMT_END_F)); - const_cast(rli)->clear_tables_to_lock(); - close_thread_tables(thd); + const_cast(rli)->slave_close_thread_tables(thd); thd->clear_error(); DBUG_RETURN(0); } @@ -91,7 +90,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info "unexpected success or fatal error")); thd->is_slave_error= 1; } - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(actual_error); } @@ -109,10 +108,8 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info { if (ptr->m_tabledef.compatible_with(rli, ptr->table)) { - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; thd->is_slave_error= 1; - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(Old_rows_log_event::ERR_BAD_TABLE_DEF); } } @@ -236,13 +233,6 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info } } - /* - We need to delay this clear until the table def is no longer needed. - The table def is needed in unpack_row(). - */ - if (rli->tables_to_lock && ev->get_flags(Old_rows_log_event::STMT_END_F)) - const_cast(rli)->clear_tables_to_lock(); - if (error) { /* error has occured during the transaction */ rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(), @@ -1440,8 +1430,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) */ DBUG_ASSERT(get_flags(STMT_END_F)); - const_cast(rli)->clear_tables_to_lock(); - close_thread_tables(thd); + const_cast(rli)->slave_close_thread_tables(thd); thd->clear_error(); DBUG_RETURN(0); } @@ -1496,7 +1485,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) "Error in %s event: when locking tables", get_type_str()); } - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(error); } @@ -1515,7 +1504,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) */ thd->binlog_flush_pending_rows_event(false); TABLE_LIST *tables= rli->tables_to_lock; - close_tables_for_reopen(thd, &tables); + close_tables_for_reopen(thd, &tables, FALSE); uint tables_count= rli->tables_to_lock_count; if ((error= open_tables(thd, &tables, &tables_count, 0))) @@ -1533,7 +1522,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) "unexpected success or fatal error")); thd->is_slave_error= 1; } - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(error); } } @@ -1552,10 +1541,8 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) { if (ptr->m_tabledef.compatible_with(rli, ptr->table)) { - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; thd->is_slave_error= 1; - const_cast(rli)->clear_tables_to_lock(); + const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(ERR_BAD_TABLE_DEF); } } @@ -1726,13 +1713,6 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) } } // if (table) - /* - We need to delay this clear until here bacause unpack_current_row() uses - master-side table definitions stored in rli. - */ - if (rli->tables_to_lock && get_flags(STMT_END_F)) - const_cast(rli)->clear_tables_to_lock(); - if (error) { /* error has occured during the transaction */ rli->report(ERROR_LEVEL, thd->net.last_errno, diff --git a/sql/mdl.cc b/sql/mdl.cc new file mode 100644 index 00000000000..00dcc12cdc8 --- /dev/null +++ b/sql/mdl.cc @@ -0,0 +1,1342 @@ +/* Copyright (C) 2007-2008 MySQL AB + + 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 */ + + + +/* + TODO: Remove this dependency on mysql_priv.h. It's not + trivial step at the moment since currently we access to + some of THD members and use some of its methods here. +*/ +#include "mysql_priv.h" +#include "mdl.h" + + +/** + The lock context. Created internally for an acquired lock. + For a given name, there exists only one MDL_LOCK_DATA instance, + and it exists only when the lock has been granted. + Can be seen as an MDL subsystem's version of TABLE_SHARE. +*/ + +struct MDL_LOCK_DATA +{ + I_P_List active_shared; + /* + There can be several upgraders and active exclusive + belonging to the same context. + */ + I_P_List active_shared_waiting_upgrade; + I_P_List active_exclusive; + I_P_List waiting_exclusive; + uint users; + void *cached_object; + mdl_cached_object_release_hook cached_object_release_hook; + + MDL_LOCK_DATA() : cached_object(0), cached_object_release_hook(0) {} + + MDL_LOCK *get_key_owner() + { + return !active_shared.is_empty() ? + active_shared.head() : + (!active_shared_waiting_upgrade.is_empty() ? + active_shared_waiting_upgrade.head() : + (!active_exclusive.is_empty() ? + active_exclusive.head() : waiting_exclusive.head())); + } + + bool has_no_other_users() + { + return (users == 1); + } +}; + + +pthread_mutex_t LOCK_mdl; +pthread_cond_t COND_mdl; +HASH mdl_locks; +uint global_shared_locks_pending; +uint global_shared_locks_acquired; +uint global_intention_exclusive_locks_acquired; + + + +extern "C" uchar *mdl_locks_key(const uchar *record, size_t *length, + my_bool not_used __attribute__((unused))) +{ + MDL_LOCK_DATA *entry=(MDL_LOCK_DATA*) record; + *length= entry->get_key_owner()->key_length; + return (uchar*) entry->get_key_owner()->key; +} + + +/** + Initialize the metadata locking subsystem. + + This function is called at server startup. + + In particular, initializes the new global mutex and + the associated condition variable: LOCK_mdl and COND_mdl. + These locking primitives are implementation details of the MDL + subsystem and are private to it. + + Note, that even though the new implementation adds acquisition + of a new global mutex to the execution flow of almost every SQL + statement, the design capitalizes on that to later save on + look ups in the table definition cache. This leads to reduced + contention overall and on LOCK_open in particular. + Please see the description of mdl_acquire_shared_lock() for details. +*/ + +void mdl_init() +{ + pthread_mutex_init(&LOCK_mdl, NULL); + pthread_cond_init(&COND_mdl, NULL); + my_hash_init(&mdl_locks, &my_charset_bin, 16 /* FIXME */, 0, 0, + mdl_locks_key, 0, 0); + global_shared_locks_pending= global_shared_locks_acquired= 0; + global_intention_exclusive_locks_acquired= 0; +} + + +/** + Release resources of metadata locking subsystem. + + Destroys the global mutex and the condition variable. + Called at server shutdown. +*/ + +void mdl_destroy() +{ + DBUG_ASSERT(!mdl_locks.records); + pthread_mutex_destroy(&LOCK_mdl); + pthread_cond_destroy(&COND_mdl); + my_hash_free(&mdl_locks); +} + + +/** + Initialize a metadata locking context. + + This is to be called when a new server connection is created. +*/ + +void mdl_context_init(MDL_CONTEXT *context, THD *thd) +{ + context->locks.empty(); + context->thd= thd; + context->has_global_shared_lock= FALSE; +} + + +/** + Destroy metadata locking context. + + Assumes and asserts that there are no active or pending locks + associated with this context at the time of the destruction. + + Currently does nothing. Asserts that there are no pending + or satisfied lock requests. The pending locks must be released + prior to destruction. This is a new way to express the assertion + that all tables are closed before a connection is destroyed. +*/ + +void mdl_context_destroy(MDL_CONTEXT *context) +{ + DBUG_ASSERT(context->locks.is_empty()); + DBUG_ASSERT(!context->has_global_shared_lock); +} + + +/** + Backup and reset state of meta-data locking context. + + mdl_context_backup_and_reset(), mdl_context_restore() and + mdl_context_merge() are used by HANDLER implementation which + needs to open table for new HANDLER independently of already + open HANDLERs and add this table/metadata lock to the set of + tables open/metadata locks for HANDLERs afterwards. +*/ + +void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) +{ + backup->locks.empty(); + ctx->locks.swap(backup->locks); +} + + +/** + Restore state of meta-data locking context from backup. +*/ + +void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) +{ + DBUG_ASSERT(ctx->locks.is_empty()); + ctx->locks.swap(backup->locks); +} + + +/** + Merge meta-data locks from one context into another. +*/ + +void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) +{ + MDL_LOCK *l; + + DBUG_ASSERT(dst->thd == src->thd); + + if (!src->locks.is_empty()) + { + I_P_List_iterator it(src->locks); + while ((l= it++)) + { + DBUG_ASSERT(l->ctx); + l->ctx= dst; + dst->locks.push_front(l); + } + src->locks.empty(); + } +} + + +/** + Initialize a lock request. + + This is to be used for every lock request. + + Note that initialization and allocation are split + into two calls. This is to allow flexible memory management + of lock requests. Normally a lock request is stored + in statement memory (e.g. is a member of struct TABLE_LIST), + but we would also like to allow allocation of lock + requests in other memory roots, for example in the grant + subsystem, to lock privilege tables. + + The MDL subsystem does not own or manage memory of lock + requests. Instead it assumes that the life time of every lock + request encloses calls to mdl_acquire_shared_lock() and + mdl_release_locks(). + + @param mdl Pointer to an MDL_LOCK object to initialize + @param key_buff Pointer to the buffer for key for the lock request + (should be at least strlen(db) + strlen(name) + + 2 bytes, or, if the lengths are not known, MAX_DBNAME_LENGTH) + @param type Id of type of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object + + Stores the database name, object name and the type in the key + buffer. Initializes mdl_el to point to the key. + We can't simply initialize mdl_el with type, db and name + by-pointer because of the underlying HASH implementation + requires the key to be a contiguous buffer. + + The initialized lock request will have MDL_SHARED type and + normal priority. + + Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 + Note that tables and views have the same lock type, since + they share the same name space in the SQL standard. +*/ + +void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db, + const char *name) +{ + int4store(key, type); + mdl->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + mdl->key= key; + mdl->type= MDL_SHARED; + mdl->state= MDL_PENDING; + mdl->prio= MDL_NORMAL_PRIO; + mdl->upgradable= FALSE; +#ifndef DBUG_OFF + mdl->ctx= 0; + mdl->lock_data= 0; +#endif +} + + +/** + Allocate and initialize one lock request. + + Same as mdl_init_lock(), but allocates the lock and the key buffer + on a memory root. Necessary to lock ad-hoc tables, e.g. + mysql.* tables of grant and data dictionary subsystems. + + @param type Id of type of object to be locked + @param db Name of database to which object belongs + @param name Name of of object + @param root MEM_ROOT on which object should be allocated + + @note The allocated lock request will have MDL_SHARED type and + normal priority. + + @retval 0 Error + @retval non-0 Pointer to an object representing a lock request +*/ + +MDL_LOCK *mdl_alloc_lock(int type, const char *db, const char *name, + MEM_ROOT *root) +{ + MDL_LOCK *lock; + char *key; + + if (!multi_alloc_root(root, &lock, sizeof(MDL_LOCK), &key, + MAX_DBKEY_LENGTH, NULL)) + return NULL; + + mdl_init_lock(lock, key, type, db, name); + + return lock; +} + + +/** + Add a lock request to the list of lock requests of the context. + + The procedure to acquire metadata locks is: + - allocate and initialize lock requests (mdl_alloc_lock()) + - associate them with a context (mdl_add_lock()) + - call mdl_acquire_shared_lock()/mdl_release_lock() (maybe repeatedly). + + Associates a lock request with the given context. + + @param context The MDL context to associate the lock with. + There should be no more than one context per + connection, to avoid deadlocks. + @param lock The lock request to be added. +*/ + +void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK *lock) +{ + DBUG_ENTER("mdl_add_lock"); + DBUG_ASSERT(lock->state == MDL_PENDING); + DBUG_ASSERT(!lock->ctx); + lock->ctx= context; + context->locks.push_front(lock); + DBUG_VOID_RETURN; +} + + +/** + Clear all lock requests in the context (clear the context). + + Disassociates lock requests from the context. + All granted locks must be released prior to calling this + function. + + In other words, the expected procedure to release locks is: + - mdl_release_locks(); + - mdl_remove_all_locks(); + + We could possibly merge mdl_remove_all_locks() and mdl_release_locks(), + but this function comes in handy when we need to back off: in that case + we release all the locks acquired so-far but do not free them, since + we know that the respective lock requests will be used again. + + Also resets lock requests back to their initial state (i.e. + sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO). + + @param context Context to be cleared. +*/ + +void mdl_remove_all_locks(MDL_CONTEXT *context) +{ + MDL_LOCK *l; + I_P_List_iterator it(context->locks); + while ((l= it++)) + { + /* Reset lock request back to its initial state. */ + l->type= MDL_SHARED; + l->prio= MDL_NORMAL_PRIO; + l->upgradable= FALSE; +#ifndef DBUG_OFF + l->ctx= 0; +#endif + } + context->locks.empty(); +} + + +/** + Auxiliary functions needed for creation/destruction of MDL_LOCK_DATA + objects. + + @todo This naive implementation should be replaced with one that saves + on memory allocation by reusing released objects. +*/ + +static MDL_LOCK_DATA* get_lock_data_object(void) +{ + return new MDL_LOCK_DATA(); +} + + +static void release_lock_data_object(MDL_LOCK_DATA *lock) +{ + delete lock; +} + + +/** + Try to acquire one shared lock. + + Unlike exclusive locks, shared locks are acquired one by + one. This is interface is chosen to simplify introduction of + the new locking API to the system. mdl_acquire_shared_lock() + is currently used from open_table(), and there we have only one + table to work with. + + In future we may consider allocating multiple shared locks at once. + + This function must be called after the lock is added to a context. + + @param lock [in] Lock request object for lock to be acquired + @param retry [out] Indicates that conflicting lock exists and another + attempt should be made after releasing all current + locks and waiting for conflicting lock go away + (using mdl_wait_for_locks()). + + @retval FALSE Success. + @retval TRUE Failure. Either error occured or conflicting lock exists. + In the latter case "retry" parameter is set to TRUE. +*/ + +bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) +{ + MDL_LOCK_DATA *lock_data; + *retry= FALSE; + + DBUG_ASSERT(l->type == MDL_SHARED && l->state == MDL_PENDING); + + safe_mutex_assert_not_owner(&LOCK_open); + + if (l->ctx->has_global_shared_lock && l->upgradable) + { + my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); + return TRUE; + } + + pthread_mutex_lock(&LOCK_mdl); + + if (l->upgradable && + (global_shared_locks_acquired || global_shared_locks_pending)) + { + pthread_mutex_unlock(&LOCK_mdl); + *retry= TRUE; + return TRUE; + } + + if (!(lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, + l->key_length))) + { + lock_data= get_lock_data_object(); + lock_data->active_shared.push_front(l); + lock_data->users= 1; + my_hash_insert(&mdl_locks, (uchar*)lock_data); + l->state= MDL_ACQUIRED; + l->lock_data= lock_data; + if (l->upgradable) + global_intention_exclusive_locks_acquired++; + } + else + { + if ((lock_data->active_exclusive.is_empty() && + (l->prio == MDL_HIGH_PRIO || + lock_data->waiting_exclusive.is_empty() && + lock_data->active_shared_waiting_upgrade.is_empty())) || + (!lock_data->active_exclusive.is_empty() && + lock_data->active_exclusive.head()->ctx == l->ctx)) + { + /* + When exclusive lock comes from the same context we can satisfy our + shared lock. This is required for CREATE TABLE ... SELECT ... and + ALTER VIEW ... AS .... + */ + lock_data->active_shared.push_front(l); + lock_data->users++; + l->state= MDL_ACQUIRED; + l->lock_data= lock_data; + if (l->upgradable) + global_intention_exclusive_locks_acquired++; + } + else + *retry= TRUE; + } + pthread_mutex_unlock(&LOCK_mdl); + + return *retry; +} + + +static void release_lock(MDL_LOCK *l); + + +/** + Acquire exclusive locks. The context must contain the list of + locks to be acquired. There must be no granted locks in the + context. + + This is a replacement of lock_table_names(). It is used in + RENAME, DROP and other DDL SQL statements. + + @param context A context containing requests for exclusive locks + The context may not have other lock requests. + + @note In case of failure (for example, if our thread was killed) + resets lock requests back to their initial state (MDL_SHARED + and MDL_NORMAL_PRIO). + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) +{ + MDL_LOCK *l, *lh; + MDL_LOCK_DATA *lock_data; + bool signalled= FALSE; + const char *old_msg; + I_P_List_iterator it(context->locks); + THD *thd= context->thd; + + DBUG_ASSERT(thd == current_thd); + + safe_mutex_assert_not_owner(&LOCK_open); + + if (context->has_global_shared_lock) + { + my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); + return TRUE; + } + + pthread_mutex_lock(&LOCK_mdl); + + old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + + while ((l= it++)) + { + DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING); + if (!(lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, + l->key_length))) + { + lock_data= get_lock_data_object(); + lock_data->waiting_exclusive.push_front(l); + lock_data->users= 1; + my_hash_insert(&mdl_locks, (uchar*)lock_data); + l->lock_data= lock_data; + } + else + { + lock_data->waiting_exclusive.push_front(l); + lock_data->users++; + l->lock_data= lock_data; + } + } + + while (1) + { + it.rewind(); + while ((l= it++)) + { + lock_data= l->lock_data; + + if (global_shared_locks_acquired || global_shared_locks_pending) + { + /* + There is active or pending global shared lock we have + to wait until it goes away. + */ + signalled= TRUE; + break; + } + else if (!lock_data->active_exclusive.is_empty() || + !lock_data->active_shared_waiting_upgrade.is_empty()) + { + /* + Exclusive MDL owner won't wait on table-level lock the same + applies to shared lock waiting upgrade (in this cases we already + have some table-level lock). + */ + signalled= TRUE; + break; + } + else if ((lh= lock_data->active_shared.head())) + { + signalled= notify_thread_having_shared_lock(thd, lh->ctx->thd); + break; + } + } + if (!l) + break; + if (signalled) + pthread_cond_wait(&COND_mdl, &LOCK_mdl); + else + { + /* + Another thread obtained shared MDL-lock on some table but + has not yet opened it and/or tried to obtain data lock on + it. In this case we need to wait until this happens and try + to abort this thread once again. + */ + struct timespec abstime; + set_timespec(abstime, 10); + pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); + } + if (thd->killed) + { + /* Remove our pending lock requests from the locks. */ + it.rewind(); + while ((l= it++)) + { + DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING); + release_lock(l); + /* Return lock request to its initial state. */ + l->type= MDL_SHARED; + l->prio= MDL_NORMAL_PRIO; + l->upgradable= FALSE; + context->locks.remove(l); + } + /* Pending requests for shared locks can be satisfied now. */ + pthread_cond_broadcast(&COND_mdl); + thd->exit_cond(old_msg); + return TRUE; + } + } + it.rewind(); + while ((l= it++)) + { + global_intention_exclusive_locks_acquired++; + lock_data= l->lock_data; + lock_data->waiting_exclusive.remove(l); + lock_data->active_exclusive.push_front(l); + l->state= MDL_ACQUIRED; + if (lock_data->cached_object) + (*lock_data->cached_object_release_hook)(lock_data->cached_object); + lock_data->cached_object= NULL; + } + /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ + thd->exit_cond(old_msg); + return FALSE; +} + + +/** + Upgrade a shared metadata lock to exclusive. + + Used in ALTER TABLE, when a copy of the table with the + new definition has been constructed. + + @param context Context to which shared long belongs + @param type Id of object type + @param db Name of the database + @param name Name of the object + + @note In case of failure to upgrade locks (e.g. because upgrader + was killed) leaves locks in their original state (locked + in shared mode). + + @retval FALSE Success + @retval TRUE Failure (thread was killed) +*/ + +bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, + const char *db, const char *name) +{ + char key[MAX_DBKEY_LENGTH]; + uint key_length; + bool signalled= FALSE; + MDL_LOCK *l, *lh; + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); + const char *old_msg; + THD *thd= context->thd; + + DBUG_ENTER("mdl_upgrade_shared_lock_to_exclusive"); + DBUG_PRINT("enter", ("db=%s name=%s", db, name)); + + DBUG_ASSERT(thd == current_thd); + + int4store(key, type); + key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + + old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + + while ((l= it++)) + if (l->key_length == key_length && !memcmp(l->key, key, key_length) && + l->type == MDL_SHARED) + { + DBUG_PRINT("info", ("found shared lock for upgrade")); + DBUG_ASSERT(l->state == MDL_ACQUIRED); + DBUG_ASSERT(l->upgradable); + l->state= MDL_PENDING_UPGRADE; + lock_data= l->lock_data; + lock_data->active_shared.remove(l); + lock_data->active_shared_waiting_upgrade.push_front(l); + } + + while (1) + { + DBUG_PRINT("info", ("looking at conflicting locks")); + it.rewind(); + while ((l= it++)) + { + if (l->state == MDL_PENDING_UPGRADE) + { + DBUG_ASSERT(l->type == MDL_SHARED); + + lock_data= l->lock_data; + + DBUG_ASSERT(global_shared_locks_acquired == 0 && + global_intention_exclusive_locks_acquired); + + if ((lh= lock_data->active_shared.head())) + { + DBUG_PRINT("info", ("found active shared locks")); + signalled= notify_thread_having_shared_lock(thd, lh->ctx->thd); + break; + } + else if (!lock_data->active_exclusive.is_empty()) + { + DBUG_PRINT("info", ("found active exclusive locks")); + signalled= TRUE; + break; + } + } + } + if (!l) + break; + if (signalled) + pthread_cond_wait(&COND_mdl, &LOCK_mdl); + else + { + /* + Another thread obtained shared MDL-lock on some table but + has not yet opened it and/or tried to obtain data lock on + it. In this case we need to wait until this happens and try + to abort this thread once again. + */ + struct timespec abstime; + set_timespec(abstime, 10); + DBUG_PRINT("info", ("Failed to wake-up from table-level lock ... sleeping")); + pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); + } + if (thd->killed) + { + it.rewind(); + while ((l= it++)) + if (l->state == MDL_PENDING_UPGRADE) + { + DBUG_ASSERT(l->type == MDL_SHARED); + l->state= MDL_ACQUIRED; + lock_data= l->lock_data; + lock_data->active_shared_waiting_upgrade.remove(l); + lock_data->active_shared.push_front(l); + } + /* Pending requests for shared locks can be satisfied now. */ + pthread_cond_broadcast(&COND_mdl); + thd->exit_cond(old_msg); + DBUG_RETURN(TRUE); + } + } + + it.rewind(); + while ((l= it++)) + if (l->state == MDL_PENDING_UPGRADE) + { + DBUG_ASSERT(l->type == MDL_SHARED); + lock_data= l->lock_data; + lock_data->active_shared_waiting_upgrade.remove(l); + lock_data->active_exclusive.push_front(l); + l->type= MDL_EXCLUSIVE; + l->state= MDL_ACQUIRED; + if (lock_data->cached_object) + (*lock_data->cached_object_release_hook)(lock_data->cached_object); + lock_data->cached_object= 0; + } + + /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ + thd->exit_cond(old_msg); + DBUG_RETURN(FALSE); +} + + +/** + Try to acquire an exclusive lock on the object if there are + no conflicting locks. + + Similar to the previous function, but returns + immediately without any side effect if encounters a lock + conflict. Otherwise takes the lock. + + This function is used in CREATE TABLE ... LIKE to acquire a lock + on the table to be created. In this statement we don't want to + block and wait for the lock if the table already exists. + + @param context [in] The context containing the lock request + @param lock [in] The lock request + + @retval FALSE the lock was granted + @retval TRUE there were conflicting locks. + + FIXME: Compared to lock_table_name_if_not_cached() + it gives sligthly more false negatives. +*/ + +bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *l) +{ + MDL_LOCK_DATA *lock_data; + + DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING); + + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + + if (!(lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, + l->key_length))) + { + lock_data= get_lock_data_object(); + lock_data->active_exclusive.push_front(l); + lock_data->users= 1; + my_hash_insert(&mdl_locks, (uchar*)lock_data); + l->state= MDL_ACQUIRED; + l->lock_data= lock_data; + lock_data= 0; + global_intention_exclusive_locks_acquired++; + } + pthread_mutex_unlock(&LOCK_mdl); + + /* + FIXME: We can't leave pending MDL_EXCLUSIVE lock request in the list since + for such locks we assume that they have MDL_LOCK::lock properly set. + Long term we should clearly define relation between lock types, + presence in the context lists and MDL_LOCK::lock values. + */ + if (lock_data) + context->locks.remove(l); + + return lock_data; +} + + +/** + Acquire global shared metadata lock. + + Holding this lock will block all requests for exclusive locks + and shared locks which can be potentially upgraded to exclusive + (see MDL_LOCK::upgradable). + + @param context Current metadata locking context. + + @retval FALSE Success -- the lock was granted. + @retval TRUE Failure -- our thread was killed. +*/ + +bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) +{ + THD *thd= context->thd; + const char *old_msg; + + safe_mutex_assert_not_owner(&LOCK_open); + DBUG_ASSERT(thd == current_thd); + DBUG_ASSERT(!context->has_global_shared_lock); + + pthread_mutex_lock(&LOCK_mdl); + + global_shared_locks_pending++; + old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + + while (!thd->killed && global_intention_exclusive_locks_acquired) + pthread_cond_wait(&COND_mdl, &LOCK_mdl); + + global_shared_locks_pending--; + if (thd->killed) + { + /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ + thd->exit_cond(old_msg); + return TRUE; + } + global_shared_locks_acquired++; + context->has_global_shared_lock= TRUE; + /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ + thd->exit_cond(old_msg); + return FALSE; +} + + +/** + Wait until there will be no locks that conflict with lock requests + in the context. + + This is a part of the locking protocol and must be used by the + acquirer of shared locks after a back-off. + + Does not acquire the locks! + + @param context Context with which lock requests are associated. + + @retval FALSE Success. One can try to obtain metadata locks. + @retval TRUE Failure (thread was killed) +*/ + +bool mdl_wait_for_locks(MDL_CONTEXT *context) +{ + MDL_LOCK *l; + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); + const char *old_msg; + THD *thd= context->thd; + + safe_mutex_assert_not_owner(&LOCK_open); + DBUG_ASSERT(thd == current_thd); + + while (!thd->killed) + { + /* + We have to check if there are some HANDLERs open by this thread + which conflict with some pending exclusive locks. Otherwise we + might have a deadlock in situations when we are waiting for + pending writer to go away, which in its turn waits for HANDLER + open by our thread. + + TODO: investigate situations in which we need to broadcast on + COND_mdl because of above scenario. + */ + mysql_ha_flush(context->thd); + pthread_mutex_lock(&LOCK_mdl); + old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + it.rewind(); + while ((l= it++)) + { + DBUG_ASSERT(l->state == MDL_PENDING); + if ((l->upgradable || l->type == MDL_EXCLUSIVE) && + (global_shared_locks_acquired || global_shared_locks_pending)) + break; + /* + To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. + */ + if (l->type == MDL_SHARED && + (lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, + l->key_length)) && + !(lock_data->active_exclusive.is_empty() && + lock_data->active_shared_waiting_upgrade.is_empty() && + lock_data->waiting_exclusive.is_empty())) + break; + } + if (!l) + { + pthread_mutex_unlock(&LOCK_mdl); + break; + } + pthread_cond_wait(&COND_mdl, &LOCK_mdl); + /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ + thd->exit_cond(old_msg); + } + return thd->killed; +} + + +/** + Auxiliary function which allows to release particular lock + ownership of which is represented by lock request object. +*/ + +static void release_lock(MDL_LOCK *l) +{ + MDL_LOCK_DATA *lock_data; + + DBUG_ENTER("release_lock"); + DBUG_PRINT("enter", ("db=%s name=%s", l->key + 4, + l->key + 4 + strlen(l->key + 4) + 1)); + + lock_data= l->lock_data; + if (lock_data->has_no_other_users()) + { + my_hash_delete(&mdl_locks, (uchar *)lock_data); + DBUG_PRINT("info", ("releasing cached_object cached_object=%p", + lock_data->cached_object)); + if (lock_data->cached_object) + (*lock_data->cached_object_release_hook)(lock_data->cached_object); + release_lock_data_object(lock_data); + if (l->type == MDL_EXCLUSIVE && l->state == MDL_ACQUIRED || + l->type == MDL_SHARED && l->state == MDL_ACQUIRED && l->upgradable) + global_intention_exclusive_locks_acquired--; + } + else + { + switch (l->type) + { + case MDL_SHARED: + lock_data->active_shared.remove(l); + if (l->upgradable) + global_intention_exclusive_locks_acquired--; + break; + case MDL_EXCLUSIVE: + if (l->state == MDL_PENDING) + lock_data->waiting_exclusive.remove(l); + else + { + lock_data->active_exclusive.remove(l); + global_intention_exclusive_locks_acquired--; + } + break; + default: + /* TODO Really? How about problems during lock upgrade ? */ + DBUG_ASSERT(0); + } + lock_data->users--; + } + + DBUG_VOID_RETURN; +} + + +/** + Release all locks associated with the context, but leave them + in the context as lock requests. + + This function is used to back off in case of a lock conflict. + It is also used to release shared locks in the end of an SQL + statement. + + @param context The context with which the locks to be released + are associated. +*/ + +void mdl_release_locks(MDL_CONTEXT *context) +{ + MDL_LOCK *l; + I_P_List_iterator it(context->locks); + DBUG_ENTER("mdl_release_locks"); + + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + while ((l= it++)) + { + DBUG_PRINT("info", ("found lock to release l=%p", l)); + /* + We should not release locks which pending shared locks as these + are not associated with lock object and don't present in its + lists. Allows us to avoid problems in open_tables() in case of + back-off + */ + if (!(l->type == MDL_SHARED && l->state == MDL_PENDING)) + { + release_lock(l); + l->state= MDL_PENDING; +#ifndef DBUG_OFF + l->lock_data= 0; +#endif + } + /* + We will return lock request to its initial state only in + mdl_remove_all_locks() since we need to know type of lock + request and if it is upgradable in mdl_wait_for_locks(). + */ + } + /* Inefficient but will do for a while */ + pthread_cond_broadcast(&COND_mdl); + pthread_mutex_unlock(&LOCK_mdl); + DBUG_VOID_RETURN; +} + + +/** + Release all exclusive locks associated with context. + Removes the locks from the context. + + @param context Context with exclusive locks. + + @note Shared locks are left intact. + @note Resets lock requests for locks released back to their + initial state (i.e.sets type and priority to MDL_SHARED + and MDL_NORMAL_PRIO). +*/ + +void mdl_release_exclusive_locks(MDL_CONTEXT *context) +{ + MDL_LOCK *l; + I_P_List_iterator it(context->locks); + + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + while ((l= it++)) + { + if (l->type == MDL_EXCLUSIVE) + { + DBUG_ASSERT(l->state == MDL_ACQUIRED); + release_lock(l); +#ifndef DBUG_OFF + l->ctx= 0; + l->lock_data= 0; +#endif + l->state= MDL_PENDING; + /* Return lock request to its initial state. */ + l->type= MDL_SHARED; + l->prio= MDL_NORMAL_PRIO; + l->upgradable= FALSE; + context->locks.remove(l); + } + } + pthread_cond_broadcast(&COND_mdl); + pthread_mutex_unlock(&LOCK_mdl); +} + + +/** + Release a lock. + Removes the lock from the context. + + @param context Context containing lock in question + @param lock Lock to be released + + @note Resets lock request for lock released back to its initial state + (i.e.sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO). +*/ + +void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lr) +{ + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + release_lock(lr); +#ifndef DBUG_OFF + lr->ctx= 0; + lr->lock_data= 0; +#endif + lr->state= MDL_PENDING; + /* Return lock request to its initial state. */ + lr->type= MDL_SHARED; + lr->prio= MDL_NORMAL_PRIO; + lr->upgradable= FALSE; + context->locks.remove(lr); + pthread_cond_broadcast(&COND_mdl); + pthread_mutex_unlock(&LOCK_mdl); +} + + +/** + Downgrade all exclusive locks in the context to + shared. + + @param context A context with exclusive locks. +*/ + +void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context) +{ + MDL_LOCK *l; + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); + + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + while ((l= it++)) + if (l->type == MDL_EXCLUSIVE) + { + DBUG_ASSERT(l->state == MDL_ACQUIRED); + if (!l->upgradable) + global_intention_exclusive_locks_acquired--; + lock_data= l->lock_data; + lock_data->active_exclusive.remove(l); + l->type= MDL_SHARED; + lock_data->active_shared.push_front(l); + } + pthread_cond_broadcast(&COND_mdl); + pthread_mutex_unlock(&LOCK_mdl); +} + + +/** + Release global shared metadata lock. + + @param context Current context +*/ + +void mdl_release_global_shared_lock(MDL_CONTEXT *context) +{ + safe_mutex_assert_not_owner(&LOCK_open); + DBUG_ASSERT(context->has_global_shared_lock); + + pthread_mutex_lock(&LOCK_mdl); + global_shared_locks_acquired--; + context->has_global_shared_lock= FALSE; + pthread_cond_broadcast(&COND_mdl); + pthread_mutex_unlock(&LOCK_mdl); +} + + +/** + Auxiliary function which allows to check if we have exclusive lock + on the object. + + @param context Current context + @param type Id of object type + @param db Name of the database + @param name Name of the object + + @return TRUE if current context contains exclusive lock for the object, + FALSE otherwise. +*/ + +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, + const char *db, const char *name) +{ + char key[MAX_DBKEY_LENGTH]; + uint key_length; + MDL_LOCK *l; + I_P_List_iterator it(context->locks); + + int4store(key, type); + key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + + while ((l= it++) && (l->key_length != key_length || memcmp(l->key, key, key_length))) + continue; + return (l && l->type == MDL_EXCLUSIVE && l->state == MDL_ACQUIRED); +} + + +/** + Auxiliary function which allows to check if we some kind of lock on + the object. + + @param context Current context + @param type Id of object type + @param db Name of the database + @param name Name of the object + + @return TRUE if current context contains satisfied lock for the object, + FALSE otherwise. +*/ + +bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, + const char *name) +{ + char key[MAX_DBKEY_LENGTH]; + uint key_length; + MDL_LOCK *l; + I_P_List_iterator it(context->locks); + + int4store(key, type); + key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + + while ((l= it++) && (l->key_length != key_length || + memcmp(l->key, key, key_length) || + l->state == MDL_PENDING)) + continue; + + return l; +} + + +/** + Check if we have any pending exclusive locks which conflict with + existing shared lock. + + @param l Shared lock against which check should be performed. + + @return TRUE if there are any conflicting locks, FALSE otherwise. +*/ + +bool mdl_has_pending_conflicting_lock(MDL_LOCK *l) +{ + bool result; + + DBUG_ASSERT(l->type == MDL_SHARED && l->state == MDL_ACQUIRED); + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_mdl); + result= !(l->lock_data->waiting_exclusive.is_empty() && + l->lock_data->active_shared_waiting_upgrade.is_empty()); + pthread_mutex_unlock(&LOCK_mdl); + return result; +} + + +/** + Associate pointer to an opaque object with a lock. + + @param l Lock request for the lock with which the + object should be associated. + @param cached_object Pointer to the object + @param release_hook Cleanup function to be called when MDL subsystem + decides to remove lock or associate another object. + + This is used to cache a pointer to TABLE_SHARE in the lock + structure. Such caching can save one acquisition of LOCK_open + and one table definition cache lookup for every table. + + Since the pointer may be stored only inside an acquired lock, + the caching is only effective when there is more than one lock + granted on a given table. + + This function has the following usage pattern: + - try to acquire an MDL lock + - when done, call for mdl_get_cached_object(). If it returns NULL, our + thread has the only lock on this table. + - look up TABLE_SHARE in the table definition cache + - call mdl_set_cache_object() to assign the share to the opaque pointer. + + The release hook is invoked when the last shared metadata + lock on this name is released. +*/ + +void mdl_set_cached_object(MDL_LOCK *l, void *cached_object, + mdl_cached_object_release_hook release_hook) +{ + DBUG_ENTER("mdl_set_cached_object"); + DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", l->key + 4, + l->key + 4 + strlen(l->key + 4) + 1, + cached_object)); + + DBUG_ASSERT(l->state == MDL_ACQUIRED || l->state == MDL_PENDING_UPGRADE); + + /* + TODO: This assumption works now since we do mdl_get_cached_object() + and mdl_set_cached_object() in the same critical section. Once + this becomes false we will have to call release_hook here and + use additional mutex protecting 'cached_object' member. + */ + DBUG_ASSERT(!l->lock_data->cached_object); + + l->lock_data->cached_object= cached_object; + l->lock_data->cached_object_release_hook= release_hook; + + DBUG_VOID_RETURN; +} + + +/** + Get a pointer to an opaque object that associated with the lock. + + @param l Lock request for the lock with which the object is + associated. + + @return Pointer to an opaque object associated with the lock. +*/ + +void* mdl_get_cached_object(MDL_LOCK *l) +{ + DBUG_ASSERT(l->state == MDL_ACQUIRED || l->state == MDL_PENDING_UPGRADE); + return l->lock_data->cached_object; +} diff --git a/sql/mdl.h b/sql/mdl.h new file mode 100644 index 00000000000..ca3d8d0a784 --- /dev/null +++ b/sql/mdl.h @@ -0,0 +1,260 @@ +#ifndef MDL_H +#define MDL_H +/* Copyright (C) 2007-2008 MySQL AB + + 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 "sql_plist.h" +#include +#include + +class THD; + +struct MDL_LOCK; +struct MDL_LOCK_DATA; +struct MDL_CONTEXT; + +/** Type of metadata lock request. */ + +enum enum_mdl_type {MDL_SHARED=0, MDL_EXCLUSIVE}; + + +/** States which metadata lock request can have. */ + +enum enum_mdl_state {MDL_PENDING=0, MDL_ACQUIRED, MDL_PENDING_UPGRADE}; + + +/** + Priority of metadata lock requests. High priority attribute is + applicable only to requests for shared locks and indicates that + such request should ignore pending requests for exclusive locks + and for upgrading of shared locks to exclusive. +*/ + +enum enum_mdl_prio {MDL_NORMAL_PRIO=0, MDL_HIGH_PRIO}; + + +/** + A pending lock request or a granted metadata lock. A lock is requested + or granted based on a fully qualified name and type. E.g. for a table + the key consists of <0 (=table)>++. + Later in this document this triple will be referred to simply as + "key" or "name". +*/ + +struct MDL_LOCK +{ + char *key; + uint key_length; + enum enum_mdl_type type; + enum enum_mdl_state state; + enum enum_mdl_prio prio; + /** + TRUE -- if shared lock corresponding to this lock request at some + point might be upgraded to an exclusive lock and therefore conflicts + with global shared lock, FALSE -- otherwise. + */ + bool upgradable; + +private: + /** + Pointers for participating in the list of lock requests for this context. + */ + MDL_LOCK *next_context; + MDL_LOCK **prev_context; + /** + Pointers for participating in the list of satisfied/pending requests + for the lock. + */ + MDL_LOCK *next_lock; + MDL_LOCK **prev_lock; + + friend struct MDL_LOCK_context; + friend struct MDL_LOCK_lock; + +public: + /* + Pointer to the lock object for this lock request. Valid only if this lock + request is satisified or is present in the list of pending lock requests + for particular lock. + */ + MDL_LOCK_DATA *lock_data; + MDL_CONTEXT *ctx; +}; + + +/** + Helper class which specifies which members of MDL_LOCK are used for + participation in the list lock requests belonging to one context. +*/ + +struct MDL_LOCK_context +{ + static inline MDL_LOCK **next_ptr(MDL_LOCK *l) + { + return &l->next_context; + } + static inline MDL_LOCK ***prev_ptr(MDL_LOCK *l) + { + return &l->prev_context; + } +}; + + +/** + Helper class which specifies which members of MDL_LOCK are used for + participation in the list of satisfied/pending requests for the lock. +*/ + +struct MDL_LOCK_lock +{ + static inline MDL_LOCK **next_ptr(MDL_LOCK *l) + { + return &l->next_lock; + } + static inline MDL_LOCK ***prev_ptr(MDL_LOCK *l) + { + return &l->prev_lock; + } +}; + + +/** + Context of the owner of metadata locks. I.e. each server + connection has such a context. +*/ + +struct MDL_CONTEXT +{ + I_P_List locks; + bool has_global_shared_lock; + THD *thd; +}; + + +void mdl_init(); +void mdl_destroy(); + +void mdl_context_init(MDL_CONTEXT *context, THD *thd); +void mdl_context_destroy(MDL_CONTEXT *context); +void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); +void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); +void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); + +void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db, + const char *name); +MDL_LOCK *mdl_alloc_lock(int type, const char *db, const char *name, + MEM_ROOT *root); +void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK *lock); +void mdl_remove_all_locks(MDL_CONTEXT *context); + +/** + Set type of lock request. Can be only applied to pending locks. +*/ + +inline void mdl_set_lock_type(MDL_LOCK *lock, enum_mdl_type lock_type) +{ + DBUG_ASSERT(lock->state == MDL_PENDING); + lock->type= lock_type; +} + +/** + Set priority for lock request. High priority can be only set + for shared locks. +*/ + +inline void mdl_set_lock_priority(MDL_LOCK *lock, enum_mdl_prio prio) +{ + DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING); + lock->prio= prio; +} + +/** + Mark request for shared lock as upgradable. Can be only applied + to pending locks. +*/ + +inline void mdl_set_upgradable(MDL_LOCK *lock) +{ + DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING); + lock->upgradable= TRUE; +} + +bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry); +bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); +bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, + const char *db, const char *name); +bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *lock); +bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); + +bool mdl_wait_for_locks(MDL_CONTEXT *context); + +void mdl_release_locks(MDL_CONTEXT *context); +void mdl_release_exclusive_locks(MDL_CONTEXT *context); +void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lock); +void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context); +void mdl_release_global_shared_lock(MDL_CONTEXT *context); + +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, + const char *name); +bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, + const char *name); + +bool mdl_has_pending_conflicting_lock(MDL_LOCK *l); + +inline bool mdl_has_locks(MDL_CONTEXT *context) +{ + return !context->locks.is_empty(); +} + + +/** + Get iterator for walking through all lock requests in the context. +*/ + +inline I_P_List_iterator mdl_get_locks(MDL_CONTEXT *ctx) +{ + I_P_List_iterator result(ctx->locks); + return result; +} + +/** + Give metadata lock request object for the table get table definition + cache key corresponding to it. + + @param l [in] Lock request object for the table. + @param key [out] LEX_STRING object where table definition cache key + should be put. + + @note This key will have the same life-time as this lock request object. + + @note This is yet another place where border between MDL subsystem and the + rest of the server is broken. OTOH it allows to save some CPU cycles + and memory by avoiding generating these TDC keys from table list. +*/ + +inline void mdl_get_tdc_key(MDL_LOCK *l, LEX_STRING *key) +{ + key->str= l->key + 4; + key->length= l->key_length - 4; +} + + +typedef void (* mdl_cached_object_release_hook)(void *); +void* mdl_get_cached_object(MDL_LOCK *l); +void mdl_set_cached_object(MDL_LOCK *l, void *cached_object, + mdl_cached_object_release_hook release_hook); + +#endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index c6454679e28..be03759b383 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -805,7 +805,7 @@ extern my_decimal decimal_zero; void free_items(Item *item); void cleanup_items(Item *item); class THD; -void close_thread_tables(THD *thd); +void close_thread_tables(THD *thd, bool skip_mdl= 0); #ifndef NO_EMBEDDED_ACCESS_CHECKS bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); @@ -993,7 +993,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, bool log_query); bool quick_rm_table(handlerton *base,const char *db, const char *table_name, uint flags); -void close_cached_table(THD *thd, TABLE *table); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent); bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, char *new_table_alias, @@ -1034,10 +1033,8 @@ bool check_dup(const char *db, const char *name, TABLE_LIST *tables); bool compare_record(TABLE *table); bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); -void wait_while_table_is_used(THD *thd, TABLE *table, +bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function); -bool table_cache_init(void); -void table_cache_free(void); bool table_def_init(void); void table_def_free(void); void assign_new_table_id(TABLE_SHARE *share); @@ -1223,28 +1220,30 @@ void release_table_share(TABLE_SHARE *share, enum release_type type); TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, uint lock_flags); +enum enum_open_table_action {OT_NO_ACTION= 0, OT_BACK_OFF_AND_RETRY, + OT_DISCOVER, OT_REPAIR}; TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, - bool *refresh, uint flags); + enum_open_table_action *action, uint flags); +bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, + char *cache_key, uint cache_key_length, + MEM_ROOT *mem_root, uint flags); bool name_lock_locked_table(THD *thd, TABLE_LIST *tables); -bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in); -TABLE *table_cache_insert_placeholder(THD *thd, const char *key, - uint key_length); -bool lock_table_name_if_not_cached(THD *thd, const char *db, - const char *table_name, TABLE **table); -TABLE *find_locked_table(THD *thd, const char *db,const char *table_name); +bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list); +TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name); +TABLE *find_write_locked_table(TABLE *list, const char *db, + const char *table_name); void detach_merge_children(TABLE *table, bool clear_refs); bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, TABLE_LIST *new_child_list, TABLE_LIST **new_last); bool reopen_table(TABLE *table); -bool reopen_tables(THD *thd,bool get_locks,bool in_refresh); +bool reopen_tables(THD *thd, bool get_locks); thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table); void close_data_files_and_morph_locks(THD *thd, const char *db, const char *table_name); void close_handle_and_leave_table_as_lock(TABLE *table); bool wait_for_tables(THD *thd); bool table_is_used(TABLE *table, bool wait_for_name_lock); -TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name); -void abort_locked_tables(THD *thd,const char *db, const char *table_name); +void unlock_locked_tables(THD *thd); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); extern Field *not_found_field; @@ -1364,7 +1363,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables); bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, List *,enum ha_rkey_function,Item *,ha_rows,ha_rows); void mysql_ha_flush(THD *thd); -void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked); +void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables); void mysql_ha_cleanup(THD *thd); /* sql_base.cc */ @@ -1501,7 +1500,7 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables); +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl); TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, const char *db_name, @@ -1550,6 +1549,9 @@ char *generate_partition_syntax(partition_info *part_info, #define RTFC_CHECK_KILLED_FLAG 0x0004 bool remove_table_from_cache(THD *thd, const char *db, const char *table, uint flags); +bool notify_thread_having_shared_lock(THD *thd, THD *in_use); +void expel_table_from_cache(THD *leave_thd, const char *db, + const char *table_name); #define NORMAL_PART_NAME 0 #define TEMP_PART_NAME 1 @@ -1681,7 +1683,7 @@ TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table, void close_performance_schema_table(THD *thd, Open_tables_state *backup); bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, - bool wait_for_refresh, bool wait_for_placeholders); + bool wait_for_refresh); bool close_cached_connection_tables(THD *thd, bool wait_for_refresh, LEX_STRING *connect_string, bool have_lock = FALSE); @@ -2001,8 +2003,9 @@ extern const char *opt_date_time_formats[]; extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[]; extern String null_string; -extern HASH open_cache, lock_db_cache; +extern HASH table_def_cache, lock_db_cache; extern TABLE *unused_tables; +extern uint table_cache_count; extern const char* any_db; extern struct my_option my_long_options[]; extern const LEX_STRING view_type; @@ -2070,18 +2073,8 @@ void unset_protect_against_global_read_lock(void); void broadcast_refresh(void); /* Lock based on name */ -int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list); -int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use); -void unlock_table_name(THD *thd, TABLE_LIST *table_list); -bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list); bool lock_table_names(THD *thd, TABLE_LIST *table_list); -void unlock_table_names(THD *thd, TABLE_LIST *table_list, - TABLE_LIST *last_table); -bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list); -bool is_table_name_exclusively_locked_by_this_thread(THD *thd, - TABLE_LIST *table_list); -bool is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key, - int key_length); +void unlock_table_names(THD *thd); /* old unireg functions */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1cab245b317..90924b5b662 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1301,9 +1301,9 @@ void clean_up(bool print_message) grant_free(); #endif query_cache_destroy(); - table_cache_free(); table_def_free(); hostname_cache_free(); + mdl_destroy(); item_user_lock_free(); lex_free(); /* Free some memory */ item_create_cleanup(); @@ -3755,7 +3755,8 @@ static int init_server_components() We need to call each of these following functions to ensure that all things are initialized so that unireg_abort() doesn't fail */ - if (table_cache_init() | table_def_init() | hostname_cache_init()) + mdl_init(); + if (table_def_init() | hostname_cache_init()) unireg_abort(1); query_cache_result_size_limit(query_cache_limit); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index ae3cbf789a5..42cd12bd66c 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1187,8 +1187,7 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) end_trans(thd, ROLLBACK); // if a "real transaction" } m_table_map.clear_tables(); - close_thread_tables(thd); - clear_tables_to_lock(); + slave_close_thread_tables(thd); clear_flag(IN_STMT); /* Cleanup for the flags that have been set at do_apply_event. @@ -1200,6 +1199,13 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) void Relay_log_info::clear_tables_to_lock() { + /* + Deallocating elements of table list below will also free memory where + meta-data locks are stored. So we want to be sure that we don't have + any references to this memory left. + */ + DBUG_ASSERT(!mdl_has_locks(&(current_thd->mdl_context))); + while (tables_to_lock) { uchar* to_free= reinterpret_cast(tables_to_lock); @@ -1216,4 +1222,15 @@ void Relay_log_info::clear_tables_to_lock() DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); } +void Relay_log_info::slave_close_thread_tables(THD *thd) +{ + /* + Since we use same memory chunks for allocation of metadata lock + objects for tables as we use for allocating corresponding elements + of 'tables_to_lock' list, we have to release metadata locks by + closing tables before calling clear_tables_to_lock(). + */ + close_thread_tables(thd); + clear_tables_to_lock(); +} #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index fd36d18adae..332bc4e53d0 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -346,6 +346,7 @@ public: bool cached_charset_compare(char *charset) const; void cleanup_context(THD *, bool); + void slave_close_thread_tables(THD *); void clear_tables_to_lock(); /* diff --git a/sql/set_var.cc b/sql/set_var.cc index 1028e5441ae..1490ffd9598 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -4333,7 +4333,7 @@ bool sys_var_opt_readonly::update(THD *thd, set_var *var) can cause to wait on a read lock, it's required for the client application to unlock everything, and acceptable for the server to wait on all locks. */ - if ((result= close_cached_tables(thd, NULL, FALSE, TRUE, TRUE))) + if ((result= close_cached_tables(thd, NULL, FALSE, TRUE))) goto end_with_read_lock; if ((result= make_global_read_lock_block_commit(thd))) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f90aefc2a3f..14573cd6884 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3984,6 +3984,9 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; + table->mdl_lock= mdl_alloc_lock(0, table->db, table->table_name, + thd->mdl_el_root ? thd->mdl_el_root : + thd->mem_root); /* Everyting else should be zeroed */ @@ -4025,7 +4028,9 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - + table->mdl_lock= mdl_alloc_lock(0, table->db, table->table_name, + thd->mdl_el_root ? thd->mdl_el_root : + thd->mem_root); lex->add_to_query_tables(table); return table; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 641423605aa..b01e7b1049d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -676,12 +676,7 @@ my_bool acl_reload(THD *thd) my_bool return_val= 1; DBUG_ENTER("acl_reload"); - if (thd->locked_tables) - { // Can't have locked tables here - thd->lock=thd->locked_tables; - thd->locked_tables=0; - close_thread_tables(thd); - } + unlock_locked_tables(thd); // Can't have locked tables here /* To avoid deadlocks we should obtain table locks before @@ -697,6 +692,7 @@ my_bool acl_reload(THD *thd) tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= tables[2].skip_temporary= TRUE; + alloc_mdl_locks(tables, thd->mem_root); if (simple_open_n_lock_tables(thd, tables)) { @@ -1601,6 +1597,7 @@ bool change_password(THD *thd, const char *host, const char *user, bzero((char*) &tables, sizeof(tables)); tables.alias= tables.table_name= (char*) "user"; tables.db= (char*) "mysql"; + alloc_mdl_locks(&tables, thd->mem_root); #ifdef HAVE_REPLICATION /* @@ -3053,6 +3050,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, ? tables+2 : 0); tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + alloc_mdl_locks(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3270,6 +3268,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + alloc_mdl_locks(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3408,6 +3407,7 @@ bool mysql_grant(THD *thd, const char *db, List &list, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + alloc_mdl_locks(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3741,6 +3741,7 @@ static my_bool grant_reload_procs_priv(THD *thd) table.db= (char *) "mysql"; table.lock_type= TL_READ; table.skip_temporary= 1; + alloc_mdl_locks(&table, thd->mem_root); if (simple_open_n_lock_tables(thd, &table)) { @@ -3807,6 +3808,8 @@ my_bool grant_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type= tables[1].lock_type= TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= TRUE; + alloc_mdl_locks(tables, thd->mem_root); + /* To avoid deadlocks we should obtain table locks before obtaining LOCK_grant rwlock. @@ -5152,6 +5155,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) (tables+4)->lock_type= TL_WRITE; tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db= (tables+4)->db= (char*) "mysql"; + alloc_mdl_locks(tables, thd->mem_root); #ifdef HAVE_REPLICATION /* diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8f31ef6999a..de4aaac633e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -96,17 +96,32 @@ bool Prelock_error_handler::safely_trapped_errors() @defgroup Data_Dictionary Data Dictionary @{ */ -TABLE *unused_tables; /* Used by mysql_test */ -HASH open_cache; /* Used by mysql_test */ -static HASH table_def_cache; + +/** + Total number of TABLE instances for tables in the table definition cache + (both in use by threads and not in use). This value is accessible to user + as "Open_tables" status variable. +*/ +uint table_cache_count= 0; +/** + List that contains all TABLE instances for tables in the table definition + cache that are not in use by any thread. Recently used TABLE instances are + appended to the end of the list. Thus the beginning of the list contains + tables which have been least recently used. +*/ +TABLE *unused_tables; +HASH table_def_cache; static TABLE_SHARE *oldest_unused_share, end_of_unused_share; static pthread_mutex_t LOCK_table_share; static bool table_def_inited= 0; -static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, - const char *alias, - char *cache_key, uint cache_key_length, - MEM_ROOT *mem_root, uint flags); +static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables, + TABLE_SHARE *table_share); +static bool reopen_table_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, + const char *alias, char *cache_key, + uint cache_key_length); +static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry); +static bool auto_repair_table(THD *thd, TABLE_LIST *table_list); static void free_cache_entry(TABLE *entry); static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, @@ -114,41 +129,14 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, TABLE_LIST *table_desc, MEM_ROOT *mem_root); static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, bool send_refresh); +static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context); static bool has_write_table_with_auto_increment(TABLE_LIST *tables); -extern "C" uchar *table_cache_key(const uchar *record, size_t *length, - my_bool not_used __attribute__((unused))) -{ - TABLE *entry=(TABLE*) record; - *length= entry->s->table_cache_key.length; - return (uchar*) entry->s->table_cache_key.str; -} - - -bool table_cache_init(void) -{ - return my_hash_init(&open_cache, &my_charset_bin, table_cache_size+16, - 0, 0, table_cache_key, - (my_hash_free_key) free_cache_entry, 0) != 0; -} - -void table_cache_free(void) -{ - DBUG_ENTER("table_cache_free"); - if (table_def_inited) - { - close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE); - if (!open_cache.records) // Safety first - my_hash_free(&open_cache); - } - DBUG_VOID_RETURN; -} - uint cached_open_tables(void) { - return open_cache.records; + return table_cache_count; } @@ -156,7 +144,8 @@ uint cached_open_tables(void) static void check_unused(void) { uint count= 0, open_files= 0, idx= 0; - TABLE *cur_link,*start_link; + TABLE *cur_link, *start_link, *entry; + TABLE_SHARE *share; if ((start_link=cur_link=unused_tables)) { @@ -167,45 +156,42 @@ static void check_unused(void) DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */ return; /* purecov: inspected */ } - } while (count++ < open_cache.records && + } while (count++ < table_cache_count && (cur_link=cur_link->next) != start_link); if (cur_link != start_link) { DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */ } } - for (idx=0 ; idx < open_cache.records ; idx++) + for (idx=0 ; idx < table_def_cache.records ; idx++) { - TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx); - if (!entry->in_use) + share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx); + + I_P_List_iterator it(share->free_tables); + while ((entry= it++)) + { + if (entry->in_use) + { + DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */ + } count--; - if (entry->file) open_files++; + } + it.init(share->used_tables); + while ((entry= it++)) + { + if (!entry->in_use) + { + DBUG_PRINT("error",("Unused table is in share's list of used tables")); /* purecov: inspected */ + } + open_files++; + } } if (count != 0) { DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */ count)); /* purecov: inspected */ } - -#ifdef NOT_SAFE_FOR_REPAIR - /* - check that open cache and table definition cache has same number of - aktive tables - */ - count= 0; - for (idx=0 ; idx < table_def_cache.records ; idx++) - { - TABLE_SHARE *entry= (TABLE_SHARE*) hash_element(&table_def_cache,idx); - count+= entry->ref_count; - } - if (count != open_files) - { - DBUG_PRINT("error", ("table_def ref_count: %u open_cache: %u", - count, open_files)); - DBUG_ASSERT(count == open_files); - } -#endif } #else #define check_unused() @@ -300,8 +286,11 @@ void table_def_free(void) DBUG_ENTER("table_def_free"); if (table_def_inited) { + /* Free all open TABLEs first. */ + close_cached_tables(NULL, NULL, FALSE, FALSE); table_def_inited= 0; pthread_mutex_destroy(&LOCK_table_share); + /* Free table definitions. */ my_hash_free(&table_def_cache); } DBUG_VOID_RETURN; @@ -314,6 +303,128 @@ uint cached_table_definitions(void) } +/* + Auxiliary routines for manipulating with per-share used/unused and + global unused lists of TABLE objects and table_cache_count counter. + Responsible for preserving invariants between those lists, counter + and TABLE::in_use member. + In fact those routines implement sort of implicit table cache as + part of table definition cache. +*/ + + +/** + Add newly created TABLE object for table share which is going + to be used right away. +*/ + +static void table_def_add_used_table(THD *thd, TABLE *table) +{ + DBUG_ASSERT(table->in_use == thd); + table->s->used_tables.push_front(table); + table_cache_count++; +} + + +/** + Prepare used or unused TABLE instance for destruction by removing + it from share's and global list. +*/ + +static void table_def_remove_table(TABLE *table) +{ + if (table->in_use) + { + /* Remove from per-share chain of used TABLE objects. */ + table->s->used_tables.remove(table); + } + else + { + /* Remove from per-share chain of unused TABLE objects. */ + table->s->free_tables.remove(table); + + /* And global unused chain. */ + table->next->prev=table->prev; + table->prev->next=table->next; + if (table == unused_tables) + { + unused_tables=unused_tables->next; + if (table == unused_tables) + unused_tables=0; + } + check_unused(); + } + table_cache_count--; +} + + +/** + Mark already existing TABLE instance as used. +*/ + +static void table_def_use_table(THD *thd, TABLE *table) +{ + DBUG_ASSERT(!table->in_use); + + /* Unlink table from list of unused tables for this share. */ + table->s->free_tables.remove(table); + /* Unlink able from global unused tables list. */ + if (table == unused_tables) + { // First unused + unused_tables=unused_tables->next; // Remove from link + if (table == unused_tables) + unused_tables=0; + } + table->prev->next=table->next; /* Remove from unused list */ + table->next->prev=table->prev; + check_unused(); + /* Add table to list of used tables for this share. */ + table->s->used_tables.push_front(table); + table->in_use= thd; +} + + +/** + Mark already existing used TABLE instance as unused. +*/ + +static void table_def_unuse_table(TABLE *table) +{ + DBUG_ASSERT(table->in_use); + + table->in_use= 0; + /* Remove table from the list of tables used in this share. */ + table->s->used_tables.remove(table); + /* Add table to the list of unused TABLE objects for this share. */ + table->s->free_tables.push_front(table); + /* Also link it last in the global list of unused TABLE objects. */ + if (unused_tables) + { + table->next=unused_tables; + table->prev=unused_tables->prev; + unused_tables->prev=table; + table->prev->next=table; + } + else + unused_tables=table->next=table->prev=table; + check_unused(); +} + + +/** + Bind used TABLE instance to another table share. + + @note Will go away once we refactor code responsible + for reopening tables under lock tables. +*/ + +static void table_def_change_share(TABLE *table, TABLE_SHARE *new_share) +{ + table->s->used_tables.remove(table); + new_share->used_tables.push_front(table); +} + + /* Get TABLE_SHARE for a table. @@ -347,6 +458,13 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, *error= 0; + /* + To be able perform any operation on table we should own + some kind of metadata lock on it. + */ + DBUG_ASSERT(mdl_is_lock_owner(&thd->mdl_context, 0, table_list->db, + table_list->table_name)); + /* Read table definition from cache */ if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, key_length))) @@ -569,6 +687,7 @@ void release_table_share(TABLE_SHARE *share, enum release_type type) safe_mutex_assert_owner(&LOCK_open); pthread_mutex_lock(&share->mutex); + DBUG_ASSERT(share->ref_count); if (!--share->ref_count) { if (share->version != refresh_version) @@ -629,6 +748,26 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name) } +/** + @brief Mark table share as having one more user (increase its reference + count). + + @param share Table share for which reference count should be increased. +*/ + +static void reference_table_share(TABLE_SHARE *share) +{ + DBUG_ENTER("reference_table_share"); + DBUG_ASSERT(share->ref_count); + pthread_mutex_lock(&share->mutex); + share->ref_count++; + pthread_mutex_unlock(&share->mutex); + DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", + (ulong) share, share->ref_count)); + DBUG_VOID_RETURN; +} + + /* Close file handle, but leave the table in the table cache @@ -680,6 +819,7 @@ void close_handle_and_leave_table_as_lock(TABLE *table) detach_merge_children(table, FALSE); table->file->close(); table->db_stat= 0; // Mark file closed + table_def_change_share(table, share); release_table_share(table->s, RELEASE_NORMAL); table->s= share; table->file->change_table_ptr(table, table->s); @@ -719,11 +859,9 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) start_list= &open_list; open_list=0; - for (uint idx=0 ; result == 0 && idx < open_cache.records; idx++) + for (uint idx=0 ; result == 0 && idx < table_def_cache.records; idx++) { - OPEN_TABLE_LIST *table; - TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx); - TABLE_SHARE *share= entry->s; + TABLE_SHARE *share= (TABLE_SHARE *)my_hash_element(&table_def_cache, idx); if (db && my_strcasecmp(system_charset_info, db, share->db.str)) continue; @@ -737,21 +875,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE)) continue; - /* need to check if we haven't already listed it */ - for (table= open_list ; table ; table=table->next) - { - if (!strcmp(table->table, share->table_name.str) && - !strcmp(table->db, share->db.str)) - { - if (entry->in_use) - table->in_use++; - if (entry->locked_by_name) - table->locked++; - break; - } - } - if (table) - continue; + if (!(*start_list = (OPEN_TABLE_LIST *) sql_alloc(sizeof(**start_list)+share->table_cache_key.length))) { @@ -762,8 +886,11 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) strmov(((*start_list)->db= (char*) ((*start_list)+1)), share->db.str)+1, share->table_name.str); - (*start_list)->in_use= entry->in_use ? 1 : 0; - (*start_list)->locked= entry->locked_by_name ? 1 : 0; + (*start_list)->in_use= 0; + I_P_List_iterator it(share->used_tables); + while (it++) + ++(*start_list)->in_use; + (*start_list)->locked= (share->version == 0) ? 1 : 0; start_list= &(*start_list)->next; *start_list=0; } @@ -809,19 +936,11 @@ static void free_cache_entry(TABLE *table) /* Assert that MERGE children are not attached before final close. */ DBUG_ASSERT(!table->is_children_attached()); + /* This should be done before releasing table share. */ + table_def_remove_table(table); + intern_close_table(table); - if (!table->in_use) - { - table->next->prev=table->prev; /* remove from used chain */ - table->prev->next=table->next; - if (table == unused_tables) - { - unused_tables=unused_tables->next; - if (table == unused_tables) - unused_tables=0; - } - check_unused(); // consisty check - } + my_free((uchar*) table,MYF(0)); DBUG_VOID_RETURN; } @@ -841,6 +960,38 @@ void free_io_cache(TABLE *table) } +/** + Auxiliary function which allows to kill delayed threads for + particular table identified by its share. + + @param share Table share. +*/ + +static void kill_delayed_threads_for_table(TABLE_SHARE *share) +{ + I_P_List_iterator it(share->used_tables); + TABLE *tab; + while ((tab= it++)) + { + THD *in_use= tab->in_use; + + if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + ! in_use->killed) + { + in_use->killed= THD::KILL_CONNECTION; + pthread_mutex_lock(&in_use->mysys_var->mutex); + if (in_use->mysys_var->current_cond) + { + pthread_mutex_lock(in_use->mysys_var->current_mutex); + pthread_cond_broadcast(in_use->mysys_var->current_cond); + pthread_mutex_unlock(in_use->mysys_var->current_mutex); + } + pthread_mutex_unlock(&in_use->mysys_var->mutex); + } + } +} + + /* Close all tables which aren't in use by any thread @@ -848,18 +999,23 @@ void free_io_cache(TABLE *table) @param tables List of tables to remove from the cache @param have_lock If LOCK_open is locked @param wait_for_refresh Wait for a impending flush - @param wait_for_placeholders Wait for tables being reopened so that the GRL - won't proceed while write-locked tables are being reopened by other - threads. - @remark THD can be NULL, but then wait_for_refresh must be FALSE - and tables must be NULL. + @note THD can be NULL, but then wait_for_refresh must be FALSE + and tables must be NULL. + + @note When called as part of FLUSH TABLES WITH READ LOCK this function + ignores metadata locks held by other threads. In order to avoid + situation when FLUSH TABLES WITH READ LOCK sneaks in at the moment + when some write-locked table is being reopened (by FLUSH TABLES or + ALTER TABLE) we have to rely on additional global shared metadata + lock taken by thread trying to obtain global read lock. */ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, - bool wait_for_refresh, bool wait_for_placeholders) + bool wait_for_refresh) { - bool result=0; + bool result= FALSE; + bool found= TRUE; DBUG_ENTER("close_cached_tables"); DBUG_ASSERT(thd || (!wait_for_refresh && !tables)); @@ -868,165 +1024,181 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, if (!tables) { refresh_version++; // Force close of open tables - while (unused_tables) - { -#ifdef EXTRA_DEBUG - if (my_hash_delete(&open_cache,(uchar*) unused_tables)) - printf("Warning: Couldn't delete open table from hash\n"); -#else - (void) my_hash_delete(&open_cache,(uchar*) unused_tables); -#endif - } - /* Free table shares */ - while (oldest_unused_share->next) - { - pthread_mutex_lock(&oldest_unused_share->mutex); - (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share); - } DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", refresh_version)); - if (wait_for_refresh) - { - /* - Other threads could wait in a loop in open_and_lock_tables(), - trying to lock one or more of our tables. - - If they wait for the locks in thr_multi_lock(), their lock - request is aborted. They loop in open_and_lock_tables() and - enter open_table(). Here they notice the table is refreshed and - wait for COND_refresh. Then they loop again in - open_and_lock_tables() and this time open_table() succeeds. At - this moment, if we (the FLUSH TABLES thread) are scheduled and - on another FLUSH TABLES enter close_cached_tables(), they could - awake while we sleep below, waiting for others threads (us) to - close their open tables. If this happens, the other threads - would find the tables unlocked. They would get the locks, one - after the other, and could do their destructive work. This is an - issue if we have LOCK TABLES in effect. - - The problem is that the other threads passed all checks in - open_table() before we refresh the table. - - The fix for this problem is to set some_tables_deleted for all - threads with open tables. These threads can still get their - locks, but will immediately release them again after checking - this variable. They will then loop in open_and_lock_tables() - again. There they will wait until we update all tables version - below. - - Setting some_tables_deleted is done by remove_table_from_cache() - in the other branch. - - In other words (reviewer suggestion): You need this setting of - some_tables_deleted for the case when table was opened and all - related checks were passed before incrementing refresh_version - (which you already have) but attempt to lock the table happened - after the call to close_old_data_files() i.e. after removal of - current thread locks. - */ - for (uint idx=0 ; idx < open_cache.records ; idx++) - { - TABLE *table=(TABLE*) my_hash_element(&open_cache,idx); - if (table->in_use) - table->in_use->some_tables_deleted= 1; - } - } + kill_delayed_threads(); } else { bool found=0; for (TABLE_LIST *table= tables; table; table= table->next_local) { - if (remove_table_from_cache(thd, table->db, table->table_name, - RTFC_OWNED_BY_THD_FLAG)) + TABLE_SHARE *share= get_cached_table_share(table->db, table->table_name); + + if (share) + { + share->version= 0; + kill_delayed_threads_for_table(share); found=1; + } } if (!found) wait_for_refresh=0; // Nothing to wait for } -#ifndef EMBEDDED_LIBRARY - if (!tables) - kill_delayed_threads(); -#endif - if (wait_for_refresh) + + /* + Get rid of all unused TABLE and TABLE_SHARE instances. By doing + this we automatically close all tables which were marked as "old". + + FIXME: Do not close all unused TABLE instances when flushing + particular table. + */ + while (unused_tables) + free_cache_entry(unused_tables); + /* Free table shares */ + while (oldest_unused_share->next) + { + pthread_mutex_lock(&oldest_unused_share->mutex); + (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share); + } + + if (!wait_for_refresh) + { + if (!have_lock) + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(result); + } + + DBUG_ASSERT(!have_lock); + pthread_mutex_unlock(&LOCK_open); + + if (thd->locked_tables) { /* - If there is any table that has a lower refresh_version, wait until - this is closed (or this thread is killed) before returning + If we are under LOCK TABLES we need to reopen tables without + opening a door for any concurrent threads to sneak in and get + lock on our tables. To achieve this we use exclusive metadata + locks. */ - thd->mysys_var->current_mutex= &LOCK_open; - thd->mysys_var->current_cond= &COND_refresh; - thd_proc_info(thd, "Flushing tables"); + if (!tables) + { + for (TABLE *tab= thd->open_tables; tab; tab= tab->next) + { + /* + Checking TABLE::db_stat is essential in case when we have + several instances of the table open and locked. + */ + if (tab->db_stat) + { + char dbname[NAME_LEN+1]; + char tname[NAME_LEN+1]; + /* + Since close_data_files_and_morph_locks() frees share's memroot + we need to make copies of database and table names. + */ + strmov(dbname, tab->s->db.str); + strmov(tname, tab->s->table_name.str); + if (wait_while_table_is_used(thd, tab, HA_EXTRA_FORCE_REOPEN)) + { + result= TRUE; + goto err_with_reopen; + } + pthread_mutex_lock(&LOCK_open); + close_data_files_and_morph_locks(thd, dbname, tname); + pthread_mutex_unlock(&LOCK_open); + } + } + } + else + { + for (TABLE_LIST *table= tables; table; table= table->next_local) + { + TABLE *tab= find_locked_table(thd->open_tables, table->db, + table->table_name); + /* + Checking TABLE::db_stat is essential in case when we have + several instances of the table open and locked. + */ + if (tab->db_stat) + { + if (wait_while_table_is_used(thd, tab, HA_EXTRA_FORCE_REOPEN)) + { + result= TRUE; + goto err_with_reopen; + } + pthread_mutex_lock(&LOCK_open); + close_data_files_and_morph_locks(thd, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); + } + } + } + } - close_old_data_files(thd,thd->open_tables,1,1); + /* Wait until all threads have closed all the tables we are flushing. */ + DBUG_PRINT("info", ("Waiting for other threads to close their open tables")); + + while (found && ! thd->killed) + { + found= FALSE; + /* + To avoid self and other kinds of deadlock we have to flush open HANDLERs. + */ mysql_ha_flush(thd); DEBUG_SYNC(thd, "after_flush_unlock"); - bool found=1; - /* Wait until all threads has closed all the tables we had locked */ - DBUG_PRINT("info", - ("Waiting for other threads to close their open tables")); - while (found && ! thd->killed) + pthread_mutex_lock(&LOCK_open); + + thd->enter_cond(&COND_refresh, &LOCK_open, "Flushing tables"); + + if (!tables) { - found=0; - for (uint idx=0 ; idx < open_cache.records ; idx++) + for (uint idx=0 ; idx < table_def_cache.records ; idx++) { - TABLE *table=(TABLE*) my_hash_element(&open_cache,idx); - /* Avoid a self-deadlock. */ - if (table->in_use == thd) - continue; - /* - Note that we wait here only for tables which are actually open, and - not for placeholders with TABLE::open_placeholder set. Waiting for - latter will cause deadlock in the following scenario, for example: - - conn1: lock table t1 write; - conn2: lock table t2 write; - conn1: flush tables; - conn2: flush tables; - - It also does not make sense to wait for those of placeholders that - are employed by CREATE TABLE as in this case table simply does not - exist yet. - */ - if (table->needs_reopen_or_name_lock() && (table->db_stat || - (table->open_placeholder && wait_for_placeholders))) - { - found=1; - DBUG_PRINT("signal", ("Waiting for COND_refresh")); - pthread_cond_wait(&COND_refresh,&LOCK_open); - break; - } + TABLE_SHARE *share=(TABLE_SHARE*) my_hash_element(&table_def_cache, + idx); + if (share->version != refresh_version) + { + found= TRUE; + break; + } } } + else + { + for (TABLE_LIST *table= tables; table; table= table->next_local) + { + TABLE_SHARE *share= get_cached_table_share(table->db, table->table_name); + if (share && share->version != refresh_version) + { + found= TRUE; + break; + } + } + } + + if (found) + { + DBUG_PRINT("signal", ("Waiting for COND_refresh")); + pthread_cond_wait(&COND_refresh,&LOCK_open); + } + + thd->exit_cond(NULL); + } + +err_with_reopen: + if (thd->locked_tables) + { + pthread_mutex_lock(&LOCK_open); /* No other thread has the locked tables open; reopen them and get the old locks. This should always succeed (unless some external process has removed the tables) */ thd->in_lock_tables=1; - result=reopen_tables(thd,1,1); + result|= reopen_tables(thd, 1); thd->in_lock_tables=0; - /* Set version for table */ - for (TABLE *table=thd->open_tables; table ; table= table->next) - { - /* - Preserve the version (0) of write locked tables so that a impending - global read lock won't sneak in. - */ - if (table->reginfo.lock_type < TL_WRITE_ALLOW_WRITE) - table->s->version= refresh_version; - } - } - if (!have_lock) pthread_mutex_unlock(&LOCK_open); - if (wait_for_refresh) - { - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - thd_proc_info(thd, 0); - pthread_mutex_unlock(&thd->mysys_var->mutex); + mdl_downgrade_exclusive_locks(&thd->mdl_context); } DBUG_RETURN(result); } @@ -1079,7 +1251,7 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh, } if (tables) - result= close_cached_tables(thd, tables, TRUE, FALSE, FALSE); + result= close_cached_tables(thd, tables, TRUE, FALSE); if (!have_lock) pthread_mutex_unlock(&LOCK_open); @@ -1208,9 +1380,8 @@ static void close_open_tables(THD *thd) thd->some_tables_deleted= 0; /* Free tables to hold down open files */ - while (open_cache.records > table_cache_size && unused_tables) - my_hash_delete(&open_cache,(uchar*) unused_tables); /* purecov: tested */ - check_unused(); + while (table_cache_count > table_cache_size && unused_tables) + free_cache_entry(unused_tables); if (found_old_table) { /* Tell threads waiting for refresh that something has happened */ @@ -1239,7 +1410,8 @@ static void close_open_tables(THD *thd) leave prelocked mode if needed. */ -void close_thread_tables(THD *thd) +void close_thread_tables(THD *thd, + bool skip_mdl) { TABLE *table; prelocked_mode_type prelocked_mode= thd->prelocked_mode; @@ -1328,6 +1500,10 @@ void close_thread_tables(THD *thd) if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES) DBUG_VOID_RETURN; + /* + Note that we are leaving prelocked mode so we don't need + to care about THD::locked_tables_root. + */ thd->lock= thd->locked_tables; thd->locked_tables= 0; /* Fallthrough */ @@ -1358,6 +1534,12 @@ void close_thread_tables(THD *thd) if (thd->open_tables) close_open_tables(thd); + mdl_release_locks(&thd->mdl_context); + if (!skip_mdl) + { + mdl_remove_all_locks(&thd->mdl_context); + } + if (prelocked_mode == PRELOCKED) { /* @@ -1381,8 +1563,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) DBUG_ENTER("close_thread_table"); DBUG_ASSERT(table->key_read == 0); DBUG_ASSERT(!table->file || table->file->inited == handler::NONE); - DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); + safe_mutex_assert_owner(&LOCK_open); *table_ptr=table->next; /* @@ -1392,10 +1573,11 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) if (table->child_l || table->parent) detach_merge_children(table, TRUE); + table->mdl_lock= 0; if (table->needs_reopen_or_name_lock() || thd->version != refresh_version || !table->db_stat) { - my_hash_delete(&open_cache,(uchar*) table); + free_cache_entry(table); found_old_table=1; } else @@ -1413,16 +1595,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) free_field_buffers_larger_than(table,MAX_TDC_BLOB_SIZE); table->file->ha_reset(); - table->in_use=0; - if (unused_tables) - { - table->next=unused_tables; /* Link in last */ - table->prev=unused_tables->prev; - unused_tables->prev=table; - table->prev->next=table; - } - else - unused_tables=table->next=table->prev=table; + table_def_unuse_table(table); } DBUG_RETURN(found_old_table); } @@ -2121,7 +2294,7 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) /* Remove table from open_tables list. */ *prev= list->next; /* Close table. */ - my_hash_delete(&open_cache,(uchar*) list); // Close table + free_cache_entry(list); } else { @@ -2231,43 +2404,34 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) bool name_lock_locked_table(THD *thd, TABLE_LIST *tables) { + bool result= TRUE; + DBUG_ENTER("name_lock_locked_table"); /* Under LOCK TABLES we must only accept write locked tables. */ - tables->table= find_locked_table(thd, tables->db, tables->table_name); + tables->table= find_write_locked_table(thd->open_tables, tables->db, + tables->table_name); - if (!tables->table) - my_error(ER_TABLE_NOT_LOCKED, MYF(0), tables->alias); - else if (tables->table->reginfo.lock_type < TL_WRITE_LOW_PRIORITY) - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->alias); - else + if (tables->table) { /* Ensures that table is opened only by this thread and that no other statement will open this table. */ - wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN); - DBUG_RETURN(FALSE); + result= wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN); } - DBUG_RETURN(TRUE); + DBUG_RETURN(result); } /* - Open table which is already name-locked by this thread. + Open table for which this thread has exclusive meta-data lock. SYNOPSIS reopen_name_locked_table() thd Thread handle - table_list TABLE_LIST object for table to be open, TABLE_LIST::table - member should point to TABLE object which was used for - name-locking. - link_in TRUE - if TABLE object for table to be opened should be - linked into THD::open_tables list. - FALSE - placeholder used for name-locking is already in - this list so we only need to preserve TABLE::next - pointer. + table_list TABLE_LIST object for table to be open. NOTE This function assumes that its caller already acquired LOCK_open mutex. @@ -2277,33 +2441,32 @@ bool name_lock_locked_table(THD *thd, TABLE_LIST *tables) TRUE - Error */ -bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in) +bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) { - TABLE *table= table_list->table; + TABLE *table; TABLE_SHARE *share; + char key[MAX_DBKEY_LENGTH]; + uint key_length; char *table_name= table_list->table_name; - TABLE orig_table; DBUG_ENTER("reopen_name_locked_table"); - safe_mutex_assert_owner(&LOCK_open); - - if (thd->killed || !table) + if (thd->killed) DBUG_RETURN(TRUE); - orig_table= *table; + key_length= create_table_def_key(thd, key, table_list, 0); - if (open_unireg_entry(thd, table, table_list, table_name, - table->s->table_cache_key.str, - table->s->table_cache_key.length, thd->mem_root, 0)) + if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME)))) + DBUG_RETURN(TRUE); + + if (reopen_table_entry(thd, table, table_list, table_name, key, key_length)) { - intern_close_table(table); /* If there was an error during opening of table (for example if it does not exist) '*table' object can be wiped out. To be able properly release name-lock in this case we should restore this object to its original state. */ - *table= orig_table; + my_free((uchar*)table, MYF(0)); DBUG_RETURN(TRUE); } @@ -2318,21 +2481,11 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in) */ share->version=0; table->in_use = thd; - check_unused(); - if (link_in) - { - table->next= thd->open_tables; - thd->open_tables= table; - } - else - { - /* - TABLE object should be already in THD::open_tables list so we just - need to set TABLE::next correctly. - */ - table->next= orig_table.next; - } + table_def_add_used_table(thd, table); + + table->next= thd->open_tables; + thd->open_tables= table; table->tablenr=thd->current_tablenr++; table->used_fields=0; @@ -2340,109 +2493,7 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in) table->null_row= table->maybe_null= 0; table->force_index= table->force_index_order= table->force_index_group= 0; table->status=STATUS_NO_RECORD; - DBUG_RETURN(FALSE); -} - - -/** - Create and insert into table cache placeholder for table - which will prevent its opening (or creation) (a.k.a lock - table name). - - @param thd Thread context - @param key Table cache key for name to be locked - @param key_length Table cache key length - - @return Pointer to TABLE object used for name locking or 0 in - case of failure. -*/ - -TABLE *table_cache_insert_placeholder(THD *thd, const char *key, - uint key_length) -{ - TABLE *table; - TABLE_SHARE *share; - char *key_buff; - DBUG_ENTER("table_cache_insert_placeholder"); - - safe_mutex_assert_owner(&LOCK_open); - - /* - Create a table entry with the right key and with an old refresh version - Note that we must use my_multi_malloc() here as this is freed by the - table cache - */ - if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), - &table, sizeof(*table), - &share, sizeof(*share), - &key_buff, key_length, - NULL)) - DBUG_RETURN(NULL); - - table->s= share; - share->set_table_cache_key(key_buff, key, key_length); - share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table - table->in_use= thd; - table->locked_by_name=1; - - if (my_hash_insert(&open_cache, (uchar*)table)) - { - my_free((uchar*) table, MYF(0)); - DBUG_RETURN(NULL); - } - - DBUG_RETURN(table); -} - - -/** - Obtain an exclusive name lock on the table if it is not cached - in the table cache. - - @param thd Thread context - @param db Name of database - @param table_name Name of table - @param[out] table Out parameter which is either: - - set to NULL if table cache contains record for - the table or - - set to point to the TABLE instance used for - name-locking. - - @note This function takes into account all records for table in table - cache, even placeholders used for name-locking. This means that - 'table' parameter can be set to NULL for some situations when - table does not really exist. - - @retval TRUE Error occured (OOM) - @retval FALSE Success. 'table' parameter set according to above rules. -*/ - -bool lock_table_name_if_not_cached(THD *thd, const char *db, - const char *table_name, TABLE **table) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length; - DBUG_ENTER("lock_table_name_if_not_cached"); - - key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1; - pthread_mutex_lock(&LOCK_open); - - if (my_hash_search(&open_cache, (uchar *)key, key_length)) - { - pthread_mutex_unlock(&LOCK_open); - DBUG_PRINT("info", ("Table is cached, name-lock is not obtained")); - *table= 0; - DBUG_RETURN(FALSE); - } - if (!(*table= table_cache_insert_placeholder(thd, key, key_length))) - { - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(TRUE); - } - (*table)->open_placeholder= 1; - (*table)->next= thd->open_tables; - thd->open_tables= *table; - pthread_mutex_unlock(&LOCK_open); + table_list->table= table; DBUG_RETURN(FALSE); } @@ -2511,6 +2562,20 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists) } +/** + @brief Helper function used by MDL subsystem for releasing TABLE_SHARE + objects in cases when it no longer wants to cache reference to it. +*/ + +void table_share_release_hook(void *share) +{ + pthread_mutex_lock(&LOCK_open); + release_table_share((TABLE_SHARE*)share, RELEASE_NORMAL); + broadcast_refresh(); + pthread_mutex_unlock(&LOCK_open); +} + + /* Open a table. @@ -2518,13 +2583,17 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists) open_table() thd Thread context. table_list Open first table in list. - refresh INOUT Pointer to memory that will be set to 1 if - we need to close all tables and reopen them. + action INOUT Pointer to variable of enum_open_table_action type + which will be set according to action which is + required to remedy problem appeared during attempt + to open table. If this is a NULL pointer, then the table is not put in the thread-open-list. flags Bitmap of flags to modify how open works: MYSQL_LOCK_IGNORE_FLUSH - Open table even if - someone has done a flush or namelock on it. + someone has done a flush or there is a pending + exclusive metadata lock requests against it + (i.e. request high priority metadata lock). No version number checking is done. MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary table not the base table or view. @@ -2545,21 +2614,23 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists) TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, - bool *refresh, uint flags) + enum_open_table_action *action, uint flags) { reg1 TABLE *table; char key[MAX_DBKEY_LENGTH]; uint key_length; char *alias= table_list->alias; - HASH_SEARCH_STATE state; + MDL_LOCK *mdl_lock; + int error; + TABLE_SHARE *share; DBUG_ENTER("open_table"); /* Parsing of partitioning information from .frm needs thd->lex set up. */ DBUG_ASSERT(thd->lex->is_lex_started); /* find a unused table in the open table cache */ - if (refresh) - *refresh=0; + if (action) + *action= OT_NO_ACTION; /* an open table operation needs a lot of the stack space */ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias)) @@ -2685,7 +2756,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, Is this table a view and not a base table? (it is work around to allow to open view with locked tables, real fix will be made after definition cache will be made) + + Since opening of view which was not explicitly locked by LOCK + TABLES breaks metadata locking protocol (potentially can lead + to deadlocks) it should be disallowed. */ + if (mdl_is_lock_owner(&thd->mdl_context, 0, table_list->db, + table_list->table_name)) { char path[FN_REFLEN + 1]; enum legacy_db_type not_used; @@ -2693,21 +2770,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table_list->db, table_list->table_name, reg_ext, 0); if (mysql_frm_type(thd, path, ¬_used) == FRMTYPE_VIEW) { - /* - Will not be used (because it's VIEW) but has to be passed. - Also we will not free it (because it is a stack variable). - */ - TABLE tab; - table= &tab; - pthread_mutex_lock(&LOCK_open); - if (!open_unireg_entry(thd, table, table_list, alias, - key, key_length, mem_root, 0)) + if (!tdc_open_view(thd, table_list, alias, key, key_length, + mem_root, 0)) { DBUG_ASSERT(table_list->view != 0); - pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); // VIEW } - pthread_mutex_unlock(&LOCK_open); } } /* @@ -2740,6 +2808,32 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, on disk. */ + mdl_lock= table_list->mdl_lock; + mdl_add_lock(&thd->mdl_context, mdl_lock); + + if (table_list->open_table_type) + { + mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + /* TODO: This case can be significantly optimized. */ + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + DBUG_RETURN(0); + } + else + { + bool retry; + + if (table_list->mdl_upgradable) + mdl_set_upgradable(mdl_lock); + mdl_set_lock_priority(mdl_lock, (flags & MYSQL_LOCK_IGNORE_FLUSH) ? + MDL_HIGH_PRIO : MDL_NORMAL_PRIO); + if (mdl_acquire_shared_lock(mdl_lock, &retry)) + { + if (action && retry) + *action= OT_BACK_OFF_AND_RETRY; + DBUG_RETURN(0); + } + } + pthread_mutex_lock(&LOCK_open); /* @@ -2756,222 +2850,210 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ! (flags & MYSQL_LOCK_IGNORE_FLUSH)) { /* Someone did a refresh while thread was opening tables */ - if (refresh) - *refresh=1; + if (action) + *action= OT_BACK_OFF_AND_RETRY; pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } - /* - In order for the back off and re-start process to work properly, - handler tables having old versions (due to FLUSH TABLES or pending - name-lock) MUST be closed. This is specially important if a name-lock - is pending for any table of the handler_tables list, otherwise a - deadlock may occur. - */ - if (thd->handler_tables) - mysql_ha_flush(thd); - - /* - Actually try to find the table in the open_cache. - The cache may contain several "TABLE" instances for the same - physical table. The instances that are currently "in use" by - some thread have their "in_use" member != NULL. - There is no good reason for having more than one entry in the - hash for the same physical table, except that we use this as - an implicit "pending locks queue" - see - wait_for_locked_table_names for details. - */ - for (table= (TABLE*) my_hash_first(&open_cache, (uchar*) key, key_length, - &state); - table && table->in_use ; - table= (TABLE*) my_hash_next(&open_cache, (uchar*) key, key_length, - &state)) + if (table_list->open_table_type == TABLE_LIST::OPEN_OR_CREATE) { - DBUG_PRINT("tcache", ("in_use table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); - /* - Here we flush tables marked for flush. - Normally, table->s->version contains the value of - refresh_version from the moment when this table was - (re-)opened and added to the cache. - If since then we did (or just started) FLUSH TABLES - statement, refresh_version has been increased. - For "name-locked" TABLE instances, table->s->version is set - to 0 (see lock_table_name for details). - In case there is a pending FLUSH TABLES or a name lock, we - need to back off and re-start opening tables. - If we do not back off now, we may dead lock in case of lock - order mismatch with some other thread: - c1: name lock t1; -- sort of exclusive lock - c2: open t2; -- sort of shared lock - c1: name lock t2; -- blocks - c2: open t1; -- blocks - */ - if (table->needs_reopen_or_name_lock()) + bool exists; + + if (check_if_table_exists(thd, table_list, &exists)) + goto err_unlock2; + + if (!exists) { - DBUG_PRINT("note", - ("Found table '%s.%s' with different refresh version", - table_list->db, table_list->table_name)); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); + } + /* Table exists. Let us try to open it. */ + } + else if (table_list->open_table_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL) + { + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); + } - if (flags & MYSQL_LOCK_IGNORE_FLUSH) - { - /* Force close at once after usage */ - thd->version= table->s->version; - continue; - } - - /* Avoid self-deadlocks by detecting self-dependencies. */ - if (table->open_placeholder && table->in_use == thd) - { - pthread_mutex_unlock(&LOCK_open); - my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name.str); - DBUG_RETURN(0); - } + if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock))) + { + if (!(share= get_table_share_with_create(thd, table_list, key, + key_length, OPEN_VIEW, + &error))) + goto err_unlock2; + if (share->is_view) + { /* - Back off, part 1: mark the table as "unused" for the - purpose of name-locking by setting table->db_stat to 0. Do - that only for the tables in this thread that have an old - table->s->version (this is an optimization (?)). - table->db_stat == 0 signals wait_for_locked_table_names - that the tables in question are not used any more. See - table_is_used call for details. + This table is a view. Validate its metadata version: in particular, + that it was a view when the statement was prepared. + */ + if (check_and_update_table_version(thd, table_list, share)) + goto err_unlock; + if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) + goto err_unlock; - Notice that HANDLER tables were already taken care of by - the earlier call to mysql_ha_flush() in this same critical - section. - */ - close_old_data_files(thd,thd->open_tables,0,0); - /* - Back-off part 2: try to avoid "busy waiting" on the table: - if the table is in use by some other thread, we suspend - and wait till the operation is complete: when any - operation that juggles with table->s->version completes, - it broadcasts COND_refresh condition variable. - If 'old' table we met is in use by current thread we return - without waiting since in this situation it's this thread - which is responsible for broadcasting on COND_refresh - (and this was done already in close_old_data_files()). - Good example of such situation is when we have statement - that needs two instances of table and FLUSH TABLES comes - after we open first instance but before we open second - instance. - */ - if (table->in_use != thd) + /* Open view */ + if (open_new_frm(thd, share, alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | + HA_GET_INDEX | HA_TRY_READ_ONLY), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | + (flags & OPEN_VIEW_NO_PARSE), thd->open_options, + 0, table_list, mem_root)) + goto err_unlock; + + /* TODO: Don't free this */ + release_table_share(share, RELEASE_NORMAL); + + if (flags & OPEN_VIEW_NO_PARSE) { - /* wait_for_conditionwill unlock LOCK_open for us */ - wait_for_condition(thd, &LOCK_open, &COND_refresh); + /* + VIEW not really opened, only frm were read. + Set 1 as a flag here + */ + table_list->view= (LEX*)1; } else { - pthread_mutex_unlock(&LOCK_open); + DBUG_ASSERT(table_list->view); } - /* - There is a refresh in progress for this table. - Signal the caller that it has to try again. - */ - if (refresh) - *refresh=1; + + pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } - } - if (table) - { - DBUG_PRINT("tcache", ("unused table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); - /* Unlink the table from "unused_tables" list. */ - if (table == unused_tables) - { // First unused - unused_tables=unused_tables->next; // Remove from link - if (table == unused_tables) - unused_tables=0; + else if (table_list->view) + { + /* + We're trying to open a table for what was a view. + This can only happen during (re-)execution. + At prepared statement prepare the view has been opened and + merged into the statement parse tree. After that, someone + performed a DDL and replaced the view with a base table. + Don't try to open the table inside a prepared statement, + invalidate it instead. + + Note, the assert below is known to fail inside stored + procedures (Bug#27011). + */ + DBUG_ASSERT(thd->m_reprepare_observer); + check_and_update_table_version(thd, table_list, share); + /* Always an error. */ + DBUG_ASSERT(thd->is_error()); + goto err_unlock; } - table->prev->next=table->next; /* Remove from unused list */ - table->next->prev=table->prev; - table->in_use= thd; + + if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) + goto err_unlock; + + /* + We are going to to store extra reference to the share in MDL-subsystem + so we need to increase reference counter; + */ + reference_table_share(share); + mdl_set_cached_object(mdl_lock, share, table_share_release_hook); } else { - /* Insert a new TABLE instance into the open cache */ - int error; - DBUG_PRINT("tcache", ("opening new table")); - /* Free cache if too big */ - while (open_cache.records > table_cache_size && unused_tables) - my_hash_delete(&open_cache,(uchar*) unused_tables); /* purecov: tested */ - - if (table_list->create) + if (table_list->view) { - bool exists; - - if (check_if_table_exists(thd, table_list, &exists)) - { - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(NULL); - } - - if (!exists) - { - /* - Table to be created, so we need to create placeholder in table-cache. - */ - if (!(table= table_cache_insert_placeholder(thd, key, key_length))) - { - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(NULL); - } - /* - Link placeholder to the open tables list so it will be automatically - removed once tables are closed. Also mark it so it won't be ignored - by other trying to take name-lock. - */ - table->open_placeholder= 1; - table->next= thd->open_tables; - thd->open_tables= table; - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(table); - } - /* Table exists. Let us try to open it. */ + DBUG_ASSERT(thd->m_reprepare_observer); + check_and_update_table_version(thd, table_list, share); + /* Always an error. */ + DBUG_ASSERT(thd->is_error()); + goto err_unlock; } + /* When we have cached TABLE_SHARE we know that is not a view. */ + if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) + goto err_unlock; + + /* + We are going to use this share for construction of new TABLE object + so reference counter should be increased. + */ + reference_table_share(share); + } + + if (share->version != refresh_version) + { + if (!(flags & MYSQL_LOCK_IGNORE_FLUSH)) + { + if (action) + *action= OT_BACK_OFF_AND_RETRY; + release_table_share(share, RELEASE_NORMAL); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); + } + /* Force close at once after usage */ + thd->version= share->version; + } + + if (!share->free_tables.is_empty()) + { + table= share->free_tables.head(); + table_def_use_table(thd, table); + /* We need to release share as we have EXTRA reference to it in our hands. */ + release_table_share(share, RELEASE_NORMAL); + } + else + { + /* We have too many TABLE instances around let us try to get rid of them. */ + while (table_cache_count > table_cache_size && unused_tables) + free_cache_entry(unused_tables); /* make a new table */ if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME)))) + goto err_unlock; + + error= open_table_from_share(thd, share, alias, + (uint) (HA_OPEN_KEYFILE | + HA_OPEN_RNDFILE | + HA_GET_INDEX | + HA_TRY_READ_ONLY), + (READ_KEYINFO | COMPUTE_TYPES | + EXTRA_RECORD), + thd->open_options, table, FALSE); + + if (error) { - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(NULL); + my_free(table, MYF(0)); + + if (action) + { + if (error == 7) + { + share->version= 0; + *action= OT_DISCOVER; + } + else if (share->crashed) + { + share->version= 0; + *action= OT_REPAIR; + } + } + + goto err_unlock; } - error= open_unireg_entry(thd, table, table_list, alias, key, key_length, - mem_root, (flags & OPEN_VIEW_NO_PARSE)); - if (error > 0) + if (open_table_entry_fini(thd, share, table)) { + closefrm(table, 0); my_free((uchar*)table, MYF(0)); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(NULL); + goto err_unlock; } - if (table_list->view || error < 0) - { - /* - VIEW not really opened, only frm were read. - Set 1 as a flag here - */ - if (error < 0) - table_list->view= (LEX*)1; - my_free((uchar*)table, MYF(0)); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); // VIEW - } - DBUG_PRINT("info", ("inserting table '%s'.'%s' 0x%lx into the cache", - table->s->db.str, table->s->table_name.str, - (long) table)); - (void) my_hash_insert(&open_cache,(uchar*) table); + /* Add table to the share's used tables list. */ + table_def_add_used_table(thd, table); } - check_unused(); // Debugging call - pthread_mutex_unlock(&LOCK_open); - if (refresh) + + // Table existed + if (table_list->open_table_type == TABLE_LIST::OPEN_OR_CREATE) + mdl_downgrade_exclusive_locks(&thd->mdl_context); + + table->mdl_lock= mdl_lock; + if (action) { table->next=thd->open_tables; /* Link into simple list */ thd->open_tables=table; @@ -3013,15 +3095,32 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->clear_column_bitmaps(); DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); + +err_unlock: + release_table_share(share, RELEASE_NORMAL); +err_unlock2: + pthread_mutex_unlock(&LOCK_open); + mdl_release_lock(&thd->mdl_context, mdl_lock); + DBUG_RETURN(0); } -TABLE *find_locked_table(THD *thd, const char *db,const char *table_name) +/** + Find table in the list of open tables. + + @param list List of TABLE objects to be inspected. + @param db Database name + @param table_name Table name + + @return Pointer to the TABLE object found, 0 if no table found. +*/ + +TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; uint key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; - for (TABLE *table=thd->open_tables; table ; table=table->next) + for (TABLE *table= list; table ; table=table->next) { if (table->s->table_cache_key.length == key_length && !memcmp(table->s->table_cache_key.str, key, key_length)) @@ -3031,6 +3130,41 @@ TABLE *find_locked_table(THD *thd, const char *db,const char *table_name) } +/** + Find write locked instance of table in the list of open tables, + emit error if no such instance found. + + @param thd List of TABLE objects to be searched + @param db Database name. + @param table_name Name of table. + + @return Pointer to write-locked TABLE instance, 0 - otherwise. +*/ + +TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_name) +{ + TABLE *tab= find_locked_table(list, db, table_name); + + if (!tab) + { + my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_name); + return 0; + } + else + { + while (tab->reginfo.lock_type < TL_WRITE_LOW_PRIORITY && + (tab= find_locked_table(tab->next, db, table_name))) + continue; + if (!tab) + { + my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name); + return 0; + } + } + return tab; +} + + /* Reopen an table because the definition has changed. @@ -3073,14 +3207,10 @@ bool reopen_table(TABLE *table) table_list.table_name= table->s->table_name.str; table_list.table= table; - if (wait_for_locked_table_names(thd, &table_list)) - DBUG_RETURN(1); // Thread was killed - - if (open_unireg_entry(thd, &tmp, &table_list, - table->alias, - table->s->table_cache_key.str, - table->s->table_cache_key.length, - thd->mem_root, 0)) + if (reopen_table_entry(thd, &tmp, &table_list, + table->alias, + table->s->table_cache_key.str, + table->s->table_cache_key.length)) goto end; /* This list copies variables set by open_table */ @@ -3112,6 +3242,12 @@ bool reopen_table(TABLE *table) (void) closefrm(&tmp, 1); // close file, free everything goto end; } + tmp.mdl_lock= table->mdl_lock; + + table_def_change_share(table, tmp.s); + /* Avoid wiping out TABLE's position in new share's used tables list. */ + tmp.share_next= table->share_next; + tmp.share_prev= table->share_prev; delete table->triggers; if (table->file) @@ -3214,6 +3350,7 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, mysql_lock_remove(thd, thd->locked_tables, table, TRUE); } table->open_placeholder= 1; + table->s->version= 0; close_handle_and_leave_table_as_lock(table); } } @@ -3282,8 +3419,6 @@ static bool reattach_merge(THD *thd, TABLE **err_tables_p) @param thd Thread context @param get_locks Should we get locks after reopening tables ? - @param mark_share_as_old Mark share as old to protect from a impending - global read lock. @note Since this function can't properly handle prelocking and create placeholders it should be used in very special @@ -3297,11 +3432,11 @@ static bool reattach_merge(THD *thd, TABLE **err_tables_p) @return FALSE in case of success, TRUE - otherwise. */ -bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) +bool reopen_tables(THD *thd, bool get_locks) { TABLE *table,*next,**prev; TABLE **tables,**tables_ptr; // For locks - TABLE *err_tables= NULL; + TABLE *err_tables= NULL, *err_tab_tmp; bool error=0, not_used; bool merge_table_found= FALSE; const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN | @@ -3356,7 +3491,7 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) */ if (table->child_l || table->parent) detach_merge_children(table, TRUE); - my_hash_delete(&open_cache,(uchar*) table); + free_cache_entry(table); error=1; } else @@ -3367,11 +3502,15 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) prev= &table->next; /* Do not handle locks of MERGE children. */ if (get_locks && !db_stat && !table->parent) - *tables_ptr++= table; // need new lock on this - if (mark_share_as_old) { - table->s->version=0; - table->open_placeholder= 0; + *tables_ptr++= table; // need new lock on this + /* + We rely on having exclusive metadata lock on the table to be + able safely re-acquire table locks on it. + */ + DBUG_ASSERT(mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, + table->s->db.str, + table->s->table_name.str)); } } } @@ -3385,8 +3524,9 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) { while (err_tables) { - my_hash_delete(&open_cache, (uchar*) err_tables); - err_tables= err_tables->next; + err_tab_tmp= err_tables->next; + free_cache_entry(err_tables); + err_tables= err_tab_tmp; } } DBUG_PRINT("tcache", ("open tables to lock: %u", @@ -3534,41 +3674,24 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) { char *key= table->s->table_cache_key.str; uint key_length= table->s->table_cache_key.length; - - DBUG_PRINT("loop", ("table_name: %s", table->alias)); - HASH_SEARCH_STATE state; - for (TABLE *search= (TABLE*) my_hash_first(&open_cache, (uchar*) key, - key_length, &state); - search ; - search= (TABLE*) my_hash_next(&open_cache, (uchar*) key, - key_length, &state)) - { - DBUG_PRINT("info", ("share: 0x%lx " - "open_placeholder: %d locked_by_name: %d " - "db_stat: %u version: %lu", - (ulong) search->s, - search->open_placeholder, search->locked_by_name, - search->db_stat, - search->s->version)); - if (search->in_use == table->in_use) - continue; // Name locked by this thread - /* - We can't use the table under any of the following conditions: - - There is an name lock on it (Table is to be deleted or altered) - - If we are in flush table and we didn't execute the flush - - If the table engine is open and it's an old version - (We must wait until all engines are shut down to use the table) - */ - if ( (search->locked_by_name && wait_for_name_lock) || - (search->is_name_opened() && search->needs_reopen_or_name_lock())) - DBUG_RETURN(1); - } + /* Note that 'table' can use artificial TABLE_SHARE object. */ + TABLE_SHARE *share= (TABLE_SHARE*)my_hash_search(&table_def_cache, + (uchar*) key, key_length); + if (share && !share->used_tables.is_empty() && + share->version != refresh_version) + DBUG_RETURN(1); } while ((table=table->next)); DBUG_RETURN(0); } -/* Wait until all used tables are refreshed */ +/* + Wait until all used tables are refreshed. + + FIXME We should remove this function since for several functions which + are invoked by it new scenarios of usage are introduced, while + this function implements optimization useful only in rare cases. +*/ bool wait_for_tables(THD *thd) { @@ -3593,7 +3716,7 @@ bool wait_for_tables(THD *thd) /* Now we can open all tables without any interference */ thd_proc_info(thd, "Reopen tables"); thd->version= refresh_version; - result=reopen_tables(thd,0,0); + result=reopen_tables(thd, 0); } pthread_mutex_unlock(&LOCK_open); thd_proc_info(thd, 0); @@ -3601,111 +3724,27 @@ bool wait_for_tables(THD *thd) } -/* - drop tables from locked list +/** + Unlock and close tables open and locked by LOCK TABLES statement. - SYNOPSIS - drop_locked_tables() - thd Thread thandler - db Database - table_name Table name - - INFORMATION - This is only called on drop tables - - The TABLE object for the dropped table is unlocked but still kept around - as a name lock, which means that the table will be available for other - thread as soon as we call unlock_table_names(). - If there is multiple copies of the table locked, all copies except - the first, which acts as a name lock, is removed. - - RETURN - # If table existed, return table - 0 Table was not locked + @param thd Current thread's context. */ - -TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name) +void unlock_locked_tables(THD *thd) { - TABLE *table,*next,**prev, *found= 0; - prev= &thd->open_tables; - DBUG_ENTER("drop_locked_tables"); + DBUG_ASSERT(!thd->in_sub_stmt && + !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); - /* - Note that we need to hold LOCK_open while changing the - open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) - Closing a MERGE child before the parent would be fatal if the - other thread tries to abort the MERGE lock in between. - */ - for (table= thd->open_tables; table ; table=next) + if (thd->locked_tables) { - next=table->next; - if (!strcmp(table->s->table_name.str, table_name) && - !strcmp(table->s->db.str, db)) - { - /* If MERGE child, forward lock handling to parent. */ - mysql_lock_remove(thd, thd->locked_tables, - table->parent ? table->parent : table, TRUE); - /* - When closing a MERGE parent or child table, detach the children first. - Clear child table references in case this object is opened again. - */ - if (table->child_l || table->parent) - detach_merge_children(table, TRUE); - - if (!found) - { - found= table; - /* Close engine table, but keep object around as a name lock */ - if (table->db_stat) - { - table->db_stat= 0; - table->file->close(); - } - } - else - { - /* We already have a name lock, remove copy */ - my_hash_delete(&open_cache,(uchar*) table); - } - } - else - { - *prev=table; - prev= &table->next; - } - } - *prev=0; - if (found) - broadcast_refresh(); - if (thd->locked_tables && thd->locked_tables->table_count == 0) - { - my_free((uchar*) thd->locked_tables,MYF(0)); + thd->lock= thd->locked_tables; thd->locked_tables=0; - } - DBUG_RETURN(found); -} - - -/* - If we have the table open, which only happens when a LOCK TABLE has been - done on the table, change the lock type to a lock that will abort all - other threads trying to get the lock. -*/ - -void abort_locked_tables(THD *thd,const char *db, const char *table_name) -{ - TABLE *table; - for (table= thd->open_tables; table ; table= table->next) - { - if (!strcmp(table->s->table_name.str, table_name) && - !strcmp(table->s->db.str, db)) - { - /* If MERGE child, forward lock handling to parent. */ - mysql_lock_abort(thd, table->parent ? table->parent : table, TRUE); - break; - } + close_thread_tables(thd); + /* + After closing tables we can free memory used for storing lock + request objects for metadata locks + */ + free_root(&thd->locked_tables_root, MYF(MY_MARK_BLOCKS_FREE)); } } @@ -3812,7 +3851,7 @@ static bool inject_reprepare(THD *thd) @retval FALSE success, version in TABLE_LIST has been updated */ -bool +static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables, TABLE_SHARE *table_share) { @@ -3838,41 +3877,98 @@ check_and_update_table_version(THD *thd, return FALSE; } -/* - Load a table definition from file and open unireg table - SYNOPSIS - open_unireg_entry() - thd Thread handle - entry Store open table definition here - table_list TABLE_LIST with db, table_name & belong_to_view - alias Alias name - cache_key Key for share_cache - cache_key_length length of cache_key - mem_root temporary mem_root for parsing - flags the OPEN_VIEW_NO_PARSE flag to be passed to - openfrm()/open_new_frm() +/** + Open view by getting its definition from disk (and table cache in future). - NOTES - Extra argument for open is taken from thd->open_options - One must have a lock on LOCK_open when calling this function + @param thd Thread handle + @param table_list TABLE_LIST with db, table_name & belong_to_view + @param alias Alias name + @param cache_key Key for table definition cache + @param cache_key_length Length of cache_key + @param mem_root Memory to be used for .frm parsing. + @param flags Flags which modify how we open the view - RETURN - 0 ok - # Error + @todo This function is needed for special handling of views under + LOCK TABLES. We probably should get rid of it in long term. + + @return FALSE if success, TRUE - otherwise. */ -static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, - const char *alias, - char *cache_key, uint cache_key_length, - MEM_ROOT *mem_root, uint flags) +bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, + char *cache_key, uint cache_key_length, + MEM_ROOT *mem_root, uint flags) +{ + TABLE not_used; + int error; + TABLE_SHARE *share; + + pthread_mutex_lock(&LOCK_open); + + if (!(share= get_table_share_with_create(thd, table_list, cache_key, + cache_key_length, + OPEN_VIEW, &error))) + goto err; + + if (share->is_view && + !open_new_frm(thd, share, alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | + HA_GET_INDEX | HA_TRY_READ_ONLY), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | + flags, thd->open_options, ¬_used, table_list, + mem_root)) + { + release_table_share(share, RELEASE_NORMAL); + pthread_mutex_unlock(&LOCK_open); + return FALSE; + } + + my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "VIEW"); + release_table_share(share, RELEASE_NORMAL); +err: + pthread_mutex_unlock(&LOCK_open); + return TRUE; +} + + +/** + Load table definition from file and open table while holding exclusive + meta-data lock on it. + + @param thd Thread handle + @param entry Memory for TABLE object to be created + @param table_list TABLE_LIST with db, table_name & belong_to_view + @param alias Alias name + @param cache_key Key for table definition cache + @param cache_key_length Length of cache_key + + @note This auxiliary function is mostly inteded for re-opening table + in situations when we hold exclusive meta-data lock. It is not + intended for normal case in which we have only shared meta-data + lock on the table to be open. + + @note Extra argument for open is taken from thd->open_options. + + @note One must have a lock on LOCK_open as well as exclusive meta-data + lock on the table when calling this function. + + @return FALSE in case of success, TRUE otherwise. +*/ + +static bool reopen_table_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, + const char *alias, char *cache_key, + uint cache_key_length) { int error; TABLE_SHARE *share; uint discover_retry_count= 0; - DBUG_ENTER("open_unireg_entry"); + DBUG_ENTER("reopen_table_entry"); safe_mutex_assert_owner(&LOCK_open); + DBUG_ASSERT(mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, + table_list->db, + table_list->table_name)); + retry: if (!(share= get_table_share_with_create(thd, table_list, cache_key, cache_key_length, @@ -3901,40 +3997,11 @@ retry: goto err; if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) goto err; - - /* Open view */ - error= (int) open_new_frm(thd, share, alias, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX | HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | - (flags & OPEN_VIEW_NO_PARSE), - thd->open_options, entry, table_list, - mem_root); - if (error) - goto err; - /* TODO: Don't free this */ + /* Attempt to reopen view will bring havoc to upper layers anyway. */ release_table_share(share, RELEASE_NORMAL); - DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0); - } - else if (table_list->view) - { - /* - We're trying to open a table for what was a view. - This can only happen during (re-)execution. - At prepared statement prepare the view has been opened and - merged into the statement parse tree. After that, someone - performed a DDL and replaced the view with a base table. - Don't try to open the table inside a prepared statement, - invalidate it instead. - - Note, the assert below is known to fail inside stored - procedures (Bug#27011). - */ - DBUG_ASSERT(thd->m_reprepare_observer); - check_and_update_table_version(thd, table_list, share); - /* Always an error. */ - DBUG_ASSERT(thd->is_error()); - goto err; + my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, + "BASE TABLE"); + DBUG_RETURN(1); } if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) @@ -3956,89 +4023,67 @@ retry: goto err; /* - TODO: - Here we should wait until all threads has released the table. - For now we do one retry. This may cause a deadlock if there - is other threads waiting for other tables used by this thread. - - Proper fix would be to if the second retry failed: - - Mark that table def changed - - Return from open table - - Close all tables used by this thread - - Start waiting that the share is released - - Retry by opening all tables again - */ - if (ha_create_table_from_engine(thd, table_list->db, - table_list->table_name)) - goto err; - /* - TO BE FIXED - To avoid deadlock, only wait for release if no one else is - using the share. + Since we have exclusive metadata lock on the table here the only + practical case when share->ref_count != 1 is when we have several + instances of the table opened by this thread (i.e we are under LOCK + TABLES). */ if (share->ref_count != 1) goto err; - /* Free share and wait until it's released by all threads */ - release_table_share(share, RELEASE_WAIT_FOR_DROP); - if (!thd->killed) - { - thd->warning_info->clear_warning_info(thd->query_id); - thd->clear_error(); // Clear error message - goto retry; - } - DBUG_RETURN(1); + + release_table_share(share, RELEASE_NORMAL); + + if (ha_create_table_from_engine(thd, table_list->db, + table_list->table_name)) + goto err; + + thd->warning_info->clear_warning_info(thd->query_id); + thd->clear_error(); // Clear error message + goto retry; } if (!entry->s || !entry->s->crashed) goto err; - // Code below is for repairing a crashed file - if ((error= lock_table_name(thd, table_list, TRUE))) - { - if (error < 0) - goto err; - if (wait_for_locked_table_names(thd, table_list)) - { - unlock_table_name(thd, table_list); - goto err; - } - } - pthread_mutex_unlock(&LOCK_open); - thd->clear_error(); // Clear error message - error= 0; - if (open_table_from_share(thd, share, alias, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX | - HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, - ha_open_options | HA_OPEN_FOR_REPAIR, - entry, FALSE) || ! entry->file || - (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd))) - { - /* Give right error message */ - thd->clear_error(); - my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str, my_errno); - sql_print_error("Couldn't repair table: %s.%s", share->db.str, - share->table_name.str); - if (entry->file) - closefrm(entry, 0); - error=1; - } - else - thd->clear_error(); // Clear error message - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - - if (error) - goto err; - break; - } - if (Table_triggers_list::check_n_load(thd, share->db.str, - share->table_name.str, entry, 0)) + entry->s->version= 0; + + /* TODO: We don't need to release share here. */ + release_table_share(share, RELEASE_NORMAL); + pthread_mutex_unlock(&LOCK_open); + error= (int)auto_repair_table(thd, table_list); + pthread_mutex_lock(&LOCK_open); + + if (error) + goto err; + + goto retry; + } + + if (open_table_entry_fini(thd, share, entry)) { closefrm(entry, 0); goto err; } + DBUG_RETURN(0); + +err: + release_table_share(share, RELEASE_NORMAL); + DBUG_RETURN(1); +} + + +/** + Auxiliary routine which finalizes process of TABLE object creation + by loading triggers and handling implicitly emptied tables. +*/ + +static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry) +{ + + if (Table_triggers_list::check_n_load(thd, share->db.str, + share->table_name.str, entry, 0)) + return TRUE; + /* If we are here, there was no fatal error (but error may be still unitialized). @@ -4070,18 +4115,141 @@ retry: */ sql_print_error("When opening HEAP table, could not allocate memory " "to write 'DELETE FROM `%s`.`%s`' to the binary log", - table_list->db, table_list->table_name); + share->db.str, share->table_name.str); delete entry->triggers; - closefrm(entry, 0); - goto err; + return TRUE; } } } - DBUG_RETURN(0); + return FALSE; +} -err: + +/** + Auxiliary routine which is used for performing automatical table repair. +*/ + +static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) +{ + char cache_key[MAX_DBKEY_LENGTH]; + uint cache_key_length; + TABLE_SHARE *share; + TABLE *entry; + int not_used; + bool result= FALSE; + + cache_key_length= create_table_def_key(thd, cache_key, table_list, 0); + + thd->clear_error(); + + pthread_mutex_lock(&LOCK_open); + + if (!(share= get_table_share_with_create(thd, table_list, cache_key, + cache_key_length, + OPEN_VIEW, ¬_used))) + { + pthread_mutex_unlock(&LOCK_open); + return TRUE; + } + + if (share->is_view) + goto end_with_lock_open; + + if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME)))) + { + result= TRUE; + goto end_with_lock_open; + } + share->version= 0; + pthread_mutex_unlock(&LOCK_open); + + if (open_table_from_share(thd, share, table_list->alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | + HA_GET_INDEX | + HA_TRY_READ_ONLY), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + ha_open_options | HA_OPEN_FOR_REPAIR, + entry, FALSE) || ! entry->file || + (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd))) + { + /* Give right error message */ + thd->clear_error(); + my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str, my_errno); + sql_print_error("Couldn't repair table: %s.%s", share->db.str, + share->table_name.str); + if (entry->file) + closefrm(entry, 0); + result= TRUE; + } + else + { + thd->clear_error(); // Clear error message + closefrm(entry, 0); + } + my_free(entry, MYF(0)); + + pthread_mutex_lock(&LOCK_open); + +end_with_lock_open: release_table_share(share, RELEASE_NORMAL); - DBUG_RETURN(1); + pthread_mutex_unlock(&LOCK_open); + return result; +} + + +/** + Handle failed attempt ot open table by performing requested action. + + @param thd Thread context + @param table Table list element for table that caused problem + @param action Type of action requested by failed open_table() call + + @retval FALSE - Success. One should try to open tables once again. + @retval TRUE - Error +*/ + +static bool handle_failed_open_table_attempt(THD *thd, TABLE_LIST *table, + enum_open_table_action action) +{ + bool result= FALSE; + + switch (action) + { + case OT_BACK_OFF_AND_RETRY: + result= (mdl_wait_for_locks(&thd->mdl_context) || + tdc_wait_for_old_versions(thd, &thd->mdl_context)); + mdl_remove_all_locks(&thd->mdl_context); + break; + case OT_DISCOVER: + mdl_set_lock_type(table->mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, table->mdl_lock); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + return TRUE; + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(0, table->db, table->table_name); + ha_create_table_from_engine(thd, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); + + thd->warning_info->clear_warning_info(thd->query_id); + thd->clear_error(); // Clear error message + mdl_release_exclusive_locks(&thd->mdl_context); + break; + case OT_REPAIR: + mdl_set_lock_type(table->mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, table->mdl_lock); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + return TRUE; + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(0, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); + + result= auto_repair_table(thd, table); + mdl_release_exclusive_locks(&thd->mdl_context); + break; + default: + DBUG_ASSERT(0); + } + return result; } @@ -4132,6 +4300,7 @@ static int add_merge_table_list(TABLE_LIST *tlist) /* Set lock type. */ child_l->lock_type= tlist->lock_type; + child_l->mdl_upgradable= tlist->mdl_upgradable; /* Set parent reference. */ child_l->parent_l= tlist; @@ -4487,7 +4656,7 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) { TABLE_LIST *tables= NULL; - bool refresh; + enum_open_table_action action; int result=0; MEM_ROOT new_frm_mem; /* Also used for indicating that prelocking is need */ @@ -4507,6 +4676,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) query_tables_last_own= 0; thd_proc_info(thd, "Opening tables"); + /* + Close HANDLER tables which are marked for flush or against which there + are pending exclusive metadata locks. Note that we do this not to avoid + deadlocks (calls to mysql_ha_flush() in mdl_wait_for_locks() and + tdc_wait_for_old_version() are enough for this) but in order to have + a point during statement execution at which such HANDLERs are closed + even if they don't create problems for current thread (i.e. to avoid + having DDL blocked by HANDLERs opened for long time). + */ + if (thd->handler_tables) + mysql_ha_flush(thd); + /* If we are not already executing prelocked statement and don't have statement for which table list for prelocking is already built, let @@ -4556,9 +4737,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ if (tables->derived) { - if (tables->view) - goto process_view_routines; - continue; + if (!tables->view) + continue; + /* + We restore view's name and database wiped out by derived tables + processing and fall back to standard open process in order to + obtain proper metadata locks and do other necessary steps like + stored routine processing. + */ + tables->db= tables->view_db.str; + tables->db_length= tables->view_db.length; + tables->table_name= tables->view_name.str; + tables->table_name_length= tables->view_name.length; } /* If this TABLE_LIST object is a placeholder for an information_schema @@ -4602,12 +4792,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ Prelock_error_handler prelock_handler; thd->push_internal_handler(& prelock_handler); - tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); + tables->table= open_table(thd, tables, &new_frm_mem, &action, flags); thd->pop_internal_handler(); safe_to_ignore_table= prelock_handler.safely_trapped_errors(); } else - tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); + tables->table= open_table(thd, tables, &new_frm_mem, &action, flags); } else DBUG_PRINT("tcache", ("referenced table: '%s'.'%s' 0x%lx", @@ -4657,7 +4847,16 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) parent_l->next_global= *parent_l->table->child_last_l; } - if (refresh) // Refresh in progress + /* + FIXME This is a temporary hack. Actually we need check that will + allow us to differentiate between error while opening/creating + table and successful table creation. + ... + */ + if (tables->open_table_type) + continue; + + if (action) { /* We have met name-locked or old version of table. Now we have @@ -4675,7 +4874,17 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ if (query_tables_last_own) thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - close_tables_for_reopen(thd, start); + close_tables_for_reopen(thd, start, (action == OT_BACK_OFF_AND_RETRY)); + /* + Here we rely on the fact that 'tables' still points to the valid + TABLE_LIST element. Altough currently this assumption is valid + it may change in future. + */ + if (handle_failed_open_table_attempt(thd, tables, action)) + { + result= -1; + goto err; + } goto restart; } @@ -4929,6 +5138,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, uint lock_flags) { TABLE *table; + enum_open_table_action action; bool refresh; DBUG_ENTER("open_ltable"); @@ -4939,9 +5149,20 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, thd->current_tablenr= 0; /* open_ltable can be used only for BASIC TABLEs */ table_list->required_type= FRMTYPE_TABLE; - while (!(table= open_table(thd, table_list, thd->mem_root, &refresh, 0)) && - refresh) - ; + +retry: + while (!(table= open_table(thd, table_list, thd->mem_root, &action, 0)) && + action) + { + /* + Even altough we have failed to open table we still need to + call close_thread_tables() to release metadata locks which + might have been acquired successfully. + */ + close_thread_tables(thd, (action == OT_BACK_OFF_AND_RETRY)); + if (handle_failed_open_table_attempt(thd, table_list, action)) + break; + } if (table) { @@ -4969,8 +5190,22 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, DBUG_ASSERT(thd->lock == 0); // You must lock everything at once if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, - lock_flags, &refresh))) - table= 0; + (lock_flags | + MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN), + &refresh))) + { + /* + FIXME: Actually we should get rid of MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN option + as all reopening should happen outside of mysql_lock_tables() code. + */ + if (refresh) + { + close_thread_tables(thd); + goto retry; + } + else + table= 0; + } } } @@ -5026,7 +5261,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) break; if (!need_reopen) DBUG_RETURN(-1); - close_tables_for_reopen(thd, &tables); + close_tables_for_reopen(thd, &tables, FALSE); } if (derived && (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -5383,6 +5618,10 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) table->table->query_id= thd->query_id; if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) { + /* + This was an attempt to enter prelocked mode so there is no + need to care about THD::locked_tables_root here. + */ mysql_unlock_tables(thd, thd->locked_tables); thd->locked_tables= 0; thd->options&= ~(OPTION_TABLE_LOCK); @@ -5469,7 +5708,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) */ -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl) { /* If table list consists only from tables from prelocking set, table list @@ -5481,7 +5720,7 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) sp_remove_not_own_routines(thd->lex); for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) tmp->table= 0; - close_thread_tables(thd); + close_thread_tables(thd, skip_mdl); } @@ -8392,36 +8631,6 @@ my_bool mysql_rm_tmp_tables(void) unireg support functions *****************************************************************************/ -/* - Invalidate any cache entries that are for some DB - - SYNOPSIS - remove_db_from_cache() - db Database name. This will be in lower case if - lower_case_table_name is set - - NOTE: - We can't use hash_delete when looping hash_elements. We mark them first - and afterwards delete those marked unused. -*/ - -void remove_db_from_cache(const char *db) -{ - for (uint idx=0 ; idx < open_cache.records ; idx++) - { - TABLE *table=(TABLE*) my_hash_element(&open_cache,idx); - if (!strcmp(table->s->db.str, db)) - { - table->s->version= 0L; /* Free when thread is ready */ - if (!table->in_use) - relink_unused(table); - } - } - while (unused_tables && !unused_tables->s->version) - my_hash_delete(&open_cache,(uchar*) unused_tables); -} - - /* free all unused tables @@ -8434,7 +8643,7 @@ void flush_tables() { (void) pthread_mutex_lock(&LOCK_open); while (unused_tables) - my_hash_delete(&open_cache,(uchar*) unused_tables); + free_cache_entry(unused_tables); (void) pthread_mutex_unlock(&LOCK_open); } @@ -8468,90 +8677,82 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; for (;;) { - HASH_SEARCH_STATE state; result= signalled= 0; - for (table= (TABLE*) my_hash_first(&open_cache, (uchar*) key, key_length, - &state); - table; - table= (TABLE*) my_hash_next(&open_cache, (uchar*) key, key_length, - &state)) - { - THD *in_use; - DBUG_PRINT("tcache", ("found table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); - - table->s->version=0L; /* Free when thread is ready */ - if (!(in_use=table->in_use)) - { - DBUG_PRINT("info",("Table was not in use")); - relink_unused(table); - } - else if (in_use != thd) - { - DBUG_PRINT("info", ("Table was in use by other thread")); - /* - Mark that table is going to be deleted from cache. This will - force threads that are in mysql_lock_tables() (but not yet - in thr_multi_lock()) to abort it's locks, close all tables and retry - */ - in_use->some_tables_deleted= 1; - if (table->is_name_opened()) - { - DBUG_PRINT("info", ("Found another active instance of the table")); - result=1; - } - /* Kill delayed insert threads */ - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - ! in_use->killed) - { - in_use->killed= THD::KILL_CONNECTION; - pthread_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - pthread_mutex_lock(in_use->mysys_var->current_mutex); - signalled= 1; - pthread_cond_broadcast(in_use->mysys_var->current_cond); - pthread_mutex_unlock(in_use->mysys_var->current_mutex); - } - pthread_mutex_unlock(&in_use->mysys_var->mutex); - } - /* - Now we must abort all tables locks used by this thread - as the thread may be waiting to get a lock for another table. - Note that we need to hold LOCK_open while going through the - list. So that the other thread cannot change it. The other - thread must also hold LOCK_open whenever changing the - open_tables list. Aborting the MERGE lock after a child was - closed and before the parent is closed would be fatal. - */ - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* Do not handle locks of MERGE children. */ - if (thd_table->db_stat && !thd_table->parent) // If table is open - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - } - } - else - { - DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u", - table->db_stat)); - result= result || (flags & RTFC_OWNED_BY_THD_FLAG); - } - } - while (unused_tables && !unused_tables->s->version) - my_hash_delete(&open_cache,(uchar*) unused_tables); - - DBUG_PRINT("info", ("Removing table from table_def_cache")); - /* Remove table from table definition cache if it's not in use */ - if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, + if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key, key_length))) { + I_P_List_iterator it(share->free_tables); + share->version= 0; + while ((table= it++)) + relink_unused(table); + + it.init(share->used_tables); + while ((table= it++)) + { + THD *in_use= table->in_use; + DBUG_ASSERT(in_use); + if (in_use != thd) + { + DBUG_PRINT("info", ("Table was in use by other thread")); + /* + Mark that table is going to be deleted from cache. This will + force threads that are in mysql_lock_tables() (but not yet + in thr_multi_lock()) to abort it's locks, close all tables and retry + */ + in_use->some_tables_deleted= 1; + + if (table->is_name_opened()) + { + DBUG_PRINT("info", ("Found another active instance of the table")); + result=1; + } + /* Kill delayed insert threads */ + if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + ! in_use->killed) + { + in_use->killed= THD::KILL_CONNECTION; + pthread_mutex_lock(&in_use->mysys_var->mutex); + if (in_use->mysys_var->current_cond) + { + pthread_mutex_lock(in_use->mysys_var->current_mutex); + signalled= 1; + pthread_cond_broadcast(in_use->mysys_var->current_cond); + pthread_mutex_unlock(in_use->mysys_var->current_mutex); + } + pthread_mutex_unlock(&in_use->mysys_var->mutex); + } + /* + Now we must abort all tables locks used by this thread + as the thread may be waiting to get a lock for another table. + Note that we need to hold LOCK_open while going through the + list. So that the other thread cannot change it. The other + thread must also hold LOCK_open whenever changing the + open_tables list. Aborting the MERGE lock after a child was + closed and before the parent is closed would be fatal. + */ + for (TABLE *thd_table= in_use->open_tables; + thd_table ; + thd_table= thd_table->next) + { + /* Do not handle locks of MERGE children. */ + if (thd_table->db_stat && !thd_table->parent) // If table is open + signalled|= mysql_lock_abort_for_thread(thd, thd_table); + } + } + else + { + DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u", + table->db_stat)); + result= result || (flags & RTFC_OWNED_BY_THD_FLAG); + } + } + + while (unused_tables && !unused_tables->s->version) + free_cache_entry(unused_tables); + DBUG_PRINT("info", ("share version: %lu ref_count: %u", share->version, share->ref_count)); - share->version= 0; // Mark for delete if (share->ref_count == 0) { pthread_mutex_lock(&share->mutex); @@ -8598,6 +8799,160 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, } +/** + A callback to the server internals that is used to address + special cases of the locking protocol. + Invoked when acquiring an exclusive lock, for each thread that + has a conflicting shared metadata lock. + + This function: + - aborts waiting of the thread on a data lock, to make it notice + the pending exclusive lock and back off. + - if the thread is an INSERT DELAYED thread, sends it a KILL + signal to terminate it. + + @note This function does not wait for the thread to give away its + locks. Waiting is done outside for all threads at once. + + @param thd Current thread context + @param in_use The thread to wake up + + @retval TRUE if the thread was woken up + @retval FALSE otherwise (e.g. it was not waiting for a table-level lock). + + @note It is one of two places where border between MDL and the + rest of the server is broken. +*/ + +bool notify_thread_having_shared_lock(THD *thd, THD *in_use) +{ + bool signalled= FALSE; + if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + !in_use->killed) + { + in_use->killed= THD::KILL_CONNECTION; + pthread_mutex_lock(&in_use->mysys_var->mutex); + if (in_use->mysys_var->current_cond) + pthread_cond_broadcast(in_use->mysys_var->current_cond); + pthread_mutex_unlock(&in_use->mysys_var->mutex); + signalled= TRUE; + } + pthread_mutex_lock(&LOCK_open); + for (TABLE *thd_table= in_use->open_tables; + thd_table ; + thd_table= thd_table->next) + { + /* TODO With new MDL check for db_stat is probably a legacy */ + if (thd_table->db_stat) + signalled|= mysql_lock_abort_for_thread(thd, thd_table); + } + pthread_mutex_unlock(&LOCK_open); + return signalled; +} + + +/** + Remove all instances of the table from cache assuming that current thread + has exclusive meta-data lock on it (optionally leave instances belonging + to the current thread in cache). + + @param leave_thd 0 If we should remove all instances + non-0 Pointer to current thread context if we should + leave instances belonging to this thread. + @param db Name of database + @param table_name Name of table + + @note Unlike remove_table_from_cache() it assumes that table instances + are already not used by any (other) thread (this should be achieved + by using meta-data locks). +*/ + +void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name) +{ + char key[MAX_DBKEY_LENGTH]; + uint key_length; + TABLE *table; + TABLE_SHARE *share; + + key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; + + if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, + key_length))) + { + I_P_List_iterator it(share->free_tables); + share->version= 0; + + while ((table= it++)) + relink_unused(table); + } + + /* This may destroy share so we have to do new look-up later. */ + while (unused_tables && !unused_tables->s->version) + free_cache_entry(unused_tables); + + if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, + key_length))) + { + DBUG_ASSERT(leave_thd || share->ref_count == 0); + if (share->ref_count == 0) + { + pthread_mutex_lock(&share->mutex); + my_hash_delete(&table_def_cache, (uchar*) share); + } + } +} + + +/** + Wait until there are no old versions of tables in the table + definition cache for the metadata locks that we try to acquire. + + @param thd Thread context + @param context Metadata locking context with locks. +*/ + +static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) +{ + MDL_LOCK *l; + TABLE_SHARE *share; + const char *old_msg; + LEX_STRING key; + + while (!thd->killed) + { + /* + Here we have situation as in mdl_wait_for_locks() we need to + get rid of offending HANDLERs to avoid deadlock. + TODO: We should also investigate in which situations we have + to broadcast on COND_refresh because of this. + */ + mysql_ha_flush(thd); + pthread_mutex_lock(&LOCK_open); + + I_P_List_iterator it= mdl_get_locks(context); + while ((l= it++)) + { + mdl_get_tdc_key(l, &key); + if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key.str, + key.length)) && + share->version != refresh_version && + !share->used_tables.is_empty()) + break; + } + if (!l) + { + pthread_mutex_unlock(&LOCK_open); + break; + } + old_msg= thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting for table"); + pthread_cond_wait(&COND_refresh, &LOCK_open); + /* LOCK_open mutex is unlocked by THD::exit_cond() as side-effect. */ + thd->exit_cond(old_msg); + } + return thd->killed; +} + + int setup_ftfuncs(SELECT_LEX *select_lex) { List_iterator li(*(select_lex->ftfunc_list)), @@ -8695,7 +9050,6 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, } err: - bzero(outparam, sizeof(TABLE)); // do not run repair DBUG_RETURN(1); } @@ -8728,15 +9082,23 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b) int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) { - uint flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG; DBUG_ENTER("abort_and_upgrade_locks"); lpt->old_lock_type= lpt->table->reginfo.lock_type; - pthread_mutex_lock(&LOCK_open); /* If MERGE child, forward lock handling to parent. */ mysql_lock_abort(lpt->thd, lpt->table->parent ? lpt->table->parent : lpt->table, TRUE); - (void) remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags); + if (mdl_upgrade_shared_lock_to_exclusive(&lpt->thd->mdl_context, 0, + lpt->db, lpt->table_name)) + { + mysql_lock_downgrade_write(lpt->thd, + lpt->table->parent ? lpt->table->parent : + lpt->table, + lpt->old_lock_type); + DBUG_RETURN(1); + } + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(lpt->thd, lpt->db, lpt->table_name); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -8771,106 +9133,12 @@ void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt) /* + Tells if two (or more) tables have auto_increment columns and we want to + lock those tables with a write lock. + SYNOPSIS - mysql_wait_completed_table() - lpt Parameter passing struct - my_table My table object - All parameters passed through the ALTER_PARTITION_PARAM object - RETURN VALUES - TRUE Failure - FALSE Success - DESCRIPTION - We have changed the frm file and now we want to wait for all users of - the old frm to complete before proceeding to ensure that no one - remains that uses the old frm definition. - Start by ensuring that all users of the table will be removed from cache - once they are done. Then abort all that have stumbled on locks and - haven't been started yet. - - thd Thread object - table Table object - db Database name - table_name Table name -*/ - -void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length; - TABLE *table; - DBUG_ENTER("mysql_wait_completed_table"); - - key_length=(uint) (strmov(strmov(key,lpt->db)+1,lpt->table_name)-key)+1; - pthread_mutex_lock(&LOCK_open); - HASH_SEARCH_STATE state; - for (table= (TABLE*) my_hash_first(&open_cache,(uchar*) key,key_length, - &state) ; - table; - table= (TABLE*) my_hash_next(&open_cache,(uchar*) key,key_length, - &state)) - { - THD *in_use= table->in_use; - table->s->version= 0L; - if (!in_use) - { - relink_unused(table); - } - else - { - /* Kill delayed insert threads */ - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - ! in_use->killed) - { - in_use->killed= THD::KILL_CONNECTION; - pthread_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - pthread_mutex_lock(in_use->mysys_var->current_mutex); - pthread_cond_broadcast(in_use->mysys_var->current_cond); - pthread_mutex_unlock(in_use->mysys_var->current_mutex); - } - pthread_mutex_unlock(&in_use->mysys_var->mutex); - } - /* - Now we must abort all tables locks used by this thread - as the thread may be waiting to get a lock for another table. - Note that we need to hold LOCK_open while going through the - list. So that the other thread cannot change it. The other - thread must also hold LOCK_open whenever changing the - open_tables list. Aborting the MERGE lock after a child was - closed and before the parent is closed would be fatal. - */ - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* Do not handle locks of MERGE children. */ - if (thd_table->db_stat && !thd_table->parent) // If table is open - mysql_lock_abort_for_thread(lpt->thd, thd_table); - } - } - } - /* - We start by removing all unused objects from the cache and marking - those in use for removal after completion. Now we also need to abort - all that are locked and are not progressing due to being locked - by our lock. We don't upgrade our lock here. - If MERGE child, forward lock handling to parent. - */ - mysql_lock_abort(lpt->thd, my_table->parent ? my_table->parent : my_table, - FALSE); - pthread_mutex_unlock(&LOCK_open); - DBUG_VOID_RETURN; -} - - -/* - Check if one (or more) write tables have auto_increment columns. - - @param[in] tables Table list - - @retval 0 if at least one write tables has an auto_increment column - @retval 1 otherwise + has_two_write_locked_tables_with_auto_increment + tables Table list NOTES: Call this function only when you have established the list of all tables @@ -8924,10 +9192,13 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, { DBUG_ENTER("open_system_tables_for_read"); + alloc_mdl_locks(table_list, thd->mem_root); + thd->reset_n_backup_open_tables_state(backup); uint count= 0; - bool not_used; + enum_open_table_action not_used; + bool not_used_2; for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) { TABLE *table= open_table(thd, tables, thd->mem_root, ¬_used, @@ -8950,7 +9221,7 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, *(ptr++)= tables->table; thd->lock= mysql_lock_tables(thd, list, count, - MYSQL_LOCK_IGNORE_FLUSH, ¬_used); + MYSQL_LOCK_IGNORE_FLUSH, ¬_used_2); } if (thd->lock) DBUG_RETURN(FALSE); @@ -9002,6 +9273,8 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table) { DBUG_ENTER("open_system_table_for_update"); + alloc_mdl_locks(one_table, thd->mem_root); + TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0); if (table) { @@ -9038,6 +9311,7 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table, thd->reset_n_backup_open_tables_state(backup); + alloc_mdl_locks(one_table, thd->mem_root); if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) { DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE); @@ -9116,6 +9390,9 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) pthread_mutex_unlock(&LOCK_open); + mdl_release_locks(&thd->mdl_context); + mdl_remove_all_locks(&thd->mdl_context); + thd->restore_backup_open_tables_state(backup); } diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 58c309ef57b..31d4430cbe6 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -241,7 +241,7 @@ void mysql_client_binlog_statement(THD* thd) my_ok(thd); end: - rli->clear_tables_to_lock(); + rli->slave_close_thread_tables(thd); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); DBUG_VOID_RETURN; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b7d88eca89a..9445f092546 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -202,10 +202,10 @@ bool foreign_key_prefix(Key *a, Key *b) ** Thread specific functions ****************************************************************************/ -Open_tables_state::Open_tables_state(ulong version_arg) +Open_tables_state::Open_tables_state(THD *thd, ulong version_arg) :version(version_arg), state_flags(0U) { - reset_open_tables_state(); + reset_open_tables_state(thd); } /* @@ -440,7 +440,7 @@ bool Drop_table_error_handler::handle_condition(THD *thd, THD::THD() :Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION, /* statement id */ 0), - Open_tables_state(refresh_version), rli_fake(0), + Open_tables_state(this, refresh_version), rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), sql_log_bin_toplevel(false), @@ -468,7 +468,8 @@ THD::THD() #if defined(ENABLED_DEBUG_SYNC) debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ - main_warning_info(0) + main_warning_info(0), + mdl_el_root(NULL) { ulong tmp; @@ -573,6 +574,8 @@ THD::THD() thr_lock_owner_init(&main_lock_id, &lock_info); m_internal_handler= NULL; + + init_sql_alloc(&locked_tables_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); } @@ -1051,6 +1054,9 @@ THD::~THD() if (!cleanup_done) cleanup(); + mdl_context_destroy(&mdl_context); + mdl_context_destroy(&handler_mdl_context); + ha_close_connection(this); plugin_thdvar_cleanup(this); @@ -1072,6 +1078,7 @@ THD::~THD() #endif free_root(&main_mem_root, MYF(0)); + free_root(&locked_tables_root, MYF(0)); DBUG_VOID_RETURN; } @@ -3014,7 +3021,7 @@ void THD::reset_n_backup_open_tables_state(Open_tables_state *backup) { DBUG_ENTER("reset_n_backup_open_tables_state"); backup->set_open_tables_state(this); - reset_open_tables_state(); + reset_open_tables_state(this); state_flags|= Open_tables_state::BACKUPS_AVAIL; DBUG_VOID_RETURN; } @@ -3032,6 +3039,9 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) lock == 0 && locked_tables == 0 && prelocked_mode == NON_PRELOCKED && m_reprepare_observer == NULL); + mdl_context_destroy(&mdl_context); + mdl_context_destroy(&handler_mdl_context); + set_open_tables_state(backup); DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 03a92c1a685..0f7d9d9a8d5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -25,6 +25,7 @@ #include "log.h" #include "rpl_tblmap.h" +#include "mdl.h" class Reprepare_observer; @@ -976,26 +977,31 @@ public: */ uint state_flags; + MDL_CONTEXT mdl_context; + MDL_CONTEXT handler_mdl_context; + /* This constructor serves for creation of Open_tables_state instances which are used as backup storage. */ Open_tables_state() : state_flags(0U) { } - Open_tables_state(ulong version_arg); + Open_tables_state(THD *thd, ulong version_arg); void set_open_tables_state(Open_tables_state *state) { *this= *state; } - void reset_open_tables_state() + void reset_open_tables_state(THD *thd) { open_tables= temporary_tables= handler_tables= derived_tables= 0; extra_lock= lock= locked_tables= 0; prelocked_mode= NON_PRELOCKED; state_flags= 0U; m_reprepare_observer= NULL; + mdl_context_init(&mdl_context, thd); + mdl_context_init(&handler_mdl_context, thd); } }; @@ -1809,6 +1815,9 @@ public: struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ + MEM_ROOT *mdl_el_root; + MEM_ROOT locked_tables_root; + THD(); ~THD(); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 17626f05aa1..44909880da0 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -904,10 +904,6 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } else { - pthread_mutex_lock(&LOCK_open); - remove_db_from_cache(db); - pthread_mutex_unlock(&LOCK_open); - Drop_table_error_handler err_handler(thd->get_internal_handler()); thd->push_internal_handler(&err_handler); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d8aa27c9695..fb48f32660b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1089,12 +1089,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; + MDL_LOCK *mdl_lock= 0; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); /* Remove tables from the HANDLER's hash. */ - mysql_ha_rm_tables(thd, table_list, FALSE); + mysql_ha_rm_tables(thd, table_list); /* If it is a temporary table, close and regenerate it */ if (!dont_send_ok && (table= find_temporary_table(thd, table_list))) @@ -1158,8 +1159,20 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - if (lock_and_wait_for_table_name(thd, table_list)) + /* + FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since + tries to get table enging and therefore accesses table in some way + without holding any kind of meta-data lock. + */ + mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(0, table_list->db, table_list->table_name); + pthread_mutex_unlock(&LOCK_open); } // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this @@ -1184,15 +1197,13 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); + if (mdl_lock) + mdl_release_lock(&thd->mdl_context, mdl_lock); } else if (error) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); + if (mdl_lock) + mdl_release_lock(&thd->mdl_context, mdl_lock); } DBUG_RETURN(error); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index da5ee93fcb9..c8a66073a67 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -116,17 +116,16 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) @param thd Thread identifier. @param tables A list of tables with the first entry to close. - @param is_locked If LOCK_open is locked. @note Though this function takes a list of tables, only the first list entry will be closed. @note Broadcasts refresh if it closed a table with old version. */ -static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables, - bool is_locked) +static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) { TABLE **table_ptr; + MDL_LOCK *mdl_lock; /* Though we could take the table pointer from hash_tables->table, @@ -142,15 +141,15 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables, if (*table_ptr) { (*table_ptr)->file->ha_index_or_rnd_end(); - if (! is_locked) - pthread_mutex_lock(&LOCK_open); + mdl_lock= (*table_ptr)->mdl_lock; + pthread_mutex_lock(&LOCK_open); if (close_thread_table(thd, table_ptr)) { /* Tell threads waiting for refresh that something has happened */ broadcast_refresh(); } - if (! is_locked) - pthread_mutex_unlock(&LOCK_open); + pthread_mutex_unlock(&LOCK_open); + mdl_release_lock(&thd->handler_mdl_context, mdl_lock); } else if (tables->table) { @@ -190,10 +189,12 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables, bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { TABLE_LIST *hash_tables = NULL; - char *db, *name, *alias; + MDL_LOCK *mdl_lock; + char *db, *name, *alias, *mdlkey; uint dblen, namelen, aliaslen, counter; int error; TABLE *backup_open_tables; + MDL_CONTEXT backup_mdl_context; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -216,7 +217,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) HANDLER_TABLES_HASH_SIZE, 0, 0, (my_hash_get_key) mysql_ha_hash_get_key, (my_hash_free_key) mysql_ha_hash_free, 0)) - goto err; + { + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(TRUE); + } } else if (! reopen) /* Otherwise we have 'tables' already. */ { @@ -224,11 +228,52 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) strlen(tables->alias) + 1)) { DBUG_PRINT("info",("duplicate '%s'", tables->alias)); + DBUG_PRINT("exit",("ERROR")); my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias); - goto err; + DBUG_RETURN(TRUE); } } + if (! reopen) + { + /* copy the TABLE_LIST struct */ + dblen= strlen(tables->db) + 1; + namelen= strlen(tables->table_name) + 1; + aliaslen= strlen(tables->alias) + 1; + if (!(my_multi_malloc(MYF(MY_WME), + &hash_tables, (uint) sizeof(*hash_tables), + &db, (uint) dblen, + &name, (uint) namelen, + &alias, (uint) aliaslen, + &mdl_lock, sizeof(MDL_LOCK), + &mdlkey, MAX_DBKEY_LENGTH, + NullS))) + { + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(TRUE); + } + /* structure copy */ + *hash_tables= *tables; + hash_tables->db= db; + hash_tables->table_name= name; + hash_tables->alias= alias; + memcpy(hash_tables->db, tables->db, dblen); + memcpy(hash_tables->table_name, tables->table_name, namelen); + memcpy(hash_tables->alias, tables->alias, aliaslen); + mdl_init_lock(mdl_lock, mdlkey, 0, db, name); + hash_tables->mdl_lock= mdl_lock; + + /* add to hash */ + if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) + { + my_free((char*) hash_tables, MYF(0)); + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(TRUE); + } + } + else + hash_tables= tables; + /* Save and reset the open_tables list so that open_tables() won't be able to access (or know about) the previous list. And on return @@ -243,21 +288,22 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) */ backup_open_tables= thd->open_tables; thd->open_tables= NULL; + mdl_context_backup_and_reset(&thd->mdl_context, &backup_mdl_context); /* - open_tables() will set 'tables->table' if successful. + open_tables() will set 'hash_tables->table' if successful. It must be NULL for a real open when calling open_tables(). */ - DBUG_ASSERT(! tables->table); + DBUG_ASSERT(! hash_tables->table); /* for now HANDLER can be used only for real TABLES */ - tables->required_type= FRMTYPE_TABLE; + hash_tables->required_type= FRMTYPE_TABLE; /* We use open_tables() here, rather than, say, open_ltable() or open_table() because we would like to be able to open a temporary table. */ - error= open_tables(thd, &tables, &counter, 0); + error= open_tables(thd, &hash_tables, &counter, 0); if (thd->open_tables) { if (thd->open_tables->next) @@ -281,52 +327,26 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) thd->handler_tables= thd->open_tables; } } + mdl_context_merge(&thd->handler_mdl_context, &thd->mdl_context); - /* Restore the state. */ thd->open_tables= backup_open_tables; + mdl_context_restore(&thd->mdl_context, &backup_mdl_context); if (error) goto err; /* There can be only one table in '*tables'. */ - if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) + if (! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) { my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); goto err; } - if (! reopen) - { - /* copy the TABLE_LIST struct */ - dblen= strlen(tables->db) + 1; - namelen= strlen(tables->table_name) + 1; - aliaslen= strlen(tables->alias) + 1; - if (!(my_multi_malloc(MYF(MY_WME), - &hash_tables, (uint) sizeof(*hash_tables), - &db, (uint) dblen, - &name, (uint) namelen, - &alias, (uint) aliaslen, - NullS))) - goto err; - /* structure copy */ - *hash_tables= *tables; - hash_tables->db= db; - hash_tables->table_name= name; - hash_tables->alias= alias; - memcpy(hash_tables->db, tables->db, dblen); - memcpy(hash_tables->table_name, tables->table_name, namelen); - memcpy(hash_tables->alias, tables->alias, aliaslen); - - /* add to hash */ - if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) - goto err; - } - /* If it's a temp table, don't reset table->query_id as the table is being used by this handler. Otherwise, no meaning at all. */ - tables->table->open_by_handler= 1; + hash_tables->table->open_by_handler= 1; if (! reopen) my_ok(thd); @@ -334,10 +354,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) DBUG_RETURN(FALSE); err: - if (hash_tables) - my_free((char*) hash_tables, MYF(0)); - if (tables->table) - mysql_ha_close_table(thd, tables, FALSE); + if (hash_tables->table) + mysql_ha_close_table(thd, hash_tables); + if (!reopen) + my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); DBUG_PRINT("exit",("ERROR")); DBUG_RETURN(TRUE); } @@ -371,7 +391,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) (uchar*) tables->alias, strlen(tables->alias) + 1))) { - mysql_ha_close_table(thd, hash_tables, FALSE); + mysql_ha_close_table(thd, hash_tables); my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); } else @@ -507,7 +527,7 @@ retry: if (need_reopen) { - mysql_ha_close_table(thd, hash_tables, FALSE); + mysql_ha_close_table(thd, hash_tables); /* The lock might have been aborted, we need to manually reset thd->some_tables_deleted because handler's tables are closed @@ -734,12 +754,11 @@ static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables) @param thd Thread identifier. @param tables The list of tables to remove. - @param is_locked If LOCK_open is locked. @note Broadcasts refresh if it closed a table with old version. */ -void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked) +void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables) { TABLE_LIST *hash_tables, *next; DBUG_ENTER("mysql_ha_rm_tables"); @@ -752,7 +771,7 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked) { next= hash_tables->next_local; if (hash_tables->table) - mysql_ha_close_table(thd, hash_tables, is_locked); + mysql_ha_close_table(thd, hash_tables); my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); hash_tables= next; } @@ -775,13 +794,16 @@ void mysql_ha_flush(THD *thd) TABLE_LIST *hash_tables; DBUG_ENTER("mysql_ha_flush"); - safe_mutex_assert_owner(&LOCK_open); + safe_mutex_assert_not_owner(&LOCK_open); for (uint i= 0; i < thd->handler_tables_hash.records; i++) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); - if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock()) - mysql_ha_close_table(thd, hash_tables, TRUE); + if (hash_tables->table && + (hash_tables->table->mdl_lock && + mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock) || + hash_tables->table->needs_reopen_or_name_lock())) + mysql_ha_close_table(thd, hash_tables); } DBUG_VOID_RETURN; @@ -805,7 +827,7 @@ void mysql_ha_cleanup(THD *thd) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); if (hash_tables->table) - mysql_ha_close_table(thd, hash_tables, FALSE); + mysql_ha_close_table(thd, hash_tables); } my_hash_free(&thd->handler_tables_hash); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index efdc8caa3e5..eb4eee9abb5 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -612,7 +612,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, upgrade the lock here instead? */ if (table_list->lock_type == TL_WRITE_DELAYED && thd->locked_tables && - find_locked_table(thd, table_list->db, table_list->table_name)) + find_locked_table(thd->open_tables, table_list->db, + table_list->table_name)) { my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0), table_list->table_name); @@ -2351,6 +2352,8 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->lex->set_stmt_unsafe(); thd->set_current_stmt_binlog_row_based_if_mixed(); + alloc_mdl_locks(&di->table_list, thd->mem_root); + /* Open table */ if (!(di->table= open_n_lock_single_table(thd, &di->table_list, TL_WRITE_DELAYED))) @@ -3495,7 +3498,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, create_table, FALSE)) + if (reopen_name_locked_table(thd, create_table)) { quick_rm_table(create_info->db_type, create_table->db, table_case_name(create_info, create_table->table_name), @@ -3507,7 +3510,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } else { - if (!(table= open_table(thd, create_table, thd->mem_root, (bool*) 0, + if (!(table= open_table(thd, create_table, thd->mem_root, + (enum_open_table_action*) 0, MYSQL_OPEN_TEMPORARY_ONLY)) && !create_info->table_existed) { @@ -3621,8 +3625,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000);); - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && - (create_table->table && create_table->table->db_stat)) + if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && create_table->table) { /* Table already exists and was open at open_and_lock_tables() stage. */ if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1271c3112ff..2b2c736fd9e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -47,6 +47,7 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); +static void adjust_mdl_locks_upgradability(TABLE_LIST *tables); const char *any_db="*any*"; // Special symbol for check_access @@ -143,17 +144,6 @@ static bool xa_trans_rollback(THD *thd) return status; } -static void unlock_locked_tables(THD *thd) -{ - if (thd->locked_tables) - { - thd->lock=thd->locked_tables; - thd->locked_tables=0; // Will be automatically closed - close_thread_tables(thd); // Free tables - } -} - - bool end_active_trans(THD *thd) { int error=0; @@ -194,12 +184,9 @@ bool begin_trans(THD *thd) my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); return 1; } - if (thd->locked_tables) - { - thd->lock=thd->locked_tables; - thd->locked_tables=0; // Will be automatically closed - close_thread_tables(thd); // Free tables - } + + unlock_locked_tables(thd); + if (end_active_trans(thd)) error= -1; else @@ -1342,6 +1329,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, select_lex.table_list.link_in_list((uchar*) &table_list, (uchar**) &table_list.next_local); thd->lex->add_to_query_tables(&table_list); + alloc_mdl_locks(&table_list, thd->mem_root); /* switch on VIEW optimisation: do not fill temporary tables */ thd->lex->sql_command= SQLCOM_SHOW_FIELDS; @@ -2643,7 +2631,7 @@ case SQLCOM_PREPARE: if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); - create_table->create= TRUE; + create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE; } if (!(res= open_and_lock_tables(thd, lex->query_tables))) @@ -3618,6 +3606,9 @@ end_with_restore_list: goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; + alloc_mdl_locks(all_tables, &thd->locked_tables_root); + thd->mdl_el_root= &thd->locked_tables_root; + adjust_mdl_locks_upgradability(all_tables); if (!(res= simple_open_n_lock_tables(thd, all_tables))) { @@ -3641,6 +3632,7 @@ end_with_restore_list: thd->options&= ~(OPTION_TABLE_LOCK); } thd->in_lock_tables=0; + thd->mdl_el_root= 0; break; case SQLCOM_CREATE_DB: { @@ -6542,6 +6534,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); + ptr->mdl_lock= mdl_alloc_lock(0 , ptr->db, ptr->table_name, + thd->mdl_el_root ? thd->mdl_el_root : + thd->mem_root); DBUG_RETURN(ptr); } @@ -7079,23 +7074,15 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if ((options & REFRESH_READ_LOCK) && thd) { /* - We must not try to aspire a global read lock if we have a write - locked table. This would lead to a deadlock when trying to - reopen (and re-lock) the table after the flush. + On the first hand we need write lock on the tables to be flushed, + on the other hand we must not try to aspire a global read lock + if we have a write locked table as this would lead to a deadlock + when trying to reopen (and re-lock) the table after the flush. */ if (thd->locked_tables) { - THR_LOCK_DATA **lock_p= thd->locked_tables->locks; - THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count; - - for (; lock_p < end_p; lock_p++) - { - if ((*lock_p)->type >= TL_WRITE_ALLOW_WRITE) - { - my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); - return 1; - } - } + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + return 1; } /* Writing to the binlog could cause deadlocks, as we don't log @@ -7105,7 +7092,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (lock_global_read_lock(thd)) return 1; // Killed if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ? - FALSE : TRUE, TRUE)) + FALSE : TRUE)) result= 1; if (make_global_read_lock_block_commit(thd)) // Killed @@ -7117,8 +7104,35 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } else { + if (thd && thd->locked_tables) + { + /* + If we are under LOCK TABLES we should have a write + lock on tables which we are going to flush. + */ + if (tables) + { + for (TABLE_LIST *t= tables; t; t= t->next_local) + if (!find_write_locked_table(thd->open_tables, t->db, + t->table_name)) + return 1; + } + else + { + for (TABLE *tab= thd->open_tables; tab; tab= tab->next) + { + if (tab->reginfo.lock_type < TL_WRITE_ALLOW_WRITE) + { + my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), + tab->s->table_name.str); + return 1; + } + } + } + } + if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ? - FALSE : TRUE, FALSE)) + FALSE : TRUE)) result= 1; } my_dbopt_cleanup(); @@ -8092,6 +8106,42 @@ bool parse_sql(THD *thd, return ret_value; } + +/** + Auxiliary function which marks metadata locks for all tables + on which we plan to take write lock as upgradable. +*/ + +static void adjust_mdl_locks_upgradability(TABLE_LIST *tables) +{ + TABLE_LIST *tab, *otab; + + for (tab= tables; tab; tab= tab->next_global) + { + if (tab->lock_type >= TL_WRITE_ALLOW_WRITE) + tab->mdl_upgradable= TRUE; + else + { + /* + TODO: To get rid of this loop we need to change our code to do + metadata lock upgrade only for those instances of tables + which are write locked instead of doing such upgrade for + all instances of tables. + */ + for (otab= tables; otab; otab= otab->next_global) + if (otab->lock_type >= TL_WRITE_ALLOW_WRITE && + otab->db_length == tab->db_length && + otab->table_name_length == tab->table_name_length && + !strcmp(otab->db, tab->db) && + !strcmp(otab->table_name, tab->table_name)) + { + tab->mdl_upgradable= TRUE; + break; + } + } + } +} + /** @} (end of group Runtime_Environment) */ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 868dfc3e968..48f33bf3295 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6225,7 +6225,7 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) */ pthread_mutex_lock(&LOCK_open); lpt->thd->in_lock_tables= 1; - err= reopen_tables(lpt->thd, 1, 1); + err= reopen_tables(lpt->thd, 1); lpt->thd->in_lock_tables= 0; if (err) { @@ -6564,7 +6564,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, write_log_drop_partition(lpt) || ERROR_INJECT_CRASH("crash_drop_partition_3") || (not_completed= FALSE) || - abort_and_upgrade_lock(lpt) || /* Always returns 0 */ + abort_and_upgrade_lock(lpt) || ERROR_INJECT_CRASH("crash_drop_partition_4") || alter_close_tables(lpt) || ERROR_INJECT_CRASH("crash_drop_partition_5") || @@ -6631,7 +6631,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_add_partition_2") || mysql_change_partitions(lpt) || ERROR_INJECT_CRASH("crash_add_partition_3") || - abort_and_upgrade_lock(lpt) || /* Always returns 0 */ + abort_and_upgrade_lock(lpt) || ERROR_INJECT_CRASH("crash_add_partition_4") || alter_close_tables(lpt) || ERROR_INJECT_CRASH("crash_add_partition_5") || @@ -6647,7 +6647,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_add_partition_8") || (write_log_completed(lpt, FALSE), FALSE) || ERROR_INJECT_CRASH("crash_add_partition_9") || - (alter_partition_lock_handling(lpt), FALSE)) + (alter_partition_lock_handling(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, FALSE, frm_install); goto err; @@ -6721,7 +6721,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, write_log_final_change_partition(lpt) || ERROR_INJECT_CRASH("crash_change_partition_4") || (not_completed= FALSE) || - abort_and_upgrade_lock(lpt) || /* Always returns 0 */ + abort_and_upgrade_lock(lpt) || ERROR_INJECT_CRASH("crash_change_partition_5") || alter_close_tables(lpt) || ERROR_INJECT_CRASH("crash_change_partition_6") || diff --git a/sql/sql_plist.h b/sql/sql_plist.h new file mode 100644 index 00000000000..af2ed227ea1 --- /dev/null +++ b/sql/sql_plist.h @@ -0,0 +1,125 @@ +#ifndef SQL_PLIST_H +#define SQL_PLIST_H +/* Copyright (C) 2008 MySQL AB + + 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 + +template class I_P_List_iterator; + + +/** + Intrusive parameterized list. + + Unlike I_List does not require its elements to be descendant of ilink + class and therefore allows them to participate in several such lists + simultaneously. + + Unlike List is doubly-linked list and thus supports efficient deletion + of element without iterator. + + @param T Type of elements which will belong to list. + @param B Class which via its methods specifies which members + of T should be used for participating in this list. + Here is typical layout of such class: + + struct B + { + static inline T **next_ptr(T *el) + { + return &el->next; + } + static inline T ***prev_ptr(T *el) + { + return &el->prev; + } + }; +*/ + +template +class I_P_List +{ + T *first; + + /* + Do not prohibit copying of I_P_List object to simplify their usage in + backup/restore scenarios. Note that performing any operations on such + is a bad idea. + */ +public: + I_P_List() : first(NULL) { }; + inline void empty() { first= NULL; } + inline bool is_empty() { return (first == NULL); } + inline void push_front(T* a) + { + *B::next_ptr(a)= first; + if (first) + *B::prev_ptr(first)= B::next_ptr(a); + first= a; + *B::prev_ptr(a)= &first; + } + inline void remove(T *a) + { + T *next= *B::next_ptr(a); + if (next) + *B::prev_ptr(next)= *B::prev_ptr(a); + **B::prev_ptr(a)= next; + } + inline T* head() { return first; } + void swap(I_P_List &rhs) + { + swap_variables(T *, first, rhs.first); + if (first) + *B::prev_ptr(first)= &first; + if (rhs.first) + *B::prev_ptr(rhs.first)= &rhs.first; + } +#ifndef _lint + friend class I_P_List_iterator; +#endif +}; + + +/** + Iterator for I_P_List. +*/ + +template +class I_P_List_iterator +{ + I_P_List *list; + T *current; +public: + I_P_List_iterator(I_P_List &a) : list(&a), current(a.first) {} + inline void init(I_P_List &a) + { + list= &a; + current= a.first; + } + inline T* operator++(int) + { + T *result= current; + if (result) + current= *B::next_ptr(current); + return result; + } + inline void rewind() + { + current= list->first; + } +}; + +#endif diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 936c9ae8866..e9f4152f861 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1366,6 +1366,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) tables.alias= tables.table_name= (char*)"plugin"; tables.lock_type= TL_READ; tables.db= new_thd->db; + alloc_mdl_locks(&tables, tmp_root); #ifdef EMBEDDED_LIBRARY /* @@ -1659,6 +1660,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); + alloc_mdl_locks(&tables, thd->mem_root); + /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(TRUE); @@ -1732,6 +1735,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; + alloc_mdl_locks(&tables, thd->mem_root); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index d624c22f43a..582e18a3abf 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1673,7 +1673,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); - create_table->create= TRUE; + create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE; } if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index dac96f2e9c4..857cccde50f 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -51,7 +51,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) DBUG_RETURN(1); } - mysql_ha_rm_tables(thd, table_list, FALSE); + mysql_ha_rm_tables(thd, table_list); if (wait_if_global_read_lock(thd,0,1)) DBUG_RETURN(1); @@ -133,12 +133,13 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) } } - pthread_mutex_lock(&LOCK_open); - if (lock_table_names_exclusively(thd, table_list)) - { - pthread_mutex_unlock(&LOCK_open); + if (lock_table_names(thd, table_list)) goto err; - } + + pthread_mutex_lock(&LOCK_open); + + for (ren_table= table_list; ren_table; ren_table= ren_table->next_local) + expel_table_from_cache(0, ren_table->db, ren_table->table_name); error=0; if ((ren_table=rename_tables(thd,table_list,0))) @@ -184,9 +185,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) if (!error) query_cache_invalidate3(thd, table_list, 0); - pthread_mutex_lock(&LOCK_open); - unlock_table_names(thd, table_list, (TABLE_LIST*) 0); - pthread_mutex_unlock(&LOCK_open); + unlock_table_names(thd); err: start_waiting_global_read_lock(thd); diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index e5fe06ce39b..5251a50cab9 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -224,12 +224,7 @@ bool servers_reload(THD *thd) bool return_val= TRUE; DBUG_ENTER("servers_reload"); - if (thd->locked_tables) - { // Can't have locked tables here - thd->lock=thd->locked_tables; - thd->locked_tables=0; - close_thread_tables(thd); - } + unlock_locked_tables(thd); // Can't have locked tables here DBUG_PRINT("info", ("locking servers_cache")); rw_wrlock(&THR_LOCK_servers); @@ -238,6 +233,7 @@ bool servers_reload(THD *thd) tables[0].alias= tables[0].table_name= (char*) "servers"; tables[0].db= (char*) "mysql"; tables[0].lock_type= TL_READ; + alloc_mdl_locks(tables, thd->mem_root); if (simple_open_n_lock_tables(thd, tables)) { @@ -368,6 +364,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server) bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; tables.alias= tables.table_name= (char*) "servers"; + alloc_mdl_locks(&tables, thd->mem_root); /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) @@ -586,6 +583,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; tables.alias= tables.table_name= (char*) "servers"; + alloc_mdl_locks(&tables, thd->mem_root); rw_wrlock(&THR_LOCK_servers); @@ -710,6 +708,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) bzero((char*) &tables, sizeof(tables)); tables.db= (char*)"mysql"; tables.alias= tables.table_name= (char*)"servers"; + alloc_mdl_locks(&tables, thd->mem_root); if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index babadc34842..c83a6981166 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2963,7 +2963,7 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, table, res, db_name, table_name)); thd->temporary_tables= 0; - close_tables_for_reopen(thd, &show_table_list); + close_tables_for_reopen(thd, &show_table_list, FALSE); DBUG_RETURN(error); } @@ -3106,6 +3106,9 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, char key[MAX_DBKEY_LENGTH]; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; + MDL_LOCK mdl_lock; + char mdlkey[MAX_DBKEY_LENGTH]; + bool retry; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); @@ -3130,6 +3133,34 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, table_list.db= db_name->str; } + mdl_init_lock(&mdl_lock, mdlkey, 0, db_name->str, table_name->str); + table_list.mdl_lock= &mdl_lock; + mdl_add_lock(&thd->mdl_context, &mdl_lock); + mdl_set_lock_priority(&mdl_lock, MDL_HIGH_PRIO); + + /* + TODO: investigate if in this particular situation we can get by + simply obtaining internal lock of data-dictionary (ATM it + is LOCK_open) instead of obtaning full-blown metadata lock. + */ + while (1) + { + if (mdl_acquire_shared_lock(&mdl_lock, &retry)) + { + if (!retry || mdl_wait_for_locks(&thd->mdl_context)) + { + /* + Some error occured or we have been killed while waiting + for conflicting locks to go away, let the caller to handle + the situation. + */ + return 1; + } + continue; + } + break; + } + key_length= create_table_def_key(thd, key, &table_list, 0); pthread_mutex_lock(&LOCK_open); share= get_table_share(thd, &table_list, key, @@ -3137,7 +3168,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, if (!share) { res= 0; - goto err; + goto err_unlock; } if (share->is_view) @@ -3146,7 +3177,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, { /* skip view processing */ res= 0; - goto err1; + goto err_share; } else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL) { @@ -3155,7 +3186,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, open_normal_and_derived_tables() */ res= 1; - goto err1; + goto err_share; } } @@ -3171,14 +3202,17 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, res= schema_table->process_table(thd, &table_list, table, res, db_name, table_name); closefrm(&tbl, true); - goto err; + goto err_unlock; } -err1: +err_share: release_table_share(share, RELEASE_NORMAL); -err: +err_unlock: pthread_mutex_unlock(&LOCK_open); + +err: + mdl_release_lock(&thd->mdl_context, &mdl_lock); thd->clear_error(); return res; } @@ -3425,7 +3459,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) res= schema_table->process_table(thd, show_table_list, table, res, &orig_db_name, &tmp_lex_string); - close_tables_for_reopen(thd, &show_table_list); + close_tables_for_reopen(thd, &show_table_list, FALSE); } DBUG_ASSERT(!lex->query_tables_own_last); if (res) @@ -7273,6 +7307,8 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ + alloc_mdl_locks(lst, thd->mem_root); + if (open_tables(thd, &lst, &num_tables, 0)) { my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0), diff --git a/sql/sql_table.cc b/sql/sql_table.cc index dc0c876e882..f91bae8b76c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -53,6 +53,7 @@ static bool mysql_prepare_alter_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, Alter_info *alter_info); +static bool close_cached_table(THD *thd, TABLE *table); #ifndef DBUG_OFF @@ -1791,11 +1792,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, DBUG_RETURN(TRUE); } - /* - Acquire LOCK_open after wait_if_global_read_lock(). If we would hold - LOCK_open during wait_if_global_read_lock(), other threads could not - close their tables. This would make a pretty deadlock. - */ thd->push_internal_handler(&err_handler); error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0); thd->pop_internal_handler(); @@ -1867,16 +1863,14 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, built_query.append("DROP TABLE "); } - mysql_ha_rm_tables(thd, tables, FALSE); - - pthread_mutex_lock(&LOCK_open); + mysql_ha_rm_tables(thd, tables); /* If we have the table in the definition cache, we don't have to check the .frm file to find if the table is a normal table (not view) and what engine to use. */ - + pthread_mutex_lock(&LOCK_open); for (table= tables; table; table= table->next_local) { TABLE_SHARE *share; @@ -1889,16 +1883,32 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, check_if_log_table(table->db_length, table->db, table->table_name_length, table->table_name, 1)) { - my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); pthread_mutex_unlock(&LOCK_open); + my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); DBUG_RETURN(1); } } + pthread_mutex_unlock(&LOCK_open); - if (!drop_temporary && lock_table_names_exclusively(thd, tables)) + if (!drop_temporary) { - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(1); + if (!thd->locked_tables) + { + if (lock_table_names(thd, tables)) + DBUG_RETURN(1); + pthread_mutex_lock(&LOCK_open); + for (table= tables; table; table= table->next_local) + expel_table_from_cache(0, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); + } + else if (thd->locked_tables) + { + for (table= tables; table; table= table->next_local) + if (!find_temporary_table(thd, table->db, table->table_name) && + !find_write_locked_table(thd->open_tables, table->db, + table->table_name)) + DBUG_RETURN(1); + } } for (table= tables; table; table= table->next_local) @@ -1973,17 +1983,21 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table_type= table->db_type; if (!drop_temporary) { - TABLE *locked_table; - abort_locked_tables(thd, db, table->table_name); - remove_table_from_cache(thd, db, table->table_name, - RTFC_WAIT_OTHER_THREAD_FLAG | - RTFC_CHECK_KILLED_FLAG); - /* - If the table was used in lock tables, remember it so that - unlock_table_names can free it - */ - if ((locked_table= drop_locked_tables(thd, db, table->table_name))) - table->table= locked_table; + if (thd->locked_tables) + { + TABLE *tab= find_locked_table(thd->open_tables, db, table->table_name); + if (close_cached_table(thd, tab)) + { + error= -1; + goto err_with_placeholders; + } + /* + Leave LOCK TABLES mode if we managed to drop all tables + which were locked. + */ + if (thd->locked_tables->table_count == 0) + unlock_locked_tables(thd); + } if (thd->killed) { @@ -1997,6 +2011,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->internal_tmp_table ? FN_IS_TMP : 0); } + /* + TODO: Investigate what should be done to remove this lock completely. + Is exclusive meta-data lock enough ? + */ + pthread_mutex_lock(&LOCK_open); if (drop_temporary || ((table_type == NULL && access(path, F_OK) && @@ -2049,6 +2068,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, error|= new_error; } } + pthread_mutex_unlock(&LOCK_open); if (error) { if (wrong_tables.length()) @@ -2064,11 +2084,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->table_name);); } - /* - It's safe to unlock LOCK_open: we have an exclusive lock - on the table name. - */ - pthread_mutex_unlock(&LOCK_open); thd->thread_specific_used|= tmp_table_deleted; error= 0; if (wrong_tables.length()) @@ -2151,10 +2166,19 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, */ } } - pthread_mutex_lock(&LOCK_open); err_with_placeholders: - unlock_table_names(thd, tables, (TABLE_LIST*) 0); - pthread_mutex_unlock(&LOCK_open); + if (!drop_temporary) + { + /* + Under LOCK TABLES we should release meta-data locks on the tables + which were dropped. Otherwise we can rely on close_thread_tables() + doing this. Unfortunately in this case we are likely to get more + false positives in lock_table_name_if_not_cached() function. So + it makes sense to remove exclusive meta-data locks in all cases. + */ + mdl_release_exclusive_locks(&thd->mdl_context); + } + DBUG_RETURN(error); } @@ -4005,6 +4029,34 @@ warn: } +/** + Auxiliary function which obtains exclusive meta-data lock on the + table if there are no shared or exclusive on it already. + + See mdl_try_acquire_exclusive_lock() function for more info. + + TODO: This function is here mostly to simplify current patch + and probably should be removed. + TODO: Investigate if it is kosher to leave lock request in the + context in the case when we fail to obtain the lock. +*/ + +static bool lock_table_name_if_not_cached(THD *thd, const char *db, + const char *table_name, + MDL_LOCK **lock) +{ + if (!(*lock= mdl_alloc_lock(0, db, table_name, thd->mem_root))) + return TRUE; + mdl_set_lock_type(*lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, *lock); + if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock)) + { + *lock= 0; + } + return FALSE; +} + + /* Database and name-locking aware wrapper for mysql_create_table_no_lock(), */ @@ -4015,7 +4067,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, bool internal_tmp_table, uint select_field_count) { - TABLE *name_lock= 0; + MDL_LOCK *target_lock= 0; bool result; DBUG_ENTER("mysql_create_table"); @@ -4038,12 +4090,12 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock)) { result= TRUE; goto unlock; } - if (!name_lock) + if (!target_lock) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) { @@ -4069,12 +4121,8 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, select_field_count); unlock: - if (name_lock) - { - pthread_mutex_lock(&LOCK_open); - unlink_open_table(thd, name_lock, FALSE); - pthread_mutex_unlock(&LOCK_open); - } + if (target_lock) + mdl_release_exclusive_locks(&thd->mdl_context); pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) pthread_cond_signal(&COND_refresh); @@ -4212,80 +4260,83 @@ mysql_rename_table(handlerton *base, const char *old_db, } -/* - Force all other threads to stop using the table +/** + Force all other threads to stop using the table by upgrading + metadata lock on it and remove unused TABLE instances from cache. - SYNOPSIS - wait_while_table_is_used() - thd Thread handler - table Table to remove from cache - function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted - HA_EXTRA_FORCE_REOPEN if table is not be used - HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed - NOTES - When returning, the table will be unusable for other threads until - the table is closed. + @param thd Thread handler + @param table Table to remove from cache + @param function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted + HA_EXTRA_FORCE_REOPEN if table is not be used + HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed - PREREQUISITES - Lock on LOCK_open - Win32 clients must also have a WRITE LOCK on the table ! + @note When returning, the table will be unusable for other threads + until metadata lock is downgraded. + + @retval FALSE Success. + @retval TRUE Failure (e.g. because thread was killed). */ -void wait_while_table_is_used(THD *thd, TABLE *table, +bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function) { + enum thr_lock_type old_lock_type; + DBUG_ENTER("wait_while_table_is_used"); DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", table->s->table_name.str, (ulong) table->s, table->db_stat, table->s->version)); - safe_mutex_assert_owner(&LOCK_open); - (void) table->file->extra(function); - /* Mark all tables that are in use as 'old' */ + + old_lock_type= table->reginfo.lock_type; mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ - /* Wait until all there are no other threads that has this table open */ - remove_table_from_cache(thd, table->s->db.str, - table->s->table_name.str, - RTFC_WAIT_OTHER_THREAD_FLAG); - DBUG_VOID_RETURN; + if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0, + table->s->db.str, + table->s->table_name.str)) + { + mysql_lock_downgrade_write(thd, table, old_lock_type); + DBUG_RETURN(TRUE); + } + + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(FALSE); } -/* - Close a cached table - SYNOPSIS - close_cached_table() - thd Thread handler - table Table to remove from cache +/** + Upgrade metadata lock on the table and close all its instances. - NOTES - Function ends by signaling threads waiting for the table to try to - reopen the table. + @param thd Thread handler + @param table Table to remove from cache - PREREQUISITES - Lock on LOCK_open - Win32 clients must also have a WRITE LOCK on the table ! + @retval FALSE Success. + @retval TRUE Failure (e.g. because thread was killed). */ -void close_cached_table(THD *thd, TABLE *table) +static bool close_cached_table(THD *thd, TABLE *table) { DBUG_ENTER("close_cached_table"); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); + /* FIXME: check if we pass proper parameters everywhere. */ + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + DBUG_RETURN(TRUE); + /* Close lock if this is not got with LOCK TABLES */ if (thd->lock) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; // Start locked threads } + + pthread_mutex_lock(&LOCK_open); /* Close all copies of 'table'. This also frees all LOCK TABLES lock */ unlink_open_table(thd, table, TRUE); - - /* When lock on LOCK_open is freed other threads can continue */ - broadcast_refresh(); - DBUG_VOID_RETURN; + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(FALSE); } static int send_check_errmsg(THD *thd, TABLE_LIST* table, @@ -4308,6 +4359,7 @@ static int send_check_errmsg(THD *thd, TABLE_LIST* table, static int prepare_for_restore(THD* thd, TABLE_LIST* table, HA_CHECK_OPT *check_opt) { + MDL_LOCK *mdl_lock= 0; DBUG_ENTER("prepare_for_restore"); if (table->table) // do not overwrite existing tables on restore @@ -4331,22 +4383,25 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); - if (lock_and_wait_for_table_name(thd,table)) - DBUG_RETURN(-1); + mdl_lock= mdl_alloc_lock(0, table->db, table->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + DBUG_RETURN(TRUE); + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(0, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); if (my_copy(src_path, dst_path, MYF(MY_WME))) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); + mdl_release_lock(&thd->mdl_context, mdl_lock); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed copying .frm file")); } if (mysql_truncate(thd, table, 1)) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); + mdl_release_lock(&thd->mdl_context, mdl_lock); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed generating table from .frm file")); } @@ -4357,10 +4412,11 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, to finish the restore in the handler later on */ pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, table, TRUE)) + if (reopen_name_locked_table(thd, table)) { - unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); + if (mdl_lock) + mdl_release_lock(&thd->mdl_context, mdl_lock); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed to open partially restored table")); } @@ -4380,6 +4436,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, char from[FN_REFLEN],tmp[FN_REFLEN+32]; const char **ext; MY_STAT stat_info; + MDL_LOCK *mdl_lock; DBUG_ENTER("prepare_for_repair"); if (!(check_opt->sql_flags & TT_USEFRM)) @@ -4391,6 +4448,17 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); + /* + TODO: Check that REPAIR's code also conforms to meta-data + locking protocol. Fix if it is not. + */ + mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + DBUG_RETURN(0); + pthread_mutex_lock(&LOCK_open); if (!(share= (get_table_share(thd, table_list, key, key_length, 0, &error)))) @@ -4457,41 +4525,29 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx", from, current_pid, thd->thread_id); - /* If we could open the table, close it */ if (table_list->table) { - pthread_mutex_lock(&LOCK_open); - close_cached_table(thd, table); - pthread_mutex_unlock(&LOCK_open); - } - if (lock_and_wait_for_table_name(thd,table_list)) - { - error= -1; - goto end; + /* If we could open the table, close it */ + if (close_cached_table(thd, table)) + goto end; + table_list->table= 0; } + // After this point we have X mdl lock in both cases + if (my_rename(from, tmp, MYF(MY_WME))) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); error= send_check_errmsg(thd, table_list, "repair", "Failed renaming data file"); goto end; } if (mysql_truncate(thd, table_list, 1)) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); error= send_check_errmsg(thd, table_list, "repair", "Failed generating table from .frm file"); goto end; } if (my_rename(tmp, from, MYF(MY_WME))) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); error= send_check_errmsg(thd, table_list, "repair", "Failed restoring .MYD file"); goto end; @@ -4502,9 +4558,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, to finish the repair in the handler later on. */ pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, table_list, TRUE)) + if (reopen_name_locked_table(thd, table_list)) { - unlock_table_name(thd, table_list); pthread_mutex_unlock(&LOCK_open); error= send_check_errmsg(thd, table_list, "repair", "Failed to open partially repaired table"); @@ -4519,6 +4574,8 @@ end: closefrm(table, 1); // Free allocated memory pthread_mutex_unlock(&LOCK_open); } + if (error) + mdl_release_exclusive_locks(&thd->mdl_context); DBUG_RETURN(error); } @@ -4566,7 +4623,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); - mysql_ha_rm_tables(thd, tables, FALSE); + mysql_ha_rm_tables(thd, tables); for (table= tables; table; table= table->next_local) { @@ -5063,6 +5120,7 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list) bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_repair_table"); + set_all_mdl_upgradable(tables); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "repair", TL_WRITE, 1, test(check_opt->sql_flags & TT_USEFRM), @@ -5250,7 +5308,7 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { - TABLE *name_lock= 0; + MDL_LOCK *target_lock= 0; char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1]; uint dst_path_length; char *db= table->db; @@ -5307,9 +5365,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock)) goto err; - if (!name_lock) + if (!target_lock) goto table_exists; dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); @@ -5453,9 +5511,8 @@ binlog: The table will be closed by unlink_open_table() at the end of this function. */ - table->table= name_lock; pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, table, FALSE)) + if (reopen_name_locked_table(thd, table)) { pthread_mutex_unlock(&LOCK_open); goto err; @@ -5468,6 +5525,10 @@ binlog: DBUG_ASSERT(result == 0); // store_create_info() always return 0 write_bin_log(thd, TRUE, query.ptr(), query.length()); + + pthread_mutex_lock(&LOCK_open); + unlink_open_table(thd, table->table, FALSE); + pthread_mutex_unlock(&LOCK_open); } else // Case 1 write_bin_log(thd, TRUE, thd->query(), thd->query_length()); @@ -5482,12 +5543,8 @@ binlog: res= FALSE; err: - if (name_lock) - { - pthread_mutex_lock(&LOCK_open); - unlink_open_table(thd, name_lock, FALSE); - pthread_mutex_unlock(&LOCK_open); - } + if (target_lock) + mdl_release_exclusive_locks(&thd->mdl_context); DBUG_RETURN(res); } @@ -5570,7 +5627,7 @@ mysql_discard_or_import_tablespace(THD *thd, err: ha_autocommit_or_rollback(thd, error); thd->tablespace_op=FALSE; - + if (error == 0) { my_ok(thd); @@ -5578,7 +5635,7 @@ err: } table->file->print_error(error, MYF(0)); - + DBUG_RETURN(-1); } @@ -6424,7 +6481,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Alter_info *alter_info, uint order_num, ORDER *order, bool ignore) { - TABLE *table, *new_table= 0, *name_lock= 0; + TABLE *table, *new_table= 0; + MDL_LOCK *target_lock= 0; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6507,7 +6565,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0); build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); - mysql_ha_rm_tables(thd, table_list, FALSE); + mysql_ha_rm_tables(thd, table_list); /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ if (alter_info->tablespace_op != NO_TABLESPACE_OP) @@ -6563,13 +6621,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (wait_if_global_read_lock(thd,0,1)) DBUG_RETURN(TRUE); - pthread_mutex_lock(&LOCK_open); if (lock_table_names(thd, table_list)) { error= 1; goto view_err; } - + + pthread_mutex_lock(&LOCK_open); + if (!do_rename(thd, table_list, new_db, new_name, new_name, 1)) { if (mysql_bin_log.is_open()) @@ -6581,15 +6640,17 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } my_ok(thd); } + pthread_mutex_unlock(&LOCK_open); - unlock_table_names(thd, table_list, (TABLE_LIST*) 0); + unlock_table_names(thd); view_err: - pthread_mutex_unlock(&LOCK_open); start_waiting_global_read_lock(thd); DBUG_RETURN(error); } + table_list->mdl_upgradable= TRUE; + if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ))) DBUG_RETURN(TRUE); table->use_all_columns(); @@ -6644,9 +6705,9 @@ view_err: } else { - if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock)) + if (lock_table_name_if_not_cached(thd, new_db, new_name, &target_lock)) DBUG_RETURN(TRUE); - if (!name_lock) + if (!target_lock) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); DBUG_RETURN(TRUE); @@ -6737,26 +6798,15 @@ view_err: case LEAVE_AS_IS: break; case ENABLE: - /* - wait_while_table_is_used() ensures that table being altered is - opened only by this thread and that TABLE::TABLE_SHARE::version - of TABLE object corresponding to this table is 0. - The latter guarantees that no DML statement will open this table - until ALTER TABLE finishes (i.e. until close_thread_tables()) - while the fact that the table is still open gives us protection - from concurrent DDL statements. - */ - pthread_mutex_lock(&LOCK_open); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - pthread_mutex_unlock(&LOCK_open); + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto err; DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000);); error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); /* COND_refresh will be signaled in close_thread_tables() */ break; case DISABLE: - pthread_mutex_lock(&LOCK_open); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - pthread_mutex_unlock(&LOCK_open); + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto err; error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); /* COND_refresh will be signaled in close_thread_tables() */ break; @@ -6773,24 +6823,19 @@ view_err: table->alias); } - pthread_mutex_lock(&LOCK_open); - /* - Unlike to the above case close_cached_table() below will remove ALL - instances of TABLE from table cache (it will also remove table lock - held by this thread). So to make actual table renaming and writing - to binlog atomic we have to put them into the same critical section - protected by LOCK_open mutex. This also removes gap for races between - access() and mysql_rename_table() calls. - */ - if (!error && (new_name != table_name || new_db != db)) { thd_proc_info(thd, "rename"); /* Then do a 'simple' rename of the table. First we need to close all instances of 'source' table. + Note that if close_cached_table() returns error here (i.e. if + this thread was killed) then it must be that previous step of + simple rename did nothing and therefore we can safely reture + without additional clean-up. */ - close_cached_table(thd, table); + if (close_cached_table(thd, table)) + goto err; /* Then, we want check once again that target table does not exist. Actually the order of these two steps does not matter since @@ -6807,6 +6852,7 @@ view_err: else { *fn_ext(new_name)=0; + pthread_mutex_lock(&LOCK_open); if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) error= -1; else if (Table_triggers_list::change_table_name(thd, db, table_name, @@ -6816,6 +6862,7 @@ view_err: table_name, 0); error= -1; } + pthread_mutex_unlock(&LOCK_open); } } @@ -6837,11 +6884,24 @@ view_err: table->file->print_error(error, MYF(0)); error= -1; } - if (name_lock) - unlink_open_table(thd, name_lock, FALSE); - pthread_mutex_unlock(&LOCK_open); table_list->table= NULL; // For query cache query_cache_invalidate3(thd, table_list, 0); + + if (thd->locked_tables) + { + /* + Under LOCK TABLES we should adjust meta-data locks before finishing + statement. Otherwise we can rely on close_thread_tables() releasing + them. + + TODO: Investigate what should be done with upgraded table-level + lock here... + */ + if (new_name != table_name || new_db != db) + mdl_release_exclusive_locks(&thd->mdl_context); + else + mdl_downgrade_exclusive_locks(&thd->mdl_context); + } DBUG_RETURN(error); } @@ -7073,7 +7133,7 @@ view_err: #ifdef WITH_PARTITION_STORAGE_ENGINE if (fast_alter_partition) { - DBUG_ASSERT(!name_lock); + DBUG_ASSERT(!target_lock); DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, db, table_name, @@ -7158,7 +7218,7 @@ view_err: tbl.db= new_db; tbl.table_name= tbl.alias= tmp_name; /* Table is in thd->temporary_tables */ - new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0, + new_table= open_table(thd, &tbl, thd->mem_root, (enum_open_table_action*) 0, MYSQL_LOCK_IGNORE_FLUSH); } else @@ -7168,10 +7228,10 @@ view_err: build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "", FN_IS_TMP); /* Open our intermediate table */ - new_table=open_temporary_table(thd, path, new_db, tmp_name,0); + new_table= open_temporary_table(thd, path, new_db, tmp_name, 1); } if (!new_table) - goto err1; + goto err_new_table_cleanup; /* Note: In case of MERGE table, we do not attach children. We do not copy data for MERGE tables. Only the children have data. @@ -7200,9 +7260,8 @@ view_err: } else { - pthread_mutex_lock(&LOCK_open); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - pthread_mutex_unlock(&LOCK_open); + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto err_new_table_cleanup; thd_proc_info(thd, "manage keys"); alter_table_manage_keys(table, table->file->indexes_are_disabled(), alter_info->keys_onoff); @@ -7253,7 +7312,7 @@ view_err: table->key_info= key_info; table->file->print_error(error, MYF(0)); table->key_info= save_key_info; - goto err1; + goto err_new_table_cleanup; } } /*end of if (index_add_count)*/ @@ -7276,14 +7335,14 @@ view_err: index_drop_count))) { table->file->print_error(error, MYF(0)); - goto err1; + goto err_new_table_cleanup; } /* Tell the handler to finally drop the indexes. */ if ((error= table->file->final_drop_index(table))) { table->file->print_error(error, MYF(0)); - goto err1; + goto err_new_table_cleanup; } } /*end of if (index_drop_count)*/ @@ -7296,16 +7355,16 @@ view_err: /* Need to commit before a table is unlocked (NDB requirement). */ DBUG_PRINT("info", ("Committing before unlocking table")); if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd)) - goto err1; + goto err_new_table_cleanup; committed= 1; } /*end of if (! new_table) for add/drop index*/ + if (error) + goto err_new_table_cleanup; + if (table->s->tmp_table != NO_TMP_TABLE) { - /* We changed a temporary table */ - if (error) - goto err1; /* Close lock if this is a transactional table */ if (thd->lock) { @@ -7316,28 +7375,22 @@ view_err: close_temporary_table(thd, table, 1, 1); /* Should pass the 'new_name' as we store table name in the cache */ if (rename_temporary_table(thd, new_table, new_db, new_name)) - goto err1; + goto err_new_table_cleanup; /* We don't replicate alter table statement on temporary tables */ if (!thd->current_stmt_binlog_row_based) write_bin_log(thd, TRUE, thd->query(), thd->query_length()); goto end_temporary; } + /* + Close the intermediate table that will be the new table, but do + not delete it! Even altough MERGE tables do not have their children + attached here it is safe to call close_temporary_table(). + */ if (new_table) { - /* - Close the intermediate table that will be the new table. - Note that MERGE tables do not have their children attached here. - */ - intern_close_table(new_table); - my_free(new_table,MYF(0)); - } - pthread_mutex_lock(&LOCK_open); - if (error) - { - (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); - pthread_mutex_unlock(&LOCK_open); - goto err; + close_temporary_table(thd, new_table, 1, 0); + new_table= 0; } /* @@ -7362,7 +7415,11 @@ view_err: if (lower_case_table_names) my_casedn_str(files_charset_info, old_name); - wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME); + if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) + goto err_new_table_cleanup; + + pthread_mutex_lock(&LOCK_open); + close_data_files_and_morph_locks(thd, db, table_name); error=0; @@ -7431,8 +7488,7 @@ view_err: table_list->table_name_length= strlen(new_name); table_list->db= new_db; table_list->db_length= strlen(new_db); - table_list->table= name_lock; - if (reopen_name_locked_table(thd, table_list, FALSE)) + if (reopen_name_locked_table(thd, table_list)) goto err_with_placeholders; t_table= table_list->table; } @@ -7446,16 +7502,24 @@ view_err: if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG, create_info)) goto err_with_placeholders; - if (thd->locked_tables && new_name == table_name && new_db == db) + if (thd->locked_tables) { - /* - We are going to reopen table down on the road, so we have to restore - state of the TABLE object which we used for obtaining of handler - object to make it suitable for reopening. - */ - DBUG_ASSERT(t_table == table); - table->open_placeholder= 1; - close_handle_and_leave_table_as_lock(table); + if (new_name == table_name && new_db == db) + { + /* + We are going to reopen table down on the road, so we have to restore + state of the TABLE object which we used for obtaining of handler + object to make it suitable for reopening. + */ + DBUG_ASSERT(t_table == table); + table->open_placeholder= 1; + close_handle_and_leave_table_as_lock(table); + } + else + { + /* Unlink the new name from the list of locked tables. */ + unlink_open_table(thd, t_table, FALSE); + } } } @@ -7464,7 +7528,7 @@ view_err: if (thd->locked_tables && new_name == table_name && new_db == db) { thd->in_lock_tables= 1; - error= reopen_tables(thd, 1, 1); + error= reopen_tables(thd, 1); thd->in_lock_tables= 0; if (error) goto err_with_placeholders; @@ -7508,18 +7572,17 @@ view_err: table_list->table=0; // For query cache query_cache_invalidate3(thd, table_list, 0); - if (thd->locked_tables && (new_name != table_name || new_db != db)) + if (thd->locked_tables) { - /* - If are we under LOCK TABLES and did ALTER TABLE with RENAME we need - to remove placeholders for the old table and for the target table - from the list of open tables and table cache. If we are not under - LOCK TABLES we can rely on close_thread_tables() doing this job. - */ - pthread_mutex_lock(&LOCK_open); - unlink_open_table(thd, table, FALSE); - unlink_open_table(thd, name_lock, FALSE); - pthread_mutex_unlock(&LOCK_open); + if ((new_name != table_name || new_db != db)) + { + pthread_mutex_lock(&LOCK_open); + unlink_open_table(thd, table, FALSE); + pthread_mutex_unlock(&LOCK_open); + mdl_release_exclusive_locks(&thd->mdl_context); + } + else + mdl_downgrade_exclusive_locks(&thd->mdl_context); } end_temporary: @@ -7530,7 +7593,7 @@ end_temporary: thd->some_tables_deleted=0; DBUG_RETURN(FALSE); -err1: +err_new_table_cleanup: if (new_table) { /* close_temporary_table() frees the new_table pointer. */ @@ -7574,12 +7637,8 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (name_lock) - { - pthread_mutex_lock(&LOCK_open); - unlink_open_table(thd, name_lock, FALSE); - pthread_mutex_unlock(&LOCK_open); - } + if (target_lock) + mdl_release_exclusive_locks(&thd->mdl_context); DBUG_RETURN(TRUE); err_with_placeholders: @@ -7589,9 +7648,8 @@ err_with_placeholders: from list of open tables list and table cache. */ unlink_open_table(thd, table, FALSE); - if (name_lock) - unlink_open_table(thd, name_lock, FALSE); pthread_mutex_unlock(&LOCK_open); + mdl_release_exclusive_locks(&thd->mdl_context); DBUG_RETURN(TRUE); } /* mysql_alter_table */ diff --git a/sql/sql_test.cc b/sql/sql_test.cc index d9beb77f546..3907aa6a5ae 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -75,7 +75,8 @@ print_where(COND *cond,const char *info, enum_query_type query_type) void print_cached_tables(void) { uint idx,count,unused; - TABLE *start_link,*lnk; + TABLE_SHARE *share; + TABLE *start_link, *lnk, *entry; compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions)); @@ -83,16 +84,26 @@ void print_cached_tables(void) pthread_mutex_lock(&LOCK_open); puts("DB Table Version Thread Open Lock"); - for (idx=unused=0 ; idx < open_cache.records ; idx++) + for (idx=unused=0 ; idx < table_def_cache.records ; idx++) { - TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx); - printf("%-14.14s %-32s%6ld%8ld%6d %s\n", - entry->s->db.str, entry->s->table_name.str, entry->s->version, - entry->in_use ? entry->in_use->thread_id : 0L, - entry->db_stat ? 1 : 0, - entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use"); - if (!entry->in_use) + share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx); + + I_P_List_iterator it(share->used_tables); + while ((entry= it++)) + { + printf("%-14.14s %-32s%6ld%8ld%6d %s\n", + entry->s->db.str, entry->s->table_name.str, entry->s->version, + entry->in_use->thread_id, entry->db_stat ? 1 : 0, + lock_descriptions[(int)entry->reginfo.lock_type]); + } + it.init(share->free_tables); + while ((entry= it++)) + { unused++; + printf("%-14.14s %-32s%6ld%8ld%6d %s\n", + entry->s->db.str, entry->s->table_name.str, entry->s->version, + 0L, entry->db_stat ? 1 : 0, "Not in use"); + } } count=0; if ((start_link=lnk=unused_tables)) @@ -104,17 +115,18 @@ void print_cached_tables(void) printf("unused_links isn't linked properly\n"); return; } - } while (count++ < open_cache.records && (lnk=lnk->next) != start_link); + } while (count++ < table_cache_count && (lnk=lnk->next) != start_link); if (lnk != start_link) { printf("Unused_links aren't connected\n"); } } if (count != unused) - printf("Unused_links (%d) doesn't match open_cache: %d\n", count,unused); + printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count, + unused); printf("\nCurrent refresh version: %ld\n",refresh_version); - if (my_hash_check(&open_cache)) - printf("Error: File hash table is corrupted\n"); + if (my_hash_check(&table_def_cache)) + printf("Error: Table definition hash table is corrupted\n"); fflush(stdout); pthread_mutex_unlock(&LOCK_open); /* purecov: end */ @@ -380,7 +392,7 @@ static void display_table_locks(void) LIST *list; DYNAMIC_ARRAY saved_table_locks; - (void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),open_cache.records + 20,50); + (void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO), table_cache_count + 20,50); pthread_mutex_lock(&THR_LOCK_lock); for (list= thr_lock_thread_list; list; list= list_rest(list)) { diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index ecbb6473ec4..a623b3c80f3 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -387,8 +387,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) DBUG_RETURN(TRUE); - pthread_mutex_lock(&LOCK_open); - if (!create) { bool if_exists= thd->lex->drop_if_exists; @@ -444,26 +442,28 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) tables->required_type= FRMTYPE_TABLE; /* Keep consistent with respect to other DDL statements */ - mysql_ha_rm_tables(thd, tables, TRUE); + mysql_ha_rm_tables(thd, tables); if (thd->locked_tables) { - /* Table must be write locked */ if (name_lock_locked_table(thd, tables)) goto end; + pthread_mutex_lock(&LOCK_open); } else { - /* Grab the name lock and insert the placeholder*/ + /* + Obtain exlusive meta-data lock on the table and remove TABLE + instances from cache. + */ if (lock_table_names(thd, tables)) goto end; - /* Convert the placeholder to a real table */ - if (reopen_name_locked_table(thd, tables, TRUE)) - { - unlock_table_name(thd, tables); - goto end; - } + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(0, tables->db, tables->table_name); + + if (reopen_name_locked_table(thd, tables)) + goto end_unlock; } table= tables->table; @@ -472,11 +472,11 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (!create) { my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); - goto end; + goto end_unlock; } if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table))) - goto end; + goto end_unlock; } result= (create ? @@ -489,7 +489,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* Make table suitable for reopening */ close_data_files_and_morph_locks(thd, tables->db, tables->table_name); thd->in_lock_tables= 1; - if (reopen_tables(thd, 1, 1)) + if (reopen_tables(thd, 1)) { /* To be safe remove this table from the set of LOCKED TABLES */ unlink_open_table(thd, tables->table, FALSE); @@ -503,14 +503,22 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) thd->in_lock_tables= 0; } -end: +end_unlock: + pthread_mutex_unlock(&LOCK_open); +end: if (!result) { write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length()); } - pthread_mutex_unlock(&LOCK_open); + /* + If we are under LOCK TABLES we should restore original state of meta-data + locks. Otherwise call to close_thread_tables() will take care about both + TABLE instance created by reopen_name_locked_table() and meta-data lock. + */ + if (thd->locked_tables) + mdl_downgrade_exclusive_locks(&thd->mdl_context); if (need_start_waiting) start_waiting_global_read_lock(thd); @@ -1879,11 +1887,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, In the future, only an exclusive table name lock will be enough. */ #ifndef DBUG_OFF - uchar key[MAX_DBKEY_LENGTH]; - uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1, - old_table)-(char*)&key[0])+1; - - if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length)) + if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table)) safe_mutex_assert_owner(&LOCK_open); #endif diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index a1a0d9633b7..25a0db2fbb8 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -142,6 +142,7 @@ void udf_init() tables.alias= tables.table_name= (char*) "func"; tables.lock_type = TL_READ; tables.db= db; + alloc_mdl_locks(&tables, new_thd->mem_root); if (simple_open_n_lock_tables(new_thd, &tables)) { @@ -485,6 +486,7 @@ int mysql_create_function(THD *thd,udf_func *udf) bzero((char*) &tables,sizeof(tables)); tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; + alloc_mdl_locks(&tables, thd->mem_root); /* Allow creation of functions even if we can't open func table */ if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; @@ -563,6 +565,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) bzero((char*) &tables,sizeof(tables)); tables.db=(char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; + alloc_mdl_locks(&tables, thd->mem_root); if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index da8b2d046bb..1ad39a2f838 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -226,7 +226,7 @@ int mysql_update(THD *thd, break; if (!need_reopen) DBUG_RETURN(1); - close_tables_for_reopen(thd, &table_list); + close_tables_for_reopen(thd, &table_list, FALSE); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -1149,7 +1149,7 @@ reopen_tables: */ cleanup_items(thd->free_list); - close_tables_for_reopen(thd, &table_list); + close_tables_for_reopen(thd, &table_list, FALSE); goto reopen_tables; } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 83341a53c3e..a0acde600c7 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -178,40 +178,17 @@ err: static bool fill_defined_view_parts (THD *thd, TABLE_LIST *view) { + char key[MAX_DBKEY_LENGTH]; + uint key_length; LEX *lex= thd->lex; - bool not_used; TABLE_LIST decoy; memcpy (&decoy, view, sizeof (TABLE_LIST)); + key_length= create_table_def_key(thd, key, view, 0); - /* - Let's reset decoy.view before calling open_table(): when we start - supporting ALTER VIEW in PS/SP that may save us from a crash. - */ - - decoy.view= NULL; - - /* - open_table() will return NULL if 'decoy' is idenitifying a view *and* - there is no TABLE object for that view in the table cache. However, - decoy.view will be set to 1. - - If there is a TABLE-instance for the oject identified by 'decoy', - open_table() will return that instance no matter if it is a table or - a view. - - Thus, there is no need to check for the return value of open_table(), - since the return value itself does not mean anything. - */ - - open_table(thd, &decoy, thd->mem_root, ¬_used, OPEN_VIEW_NO_PARSE); - - if (!decoy.view) - { - /* It's a table. */ - my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); + if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length, + thd->mem_root, OPEN_VIEW_NO_PARSE)) return TRUE; - } if (!lex->definer) { @@ -400,6 +377,35 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, DBUG_ASSERT(!lex->proc_list.first && !lex->result && !lex->param_list.elements); + /* + We can't allow taking exclusive meta-data locks of unlocked view under + LOCK TABLES since this might lead to deadlock. Since at the moment we + can't really lock view with LOCK TABLES we simply prohibit creation/ + alteration of views under LOCK TABLES. + */ + + if (thd->locked_tables) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + res= TRUE; + goto err; + } + + if ((res= create_view_precheck(thd, tables, view, mode))) + goto err; + + lex->link_first_table_back(view, link_to_local); + view->open_table_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL; + + if (open_and_lock_tables(thd, lex->query_tables)) + { + view= lex->unlink_first_table(&link_to_local); + res= TRUE; + goto err; + } + + view= lex->unlink_first_table(&link_to_local); + if (mode != VIEW_CREATE_NEW) { if (mode == VIEW_ALTER && @@ -464,16 +470,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } } #endif - - if ((res= create_view_precheck(thd, tables, view, mode))) - goto err; - - if (open_and_lock_tables(thd, tables)) - { - res= TRUE; - goto err; - } - /* check that tables are not temporary and this VIEW do not used in query (it is possible with ALTERing VIEW). @@ -615,11 +611,13 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } #endif + if (wait_if_global_read_lock(thd, 0, 0)) { res= TRUE; goto err; } + pthread_mutex_lock(&LOCK_open); res= mysql_register_view(thd, view, mode); @@ -1331,7 +1329,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, anyway. */ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) + { tbl->lock_type= table->lock_type; + tbl->mdl_upgradable= table->mdl_upgradable; + } /* If the view is mergeable, we might want to INSERT/UPDATE/DELETE into tables of this view. Preserve the @@ -1579,6 +1580,21 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) bool something_wrong= FALSE; DBUG_ENTER("mysql_drop_view"); + /* + We can't allow dropping of unlocked view under LOCK TABLES since this + might lead to deadlock. But since we can't really lock view with LOCK + TABLES we have to simply prohibit dropping of views. + */ + + if (thd->locked_tables) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(TRUE); + } + + if (lock_table_names(thd, views)) + DBUG_RETURN(TRUE); + pthread_mutex_lock(&LOCK_open); for (view= views; view; view= view->next_local) { diff --git a/sql/table.cc b/sql/table.cc index 7ea04ed3e15..181014df9f4 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -316,6 +316,9 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, share->table_map_id= ~0UL; share->cached_row_logging_check= -1; + share->used_tables.empty(); + share->free_tables.empty(); + memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST); pthread_cond_init(&share->cond, NULL); @@ -382,6 +385,9 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, */ share->table_map_id= (ulong) thd->query_id; + share->used_tables.empty(); + share->free_tables.empty(); + DBUG_VOID_RETURN; } @@ -4832,6 +4838,20 @@ size_t max_row_length(TABLE *table, const uchar *data) return length; } + +/** + Helper function which allows to allocate metadata lock request + objects for all elements of table list. +*/ + +void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root) +{ + for ( ; table_list ; table_list= table_list->next_global) + table_list->mdl_lock= mdl_alloc_lock(0, table_list->db, + table_list->table_name, root); +} + + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/table.h b/sql/table.h index 76bebd3fdaa..b0598e07299 100644 --- a/sql/table.h +++ b/sql/table.h @@ -17,6 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "sql_plist.h" + /* Structs that defines the TABLE */ class Item; /* Needed by ORDER */ @@ -28,6 +30,7 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; +struct MDL_LOCK; /*************************************************************************/ @@ -288,6 +291,9 @@ typedef enum enum_table_category TABLE_CATEGORY; TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name); + +struct TABLE_share; + /* This structure is shared between different table objects. There is one instance of table share per one table in the database. @@ -310,6 +316,13 @@ struct TABLE_SHARE pthread_cond_t cond; /* To signal that share is ready */ TABLE_SHARE *next, **prev; /* Link to unused shares */ + /* + Doubly-linked (back-linked) lists of used and unused TABLE objects + for this share. + */ + I_P_List used_tables; + I_P_List free_tables; + /* The following is copied to each TABLE on OPEN */ Field **field; Field **found_next_number_field; @@ -613,6 +626,19 @@ struct TABLE handler *file; TABLE *next, *prev; +private: + /** + Links for the lists of used/unused TABLE objects for this share. + Declared as private to avoid direct manipulation with those objects. + One should use methods of I_P_List template instead. + */ + TABLE *share_next, **share_prev; + + friend struct TABLE_share; + friend bool reopen_table(TABLE *table); + +public: + /* For the below MERGE related members see top comment in ha_myisammrg.cc */ TABLE *parent; /* Set in MERGE child. Ptr to parent */ TABLE_LIST *child_l; /* Set in MERGE parent. List of children */ @@ -814,6 +840,7 @@ struct TABLE partition_info *part_info; /* Partition related information */ bool no_partitions_used; /* If true, all partitions have been pruned away */ #endif + MDL_LOCK *mdl_lock; bool fill_item_list(List *item_list) const; void reset_item_list(List *item_list) const; @@ -859,6 +886,25 @@ struct TABLE bool is_children_attached(void); }; + +/** + Helper class which specifies which members of TABLE are used for + participation in the list of used/unused TABLE objects for the share. +*/ + +struct TABLE_share +{ + static inline TABLE **next_ptr(TABLE *l) + { + return &l->share_next; + } + static inline TABLE ***prev_ptr(TABLE *l) + { + return &l->share_prev; + } +}; + + enum enum_schema_table_state { NOT_PROCESSED= 0, @@ -1326,11 +1372,18 @@ struct TABLE_LIST */ bool prelocking_placeholder; /* - This TABLE_LIST object corresponds to the table to be created - so it is possible that it does not exist (used in CREATE TABLE - ... SELECT implementation). + This TABLE_LIST object corresponds to the table/view which requires + special handling/meta-data locking. For example this is a target + table in CREATE TABLE ... SELECT so it is possible that it does not + exist and we should take exclusive meta-data lock on it in this + case. */ - bool create; + enum {NORMAL_OPEN= 0, OPEN_OR_CREATE, TAKE_EXCLUSIVE_MDL} open_table_type; + /** + Indicates that for this table/view we need to take shared metadata + lock which should be upgradable to exclusive metadata lock. + */ + bool mdl_upgradable; bool internal_tmp_table; /** TRUE if an alias for this table was specified in the SQL. */ bool is_alias; @@ -1379,6 +1432,9 @@ struct TABLE_LIST bool has_table_lookup_value; uint table_open_method; enum enum_schema_table_state schema_table_state; + + MDL_LOCK *mdl_lock; + void calc_md5(char *buffer); void set_underlying_merge(); int view_check_option(THD *thd, bool ignore_failure); @@ -1386,8 +1442,7 @@ struct TABLE_LIST void cleanup_items(); bool placeholder() { - return derived || view || schema_table || (create && !table->db_stat) || - !table; + return derived || view || schema_table || !table; } void print(THD *thd, String *str, enum_query_type query_type); bool check_single_table(TABLE_LIST **table, table_map map, @@ -1746,4 +1801,17 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, size_t max_row_length(TABLE *table, const uchar *data); + +void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root); + +/** + Helper function which allows to mark all elements in table list + as requiring upgradable metadata locks. +*/ + +inline void set_all_mdl_upgradable(TABLE_LIST *tables) +{ + for (; tables; tables= tables->next_global) + tables->mdl_upgradable= TRUE; +} #endif /* TABLE_INCLUDED */ diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 471e2243aac..b2181636492 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -270,6 +270,12 @@ static int myisammrg_parent_open_callback(void *callback_param, /* Set alias. */ child_l->alias= child_l->table_name; + /* + FIXME: Actually we should use some other mem-root here. + To be fixed once Ingo pushes his patch for WL4144. + */ + alloc_mdl_locks(child_l, &parent->mem_root); + /* Initialize table map to 'undefined'. */ child_l->init_child_def_version(); From b2ac2b8b1d010b5364f83c6400e9145b01b70a87 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 19:09:42 +0300 Subject: [PATCH 039/466] Backport of: ------------------------------------------------------------ revno: 2630.4.7 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sun 2008-05-25 11:19:02 +0400 message: WL#3726 "DDL locking for all metadata objects". Fixed silly mistake in test case which caused sporadic kill.test failures. --- mysql-test/t/kill.test | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index 98cb9f64d98..b91feb3a1d5 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -374,6 +374,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "drop table t1"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -389,6 +390,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "create trigger t1_bi before insert on t1 for each row set @a:=1"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -407,6 +409,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 add column j int"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -422,6 +425,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 rename to t2"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -435,6 +439,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 disable keys"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -449,6 +454,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 alter column i set default 100"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -470,6 +476,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t2 alter column i set default 100"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -494,6 +501,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "rename tables t1 to t3, t2 to t1"; +--source include/wait_condition.inc let $ID2= `select connection_id()`; --send insert into t2 values (1) --echo # Switching to connection 'default' @@ -502,6 +510,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "insert into t2 values (1)"; +--source include/wait_condition.inc --replace_result $ID2 ID2 eval kill query $ID2; --echo # Switching to connection 'dml' @@ -529,6 +538,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Flushing tables" and info = "flush tables"; +--source include/wait_condition.inc --send select * from t1 --echo # Switching to connection 'default' connection default; @@ -536,6 +546,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "select * from t1"; +--source include/wait_condition.inc --replace_result $ID2 ID2 eval kill query $ID2; --echo # Switching to connection 'dml' From 076722c4af766fef454d23326c943df8bb633fc0 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 19:21:40 +0300 Subject: [PATCH 040/466] Backport of: ---------------------------------------------------------------- 2630.4.9 Dmitry Lenev 2008-05-26 WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Adjusted test case according to review. --- mysql-test/r/view_multi.result | 48 ------- mysql-test/suite/rpl/r/rpl_view_multi.result | 90 +++++++++++++ mysql-test/suite/rpl/t/rpl_view_multi.test | 134 +++++++++++++++++++ mysql-test/t/view_multi.test | 110 --------------- 4 files changed, 224 insertions(+), 158 deletions(-) delete mode 100644 mysql-test/r/view_multi.result create mode 100644 mysql-test/suite/rpl/r/rpl_view_multi.result create mode 100644 mysql-test/suite/rpl/t/rpl_view_multi.test delete mode 100644 mysql-test/t/view_multi.test diff --git a/mysql-test/r/view_multi.result b/mysql-test/r/view_multi.result deleted file mode 100644 index 95a8d572be4..00000000000 --- a/mysql-test/r/view_multi.result +++ /dev/null @@ -1,48 +0,0 @@ -reset master; -create table t1 (i int); -create table t2 (i int); -create view v1 as select * from t1; -select get_lock("lock_bg25144", 1); -get_lock("lock_bg25144", 1) -1 -insert into v1 values (get_lock("lock_bg25144", 100));; -drop view v1;; -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select * from t1; -i -1 -create view v1 as select * from t1; -select get_lock("lock_bg25144", 1); -get_lock("lock_bg25144", 1) -1 -insert into v1 values (get_lock("lock_bg25144", 100));; -alter view v1 as select * from t2;; -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select * from t1; -i -1 -1 -select * from t2; -i -show binlog events in 'master-bin.000001' from 107; -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query 1 # use `test`; create table t1 (i int) -master-bin.000001 # Query 1 # use `test`; create table t2 (i int) -master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 -master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) -master-bin.000001 # Query 1 # use `test`; drop view v1 -master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 -master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) -master-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t2 -drop table t1, t2; -drop view v1; diff --git a/mysql-test/suite/rpl/r/rpl_view_multi.result b/mysql-test/suite/rpl/r/rpl_view_multi.result new file mode 100644 index 00000000000..b3f10584a24 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_view_multi.result @@ -0,0 +1,90 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +# +# Bug #25144 "replication / binlog with view breaks". +# Statements that used views didn't ensure that view were not modified +# during their execution. Indeed this led to incorrect binary log with +# statement based logging and as result to broken replication. +# +drop tables if exists t1, t2; +drop view if exists v1; +# Syncing slave with master and switching to connection 'slave' +# Switching to connection 'master' +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; +# First we try to concurrently execute statement that uses view +# and statement that drops it. We use "user" locks as means to +# suspend execution of first statement once it opens our view. +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +# Switching to connection 'master1' +insert into v1 values (get_lock("lock_bg25144", 100)); +# Switching to connection 'master2' +drop view v1; +# Switching to connection 'master' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master1' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master2' +# Switching to connection 'master' +# Check that insertion through view did happen. +select * from t1; +i +1 +# Syncing slave with master and switching to connection 'slave' +# Check that slave was able to replicate this sequence +# which means that we got correct binlog order. +select * from t1; +i +1 +# Switching to connection 'master' +# Now we will repeat the test by trying concurrently execute +# statement that uses a view and statement that alters it. +create view v1 as select * from t1; +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +# Switching to connection 'master1' +insert into v1 values (get_lock("lock_bg25144", 100)); +# Switching to connection 'master2' +alter view v1 as select * from t2; +# Switching to connection 'master' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master1' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master2' +# Switching to connection 'master' +# Second insertion should go to t1 as well. +select * from t1; +i +1 +1 +select * from t2; +i +# Syncing slave with master and switching to connection 'slave' +# Now let us check that statements were logged in proper order +# So we have same result on slave. +select * from t1; +i +1 +1 +select * from t2; +i +# Switching to connection 'master' +drop table t1, t2; +drop view v1; +# Syncing slave with master and switching to connection 'slave' diff --git a/mysql-test/suite/rpl/t/rpl_view_multi.test b/mysql-test/suite/rpl/t/rpl_view_multi.test new file mode 100644 index 00000000000..777ccb2a945 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_view_multi.test @@ -0,0 +1,134 @@ +# +# This file contains test cases for bugs which involve views, several +# concurren connections and manifest themselves as wrong binary log +# sequence which results in broken replication. In principle we are +# mostly interested in SBR here but this test will also work with RBR. +# +--source include/master-slave.inc + +--echo # +--echo # Bug #25144 "replication / binlog with view breaks". +--echo # Statements that used views didn't ensure that view were not modified +--echo # during their execution. Indeed this led to incorrect binary log with +--echo # statement based logging and as result to broken replication. +--echo # + +--disable_warnings +drop tables if exists t1, t2; +drop view if exists v1; +--enable_warnings +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master + +connect (master2,127.0.0.1,root,,test,$MASTER_MYPORT,); + +--echo # Switching to connection 'master' +connection master; +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; + +--echo # First we try to concurrently execute statement that uses view +--echo # and statement that drops it. We use "user" locks as means to +--echo # suspend execution of first statement once it opens our view. +select get_lock("lock_bg25144", 1); + +--echo # Switching to connection 'master1' +connection master1; +--send insert into v1 values (get_lock("lock_bg25144", 100)) + +--echo # Switching to connection 'master2' +connection master2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send drop view v1 + +--echo # Switching to connection 'master' +connection master; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "drop view v1"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master1' +connection master1; +--reap +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master2' +connection master2; +--reap + +--echo # Switching to connection 'master' +connection master; +--echo # Check that insertion through view did happen. +select * from t1; +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master +--echo # Check that slave was able to replicate this sequence +--echo # which means that we got correct binlog order. +select * from t1; + +--echo # Switching to connection 'master' +connection master; +--echo # Now we will repeat the test by trying concurrently execute +--echo # statement that uses a view and statement that alters it. +create view v1 as select * from t1; + +select get_lock("lock_bg25144", 1); + +--echo # Switching to connection 'master1' +connection master1; +--send insert into v1 values (get_lock("lock_bg25144", 100)) + +--echo # Switching to connection 'master2' +connection master2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send alter view v1 as select * from t2 + +--echo # Switching to connection 'master' +connection master; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter view v1 as select * from t2"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master1' +connection master1; +--reap +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master2' +connection master2; +--reap + +--echo # Switching to connection 'master' +connection master; + +--echo # Second insertion should go to t1 as well. +select * from t1; +select * from t2; + +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master +--echo # Now let us check that statements were logged in proper order +--echo # So we have same result on slave. +select * from t1; +select * from t2; + +--echo # Switching to connection 'master' +connection master; +drop table t1, t2; +drop view v1; +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master diff --git a/mysql-test/t/view_multi.test b/mysql-test/t/view_multi.test deleted file mode 100644 index a61e7738095..00000000000 --- a/mysql-test/t/view_multi.test +++ /dev/null @@ -1,110 +0,0 @@ -# -# QQ: Should we find a better place for this test? -# May be binlog or rpl suites ? -# ---source include/have_log_bin.inc ---source include/have_binlog_format_mixed_or_statement.inc - -# -# Bug #25144 "replication / binlog with view breaks". -# Statements that used views didn't ensure that view were not modified -# during their execution. Indeed this led to incorrect binary log with -# statement based logging. -# ---disable_parsing -drop table if not exists t1, t2; -drop view if exists v1; ---enable_parsing - -# We are going to use binary log later to check that statements are -# logged in proper order, so it is good idea to reset it here. -reset master; - -connect (addconn1,localhost,root,,); -connect (addconn2,localhost,root,,); -connection default; - -create table t1 (i int); -create table t2 (i int); -create view v1 as select * from t1; - -# First we try to concurrently execute statement that uses view -# and statement that drops it. We use "user" locks as means to -# suspend execution of first statement once it opens our view. -select get_lock("lock_bg25144", 1); - -connection addconn1; ---send insert into v1 values (get_lock("lock_bg25144", 100)); - -connection addconn2; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "User lock" and info like "insert into v1 %lock_bg25144%"; ---source include/wait_condition.inc ---send drop view v1; - -connection default; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and info = "drop view v1"; ---source include/wait_condition.inc - -select release_lock("lock_bg25144"); - -connection addconn1; ---reap -select release_lock("lock_bg25144"); - -connection addconn2; ---reap - -connection default; -# Check that insertion through view did happen. -select * from t1; -# At the end of test we will check that statements were -# logged in proper order. - -# Now we will repeat the test by trying concurrently execute -# statement that uses a view and statement that alters it. -create view v1 as select * from t1; - -select get_lock("lock_bg25144", 1); - -connection addconn1; ---send insert into v1 values (get_lock("lock_bg25144", 100)); - -connection addconn2; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "User lock" and info like "insert into v1 %lock_bg25144%"; ---source include/wait_condition.inc ---send alter view v1 as select * from t2; - -connection default; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and - info = "alter view v1 as select * from t2"; ---source include/wait_condition.inc - -select release_lock("lock_bg25144"); - -connection addconn1; ---reap -select release_lock("lock_bg25144"); - -connection addconn2; ---reap - -connection default; - -# Second insertion should go to t1 as well. -select * from t1; -select * from t2; - -# Now let us check that statements were logged in proper order ---replace_column 2 # 5 # -show binlog events in 'master-bin.000001' from 107; - -drop table t1, t2; -drop view v1; From 6fd51b850268d333d2cdad3a574e6a601d5a091d Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 19:44:46 +0300 Subject: [PATCH 041/466] Backport of: ------------------------------------------------------------ revno: 2630.4.10 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Mon 2008-05-26 15:11:26 +0400 message: WL#3726 "DDL locking for all metadata objects". After review changes in progress. Implemented some renames suggested by reviewer. --- sql/mdl.cc | 113 ++++++++++++++++++++++++++++++----------------------- sql/mdl.h | 4 +- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 00dcc12cdc8..ddd027c4027 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -41,7 +41,13 @@ struct MDL_LOCK_DATA I_P_List active_shared_waiting_upgrade; I_P_List active_exclusive; I_P_List waiting_exclusive; - uint users; + /** + Number of MDL_LOCK objects associated with this MDL_LOCK_DATA instance + and therefore present in one of above lists. Note that this number + doesn't account for pending requests for shared lock since we don't + associate them with MDL_LOCK_DATA and don't keep them in any list. + */ + uint lock_count; void *cached_object; mdl_cached_object_release_hook cached_object_release_hook; @@ -57,9 +63,9 @@ struct MDL_LOCK_DATA active_exclusive.head() : waiting_exclusive.head())); } - bool has_no_other_users() + bool has_one_lock() { - return (users == 1); + return (lock_count == 1); } }; @@ -67,10 +73,21 @@ struct MDL_LOCK_DATA pthread_mutex_t LOCK_mdl; pthread_cond_t COND_mdl; HASH mdl_locks; -uint global_shared_locks_pending; -uint global_shared_locks_acquired; -uint global_intention_exclusive_locks_acquired; +/** + Structure implementing global metadata lock. The only types + of locks which are supported at the moment are shared and + intention exclusive locks. Note that the latter type of global + lock acquired automatically when one tries to acquire exclusive + or shared upgradable lock on particular object. +*/ + +struct MDL_GLOBAL_LOCK_DATA +{ + uint shared_pending; + uint shared_acquired; + uint intention_exclusive_acquired; +} global_lock; extern "C" uchar *mdl_locks_key(const uchar *record, size_t *length, @@ -106,8 +123,8 @@ void mdl_init() pthread_cond_init(&COND_mdl, NULL); my_hash_init(&mdl_locks, &my_charset_bin, 16 /* FIXME */, 0, 0, mdl_locks_key, 0, 0); - global_shared_locks_pending= global_shared_locks_acquired= 0; - global_intention_exclusive_locks_acquired= 0; + global_lock.shared_pending= global_lock.shared_acquired= 0; + global_lock.intention_exclusive_acquired= 0; } @@ -261,7 +278,7 @@ void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db, mdl->type= MDL_SHARED; mdl->state= MDL_PENDING; mdl->prio= MDL_NORMAL_PRIO; - mdl->upgradable= FALSE; + mdl->is_upgradable= FALSE; #ifndef DBUG_OFF mdl->ctx= 0; mdl->lock_data= 0; @@ -362,7 +379,7 @@ void mdl_remove_all_locks(MDL_CONTEXT *context) /* Reset lock request back to its initial state. */ l->type= MDL_SHARED; l->prio= MDL_NORMAL_PRIO; - l->upgradable= FALSE; + l->is_upgradable= FALSE; #ifndef DBUG_OFF l->ctx= 0; #endif @@ -424,7 +441,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) safe_mutex_assert_not_owner(&LOCK_open); - if (l->ctx->has_global_shared_lock && l->upgradable) + if (l->ctx->has_global_shared_lock && l->is_upgradable) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; @@ -432,8 +449,8 @@ bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) pthread_mutex_lock(&LOCK_mdl); - if (l->upgradable && - (global_shared_locks_acquired || global_shared_locks_pending)) + if (l->is_upgradable && + (global_lock.shared_acquired || global_lock.shared_pending)) { pthread_mutex_unlock(&LOCK_mdl); *retry= TRUE; @@ -445,12 +462,12 @@ bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) { lock_data= get_lock_data_object(); lock_data->active_shared.push_front(l); - lock_data->users= 1; + lock_data->lock_count= 1; my_hash_insert(&mdl_locks, (uchar*)lock_data); l->state= MDL_ACQUIRED; l->lock_data= lock_data; - if (l->upgradable) - global_intention_exclusive_locks_acquired++; + if (l->is_upgradable) + global_lock.intention_exclusive_acquired++; } else { @@ -467,11 +484,11 @@ bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) ALTER VIEW ... AS .... */ lock_data->active_shared.push_front(l); - lock_data->users++; + lock_data->lock_count++; l->state= MDL_ACQUIRED; l->lock_data= lock_data; - if (l->upgradable) - global_intention_exclusive_locks_acquired++; + if (l->is_upgradable) + global_lock.intention_exclusive_acquired++; } else *retry= TRUE; @@ -535,14 +552,14 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { lock_data= get_lock_data_object(); lock_data->waiting_exclusive.push_front(l); - lock_data->users= 1; + lock_data->lock_count= 1; my_hash_insert(&mdl_locks, (uchar*)lock_data); l->lock_data= lock_data; } else { lock_data->waiting_exclusive.push_front(l); - lock_data->users++; + lock_data->lock_count++; l->lock_data= lock_data; } } @@ -554,7 +571,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { lock_data= l->lock_data; - if (global_shared_locks_acquired || global_shared_locks_pending) + if (global_lock.shared_acquired || global_lock.shared_pending) { /* There is active or pending global shared lock we have @@ -607,7 +624,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) /* Return lock request to its initial state. */ l->type= MDL_SHARED; l->prio= MDL_NORMAL_PRIO; - l->upgradable= FALSE; + l->is_upgradable= FALSE; context->locks.remove(l); } /* Pending requests for shared locks can be satisfied now. */ @@ -619,7 +636,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) it.rewind(); while ((l= it++)) { - global_intention_exclusive_locks_acquired++; + global_lock.intention_exclusive_acquired++; lock_data= l->lock_data; lock_data->waiting_exclusive.remove(l); lock_data->active_exclusive.push_front(l); @@ -685,7 +702,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, { DBUG_PRINT("info", ("found shared lock for upgrade")); DBUG_ASSERT(l->state == MDL_ACQUIRED); - DBUG_ASSERT(l->upgradable); + DBUG_ASSERT(l->is_upgradable); l->state= MDL_PENDING_UPGRADE; lock_data= l->lock_data; lock_data->active_shared.remove(l); @@ -704,8 +721,8 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, lock_data= l->lock_data; - DBUG_ASSERT(global_shared_locks_acquired == 0 && - global_intention_exclusive_locks_acquired); + DBUG_ASSERT(global_lock.shared_acquired == 0 && + global_lock.intention_exclusive_acquired); if ((lh= lock_data->active_shared.head())) { @@ -815,12 +832,12 @@ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *l) { lock_data= get_lock_data_object(); lock_data->active_exclusive.push_front(l); - lock_data->users= 1; + lock_data->lock_count= 1; my_hash_insert(&mdl_locks, (uchar*)lock_data); l->state= MDL_ACQUIRED; l->lock_data= lock_data; lock_data= 0; - global_intention_exclusive_locks_acquired++; + global_lock.intention_exclusive_acquired++; } pthread_mutex_unlock(&LOCK_mdl); @@ -842,7 +859,7 @@ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *l) Holding this lock will block all requests for exclusive locks and shared locks which can be potentially upgraded to exclusive - (see MDL_LOCK::upgradable). + (see MDL_LOCK::is_upgradable). @param context Current metadata locking context. @@ -861,20 +878,20 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) pthread_mutex_lock(&LOCK_mdl); - global_shared_locks_pending++; + global_lock.shared_pending++; old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); - while (!thd->killed && global_intention_exclusive_locks_acquired) + while (!thd->killed && global_lock.intention_exclusive_acquired) pthread_cond_wait(&COND_mdl, &LOCK_mdl); - global_shared_locks_pending--; + global_lock.shared_pending--; if (thd->killed) { /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); return TRUE; } - global_shared_locks_acquired++; + global_lock.shared_acquired++; context->has_global_shared_lock= TRUE; /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); @@ -927,8 +944,8 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) while ((l= it++)) { DBUG_ASSERT(l->state == MDL_PENDING); - if ((l->upgradable || l->type == MDL_EXCLUSIVE) && - (global_shared_locks_acquired || global_shared_locks_pending)) + if ((l->is_upgradable || l->type == MDL_EXCLUSIVE) && + (global_lock.shared_acquired || global_lock.shared_pending)) break; /* To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. @@ -968,7 +985,7 @@ static void release_lock(MDL_LOCK *l) l->key + 4 + strlen(l->key + 4) + 1)); lock_data= l->lock_data; - if (lock_data->has_no_other_users()) + if (lock_data->has_one_lock()) { my_hash_delete(&mdl_locks, (uchar *)lock_data); DBUG_PRINT("info", ("releasing cached_object cached_object=%p", @@ -977,8 +994,8 @@ static void release_lock(MDL_LOCK *l) (*lock_data->cached_object_release_hook)(lock_data->cached_object); release_lock_data_object(lock_data); if (l->type == MDL_EXCLUSIVE && l->state == MDL_ACQUIRED || - l->type == MDL_SHARED && l->state == MDL_ACQUIRED && l->upgradable) - global_intention_exclusive_locks_acquired--; + l->type == MDL_SHARED && l->state == MDL_ACQUIRED && l->is_upgradable) + global_lock.intention_exclusive_acquired--; } else { @@ -986,8 +1003,8 @@ static void release_lock(MDL_LOCK *l) { case MDL_SHARED: lock_data->active_shared.remove(l); - if (l->upgradable) - global_intention_exclusive_locks_acquired--; + if (l->is_upgradable) + global_lock.intention_exclusive_acquired--; break; case MDL_EXCLUSIVE: if (l->state == MDL_PENDING) @@ -995,14 +1012,14 @@ static void release_lock(MDL_LOCK *l) else { lock_data->active_exclusive.remove(l); - global_intention_exclusive_locks_acquired--; + global_lock.intention_exclusive_acquired--; } break; default: /* TODO Really? How about problems during lock upgrade ? */ DBUG_ASSERT(0); } - lock_data->users--; + lock_data->lock_count--; } DBUG_VOID_RETURN; @@ -1094,7 +1111,7 @@ void mdl_release_exclusive_locks(MDL_CONTEXT *context) /* Return lock request to its initial state. */ l->type= MDL_SHARED; l->prio= MDL_NORMAL_PRIO; - l->upgradable= FALSE; + l->is_upgradable= FALSE; context->locks.remove(l); } } @@ -1128,7 +1145,7 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lr) /* Return lock request to its initial state. */ lr->type= MDL_SHARED; lr->prio= MDL_NORMAL_PRIO; - lr->upgradable= FALSE; + lr->is_upgradable= FALSE; context->locks.remove(lr); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); @@ -1155,8 +1172,8 @@ void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context) if (l->type == MDL_EXCLUSIVE) { DBUG_ASSERT(l->state == MDL_ACQUIRED); - if (!l->upgradable) - global_intention_exclusive_locks_acquired--; + if (!l->is_upgradable) + global_lock.intention_exclusive_acquired--; lock_data= l->lock_data; lock_data->active_exclusive.remove(l); l->type= MDL_SHARED; @@ -1179,7 +1196,7 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) DBUG_ASSERT(context->has_global_shared_lock); pthread_mutex_lock(&LOCK_mdl); - global_shared_locks_acquired--; + global_lock.shared_acquired--; context->has_global_shared_lock= FALSE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); diff --git a/sql/mdl.h b/sql/mdl.h index ca3d8d0a784..2b144e250b1 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -66,7 +66,7 @@ struct MDL_LOCK point might be upgraded to an exclusive lock and therefore conflicts with global shared lock, FALSE -- otherwise. */ - bool upgradable; + bool is_upgradable; private: /** @@ -189,7 +189,7 @@ inline void mdl_set_lock_priority(MDL_LOCK *lock, enum_mdl_prio prio) inline void mdl_set_upgradable(MDL_LOCK *lock) { DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING); - lock->upgradable= TRUE; + lock->is_upgradable= TRUE; } bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry); From 3226c107fbf0a5533411b834daec4b06048b36ad Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 22:03:37 +0300 Subject: [PATCH 042/466] Backport of: ------------------------------------------------------------ revno: 2630.4.11 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Tue 2008-05-27 21:31:53 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Changed mysql_lock_tables() to be no longer responsible for reopening table if waiting for the lock on it was aborted. This allows to get rid of several annoying functions. --- sql/ha_ndbcluster_binlog.cc | 2 +- sql/lock.cc | 64 +++++----- sql/log_event_old.cc | 3 +- sql/mysql_priv.h | 19 ++- sql/sql_base.cc | 246 ++++++------------------------------ sql/sql_handler.cc | 3 +- sql/sql_insert.cc | 94 ++++++++++---- sql/sql_update.cc | 4 +- 8 files changed, 152 insertions(+), 283 deletions(-) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index bf76960201d..f272526edb8 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -2391,7 +2391,7 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) goto add_ndb_binlog_index_err; } - if (lock_tables(thd, &binlog_tables, 1, &need_reopen)) + if (lock_tables(thd, &binlog_tables, 1, 0, &need_reopen)) { if (need_reopen) { diff --git a/sql/lock.cc b/sql/lock.cc index 0c8c3095844..38b2d22f91f 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -94,31 +94,6 @@ static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error, const char *); -/* - Lock tables. - - SYNOPSIS - mysql_lock_tables() - thd The current thread. - tables An array of pointers to the tables to lock. - count The number of tables to lock. - flags Options: - MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock - MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY - MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables. - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered - or dropped tables by itself, - mysql_lock_tables() should - notify upper level and rely - on caller doing this. - need_reopen Out parameter, TRUE if some tables were altered - or deleted and should be reopened by caller. - - RETURN - A lock structure pointer on success. - NULL on error or if some tables should be reopen. -*/ - /* Map the return value of thr_lock to an error from errmsg.txt */ static int thr_lock_errno_to_mysql[]= { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; @@ -247,6 +222,28 @@ static void reset_lock_data_and_free(MYSQL_LOCK **mysql_lock) } +/** + Lock tables. + + @param thd The current thread. + @param tables An array of pointers to the tables to lock. + @param count The number of tables to lock. + @param flags Options: + MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY + MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables. + @param need_reopen Out parameter, TRUE if some tables were altered + or deleted and should be reopened by caller. + + @note Caller of this function should always be ready to handle request to + reopen table unless there are external invariants which guarantee + that such thing won't be needed (for example we are obtaining lock + on table on which we already have exclusive metadata lock). + + @retval A lock structure pointer on success. + @retval NULL on error or if some tables should be reopen. +*/ + MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags, bool *need_reopen) { @@ -330,7 +327,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, my_error(rc, MYF(0)); break; } - else if (rc == 1) /* aborted */ + else if (rc == 1) /* aborted or killed */ { thd->some_tables_deleted=1; // Try again sql_lock->lock_count= 0; // Locks are already freed @@ -339,8 +336,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH)) { /* - Thread was killed or lock aborted. Let upper level close all - used tables and retry or give error. + Success and nobody set thd->some_tables_deleted to force reopen + or we were called with MYSQL_LOCK_IGNORE_FLUSH so such attempts + should be ignored. */ break; } @@ -366,13 +364,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, */ reset_lock_data_and_free(&sql_lock); retry: - if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN) - { - *need_reopen= TRUE; - break; - } - if (wait_for_tables(thd)) - break; // Couldn't open tables + /* Let upper level close all used tables and retry or give error. */ + *need_reopen= TRUE; + break; } thd_proc_info(thd, 0); if (thd->killed) diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 030d51b3618..3dcf19f6b32 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1461,7 +1461,8 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) lex_start(thd); while ((error= lock_tables(thd, rli->tables_to_lock, - rli->tables_to_lock_count, &need_reopen))) + rli->tables_to_lock_count, 0, + &need_reopen))) { if (!need_reopen) { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index be03759b383..ab692c3ab22 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1241,8 +1241,6 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table); void close_data_files_and_morph_locks(THD *thd, const char *db, const char *table_name); void close_handle_and_leave_table_as_lock(TABLE *table); -bool wait_for_tables(THD *thd); -bool table_is_used(TABLE *table, bool wait_for_name_lock); void unlock_locked_tables(THD *thd); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); @@ -1476,22 +1474,24 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond); int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags); /* open_and_lock_tables with optional derived handling */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, + uint flags); /* simple open_and_lock_tables without derived handling */ inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { - return open_and_lock_tables_derived(thd, tables, FALSE); + return open_and_lock_tables_derived(thd, tables, FALSE, 0); } /* open_and_lock_tables with derived handling */ inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) { - return open_and_lock_tables_derived(thd, tables, TRUE); + return open_and_lock_tables_derived(thd, tables, TRUE, 0); } /* simple open_and_lock_tables without derived handling for single table */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, thr_lock_type lock_type); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); -int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen); +int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags, + bool *need_reopen); int decide_logging_format(THD *thd, TABLE_LIST *tables); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); @@ -2045,10 +2045,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, /* mysql_lock_tables() and open_table() flags bits */ #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001 #define MYSQL_LOCK_IGNORE_FLUSH 0x0002 -#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004 -#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008 -#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010 -#define MYSQL_LOCK_PERF_SCHEMA 0x0020 +#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 +#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 +#define MYSQL_LOCK_PERF_SCHEMA 0x0010 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index de4aaac633e..6b0efadb712 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -127,8 +127,6 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); -static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, - bool send_refresh); static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context); static bool has_write_table_with_auto_increment(TABLE_LIST *tables); @@ -3439,9 +3437,6 @@ bool reopen_tables(THD *thd, bool get_locks) TABLE *err_tables= NULL, *err_tab_tmp; bool error=0, not_used; bool merge_table_found= FALSE; - const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN | - MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | - MYSQL_LOCK_IGNORE_FLUSH; DBUG_ENTER("reopen_tables"); @@ -3534,10 +3529,14 @@ bool reopen_tables(THD *thd, bool get_locks) if (tables != tables_ptr) // Should we get back old locks { MYSQL_LOCK *lock; + const uint flags= MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | + MYSQL_LOCK_IGNORE_FLUSH; /* - We should always get these locks. Anyway, we must not go into - wait_for_tables() as it tries to acquire LOCK_open, which is - already locked. + Since we have exclusive metadata locks on tables which we + are reopening we should always get these locks (We won't + wait on table level locks so can't get aborted and we ignore + other threads that set THD::some_tables_deleted by using + MYSQL_LOCK_IGNORE_FLUSH flag). */ thd->some_tables_deleted=0; if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), @@ -3565,165 +3564,6 @@ bool reopen_tables(THD *thd, bool get_locks) } -/** - Close handlers for tables in list, but leave the TABLE structure - intact so that we can re-open these quickly. - - @param thd Thread context - @param table Head of the list of TABLE objects - @param morph_locks TRUE - remove locks which we have on tables being closed - but ensure that no DML or DDL will sneak in before - we will re-open the table (i.e. temporarily morph - our table-level locks into name-locks). - FALSE - otherwise - @param send_refresh Should we awake waiters even if we didn't close any tables? -*/ - -static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, - bool send_refresh) -{ - bool found= send_refresh; - DBUG_ENTER("close_old_data_files"); - - for (; table ; table=table->next) - { - DBUG_PRINT("tcache", ("checking table: '%s'.'%s' 0x%lx", - table->s->db.str, table->s->table_name.str, - (long) table)); - DBUG_PRINT("tcache", ("needs refresh: %d is open: %u", - table->needs_reopen_or_name_lock(), table->db_stat)); - /* - Reopen marked for flush. - */ - if (table->needs_reopen_or_name_lock()) - { - found=1; - if (table->db_stat) - { - if (morph_locks) - { - /* - Forward lock handling to MERGE parent. But unlock parent - once only. - */ - TABLE *ulcktbl= table->parent ? table->parent : table; - if (ulcktbl->lock_count) - { - /* - Wake up threads waiting for table-level lock on this table - so they won't sneak in when we will temporarily remove our - lock on it. This will also give them a chance to close their - instances of this table. - */ - mysql_lock_abort(thd, ulcktbl, TRUE); - mysql_lock_remove(thd, thd->locked_tables, ulcktbl, TRUE); - ulcktbl->lock_count= 0; - } - if ((ulcktbl != table) && ulcktbl->db_stat) - { - /* - Close the parent too. Note that parent can come later in - the list of tables. It will then be noticed as closed and - as a placeholder. When this happens, do not clear the - placeholder flag. See the branch below ("***"). - */ - ulcktbl->open_placeholder= 1; - close_handle_and_leave_table_as_lock(ulcktbl); - } - /* - We want to protect the table from concurrent DDL operations - (like RENAME TABLE) until we will re-open and re-lock it. - */ - table->open_placeholder= 1; - } - close_handle_and_leave_table_as_lock(table); - } - else if (table->open_placeholder && !morph_locks) - { - /* - We come here only in close-for-back-off scenario. So we have to - "close" create placeholder here to avoid deadlocks (for example, - in case of concurrent execution of CREATE TABLE t1 SELECT * FROM t2 - and RENAME TABLE t2 TO t1). In close-for-re-open scenario we will - probably want to let it stay. - - Note "***": We must not enter this branch if the placeholder - flag has been set because of a former close through a child. - See above the comment that refers to this note. - */ - table->open_placeholder= 0; - } - } - } - if (found) - broadcast_refresh(); - DBUG_VOID_RETURN; -} - - -/* - Wait until all threads has closed the tables in the list - We have also to wait if there is thread that has a lock on this table even - if the table is closed -*/ - -bool table_is_used(TABLE *table, bool wait_for_name_lock) -{ - DBUG_ENTER("table_is_used"); - do - { - char *key= table->s->table_cache_key.str; - uint key_length= table->s->table_cache_key.length; - /* Note that 'table' can use artificial TABLE_SHARE object. */ - TABLE_SHARE *share= (TABLE_SHARE*)my_hash_search(&table_def_cache, - (uchar*) key, key_length); - if (share && !share->used_tables.is_empty() && - share->version != refresh_version) - DBUG_RETURN(1); - } while ((table=table->next)); - DBUG_RETURN(0); -} - - -/* - Wait until all used tables are refreshed. - - FIXME We should remove this function since for several functions which - are invoked by it new scenarios of usage are introduced, while - this function implements optimization useful only in rare cases. -*/ - -bool wait_for_tables(THD *thd) -{ - bool result; - DBUG_ENTER("wait_for_tables"); - - thd_proc_info(thd, "Waiting for tables"); - pthread_mutex_lock(&LOCK_open); - while (!thd->killed) - { - thd->some_tables_deleted=0; - close_old_data_files(thd,thd->open_tables,0,dropping_tables != 0); - mysql_ha_flush(thd); - if (!table_is_used(thd->open_tables,1)) - break; - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - if (thd->killed) - result= 1; // aborted - else - { - /* Now we can open all tables without any interference */ - thd_proc_info(thd, "Reopen tables"); - thd->version= refresh_version; - result=reopen_tables(thd, 0); - } - pthread_mutex_unlock(&LOCK_open); - thd_proc_info(thd, 0); - DBUG_RETURN(result); -} - - /** Unlock and close tables open and locked by LOCK TABLES statement. @@ -5190,14 +5030,8 @@ retry: DBUG_ASSERT(thd->lock == 0); // You must lock everything at once if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, - (lock_flags | - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN), - &refresh))) + lock_flags, &refresh))) { - /* - FIXME: Actually we should get rid of MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN option - as all reopening should happen outside of mysql_lock_tables() code. - */ if (refresh) { close_thread_tables(thd); @@ -5222,6 +5056,8 @@ retry: open_and_lock_tables_derived() thd - thread handler tables - list of tables for open&locking + flags - set of options to be used to open and lock tables (see + open_tables() and mysql_lock_tables() for details). derived - if to handle derived tables RETURN @@ -5239,7 +5075,8 @@ retry: the third argument set appropriately. */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, + uint flags) { uint counter; bool need_reopen; @@ -5248,7 +5085,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) for ( ; ; ) { - if (open_tables(thd, &tables, &counter, 0)) + if (open_tables(thd, &tables, &counter, flags)) DBUG_RETURN(-1); DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { @@ -5257,7 +5094,8 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) my_sleep(6000000); thd->proc_info= old_proc_info;}); - if (!lock_tables(thd, tables, counter, &need_reopen)) + if (!lock_tables(thd, tables, counter, flags, + &need_reopen)) break; if (!need_reopen) DBUG_RETURN(-1); @@ -5492,6 +5330,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) thd Thread handler tables Tables to lock count Number of opened tables + flags Options (see mysql_lock_tables() for details) need_reopen Out parameter which if TRUE indicates that some tables were dropped or altered during this call and therefore invoker should reopen tables and @@ -5512,7 +5351,8 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) -1 Error */ -int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) +int lock_tables(THD *thd, TABLE_LIST *tables, uint count, + uint flags, bool *need_reopen) { TABLE_LIST *table; @@ -5540,7 +5380,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; - uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN; if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) DBUG_RETURN(-1); @@ -5573,7 +5412,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) DEBUG_SYNC(thd, "before_lock_tables_takes_lock"); if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), - lock_flag, need_reopen))) + flags, need_reopen))) { if (thd->lex->requires_prelocking()) { @@ -9190,41 +9029,38 @@ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, Open_tables_state *backup) { + Query_tables_list query_tables_list_backup; + LEX *lex= thd->lex; + DBUG_ENTER("open_system_tables_for_read"); alloc_mdl_locks(table_list, thd->mem_root); + /* + Besides using new Open_tables_state for opening system tables, + we also have to backup and reset/and then restore part of LEX + which is accessed by open_tables() in order to determine if + prelocking is needed and what tables should be added for it. + close_system_tables() doesn't require such treatment. + */ + lex->reset_n_backup_query_tables_list(&query_tables_list_backup); thd->reset_n_backup_open_tables_state(backup); - uint count= 0; - enum_open_table_action not_used; - bool not_used_2; + if (open_and_lock_tables_derived(thd, table_list, FALSE, + MYSQL_LOCK_IGNORE_FLUSH)) + { + lex->restore_backup_query_tables_list(&query_tables_list_backup); + goto error; + } + for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) { - TABLE *table= open_table(thd, tables, thd->mem_root, ¬_used, - MYSQL_LOCK_IGNORE_FLUSH); - if (!table) - goto error; - - DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM); - - table->use_all_columns(); - table->reginfo.lock_type= tables->lock_type; - tables->table= table; - count++; + DBUG_ASSERT(tables->table->s->table_category == TABLE_CATEGORY_SYSTEM); + tables->table->use_all_columns(); } + lex->restore_backup_query_tables_list(&query_tables_list_backup); - { - TABLE **list= (TABLE**) thd->alloc(sizeof(TABLE*) * count); - TABLE **ptr= list; - for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) - *(ptr++)= tables->table; - - thd->lock= mysql_lock_tables(thd, list, count, - MYSQL_LOCK_IGNORE_FLUSH, ¬_used_2); - } - if (thd->lock) - DBUG_RETURN(FALSE); + DBUG_RETURN(FALSE); error: close_system_tables(thd, backup); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index c8a66073a67..5c034a0452a 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -519,8 +519,7 @@ retry: */ thd->open_tables= thd->handler_tables; - lock= mysql_lock_tables(thd, &tables->table, 1, - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen); + lock= mysql_lock_tables(thd, &tables->table, 1, 0, &need_reopen); /* restore previous context */ thd->open_tables= backup_open_tables; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eb4eee9abb5..525f6b9a4d8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1817,6 +1817,7 @@ public: inline uint lock_count() { return locks_in_memory; } TABLE* get_local_table(THD* client_thd); + bool open_and_lock_table(); bool handle_inserts(void); }; @@ -2292,6 +2293,42 @@ void kill_delayed_threads(void) } +/** + Open and lock table for use by delayed thread and check that + this table is suitable for delayed inserts. + + @retval FALSE - Success. + @retval TRUE - Failure. +*/ + +bool Delayed_insert::open_and_lock_table() +{ + if (!(table= open_n_lock_single_table(&thd, &table_list, + TL_WRITE_DELAYED))) + { + thd.fatal_error(); // Abort waiting inserts + return TRUE; + } + if (!(table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) + { + my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR), + table_list.table_name); + return TRUE; + } + if (table->triggers) + { + /* + Table has triggers. This is not an error, but we do + not support triggers with delayed insert. Terminate the delayed + thread without an error and thus request lock upgrade. + */ + return TRUE; + } + table->copy_blobs= 1; + return FALSE; +} + + /* * Create a new delayed insert thread */ @@ -2354,29 +2391,8 @@ pthread_handler_t handle_delayed_insert(void *arg) alloc_mdl_locks(&di->table_list, thd->mem_root); - /* Open table */ - if (!(di->table= open_n_lock_single_table(thd, &di->table_list, - TL_WRITE_DELAYED))) - { - thd->fatal_error(); // Abort waiting inserts + if (di->open_and_lock_table()) goto err; - } - if (!(di->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) - { - my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR), - di->table_list.table_name); - goto err; - } - if (di->table->triggers) - { - /* - Table has triggers. This is not an error, but we do - not support triggers with delayed insert. Terminate the delayed - thread without an error and thus request lock upgrade. - */ - goto err; - } - di->table->copy_blobs=1; /* Tell client that the thread is initialized */ pthread_cond_signal(&di->cond_client); @@ -2450,7 +2466,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (di->tables_in_use && ! thd->lock) { - bool not_used; + bool need_reopen; /* Request for new delayed insert. Lock the table, but avoid to be blocked by a global read lock. @@ -2463,11 +2479,29 @@ pthread_handler_t handle_delayed_insert(void *arg) */ if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK, - ¬_used))) + &need_reopen))) { - /* Fatal error */ - di->dead= 1; - thd->killed= THD::KILL_CONNECTION; + if (need_reopen) + { + /* + We were waiting to obtain TL_WRITE_DELAYED (probably due to + someone having or requesting TL_WRITE_ALLOW_READ) and got + aborted. Try to reopen table and if it fails die. + */ + close_thread_tables(thd); + di->table= 0; + if (di->open_and_lock_table()) + { + di->dead= 1; + thd->killed= THD::KILL_CONNECTION; + } + } + else + { + /* Fatal error */ + di->dead= 1; + thd->killed= THD::KILL_CONNECTION; + } } pthread_cond_broadcast(&di->cond_client); } @@ -3533,6 +3567,12 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, table->reginfo.lock_type=TL_WRITE; hooks->prelock(&table, 1); // Call prelock hooks + /* + mysql_lock_tables() below should never fail with request to reopen table + since it won't wait for the table lock (we have exclusive metadata lock on + the table) and thus can't get aborted and since it ignores other threads + setting THD::some_tables_deleted thanks to MYSQL_LOCK_IGNORE_FLUSH. + */ if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH, ¬_used)) || hooks->postlock(&table, 1)) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1ad39a2f838..5cf6ceaa394 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -222,7 +222,7 @@ int mysql_update(THD *thd, /* convert to multiupdate */ DBUG_RETURN(2); } - if (!lock_tables(thd, table_list, table_count, &need_reopen)) + if (!lock_tables(thd, table_list, table_count, 0, &need_reopen)) break; if (!need_reopen) DBUG_RETURN(1); @@ -1099,7 +1099,7 @@ reopen_tables: /* now lock and fill tables */ if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, &need_reopen)) + lock_tables(thd, table_list, table_count, 0, &need_reopen)) { if (!need_reopen) DBUG_RETURN(TRUE); From b6c33a9a63a13d37822c0439bafb0daf9b2f3b5a Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 22:11:32 +0300 Subject: [PATCH 043/466] Backport of: ------------------------------------------------------------ revno: 2630.6.1 committer: Konstantin Osipov branch nick: mysql-6.0-3726 timestamp: Tue 2008-05-27 13:45:34 +0400 message: Remove an unused argument from release_table_share(). Remove unused members from TABLE_SHARE struct. Review comments in scope of WL#3726 "DDL locking for all metadata objects" --- sql/mysql_priv.h | 2 +- sql/sql_base.cc | 52 +++++++++++++++++++----------------------------- sql/sql_plist.h | 2 +- sql/sql_show.cc | 2 +- sql/sql_table.cc | 2 +- sql/sql_view.cc | 2 +- sql/table.cc | 10 +--------- sql/table.h | 2 -- 8 files changed, 26 insertions(+), 48 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ab692c3ab22..063b36c4ddc 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1216,7 +1216,7 @@ uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list, bool tmp_table); TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, uint key_length, uint db_flags, int *error); -void release_table_share(TABLE_SHARE *share, enum release_type type); +void release_table_share(TABLE_SHARE *share); TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, uint lock_flags); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6b0efadb712..f963d74102b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -651,29 +651,17 @@ static TABLE_SHARE db_flags, error)); } +/** + Mark that we are not using table share anymore. -/* - Mark that we are not using table share anymore. + @param share Table share - SYNOPSIS - release_table_share() - share Table share - release_type How the release should be done: - RELEASE_NORMAL - - Release without checking - RELEASE_WAIT_FOR_DROP - - Don't return until we get a signal that the - table is deleted or the thread is killed. - - IMPLEMENTATION - If ref_count goes to zero and (we have done a refresh or if we have - already too many open table shares) then delete the definition. - - If type == RELEASE_WAIT_FOR_DROP then don't return until we get a signal - that the table is deleted or the thread is killed. + If the share has no open tables and (we have done a refresh or + if we have already too many open table shares) then delete the + definition. */ -void release_table_share(TABLE_SHARE *share, enum release_type type) +void release_table_share(TABLE_SHARE *share) { bool to_be_deleted= 0; DBUG_ENTER("release_table_share"); @@ -818,7 +806,7 @@ void close_handle_and_leave_table_as_lock(TABLE *table) table->file->close(); table->db_stat= 0; // Mark file closed table_def_change_share(table, share); - release_table_share(table->s, RELEASE_NORMAL); + release_table_share(table->s); table->s= share; table->file->change_table_ptr(table, table->s); @@ -2568,7 +2556,7 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists) void table_share_release_hook(void *share) { pthread_mutex_lock(&LOCK_open); - release_table_share((TABLE_SHARE*)share, RELEASE_NORMAL); + release_table_share((TABLE_SHARE*) share); broadcast_refresh(); pthread_mutex_unlock(&LOCK_open); } @@ -2902,7 +2890,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, goto err_unlock; /* TODO: Don't free this */ - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); if (flags & OPEN_VIEW_NO_PARSE) { @@ -2978,7 +2966,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { if (action) *action= OT_BACK_OFF_AND_RETRY; - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -2991,7 +2979,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table= share->free_tables.head(); table_def_use_table(thd, table); /* We need to release share as we have EXTRA reference to it in our hands. */ - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); } else { @@ -3095,7 +3083,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(table); err_unlock: - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); err_unlock2: pthread_mutex_unlock(&LOCK_open); mdl_release_lock(&thd->mdl_context, mdl_lock); @@ -3758,13 +3746,13 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, flags, thd->open_options, ¬_used, table_list, mem_root)) { - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); pthread_mutex_unlock(&LOCK_open); return FALSE; } my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "VIEW"); - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); err: pthread_mutex_unlock(&LOCK_open); return TRUE; @@ -3838,7 +3826,7 @@ retry: if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) goto err; /* Attempt to reopen view will bring havoc to upper layers anyway. */ - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "BASE TABLE"); DBUG_RETURN(1); @@ -3871,7 +3859,7 @@ retry: if (share->ref_count != 1) goto err; - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); if (ha_create_table_from_engine(thd, table_list->db, table_list->table_name)) @@ -3887,7 +3875,7 @@ retry: entry->s->version= 0; /* TODO: We don't need to release share here. */ - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); pthread_mutex_unlock(&LOCK_open); error= (int)auto_repair_table(thd, table_list); pthread_mutex_lock(&LOCK_open); @@ -3907,7 +3895,7 @@ retry: DBUG_RETURN(0); err: - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); DBUG_RETURN(1); } @@ -4031,7 +4019,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) pthread_mutex_lock(&LOCK_open); end_with_lock_open: - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); pthread_mutex_unlock(&LOCK_open); return result; } diff --git a/sql/sql_plist.h b/sql/sql_plist.h index af2ed227ea1..b05a6318f0f 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -62,7 +62,7 @@ class I_P_List public: I_P_List() : first(NULL) { }; inline void empty() { first= NULL; } - inline bool is_empty() { return (first == NULL); } + inline bool is_empty() const { return (first == NULL); } inline void push_front(T* a) { *B::next_ptr(a)= first; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c83a6981166..236bca76c7d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3206,7 +3206,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, } err_share: - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); err_unlock: pthread_mutex_unlock(&LOCK_open); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f91bae8b76c..640cd71fc62 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4469,7 +4469,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE)) { - release_table_share(share, RELEASE_NORMAL); + release_table_share(share); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); // Out of memory } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index a0acde600c7..3dc5ca4e09c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1647,7 +1647,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) share->ref_count++; share->version= 0; pthread_mutex_unlock(&share->mutex); - release_table_share(share, RELEASE_WAIT_FOR_DROP); + release_table_share(share); } query_cache_invalidate3(thd, view, 0); sp_cache_invalidate(); diff --git a/sql/table.cc b/sql/table.cc index 181014df9f4..b232ba23a89 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -321,7 +321,6 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST); - pthread_cond_init(&share->cond, NULL); } DBUG_RETURN(share); } @@ -418,16 +417,9 @@ void free_table_share(TABLE_SHARE *share) */ if (share->tmp_table == NO_TMP_TABLE) { - /* share->mutex is locked in release_table_share() */ - while (share->waiting_on_cond) - { - pthread_cond_broadcast(&share->cond); - pthread_cond_wait(&share->cond, &share->mutex); - } /* No thread refers to this anymore */ pthread_mutex_unlock(&share->mutex); pthread_mutex_destroy(&share->mutex); - pthread_cond_destroy(&share->cond); } my_hash_free(&share->name_hash); @@ -2001,7 +1993,7 @@ int closefrm(register TABLE *table, bool free_share) if (free_share) { if (table->s->tmp_table == NO_TMP_TABLE) - release_table_share(table->s, RELEASE_NORMAL); + release_table_share(table->s); else free_table_share(table->s); } diff --git a/sql/table.h b/sql/table.h index b0598e07299..016a99e5452 100644 --- a/sql/table.h +++ b/sql/table.h @@ -313,7 +313,6 @@ struct TABLE_SHARE TYPELIB fieldnames; /* Pointer to fieldnames */ TYPELIB *intervals; /* pointer to interval info */ pthread_mutex_t mutex; /* For locking the share */ - pthread_cond_t cond; /* To signal that share is ready */ TABLE_SHARE *next, **prev; /* Link to unused shares */ /* @@ -410,7 +409,6 @@ struct TABLE_SHARE bool crashed; bool is_view; bool name_lock, replace_with_name_lock; - bool waiting_on_cond; /* Protection against free */ ulong table_map_id; /* for row-based replication */ ulonglong table_map_version; From 511c68fbd461efecd5bd9f7c08b42916fa8416c9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 22:38:25 +0300 Subject: [PATCH 044/466] Backport of: ------------------------------------------------------------------- revno: 2630.6.6 committer: Konstantin Osipov branch nick: mysql-6.0-3726 timestamp: Tue 2008-05-27 16:15:44 +0400 message: Implement code review fixes for WL#3726 "DDL locking for all metadata objects": cleanup the code from share->mutex acquisitions, which are now obsolete. --- sql/ha_partition.cc | 6 +++--- sql/ha_partition.h | 4 ++-- sql/sql_base.cc | 41 ++----------------------------------- sql/sql_view.cc | 2 -- sql/table.cc | 15 ++++---------- sql/table.h | 2 +- storage/myisam/ha_myisam.cc | 4 ++-- 7 files changed, 14 insertions(+), 60 deletions(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 2ec92173d14..1514651c1c5 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2606,7 +2606,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) for the same table. */ if (is_not_tmp_table) - pthread_mutex_lock(&table_share->mutex); + pthread_mutex_lock(&table_share->LOCK_ha_data); if (!table_share->ha_data) { HA_DATA_PARTITION *ha_data; @@ -2617,7 +2617,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) if (!ha_data) { if (is_not_tmp_table) - pthread_mutex_unlock(&table_share->mutex); + pthread_mutex_unlock(&table_share->LOCK_ha_data); goto err_handler; } DBUG_PRINT("info", ("table_share->ha_data 0x%p", ha_data)); @@ -2626,7 +2626,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) pthread_mutex_init(&ha_data->mutex, MY_MUTEX_INIT_FAST); } if (is_not_tmp_table) - pthread_mutex_unlock(&table_share->mutex); + pthread_mutex_unlock(&table_share->LOCK_ha_data); /* Some handlers update statistics as part of the open call. This will in some cases corrupt the statistics of the partition handler and thus diff --git a/sql/ha_partition.h b/sql/ha_partition.h index d4579d013fd..fc0e98348db 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -933,7 +933,7 @@ private: if(table_share->tmp_table == NO_TMP_TABLE) { auto_increment_lock= TRUE; - pthread_mutex_lock(&table_share->mutex); + pthread_mutex_lock(&table_share->LOCK_ha_data); } } virtual void unlock_auto_increment() @@ -946,7 +946,7 @@ private: */ if(auto_increment_lock && !auto_increment_safe_stmt_log_lock) { - pthread_mutex_unlock(&table_share->mutex); + pthread_mutex_unlock(&table_share->LOCK_ha_data); auto_increment_lock= FALSE; } } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f963d74102b..1356a2cd16f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -112,7 +112,6 @@ uint table_cache_count= 0; TABLE *unused_tables; HASH table_def_cache; static TABLE_SHARE *oldest_unused_share, end_of_unused_share; -static pthread_mutex_t LOCK_table_share; static bool table_def_inited= 0; static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables, @@ -253,13 +252,12 @@ extern "C" uchar *table_def_key(const uchar *record, size_t *length, static void table_def_free_entry(TABLE_SHARE *share) { DBUG_ENTER("table_def_free_entry"); + safe_mutex_assert_owner(&LOCK_open); if (share->prev) { /* remove from old_unused_share list */ - pthread_mutex_lock(&LOCK_table_share); *share->prev= share->next; share->next->prev= share->prev; - pthread_mutex_unlock(&LOCK_table_share); } free_table_share(share); DBUG_VOID_RETURN; @@ -269,7 +267,6 @@ static void table_def_free_entry(TABLE_SHARE *share) bool table_def_init(void) { table_def_inited= 1; - pthread_mutex_init(&LOCK_table_share, MY_MUTEX_INIT_FAST); oldest_unused_share= &end_of_unused_share; end_of_unused_share.prev= &oldest_unused_share; @@ -287,7 +284,6 @@ void table_def_free(void) /* Free all open TABLEs first. */ close_cached_tables(NULL, NULL, FALSE, FALSE); table_def_inited= 0; - pthread_mutex_destroy(&LOCK_table_share); /* Free table definitions. */ my_hash_free(&table_def_cache); } @@ -473,12 +469,6 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, DBUG_RETURN(0); } - /* - Lock mutex to be able to read table definition from file without - conflicts - */ - (void) pthread_mutex_lock(&share->mutex); - /* We assign a new table id under the protection of the LOCK_open and the share's own mutex. We do this insted of creating a new mutex @@ -508,7 +498,6 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, share->ref_count++; // Mark in use DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", (ulong) share, share->ref_count)); - (void) pthread_mutex_unlock(&share->mutex); DBUG_RETURN(share); found: @@ -516,20 +505,15 @@ found: We found an existing table definition. Return it if we didn't get an error when reading the table definition from file. */ - - /* We must do a lock to ensure that the structure is initialized */ - (void) pthread_mutex_lock(&share->mutex); if (share->error) { /* Table definition contained an error */ open_table_error(share, share->error, share->open_errno, share->errarg); - (void) pthread_mutex_unlock(&share->mutex); DBUG_RETURN(0); } if (share->is_view && !(db_flags & OPEN_VIEW)) { open_table_error(share, 1, ENOENT, 0); - (void) pthread_mutex_unlock(&share->mutex); DBUG_RETURN(0); } @@ -540,22 +524,16 @@ found: Unlink share from this list */ DBUG_PRINT("info", ("Unlinking from not used list")); - pthread_mutex_lock(&LOCK_table_share); *share->prev= share->next; share->next->prev= share->prev; share->next= 0; share->prev= 0; - pthread_mutex_unlock(&LOCK_table_share); } - (void) pthread_mutex_unlock(&share->mutex); /* Free cache if too big */ while (table_def_cache.records > table_def_size && oldest_unused_share->next) - { - pthread_mutex_lock(&oldest_unused_share->mutex); my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share); - } DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", (ulong) share, share->ref_count)); @@ -672,7 +650,6 @@ void release_table_share(TABLE_SHARE *share) safe_mutex_assert_owner(&LOCK_open); - pthread_mutex_lock(&share->mutex); DBUG_ASSERT(share->ref_count); if (!--share->ref_count) { @@ -684,12 +661,10 @@ void release_table_share(TABLE_SHARE *share) DBUG_PRINT("info",("moving share to unused list")); DBUG_ASSERT(share->next == 0); - pthread_mutex_lock(&LOCK_table_share); share->prev= end_of_unused_share.prev; *end_of_unused_share.prev= share; end_of_unused_share.prev= &share->next; share->next= &end_of_unused_share; - pthread_mutex_unlock(&LOCK_table_share); to_be_deleted= (table_def_cache.records > table_def_size); } @@ -699,9 +674,7 @@ void release_table_share(TABLE_SHARE *share) { DBUG_PRINT("info", ("Deleting share")); my_hash_delete(&table_def_cache, (uchar*) share); - DBUG_VOID_RETURN; } - pthread_mutex_unlock(&share->mutex); DBUG_VOID_RETURN; } @@ -745,9 +718,8 @@ static void reference_table_share(TABLE_SHARE *share) { DBUG_ENTER("reference_table_share"); DBUG_ASSERT(share->ref_count); - pthread_mutex_lock(&share->mutex); + safe_mutex_assert_owner(&LOCK_open); share->ref_count++; - pthread_mutex_unlock(&share->mutex); DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", (ulong) share, share->ref_count)); DBUG_VOID_RETURN; @@ -1043,10 +1015,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, free_cache_entry(unused_tables); /* Free table shares */ while (oldest_unused_share->next) - { - pthread_mutex_lock(&oldest_unused_share->mutex); (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share); - } if (!wait_for_refresh) { @@ -8581,10 +8550,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, DBUG_PRINT("info", ("share version: %lu ref_count: %u", share->version, share->ref_count)); if (share->ref_count == 0) - { - pthread_mutex_lock(&share->mutex); my_hash_delete(&table_def_cache, (uchar*) share); - } } if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG)) @@ -8722,10 +8688,7 @@ void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_na { DBUG_ASSERT(leave_thd || share->ref_count == 0); if (share->ref_count == 0) - { - pthread_mutex_lock(&share->mutex); my_hash_delete(&table_def_cache, (uchar*) share); - } } } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 3dc5ca4e09c..55181a58e53 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1643,10 +1643,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) if ((share= get_cached_table_share(view->db, view->table_name))) { DBUG_ASSERT(share->ref_count == 0); - pthread_mutex_lock(&share->mutex); share->ref_count++; share->version= 0; - pthread_mutex_unlock(&share->mutex); release_table_share(share); } query_cache_invalidate3(thd, view, 0); diff --git a/sql/table.cc b/sql/table.cc index b232ba23a89..c8814cff685 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -320,7 +320,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, share->free_tables.empty(); memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); - pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&share->LOCK_ha_data, MY_MUTEX_INIT_FAST); } DBUG_RETURN(share); } @@ -411,18 +411,11 @@ void free_table_share(TABLE_SHARE *share) DBUG_PRINT("enter", ("table: %s.%s", share->db.str, share->table_name.str)); DBUG_ASSERT(share->ref_count == 0); - /* - If someone is waiting for this to be deleted, inform it about this. - Don't do a delete until we know that no one is refering to this anymore. - */ + /* The mutex is initialized only for shares that are part of the TDC */ if (share->tmp_table == NO_TMP_TABLE) - { - /* No thread refers to this anymore */ - pthread_mutex_unlock(&share->mutex); - pthread_mutex_destroy(&share->mutex); - } + pthread_mutex_destroy(&share->LOCK_ha_data); my_hash_free(&share->name_hash); - + plugin_unlock(NULL, share->db_plugin); share->db_plugin= NULL; diff --git a/sql/table.h b/sql/table.h index 016a99e5452..60560029725 100644 --- a/sql/table.h +++ b/sql/table.h @@ -312,7 +312,7 @@ struct TABLE_SHARE TYPELIB keynames; /* Pointers to keynames */ TYPELIB fieldnames; /* Pointer to fieldnames */ TYPELIB *intervals; /* pointer to interval info */ - pthread_mutex_t mutex; /* For locking the share */ + pthread_mutex_t LOCK_ha_data; /* To protect access to ha_data */ TABLE_SHARE *next, **prev; /* Link to unused shares */ /* diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index cb8333767f8..9269b331754 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1812,7 +1812,7 @@ int ha_myisam::info(uint flag) /* Update share */ if (share->tmp_table == NO_TMP_TABLE) - pthread_mutex_lock(&share->mutex); + pthread_mutex_lock(&share->LOCK_ha_data); share->keys_in_use.set_prefix(share->keys); share->keys_in_use.intersect_extended(misam_info.key_map); share->keys_for_keyread.intersect(share->keys_in_use); @@ -1822,7 +1822,7 @@ int ha_myisam::info(uint flag) (char*) misam_info.rec_per_key, sizeof(table->key_info[0].rec_per_key[0])*share->key_parts); if (share->tmp_table == NO_TMP_TABLE) - pthread_mutex_unlock(&share->mutex); + pthread_mutex_unlock(&share->LOCK_ha_data); /* Set data_file_name and index_file_name to point at the symlink value From af4ee7432a83f86a0bf8c7b684ae74e0ef093bcd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 00:44:05 +0300 Subject: [PATCH 045/466] Backport of: ------------------------------------------------------------ revno: 2630.4.13 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Wed 2008-05-28 12:07:30 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Get rid of remove_table_from_cache() function since it was doing two things at once -- waiting while no one uses particular table (now job of metadata locking) and removing TABLE/TABLE_SHARE instances from table definition cache (now job of expel_table_from_cache()). --- sql/mysql_priv.h | 8 --- sql/sql_base.cc | 182 +---------------------------------------------- sql/sql_table.cc | 29 ++++---- 3 files changed, 15 insertions(+), 204 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 063b36c4ddc..e96534bad15 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1542,13 +1542,6 @@ char *generate_partition_syntax(partition_info *part_info, Alter_info *alter_info); #endif -/* bits for last argument to remove_table_from_cache() */ -#define RTFC_NO_FLAG 0x0000 -#define RTFC_OWNED_BY_THD_FLAG 0x0001 -#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002 -#define RTFC_CHECK_KILLED_FLAG 0x0004 -bool remove_table_from_cache(THD *thd, const char *db, const char *table, - uint flags); bool notify_thread_having_shared_lock(THD *thd, THD *in_use); void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name); @@ -1670,7 +1663,6 @@ extern pthread_mutex_t LOCK_gdl; bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt); void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt); -void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table); /* Functions to work with system tables. */ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1356a2cd16f..40377a99d21 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1482,7 +1482,7 @@ void close_thread_tables(THD *thd, /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) + (See: notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ @@ -2228,7 +2228,7 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) + (See: notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ @@ -8444,154 +8444,6 @@ void flush_tables() } -/* - Mark all entries with the table as deleted to force an reopen of the table - - The table will be closed (not stored in cache) by the current thread when - close_thread_tables() is called. - - PREREQUISITES - Lock on LOCK_open() - - RETURN - 0 This thread now have exclusive access to this table and no other thread - can access the table until close_thread_tables() is called. - 1 Table is in use by another thread -*/ - -bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, - uint flags) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length; - TABLE *table; - TABLE_SHARE *share; - bool result= 0, signalled= 0; - DBUG_ENTER("remove_table_from_cache"); - DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags)); - - key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; - for (;;) - { - result= signalled= 0; - - if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key, - key_length))) - { - I_P_List_iterator it(share->free_tables); - share->version= 0; - while ((table= it++)) - relink_unused(table); - - it.init(share->used_tables); - while ((table= it++)) - { - THD *in_use= table->in_use; - DBUG_ASSERT(in_use); - if (in_use != thd) - { - DBUG_PRINT("info", ("Table was in use by other thread")); - /* - Mark that table is going to be deleted from cache. This will - force threads that are in mysql_lock_tables() (but not yet - in thr_multi_lock()) to abort it's locks, close all tables and retry - */ - in_use->some_tables_deleted= 1; - - if (table->is_name_opened()) - { - DBUG_PRINT("info", ("Found another active instance of the table")); - result=1; - } - /* Kill delayed insert threads */ - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - ! in_use->killed) - { - in_use->killed= THD::KILL_CONNECTION; - pthread_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - pthread_mutex_lock(in_use->mysys_var->current_mutex); - signalled= 1; - pthread_cond_broadcast(in_use->mysys_var->current_cond); - pthread_mutex_unlock(in_use->mysys_var->current_mutex); - } - pthread_mutex_unlock(&in_use->mysys_var->mutex); - } - /* - Now we must abort all tables locks used by this thread - as the thread may be waiting to get a lock for another table. - Note that we need to hold LOCK_open while going through the - list. So that the other thread cannot change it. The other - thread must also hold LOCK_open whenever changing the - open_tables list. Aborting the MERGE lock after a child was - closed and before the parent is closed would be fatal. - */ - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* Do not handle locks of MERGE children. */ - if (thd_table->db_stat && !thd_table->parent) // If table is open - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - } - } - else - { - DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u", - table->db_stat)); - result= result || (flags & RTFC_OWNED_BY_THD_FLAG); - } - } - - while (unused_tables && !unused_tables->s->version) - free_cache_entry(unused_tables); - - DBUG_PRINT("info", ("share version: %lu ref_count: %u", - share->version, share->ref_count)); - if (share->ref_count == 0) - my_hash_delete(&table_def_cache, (uchar*) share); - } - - if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG)) - { - /* - Signal any thread waiting for tables to be freed to - reopen their tables - */ - broadcast_refresh(); - DBUG_PRINT("info", ("Waiting for refresh signal")); - if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed) - { - dropping_tables++; - if (likely(signalled)) - (void) pthread_cond_wait(&COND_refresh, &LOCK_open); - else - { - struct timespec abstime; - /* - It can happen that another thread has opened the - table but has not yet locked any table at all. Since - it can be locked waiting for a table that our thread - has done LOCK TABLE x WRITE on previously, we need to - ensure that the thread actually hears our signal - before we go to sleep. Thus we wait for a short time - and then we retry another loop in the - remove_table_from_cache routine. - */ - set_timespec(abstime, 10); - pthread_cond_timedwait(&COND_refresh, &LOCK_open, &abstime); - } - dropping_tables--; - continue; - } - } - break; - } - DBUG_RETURN(result); -} - - /** A callback to the server internals that is used to address special cases of the locking protocol. @@ -8894,34 +8746,6 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) } -/* - SYNOPSIS - close_open_tables_and_downgrade() - RESULT VALUES - NONE - DESCRIPTION - We need to ensure that any thread that has managed to open the table - but not yet encountered our lock on the table is also thrown out to - ensure that no threads see our frm changes premature to the final - version. The intermediate versions are only meant for use after a - crash and later REPAIR TABLE. - We also downgrade locks after the upgrade to WRITE_ONLY -*/ - -/* purecov: begin deadcode */ -void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - pthread_mutex_lock(&LOCK_open); - remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, - RTFC_WAIT_OTHER_THREAD_FLAG); - pthread_mutex_unlock(&LOCK_open); - /* If MERGE child, forward lock handling to parent. */ - mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent : - lpt->table, lpt->old_lock_type); -} -/* purecov: end */ - - /* Tells if two (or more) tables have auto_increment columns and we want to lock those tables with a write lock. @@ -9165,7 +8989,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) + (See: notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 640cd71fc62..5dee7015bd4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4800,19 +4800,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, /* Close all instances of the table to allow repair to rename files */ if (lock_type == TL_WRITE && table->table->s->version) { - DBUG_PRINT("admin", ("removing table from cache")); - pthread_mutex_lock(&LOCK_open); - const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, - "Waiting to get writelock"); - mysql_lock_abort(thd,table->table, TRUE); - remove_table_from_cache(thd, table->table->s->db.str, - table->table->s->table_name.str, - RTFC_WAIT_OTHER_THREAD_FLAG | - RTFC_CHECK_KILLED_FLAG); - thd->exit_cond(old_message); - DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd);); - if (thd->killed) - goto err; + if (wait_while_table_is_used(thd, table->table, + HA_EXTRA_PREPARE_FOR_RENAME)) + goto err; + DBUG_EXECUTE_IF("wait_in_mysql_admin_table", + wait_for_kill_signal(thd); + if (thd->killed) + goto err;); /* Flush entries in the query cache involving this table. */ query_cache_invalidate3(thd, table->table, 0); open_for_modify= 0; @@ -5064,10 +5058,10 @@ send_result_message: table->table->file->info(HA_STATUS_CONST); else { - pthread_mutex_lock(&LOCK_open); - remove_table_from_cache(thd, table->table->s->db.str, - table->table->s->table_name.str, RTFC_NO_FLAG); - pthread_mutex_unlock(&LOCK_open); + TABLE_LIST *save_next_global= table->next_global; + table->next_global= 0; + close_cached_tables(thd, table, FALSE, FALSE); + table->next_global= save_next_global; } /* May be something modified consequently we have to invalidate cache */ query_cache_invalidate3(thd, table->table, 0); @@ -5133,6 +5127,7 @@ bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_optimize_table"); + set_all_mdl_upgradable(tables); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "optimize", TL_WRITE, 1,0,0,0, &handler::ha_optimize, 0)); From 3d19fdad34c5fa23ac800a0b4819325ee66d8bd4 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 01:01:27 +0300 Subject: [PATCH 046/466] Backport of: ------------------------------------------------------------ revno: 2630.4.14 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Wed 2008-05-28 12:16:03 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Removed unused code and adjusted names of functions/methods to better reflect their current function. --- sql/mysql_priv.h | 6 ++--- sql/sql_base.cc | 60 +++++++++++++++++--------------------------- sql/sql_handler.cc | 2 +- sql/sql_insert.cc | 2 +- sql/sql_partition.cc | 2 +- sql/sql_table.cc | 5 ++-- sql/sql_trigger.cc | 3 ++- sql/table.h | 24 ++---------------- 8 files changed, 35 insertions(+), 69 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e96534bad15..1028c7d0d29 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1238,9 +1238,9 @@ bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, bool reopen_table(TABLE *table); bool reopen_tables(THD *thd, bool get_locks); thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table); -void close_data_files_and_morph_locks(THD *thd, const char *db, - const char *table_name); -void close_handle_and_leave_table_as_lock(TABLE *table); +void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, + const char *table_name); +void close_handle_and_leave_table_as_placeholder(TABLE *table); void unlock_locked_tables(THD *thd); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 40377a99d21..b21a9f3b931 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -726,25 +726,23 @@ static void reference_table_share(TABLE_SHARE *share) } -/* - Close file handle, but leave the table in the table cache +/** + Close file handle, but leave the table in THD::open_tables list + to allow its future reopening. - SYNOPSIS - close_handle_and_leave_table_as_lock() - table Table handler + @param table Table handler - NOTES - By leaving the table in the table cache, it disallows any other thread - to open the table + @note THD::killed will be set if we run out of memory - thd->killed will be set if we run out of memory + @note If closing a MERGE child, the calling function has to + take care for closing the parent too, if necessary. - If closing a MERGE child, the calling function has to take care for - closing the parent too, if necessary. + @todo Get rid of this function once we refactor LOCK TABLES + to keep around TABLE_LIST elements used for opening + of tables until UNLOCK TABLES. */ - -void close_handle_and_leave_table_as_lock(TABLE *table) +void close_handle_and_leave_table_as_placeholder(TABLE *table) { TABLE_SHARE *share, *old_share= table->s; char *key_buff; @@ -1048,8 +1046,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, char dbname[NAME_LEN+1]; char tname[NAME_LEN+1]; /* - Since close_data_files_and_morph_locks() frees share's memroot - we need to make copies of database and table names. + Since close_data_files_and_leave_as_placeholders() frees share's + memroot we need to make copies of database and table names. */ strmov(dbname, tab->s->db.str); strmov(tname, tab->s->table_name.str); @@ -1059,7 +1057,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, goto err_with_reopen; } pthread_mutex_lock(&LOCK_open); - close_data_files_and_morph_locks(thd, dbname, tname); + close_data_files_and_leave_as_placeholders(thd, dbname, tname); pthread_mutex_unlock(&LOCK_open); } } @@ -1082,7 +1080,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, goto err_with_reopen; } pthread_mutex_lock(&LOCK_open); - close_data_files_and_morph_locks(thd, table->db, table->table_name); + close_data_files_and_leave_as_placeholders(thd, table->db, + table->table_name); pthread_mutex_unlock(&LOCK_open); } } @@ -1529,7 +1528,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) detach_merge_children(table, TRUE); table->mdl_lock= 0; - if (table->needs_reopen_or_name_lock() || + if (table->needs_reopen() || thd->version != refresh_version || !table->db_stat) { free_cache_entry(table); @@ -1537,12 +1536,6 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) } else { - /* - Open placeholders have TABLE::db_stat set to 0, so they should be - handled by the first alternative. - */ - DBUG_ASSERT(!table->open_placeholder); - /* Assert that MERGE children are not attached in unused_tables. */ DBUG_ASSERT(!table->is_children_attached()); @@ -3244,7 +3237,7 @@ bool reopen_table(TABLE *table) /** Close all instances of a table open by this thread and replace - them with exclusive name-locks. + them with placeholder in THD::open_tables list for future reopening. @param thd Thread context @param db Database name for the table to be closed @@ -3259,11 +3252,11 @@ bool reopen_table(TABLE *table) the strings are used in a loop even after the share may be freed. */ -void close_data_files_and_morph_locks(THD *thd, const char *db, - const char *table_name) +void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, + const char *table_name) { TABLE *table; - DBUG_ENTER("close_data_files_and_morph_locks"); + DBUG_ENTER("close_data_files_and_leave_as_placeholders"); safe_mutex_assert_owner(&LOCK_open); @@ -3277,11 +3270,6 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, thd->lock= 0; } - /* - Note that open table list may contain a name-lock placeholder - for target table name if we process ALTER TABLE ... RENAME. - So loop below makes sense even if we are not under LOCK TABLES. - */ for (table=thd->open_tables; table ; table=table->next) { if (!strcmp(table->s->table_name.str, table_name) && @@ -3298,15 +3286,13 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, won't have multiple children with the same db.table_name. */ mysql_lock_remove(thd, thd->locked_tables, table->parent, TRUE); - table->parent->open_placeholder= 1; - close_handle_and_leave_table_as_lock(table->parent); + close_handle_and_leave_table_as_placeholder(table->parent); } else mysql_lock_remove(thd, thd->locked_tables, table, TRUE); } - table->open_placeholder= 1; table->s->version= 0; - close_handle_and_leave_table_as_lock(table); + close_handle_and_leave_table_as_placeholder(table); } } DBUG_VOID_RETURN; diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 5c034a0452a..6087b0b7700 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -801,7 +801,7 @@ void mysql_ha_flush(THD *thd) if (hash_tables->table && (hash_tables->table->mdl_lock && mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock) || - hash_tables->table->needs_reopen_or_name_lock())) + hash_tables->table->needs_reopen())) mysql_ha_close_table(thd, hash_tables); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 525f6b9a4d8..5da9f2e6bd4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2638,7 +2638,7 @@ bool Delayed_insert::handle_inserts(void) thd_proc_info(&thd, "insert"); max_rows= delayed_insert_limit; - if (thd.killed || table->needs_reopen_or_name_lock()) + if (thd.killed || table->needs_reopen()) { thd.killed= THD::KILL_CONNECTION; max_rows= ULONG_MAX; // Do as much as possible diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 48f33bf3295..3c67574d8c1 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6262,7 +6262,7 @@ static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt) and we set db_stat to zero to ensure we don't close twice. */ pthread_mutex_lock(&LOCK_open); - close_data_files_and_morph_locks(thd, db, table_name); + close_data_files_and_leave_as_placeholders(thd, db, table_name); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 5dee7015bd4..649ba28bcac 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7415,7 +7415,7 @@ view_err: pthread_mutex_lock(&LOCK_open); - close_data_files_and_morph_locks(thd, db, table_name); + close_data_files_and_leave_as_placeholders(thd, db, table_name); error=0; save_old_db_type= old_db_type; @@ -7507,8 +7507,7 @@ view_err: object to make it suitable for reopening. */ DBUG_ASSERT(t_table == table); - table->open_placeholder= 1; - close_handle_and_leave_table_as_lock(table); + close_handle_and_leave_table_as_placeholder(table); } else { diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index a623b3c80f3..a7a4d48b593 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -487,7 +487,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (!result && thd->locked_tables) { /* Make table suitable for reopening */ - close_data_files_and_morph_locks(thd, tables->db, tables->table_name); + close_data_files_and_leave_as_placeholders(thd, tables->db, + tables->table_name); thd->in_lock_tables= 1; if (reopen_tables(thd, 1)) { diff --git a/sql/table.h b/sql/table.h index 60560029725..a31b96e0828 100644 --- a/sql/table.h +++ b/sql/table.h @@ -793,24 +793,6 @@ public: */ my_bool key_read; my_bool no_keyread; - /* - Placeholder for an open table which prevents other connections - from taking name-locks on this table. Typically used with - TABLE_SHARE::version member to take an exclusive name-lock on - this table name -- a name lock that not only prevents other - threads from opening the table, but also blocks other name - locks. This is achieved by: - - setting open_placeholder to 1 - this will block other name - locks, as wait_for_locked_table_name will be forced to wait, - see table_is_used for details. - - setting version to 0 - this will force other threads to close - the instance of this table and wait (this is the same approach - as used for usual name locks). - An exclusively name-locked table currently can have no handler - object associated with it (db_stat is always 0), but please do - not rely on that. - */ - my_bool open_placeholder; my_bool locked_by_logger; my_bool no_replicate; my_bool locked_by_name; @@ -874,12 +856,10 @@ public: read_set= &def_read_set; write_set= &def_write_set; } - /* Is table open or should be treated as such by name-locking? */ - inline bool is_name_opened() { return db_stat || open_placeholder; } /* - Is this instance of the table should be reopen or represents a name-lock? + Is this instance of the table should be reopen? */ - inline bool needs_reopen_or_name_lock() + inline bool needs_reopen() { return s->version != refresh_version; } bool is_children_attached(void); }; From f56cc2a3351eff1c7c01706ad8b93dad5770ed93 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 01:13:06 +0300 Subject: [PATCH 047/466] Backport of: ------------------------------------------------------------ revno: 2630.4.16 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Thu 2008-05-29 09:45:02 +0400 message: WL#3726 "DDL locking for all metadata objects". After review changes in progress. Tweaked some comments and did some renames to avoid ambiguites. --- sql/mysql_priv.h | 1 - sql/sql_base.cc | 135 +++++++++++++++++++-------------------------- sql/sql_handler.cc | 1 + sql/sql_insert.cc | 4 +- sql/sql_parse.cc | 2 +- sql/sql_prepare.cc | 2 +- sql/sql_table.cc | 3 +- sql/sql_trigger.cc | 11 +++- sql/sql_view.cc | 2 +- sql/table.h | 26 ++++++--- 10 files changed, 93 insertions(+), 94 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1028c7d0d29..3e3d3b6df24 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1227,7 +1227,6 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags); -bool name_lock_locked_table(THD *thd, TABLE_LIST *tables); bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list); TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name); TABLE *find_write_locked_table(TABLE *list, const char *db, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b21a9f3b931..13218f3a193 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2340,39 +2340,6 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) } -/** - Exclusively name-lock a table that is already write-locked by the - current thread. - - @param thd current thread context - @param tables table list containing one table to open. - - @return FALSE on success, TRUE otherwise. -*/ - -bool name_lock_locked_table(THD *thd, TABLE_LIST *tables) -{ - bool result= TRUE; - - DBUG_ENTER("name_lock_locked_table"); - - /* Under LOCK TABLES we must only accept write locked tables. */ - tables->table= find_write_locked_table(thd->open_tables, tables->db, - tables->table_name); - - if (tables->table) - { - /* - Ensures that table is opened only by this thread and that no - other statement will open this table. - */ - result= wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN); - } - - DBUG_RETURN(result); -} - - /* Open table for which this thread has exclusive meta-data lock. @@ -2576,9 +2543,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, /* Parsing of partitioning information from .frm needs thd->lex set up. */ DBUG_ASSERT(thd->lex->is_lex_started); - /* find a unused table in the open table cache */ - if (action) - *action= OT_NO_ACTION; + *action= OT_NO_ACTION; /* an open table operation needs a lot of the stack space */ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias)) @@ -2716,6 +2681,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, enum legacy_db_type not_used; build_table_filename(path, sizeof(path) - 1, table_list->db, table_list->table_name, reg_ext, 0); + /* + Note that we can't be 100% sure that it is a view since it's + possible that we either simply have not found unused TABLE + instance in THD::open_tables list or were unable to open table + during prelocking process (in this case in theory we still + should hold shared metadata lock on it). + */ if (mysql_frm_type(thd, path, ¬_used) == FRMTYPE_VIEW) { if (!tdc_open_view(thd, table_list, alias, key, key_length, @@ -2741,28 +2713,25 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } /* - Non pre-locked/LOCK TABLES mode, and the table is not temporary: - this is the normal use case. - Now we should: - - try to find the table in the table cache. - - if one of the discovered TABLE instances is name-locked - (table->s->version == 0) or some thread has started FLUSH TABLES - (refresh_version > table->s->version), back off -- we have to wait - until no one holds a name lock on the table. - - if there is no such TABLE in the name cache, read the table definition - and insert it into the cache. - We perform all of the above under LOCK_open which currently protects - the open cache (also known as table cache) and table definitions stored - on disk. + Non pre-locked/LOCK TABLES mode, and the table is not temporary. + This is the normal use case. */ mdl_lock= table_list->mdl_lock; mdl_add_lock(&thd->mdl_context, mdl_lock); - if (table_list->open_table_type) + if (table_list->open_type) { + /* + In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table + may not yet exist. Let's acquire an exclusive lock for that + case. If later it turns out the table existsed, we will + downgrade the lock to shared. Note that, according to the + locking protocol, all exclusive locks must be acquired before + shared locks. This invariant is preserved here and is also + enforced by asserts in metadata locking subsystem. + */ mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); - /* TODO: This case can be significantly optimized. */ if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(0); } @@ -2776,7 +2745,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, MDL_HIGH_PRIO : MDL_NORMAL_PRIO); if (mdl_acquire_shared_lock(mdl_lock, &retry)) { - if (action && retry) + if (retry) *action= OT_BACK_OFF_AND_RETRY; DBUG_RETURN(0); } @@ -2798,13 +2767,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ! (flags & MYSQL_LOCK_IGNORE_FLUSH)) { /* Someone did a refresh while thread was opening tables */ - if (action) - *action= OT_BACK_OFF_AND_RETRY; + *action= OT_BACK_OFF_AND_RETRY; pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } - if (table_list->open_table_type == TABLE_LIST::OPEN_OR_CREATE) + if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) { bool exists; @@ -2818,7 +2786,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } /* Table exists. Let us try to open it. */ } - else if (table_list->open_table_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL) + else if (table_list->open_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL) { pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); @@ -2926,8 +2894,17 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { if (!(flags & MYSQL_LOCK_IGNORE_FLUSH)) { - if (action) - *action= OT_BACK_OFF_AND_RETRY; + /* + We already have an MDL lock. But we have encountered an old + version of table in the table definition cache which is possible + when someone changes the table version directly in the cache + without acquiring a metadata lock (e.g. this can happen during + "rolling" FLUSH TABLE(S)). + Note, that to avoid a "busywait" in this case, we have to wait + separately in the caller for old table versions to go away + (see tdc_wait_for_old_versions()). + */ + *action= OT_BACK_OFF_AND_RETRY; release_table_share(share); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); @@ -2966,18 +2943,15 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { my_free(table, MYF(0)); - if (action) + if (error == 7) { - if (error == 7) - { - share->version= 0; - *action= OT_DISCOVER; - } - else if (share->crashed) - { - share->version= 0; - *action= OT_REPAIR; - } + share->version= 0; + *action= OT_DISCOVER; + } + else if (share->crashed) + { + share->version= 0; + *action= OT_REPAIR; } goto err_unlock; @@ -2996,16 +2970,19 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, pthread_mutex_unlock(&LOCK_open); - // Table existed - if (table_list->open_table_type == TABLE_LIST::OPEN_OR_CREATE) + /* + In CREATE TABLE .. If NOT EXISTS .. SELECT we have found that + table exists now we should downgrade our exclusive metadata + lock on this table to shared metadata lock. + */ + if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) mdl_downgrade_exclusive_locks(&thd->mdl_context); table->mdl_lock= mdl_lock; - if (action) - { - table->next=thd->open_tables; /* Link into simple list */ - thd->open_tables=table; - } + + table->next=thd->open_tables; /* Link into simple list */ + thd->open_tables=table; + table->reginfo.lock_type=TL_READ; /* Assume read */ reset: @@ -3856,8 +3833,8 @@ err: /** - Auxiliary routine which finalizes process of TABLE object creation - by loading triggers and handling implicitly emptied tables. + Finalize the process of TABLE creation by loading table triggers + and taking action if a HEAP table content was emptied implicitly. */ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry) @@ -4636,7 +4613,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) table and successful table creation. ... */ - if (tables->open_table_type) + if (tables->open_type) continue; if (action) diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 6087b0b7700..87e9538b48f 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -798,6 +798,7 @@ void mysql_ha_flush(THD *thd) for (uint i= 0; i < thd->handler_tables_hash.records; i++) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); + /* TABLE::mdl_lock is 0 for temporary tables so we need extra check. */ if (hash_tables->table && (hash_tables->table->mdl_lock && mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock) || diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 5da9f2e6bd4..a0c198f3196 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3453,6 +3453,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, Item *item; Field *tmp_field; bool not_used; + enum_open_table_action not_used2; DBUG_ENTER("create_table_from_items"); tmp_table.alias= 0; @@ -3544,8 +3545,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } else { - if (!(table= open_table(thd, create_table, thd->mem_root, - (enum_open_table_action*) 0, + if (!(table= open_table(thd, create_table, thd->mem_root, ¬_used2, MYSQL_OPEN_TEMPORARY_ONLY)) && !create_info->table_existed) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2b2c736fd9e..2c043922cc8 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2631,7 +2631,7 @@ case SQLCOM_PREPARE: if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); - create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE; + create_table->open_type= TABLE_LIST::OPEN_OR_CREATE; } if (!(res= open_and_lock_tables(thd, lex->query_tables))) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 582e18a3abf..5efa0cea7a9 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1673,7 +1673,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); - create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE; + create_table->open_type= TABLE_LIST::OPEN_OR_CREATE; } if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 649ba28bcac..9888dceef54 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7208,12 +7208,13 @@ view_err: { if (table->s->tmp_table) { + enum_open_table_action not_used; TABLE_LIST tbl; bzero((void*) &tbl, sizeof(tbl)); tbl.db= new_db; tbl.table_name= tbl.alias= tmp_name; /* Table is in thd->temporary_tables */ - new_table= open_table(thd, &tbl, thd->mem_root, (enum_open_table_action*) 0, + new_table= open_table(thd, &tbl, thd->mem_root, ¬_used, MYSQL_LOCK_IGNORE_FLUSH); } else diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index a7a4d48b593..4e2b77292d8 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -446,8 +446,17 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (thd->locked_tables) { - if (name_lock_locked_table(thd, tables)) + /* Under LOCK TABLES we must only accept write locked tables. */ + if (!(tables->table= find_write_locked_table(thd->open_tables, tables->db, + tables->table_name))) goto end; + /* + Ensure that table is opened only by this thread and that no other + statement will open this table. + */ + if (wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN)) + goto end; + pthread_mutex_lock(&LOCK_open); } else diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 55181a58e53..c40f6643042 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -395,7 +395,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; lex->link_first_table_back(view, link_to_local); - view->open_table_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL; + view->open_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL; if (open_and_lock_tables(thd, lex->query_tables)) { diff --git a/sql/table.h b/sql/table.h index a31b96e0828..e64111ef988 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1349,14 +1349,26 @@ struct TABLE_LIST used for implicit LOCK TABLES only and won't be used in real statement. */ bool prelocking_placeholder; - /* - This TABLE_LIST object corresponds to the table/view which requires - special handling/meta-data locking. For example this is a target - table in CREATE TABLE ... SELECT so it is possible that it does not - exist and we should take exclusive meta-data lock on it in this - case. + /** + Indicates that if TABLE_LIST object corresponds to the table/view + which requires special handling/meta-data locking. */ - enum {NORMAL_OPEN= 0, OPEN_OR_CREATE, TAKE_EXCLUSIVE_MDL} open_table_type; + enum + { + /* Normal open, shared metadata lock should be taken. */ + NORMAL_OPEN= 0, + /* + It's target table of CREATE TABLE ... SELECT so we should + either open table if it exists (and take shared metadata lock) + or take exclusive metadata lock if it doesn't exist. + */ + OPEN_OR_CREATE, + /* + It's target view of CREATE/ALTER VIEW. We should take exclusive + metadata lock for this table list element. + */ + TAKE_EXCLUSIVE_MDL + } open_type; /** Indicates that for this table/view we need to take shared metadata lock which should be upgradable to exclusive metadata lock. From a9dbad1afd452ca10116a0bbf83ab3d98b33723e Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 01:33:22 +0300 Subject: [PATCH 048/466] Backport of: ------------------------------------------------------------ revno: 2630.4.17 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Thu 2008-05-29 16:52:56 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. "The great correction of names". Renamed MDL_LOCK and MDL_LOCK_DATA classes to make usage of these names in metadata locking subsystem consistent with other parts of server (i.e. thr_lock.cc). Now we MDL_LOCK_DATA corresponds to request for a lock and MDL_LOCK to the lock itself. Adjusted code in MDL subsystem and other places using these classes accordingly. Did similar thing for GLOBAL_MDL_LOCK_DATA class and also changed name of its members to correspond to names of MDL_LOCK_DATA members. Finally got rid of usage of one letter variables in MDL code since it makes code harder to search in (according to reviewer). --- sql/ha_ndbcluster_binlog.cc | 8 +- sql/lock.cc | 11 +- sql/log_event.cc | 9 +- sql/mdl.cc | 624 ++++++++++++++++++------------------ sql/mdl.h | 94 +++--- sql/sp_head.cc | 13 +- sql/sql_base.cc | 47 +-- sql/sql_delete.cc | 18 +- sql/sql_handler.cc | 22 +- sql/sql_parse.cc | 6 +- sql/sql_show.cc | 14 +- sql/sql_table.cc | 67 ++-- sql/table.cc | 5 +- sql/table.h | 6 +- 14 files changed, 484 insertions(+), 460 deletions(-) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index f272526edb8..14d14db6b7d 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -140,8 +140,8 @@ static Uint64 *p_latest_trans_gci= 0; */ static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; -static MDL_LOCK binlog_mdl_lock; -static char binlog_mdlkey[MAX_DBKEY_LENGTH]; +static MDL_LOCK_DATA binlog_mdl_lock_data; +static char binlog_mdlkey[MAX_DBKEY_LENGTH]; /* Helper functions @@ -2343,9 +2343,9 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) tables->alias= tables->table_name= reptable; tables->lock_type= TL_WRITE; thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE; - mdl_init_lock(&binlog_mdl_lock, binlog_mdlkey, 0, tables->db, + mdl_init_lock(&binlog_mdl_lock_data, binlog_mdlkey, 0, tables->db, tables->table_name); - tables->mdl_lock= &binlog_mdl_lock; + tables->mdl_lock_data= &binlog_mdl_lock_data; tables->required_type= FRMTYPE_TABLE; uint counter; thd->clear_error(); diff --git a/sql/lock.cc b/sql/lock.cc index 38b2d22f91f..b5eaaa05fff 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -966,15 +966,16 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, bool lock_table_names(THD *thd, TABLE_LIST *table_list) { TABLE_LIST *lock_table; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - if (!(mdl_lock= mdl_alloc_lock(0, lock_table->db, lock_table->table_name, - thd->mem_root))) + if (!(mdl_lock_data= mdl_alloc_lock(0, lock_table->db, + lock_table->table_name, + thd->mem_root))) goto end; - mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock); + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock_data); } if (mdl_acquire_exclusive_locks(&thd->mdl_context)) return 1; diff --git a/sql/log_event.cc b/sql/log_event.cc index fd0e20d690d..92de9933181 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8058,7 +8058,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem, *mdlkey; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; size_t dummy_len; void *memory; DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); @@ -8073,7 +8073,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) &table_list, (uint) sizeof(RPL_TABLE_LIST), &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, - &mdl_lock, sizeof(MDL_LOCK), + &mdl_lock_data, sizeof(MDL_LOCK_DATA), &mdlkey, MAX_DBKEY_LENGTH, NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -8087,8 +8087,9 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) table_list->updating= 1; strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); strmov(table_list->table_name, m_tblnam); - mdl_init_lock(mdl_lock, mdlkey, 0, table_list->db, table_list->table_name); - table_list->mdl_lock= mdl_lock; + mdl_init_lock(mdl_lock_data, mdlkey, 0, table_list->db, + table_list->table_name); + table_list->mdl_lock_data= mdl_lock_data; int error= 0; diff --git a/sql/mdl.cc b/sql/mdl.cc index ddd027c4027..64c011d34bf 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -26,34 +26,34 @@ /** The lock context. Created internally for an acquired lock. - For a given name, there exists only one MDL_LOCK_DATA instance, + For a given name, there exists only one MDL_LOCK instance, and it exists only when the lock has been granted. Can be seen as an MDL subsystem's version of TABLE_SHARE. */ -struct MDL_LOCK_DATA +struct MDL_LOCK { - I_P_List active_shared; + I_P_List active_shared; /* There can be several upgraders and active exclusive belonging to the same context. */ - I_P_List active_shared_waiting_upgrade; - I_P_List active_exclusive; - I_P_List waiting_exclusive; + I_P_List active_shared_waiting_upgrade; + I_P_List active_exclusive; + I_P_List waiting_exclusive; /** - Number of MDL_LOCK objects associated with this MDL_LOCK_DATA instance + Number of MDL_LOCK_DATA objects associated with this MDL_LOCK instance and therefore present in one of above lists. Note that this number doesn't account for pending requests for shared lock since we don't - associate them with MDL_LOCK_DATA and don't keep them in any list. + associate them with MDL_LOCK and don't keep them in any list. */ - uint lock_count; + uint lock_data_count; void *cached_object; mdl_cached_object_release_hook cached_object_release_hook; - MDL_LOCK_DATA() : cached_object(0), cached_object_release_hook(0) {} + MDL_LOCK() : cached_object(0), cached_object_release_hook(0) {} - MDL_LOCK *get_key_owner() + MDL_LOCK_DATA *get_key_owner() { return !active_shared.is_empty() ? active_shared.head() : @@ -63,9 +63,9 @@ struct MDL_LOCK_DATA active_exclusive.head() : waiting_exclusive.head())); } - bool has_one_lock() + bool has_one_lock_data() { - return (lock_count == 1); + return (lock_data_count == 1); } }; @@ -82,18 +82,18 @@ HASH mdl_locks; or shared upgradable lock on particular object. */ -struct MDL_GLOBAL_LOCK_DATA +struct MDL_GLOBAL_LOCK { - uint shared_pending; - uint shared_acquired; - uint intention_exclusive_acquired; + uint waiting_shared; + uint active_shared; + uint active_intention_exclusive; } global_lock; extern "C" uchar *mdl_locks_key(const uchar *record, size_t *length, my_bool not_used __attribute__((unused))) { - MDL_LOCK_DATA *entry=(MDL_LOCK_DATA*) record; + MDL_LOCK *entry=(MDL_LOCK*) record; *length= entry->get_key_owner()->key_length; return (uchar*) entry->get_key_owner()->key; } @@ -123,8 +123,8 @@ void mdl_init() pthread_cond_init(&COND_mdl, NULL); my_hash_init(&mdl_locks, &my_charset_bin, 16 /* FIXME */, 0, 0, mdl_locks_key, 0, 0); - global_lock.shared_pending= global_lock.shared_acquired= 0; - global_lock.intention_exclusive_acquired= 0; + global_lock.waiting_shared= global_lock.active_shared= 0; + global_lock.active_intention_exclusive= 0; } @@ -211,18 +211,18 @@ void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) { - MDL_LOCK *l; + MDL_LOCK_DATA *lock_data; DBUG_ASSERT(dst->thd == src->thd); if (!src->locks.is_empty()) { - I_P_List_iterator it(src->locks); - while ((l= it++)) + I_P_List_iterator it(src->locks); + while ((lock_data= it++)) { - DBUG_ASSERT(l->ctx); - l->ctx= dst; - dst->locks.push_front(l); + DBUG_ASSERT(lock_data->ctx); + lock_data->ctx= dst; + dst->locks.push_front(lock_data); } src->locks.empty(); } @@ -247,7 +247,7 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) request encloses calls to mdl_acquire_shared_lock() and mdl_release_locks(). - @param mdl Pointer to an MDL_LOCK object to initialize + @param lock_data Pointer to an MDL_LOCK_DATA object to initialize @param key_buff Pointer to the buffer for key for the lock request (should be at least strlen(db) + strlen(name) + 2 bytes, or, if the lengths are not known, MAX_DBNAME_LENGTH) @@ -269,19 +269,19 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) they share the same name space in the SQL standard. */ -void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db, - const char *name) +void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, + const char *db, const char *name) { int4store(key, type); - mdl->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - mdl->key= key; - mdl->type= MDL_SHARED; - mdl->state= MDL_PENDING; - mdl->prio= MDL_NORMAL_PRIO; - mdl->is_upgradable= FALSE; + lock_data->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + lock_data->key= key; + lock_data->type= MDL_SHARED; + lock_data->state= MDL_PENDING; + lock_data->prio= MDL_NORMAL_PRIO; + lock_data->is_upgradable= FALSE; #ifndef DBUG_OFF - mdl->ctx= 0; - mdl->lock_data= 0; + lock_data->ctx= 0; + lock_data->lock= 0; #endif } @@ -305,19 +305,19 @@ void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db, @retval non-0 Pointer to an object representing a lock request */ -MDL_LOCK *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root) +MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, + MEM_ROOT *root) { - MDL_LOCK *lock; + MDL_LOCK_DATA *lock_data; char *key; - if (!multi_alloc_root(root, &lock, sizeof(MDL_LOCK), &key, + if (!multi_alloc_root(root, &lock_data, sizeof(MDL_LOCK_DATA), &key, MAX_DBKEY_LENGTH, NULL)) return NULL; - mdl_init_lock(lock, key, type, db, name); + mdl_init_lock(lock_data, key, type, db, name); - return lock; + return lock_data; } @@ -334,16 +334,16 @@ MDL_LOCK *mdl_alloc_lock(int type, const char *db, const char *name, @param context The MDL context to associate the lock with. There should be no more than one context per connection, to avoid deadlocks. - @param lock The lock request to be added. + @param lock_data The lock request to be added. */ -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK *lock) +void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) { DBUG_ENTER("mdl_add_lock"); - DBUG_ASSERT(lock->state == MDL_PENDING); - DBUG_ASSERT(!lock->ctx); - lock->ctx= context; - context->locks.push_front(lock); + DBUG_ASSERT(lock_data->state == MDL_PENDING); + DBUG_ASSERT(!lock_data->ctx); + lock_data->ctx= context; + context->locks.push_front(lock_data); DBUG_VOID_RETURN; } @@ -372,16 +372,16 @@ void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK *lock) void mdl_remove_all_locks(MDL_CONTEXT *context) { - MDL_LOCK *l; - I_P_List_iterator it(context->locks); - while ((l= it++)) + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); + while ((lock_data= it++)) { /* Reset lock request back to its initial state. */ - l->type= MDL_SHARED; - l->prio= MDL_NORMAL_PRIO; - l->is_upgradable= FALSE; + lock_data->type= MDL_SHARED; + lock_data->prio= MDL_NORMAL_PRIO; + lock_data->is_upgradable= FALSE; #ifndef DBUG_OFF - l->ctx= 0; + lock_data->ctx= 0; #endif } context->locks.empty(); @@ -389,20 +389,20 @@ void mdl_remove_all_locks(MDL_CONTEXT *context) /** - Auxiliary functions needed for creation/destruction of MDL_LOCK_DATA + Auxiliary functions needed for creation/destruction of MDL_LOCK objects. @todo This naive implementation should be replaced with one that saves on memory allocation by reusing released objects. */ -static MDL_LOCK_DATA* get_lock_data_object(void) +static MDL_LOCK* get_lock_object(void) { - return new MDL_LOCK_DATA(); + return new MDL_LOCK(); } -static void release_lock_data_object(MDL_LOCK_DATA *lock) +static void release_lock_object(MDL_LOCK *lock) { delete lock; } @@ -421,27 +421,27 @@ static void release_lock_data_object(MDL_LOCK_DATA *lock) This function must be called after the lock is added to a context. - @param lock [in] Lock request object for lock to be acquired - @param retry [out] Indicates that conflicting lock exists and another - attempt should be made after releasing all current - locks and waiting for conflicting lock go away - (using mdl_wait_for_locks()). + @param lock_data [in] Lock request object for lock to be acquired + @param retry [out] Indicates that conflicting lock exists and another + attempt should be made after releasing all current + locks and waiting for conflicting lock go away + (using mdl_wait_for_locks()). @retval FALSE Success. @retval TRUE Failure. Either error occured or conflicting lock exists. In the latter case "retry" parameter is set to TRUE. */ -bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) +bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) { - MDL_LOCK_DATA *lock_data; + MDL_LOCK *lock; *retry= FALSE; - DBUG_ASSERT(l->type == MDL_SHARED && l->state == MDL_PENDING); + DBUG_ASSERT(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING); safe_mutex_assert_not_owner(&LOCK_open); - if (l->ctx->has_global_shared_lock && l->is_upgradable) + if (lock_data->ctx->has_global_shared_lock && lock_data->is_upgradable) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; @@ -449,46 +449,46 @@ bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) pthread_mutex_lock(&LOCK_mdl); - if (l->is_upgradable && - (global_lock.shared_acquired || global_lock.shared_pending)) + if (lock_data->is_upgradable && + (global_lock.active_shared || global_lock.waiting_shared)) { pthread_mutex_unlock(&LOCK_mdl); *retry= TRUE; return TRUE; } - if (!(lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, - l->key_length))) + if (!(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, + lock_data->key_length))) { - lock_data= get_lock_data_object(); - lock_data->active_shared.push_front(l); - lock_data->lock_count= 1; - my_hash_insert(&mdl_locks, (uchar*)lock_data); - l->state= MDL_ACQUIRED; - l->lock_data= lock_data; - if (l->is_upgradable) - global_lock.intention_exclusive_acquired++; + lock= get_lock_object(); + lock->active_shared.push_front(lock_data); + lock->lock_data_count= 1; + my_hash_insert(&mdl_locks, (uchar*)lock); + lock_data->state= MDL_ACQUIRED; + lock_data->lock= lock; + if (lock_data->is_upgradable) + global_lock.active_intention_exclusive++; } else { - if ((lock_data->active_exclusive.is_empty() && - (l->prio == MDL_HIGH_PRIO || - lock_data->waiting_exclusive.is_empty() && - lock_data->active_shared_waiting_upgrade.is_empty())) || - (!lock_data->active_exclusive.is_empty() && - lock_data->active_exclusive.head()->ctx == l->ctx)) + if ((lock->active_exclusive.is_empty() && + (lock_data->prio == MDL_HIGH_PRIO || + lock->waiting_exclusive.is_empty() && + lock->active_shared_waiting_upgrade.is_empty())) || + (!lock->active_exclusive.is_empty() && + lock->active_exclusive.head()->ctx == lock_data->ctx)) { /* When exclusive lock comes from the same context we can satisfy our shared lock. This is required for CREATE TABLE ... SELECT ... and ALTER VIEW ... AS .... */ - lock_data->active_shared.push_front(l); - lock_data->lock_count++; - l->state= MDL_ACQUIRED; - l->lock_data= lock_data; - if (l->is_upgradable) - global_lock.intention_exclusive_acquired++; + lock->active_shared.push_front(lock_data); + lock->lock_data_count++; + lock_data->state= MDL_ACQUIRED; + lock_data->lock= lock; + if (lock_data->is_upgradable) + global_lock.active_intention_exclusive++; } else *retry= TRUE; @@ -499,7 +499,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry) } -static void release_lock(MDL_LOCK *l); +static void release_lock(MDL_LOCK_DATA *lock_data); /** @@ -523,11 +523,11 @@ static void release_lock(MDL_LOCK *l); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { - MDL_LOCK *l, *lh; - MDL_LOCK_DATA *lock_data; + MDL_LOCK_DATA *lock_data, *conf_lock_data; + MDL_LOCK *lock; bool signalled= FALSE; const char *old_msg; - I_P_List_iterator it(context->locks); + I_P_List_iterator it(context->locks); THD *thd= context->thd; DBUG_ASSERT(thd == current_thd); @@ -544,34 +544,35 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); - while ((l= it++)) + while ((lock_data= it++)) { - DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING); - if (!(lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, - l->key_length))) + DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && + lock_data->state == MDL_PENDING); + if (!(lock= (MDL_LOCK *) my_hash_search(&mdl_locks, (uchar*)lock_data->key, + lock_data->key_length))) { - lock_data= get_lock_data_object(); - lock_data->waiting_exclusive.push_front(l); - lock_data->lock_count= 1; - my_hash_insert(&mdl_locks, (uchar*)lock_data); - l->lock_data= lock_data; + lock= get_lock_object(); + lock->waiting_exclusive.push_front(lock_data); + lock->lock_data_count= 1; + my_hash_insert(&mdl_locks, (uchar*)lock); + lock_data->lock= lock; } else { - lock_data->waiting_exclusive.push_front(l); - lock_data->lock_count++; - l->lock_data= lock_data; + lock->waiting_exclusive.push_front(lock_data); + lock->lock_data_count++; + lock_data->lock= lock; } } while (1) { it.rewind(); - while ((l= it++)) + while ((lock_data= it++)) { - lock_data= l->lock_data; + lock= lock_data->lock; - if (global_lock.shared_acquired || global_lock.shared_pending) + if (global_lock.active_shared || global_lock.waiting_shared) { /* There is active or pending global shared lock we have @@ -580,8 +581,8 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) signalled= TRUE; break; } - else if (!lock_data->active_exclusive.is_empty() || - !lock_data->active_shared_waiting_upgrade.is_empty()) + else if (!lock->active_exclusive.is_empty() || + !lock->active_shared_waiting_upgrade.is_empty()) { /* Exclusive MDL owner won't wait on table-level lock the same @@ -591,13 +592,14 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) signalled= TRUE; break; } - else if ((lh= lock_data->active_shared.head())) + else if ((conf_lock_data= lock->active_shared.head())) { - signalled= notify_thread_having_shared_lock(thd, lh->ctx->thd); + signalled= notify_thread_having_shared_lock(thd, + conf_lock_data->ctx->thd); break; } } - if (!l) + if (!lock_data) break; if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); @@ -617,15 +619,16 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { /* Remove our pending lock requests from the locks. */ it.rewind(); - while ((l= it++)) + while ((lock_data= it++)) { - DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING); - release_lock(l); + DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && + lock_data->state == MDL_PENDING); + release_lock(lock_data); /* Return lock request to its initial state. */ - l->type= MDL_SHARED; - l->prio= MDL_NORMAL_PRIO; - l->is_upgradable= FALSE; - context->locks.remove(l); + lock_data->type= MDL_SHARED; + lock_data->prio= MDL_NORMAL_PRIO; + lock_data->is_upgradable= FALSE; + context->locks.remove(lock_data); } /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); @@ -634,20 +637,20 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) } } it.rewind(); - while ((l= it++)) + while ((lock_data= it++)) { - global_lock.intention_exclusive_acquired++; - lock_data= l->lock_data; - lock_data->waiting_exclusive.remove(l); - lock_data->active_exclusive.push_front(l); - l->state= MDL_ACQUIRED; - if (lock_data->cached_object) - (*lock_data->cached_object_release_hook)(lock_data->cached_object); - lock_data->cached_object= NULL; + global_lock.active_intention_exclusive++; + lock= lock_data->lock; + lock->waiting_exclusive.remove(lock_data); + lock->active_exclusive.push_front(lock_data); + lock_data->state= MDL_ACQUIRED; + if (lock->cached_object) + (*lock->cached_object_release_hook)(lock->cached_object); + lock->cached_object= NULL; } /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); - return FALSE; + return FALSE; } @@ -676,9 +679,9 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, char key[MAX_DBKEY_LENGTH]; uint key_length; bool signalled= FALSE; - MDL_LOCK *l, *lh; - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); + MDL_LOCK_DATA *lock_data, *conf_lock_data; + MDL_LOCK *lock; + I_P_List_iterator it(context->locks); const char *old_msg; THD *thd= context->thd; @@ -696,41 +699,43 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); - while ((l= it++)) - if (l->key_length == key_length && !memcmp(l->key, key, key_length) && - l->type == MDL_SHARED) + while ((lock_data= it++)) + if (lock_data->key_length == key_length && + !memcmp(lock_data->key, key, key_length) && + lock_data->type == MDL_SHARED) { DBUG_PRINT("info", ("found shared lock for upgrade")); - DBUG_ASSERT(l->state == MDL_ACQUIRED); - DBUG_ASSERT(l->is_upgradable); - l->state= MDL_PENDING_UPGRADE; - lock_data= l->lock_data; - lock_data->active_shared.remove(l); - lock_data->active_shared_waiting_upgrade.push_front(l); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + DBUG_ASSERT(lock_data->is_upgradable); + lock_data->state= MDL_PENDING_UPGRADE; + lock= lock_data->lock; + lock->active_shared.remove(lock_data); + lock->active_shared_waiting_upgrade.push_front(lock_data); } while (1) { DBUG_PRINT("info", ("looking at conflicting locks")); it.rewind(); - while ((l= it++)) + while ((lock_data= it++)) { - if (l->state == MDL_PENDING_UPGRADE) + if (lock_data->state == MDL_PENDING_UPGRADE) { - DBUG_ASSERT(l->type == MDL_SHARED); + DBUG_ASSERT(lock_data->type == MDL_SHARED); - lock_data= l->lock_data; + lock= lock_data->lock; - DBUG_ASSERT(global_lock.shared_acquired == 0 && - global_lock.intention_exclusive_acquired); + DBUG_ASSERT(global_lock.active_shared == 0 && + global_lock.active_intention_exclusive); - if ((lh= lock_data->active_shared.head())) + if ((conf_lock_data= lock->active_shared.head())) { DBUG_PRINT("info", ("found active shared locks")); - signalled= notify_thread_having_shared_lock(thd, lh->ctx->thd); + signalled= notify_thread_having_shared_lock(thd, + conf_lock_data->ctx->thd); break; } - else if (!lock_data->active_exclusive.is_empty()) + else if (!lock->active_exclusive.is_empty()) { DBUG_PRINT("info", ("found active exclusive locks")); signalled= TRUE; @@ -738,7 +743,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, } } } - if (!l) + if (!lock_data) break; if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); @@ -758,14 +763,14 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, if (thd->killed) { it.rewind(); - while ((l= it++)) - if (l->state == MDL_PENDING_UPGRADE) + while ((lock_data= it++)) + if (lock_data->state == MDL_PENDING_UPGRADE) { - DBUG_ASSERT(l->type == MDL_SHARED); - l->state= MDL_ACQUIRED; - lock_data= l->lock_data; - lock_data->active_shared_waiting_upgrade.remove(l); - lock_data->active_shared.push_front(l); + DBUG_ASSERT(lock_data->type == MDL_SHARED); + lock_data->state= MDL_ACQUIRED; + lock= lock_data->lock; + lock->active_shared_waiting_upgrade.remove(lock_data); + lock->active_shared.push_front(lock_data); } /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); @@ -775,18 +780,18 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, } it.rewind(); - while ((l= it++)) - if (l->state == MDL_PENDING_UPGRADE) + while ((lock_data= it++)) + if (lock_data->state == MDL_PENDING_UPGRADE) { - DBUG_ASSERT(l->type == MDL_SHARED); - lock_data= l->lock_data; - lock_data->active_shared_waiting_upgrade.remove(l); - lock_data->active_exclusive.push_front(l); - l->type= MDL_EXCLUSIVE; - l->state= MDL_ACQUIRED; - if (lock_data->cached_object) - (*lock_data->cached_object_release_hook)(lock_data->cached_object); - lock_data->cached_object= 0; + DBUG_ASSERT(lock_data->type == MDL_SHARED); + lock= lock_data->lock; + lock->active_shared_waiting_upgrade.remove(lock_data); + lock->active_exclusive.push_front(lock_data); + lock_data->type= MDL_EXCLUSIVE; + lock_data->state= MDL_ACQUIRED; + if (lock->cached_object) + (*lock->cached_object_release_hook)(lock->cached_object); + lock->cached_object= 0; } /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ @@ -817,40 +822,42 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, it gives sligthly more false negatives. */ -bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *l) +bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data) { - MDL_LOCK_DATA *lock_data; + MDL_LOCK *lock; - DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING); + DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && + lock_data->state == MDL_PENDING); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - if (!(lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, - l->key_length))) + if (!(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, + lock_data->key_length))) { - lock_data= get_lock_data_object(); - lock_data->active_exclusive.push_front(l); - lock_data->lock_count= 1; - my_hash_insert(&mdl_locks, (uchar*)lock_data); - l->state= MDL_ACQUIRED; - l->lock_data= lock_data; - lock_data= 0; - global_lock.intention_exclusive_acquired++; + lock= get_lock_object(); + lock->active_exclusive.push_front(lock_data); + lock->lock_data_count= 1; + my_hash_insert(&mdl_locks, (uchar*)lock); + lock_data->state= MDL_ACQUIRED; + lock_data->lock= lock; + lock= 0; + global_lock.active_intention_exclusive++; } pthread_mutex_unlock(&LOCK_mdl); /* FIXME: We can't leave pending MDL_EXCLUSIVE lock request in the list since - for such locks we assume that they have MDL_LOCK::lock properly set. + for such locks we assume that they have MDL_LOCK_DATA::lock properly set. Long term we should clearly define relation between lock types, - presence in the context lists and MDL_LOCK::lock values. + presence in the context lists and MDL_LOCK_DATA::lock values. */ - if (lock_data) - context->locks.remove(l); + if (lock) + context->locks.remove(lock_data); - return lock_data; + return lock; } @@ -859,7 +866,7 @@ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *l) Holding this lock will block all requests for exclusive locks and shared locks which can be potentially upgraded to exclusive - (see MDL_LOCK::is_upgradable). + (see MDL_LOCK_DATA::is_upgradable). @param context Current metadata locking context. @@ -878,20 +885,20 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) pthread_mutex_lock(&LOCK_mdl); - global_lock.shared_pending++; + global_lock.waiting_shared++; old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); - while (!thd->killed && global_lock.intention_exclusive_acquired) + while (!thd->killed && global_lock.active_intention_exclusive) pthread_cond_wait(&COND_mdl, &LOCK_mdl); - global_lock.shared_pending--; + global_lock.waiting_shared--; if (thd->killed) { /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); return TRUE; } - global_lock.shared_acquired++; + global_lock.active_shared++; context->has_global_shared_lock= TRUE; /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); @@ -916,9 +923,9 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) bool mdl_wait_for_locks(MDL_CONTEXT *context) { - MDL_LOCK *l; MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); + MDL_LOCK *lock; + I_P_List_iterator it(context->locks); const char *old_msg; THD *thd= context->thd; @@ -941,24 +948,24 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) pthread_mutex_lock(&LOCK_mdl); old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); it.rewind(); - while ((l= it++)) + while ((lock_data= it++)) { - DBUG_ASSERT(l->state == MDL_PENDING); - if ((l->is_upgradable || l->type == MDL_EXCLUSIVE) && - (global_lock.shared_acquired || global_lock.shared_pending)) + DBUG_ASSERT(lock_data->state == MDL_PENDING); + if ((lock_data->is_upgradable || lock_data->type == MDL_EXCLUSIVE) && + (global_lock.active_shared || global_lock.waiting_shared)) break; /* To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. */ - if (l->type == MDL_SHARED && - (lock_data= (MDL_LOCK_DATA *)my_hash_search(&mdl_locks, (uchar*)l->key, - l->key_length)) && - !(lock_data->active_exclusive.is_empty() && - lock_data->active_shared_waiting_upgrade.is_empty() && - lock_data->waiting_exclusive.is_empty())) + if (lock_data->type == MDL_SHARED && + (lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, + lock_data->key_length)) && + !(lock->active_exclusive.is_empty() && + lock->active_shared_waiting_upgrade.is_empty() && + lock->waiting_exclusive.is_empty())) break; } - if (!l) + if (!lock_data) { pthread_mutex_unlock(&LOCK_mdl); break; @@ -976,50 +983,51 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) ownership of which is represented by lock request object. */ -static void release_lock(MDL_LOCK *l) +static void release_lock(MDL_LOCK_DATA *lock_data) { - MDL_LOCK_DATA *lock_data; + MDL_LOCK *lock; DBUG_ENTER("release_lock"); - DBUG_PRINT("enter", ("db=%s name=%s", l->key + 4, - l->key + 4 + strlen(l->key + 4) + 1)); + DBUG_PRINT("enter", ("db=%s name=%s", lock_data->key + 4, + lock_data->key + 4 + strlen(lock_data->key + 4) + 1)); - lock_data= l->lock_data; - if (lock_data->has_one_lock()) + lock= lock_data->lock; + if (lock->has_one_lock_data()) { - my_hash_delete(&mdl_locks, (uchar *)lock_data); + my_hash_delete(&mdl_locks, (uchar *)lock); DBUG_PRINT("info", ("releasing cached_object cached_object=%p", - lock_data->cached_object)); - if (lock_data->cached_object) - (*lock_data->cached_object_release_hook)(lock_data->cached_object); - release_lock_data_object(lock_data); - if (l->type == MDL_EXCLUSIVE && l->state == MDL_ACQUIRED || - l->type == MDL_SHARED && l->state == MDL_ACQUIRED && l->is_upgradable) - global_lock.intention_exclusive_acquired--; + lock->cached_object)); + if (lock->cached_object) + (*lock->cached_object_release_hook)(lock->cached_object); + release_lock_object(lock); + if (lock_data->type == MDL_EXCLUSIVE && lock_data->state == MDL_ACQUIRED || + lock_data->type == MDL_SHARED && lock_data->state == MDL_ACQUIRED && + lock_data->is_upgradable) + global_lock.active_intention_exclusive--; } else { - switch (l->type) + switch (lock_data->type) { case MDL_SHARED: - lock_data->active_shared.remove(l); - if (l->is_upgradable) - global_lock.intention_exclusive_acquired--; + lock->active_shared.remove(lock_data); + if (lock_data->is_upgradable) + global_lock.active_intention_exclusive--; break; case MDL_EXCLUSIVE: - if (l->state == MDL_PENDING) - lock_data->waiting_exclusive.remove(l); + if (lock_data->state == MDL_PENDING) + lock->waiting_exclusive.remove(lock_data); else { - lock_data->active_exclusive.remove(l); - global_lock.intention_exclusive_acquired--; + lock->active_exclusive.remove(lock_data); + global_lock.active_intention_exclusive--; } break; default: /* TODO Really? How about problems during lock upgrade ? */ DBUG_ASSERT(0); } - lock_data->lock_count--; + lock->lock_data_count--; } DBUG_VOID_RETURN; @@ -1040,28 +1048,28 @@ static void release_lock(MDL_LOCK *l) void mdl_release_locks(MDL_CONTEXT *context) { - MDL_LOCK *l; - I_P_List_iterator it(context->locks); + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); DBUG_ENTER("mdl_release_locks"); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - while ((l= it++)) + while ((lock_data= it++)) { - DBUG_PRINT("info", ("found lock to release l=%p", l)); + DBUG_PRINT("info", ("found lock to release lock_data=%p", lock_data)); /* We should not release locks which pending shared locks as these are not associated with lock object and don't present in its lists. Allows us to avoid problems in open_tables() in case of back-off */ - if (!(l->type == MDL_SHARED && l->state == MDL_PENDING)) + if (!(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING)) { - release_lock(l); - l->state= MDL_PENDING; + release_lock(lock_data); + lock_data->state= MDL_PENDING; #ifndef DBUG_OFF - l->lock_data= 0; + lock_data->lock= 0; #endif } /* @@ -1091,28 +1099,28 @@ void mdl_release_locks(MDL_CONTEXT *context) void mdl_release_exclusive_locks(MDL_CONTEXT *context) { - MDL_LOCK *l; - I_P_List_iterator it(context->locks); + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - while ((l= it++)) + while ((lock_data= it++)) { - if (l->type == MDL_EXCLUSIVE) + if (lock_data->type == MDL_EXCLUSIVE) { - DBUG_ASSERT(l->state == MDL_ACQUIRED); - release_lock(l); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + release_lock(lock_data); #ifndef DBUG_OFF - l->ctx= 0; - l->lock_data= 0; + lock_data->ctx= 0; + lock_data->lock= 0; #endif - l->state= MDL_PENDING; + lock_data->state= MDL_PENDING; /* Return lock request to its initial state. */ - l->type= MDL_SHARED; - l->prio= MDL_NORMAL_PRIO; - l->is_upgradable= FALSE; - context->locks.remove(l); + lock_data->type= MDL_SHARED; + lock_data->prio= MDL_NORMAL_PRIO; + lock_data->is_upgradable= FALSE; + context->locks.remove(lock_data); } } pthread_cond_broadcast(&COND_mdl); @@ -1124,29 +1132,29 @@ void mdl_release_exclusive_locks(MDL_CONTEXT *context) Release a lock. Removes the lock from the context. - @param context Context containing lock in question - @param lock Lock to be released + @param context Context containing lock in question + @param lock_data Lock to be released @note Resets lock request for lock released back to its initial state (i.e.sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO). */ -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lr) +void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) { safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - release_lock(lr); + release_lock(lock_data); #ifndef DBUG_OFF - lr->ctx= 0; - lr->lock_data= 0; + lock_data->ctx= 0; + lock_data->lock= 0; #endif - lr->state= MDL_PENDING; + lock_data->state= MDL_PENDING; /* Return lock request to its initial state. */ - lr->type= MDL_SHARED; - lr->prio= MDL_NORMAL_PRIO; - lr->is_upgradable= FALSE; - context->locks.remove(lr); + lock_data->type= MDL_SHARED; + lock_data->prio= MDL_NORMAL_PRIO; + lock_data->is_upgradable= FALSE; + context->locks.remove(lock_data); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1161,23 +1169,23 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lr) void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context) { - MDL_LOCK *l; MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); + MDL_LOCK *lock; + I_P_List_iterator it(context->locks); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - while ((l= it++)) - if (l->type == MDL_EXCLUSIVE) + while ((lock_data= it++)) + if (lock_data->type == MDL_EXCLUSIVE) { - DBUG_ASSERT(l->state == MDL_ACQUIRED); - if (!l->is_upgradable) - global_lock.intention_exclusive_acquired--; - lock_data= l->lock_data; - lock_data->active_exclusive.remove(l); - l->type= MDL_SHARED; - lock_data->active_shared.push_front(l); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + if (!lock_data->is_upgradable) + global_lock.active_intention_exclusive--; + lock= lock_data->lock; + lock->active_exclusive.remove(lock_data); + lock_data->type= MDL_SHARED; + lock->active_shared.push_front(lock_data); } pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); @@ -1196,7 +1204,7 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) DBUG_ASSERT(context->has_global_shared_lock); pthread_mutex_lock(&LOCK_mdl); - global_lock.shared_acquired--; + global_lock.active_shared--; context->has_global_shared_lock= FALSE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); @@ -1221,15 +1229,18 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, { char key[MAX_DBKEY_LENGTH]; uint key_length; - MDL_LOCK *l; - I_P_List_iterator it(context->locks); + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); int4store(key, type); key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - while ((l= it++) && (l->key_length != key_length || memcmp(l->key, key, key_length))) + while ((lock_data= it++) && + (lock_data->key_length != key_length || + memcmp(lock_data->key, key, key_length))) continue; - return (l && l->type == MDL_EXCLUSIVE && l->state == MDL_ACQUIRED); + return (lock_data && lock_data->type == MDL_EXCLUSIVE && + lock_data->state == MDL_ACQUIRED); } @@ -1251,18 +1262,18 @@ bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, { char key[MAX_DBKEY_LENGTH]; uint key_length; - MDL_LOCK *l; - I_P_List_iterator it(context->locks); + MDL_LOCK_DATA *lock_data; + I_P_List_iterator it(context->locks); int4store(key, type); key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - while ((l= it++) && (l->key_length != key_length || - memcmp(l->key, key, key_length) || - l->state == MDL_PENDING)) + while ((lock_data= it++) && (lock_data->key_length != key_length || + memcmp(lock_data->key, key, key_length) || + lock_data->state == MDL_PENDING)) continue; - return l; + return lock_data; } @@ -1270,21 +1281,22 @@ bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, Check if we have any pending exclusive locks which conflict with existing shared lock. - @param l Shared lock against which check should be performed. + @param lock_data Shared lock against which check should be performed. @return TRUE if there are any conflicting locks, FALSE otherwise. */ -bool mdl_has_pending_conflicting_lock(MDL_LOCK *l) +bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data) { bool result; - DBUG_ASSERT(l->type == MDL_SHARED && l->state == MDL_ACQUIRED); + DBUG_ASSERT(lock_data->type == MDL_SHARED && + lock_data->state == MDL_ACQUIRED); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - result= !(l->lock_data->waiting_exclusive.is_empty() && - l->lock_data->active_shared_waiting_upgrade.is_empty()); + result= !(lock_data->lock->waiting_exclusive.is_empty() && + lock_data->lock->active_shared_waiting_upgrade.is_empty()); pthread_mutex_unlock(&LOCK_mdl); return result; } @@ -1293,7 +1305,7 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK *l) /** Associate pointer to an opaque object with a lock. - @param l Lock request for the lock with which the + @param lock_data Lock request for the lock with which the object should be associated. @param cached_object Pointer to the object @param release_hook Cleanup function to be called when MDL subsystem @@ -1318,15 +1330,16 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK *l) lock on this name is released. */ -void mdl_set_cached_object(MDL_LOCK *l, void *cached_object, +void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, mdl_cached_object_release_hook release_hook) { DBUG_ENTER("mdl_set_cached_object"); - DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", l->key + 4, - l->key + 4 + strlen(l->key + 4) + 1, + DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", lock_data->key + 4, + lock_data->key + 4 + strlen(lock_data->key + 4) + 1, cached_object)); - DBUG_ASSERT(l->state == MDL_ACQUIRED || l->state == MDL_PENDING_UPGRADE); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED || + lock_data->state == MDL_PENDING_UPGRADE); /* TODO: This assumption works now since we do mdl_get_cached_object() @@ -1334,10 +1347,10 @@ void mdl_set_cached_object(MDL_LOCK *l, void *cached_object, this becomes false we will have to call release_hook here and use additional mutex protecting 'cached_object' member. */ - DBUG_ASSERT(!l->lock_data->cached_object); + DBUG_ASSERT(!lock_data->lock->cached_object); - l->lock_data->cached_object= cached_object; - l->lock_data->cached_object_release_hook= release_hook; + lock_data->lock->cached_object= cached_object; + lock_data->lock->cached_object_release_hook= release_hook; DBUG_VOID_RETURN; } @@ -1346,14 +1359,15 @@ void mdl_set_cached_object(MDL_LOCK *l, void *cached_object, /** Get a pointer to an opaque object that associated with the lock. - @param l Lock request for the lock with which the object is - associated. + @param lock_data Lock request for the lock with which the object is + associated. @return Pointer to an opaque object associated with the lock. */ -void* mdl_get_cached_object(MDL_LOCK *l) +void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data) { - DBUG_ASSERT(l->state == MDL_ACQUIRED || l->state == MDL_PENDING_UPGRADE); - return l->lock_data->cached_object; + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED || + lock_data->state == MDL_PENDING_UPGRADE); + return lock_data->lock->cached_object; } diff --git a/sql/mdl.h b/sql/mdl.h index 2b144e250b1..74c90c01730 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -22,8 +22,8 @@ class THD; -struct MDL_LOCK; struct MDL_LOCK_DATA; +struct MDL_LOCK; struct MDL_CONTEXT; /** Type of metadata lock request. */ @@ -54,7 +54,7 @@ enum enum_mdl_prio {MDL_NORMAL_PRIO=0, MDL_HIGH_PRIO}; "key" or "name". */ -struct MDL_LOCK +struct MDL_LOCK_DATA { char *key; uint key_length; @@ -72,17 +72,17 @@ private: /** Pointers for participating in the list of lock requests for this context. */ - MDL_LOCK *next_context; - MDL_LOCK **prev_context; + MDL_LOCK_DATA *next_context; + MDL_LOCK_DATA **prev_context; /** Pointers for participating in the list of satisfied/pending requests for the lock. */ - MDL_LOCK *next_lock; - MDL_LOCK **prev_lock; + MDL_LOCK_DATA *next_lock; + MDL_LOCK_DATA **prev_lock; - friend struct MDL_LOCK_context; - friend struct MDL_LOCK_lock; + friend struct MDL_LOCK_DATA_context; + friend struct MDL_LOCK_DATA_lock; public: /* @@ -90,23 +90,23 @@ public: request is satisified or is present in the list of pending lock requests for particular lock. */ - MDL_LOCK_DATA *lock_data; + MDL_LOCK *lock; MDL_CONTEXT *ctx; }; /** - Helper class which specifies which members of MDL_LOCK are used for + Helper class which specifies which members of MDL_LOCK_DATA are used for participation in the list lock requests belonging to one context. */ -struct MDL_LOCK_context +struct MDL_LOCK_DATA_context { - static inline MDL_LOCK **next_ptr(MDL_LOCK *l) + static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) { return &l->next_context; } - static inline MDL_LOCK ***prev_ptr(MDL_LOCK *l) + static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) { return &l->prev_context; } @@ -114,17 +114,17 @@ struct MDL_LOCK_context /** - Helper class which specifies which members of MDL_LOCK are used for + Helper class which specifies which members of MDL_LOCK_DATA are used for participation in the list of satisfied/pending requests for the lock. */ -struct MDL_LOCK_lock +struct MDL_LOCK_DATA_lock { - static inline MDL_LOCK **next_ptr(MDL_LOCK *l) + static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) { return &l->next_lock; } - static inline MDL_LOCK ***prev_ptr(MDL_LOCK *l) + static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) { return &l->prev_lock; } @@ -138,7 +138,7 @@ struct MDL_LOCK_lock struct MDL_CONTEXT { - I_P_List locks; + I_P_List locks; bool has_global_shared_lock; THD *thd; }; @@ -153,21 +153,21 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); -void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db, - const char *name); -MDL_LOCK *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root); -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK *lock); +void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, + const char *db, const char *name); +MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, + MEM_ROOT *root); +void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); void mdl_remove_all_locks(MDL_CONTEXT *context); /** Set type of lock request. Can be only applied to pending locks. */ -inline void mdl_set_lock_type(MDL_LOCK *lock, enum_mdl_type lock_type) +inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) { - DBUG_ASSERT(lock->state == MDL_PENDING); - lock->type= lock_type; + DBUG_ASSERT(lock_data->state == MDL_PENDING); + lock_data->type= lock_type; } /** @@ -175,10 +175,10 @@ inline void mdl_set_lock_type(MDL_LOCK *lock, enum_mdl_type lock_type) for shared locks. */ -inline void mdl_set_lock_priority(MDL_LOCK *lock, enum_mdl_prio prio) +inline void mdl_set_lock_priority(MDL_LOCK_DATA *lock_data, enum_mdl_prio prio) { - DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING); - lock->prio= prio; + DBUG_ASSERT(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING); + lock_data->prio= prio; } /** @@ -186,24 +186,25 @@ inline void mdl_set_lock_priority(MDL_LOCK *lock, enum_mdl_prio prio) to pending locks. */ -inline void mdl_set_upgradable(MDL_LOCK *lock) +inline void mdl_set_upgradable(MDL_LOCK_DATA *lock_data) { - DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING); - lock->is_upgradable= TRUE; + DBUG_ASSERT(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING); + lock_data->is_upgradable= TRUE; } -bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry); +bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, const char *db, const char *name); -bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *lock); +bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data); bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); bool mdl_wait_for_locks(MDL_CONTEXT *context); void mdl_release_locks(MDL_CONTEXT *context); void mdl_release_exclusive_locks(MDL_CONTEXT *context); -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lock); +void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context); void mdl_release_global_shared_lock(MDL_CONTEXT *context); @@ -212,7 +213,7 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, const char *name); -bool mdl_has_pending_conflicting_lock(MDL_LOCK *l); +bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data); inline bool mdl_has_locks(MDL_CONTEXT *context) { @@ -224,9 +225,10 @@ inline bool mdl_has_locks(MDL_CONTEXT *context) Get iterator for walking through all lock requests in the context. */ -inline I_P_List_iterator mdl_get_locks(MDL_CONTEXT *ctx) +inline I_P_List_iterator +mdl_get_locks(MDL_CONTEXT *ctx) { - I_P_List_iterator result(ctx->locks); + I_P_List_iterator result(ctx->locks); return result; } @@ -234,9 +236,9 @@ inline I_P_List_iterator mdl_get_locks(MDL_CONTEXT * Give metadata lock request object for the table get table definition cache key corresponding to it. - @param l [in] Lock request object for the table. - @param key [out] LEX_STRING object where table definition cache key - should be put. + @param lock_data [in] Lock request object for the table. + @param key [out] LEX_STRING object where table definition cache key + should be put. @note This key will have the same life-time as this lock request object. @@ -245,16 +247,16 @@ inline I_P_List_iterator mdl_get_locks(MDL_CONTEXT * and memory by avoiding generating these TDC keys from table list. */ -inline void mdl_get_tdc_key(MDL_LOCK *l, LEX_STRING *key) +inline void mdl_get_tdc_key(MDL_LOCK_DATA *lock_data, LEX_STRING *key) { - key->str= l->key + 4; - key->length= l->key_length - 4; + key->str= lock_data->key + 4; + key->length= lock_data->key_length - 4; } typedef void (* mdl_cached_object_release_hook)(void *); -void* mdl_get_cached_object(MDL_LOCK *l); -void mdl_set_cached_object(MDL_LOCK *l, void *cached_object, +void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data); +void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, mdl_cached_object_release_hook release_hook); #endif diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 14573cd6884..fdf9909f85e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3984,9 +3984,10 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_lock= mdl_alloc_lock(0, table->db, table->table_name, - thd->mdl_el_root ? thd->mdl_el_root : - thd->mem_root); + table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, + thd->mdl_el_root ? + thd->mdl_el_root : + thd->mem_root); /* Everyting else should be zeroed */ @@ -4028,9 +4029,9 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_lock= mdl_alloc_lock(0, table->db, table->table_name, - thd->mdl_el_root ? thd->mdl_el_root : - thd->mem_root); + table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, + thd->mdl_el_root ? thd->mdl_el_root : + thd->mem_root); lex->add_to_query_tables(table); return table; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 13218f3a193..44db7938caf 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1527,7 +1527,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) if (table->child_l || table->parent) detach_merge_children(table, TRUE); - table->mdl_lock= 0; + table->mdl_lock_data= 0; if (table->needs_reopen() || thd->version != refresh_version || !table->db_stat) { @@ -2535,7 +2535,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, char key[MAX_DBKEY_LENGTH]; uint key_length; char *alias= table_list->alias; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; int error; TABLE_SHARE *share; DBUG_ENTER("open_table"); @@ -2717,8 +2717,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, This is the normal use case. */ - mdl_lock= table_list->mdl_lock; - mdl_add_lock(&thd->mdl_context, mdl_lock); + mdl_lock_data= table_list->mdl_lock_data; + mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (table_list->open_type) { @@ -2731,7 +2731,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, shared locks. This invariant is preserved here and is also enforced by asserts in metadata locking subsystem. */ - mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(0); } @@ -2740,10 +2740,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, bool retry; if (table_list->mdl_upgradable) - mdl_set_upgradable(mdl_lock); - mdl_set_lock_priority(mdl_lock, (flags & MYSQL_LOCK_IGNORE_FLUSH) ? - MDL_HIGH_PRIO : MDL_NORMAL_PRIO); - if (mdl_acquire_shared_lock(mdl_lock, &retry)) + mdl_set_upgradable(mdl_lock_data); + mdl_set_lock_priority(mdl_lock_data, (flags & MYSQL_LOCK_IGNORE_FLUSH) ? + MDL_HIGH_PRIO : MDL_NORMAL_PRIO); + if (mdl_acquire_shared_lock(mdl_lock_data, &retry)) { if (retry) *action= OT_BACK_OFF_AND_RETRY; @@ -2792,7 +2792,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(0); } - if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock))) + if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_data))) { if (!(share= get_table_share_with_create(thd, table_list, key, key_length, OPEN_VIEW, @@ -2867,7 +2867,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, so we need to increase reference counter; */ reference_table_share(share); - mdl_set_cached_object(mdl_lock, share, table_share_release_hook); + mdl_set_cached_object(mdl_lock_data, share, table_share_release_hook); } else { @@ -2978,7 +2978,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) mdl_downgrade_exclusive_locks(&thd->mdl_context); - table->mdl_lock= mdl_lock; + table->mdl_lock_data= mdl_lock_data; table->next=thd->open_tables; /* Link into simple list */ thd->open_tables=table; @@ -3025,7 +3025,7 @@ err_unlock: release_table_share(share); err_unlock2: pthread_mutex_unlock(&LOCK_open); - mdl_release_lock(&thd->mdl_context, mdl_lock); + mdl_release_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(0); } @@ -3167,7 +3167,7 @@ bool reopen_table(TABLE *table) (void) closefrm(&tmp, 1); // close file, free everything goto end; } - tmp.mdl_lock= table->mdl_lock; + tmp.mdl_lock_data= table->mdl_lock_data; table_def_change_share(table, tmp.s); /* Avoid wiping out TABLE's position in new share's used tables list. */ @@ -3981,8 +3981,8 @@ static bool handle_failed_open_table_attempt(THD *thd, TABLE_LIST *table, mdl_remove_all_locks(&thd->mdl_context); break; case OT_DISCOVER: - mdl_set_lock_type(table->mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, table->mdl_lock); + mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) return TRUE; pthread_mutex_lock(&LOCK_open); @@ -3995,8 +3995,8 @@ static bool handle_failed_open_table_attempt(THD *thd, TABLE_LIST *table, mdl_release_exclusive_locks(&thd->mdl_context); break; case OT_REPAIR: - mdl_set_lock_type(table->mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, table->mdl_lock); + mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) return TRUE; pthread_mutex_lock(&LOCK_open); @@ -8518,7 +8518,7 @@ void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_na static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) { - MDL_LOCK *l; + MDL_LOCK_DATA *lock_data; TABLE_SHARE *share; const char *old_msg; LEX_STRING key; @@ -8534,17 +8534,18 @@ static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) mysql_ha_flush(thd); pthread_mutex_lock(&LOCK_open); - I_P_List_iterator it= mdl_get_locks(context); - while ((l= it++)) + I_P_List_iterator it= mdl_get_locks(context); + while ((lock_data= it++)) { - mdl_get_tdc_key(l, &key); + mdl_get_tdc_key(lock_data, &key); if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key.str, key.length)) && share->version != refresh_version && !share->used_tables.is_empty()) break; } - if (!l) + if (!lock_data) { pthread_mutex_unlock(&LOCK_open); break; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fb48f32660b..f5c6dfd8986 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1089,7 +1089,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_LOCK *mdl_lock= 0; + MDL_LOCK_DATA *mdl_lock_data= 0; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1164,10 +1164,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock); + mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_open); @@ -1197,13 +1197,13 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_lock) - mdl_release_lock(&thd->mdl_context, mdl_lock); + if (mdl_lock_data) + mdl_release_lock(&thd->mdl_context, mdl_lock_data); } else if (error) { - if (mdl_lock) - mdl_release_lock(&thd->mdl_context, mdl_lock); + if (mdl_lock_data) + mdl_release_lock(&thd->mdl_context, mdl_lock_data); } DBUG_RETURN(error); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 87e9538b48f..15c73fcadfe 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -125,7 +125,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) { TABLE **table_ptr; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; /* Though we could take the table pointer from hash_tables->table, @@ -141,7 +141,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) if (*table_ptr) { (*table_ptr)->file->ha_index_or_rnd_end(); - mdl_lock= (*table_ptr)->mdl_lock; + mdl_lock_data= (*table_ptr)->mdl_lock_data; pthread_mutex_lock(&LOCK_open); if (close_thread_table(thd, table_ptr)) { @@ -149,7 +149,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) broadcast_refresh(); } pthread_mutex_unlock(&LOCK_open); - mdl_release_lock(&thd->handler_mdl_context, mdl_lock); + mdl_release_lock(&thd->handler_mdl_context, mdl_lock_data); } else if (tables->table) { @@ -189,7 +189,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { TABLE_LIST *hash_tables = NULL; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; char *db, *name, *alias, *mdlkey; uint dblen, namelen, aliaslen, counter; int error; @@ -245,7 +245,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) &db, (uint) dblen, &name, (uint) namelen, &alias, (uint) aliaslen, - &mdl_lock, sizeof(MDL_LOCK), + &mdl_lock_data, sizeof(MDL_LOCK_DATA), &mdlkey, MAX_DBKEY_LENGTH, NullS))) { @@ -260,8 +260,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - mdl_init_lock(mdl_lock, mdlkey, 0, db, name); - hash_tables->mdl_lock= mdl_lock; + mdl_init_lock(mdl_lock_data, mdlkey, 0, db, name); + hash_tables->mdl_lock_data= mdl_lock_data; /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) @@ -798,10 +798,12 @@ void mysql_ha_flush(THD *thd) for (uint i= 0; i < thd->handler_tables_hash.records; i++) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); - /* TABLE::mdl_lock is 0 for temporary tables so we need extra check. */ + /* + TABLE::mdl_lock_data is 0 for temporary tables so we need extra check. + */ if (hash_tables->table && - (hash_tables->table->mdl_lock && - mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock) || + (hash_tables->table->mdl_lock_data && + mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock_data) || hash_tables->table->needs_reopen())) mysql_ha_close_table(thd, hash_tables); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2c043922cc8..24939f1cd21 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6534,9 +6534,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_lock= mdl_alloc_lock(0 , ptr->db, ptr->table_name, - thd->mdl_el_root ? thd->mdl_el_root : - thd->mem_root); + ptr->mdl_lock_data= mdl_alloc_lock(0 , ptr->db, ptr->table_name, + thd->mdl_el_root ? thd->mdl_el_root : + thd->mem_root); DBUG_RETURN(ptr); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 236bca76c7d..647f914d28c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3106,7 +3106,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, char key[MAX_DBKEY_LENGTH]; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; - MDL_LOCK mdl_lock; + MDL_LOCK_DATA mdl_lock_data; char mdlkey[MAX_DBKEY_LENGTH]; bool retry; @@ -3133,10 +3133,10 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, table_list.db= db_name->str; } - mdl_init_lock(&mdl_lock, mdlkey, 0, db_name->str, table_name->str); - table_list.mdl_lock= &mdl_lock; - mdl_add_lock(&thd->mdl_context, &mdl_lock); - mdl_set_lock_priority(&mdl_lock, MDL_HIGH_PRIO); + mdl_init_lock(&mdl_lock_data, mdlkey, 0, db_name->str, table_name->str); + table_list.mdl_lock_data= &mdl_lock_data; + mdl_add_lock(&thd->mdl_context, &mdl_lock_data); + mdl_set_lock_priority(&mdl_lock_data, MDL_HIGH_PRIO); /* TODO: investigate if in this particular situation we can get by @@ -3145,7 +3145,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, */ while (1) { - if (mdl_acquire_shared_lock(&mdl_lock, &retry)) + if (mdl_acquire_shared_lock(&mdl_lock_data, &retry)) { if (!retry || mdl_wait_for_locks(&thd->mdl_context)) { @@ -3212,7 +3212,7 @@ err_unlock: pthread_mutex_unlock(&LOCK_open); err: - mdl_release_lock(&thd->mdl_context, &mdl_lock); + mdl_release_lock(&thd->mdl_context, &mdl_lock_data); thd->clear_error(); return res; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9888dceef54..2a41fe2008c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4043,15 +4043,15 @@ warn: static bool lock_table_name_if_not_cached(THD *thd, const char *db, const char *table_name, - MDL_LOCK **lock) + MDL_LOCK_DATA **lock_data) { - if (!(*lock= mdl_alloc_lock(0, db, table_name, thd->mem_root))) + if (!(*lock_data= mdl_alloc_lock(0, db, table_name, thd->mem_root))) return TRUE; - mdl_set_lock_type(*lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, *lock); - if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock)) + mdl_set_lock_type(*lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, *lock_data); + if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_data)) { - *lock= 0; + *lock_data= 0; } return FALSE; } @@ -4067,7 +4067,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, bool internal_tmp_table, uint select_field_count) { - MDL_LOCK *target_lock= 0; + MDL_LOCK_DATA *target_lock_data= 0; bool result; DBUG_ENTER("mysql_create_table"); @@ -4090,12 +4090,12 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data)) { result= TRUE; goto unlock; } - if (!target_lock) + if (!target_lock_data) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) { @@ -4121,7 +4121,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, select_field_count); unlock: - if (target_lock) + if (target_lock_data) mdl_release_exclusive_locks(&thd->mdl_context); pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) @@ -4359,7 +4359,7 @@ static int send_check_errmsg(THD *thd, TABLE_LIST* table, static int prepare_for_restore(THD* thd, TABLE_LIST* table, HA_CHECK_OPT *check_opt) { - MDL_LOCK *mdl_lock= 0; + MDL_LOCK_DATA *mdl_lock_data= 0; DBUG_ENTER("prepare_for_restore"); if (table->table) // do not overwrite existing tables on restore @@ -4383,10 +4383,10 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); - mdl_lock= mdl_alloc_lock(0, table->db, table->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock); + mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_open); @@ -4395,13 +4395,13 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, if (my_copy(src_path, dst_path, MYF(MY_WME))) { - mdl_release_lock(&thd->mdl_context, mdl_lock); + mdl_release_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed copying .frm file")); } if (mysql_truncate(thd, table, 1)) { - mdl_release_lock(&thd->mdl_context, mdl_lock); + mdl_release_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed generating table from .frm file")); } @@ -4415,8 +4415,8 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, if (reopen_name_locked_table(thd, table)) { pthread_mutex_unlock(&LOCK_open); - if (mdl_lock) - mdl_release_lock(&thd->mdl_context, mdl_lock); + if (mdl_lock_data) + mdl_release_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed to open partially restored table")); } @@ -4436,7 +4436,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, char from[FN_REFLEN],tmp[FN_REFLEN+32]; const char **ext; MY_STAT stat_info; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; DBUG_ENTER("prepare_for_repair"); if (!(check_opt->sql_flags & TT_USEFRM)) @@ -4452,10 +4452,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, TODO: Check that REPAIR's code also conforms to meta-data locking protocol. Fix if it is not. */ - mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock); + mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(0); @@ -5303,7 +5303,7 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { - MDL_LOCK *target_lock= 0; + MDL_LOCK_DATA *target_lock_data= 0; char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1]; uint dst_path_length; char *db= table->db; @@ -5360,9 +5360,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data)) goto err; - if (!target_lock) + if (!target_lock_data) goto table_exists; dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); @@ -5538,7 +5538,7 @@ binlog: res= FALSE; err: - if (target_lock) + if (target_lock_data) mdl_release_exclusive_locks(&thd->mdl_context); DBUG_RETURN(res); } @@ -6477,7 +6477,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint order_num, ORDER *order, bool ignore) { TABLE *table, *new_table= 0; - MDL_LOCK *target_lock= 0; + MDL_LOCK_DATA *target_lock_data= 0; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6700,9 +6700,10 @@ view_err: } else { - if (lock_table_name_if_not_cached(thd, new_db, new_name, &target_lock)) + if (lock_table_name_if_not_cached(thd, new_db, new_name, + &target_lock_data)) DBUG_RETURN(TRUE); - if (!target_lock) + if (!target_lock_data) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); DBUG_RETURN(TRUE); @@ -7128,7 +7129,7 @@ view_err: #ifdef WITH_PARTITION_STORAGE_ENGINE if (fast_alter_partition) { - DBUG_ASSERT(!target_lock); + DBUG_ASSERT(!target_lock_data); DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, db, table_name, @@ -7632,7 +7633,7 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (target_lock) + if (target_lock_data) mdl_release_exclusive_locks(&thd->mdl_context); DBUG_RETURN(TRUE); diff --git a/sql/table.cc b/sql/table.cc index c8814cff685..3943dc8e50b 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4832,8 +4832,9 @@ size_t max_row_length(TABLE *table, const uchar *data) void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_lock= mdl_alloc_lock(0, table_list->db, - table_list->table_name, root); + table_list->mdl_lock_data= mdl_alloc_lock(0, table_list->db, + table_list->table_name, + root); } diff --git a/sql/table.h b/sql/table.h index e64111ef988..601f1e154c9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -30,7 +30,7 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; -struct MDL_LOCK; +struct MDL_LOCK_DATA; /*************************************************************************/ @@ -820,7 +820,7 @@ public: partition_info *part_info; /* Partition related information */ bool no_partitions_used; /* If true, all partitions have been pruned away */ #endif - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; bool fill_item_list(List *item_list) const; void reset_item_list(List *item_list) const; @@ -1423,7 +1423,7 @@ struct TABLE_LIST uint table_open_method; enum enum_schema_table_state schema_table_state; - MDL_LOCK *mdl_lock; + MDL_LOCK_DATA *mdl_lock_data; void calc_md5(char *buffer); void set_underlying_merge(); From 4689cddb23749b161f5822dddbe3ea25e3ca5a5a Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 01:39:13 +0300 Subject: [PATCH 049/466] Backport of: ------------------------------------------------------------ revno: 2630.4.18 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Tue 2008-06-03 21:07:58 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Now during upgrading/downgrading metadata locks we deal with individual metadata lock requests rather than with all requests for this object in the context. This makes API a bit more clear and makes adjust_mdl_locks_upgradability() much nicer. --- sql/lock.cc | 1 + sql/mdl.cc | 259 +++++++++++++++++++++------------------------ sql/mdl.h | 10 +- sql/sql_base.cc | 23 ++-- sql/sql_parse.cc | 23 +--- sql/sql_table.cc | 102 +++++++++++++----- sql/sql_trigger.cc | 5 +- 7 files changed, 221 insertions(+), 202 deletions(-) diff --git a/sql/lock.cc b/sql/lock.cc index b5eaaa05fff..f391b323a59 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -976,6 +976,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) goto end; mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, mdl_lock_data); + lock_table->mdl_lock_data= mdl_lock_data; } if (mdl_acquire_exclusive_locks(&thd->mdl_context)) return 1; diff --git a/sql/mdl.cc b/sql/mdl.cc index 64c011d34bf..7ed2f6e8bdf 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -660,91 +660,95 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) Used in ALTER TABLE, when a copy of the table with the new definition has been constructed. - @param context Context to which shared long belongs - @param type Id of object type - @param db Name of the database - @param name Name of the object + @param context Context to which shared lock belongs + @param lock_data Satisfied request for shared lock to be upgraded - @note In case of failure to upgrade locks (e.g. because upgrader - was killed) leaves locks in their original state (locked - in shared mode). + @note In case of failure to upgrade lock (e.g. because upgrader + was killed) leaves lock in its original state (locked in + shared mode). @retval FALSE Success @retval TRUE Failure (thread was killed) */ -bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, - const char *db, const char *name) +bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data) { - char key[MAX_DBKEY_LENGTH]; - uint key_length; - bool signalled= FALSE; - MDL_LOCK_DATA *lock_data, *conf_lock_data; + MDL_LOCK_DATA *conf_lock_data; MDL_LOCK *lock; - I_P_List_iterator it(context->locks); const char *old_msg; THD *thd= context->thd; DBUG_ENTER("mdl_upgrade_shared_lock_to_exclusive"); - DBUG_PRINT("enter", ("db=%s name=%s", db, name)); DBUG_ASSERT(thd == current_thd); - int4store(key, type); - key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - safe_mutex_assert_not_owner(&LOCK_open); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + + /* Allow this function to be called twice for the same lock request. */ + if (lock_data->type == MDL_EXCLUSIVE) + DBUG_RETURN(FALSE); + + DBUG_ASSERT(lock_data->is_upgradable); + + lock= lock_data->lock; + pthread_mutex_lock(&LOCK_mdl); old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); - while ((lock_data= it++)) - if (lock_data->key_length == key_length && - !memcmp(lock_data->key, key, key_length) && - lock_data->type == MDL_SHARED) - { - DBUG_PRINT("info", ("found shared lock for upgrade")); - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - DBUG_ASSERT(lock_data->is_upgradable); - lock_data->state= MDL_PENDING_UPGRADE; - lock= lock_data->lock; - lock->active_shared.remove(lock_data); - lock->active_shared_waiting_upgrade.push_front(lock_data); - } + lock_data->state= MDL_PENDING_UPGRADE; + lock->active_shared.remove(lock_data); + /* + There can be only one upgrader for this lock or we will have deadlock. + This invariant is ensured by code outside of metadata subsystem usually + by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, + TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. + */ + DBUG_ASSERT(lock->active_shared_waiting_upgrade.is_empty()); + lock->active_shared_waiting_upgrade.push_front(lock_data); + + /* + There should be no conflicting global locks since for each upgradable + shared lock we obtain intention exclusive global lock first. + */ + DBUG_ASSERT(global_lock.active_shared == 0 && + global_lock.active_intention_exclusive); while (1) { + bool signalled= FALSE; + bool found_conflict= FALSE; + I_P_List_iterator it(lock->active_shared); + DBUG_PRINT("info", ("looking at conflicting locks")); - it.rewind(); - while ((lock_data= it++)) + + while ((conf_lock_data= it++)) { - if (lock_data->state == MDL_PENDING_UPGRADE) + /* + We can have other shared locks for the same object in the same context, + e.g. in case when several instances of TABLE are open. + */ + if (conf_lock_data->ctx != context) { - DBUG_ASSERT(lock_data->type == MDL_SHARED); - - lock= lock_data->lock; - - DBUG_ASSERT(global_lock.active_shared == 0 && - global_lock.active_intention_exclusive); - - if ((conf_lock_data= lock->active_shared.head())) - { - DBUG_PRINT("info", ("found active shared locks")); - signalled= notify_thread_having_shared_lock(thd, - conf_lock_data->ctx->thd); - break; - } - else if (!lock->active_exclusive.is_empty()) - { - DBUG_PRINT("info", ("found active exclusive locks")); - signalled= TRUE; - break; - } + DBUG_PRINT("info", ("found active shared locks")); + found_conflict= TRUE; + signalled|= notify_thread_having_shared_lock(thd, + conf_lock_data->ctx->thd); } } - if (!lock_data) + + /* + There should be no active exclusive locks since we own shared lock + on the object. + */ + DBUG_ASSERT(lock->active_exclusive.is_empty()); + + if (!found_conflict) break; + if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); else @@ -762,16 +766,9 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, } if (thd->killed) { - it.rewind(); - while ((lock_data= it++)) - if (lock_data->state == MDL_PENDING_UPGRADE) - { - DBUG_ASSERT(lock_data->type == MDL_SHARED); - lock_data->state= MDL_ACQUIRED; - lock= lock_data->lock; - lock->active_shared_waiting_upgrade.remove(lock_data); - lock->active_shared.push_front(lock_data); - } + lock_data->state= MDL_ACQUIRED; + lock->active_shared_waiting_upgrade.remove(lock_data); + lock->active_shared.push_front(lock_data); /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); thd->exit_cond(old_msg); @@ -779,20 +776,13 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, } } - it.rewind(); - while ((lock_data= it++)) - if (lock_data->state == MDL_PENDING_UPGRADE) - { - DBUG_ASSERT(lock_data->type == MDL_SHARED); - lock= lock_data->lock; - lock->active_shared_waiting_upgrade.remove(lock_data); - lock->active_exclusive.push_front(lock_data); - lock_data->type= MDL_EXCLUSIVE; - lock_data->state= MDL_ACQUIRED; - if (lock->cached_object) - (*lock->cached_object_release_hook)(lock->cached_object); - lock->cached_object= 0; - } + lock->active_shared_waiting_upgrade.remove(lock_data); + lock->active_exclusive.push_front(lock_data); + lock_data->type= MDL_EXCLUSIVE; + lock_data->state= MDL_ACQUIRED; + if (lock->cached_object) + (*lock->cached_object_release_hook)(lock->cached_object); + lock->cached_object= 0; /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); @@ -1085,49 +1075,6 @@ void mdl_release_locks(MDL_CONTEXT *context) } -/** - Release all exclusive locks associated with context. - Removes the locks from the context. - - @param context Context with exclusive locks. - - @note Shared locks are left intact. - @note Resets lock requests for locks released back to their - initial state (i.e.sets type and priority to MDL_SHARED - and MDL_NORMAL_PRIO). -*/ - -void mdl_release_exclusive_locks(MDL_CONTEXT *context) -{ - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); - - safe_mutex_assert_not_owner(&LOCK_open); - - pthread_mutex_lock(&LOCK_mdl); - while ((lock_data= it++)) - { - if (lock_data->type == MDL_EXCLUSIVE) - { - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - release_lock(lock_data); -#ifndef DBUG_OFF - lock_data->ctx= 0; - lock_data->lock= 0; -#endif - lock_data->state= MDL_PENDING; - /* Return lock request to its initial state. */ - lock_data->type= MDL_SHARED; - lock_data->prio= MDL_NORMAL_PRIO; - lock_data->is_upgradable= FALSE; - context->locks.remove(lock_data); - } - } - pthread_cond_broadcast(&COND_mdl); - pthread_mutex_unlock(&LOCK_mdl); -} - - /** Release a lock. Removes the lock from the context. @@ -1161,32 +1108,68 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) /** - Downgrade all exclusive locks in the context to - shared. + Release all locks in the context which correspond to the same name/ + object as this lock request. - @param context A context with exclusive locks. + @param context Context containing locks in question + @param lock_data One of the locks for the name/object for which all + locks should be released. + + @see mdl_release_lock() */ -void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context) +void mdl_release_all_locks_for_name(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data) { - MDL_LOCK_DATA *lock_data; MDL_LOCK *lock; I_P_List_iterator it(context->locks); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + + /* + We can use MDL_LOCK_DATA::lock here to identify other locks for the same + object since even altough MDL_LOCK object might be reused for different + lock after the first lock for this object have been released we can't + have references to this other MDL_LOCK object in this context. + */ + lock= lock_data->lock; + + while ((lock_data= it++)) + { + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + if (lock_data->lock == lock) + mdl_release_lock(context, lock_data); + } +} + + +/** + Downgrade an exclusive lock to shared metadata lock. + + @param context A context to which exclusive lock belongs + @param lock_data Satisfied request for exclusive lock to be downgraded +*/ + +void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data) +{ + MDL_LOCK *lock; + safe_mutex_assert_not_owner(&LOCK_open); + DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + + if (lock_data->type == MDL_SHARED) + return; + + lock= lock_data->lock; + pthread_mutex_lock(&LOCK_mdl); - while ((lock_data= it++)) - if (lock_data->type == MDL_EXCLUSIVE) - { - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - if (!lock_data->is_upgradable) - global_lock.active_intention_exclusive--; - lock= lock_data->lock; - lock->active_exclusive.remove(lock_data); - lock_data->type= MDL_SHARED; - lock->active_shared.push_front(lock_data); - } + if (!lock_data->is_upgradable) + global_lock.active_intention_exclusive--; + lock->active_exclusive.remove(lock_data); + lock_data->type= MDL_SHARED; + lock->active_shared.push_front(lock_data); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } diff --git a/sql/mdl.h b/sql/mdl.h index 74c90c01730..12ce2bb9820 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -194,8 +194,8 @@ inline void mdl_set_upgradable(MDL_LOCK_DATA *lock_data) bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); -bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type, - const char *db, const char *name); +bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data); bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); @@ -203,9 +203,11 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); bool mdl_wait_for_locks(MDL_CONTEXT *context); void mdl_release_locks(MDL_CONTEXT *context); -void mdl_release_exclusive_locks(MDL_CONTEXT *context); +void mdl_release_all_locks_for_name(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data); void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context); +void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data); void mdl_release_global_shared_lock(MDL_CONTEXT *context); bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 44db7938caf..6dc9c67f348 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1066,8 +1066,9 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, { for (TABLE_LIST *table= tables; table; table= table->next_local) { - TABLE *tab= find_locked_table(thd->open_tables, table->db, - table->table_name); + /* This should always succeed thanks to check in caller. */ + TABLE *tab= find_write_locked_table(thd->open_tables, table->db, + table->table_name); /* Checking TABLE::db_stat is essential in case when we have several instances of the table open and locked. @@ -1152,7 +1153,13 @@ err_with_reopen: result|= reopen_tables(thd, 1); thd->in_lock_tables=0; pthread_mutex_unlock(&LOCK_open); - mdl_downgrade_exclusive_locks(&thd->mdl_context); + /* + Since mdl_downgrade_exclusive_lock() won't do anything with shared + metadata lock it is much simplier to go through all open tables rather + than picking only those tables that were flushed. + */ + for (TABLE *tab= thd->open_tables; tab; tab= tab->next) + mdl_downgrade_exclusive_lock(&thd->mdl_context, tab->mdl_lock_data); } DBUG_RETURN(result); } @@ -2976,7 +2983,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, lock on this table to shared metadata lock. */ if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) - mdl_downgrade_exclusive_locks(&thd->mdl_context); + mdl_downgrade_exclusive_lock(&thd->mdl_context, table_list->mdl_lock_data); table->mdl_lock_data= mdl_lock_data; @@ -3992,7 +3999,7 @@ static bool handle_failed_open_table_attempt(THD *thd, TABLE_LIST *table, thd->warning_info->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message - mdl_release_exclusive_locks(&thd->mdl_context); + mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); break; case OT_REPAIR: mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); @@ -4004,7 +4011,7 @@ static bool handle_failed_open_table_attempt(THD *thd, TABLE_LIST *table, pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); - mdl_release_exclusive_locks(&thd->mdl_context); + mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); break; default: DBUG_ASSERT(0); @@ -8694,8 +8701,8 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) /* If MERGE child, forward lock handling to parent. */ mysql_lock_abort(lpt->thd, lpt->table->parent ? lpt->table->parent : lpt->table, TRUE); - if (mdl_upgrade_shared_lock_to_exclusive(&lpt->thd->mdl_context, 0, - lpt->db, lpt->table_name)) + if (mdl_upgrade_shared_lock_to_exclusive(&lpt->thd->mdl_context, + lpt->table->mdl_lock_data)) { mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent : diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 24939f1cd21..52a4a4e0144 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8114,31 +8114,10 @@ bool parse_sql(THD *thd, static void adjust_mdl_locks_upgradability(TABLE_LIST *tables) { - TABLE_LIST *tab, *otab; - - for (tab= tables; tab; tab= tab->next_global) + for (TABLE_LIST *tab= tables; tab; tab= tab->next_global) { if (tab->lock_type >= TL_WRITE_ALLOW_WRITE) tab->mdl_upgradable= TRUE; - else - { - /* - TODO: To get rid of this loop we need to change our code to do - metadata lock upgrade only for those instances of tables - which are write locked instead of doing such upgrade for - all instances of tables. - */ - for (otab= tables; otab; otab= otab->next_global) - if (otab->lock_type >= TL_WRITE_ALLOW_WRITE && - otab->db_length == tab->db_length && - otab->table_name_length == tab->table_name_length && - !strcmp(otab->db, tab->db) && - !strcmp(otab->table_name, tab->table_name)) - { - tab->mdl_upgradable= TRUE; - break; - } - } } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2a41fe2008c..9a55b386628 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1904,10 +1904,27 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, else if (thd->locked_tables) { for (table= tables; table; table= table->next_local) - if (!find_temporary_table(thd, table->db, table->table_name) && - !find_write_locked_table(thd->open_tables, table->db, - table->table_name)) - DBUG_RETURN(1); + if (find_temporary_table(thd, table->db, table->table_name)) + { + /* + Since we don't acquire metadata lock if we have found temporary + table, we should do something to avoid releasing it at the end. + */ + table->mdl_lock_data= 0; + } + else + { + /* + Since 'tables' list can't contain duplicates (this is ensured + by parser) it is safe to cache pointer to the TABLE instances + in its elements. + */ + table->table= find_write_locked_table(thd->open_tables, table->db, + table->table_name); + if (!table->table) + DBUG_RETURN(1); + table->mdl_lock_data= table->table->mdl_lock_data; + } } } @@ -1956,6 +1973,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, error= 0; } + /* Probably a non-temporary table. */ + non_temp_tables_count++; + /* If row-based replication is used and the table is not a temporary table, we add the table name to the drop statement @@ -1964,7 +1984,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, */ if (!drop_temporary && thd->current_stmt_binlog_row_based && !dont_log_query) { - non_temp_tables_count++; /* Don't write the database name if it is the current one (or if thd->db is NULL). @@ -1985,18 +2004,12 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, { if (thd->locked_tables) { - TABLE *tab= find_locked_table(thd->open_tables, db, table->table_name); - if (close_cached_table(thd, tab)) + if (close_cached_table(thd, table->table)) { error= -1; goto err_with_placeholders; } - /* - Leave LOCK TABLES mode if we managed to drop all tables - which were locked. - */ - if (thd->locked_tables->table_count == 0) - unlock_locked_tables(thd); + table->table= 0; } if (thd->killed) @@ -2175,10 +2188,32 @@ err_with_placeholders: doing this. Unfortunately in this case we are likely to get more false positives in lock_table_name_if_not_cached() function. So it makes sense to remove exclusive meta-data locks in all cases. + + Leave LOCK TABLES mode if we managed to drop all tables which were + locked. Additional check for 'non_temp_tables_count' is to avoid + leaving LOCK TABLES mode if we have dropped only temporary tables. */ - mdl_release_exclusive_locks(&thd->mdl_context); + if (thd->locked_tables && thd->locked_tables->table_count == 0 && + non_temp_tables_count > 0) + { + unlock_locked_tables(thd); + goto end; + } + for (table= tables; table; table= table->next_local) + { + if (table->mdl_lock_data) + { + /* + Under LOCK TABLES we may have several instances of table open + and locked and therefore have to remove several metadata lock + requests associated with them. + */ + mdl_release_all_locks_for_name(&thd->mdl_context, table->mdl_lock_data); + } + } } +end: DBUG_RETURN(error); } @@ -4122,7 +4157,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, unlock: if (target_lock_data) - mdl_release_exclusive_locks(&thd->mdl_context); + mdl_release_lock(&thd->mdl_context, target_lock_data); pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) pthread_cond_signal(&COND_refresh); @@ -4292,9 +4327,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, old_lock_type= table->reginfo.lock_type; mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ - if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0, - table->s->db.str, - table->s->table_name.str)) + if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, + table->mdl_lock_data)) { mysql_lock_downgrade_write(thd, table, old_lock_type); DBUG_RETURN(TRUE); @@ -4476,6 +4510,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, table= &tmp_table; pthread_mutex_unlock(&LOCK_open); } + else + { + mdl_lock_data= table->mdl_lock_data; + } /* A MERGE table must not come here. */ DBUG_ASSERT(!table->child_l); @@ -4574,8 +4612,9 @@ end: closefrm(table, 1); // Free allocated memory pthread_mutex_unlock(&LOCK_open); } - if (error) - mdl_release_exclusive_locks(&thd->mdl_context); + /* In case of a temporary table there will be no metadata lock. */ + if (error && mdl_lock_data) + mdl_release_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(error); } @@ -5539,7 +5578,7 @@ binlog: err: if (target_lock_data) - mdl_release_exclusive_locks(&thd->mdl_context); + mdl_release_lock(&thd->mdl_context, target_lock_data); DBUG_RETURN(res); } @@ -6477,7 +6516,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint order_num, ORDER *order, bool ignore) { TABLE *table, *new_table= 0; - MDL_LOCK_DATA *target_lock_data= 0; + MDL_LOCK_DATA *mdl_lock_data, *target_lock_data= 0; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6649,6 +6688,7 @@ view_err: if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ))) DBUG_RETURN(TRUE); table->use_all_columns(); + mdl_lock_data= table->mdl_lock_data; /* Prohibit changing of the UNION list of a non-temporary MERGE table @@ -6894,9 +6934,12 @@ view_err: lock here... */ if (new_name != table_name || new_db != db) - mdl_release_exclusive_locks(&thd->mdl_context); + { + mdl_release_lock(&thd->mdl_context, target_lock_data); + mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); + } else - mdl_downgrade_exclusive_locks(&thd->mdl_context); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data); } DBUG_RETURN(error); } @@ -7575,10 +7618,11 @@ view_err: pthread_mutex_lock(&LOCK_open); unlink_open_table(thd, table, FALSE); pthread_mutex_unlock(&LOCK_open); - mdl_release_exclusive_locks(&thd->mdl_context); + mdl_release_lock(&thd->mdl_context, target_lock_data); + mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); } else - mdl_downgrade_exclusive_locks(&thd->mdl_context); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data); } end_temporary: @@ -7634,7 +7678,7 @@ err: thd->abort_on_warning= save_abort_on_warning; } if (target_lock_data) - mdl_release_exclusive_locks(&thd->mdl_context); + mdl_release_lock(&thd->mdl_context, target_lock_data); DBUG_RETURN(TRUE); err_with_placeholders: @@ -7645,7 +7689,9 @@ err_with_placeholders: */ unlink_open_table(thd, table, FALSE); pthread_mutex_unlock(&LOCK_open); - mdl_release_exclusive_locks(&thd->mdl_context); + if (target_lock_data) + mdl_release_lock(&thd->mdl_context, target_lock_data); + mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(TRUE); } /* mysql_alter_table */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 4e2b77292d8..caf5c84e1f9 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -527,8 +527,9 @@ end: locks. Otherwise call to close_thread_tables() will take care about both TABLE instance created by reopen_name_locked_table() and meta-data lock. */ - if (thd->locked_tables) - mdl_downgrade_exclusive_locks(&thd->mdl_context); + if (thd->locked_tables && tables && tables->table) + mdl_downgrade_exclusive_lock(&thd->mdl_context, + tables->table->mdl_lock_data); if (need_start_waiting) start_waiting_global_read_lock(thd); From 5dfa313eb7c9c1663b0e79b6ff4bca1f9ac77e51 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 1 Dec 2009 12:00:50 +0100 Subject: [PATCH 050/466] - Introduce MYSQL_ADD_PLUGIN that replaces MYSQL_STORAGE_ENGINE - Fix semisync library prefix (remove lib on Unixes) - restrict exported symbols from zlib and yassl (fvisibility=hidden) --- CMakeLists.txt | 4 +- cmake/Makefile.am | 2 +- cmake/libutils.cmake | 45 ++---- cmake/plugin.cmake | 198 +++++++++++++-------------- extra/yassl/CMakeLists.txt | 1 + extra/yassl/taocrypt/CMakeLists.txt | 1 + mysql-test/mysql-test-run.pl | 14 +- plugin/daemon_example/CMakeLists.txt | 4 +- plugin/fulltext/CMakeLists.txt | 5 +- plugin/semisync/CMakeLists.txt | 15 +- sql/CMakeLists.txt | 11 +- storage/archive/CMakeLists.txt | 9 +- storage/blackhole/CMakeLists.txt | 4 +- storage/csv/CMakeLists.txt | 2 +- storage/example/CMakeLists.txt | 2 +- storage/federated/CMakeLists.txt | 2 +- storage/heap/CMakeLists.txt | 3 +- storage/ibmdb2i/CMakeLists.txt | 4 +- storage/innobase/CMakeLists.txt | 20 +-- storage/myisam/CMakeLists.txt | 5 +- storage/myisammrg/CMakeLists.txt | 2 +- zlib/CMakeLists.txt | 1 + 22 files changed, 148 insertions(+), 206 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b5e5464001..bab5595a786 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,7 @@ MYSQL_CHECK_SSL() MYSQL_CHECK_READLINE() IF(NOT WITHOUT_SERVER) +SET (MYSQLD_STATIC_PLUGIN_LIBS "" CACHE INTERNAL "") # Add storage engines and plugins. CONFIGURE_PLUGINS() ENDIF() @@ -219,7 +220,8 @@ CONFIGURE_FILE(config.h.cmake ${CMAKE_BINARY_DIR}/include/my_config.h) CONFIGURE_FILE(config.h.cmake ${CMAKE_BINARY_DIR}/include/config.h) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/include/mysql_version.h.in ${CMAKE_BINARY_DIR}/include/mysql_version.h ) - +CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc.in + ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc) # Packaging IF(WIN32) diff --git a/cmake/Makefile.am b/cmake/Makefile.am index c8cde52ca2d..40c7771662f 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -1,5 +1,5 @@ EXTRA_DIST = \ - cat.cmake \ + cmake_parse_arguments.cmake configurable_file_content.in \ check_minimal_version.cmake \ create_initial_db.cmake.in \ diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index ea8f809de9f..be0523d7080 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -53,11 +53,11 @@ GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) -IF(NOT WIN32 AND NOT CYGWIN AND NOT APPLE AND NOT WITH_PIC AND NOT DISABLE_SHARED - AND CMAKE_SHARED_LIBRARY_C_FLAGS) +IF(WIN32 OR CYGWIN OR APPLE OR WITH_PIC OR DISABLE_SHARED OR NOT CMAKE_SHARED_LIBRARY_C_FLAGS) SET(_SKIP_PIC 1) ENDIF() - + +INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake) # CREATE_EXPORT_FILE (VAR target api_functions) # Internal macro, used to create source file for shared libraries that # otherwise consists entirely of "convenience" libraries. On Windows, @@ -108,38 +108,6 @@ MACRO(ADD_CONVENIENCE_LIBRARY) ENDMACRO() -# Handy macro to parse macro arguments -MACRO(CMAKE_PARSE_ARGUMENTS prefix arg_names option_names) - SET(DEFAULT_ARGS) - FOREACH(arg_name ${arg_names}) - SET(${prefix}_${arg_name}) - ENDFOREACH(arg_name) - FOREACH(option ${option_names}) - SET(${prefix}_${option} FALSE) - ENDFOREACH(option) - - SET(current_arg_name DEFAULT_ARGS) - SET(current_arg_list) - FOREACH(arg ${ARGN}) - SET(larg_names ${arg_names}) - LIST(FIND larg_names "${arg}" is_arg_name) - IF (is_arg_name GREATER -1) - SET(${prefix}_${current_arg_name} ${current_arg_list}) - SET(current_arg_name ${arg}) - SET(current_arg_list) - ELSE (is_arg_name GREATER -1) - SET(loption_names ${option_names}) - LIST(FIND loption_names "${arg}" is_option) - IF (is_option GREATER -1) - SET(${prefix}_${arg} TRUE) - ELSE (is_option GREATER -1) - SET(current_arg_list ${current_arg_list} ${arg}) - ENDIF (is_option GREATER -1) - ENDIF (is_arg_name GREATER -1) - ENDFOREACH(arg) - SET(${prefix}_${current_arg_name} ${current_arg_list}) -ENDMACRO() - # Write content to file, using CONFIGURE_FILE # The advantage compared to FILE(WRITE) is that timestamp # does not change if file already has the same content @@ -289,3 +257,10 @@ MACRO(MERGE_LIBRARIES) ENDIF() ENDMACRO() +MACRO(RESTRICT_SYMBOL_EXPORTS target) + IF(CMAKE_COMPILER_IS_GNUCXX AND UNIX) + GET_TARGET_PROPERTY(COMPILE_FLAGS ${target} COMPILE_FLAGS) + SET_TARGET_PROPERTIES(${target} PROPERTIES + COMPILE_FLAGS "${COMPILE_FLAGS} -fvisibility=hidden") + ENDIF() +ENDMACRO() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 40e31aeea02..3ad52ce4c98 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -13,15 +13,27 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# Creates a project to build plugin either as static or shared library -# Parameters: -# plugin - storage engine name. -# variable BUILD_TYPE should be set to "STATIC" or "DYNAMIC" -# Remarks: -# ${PLUGIN}_SOURCES variable containing source files to produce the -# library must set before calling this macro -MACRO(MYSQL_PLUGIN plugin) +GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake) + +# MYSQL_ADD_PLUGIN(plugin_name source1...sourceN +# [STORAGE_ENGINE] +# [MANDATORY|DEFAULT] +# [STATIC_ONLY|DYNAMIC_ONLY] +# [MODULE_OUTPUT_NAME module_name] +# [STATIC_OUTPUT_NAME static_name] +# [RECOMPILE_FOR_EMBEDDED] +# [LINK_LIBRARIES lib1...libN] +# [DEPENDENCIES target1...targetN] + +MACRO(MYSQL_ADD_PLUGIN) + CMAKE_PARSE_ARGUMENTS(ARG + "LINK_LIBRARIES;DEPENDENCIES;MODULE_OUTPUT_NAME;STATIC_OUTPUT_NAME" + "STORAGE_ENGINE;STATIC_ONLY;MODULE_ONLY;MANDATORY;DEFAULT;DISABLED;RECOMPILE_FOR_EMBEDDED" + ${ARGN} + ) + # Add common include directories INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql @@ -29,90 +41,91 @@ MACRO(MYSQL_PLUGIN plugin) ${SSL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR}) + LIST(GET ARG_DEFAULT_ARGS 0 plugin) + SET(SOURCES ${ARG_DEFAULT_ARGS}) + LIST(REMOVE_AT SOURCES 0) STRING(TOUPPER ${plugin} plugin) STRING(TOLOWER ${plugin} target) - - IF(NOT ${plugin}_PLUGIN_STATIC AND NOT ${plugin}_PLUGIN_DYNAMIC) - MESSAGE(FATAL_ERROR - "Neither ${plugin}_PLUGIN_STATIC nor ${plugin}_PLUGIN_DYNAMIC is defined. - Please set at least one of these variables to the name of the output - library in CMakeLists.txt prior to calling MYSQL_PLUGIN" - ) - ENDIF() + # Figure out whether to build plugin IF(WITH_PLUGIN_${plugin}) SET(WITH_${plugin} 1) ENDIF() - IF(WITH_${plugin}_STORAGE_ENGINE OR WITH_{$plugin} OR WITH_ALL - OR WITH_MAX AND NOT WITHOUT_${plugin}_STORAGE_ENGINE AND NOT - WITHOUT_${plugin}) + IF(WITH_${plugin}_STORAGE_ENGINE + OR WITH_{$plugin} + OR WITH_ALL + OR WITH_MAX + OR ARG_DEFAULT + AND NOT WITHOUT_${plugin}_STORAGE_ENGINE + AND NOT WITHOUT_${plugin} + AND NOT ARG_MODULE_ONLY) + SET(WITH_${plugin} 1) ELSEIF(WITHOUT_${plugin}_STORAGE_ENGINE OR WITH_NONE OR ${plugin}_DISABLED) SET(WITHOUT_${plugin} 1) SET(WITH_${plugin}_STORAGE_ENGINE 0) SET(WITH_${plugin} 0) ENDIF() - - IF(${plugin}_PLUGIN_MANDATORY) + + + IF(ARG_MANDATORY) SET(WITH_${plugin} 1) ENDIF() - - IF(${plugin} MATCHES NDBCLUSTER AND WITH_MAX_NO_NDB) - SET(WITH_${plugin} 0) - SET(WITH_${plugin}_STORAGE_ENGINE 0) - SET(WITHOUT_${plugin} 1) - SET(WITHOUT_${plugin}_STORAGE_ENGINE 0) - ENDIF() - - IF(STORAGE_ENGINE) - SET(with_var "WITH_${plugin}_STORAGE_ENGINE" ) - ELSE() - SET(with_var "WITH_${plugin}") - ENDIF() - - IF (WITH_${plugin} AND ${plugin}_PLUGIN_STATIC) - ADD_DEFINITIONS(-DMYSQL_SERVER) - #Create static library. - ADD_LIBRARY(${target} ${${plugin}_SOURCES}) - DTRACE_INSTRUMENT(${target}) - ADD_DEPENDENCIES(${target} GenError) - IF(WITH_EMBEDDED_SERVER AND NOT ${plugin}_PLUGIN_DYNAMIC) - # Recompile couple of plugins for embedded - ADD_LIBRARY(${target}_embedded ${${plugin}_SOURCES}) - DTRACE_INSTRUMENT(${target}_embedded) - SET_TARGET_PROPERTIES(${target}_embedded - PROPERTIES COMPILE_DEFINITIONS "EMBEDDED_LIBRARY") - ADD_DEPENDENCIES(${target}_embedded GenError) - ENDIF() - IF(${plugin}_LIBS) - TARGET_LINK_LIBRARIES(${target} ${${plugin}_LIBS}) + + IF(ARG_STORAGE_ENGINE) + SET(with_var "WITH_${plugin}_STORAGE_ENGINE" ) + ELSE() + SET(with_var "WITH_${plugin}") + ENDIF() + + IF(NOT ARG_DEPENDENCIES) + SET(ARG_DEPENDENCIES) + ENDIF() + # Build either static library or module + IF (WITH_${plugin} AND NOT ARG_MODULE_ONLY) + ADD_LIBRARY(${target} STATIC ${SOURCES}) + SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_DEFINITONS "MYSQL_SERVER") + DTRACE_INSTRUMENT(${target}) + ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) + IF(WITH_EMBEDDED_SERVER) + # Embedded library should contain PIC code and be linkable + # to shared libraries (on systems that need PIC) + IF(ARG_RECOMPILE_FOR_EMBEDDED OR NOT _SKIP_PIC) + # Recompile some plugins for embedded + ADD_CONVENIENCE_LIBRARY(${target}_embedded ${SOURCES}) + DTRACE_INSTRUMENT(${target}_embedded) + IF(ARG_RECOMPILE_FOR_EMBEDDED) + SET_TARGET_PROPERTIES(${target}_embedded + PROPERTIES COMPILE_DEFINITIONS "MYSQL_SERVER;EMBEDDED_LIBRARY") + ENDIF() + ADD_DEPENDENCIES(${target}_embedded GenError) + ENDIF() ENDIF() - SET_TARGET_PROPERTIES(${target} PROPERTIES - OUTPUT_NAME "${${plugin}_PLUGIN_STATIC}") + IF(ARG_STATIC_OUTPUT_NAME) + SET_TARGET_PROPERTIES(${target} PROPERTIES + OUTPUT_NAME ${ARG_STATIC_OUTPUT_NAME}) + ENDIF() + # Update mysqld dependencies SET (MYSQLD_STATIC_PLUGIN_LIBS ${MYSQLD_STATIC_PLUGIN_LIBS} - ${target} PARENT_SCOPE) + ${target} CACHE INTERNAL "") + SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_${target}_plugin" - PARENT_SCOPE) - SET(${with_var} ON CACHE BOOL "Link ${plugin} statically to the server" - FORCE) - - ELSEIF(NOT WITHOUT_${plugin} AND ${plugin}_PLUGIN_DYNAMIC - AND NOT WITHOUT_DYNAMIC_PLUGINS) - - # Create a shared module. - ADD_DEFINITIONS(-DMYSQL_DYNAMIC_PLUGIN) - ADD_LIBRARY(${target} MODULE ${${plugin}_SOURCES}) - IF(${plugin}_LIBS) - TARGET_LINK_LIBRARIES(${target} ${${plugin}_LIBS}) - ENDIF() - DTRACE_INSTRUMENT(${target}) - SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "") + PARENT_SCOPE) + IF(ARG_STORAGE_ENGINE) + SET(${with_var} ON CACHE BOOL "Link ${plugin} statically to the server" + FORCE) + ENDIF() + ELSEIF(NOT WITHOUT_${plugin} AND NOT ARG_STATIC_ONLY AND NOT WITHOUT_DYNAMIC_PLUGINS) + + ADD_LIBRARY(${target} MODULE ${SOURCES}) + DTRACE_INSTRUMENT(${target}) + SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "" + COMPILE_DEFINITIONS "MYSQL_DYNAMIC_PLUGIN") TARGET_LINK_LIBRARIES (${target} mysqlservices) - # Plugin uses symbols defined in mysqld executable. # Some operating systems like Windows and OSX and are pretty strict about # unresolved symbols. Others are less strict and allow unresolved symbols @@ -121,41 +134,26 @@ MACRO(MYSQL_PLUGIN plugin) # Thus we skip TARGET_LINK_LIBRARIES on Linux, as it would only generate # an additional dependency. IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") - TARGET_LINK_LIBRARIES (${target} mysqld) + TARGET_LINK_LIBRARIES (${target} mysqld ${ARG_LINK_LIBRARIES}) ENDIF() - ADD_DEPENDENCIES(${target} GenError) - - IF(${plugin}_PLUGIN_DYNAMIC) - SET_TARGET_PROPERTIES(${target} PROPERTIES - OUTPUT_NAME "${${plugin}_PLUGIN_DYNAMIC}") + ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) + + IF(NOT ARG_MODULE_OUTPUT_NAME) + IF(ARG_STORAGE_ENGINE) + SET(ARG_MODULE_OUTPUT_NAME "ha_${target}") + ELSE() + SET(ARG_MODULE_OUTPUT_NAME "${target}") + ENDIF() ENDIF() - - # Update cache "WITH" variable for plugins that support static linking - IF(${plugin}_PLUGIN_STATIC) - SET(${with_var} OFF CACHE BOOL "Link ${plugin} statically to the server" - FORCE) - ENDIF() - + SET_TARGET_PROPERTIES(${target} PROPERTIES + OUTPUT_NAME "${ARG_MODULE_OUTPUT_NAME}") # Install dynamic library SET(INSTALL_LOCATION lib/plugin) INSTALL(TARGETS ${target} DESTINATION ${INSTALL_LOCATION}) INSTALL_DEBUG_SYMBOLS(${target}) - ELSE() - IF(STORAGE_ENGINE) - SET(without_var "WITHOUT_${plugin}_STORAGE_ENGINE") - ELSE() - SET(without_var "WITHOUT_${plugin}") - ENDIF() - SET(${without_var} ON CACHE BOOL "Link ${plugin} statically to the server" - FORCE) - MARK_AS_ADVANCED(${without_var}) ENDIF() ENDMACRO() -MACRO (MYSQL_STORAGE_ENGINE engine) - SET(STORAGE_ENGINE 1) - MYSQL_PLUGIN(${engine}) -ENDMACRO() # Add all CMake projects under storage and plugin # subdirectories, configure sql_builtins.cc @@ -167,12 +165,4 @@ MACRO(CONFIGURE_PLUGINS) ADD_SUBDIRECTORY(${dir}) ENDIF() ENDFOREACH() - # Special handling for partition(not really pluggable) - IF(NOT WITHOUT_PARTITION_STORAGE_ENGINE) - SET (WITH_PARTITION_STORAGE_ENGINE 1) - SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_partition_plugin") - ENDIF(NOT WITHOUT_PARTITION_STORAGE_ENGINE) - ADD_DEFINITIONS(${STORAGE_ENGINE_DEFS}) - CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc.in - ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc) ENDMACRO() diff --git a/extra/yassl/CMakeLists.txt b/extra/yassl/CMakeLists.txt index 1bc75956221..82d7e5b7581 100755 --- a/extra/yassl/CMakeLists.txt +++ b/extra/yassl/CMakeLists.txt @@ -30,5 +30,6 @@ SET(YASSL_SOURCES src/buffer.cpp src/cert_wrapper.cpp src/crypto_wrapper.cpp sr src/log.cpp src/socket_wrapper.cpp src/ssl.cpp src/timer.cpp src/yassl_error.cpp src/yassl_imp.cpp src/yassl_int.cpp) ADD_CONVENIENCE_LIBRARY(yassl ${YASSL_SOURCES}) +RESTRICT_SYMBOL_EXPORTS(yassl) diff --git a/extra/yassl/taocrypt/CMakeLists.txt b/extra/yassl/taocrypt/CMakeLists.txt index 34ff2323390..2c43756b6f4 100755 --- a/extra/yassl/taocrypt/CMakeLists.txt +++ b/extra/yassl/taocrypt/CMakeLists.txt @@ -27,4 +27,5 @@ SET(TAOCRYPT_SOURCES src/aes.cpp src/aestables.cpp src/algebra.cpp src/arc4.cpp include/md2.hpp include/md5.hpp include/misc.hpp include/modarith.hpp include/modes.hpp include/random.hpp include/ripemd.hpp include/rsa.hpp include/sha.hpp) ADD_CONVENIENCE_LIBRARY(taocrypt ${TAOCRYPT_SOURCES}) +RESTRICT_SYMBOL_EXPORTS(taocrypt) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 3764cff2d9a..4bb9dcfa5e3 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1873,18 +1873,10 @@ sub environment_setup { # Add the path where mysqld will find semisync plugins # -------------------------------------------------------------------------- if (!$opt_embedded_server) { - my $semisync_lib_prefix; - if (IS_WINDOWS) - { - $semisync_lib_prefix = ""; - } - else - { - $semisync_lib_prefix= "lib"; - } - my ($lib_semisync_master_plugin) = find_plugin($semisync_lib_prefix."semisync_master", "plugin/semisync"); - my ($lib_semisync_slave_plugin) = find_plugin($semisync_lib_prefix."semisync_slave", "plugin/semisync"); + + my ($lib_semisync_master_plugin) = find_plugin("semisync_master", "plugin/semisync"); + my ($lib_semisync_slave_plugin) = find_plugin("semisync_slave", "plugin/semisync"); if ($lib_semisync_master_plugin && $lib_semisync_slave_plugin) { diff --git a/plugin/daemon_example/CMakeLists.txt b/plugin/daemon_example/CMakeLists.txt index 97ada05f935..e0f5f1caf78 100644 --- a/plugin/daemon_example/CMakeLists.txt +++ b/plugin/daemon_example/CMakeLists.txt @@ -13,6 +13,4 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(DAEMON_EXAMPLE_PLUGIN_DYNAMIC daemon_example) -SET(DAEMON_EXAMPLE_SOURCES daemon_example.cc) -MYSQL_PLUGIN(DAEMON_EXAMPLE) +MYSQL_ADD_PLUGIN(DAEMON_EXAMPLE daemon_example.cc MODULE_ONLY) diff --git a/plugin/fulltext/CMakeLists.txt b/plugin/fulltext/CMakeLists.txt index e0dd2ac510c..92cbd7acee5 100644 --- a/plugin/fulltext/CMakeLists.txt +++ b/plugin/fulltext/CMakeLists.txt @@ -13,6 +13,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(FTEXAMPLE_PLUGIN_DYNAMIC mypluglib) -SET(FTEXAMPLE_SOURCES plugin_example.c) -MYSQL_PLUGIN(FTEXAMPLE) + +MYSQL_ADD_PLUGIN(FTEXAMPLE plugin_example.c MODULE_ONLY MODULE_OUTPUT_NAME mypluglib) diff --git a/plugin/semisync/CMakeLists.txt b/plugin/semisync/CMakeLists.txt index 7681894207d..b852bce9463 100644 --- a/plugin/semisync/CMakeLists.txt +++ b/plugin/semisync/CMakeLists.txt @@ -13,20 +13,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02 -IF(WIN32) - SET(LIBPREFIX "") -ELSE() - SET(LIBPREFIX "lib") -ENDIF() + SET(SEMISYNC_MASTER_SOURCES semisync.cc semisync_master.cc semisync_master_plugin.cc semisync.h semisync_master.h) -SET(SEMISYNC_MASTER_PLUGIN_DYNAMIC ${LIBPREFIX}semisync_master) -MYSQL_PLUGIN(SEMISYNC_MASTER) +MYSQL_ADD_PLUGIN(semisync_master ${SEMISYNC_MASTER_SOURCES} MODULE_ONLY MODULE_OUTPUT_NAME "semisync_master") -SET(SEMISYNC_SLAVE_SOURCES semisync.cc semisync_slave.cc semisync_slave_plugin.cc -semisync.h semisync_slave.h ) -SET(SEMISYNC_SLAVE_PLUGIN_DYNAMIC ${LIBPREFIX}semisync_slave) -MYSQL_PLUGIN(SEMISYNC_SLAVE) +SET(SEMISYNC_SLAVE_SOURCES semisync.cc semisync_slave.cc semisync_slave_plugin.cc semisync.h semisync_slave.h ) +MYSQL_ADD_PLUGIN(semisync_slave ${SEMISYNC_MASTER_SOURCES} MODULE_ONLY MODULE_OUTPUT_NAME "semisync_slave") diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 82ad781c73b..158f0cbc884 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -48,7 +48,6 @@ SET (SQL_SOURCE ../sql-common/client.c derror.cc des_key_file.cc discover.cc ../libmysql/errmsg.c field.cc field_conv.cc filesort.cc gstream.cc - ha_partition.cc handler.cc hash_filo.cc hash_filo.h sql_plugin_services.h hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc @@ -81,11 +80,12 @@ SET (SQL_SOURCE sql_connect.cc scheduler.cc sql_profile.cc event_parse_data.cc sql_signal.cc rpl_handler.cc - ${GEN_SOURCES} + ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE}) +MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY) + -SET (MYSQLD_CORE_LIBS mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} vio regex ) IF(WIN32) SET(SQL_SOURCE ${SQL_SOURCE} nt_servc.cc nt_servc.h) ENDIF() @@ -113,7 +113,7 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS) # incremental appears to crash from time to time,if used with /DEF option SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "/DEF:mysqld.def /INCREMENTAL:NO") - FOREACH (CORELIB ${MYSQLD_CORE_LIBS} dbug strings) + FOREACH (CORELIB sql mysys dbug strings) GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION) FILE(TO_NATIVE_PATH ${LOC} LOC) SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC}) @@ -129,7 +129,8 @@ ENDIF() SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) -TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} ${MYSQLD_CORE_LIBS} ${LIBWRAP_LIBRARY}) +TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} sql mysys dbug strings vio regex ${MYSQLD_CORE_LIBS} + ${LIBWRAP_LIBRARY} ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) INSTALL(TARGETS mysqld DESTINATION bin) INSTALL_DEBUG_SYMBOLS(mysqld) diff --git a/storage/archive/CMakeLists.txt b/storage/archive/CMakeLists.txt index 458f21bfb6c..d47fd254b0e 100644 --- a/storage/archive/CMakeLists.txt +++ b/storage/archive/CMakeLists.txt @@ -13,14 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(ARCHIVE_PLUGIN_STATIC "archive") -SET(ARCHIVE_PLUGIN_DYNAMIC "ha_archive") SET(ARCHIVE_SOURCES azio.c ha_archive.cc ha_archive.h) -MYSQL_STORAGE_ENGINE(ARCHIVE) -IF(NOT WITH_ARCHIVE_STORAGE_ENGINE AND NOT WITHOUT_ARCHIVE_STORAGE_ENGINE - AND NOT WITH_ZLIB STREQUAL "bundled") - TARGET_LINK_LIBRARIES(archive ${ZLIB_LIBRARY}) -ENDIF() - +MYSQL_ADD_PLUGIN(archive ${ARCHIVE_SOURCES} STORAGE_ENGINE LINK_LIBRARIES ${ZLIB_LIBRARY}) diff --git a/storage/blackhole/CMakeLists.txt b/storage/blackhole/CMakeLists.txt index ec28208c311..6b6e417c091 100644 --- a/storage/blackhole/CMakeLists.txt +++ b/storage/blackhole/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(BLACKHOLE_PLUGIN_STATIC "blackhole") -SET(BLACKHOLE_PLUGIN_DYNAMIC "ha_blackhole") SET(BLACKHOLE_SOURCES ha_blackhole.cc ha_blackhole.h) -MYSQL_STORAGE_ENGINE(BLACKHOLE) +MYSQL_ADD_PLUGIN(BLACKHOLE ${BLACKHOLE_SOURCES} STORAGE_ENGINE) diff --git a/storage/csv/CMakeLists.txt b/storage/csv/CMakeLists.txt index b9d31fc372f..73d1794a369 100644 --- a/storage/csv/CMakeLists.txt +++ b/storage/csv/CMakeLists.txt @@ -17,4 +17,4 @@ SET(CSV_PLUGIN_STATIC "csv") SET(CSV_PLUGIN_MANDATORY TRUE) SET(CSV_SOURCES ha_tina.cc ha_tina.h transparent_file.cc transparent_file.h) -MYSQL_STORAGE_ENGINE(CSV) \ No newline at end of file +MYSQL_ADD_PLUGIN(csv ${CSV_SOURCES} STORAGE_ENGINE MANDATORY) diff --git a/storage/example/CMakeLists.txt b/storage/example/CMakeLists.txt index 47722dfd53b..109f4bfa844 100644 --- a/storage/example/CMakeLists.txt +++ b/storage/example/CMakeLists.txt @@ -15,4 +15,4 @@ SET(EXAMPLE_PLUGIN_DYNAMIC "ha_example") SET(EXAMPLE_SOURCES ha_example.cc) -MYSQL_STORAGE_ENGINE(EXAMPLE) +MYSQL_ADD_PLUGIN(example ${EXAMPLE_SOURCES} STORAGE_ENGINE) diff --git a/storage/federated/CMakeLists.txt b/storage/federated/CMakeLists.txt index f47c7240682..ac55543f276 100644 --- a/storage/federated/CMakeLists.txt +++ b/storage/federated/CMakeLists.txt @@ -21,4 +21,4 @@ IF(NOT WITH_FEDERATED AND NOT WITH_FEDERATED_STORAGE_ENGINE) # mysqld and are optimized away by the linker. SET(FEDERATED_SOURCES ${FEDERATED_SOURCES} ${CMAKE_SOURCE_DIR}/mysys/string.c) ENDIF() -MYSQL_STORAGE_ENGINE(FEDERATED) +MYSQL_ADD_PLUGIN(federated ${FEDERATED_SOURCES} STORAGE_ENGINE) diff --git a/storage/heap/CMakeLists.txt b/storage/heap/CMakeLists.txt index 1e88ecd503f..32359759abc 100755 --- a/storage/heap/CMakeLists.txt +++ b/storage/heap/CMakeLists.txt @@ -21,4 +21,5 @@ SET(HEAP_SOURCES _check.c _rectest.c hp_block.c hp_clear.c hp_close.c hp_create hp_delete.c hp_extra.c hp_hash.c hp_info.c hp_open.c hp_panic.c hp_rename.c hp_rfirst.c hp_rkey.c hp_rlast.c hp_rnext.c hp_rprev.c hp_rrnd.c hp_rsame.c hp_scan.c hp_static.c hp_update.c hp_write.c) -MYSQL_STORAGE_ENGINE(HEAP) + +MYSQL_ADD_PLUGIN(heap ${HEAP_SOURCES} STORAGE_ENGINE MANDATORY RECOMPILE_FOR_EMBEDDED) diff --git a/storage/ibmdb2i/CMakeLists.txt b/storage/ibmdb2i/CMakeLists.txt index 76ef3b2662d..b1517f53f7f 100644 --- a/storage/ibmdb2i/CMakeLists.txt +++ b/storage/ibmdb2i/CMakeLists.txt @@ -34,7 +34,7 @@ SET (IBMDB2I_SOURCES ha_ibmdb2i.cc db2i_ileBridge.cc db2i_conversion.cc db2i_collationSupport.cc db2i_errors.cc db2i_constraints.cc db2i_rir.cc db2i_sqlStatementStream.cc db2i_ioBuffers.cc db2i_myconv.cc) -SET(IBMDB2I_LIBS iconv) -MYSQL_STORAGE_ENGINE(IBMDB2I) + +MYSQL_ADD_PLUGIN(ibmdb2i ${IBMDB2I_SOURCES} STORAGE_ENGINE LINK_LIBRARIES iconv) ENDIF(HAVE_PASE_ENVIRONMENT) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index c8ec56ee5c9..e5ecc98dc3a 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -42,7 +42,8 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") CHECK_FUNCTION_EXISTS(atomic_add_long HAVE_ATOMIC_ADD_LONG) IF(HAVE_ATOMIC_CAS_ULONG AND HAVE_ATOMIC_CAS_32 AND HAVE_ATOMIC_CAS_64 AND HAVE_ATOMIC_ADD_LONG) - SET(HAVE_SOLARIS_ATOMICS 1) + + SET(HAVE_SOLARIS_ATOMICS 1) ENDIF() ENDIF() @@ -67,7 +68,7 @@ IF(NOT HAVE_ATOMIC_PTHREAD_T_GCC AND HAVE_SOLARIS_ATOMICS) int main() { pthread_t x = 0; - return(0); + return(0); }" HAVE_ATOMIC_PTHREAD_T_SOLARIS ) IF(HAVE_ATOMIC_PTHREAD_T_SOLARIS) @@ -88,7 +89,7 @@ IF(NOT CMAKE_CROSSCOMPILING) # http://bugs.opensolaris.org/bugdatabase/printableBug.do?bug_id=6478684 CHECK_C_SOURCE_RUNS(" int main() - { + { __asm__ __volatile__ (\"pause\"); return 0; }" IB_HAVE_PAUSE_INSTRUCTION) @@ -176,15 +177,6 @@ IF(WITH_INNODB) SET(WITH_INNOBASE_STORAGE_ENGINE TRUE) ENDIF() -SET(INNOBASE_PLUGIN_STATIC "innobase") -SET(INNOBASE_PLUGIN_DYNAMIC "ha_innodb") - -# Innobase depends on zlib. If server links with system -# zlib shared library, and innobase builds as shared library, -# innobase need to link with it too, to avoid unresolved symbols. -IF(ZLIB_FOUND AND NOT WITH_ZLIB STREQUAL "bundled") - SET(INNOBASE_LIBS ${ZLIB_LIBRARY}) -ENDIF() #The plugin's CMakeLists.txt still needs to work with previous versions of MySQL. IF(EXISTS ${SOURCE_DIR}/storage/mysql_storage_engine.cmake) @@ -207,5 +199,7 @@ ELSEIF (MYSQL_VERSION_ID LESS "50137") ELSE() # New plugin support, cross-platform , name for shared library # is given in INNOBASE_PLUGIN_STATIC and INNOBASE_PLUGIN_DYNAMIC - MYSQL_STORAGE_ENGINE(INNOBASE) + MYSQL_ADD_PLUGIN(INNOBASE ${INNOBASE_SOURCES} STORAGE_ENGINE + MODULE_OUTPUT_NAME ha_innodb + LINK_LIBRARIES ${ZLIB_LIBRARY}) ENDIF() diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt index 080448eb3f6..35b7629c597 100755 --- a/storage/myisam/CMakeLists.txt +++ b/storage/myisam/CMakeLists.txt @@ -28,7 +28,10 @@ SET(MYISAM_SOURCES ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c mi_unique.c mi_update.c mi_write.c rt_index.c rt_key.c rt_mbr.c rt_split.c sort.c sp_key.c ft_eval.h myisamdef.h rt_index.h mi_rkey.c) -MYSQL_STORAGE_ENGINE(MYISAM) +MYSQL_ADD_PLUGIN(myisam ${MYISAM_SOURCES} + STORAGE_ENGINE + MANDATORY + RECOMPILE_FOR_EMBEDDED) ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c) TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys) diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt index 206848b1715..59bc5ce4fc1 100755 --- a/storage/myisammrg/CMakeLists.txt +++ b/storage/myisammrg/CMakeLists.txt @@ -23,4 +23,4 @@ SET(MYISAMMRG_SOURCES myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myr myrg_rprev.c myrg_rrnd.c myrg_rsame.c myrg_static.c myrg_update.c myrg_write.c myrg_records.c) -MYSQL_STORAGE_ENGINE(MYISAMMRG) +MYSQL_ADD_PLUGIN(MYISAMMRG ${MYISAMMRG_SOURCES} STORAGE_ENGINE MANDATORY RECOMPILE_FOR_EMBEDDED) diff --git a/zlib/CMakeLists.txt b/zlib/CMakeLists.txt index 8aae6bdd287..4be153bfdfa 100755 --- a/zlib/CMakeLists.txt +++ b/zlib/CMakeLists.txt @@ -22,4 +22,5 @@ SET(ZLIB_SOURCES adler32.c compress.c crc32.c crc32.h deflate.c deflate.h gzio. inffixed.h inflate.c inflate.h inftrees.c inftrees.h trees.c trees.h uncompr.c zconf.h zlib.h zutil.c zutil.h) ADD_CONVENIENCE_LIBRARY(zlib ${ZLIB_SOURCES}) +RESTRICT_SYMBOL_EXPORTS(zlib) From 8486ae2114c134a11a1463e14378bf9922cf954c Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:14:06 +0300 Subject: [PATCH 051/466] Backport of: --------------------------------------------------------- 2630.7.2 Konstantin Osipov 2008-06-02 Fix alignment in sql_table.cc (no other changes). --- sql/sql_table.cc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9a55b386628..6aa3566b8fa 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6855,8 +6855,8 @@ view_err: { error= 0; push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), - table->alias); + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), + table->alias); } if (!error && (new_name != table_name || new_db != db)) @@ -6882,20 +6882,20 @@ view_err: */ if (!access(new_name_buff,F_OK)) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name); - error= -1; + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name); + error= -1; } else { - *fn_ext(new_name)=0; + *fn_ext(new_name)=0; pthread_mutex_lock(&LOCK_open); - if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) - error= -1; + if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) + error= -1; else if (Table_triggers_list::change_table_name(thd, db, table_name, new_db, new_alias)) { (void) mysql_rename_table(old_db_type, new_db, new_alias, db, - table_name, 0); + table_name, 0); error= -1; } pthread_mutex_unlock(&LOCK_open); @@ -6906,8 +6906,8 @@ view_err: { error= 0; push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), - table->alias); + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), + table->alias); } if (!error) @@ -6931,7 +6931,7 @@ view_err: them. TODO: Investigate what should be done with upgraded table-level - lock here... + lock here... */ if (new_name != table_name || new_db != db) { @@ -6966,7 +6966,7 @@ view_err: if (mysql_prepare_alter_table(thd, table, create_info, alter_info)) goto err; - + need_copy_table= alter_info->change_level; set_table_default_charset(thd, create_info, db); @@ -6990,7 +6990,7 @@ view_err: &index_add_buffer, &index_add_count, &candidate_key_count)) goto err; - + if (need_copy_table == ALTER_TABLE_METADATA_ONLY) need_copy_table= need_copy_table_res; } From b5f2b3bc4514e040220bcba8c63abb2cb273ee41 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 1 Dec 2009 14:18:53 +0100 Subject: [PATCH 052/466] fix make dist errors --- cmake/Makefile.am | 2 +- cmake/cmake_parse_arguments.cmake | 47 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 cmake/cmake_parse_arguments.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 40c7771662f..6beb4370ebf 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -1,5 +1,5 @@ EXTRA_DIST = \ - cmake_parse_arguments.cmake + cmake_parse_arguments.cmake \ configurable_file_content.in \ check_minimal_version.cmake \ create_initial_db.cmake.in \ diff --git a/cmake/cmake_parse_arguments.cmake b/cmake/cmake_parse_arguments.cmake new file mode 100644 index 00000000000..e9bd7f872d1 --- /dev/null +++ b/cmake/cmake_parse_arguments.cmake @@ -0,0 +1,47 @@ + +# Copyright (C) 2007 MySQL AB, 2009 Sun Microsystems,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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Handy macro to parse macro arguments +MACRO(CMAKE_PARSE_ARGUMENTS prefix arg_names option_names) + SET(DEFAULT_ARGS) + FOREACH(arg_name ${arg_names}) + SET(${prefix}_${arg_name}) + ENDFOREACH(arg_name) + FOREACH(option ${option_names}) + SET(${prefix}_${option} FALSE) + ENDFOREACH(option) + + SET(current_arg_name DEFAULT_ARGS) + SET(current_arg_list) + FOREACH(arg ${ARGN}) + SET(larg_names ${arg_names}) + LIST(FIND larg_names "${arg}" is_arg_name) + IF (is_arg_name GREATER -1) + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name ${arg}) + SET(current_arg_list) + ELSE (is_arg_name GREATER -1) + SET(loption_names ${option_names}) + LIST(FIND loption_names "${arg}" is_option) + IF (is_option GREATER -1) + SET(${prefix}_${arg} TRUE) + ELSE (is_option GREATER -1) + SET(current_arg_list ${current_arg_list} ${arg}) + ENDIF (is_option GREATER -1) + ENDIF (is_arg_name GREATER -1) + ENDFOREACH(arg) + SET(${prefix}_${current_arg_name} ${current_arg_list}) +ENDMACRO() \ No newline at end of file From 11eb7b94589c41d131d58c6525a8aeff85c2a5b2 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:27:03 +0300 Subject: [PATCH 053/466] Backport of: --------------------------------------------- 2630.7.3 Konstantin Osipov 2008-06-02 Various style changes preceding the removal of reopen_table(). (Post-review fixes for WL#3726). --- sql/event_db_repository.cc | 8 ++-- sql/mysql_priv.h | 5 ++- sql/sql_base.cc | 79 ++++++++++++++++++++++++++++++++++++++ sql/sql_table.cc | 79 -------------------------------------- sql/table.h | 4 +- 5 files changed, 89 insertions(+), 86 deletions(-) diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 0cf16e3a8a4..b17785a6be7 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -554,7 +554,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE_LIST tables; DBUG_ENTER("Event_db_repository::open_event_table"); - tables.init_one_table("mysql", "event", lock_type); + tables.init_one_table("mysql", "event", "event", lock_type); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) @@ -1109,7 +1109,7 @@ Event_db_repository::check_system_tables(THD *thd) /* Check mysql.db */ - tables.init_one_table("mysql", "db", TL_READ); + tables.init_one_table("mysql", "db", "db", TL_READ); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) @@ -1127,7 +1127,7 @@ Event_db_repository::check_system_tables(THD *thd) close_thread_tables(thd); } /* Check mysql.user */ - tables.init_one_table("mysql", "user", TL_READ); + tables.init_one_table("mysql", "user", "user", TL_READ); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) @@ -1148,7 +1148,7 @@ Event_db_repository::check_system_tables(THD *thd) close_thread_tables(thd); } /* Check mysql.event */ - tables.init_one_table("mysql", "event", TL_READ); + tables.init_one_table("mysql", "event", "event", TL_READ); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3e3d3b6df24..a29e819f90b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1033,8 +1033,6 @@ bool check_dup(const char *db, const char *name, TABLE_LIST *tables); bool compare_record(TABLE *table); bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); -bool wait_while_table_is_used(THD *thd, TABLE *table, - enum ha_extra_function function); bool table_def_init(void); void table_def_free(void); void assign_new_table_id(TABLE_SHARE *share); @@ -1390,6 +1388,9 @@ void add_join_on(TABLE_LIST *b,Item *expr); void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List *using_fields, SELECT_LEX *lex); bool add_proc_to_list(THD *thd, Item *item); +bool close_cached_table(THD *thd, TABLE *table); +bool wait_while_table_is_used(THD *thd, TABLE *table, + enum ha_extra_function function); void unlink_open_table(THD *thd, TABLE *find, bool unlock); void drop_open_table(THD *thd, TABLE *table, const char *db_name, const char *table_name); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6dc9c67f348..237c4c8e771 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2200,6 +2200,85 @@ static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp) } +/** + Force all other threads to stop using the table by upgrading + metadata lock on it and remove unused TABLE instances from cache. + + @param thd Thread handler + @param table Table to remove from cache + @param function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted + HA_EXTRA_FORCE_REOPEN if table is not be used + HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed + + @note When returning, the table will be unusable for other threads + until metadata lock is downgraded. + + @retval FALSE Success. + @retval TRUE Failure (e.g. because thread was killed). +*/ + +bool wait_while_table_is_used(THD *thd, TABLE *table, + enum ha_extra_function function) +{ + enum thr_lock_type old_lock_type; + + DBUG_ENTER("wait_while_table_is_used"); + DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", + table->s->table_name.str, (ulong) table->s, + table->db_stat, table->s->version)); + + (void) table->file->extra(function); + + old_lock_type= table->reginfo.lock_type; + mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ + + if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, + table->mdl_lock_data)) + { + mysql_lock_downgrade_write(thd, table, old_lock_type); + DBUG_RETURN(TRUE); + } + + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(FALSE); +} + + +/** + Upgrade metadata lock on the table and close all its instances. + + @param thd Thread handler + @param table Table to remove from cache + + @retval FALSE Success. + @retval TRUE Failure (e.g. because thread was killed). +*/ + +bool close_cached_table(THD *thd, TABLE *table) +{ + DBUG_ENTER("close_cached_table"); + + /* FIXME: check if we pass proper parameters everywhere. */ + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + DBUG_RETURN(TRUE); + + /* Close lock if this is not got with LOCK TABLES */ + if (thd->lock) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock=0; // Start locked threads + } + + pthread_mutex_lock(&LOCK_open); + /* Close all copies of 'table'. This also frees all LOCK TABLES lock */ + unlink_open_table(thd, table, TRUE); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(FALSE); +} + + /** Remove all instances of table from thread's open list and table cache. diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6aa3566b8fa..f5c22d32e94 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -53,7 +53,6 @@ static bool mysql_prepare_alter_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, Alter_info *alter_info); -static bool close_cached_table(THD *thd, TABLE *table); #ifndef DBUG_OFF @@ -4295,84 +4294,6 @@ mysql_rename_table(handlerton *base, const char *old_db, } -/** - Force all other threads to stop using the table by upgrading - metadata lock on it and remove unused TABLE instances from cache. - - @param thd Thread handler - @param table Table to remove from cache - @param function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted - HA_EXTRA_FORCE_REOPEN if table is not be used - HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed - - @note When returning, the table will be unusable for other threads - until metadata lock is downgraded. - - @retval FALSE Success. - @retval TRUE Failure (e.g. because thread was killed). -*/ - -bool wait_while_table_is_used(THD *thd, TABLE *table, - enum ha_extra_function function) -{ - enum thr_lock_type old_lock_type; - - DBUG_ENTER("wait_while_table_is_used"); - DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", - table->s->table_name.str, (ulong) table->s, - table->db_stat, table->s->version)); - - (void) table->file->extra(function); - - old_lock_type= table->reginfo.lock_type; - mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ - - if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, - table->mdl_lock_data)) - { - mysql_lock_downgrade_write(thd, table, old_lock_type); - DBUG_RETURN(TRUE); - } - - pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(FALSE); -} - - -/** - Upgrade metadata lock on the table and close all its instances. - - @param thd Thread handler - @param table Table to remove from cache - - @retval FALSE Success. - @retval TRUE Failure (e.g. because thread was killed). -*/ - -static bool close_cached_table(THD *thd, TABLE *table) -{ - DBUG_ENTER("close_cached_table"); - - /* FIXME: check if we pass proper parameters everywhere. */ - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) - DBUG_RETURN(TRUE); - - /* Close lock if this is not got with LOCK TABLES */ - if (thd->lock) - { - mysql_unlock_tables(thd, thd->lock); - thd->lock=0; // Start locked threads - } - - pthread_mutex_lock(&LOCK_open); - /* Close all copies of 'table'. This also frees all LOCK TABLES lock */ - unlink_open_table(thd, table, TRUE); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(FALSE); -} - static int send_check_errmsg(THD *thd, TABLE_LIST* table, const char* operator_name, const char* errmsg) diff --git a/sql/table.h b/sql/table.h index 601f1e154c9..de1354e17e5 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1121,11 +1121,13 @@ struct TABLE_LIST */ inline void init_one_table(const char *db_name_arg, const char *table_name_arg, + const char *alias_arg, enum thr_lock_type lock_type_arg) { bzero((char*) this, sizeof(*this)); db= (char*) db_name_arg; - table_name= alias= (char*) table_name_arg; + table_name= (char*) table_name_arg; + alias= (char*) alias_arg; lock_type= lock_type_arg; } From a2a276b200926a747c7cd2c8d2b8ce420fc2200d Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 1 Dec 2009 14:35:09 +0100 Subject: [PATCH 054/466] in RESTRICT_SYMBOL_EXPORTS,handle cases where COMPILE_FLAGS are not set (COMPILE_FLAGS-NOTFOUND) --- cmake/libutils.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index be0523d7080..0c34754bc75 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -260,6 +260,10 @@ ENDMACRO() MACRO(RESTRICT_SYMBOL_EXPORTS target) IF(CMAKE_COMPILER_IS_GNUCXX AND UNIX) GET_TARGET_PROPERTY(COMPILE_FLAGS ${target} COMPILE_FLAGS) + IF(NOT COMPILE_FLAGS) + # Avoid COMPILE_FLAGS-NOTFOUND + SET(COMPILE_FLAGS) + ENDIF() SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -fvisibility=hidden") ENDIF() From ded468704d485d04dd696d993822e1b05a818654 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:38:00 +0300 Subject: [PATCH 055/466] Backport of: ------------------------------------------------------------ revno: 2630.4.20 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Wed 2008-06-04 16:27:06 +0400 message: WL#3726 "DDL locking for all metadata objects" After review fixes in progress. Got rid of TABLE_LIST::mdl_upgradable member and related functions by using special flag which to be passed to open_table() which asks it to take upgradable metadata lock on table being opened. --- sql/mysql_priv.h | 3 ++- sql/sql_base.cc | 13 +++++++++---- sql/sql_insert.cc | 2 +- sql/sql_parse.cc | 20 ++------------------ sql/sql_table.cc | 12 +++++------- sql/sql_view.cc | 3 --- sql/table.h | 15 --------------- 7 files changed, 19 insertions(+), 49 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a29e819f90b..94875210d0b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1488,7 +1488,7 @@ inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) } /* simple open_and_lock_tables without derived handling for single table */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, - thr_lock_type lock_type); + thr_lock_type lock_type, uint flags); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags, bool *need_reopen); @@ -2040,6 +2040,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, #define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 #define MYSQL_LOCK_PERF_SCHEMA 0x0010 +#define MYSQL_OPEN_TAKE_UPGRADABLE_MDL 0x0020 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 237c4c8e771..a08a96465ca 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2598,6 +2598,9 @@ void table_share_release_hook(void *share) No version number checking is done. MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary table not the base table or view. + MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable + metadata lock for tables on which we are going to + take some kind of write table-level lock. IMPLEMENTATION Uses a cache of open tables to find a table not in use. @@ -2825,7 +2828,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { bool retry; - if (table_list->mdl_upgradable) + if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && + table_list->lock_type >= TL_WRITE_ALLOW_WRITE) mdl_set_upgradable(mdl_lock_data); mdl_set_lock_priority(mdl_lock_data, (flags & MYSQL_LOCK_IGNORE_FLUSH) ? MDL_HIGH_PRIO : MDL_NORMAL_PRIO); @@ -4146,7 +4150,6 @@ static int add_merge_table_list(TABLE_LIST *tlist) /* Set lock type. */ child_l->lock_type= tlist->lock_type; - child_l->mdl_upgradable= tlist->mdl_upgradable; /* Set parent reference. */ child_l->parent_l= tlist; @@ -4905,6 +4908,8 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, @param[in] thd thread handle @param[in] table_l table to open is first table in this list @param[in] lock_type lock to use for table + @param[in] flags options to be used while opening and locking + table (see open_table(), mysql_lock_tables()) @return table @retval != NULL OK, opened table returned @@ -4930,7 +4935,7 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, - thr_lock_type lock_type) + thr_lock_type lock_type, uint flags) { TABLE_LIST *save_next_global; DBUG_ENTER("open_n_lock_single_table"); @@ -4946,7 +4951,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, table_l->required_type= FRMTYPE_TABLE; /* Open the table. */ - if (simple_open_n_lock_tables(thd, table_l)) + if (open_and_lock_tables_derived(thd, table_l, FALSE, flags)) table_l->table= NULL; /* Just to be sure. */ /* Restore list. */ diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index a0c198f3196..75ad46f1440 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2304,7 +2304,7 @@ void kill_delayed_threads(void) bool Delayed_insert::open_and_lock_table() { if (!(table= open_n_lock_single_table(&thd, &table_list, - TL_WRITE_DELAYED))) + TL_WRITE_DELAYED, 0))) { thd.fatal_error(); // Abort waiting inserts return TRUE; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 52a4a4e0144..31bd34531e3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -47,7 +47,6 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); -static void adjust_mdl_locks_upgradability(TABLE_LIST *tables); const char *any_db="*any*"; // Special symbol for check_access @@ -3608,9 +3607,9 @@ end_with_restore_list: thd->options|= OPTION_TABLE_LOCK; alloc_mdl_locks(all_tables, &thd->locked_tables_root); thd->mdl_el_root= &thd->locked_tables_root; - adjust_mdl_locks_upgradability(all_tables); - if (!(res= simple_open_n_lock_tables(thd, all_tables))) + if (!(res= open_and_lock_tables_derived(thd, all_tables, FALSE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) { #ifdef HAVE_QUERY_CACHE if (thd->variables.query_cache_wlock_invalidate) @@ -8106,21 +8105,6 @@ bool parse_sql(THD *thd, return ret_value; } - -/** - Auxiliary function which marks metadata locks for all tables - on which we plan to take write lock as upgradable. -*/ - -static void adjust_mdl_locks_upgradability(TABLE_LIST *tables) -{ - for (TABLE_LIST *tab= tables; tab; tab= tab->next_global) - { - if (tab->lock_type >= TL_WRITE_ALLOW_WRITE) - tab->mdl_upgradable= TRUE; - } -} - /** @} (end of group Runtime_Environment) */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f5c22d32e94..306fa5dcdf4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4617,7 +4617,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - open_and_lock_tables(thd, table); + open_and_lock_tables_derived(thd, table, TRUE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL); thd->no_warnings_for_error= 0; table->next_global= save_next_global; table->next_local= save_next_local; @@ -5074,7 +5075,6 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list) bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_repair_table"); - set_all_mdl_upgradable(tables); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "repair", TL_WRITE, 1, test(check_opt->sql_flags & TT_USEFRM), @@ -5087,7 +5087,6 @@ bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_optimize_table"); - set_all_mdl_upgradable(tables); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "optimize", TL_WRITE, 1,0,0,0, &handler::ha_optimize, 0)); @@ -6604,9 +6603,8 @@ view_err: DBUG_RETURN(error); } - table_list->mdl_upgradable= TRUE; - - if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ))) + if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) DBUG_RETURN(TRUE); table->use_all_columns(); mdl_lock_data= table->mdl_lock_data; @@ -7898,7 +7896,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_n_lock_single_table(thd, table, TL_READ); + t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0); thd->clear_error(); // these errors shouldn't get client protocol->prepare_for_resend(); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c40f6643042..9382880ba1b 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1329,10 +1329,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, anyway. */ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) - { tbl->lock_type= table->lock_type; - tbl->mdl_upgradable= table->mdl_upgradable; - } /* If the view is mergeable, we might want to INSERT/UPDATE/DELETE into tables of this view. Preserve the diff --git a/sql/table.h b/sql/table.h index de1354e17e5..e7d7e2b08bf 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1371,11 +1371,6 @@ struct TABLE_LIST */ TAKE_EXCLUSIVE_MDL } open_type; - /** - Indicates that for this table/view we need to take shared metadata - lock which should be upgradable to exclusive metadata lock. - */ - bool mdl_upgradable; bool internal_tmp_table; /** TRUE if an alias for this table was specified in the SQL. */ bool is_alias; @@ -1796,14 +1791,4 @@ size_t max_row_length(TABLE *table, const uchar *data); void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root); -/** - Helper function which allows to mark all elements in table list - as requiring upgradable metadata locks. -*/ - -inline void set_all_mdl_upgradable(TABLE_LIST *tables) -{ - for (; tables; tables= tables->next_global) - tables->mdl_upgradable= TRUE; -} #endif /* TABLE_INCLUDED */ From f198e8bee9abad277607a427842edfae60ea35f1 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:42:28 +0300 Subject: [PATCH 056/466] Backport of: ---------------------------------------------------------- revno: 2630.2.20 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Fri 2008-06-27 20:10:42 +0400 message: Fix a regression introduced by WL#3726 when a table was left in the table cache after DROP DATABASE. Implementation of DROP DATABASE reads a list of files in the database directory and constructs from it the list of tables to be dropped. If the filesystem is case-insensitive and case-preserving, the table names should be lowercased, because the same has been done when entries for them were inserted into the table cache. Skipping this step will lead to orphaned TABLEs left in the table cache. Fixes lowercase_table2 failure on powermacg5. --- sql/sql_db.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 44909880da0..b5c51601faf 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1142,6 +1142,11 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, (void) filename_to_tablename(file->name, table_list->table_name, MYSQL50_TABLE_NAME_PREFIX_LENGTH + strlen(file->name) + 1); + + /* To be able to correctly look up the table in the table cache. */ + if (lower_case_table_names) + my_casedn_str(files_charset_info, table_list->table_name); + table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix); /* Link into list */ From 386b95dfef19d0ca248a72783e14dda5a925ec17 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:51:50 +0300 Subject: [PATCH 057/466] Backport of: ------------------------------------------------------------ revno: 2630.8.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w4 timestamp: Thu 2008-06-05 10:48:36 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Adjust some comments that were using old terminology (name locks instead of exclusive metadata locks), brought some of them up-to-date with current situation in code. --- sql/sql_base.cc | 30 +++++++++------------ sql/sql_delete.cc | 3 ++- sql/sql_handler.cc | 3 ++- sql/sql_partition.cc | 48 ++++++++++++++++----------------- sql/sql_show.cc | 7 ++--- sql/sql_table.cc | 63 ++++++++++++++++++++++---------------------- sql/sql_trigger.cc | 8 +++--- sql/table.h | 1 - 8 files changed, 77 insertions(+), 86 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a08a96465ca..8d7b0a3c88c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -869,7 +869,7 @@ void intern_close_table(TABLE *table) free_io_cache(table); delete table->triggers; - if (table->file) // Not true if name lock + if (table->file) // Not true if placeholder (void) closefrm(table, 1); // close file DBUG_VOID_RETURN; } @@ -2461,12 +2461,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) if (reopen_table_entry(thd, table, table_list, table_name, key, key_length)) { - /* - If there was an error during opening of table (for example if it - does not exist) '*table' object can be wiped out. To be able - properly release name-lock in this case we should restore this - object to its original state. - */ my_free((uchar*)table, MYF(0)); DBUG_RETURN(TRUE); } @@ -2509,8 +2503,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) exists and to FALSE otherwise. @note This function assumes that caller owns LOCK_open mutex. - It also assumes that the fact that there are no name-locks - on the table was checked beforehand. + It also assumes that the fact that there are no exclusive + metadata locks on the table was checked beforehand. @note If there is no .FRM file for the table but it exists in one of engines (e.g. it was created on another node of NDB cluster) @@ -2605,10 +2599,10 @@ void table_share_release_hook(void *share) IMPLEMENTATION Uses a cache of open tables to find a table not in use. - If table list element for the table to be opened has "create" flag - set and table does not exist, this function will automatically insert - a placeholder for exclusive name lock into the open tables cache and - will return the TABLE instance that corresponds to this placeholder. + If table list element for the table to be opened has "open_type" set + to OPEN_OR_CREATE and table does not exist, this function will take + exclusive metadata lock on the table, also it will do this if + "open_type" is TAKE_EXCLUSIVE_MDL. RETURN NULL Open failed. If refresh is set then one should close @@ -4708,11 +4702,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (action) { /* - We have met name-locked or old version of table. Now we have - to close all tables which are not up to date. We also have to - throw away set of prelocked tables (and thus close tables from - this set that were open by now) since it possible that one of - tables which determined its content was changed. + We have met exclusive metadata lock or old version of table. Now we + have to close all tables which are not up to date/release metadata + locks. We also have to throw away set of prelocked tables (and thus + close tables from this set that were open by now) since it possible + that one of tables which determined its content was changed. Instead of implementing complex/non-robust logic mentioned above we simply close and then reopen all tables. diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f5c6dfd8986..612f9d1954d 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1079,7 +1079,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) normally can't safely do this. - We don't want an ok to be sent to the end user. - We don't want to log the truncate command - - If we want to have a name lock on the table on exit without errors. + - If we want to keep exclusive metadata lock on the table (obtained by + caller) on exit without errors. */ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 15c73fcadfe..a2c1f0e3782 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -282,7 +282,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) The thd->handler_tables list is kept as-is to avoid deadlocks if open_table(), called by open_tables(), needs to back-off because - of a pending name-lock on the table being opened. + of a pending exclusive metadata lock or flush for the table being + opened. See open_table() back-off comments for more details. */ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 3c67574d8c1..edc9055fb39 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6528,10 +6528,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to the table have completed. This ensures that other threads can not execute on the table in parallel. - 4) Get a name lock on the table. This ensures that we can release all - locks on the table and since no one can open the table, there can - be no new threads accessing the table. They will be hanging on the - name lock. + 4) Get an exclusive metadata lock on the table. This ensures that we + can release all other locks on the table and since no one can open + the table, there can be no new threads accessing the table. They + will be hanging on this exclusive lock. 5) Close all tables that have already been opened but didn't stumble on the abort locked previously. This is done as part of the close_data_files_and_morph_locks call. @@ -6606,14 +6606,15 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users are still using the old partitioning scheme. Wait until all ongoing users have completed before progressing. - 4) Get a name lock on the table. This ensures that we can release all - locks on the table and since no one can open the table, there can - be no new threads accessing the table. They will be hanging on the - name lock. + 4) Get an exclusive metadata lock on the table. This ensures that we + can release all other locks on the table and since no one can open + the table, there can be no new threads accessing the table. They + will be hanging on this exclusive lock. 5) Close all tables that have already been opened but didn't stumble on the abort locked previously. This is done as part of the close_data_files_and_morph_locks call. - 6) Close all table handlers and unlock all handlers but retain name lock + 6) Close all table handlers and unlock all handlers but retain + metadata lock. 7) Write binlog 8) Now the change is completed except for the installation of the new frm file. We thus write an action in the log to change to @@ -6694,23 +6695,18 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, Copy from the reorganised partitions to the new partitions 4) Log that operation is completed and log all complete actions needed to complete operation from here - 5) Lock all partitions in TL_WRITE_ONLY to ensure that no users - are still using the old partitioning scheme. Wait until all - ongoing users have completed before progressing. - 6) Get a name lock of the table - 7) Close all tables opened but not yet locked, after this call we are - certain that no other thread is in the lock wait queue or has - opened the table. The name lock will ensure that they are blocked - on the open call. - This is achieved also by close_data_files_and_morph_locks call. - 8) Close all partitions opened by this thread, but retain name lock. - 9) Write bin log - 10) Prepare handlers for rename and delete of partitions - 11) Rename and drop the reorged partitions such that they are no - longer used and rename those added to their real new names. - 12) Install the shadow frm file - 13) Reopen the table if under lock tables - 14) Complete query + 5) Upgrade shared metadata lock on the table to an exclusive one. + After this we can be sure that there is no other connection + using this table (they will be waiting for metadata lock). + 6) Close all table instances opened by this thread, but retain + exclusive metadata lock. + 7) Write bin log + 8) Prepare handlers for rename and delete of partitions + 9) Rename and drop the reorged partitions such that they are no + longer used and rename those added to their real new names. + 10) Install the shadow frm file + 11) Reopen the table if under lock tables + 12) Complete query */ if (write_log_add_change_partition(lpt) || ERROR_INJECT_CRASH("crash_change_partition_1") || diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 647f914d28c..4d8e482cf04 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3271,7 +3271,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) /* We should not introduce deadlocks even if we already have some tables open and locked, since we won't lock tables which we will - open and will ignore possible name-locks for these tables. + open and will ignore pending exclusive metadata locks for these + tables by using high-priority requests for shared metadata locks. */ thd->reset_n_backup_open_tables_state(&open_tables_state_backup); @@ -7301,8 +7302,8 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) Open the table by name in order to load Table_triggers_list object. NOTE: there is race condition here -- the table can be dropped after - LOCK_open is released. It will be fixed later by introducing - acquire-shared-table-name-lock functionality. + LOCK_open is released. It will be fixed later by acquiring shared + metadata lock on trigger or table name. */ uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 306fa5dcdf4..b5f7fad36a6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3674,9 +3674,9 @@ static inline void write_create_table_bin_log(THD *thd, If one creates a temporary table, this is automatically opened Note that this function assumes that caller already have taken - name-lock on table being created or used some other way to ensure - that concurrent operations won't intervene. mysql_create_table() - is a wrapper that can be used for this. + exclusive metadata lock on table being created or used some other + way to ensure that concurrent operations won't intervene. + mysql_create_table() is a wrapper that can be used for this. no_log is needed for the case of CREATE ... SELECT, as the logging will be done later in sql_insert.cc @@ -5279,13 +5279,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, /* - By opening source table we guarantee that it exists and no concurrent - DDL operation will mess with it. Later we also take an exclusive - name-lock on target table name, which makes copying of .frm file, - call to ha_create_table() and binlogging atomic against concurrent DML - and DDL operations on target table. Thus by holding both these "locks" - we ensure that our statement is properly isolated from all concurrent - operations which matter. + By opening source table and thus acquiring shared metadata lock on it + we guarantee that it exists and no concurrent DDL operation will mess + with it. Later we also take an exclusive metadata lock on target table + name, which makes copying of .frm file, call to ha_create_table() and + binlogging atomic against concurrent DML and DDL operations on target + table. Thus by holding both these "locks" we ensure that our statement + is properly isolated from all concurrent operations which matter. */ if (open_tables(thd, &src_table, ¬_used, 0)) DBUG_RETURN(TRUE); @@ -5338,15 +5338,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Create a new table by copying from source table and sync the new table if the flag MY_SYNC is set - Altough exclusive name-lock on target table protects us from concurrent - DML and DDL operations on it we still want to wrap .FRM creation and call - to ha_create_table() in critical section protected by LOCK_open in order - to provide minimal atomicity against operations which disregard name-locks, - like I_S implementation, for example. This is a temporary and should not - be copied. Instead we should fix our code to always honor name-locks. - - Also some engines (e.g. NDB cluster) require that LOCK_open should be held - during the call to ha_create_table(). See bug #28614 for more info. + TODO: Obtaining LOCK_open mutex here is actually a legacy from the + times when some operations (e.g. I_S implementation) ignored + exclusive metadata lock on target table. Also some engines + (e.g. NDB cluster) require that LOCK_open should be held + during the call to ha_create_table() (See bug #28614 for more + info). So we should double check and probably fix this code + to not acquire this mutex. */ pthread_mutex_lock(&LOCK_open); if (src_table->schema_table) @@ -5461,9 +5459,9 @@ binlog: /* Here we open the destination table, on which we already have - name-lock. This is needed for store_create_info() to work. - The table will be closed by unlink_open_table() at the end - of this function. + exclusive metada lock. This is needed for store_create_info() + to work. The table will be closed by unlink_open_table() at + the end of this function. */ pthread_mutex_lock(&LOCK_open); if (reopen_name_locked_table(thd, table)) @@ -6794,9 +6792,9 @@ view_err: /* Then, we want check once again that target table does not exist. Actually the order of these two steps does not matter since - earlier we took name-lock on the target table, so we do them - in this particular order only to be consistent with 5.0, in which - we don't take this name-lock and where this order really matters. + earlier we took exclusive metadata lock on the target table, so + we do them in this particular order only to be consistent with 5.0, + in which we don't take this lock and where this order really matters. TODO: Investigate if we need this access() check at all. */ if (!access(new_name_buff,F_OK)) @@ -7354,18 +7352,19 @@ view_err: /* Data is copied. Now we: - 1) Wait until all other threads close old version of table. + 1) Wait until all other threads will stop using old version of table + by upgrading shared metadata lock to exclusive one. 2) Close instances of table open by this thread and replace them - with exclusive name-locks. + with placeholders to simplify reopen process. 3) Rename the old table to a temp name, rename the new one to the old name. 4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME we reopen new version of table. 5) Write statement to the binary log. 6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we - remove name-locks from list of open tables and table cache. + remove placeholders and release metadata locks. 7) If we are not not under LOCK TABLES we rely on close_thread_tables() - call to remove name-locks from table cache and list of open table. + call to remove placeholders and releasing metadata locks. */ thd_proc_info(thd, "rename result table"); @@ -7602,9 +7601,9 @@ err: err_with_placeholders: /* - An error happened while we were holding exclusive name-lock on table - being altered. To be safe under LOCK TABLES we should remove placeholders - from list of open tables list and table cache. + An error happened while we were holding exclusive name metadata lock + on table being altered. To be safe under LOCK TABLES we should remove + placeholders from the list of open tables and relese metadata lock. */ unlink_open_table(thd, table, FALSE); pthread_mutex_unlock(&LOCK_open); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index caf5c84e1f9..61cd9bffa57 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -525,7 +525,7 @@ end: /* If we are under LOCK TABLES we should restore original state of meta-data locks. Otherwise call to close_thread_tables() will take care about both - TABLE instance created by reopen_name_locked_table() and meta-data lock. + TABLE instance created by reopen_name_locked_table() and metadata lock. */ if (thd->locked_tables && tables && tables->table) mdl_downgrade_exclusive_lock(&thd->mdl_context, @@ -1872,7 +1872,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, i.e. it either will complete successfully, or will fail leaving files in their initial state. Also this method assumes that subject table is not renamed to itself. - This method needs to be called under an exclusive table name lock. + This method needs to be called under an exclusive table metadata lock. @retval FALSE Success @retval TRUE Error @@ -1894,8 +1894,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, /* This method interfaces the mysql server code protected by - either LOCK_open mutex or with an exclusive table name lock. - In the future, only an exclusive table name lock will be enough. + either LOCK_open mutex or with an exclusive metadata lock. + In the future, only an exclusive metadata lock will be enough. */ #ifndef DBUG_OFF if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table)) diff --git a/sql/table.h b/sql/table.h index e7d7e2b08bf..48caf962894 100644 --- a/sql/table.h +++ b/sql/table.h @@ -408,7 +408,6 @@ struct TABLE_SHARE bool db_low_byte_first; /* Portable row format */ bool crashed; bool is_view; - bool name_lock, replace_with_name_lock; ulong table_map_id; /* for row-based replication */ ulonglong table_map_version; From 8227a34e3686568a7e8511ef9de05b907534f5e2 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:55:45 +0300 Subject: [PATCH 058/466] Backport of: ------------------------------------------------------------ revno: 2630.4.22 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Thu 2008-06-05 22:06:48 +0400 message: WL#3726 "DDL locking for all metadata objects" After review fixes in progress. Moved code checking that current lock request can be satisfied given the current state of individual or global metadata lock to separate well-documented functions. --- sql/mdl.cc | 283 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 221 insertions(+), 62 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 7ed2f6e8bdf..c63edf143e8 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -408,6 +408,188 @@ static void release_lock_object(MDL_LOCK *lock) } +/** + Check if request for the lock on particular object can be satisfied given + current state of the global metadata lock. + + @note In other words, we're trying to check that the individual lock + request, implying a form of lock on the global metadata, is + compatible with the current state of the global metadata lock. + + @param lock_data Request for lock on an individual object, implying a + certain kind of global metadata lock. + + @retval TRUE - Lock request can be satisfied + @retval FALSE - There is some conflicting lock + + Here is a compatibility matrix defined by this function: + + | | Satisfied or pending requests + | | for global metadata lock + ----------------+-------------+-------------------------------------------- + Type of request | Correspond. | + for indiv. lock | global lock | Active-S Pending-S Active-IS(**) Active-IX + ----------------+-------------+-------------------------------------------- + S | IS | + + + + + upgradable S | IX | - - + + + X | IX | - - + + + S upgraded to X | IX (*) | 0 + + + + + Here: "+" -- means that request can be satisfied + "-" -- means that request can't be satisfied and should wait + "0" -- means impossible situation which will trigger assert + + (*) Since for upgradable shared locks we always take intention exclusive + global lock at the same time when obtaining the shared lock, there + is no need to obtain such lock during the upgrade itself. + (**) Since intention shared global locks are compatible with all other + type of locks we don't even have any accounting for them. +*/ + +static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) +{ + switch (lock_data->type) + { + case MDL_SHARED: + if (lock_data->is_upgradable && + (global_lock.active_shared || global_lock.waiting_shared)) + { + /* + We are going to obtain intention exclusive global lock and + there is active or pending shared global lock. Have to wait. + */ + return FALSE; + } + else + return TRUE; + break; + case MDL_EXCLUSIVE: + if (lock_data->state == MDL_PENDING_UPGRADE) + { + /* + We are upgrading MDL_SHARED to MDL_EXCLUSIVE. + + There should be no conflicting global locks since for each upgradable + shared lock we obtain intention exclusive global lock first. + */ + DBUG_ASSERT(global_lock.active_shared == 0 && + global_lock.active_intention_exclusive); + return TRUE; + } + else + { + if (global_lock.active_shared || global_lock.waiting_shared) + { + /* + We are going to obtain intention exclusive global lock and + there is active or pending shared global lock. + */ + return FALSE; + } + else + return TRUE; + } + break; + default: + DBUG_ASSERT(0); + } + return FALSE; +} + + +/** + Check if request for the lock can be satisfied given current state of lock. + + @param lock Lock. + @param lock_data Request for lock. + + @retval TRUE Lock request can be satisfied + @retval FALSE There is some conflicting lock. + + This function defines the following compatibility matrix for metadata locks: + + | Satisfied or pending requests which we have in MDL_LOCK + ----------------+--------------------------------------------------------- + Current request | Active-S Pending-X Active-X Act-S-pend-upgrade-to-X + ----------------+--------------------------------------------------------- + S | + - - (*) - + High-prio S | + + - + + X | - + - - + S upgraded to X | - (**) + 0 0 + + Here: "+" -- means that request can be satisfied + "-" -- means that request can't be satisfied and should wait + "0" -- means impossible situation which will trigger assert + + (*) Unless active exclusive lock belongs to the same context as shared + lock being requested. + (**) Unless all active shared locks belong to the same context as one + being upgraded. +*/ + +static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) +{ + switch (lock_data->type) + { + case MDL_SHARED: + if ((lock->active_exclusive.is_empty() && + (lock_data->prio == MDL_HIGH_PRIO || + lock->waiting_exclusive.is_empty() && + lock->active_shared_waiting_upgrade.is_empty())) || + (!lock->active_exclusive.is_empty() && + lock->active_exclusive.head()->ctx == lock_data->ctx)) + { + /* + When exclusive lock comes from the same context we can satisfy our + shared lock. This is required for CREATE TABLE ... SELECT ... and + ALTER VIEW ... AS .... + */ + return TRUE; + } + else + return FALSE; + break; + case MDL_EXCLUSIVE: + if (lock_data->state == MDL_PENDING_UPGRADE) + { + /* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. */ + MDL_LOCK_DATA *conf_lock_data; + I_P_List_iterator it(lock->active_shared); + + /* + There should be no active exclusive locks since we own shared lock + on the object. + */ + DBUG_ASSERT(lock->active_exclusive.is_empty() && + lock->active_shared_waiting_upgrade.head() == lock_data); + + while ((conf_lock_data= it++)) + { + /* + When upgrading shared lock to exclusive one we can have other shared + locks for the same object in the same context, e.g. in case when several + instances of TABLE are open. + */ + if (conf_lock_data->ctx != lock_data->ctx) + return FALSE; + } + return TRUE; + } + else + { + return (lock->active_exclusive.is_empty() && + lock->active_shared_waiting_upgrade.is_empty() && + lock->active_shared.is_empty()); + } + break; + default: + DBUG_ASSERT(0); + } + return FALSE; +} + + /** Try to acquire one shared lock. @@ -449,8 +631,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) pthread_mutex_lock(&LOCK_mdl); - if (lock_data->is_upgradable && - (global_lock.active_shared || global_lock.waiting_shared)) + if (!can_grant_global_lock(lock_data)) { pthread_mutex_unlock(&LOCK_mdl); *retry= TRUE; @@ -461,6 +642,11 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) lock_data->key_length))) { lock= get_lock_object(); + /* + Before inserting MDL_LOCK object into hash we should add at least one + MDL_LOCK_DATA to its lists in order to provide key for this element. + Thus we can't merge two branches of the above if-statement. + */ lock->active_shared.push_front(lock_data); lock->lock_data_count= 1; my_hash_insert(&mdl_locks, (uchar*)lock); @@ -471,18 +657,8 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) } else { - if ((lock->active_exclusive.is_empty() && - (lock_data->prio == MDL_HIGH_PRIO || - lock->waiting_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.is_empty())) || - (!lock->active_exclusive.is_empty() && - lock->active_exclusive.head()->ctx == lock_data->ctx)) + if (can_grant_lock(lock, lock_data)) { - /* - When exclusive lock comes from the same context we can satisfy our - shared lock. This is required for CREATE TABLE ... SELECT ... and - ALTER VIEW ... AS .... - */ lock->active_shared.push_front(lock_data); lock->lock_data_count++; lock_data->state= MDL_ACQUIRED; @@ -523,7 +699,7 @@ static void release_lock(MDL_LOCK_DATA *lock_data); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data, *conf_lock_data; + MDL_LOCK_DATA *lock_data; MDL_LOCK *lock; bool signalled= FALSE; const char *old_msg; @@ -552,6 +728,10 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) lock_data->key_length))) { lock= get_lock_object(); + /* + Again before inserting MDL_LOCK into hash provide key for + it by adding MDL_LOCK_DATA to one of its lists. + */ lock->waiting_exclusive.push_front(lock_data); lock->lock_data_count= 1; my_hash_insert(&mdl_locks, (uchar*)lock); @@ -572,30 +752,28 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { lock= lock_data->lock; - if (global_lock.active_shared || global_lock.waiting_shared) + if (!can_grant_global_lock(lock_data)) { /* - There is active or pending global shared lock we have + There is an active or pending global shared lock so we have to wait until it goes away. */ signalled= TRUE; break; } - else if (!lock->active_exclusive.is_empty() || - !lock->active_shared_waiting_upgrade.is_empty()) + else if (!can_grant_lock(lock, lock_data)) { - /* - Exclusive MDL owner won't wait on table-level lock the same - applies to shared lock waiting upgrade (in this cases we already - have some table-level lock). - */ - signalled= TRUE; - break; - } - else if ((conf_lock_data= lock->active_shared.head())) - { - signalled= notify_thread_having_shared_lock(thd, - conf_lock_data->ctx->thd); + MDL_LOCK_DATA *conf_lock_data; + I_P_List_iterator it(lock->active_shared); + + signalled= !lock->active_exclusive.is_empty() || + !lock->active_shared_waiting_upgrade.is_empty(); + + while ((conf_lock_data= it++)) + signalled|= + notify_thread_having_shared_lock(thd, conf_lock_data->ctx->thd); + break; } } @@ -674,7 +852,6 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) { - MDL_LOCK_DATA *conf_lock_data; MDL_LOCK *lock; const char *old_msg; THD *thd= context->thd; @@ -700,6 +877,8 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); lock_data->state= MDL_PENDING_UPGRADE; + /* Set type of lock request to the type at which we are aiming. */ + lock_data->type= MDL_EXCLUSIVE; lock->active_shared.remove(lock_data); /* There can be only one upgrader for this lock or we will have deadlock. @@ -711,44 +890,27 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, lock->active_shared_waiting_upgrade.push_front(lock_data); /* - There should be no conflicting global locks since for each upgradable - shared lock we obtain intention exclusive global lock first. + Since we should have been already acquired intention exclusive global lock + this call is only enforcing asserts. */ - DBUG_ASSERT(global_lock.active_shared == 0 && - global_lock.active_intention_exclusive); + DBUG_ASSERT(can_grant_global_lock(lock_data)); while (1) { - bool signalled= FALSE; - bool found_conflict= FALSE; - I_P_List_iterator it(lock->active_shared); + if (can_grant_lock(lock, lock_data)) + break; - DBUG_PRINT("info", ("looking at conflicting locks")); + bool signalled= FALSE; + MDL_LOCK_DATA *conf_lock_data; + I_P_List_iterator it(lock->active_shared); while ((conf_lock_data= it++)) { - /* - We can have other shared locks for the same object in the same context, - e.g. in case when several instances of TABLE are open. - */ if (conf_lock_data->ctx != context) - { - DBUG_PRINT("info", ("found active shared locks")); - found_conflict= TRUE; signalled|= notify_thread_having_shared_lock(thd, conf_lock_data->ctx->thd); - } } - /* - There should be no active exclusive locks since we own shared lock - on the object. - */ - DBUG_ASSERT(lock->active_exclusive.is_empty()); - - if (!found_conflict) - break; - if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); else @@ -767,6 +929,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, if (thd->killed) { lock_data->state= MDL_ACQUIRED; + lock_data->type= MDL_SHARED; lock->active_shared_waiting_upgrade.remove(lock_data); lock->active_shared.push_front(lock_data); /* Pending requests for shared locks can be satisfied now. */ @@ -778,7 +941,6 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, lock->active_shared_waiting_upgrade.remove(lock_data); lock->active_exclusive.push_front(lock_data); - lock_data->type= MDL_EXCLUSIVE; lock_data->state= MDL_ACQUIRED; if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); @@ -941,8 +1103,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) while ((lock_data= it++)) { DBUG_ASSERT(lock_data->state == MDL_PENDING); - if ((lock_data->is_upgradable || lock_data->type == MDL_EXCLUSIVE) && - (global_lock.active_shared || global_lock.waiting_shared)) + if (!can_grant_global_lock(lock_data)) break; /* To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. @@ -950,9 +1111,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) if (lock_data->type == MDL_SHARED && (lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, lock_data->key_length)) && - !(lock->active_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.is_empty() && - lock->waiting_exclusive.is_empty())) + !can_grant_lock(lock, lock_data)) break; } if (!lock_data) From 4c628c04e2341d932c05a2a2d7b1548f64792240 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:59:11 +0300 Subject: [PATCH 059/466] Backport of: ------------------------------------------------------------ revno: 2630.4.24 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Fri 2008-06-06 14:28:58 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Get rid of upgradability and priority attributes of metadata lock requests by replacing them with two new types of lock requests MDL_SHARED_UPGRADABLE and MDL_SHARED_HIGH_PRIO correspondingly. --- sql/mdl.cc | 97 +++++++++++++++++++++++++------------------------ sql/mdl.h | 54 +++++++-------------------- sql/sql_base.cc | 14 +++++-- sql/sql_show.cc | 2 +- 4 files changed, 75 insertions(+), 92 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index c63edf143e8..dbf08101159 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -261,8 +261,7 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) by-pointer because of the underlying HASH implementation requires the key to be a contiguous buffer. - The initialized lock request will have MDL_SHARED type and - normal priority. + The initialized lock request will have MDL_SHARED type. Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 Note that tables and views have the same lock type, since @@ -277,8 +276,6 @@ void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, lock_data->key= key; lock_data->type= MDL_SHARED; lock_data->state= MDL_PENDING; - lock_data->prio= MDL_NORMAL_PRIO; - lock_data->is_upgradable= FALSE; #ifndef DBUG_OFF lock_data->ctx= 0; lock_data->lock= 0; @@ -298,8 +295,7 @@ void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, @param name Name of of object @param root MEM_ROOT on which object should be allocated - @note The allocated lock request will have MDL_SHARED type and - normal priority. + @note The allocated lock request will have MDL_SHARED type. @retval 0 Error @retval non-0 Pointer to an object representing a lock request @@ -364,8 +360,7 @@ void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) we release all the locks acquired so-far but do not free them, since we know that the respective lock requests will be used again. - Also resets lock requests back to their initial state (i.e. - sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO). + Also resets lock requests back to their initial state (i.e. MDL_SHARED). @param context Context to be cleared. */ @@ -378,8 +373,6 @@ void mdl_remove_all_locks(MDL_CONTEXT *context) { /* Reset lock request back to its initial state. */ lock_data->type= MDL_SHARED; - lock_data->prio= MDL_NORMAL_PRIO; - lock_data->is_upgradable= FALSE; #ifndef DBUG_OFF lock_data->ctx= 0; #endif @@ -408,6 +401,16 @@ static void release_lock_object(MDL_LOCK *lock) } +/** + Helper functions which simplifies writing various checks and asserts. +*/ + +static bool is_shared(MDL_LOCK_DATA *lock_data) +{ + return (lock_data->type < MDL_EXCLUSIVE); +} + + /** Check if request for the lock on particular object can be satisfied given current state of the global metadata lock. @@ -430,7 +433,7 @@ static void release_lock_object(MDL_LOCK *lock) Type of request | Correspond. | for indiv. lock | global lock | Active-S Pending-S Active-IS(**) Active-IX ----------------+-------------+-------------------------------------------- - S | IS | + + + + + S, high-prio S | IS | + + + + upgradable S | IX | - - + + X | IX | - - + + S upgraded to X | IX (*) | 0 + + + @@ -451,8 +454,11 @@ static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) switch (lock_data->type) { case MDL_SHARED: - if (lock_data->is_upgradable && - (global_lock.active_shared || global_lock.waiting_shared)) + case MDL_SHARED_HIGH_PRIO: + return TRUE; + break; + case MDL_SHARED_UPGRADABLE: + if (global_lock.active_shared || global_lock.waiting_shared) { /* We are going to obtain intention exclusive global lock and @@ -512,7 +518,7 @@ static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) ----------------+--------------------------------------------------------- Current request | Active-S Pending-X Active-X Act-S-pend-upgrade-to-X ----------------+--------------------------------------------------------- - S | + - - (*) - + S, upgradable S | + - - (*) - High-prio S | + + - + X | - + - - S upgraded to X | - (**) + 0 0 @@ -532,8 +538,10 @@ static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) switch (lock_data->type) { case MDL_SHARED: + case MDL_SHARED_UPGRADABLE: + case MDL_SHARED_HIGH_PRIO: if ((lock->active_exclusive.is_empty() && - (lock_data->prio == MDL_HIGH_PRIO || + (lock_data->type == MDL_SHARED_HIGH_PRIO || lock->waiting_exclusive.is_empty() && lock->active_shared_waiting_upgrade.is_empty())) || (!lock->active_exclusive.is_empty() && @@ -619,11 +627,12 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) MDL_LOCK *lock; *retry= FALSE; - DBUG_ASSERT(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING); + DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_PENDING); safe_mutex_assert_not_owner(&LOCK_open); - if (lock_data->ctx->has_global_shared_lock && lock_data->is_upgradable) + if (lock_data->ctx->has_global_shared_lock && + lock_data->type == MDL_SHARED_UPGRADABLE) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; @@ -652,7 +661,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) my_hash_insert(&mdl_locks, (uchar*)lock); lock_data->state= MDL_ACQUIRED; lock_data->lock= lock; - if (lock_data->is_upgradable) + if (lock_data->type == MDL_SHARED_UPGRADABLE) global_lock.active_intention_exclusive++; } else @@ -663,7 +672,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) lock->lock_data_count++; lock_data->state= MDL_ACQUIRED; lock_data->lock= lock; - if (lock_data->is_upgradable) + if (lock_data->type == MDL_SHARED_UPGRADABLE) global_lock.active_intention_exclusive++; } else @@ -690,8 +699,7 @@ static void release_lock(MDL_LOCK_DATA *lock_data); The context may not have other lock requests. @note In case of failure (for example, if our thread was killed) - resets lock requests back to their initial state (MDL_SHARED - and MDL_NORMAL_PRIO). + resets lock requests back to their initial state (MDL_SHARED) @retval FALSE Success @retval TRUE Failure @@ -804,8 +812,6 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) release_lock(lock_data); /* Return lock request to its initial state. */ lock_data->type= MDL_SHARED; - lock_data->prio= MDL_NORMAL_PRIO; - lock_data->is_upgradable= FALSE; context->locks.remove(lock_data); } /* Pending requests for shared locks can be satisfied now. */ @@ -868,7 +874,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, if (lock_data->type == MDL_EXCLUSIVE) DBUG_RETURN(FALSE); - DBUG_ASSERT(lock_data->is_upgradable); + DBUG_ASSERT(lock_data->type == MDL_SHARED_UPGRADABLE); lock= lock_data->lock; @@ -929,7 +935,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, if (thd->killed) { lock_data->state= MDL_ACQUIRED; - lock_data->type= MDL_SHARED; + lock_data->type= MDL_SHARED_UPGRADABLE; lock->active_shared_waiting_upgrade.remove(lock_data); lock->active_shared.push_front(lock_data); /* Pending requests for shared locks can be satisfied now. */ @@ -1017,8 +1023,7 @@ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, Acquire global shared metadata lock. Holding this lock will block all requests for exclusive locks - and shared locks which can be potentially upgraded to exclusive - (see MDL_LOCK_DATA::is_upgradable). + and shared locks which can be potentially upgraded to exclusive. @param context Current metadata locking context. @@ -1108,7 +1113,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) /* To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. */ - if (lock_data->type == MDL_SHARED && + if (is_shared(lock_data) && (lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, lock_data->key_length)) && !can_grant_lock(lock, lock_data)) @@ -1149,19 +1154,21 @@ static void release_lock(MDL_LOCK_DATA *lock_data) if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); release_lock_object(lock); - if (lock_data->type == MDL_EXCLUSIVE && lock_data->state == MDL_ACQUIRED || - lock_data->type == MDL_SHARED && lock_data->state == MDL_ACQUIRED && - lock_data->is_upgradable) + if (lock_data->state == MDL_ACQUIRED && + (lock_data->type == MDL_EXCLUSIVE || + lock_data->type == MDL_SHARED_UPGRADABLE)) global_lock.active_intention_exclusive--; } else { switch (lock_data->type) { + case MDL_SHARED_UPGRADABLE: + global_lock.active_intention_exclusive--; + /* Fallthrough. */ case MDL_SHARED: + case MDL_SHARED_HIGH_PRIO: lock->active_shared.remove(lock_data); - if (lock_data->is_upgradable) - global_lock.active_intention_exclusive--; break; case MDL_EXCLUSIVE: if (lock_data->state == MDL_PENDING) @@ -1213,7 +1220,7 @@ void mdl_release_locks(MDL_CONTEXT *context) lists. Allows us to avoid problems in open_tables() in case of back-off */ - if (!(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING)) + if (!(is_shared(lock_data) && lock_data->state == MDL_PENDING)) { release_lock(lock_data); lock_data->state= MDL_PENDING; @@ -1224,7 +1231,7 @@ void mdl_release_locks(MDL_CONTEXT *context) /* We will return lock request to its initial state only in mdl_remove_all_locks() since we need to know type of lock - request and if it is upgradable in mdl_wait_for_locks(). + request in mdl_wait_for_locks(). */ } /* Inefficient but will do for a while */ @@ -1242,7 +1249,7 @@ void mdl_release_locks(MDL_CONTEXT *context) @param lock_data Lock to be released @note Resets lock request for lock released back to its initial state - (i.e.sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO). + (i.e. sets type to MDL_SHARED). */ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) @@ -1258,8 +1265,6 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) lock_data->state= MDL_PENDING; /* Return lock request to its initial state. */ lock_data->type= MDL_SHARED; - lock_data->prio= MDL_NORMAL_PRIO; - lock_data->is_upgradable= FALSE; context->locks.remove(lock_data); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); @@ -1318,16 +1323,14 @@ void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - if (lock_data->type == MDL_SHARED) + if (is_shared(lock_data)) return; lock= lock_data->lock; pthread_mutex_lock(&LOCK_mdl); - if (!lock_data->is_upgradable) - global_lock.active_intention_exclusive--; lock->active_exclusive.remove(lock_data); - lock_data->type= MDL_SHARED; + lock_data->type= MDL_SHARED_UPGRADABLE; lock->active_shared.push_front(lock_data); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); @@ -1379,10 +1382,11 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, while ((lock_data= it++) && (lock_data->key_length != key_length || - memcmp(lock_data->key, key, key_length))) + memcmp(lock_data->key, key, key_length) || + !(lock_data->type == MDL_EXCLUSIVE && + lock_data->state == MDL_ACQUIRED))) continue; - return (lock_data && lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_ACQUIRED); + return lock_data; } @@ -1432,8 +1436,7 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data) { bool result; - DBUG_ASSERT(lock_data->type == MDL_SHARED && - lock_data->state == MDL_ACQUIRED); + DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_ACQUIRED); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); diff --git a/sql/mdl.h b/sql/mdl.h index 12ce2bb9820..f99f38d6285 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -26,9 +26,20 @@ struct MDL_LOCK_DATA; struct MDL_LOCK; struct MDL_CONTEXT; -/** Type of metadata lock request. */ +/** + Type of metadata lock request. -enum enum_mdl_type {MDL_SHARED=0, MDL_EXCLUSIVE}; + - High-priority shared locks differ from ordinary shared locks by + that they ignore pending requests for exclusive locks. + - Upgradable shared locks can be later upgraded to exclusive + (because of that their acquisition involves implicit + acquisition of global intention-exclusive lock). + + @see Comments for can_grant_lock() and can_grant_global_lock() for details. +*/ + +enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, + MDL_SHARED_UPGRADABLE, MDL_EXCLUSIVE}; /** States which metadata lock request can have. */ @@ -36,16 +47,6 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_EXCLUSIVE}; enum enum_mdl_state {MDL_PENDING=0, MDL_ACQUIRED, MDL_PENDING_UPGRADE}; -/** - Priority of metadata lock requests. High priority attribute is - applicable only to requests for shared locks and indicates that - such request should ignore pending requests for exclusive locks - and for upgrading of shared locks to exclusive. -*/ - -enum enum_mdl_prio {MDL_NORMAL_PRIO=0, MDL_HIGH_PRIO}; - - /** A pending lock request or a granted metadata lock. A lock is requested or granted based on a fully qualified name and type. E.g. for a table @@ -60,13 +61,6 @@ struct MDL_LOCK_DATA uint key_length; enum enum_mdl_type type; enum enum_mdl_state state; - enum enum_mdl_prio prio; - /** - TRUE -- if shared lock corresponding to this lock request at some - point might be upgraded to an exclusive lock and therefore conflicts - with global shared lock, FALSE -- otherwise. - */ - bool is_upgradable; private: /** @@ -170,28 +164,6 @@ inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) lock_data->type= lock_type; } -/** - Set priority for lock request. High priority can be only set - for shared locks. -*/ - -inline void mdl_set_lock_priority(MDL_LOCK_DATA *lock_data, enum_mdl_prio prio) -{ - DBUG_ASSERT(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING); - lock_data->prio= prio; -} - -/** - Mark request for shared lock as upgradable. Can be only applied - to pending locks. -*/ - -inline void mdl_set_upgradable(MDL_LOCK_DATA *lock_data) -{ - DBUG_ASSERT(lock_data->type == MDL_SHARED && lock_data->state == MDL_PENDING); - lock_data->is_upgradable= TRUE; -} - bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8d7b0a3c88c..e1a8e9e79cb 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2822,11 +2822,19 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { bool retry; + /* + There is no MDL_SHARED_UPGRADABLE_HIGH_PRIO type of metadata lock so we + want to be sure that caller doesn't pass us both flags simultaneously. + */ + DBUG_ASSERT(!(flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL) || + !(flags & MYSQL_LOCK_IGNORE_FLUSH)); + if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && table_list->lock_type >= TL_WRITE_ALLOW_WRITE) - mdl_set_upgradable(mdl_lock_data); - mdl_set_lock_priority(mdl_lock_data, (flags & MYSQL_LOCK_IGNORE_FLUSH) ? - MDL_HIGH_PRIO : MDL_NORMAL_PRIO); + mdl_set_lock_type(mdl_lock_data, MDL_SHARED_UPGRADABLE); + if (flags & MYSQL_LOCK_IGNORE_FLUSH) + mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + if (mdl_acquire_shared_lock(mdl_lock_data, &retry)) { if (retry) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 4d8e482cf04..3d4c0a5aaf7 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3136,7 +3136,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, mdl_init_lock(&mdl_lock_data, mdlkey, 0, db_name->str, table_name->str); table_list.mdl_lock_data= &mdl_lock_data; mdl_add_lock(&thd->mdl_context, &mdl_lock_data); - mdl_set_lock_priority(&mdl_lock_data, MDL_HIGH_PRIO); + mdl_set_lock_type(&mdl_lock_data, MDL_SHARED_HIGH_PRIO); /* TODO: investigate if in this particular situation we can get by From 801ef812ba08174852c05f254a28a585cf0438dd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 17:02:32 +0300 Subject: [PATCH 060/466] Backport of: ------------------------------------------------------------ revno: 2630.4.25 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Fri 2008-06-06 15:32:48 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Clarified some comments explaining control flow in prepare_for_repair(). --- sql/sql_table.cc | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b5f7fad36a6..cac4bab27b7 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4397,16 +4397,16 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (!(check_opt->sql_flags & TT_USEFRM)) DBUG_RETURN(0); - if (!(table= table_list->table)) /* if open_ltable failed */ + if (!(table= table_list->table)) { + /* + Attempt to do full-blown table open in mysql_admin_table() has failed. + Let us try to open at least a .FRM for this table. + */ char key[MAX_DBKEY_LENGTH]; uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); - /* - TODO: Check that REPAIR's code also conforms to meta-data - locking protocol. Fix if it is not. - */ mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, thd->mem_root); mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); @@ -4486,12 +4486,19 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (table_list->table) { - /* If we could open the table, close it */ + /* + Table was successfully open in mysql_admin_table(). Now we need + to close it, but leave it protected by exclusive metadata lock. + */ if (close_cached_table(thd, table)) goto end; table_list->table= 0; } - // After this point we have X mdl lock in both cases + /* + After this point we have an exclusive metadata lock on our table + in both cases when table was successfully open in mysql_admin_table() + and when it was open in prepare_for_repair(). + */ if (my_rename(from, tmp, MYF(MY_WME))) { From 1523cea6e9f3b93f7c94036228084bd711a0d9a0 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 17:39:03 +0300 Subject: [PATCH 061/466] Backport of: ---------------------------------------------------------- revno: 2630.4.26 committer: Konstantin Osipov branch nick: mysql-6.0-prelocked_mode-to-push timestamp: Fri 2008-06-06 23:19:04 +0400 message: WL#3726: work on review comments. Remove thd->locked_tables. Always store MYSQL_LOCK instances in thd->lock. Rename thd->prelocked_mode to thd->locked_tables_mode. Use thd->locked_tables_mode to determine if we are under LOCK TABLES. Update the code to not assume that if thd->lock is set, LOCK TABLES mode is off. Review comments. --- sql/ha_ndbcluster_binlog.cc | 10 ++- sql/handler.cc | 13 +-- sql/lock.cc | 2 +- sql/log.cc | 2 +- sql/set_var.cc | 2 +- sql/sp_head.cc | 6 +- sql/sql_base.cc | 165 +++++++++++++++++------------------- sql/sql_cache.cc | 4 +- sql/sql_class.cc | 14 ++- sql/sql_class.h | 74 ++++++++-------- sql/sql_cursor.cc | 2 - sql/sql_insert.cc | 28 ++++-- sql/sql_load.cc | 5 +- sql/sql_parse.cc | 42 ++++----- sql/sql_partition.cc | 2 +- sql/sql_rename.cc | 2 +- sql/sql_select.cc | 4 +- sql/sql_table.cc | 26 +++--- sql/sql_trigger.cc | 8 +- sql/sql_update.cc | 2 +- sql/sql_view.cc | 4 +- storage/myisam/ha_myisam.cc | 4 +- 22 files changed, 208 insertions(+), 213 deletions(-) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 14d14db6b7d..6f0e4498d75 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -264,7 +264,7 @@ static void run_query(THD *thd, char *buf, char *end, DBUG_PRINT("query", ("%s", thd->query())); DBUG_ASSERT(!thd->in_sub_stmt); - DBUG_ASSERT(!thd->prelocked_mode); + DBUG_ASSERT(!thd->locked_tables_mode); mysql_parse(thd, thd->query(), thd->query_length(), &found_semicolon); @@ -2429,8 +2429,12 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) goto add_ndb_binlog_index_err; } - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; + + if (! thd->locked_tables_mode) /* Is always TRUE */ + { + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + } thd->options= saved_options; return 0; add_ndb_binlog_index_err: diff --git a/sql/handler.cc b/sql/handler.cc index 9c32171eefd..9366d91b290 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4527,9 +4527,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) DESCRIPTION This function will generate and write table maps for all tables - that are locked by the thread 'thd'. Either manually locked - (stored in THD::locked_tables) and automatically locked (stored - in THD::lock) are considered. + that are locked by the thread 'thd'. RETURN VALUE 0 All OK @@ -4537,25 +4535,22 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) SEE ALSO THD::lock - THD::locked_tables */ static int write_locked_table_maps(THD *thd) { DBUG_ENTER("write_locked_table_maps"); - DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx " + DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx " "thd->extra_lock: 0x%lx", - (long) thd, (long) thd->lock, - (long) thd->locked_tables, (long) thd->extra_lock)); + (long) thd, (long) thd->lock, (long) thd->extra_lock)); DBUG_PRINT("debug", ("get_binlog_table_maps(): %d", thd->get_binlog_table_maps())); if (thd->get_binlog_table_maps() == 0) { - MYSQL_LOCK *locks[3]; + MYSQL_LOCK *locks[2]; locks[0]= thd->extra_lock; locks[1]= thd->lock; - locks[2]= thd->locked_tables; for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i ) { MYSQL_LOCK const *const lock= locks[i]; diff --git a/sql/lock.cc b/sql/lock.cc index f391b323a59..33c9edcea48 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -754,7 +754,7 @@ TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle, goto end; /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */ - if (! (mylock= thd->lock ? thd->lock : thd->locked_tables)) + if (! (mylock= thd->lock)) goto end; /* If we have less than two tables, we cannot have duplicates. */ diff --git a/sql/log.cc b/sql/log.cc index c6802a9a6ed..dd82134ae56 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4060,7 +4060,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) this will close all tables on the slave. */ bool const end_stmt= - thd->prelocked_mode && thd->lex->requires_prelocking(); + thd->locked_tables_mode && thd->lex->requires_prelocking(); if (thd->binlog_flush_pending_rows_event(end_stmt)) DBUG_RETURN(error); diff --git a/sql/set_var.cc b/sql/set_var.cc index 1490ffd9598..cff85df6e7c 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -4292,7 +4292,7 @@ bool sys_var_opt_readonly::update(THD *thd, set_var *var) DBUG_ENTER("sys_var_opt_readonly::update"); /* Prevent self dead-lock */ - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); DBUG_RETURN(true); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index fdf9909f85e..a0832285742 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1257,7 +1257,7 @@ sp_head::execute(THD *thd) Will write this SP statement into binlog separately (TODO: consider changing the condition to "not inside event union") */ - if (thd->prelocked_mode == NON_PRELOCKED) + if (thd->locked_tables_mode == LTM_NONE) thd->user_var_events_alloc= thd->mem_root; err_status= i->execute(thd, &ip); @@ -1269,7 +1269,7 @@ sp_head::execute(THD *thd) If we've set thd->user_var_events_alloc to mem_root of this SP statement, clean all the events allocated in it. */ - if (thd->prelocked_mode == NON_PRELOCKED) + if (thd->locked_tables_mode == LTM_NONE) { reset_dynamic(&thd->user_var_events); thd->user_var_events_alloc= NULL;//DEBUG @@ -2740,7 +2740,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, thd->query_id= next_query_id(); pthread_mutex_unlock(&LOCK_thread_count); - if (thd->prelocked_mode == NON_PRELOCKED) + if (thd->locked_tables_mode == LTM_NONE) { /* This statement will enter/leave prelocked mode on its own. diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e1a8e9e79cb..09bb05a91c1 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1025,7 +1025,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, DBUG_ASSERT(!have_lock); pthread_mutex_unlock(&LOCK_open); - if (thd->locked_tables) + if (thd->locked_tables_mode) { /* If we are under LOCK TABLES we need to reopen tables without @@ -1141,7 +1141,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, } err_with_reopen: - if (thd->locked_tables) + if (thd->locked_tables_mode) { pthread_mutex_lock(&LOCK_open); /* @@ -1375,7 +1375,6 @@ void close_thread_tables(THD *thd, bool skip_mdl) { TABLE *table; - prelocked_mode_type prelocked_mode= thd->prelocked_mode; DBUG_ENTER("close_thread_tables"); #ifdef EXTRA_DEBUG @@ -1433,11 +1432,12 @@ void close_thread_tables(THD *thd, Reset transaction state, but only if we're not inside a sub-statement of a prelocked statement. */ - if (! prelocked_mode || thd->lex->requires_prelocking()) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES || + thd->lex->requires_prelocking()) thd->transaction.stmt.reset(); } - if (thd->locked_tables || prelocked_mode) + if (thd->locked_tables_mode) { /* Ensure we are calling ha_reset() for all used tables */ @@ -1447,7 +1447,7 @@ void close_thread_tables(THD *thd, We are under simple LOCK TABLES or we're inside a sub-statement of a prelocked statement, so should not do anything else. */ - if (!prelocked_mode || !thd->lex->requires_prelocking()) + if (! thd->lex->requires_prelocking()) DBUG_VOID_RETURN; /* @@ -1455,18 +1455,19 @@ void close_thread_tables(THD *thd, so we have to leave the prelocked mode now with doing implicit UNLOCK TABLES if needed. */ - DBUG_PRINT("info",("thd->prelocked_mode= NON_PRELOCKED")); - thd->prelocked_mode= NON_PRELOCKED; + if (thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) + thd->locked_tables_mode= LTM_LOCK_TABLES; - if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES) + if (thd->locked_tables_mode == LTM_LOCK_TABLES) DBUG_VOID_RETURN; + thd->locked_tables_mode= LTM_NONE; + thd->options&= ~OPTION_TABLE_LOCK; + /* Note that we are leaving prelocked mode so we don't need to care about THD::locked_tables_root. */ - thd->lock= thd->locked_tables; - thd->locked_tables= 0; /* Fallthrough */ } @@ -1501,16 +1502,6 @@ void close_thread_tables(THD *thd, mdl_remove_all_locks(&thd->mdl_context); } - if (prelocked_mode == PRELOCKED) - { - /* - If we are here then we are leaving normal prelocked mode, so it is - good idea to turn off OPTION_TABLE_LOCK flag. - */ - DBUG_ASSERT(thd->lex->requires_prelocking()); - thd->options&= ~(OPTION_TABLE_LOCK); - } - DBUG_VOID_RETURN; } @@ -1951,9 +1942,10 @@ TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list) Try to locate the table in the list of thd->temporary_tables. If the table is found: - if the table is being used by some outer statement, fail. - - if the table is in thd->locked_tables, unlock it and - remove it from the list of locked tables. Currently only transactional - temporary tables are present in the locked_tables list. + - if the table is locked with LOCK TABLES or by prelocking, + unlock it and remove it from the list of locked tables + (THD::lock). Currently only transactional temporary tables + are locked. - Close the temporary table, remove its .FRM - remove the table from the list of temporary tables @@ -1992,7 +1984,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list) If LOCK TABLES list is not empty and contains this table, unlock the table and remove the table from this list. */ - mysql_lock_remove(thd, thd->locked_tables, table, FALSE); + mysql_lock_remove(thd, thd->lock, table, FALSE); close_temporary_table(thd, table, 1, 1); DBUG_RETURN(0); } @@ -2265,7 +2257,7 @@ bool close_cached_table(THD *thd, TABLE *table) DBUG_RETURN(TRUE); /* Close lock if this is not got with LOCK TABLES */ - if (thd->lock) + if (! thd->locked_tables_mode) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; // Start locked threads @@ -2318,8 +2310,8 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) if (list->s->table_cache_key.length == key_length && !memcmp(list->s->table_cache_key.str, key, key_length)) { - if (unlock && thd->locked_tables) - mysql_lock_remove(thd, thd->locked_tables, + if (unlock && thd->locked_tables_mode) + mysql_lock_remove(thd, thd->lock, list->parent ? list->parent : list, TRUE); /* Prepare MERGE table for close. Close parent if necessary. */ @@ -2690,7 +2682,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, open not pre-opened tables in pre-locked/LOCK TABLES mode. TODO: move this block into a separate function. */ - if (thd->locked_tables || thd->prelocked_mode) + if (thd->locked_tables_mode) { // Using table locks TABLE *best_table= 0; int best_distance= INT_MIN; @@ -2705,7 +2697,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, */ if (!my_strcasecmp(system_charset_info, table->alias, alias) && table->query_id != thd->query_id && /* skip tables already used */ - !(thd->prelocked_mode && table->query_id) && + (thd->locked_tables_mode == LTM_LOCK_TABLES || + table->query_id == 0) && !table->parent) { int distance= ((int) table->reginfo.lock_type - @@ -2788,7 +2781,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, so we may only end up here if the table did not exist when locked tables list was created. */ - if (thd->prelocked_mode == PRELOCKED) + if (thd->locked_tables_mode == LTM_PRELOCKED) my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); else my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); @@ -3329,7 +3322,7 @@ void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, safe_mutex_assert_owner(&LOCK_open); - if (thd->lock) + if (! thd->locked_tables_mode) { /* If we are not under LOCK TABLES we should have only one table @@ -3344,7 +3337,7 @@ void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, if (!strcmp(table->s->table_name.str, table_name) && !strcmp(table->s->db.str, db)) { - if (thd->locked_tables) + if (thd->locked_tables_mode) { if (table->parent) { @@ -3354,11 +3347,11 @@ void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, the parent and close it. OTOH in most cases a MERGE table won't have multiple children with the same db.table_name. */ - mysql_lock_remove(thd, thd->locked_tables, table->parent, TRUE); + mysql_lock_remove(thd, thd->lock, table->parent, TRUE); close_handle_and_leave_table_as_placeholder(table->parent); } else - mysql_lock_remove(thd, thd->locked_tables, table, TRUE); + mysql_lock_remove(thd, thd->lock, table, TRUE); } table->s->version= 0; close_handle_and_leave_table_as_placeholder(table); @@ -3554,7 +3547,7 @@ bool reopen_tables(THD *thd, bool get_locks) if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), flags, ¬_used))) { - thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock); + thd->lock= mysql_lock_merge(thd->lock, lock); } else { @@ -3587,17 +3580,22 @@ void unlock_locked_tables(THD *thd) DBUG_ASSERT(!thd->in_sub_stmt && !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); - if (thd->locked_tables) - { - thd->lock= thd->locked_tables; - thd->locked_tables=0; - close_thread_tables(thd); - /* - After closing tables we can free memory used for storing lock - request objects for metadata locks - */ - free_root(&thd->locked_tables_root, MYF(MY_MARK_BLOCKS_FREE)); - } + /* + Sic: we must be careful to not close open tables if + we're not in LOCK TABLES mode: unlock_locked_tables() is + sometimes called implicitly, expecting no effect on + open tables, e.g. from begin_trans(). + */ + if (thd->locked_tables_mode != LTM_LOCK_TABLES) + return; + + thd->locked_tables_mode= LTM_NONE; + close_thread_tables(thd); + /* + After closing tables we can free memory used for storing lock + request objects for metadata locks + */ + free_root(&thd->locked_tables_root, MYF(MY_MARK_BLOCKS_FREE)); } @@ -4494,11 +4492,6 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) prelocking it won't do such precaching and will simply reuse table list which is already built. - If any table has a trigger and start->trg_event_map is non-zero - the final lock will end up in thd->locked_tables, otherwise, the - lock will be placed in thd->lock. See also comments in - st_lex::set_trg_event_type_for_tables(). - RETURN 0 - OK -1 - error @@ -4543,10 +4536,10 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) If we are not already executing prelocked statement and don't have statement for which table list for prelocking is already built, let us cache routines and try to build such table list. - */ - if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + !thd->lex->requires_prelocking() && thd->lex->uses_stored_routines()) { bool first_no_prelocking, need_prelocking; @@ -4759,7 +4752,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) If we lock table for reading we won't update it so there is no need to process its triggers since they never will be activated. */ - if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + !thd->lex->requires_prelocking() && tables->trg_event_map && tables->table->triggers && tables->lock_type >= TL_WRITE_ALLOW_WRITE) { @@ -4780,7 +4774,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); } - if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) + if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) { if (tables->lock_type == TL_WRITE_DEFAULT) tables->table->reginfo.lock_type= thd->update_lock_default; @@ -4803,10 +4797,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) DBUG_PRINT("tcache", ("is parent: %d is child: %d", test(tables->table->child_l), test(tables->parent_l))); - DBUG_PRINT("tcache", ("in lock tables: %d in prelock mode: %d", - test(thd->locked_tables), test(thd->prelocked_mode))); - if (((!thd->locked_tables && !thd->prelocked_mode) || - tables->table->s->tmp_table) && + if ((!thd->locked_tables_mode || tables->table->s->tmp_table) && ((tables->table->child_l && add_merge_table_list(tables)) || (tables->parent_l && @@ -4822,7 +4813,8 @@ process_view_routines: Again we may need cache all routines used by this view and add tables used by them to table list. */ - if (tables->view && !thd->prelocked_mode && + if (tables->view && + thd->locked_tables_mode <= LTM_LOCK_TABLES && !thd->lex->requires_prelocking() && tables->view->uses_stored_routines()) { @@ -4996,7 +4988,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, DBUG_ENTER("open_ltable"); /* should not be used in a prelocked_mode context, see NOTE above */ - DBUG_ASSERT(!thd->prelocked_mode); + DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED); thd_proc_info(thd, "Opening table"); thd->current_tablenr= 0; @@ -5033,7 +5025,7 @@ retry: table_list->lock_type= lock_type; table_list->table= table; table->grant= table_list->grant; - if (thd->locked_tables) + if (thd->locked_tables_mode) { if (check_lock_and_start_stmt(thd, table, lock_type)) table= 0; @@ -5355,9 +5347,9 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) handling thr_lock gives us. You most always get all needed locks at once. - If query for which we are calling this function marked as requring - prelocking, this function will do implicit LOCK TABLES and change - thd::prelocked_mode accordingly. + If query for which we are calling this function marked as requiring + prelocking, this function will change locked_tables_mode to + LTM_PRELOCKED. RETURN VALUES 0 ok @@ -5374,22 +5366,24 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, We can't meet statement requiring prelocking if we already in prelocked mode. */ - DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking()); + DBUG_ASSERT(thd->locked_tables_mode <= LTM_LOCK_TABLES || + !thd->lex->requires_prelocking()); *need_reopen= FALSE; if (!tables && !thd->lex->requires_prelocking()) DBUG_RETURN(decide_logging_format(thd, tables)); /* - We need this extra check for thd->prelocked_mode because we want to avoid - attempts to lock tables in substatements. Checking for thd->locked_tables - is not enough in some situations. For example for SP containing + Check for thd->locked_tables_mode to avoid a redundant + and harmful attempt to lock the already locked tables again. + Checking for thd->lock is not enough in some situations. For example, + if a stored function contains "drop table t3; create temporary t3 ..; insert into t3 ...;" - thd->locked_tables may be 0 after drop tables, and without this extra - check insert will try to lock temporary table t3, that will lead - to memory leak... + thd->lock may be 0 after drop tables, whereas locked_tables_mode + is still on. In this situation an attempt to lock temporary + table t3 will lead to a memory leak. */ - if (!thd->locked_tables && !thd->prelocked_mode) + if (! thd->locked_tables_mode) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; @@ -5443,15 +5437,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, We just have done implicit LOCK TABLES, and now we have to emulate first open_and_lock_tables() after it. - Note that "LOCK TABLES" can also be marked as requiring prelocking - (e.g. if one locks view which uses functions). We should not emulate - such open_and_lock_tables() in this case. We also should not set - THD::prelocked_mode or first close_thread_tables() call will do - "UNLOCK TABLES". */ - thd->locked_tables= thd->lock; - thd->lock= 0; - thd->in_lock_tables=0; + thd->in_lock_tables= 0; /* When open_and_lock_tables() is called for a single table out of @@ -5474,8 +5461,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, This was an attempt to enter prelocked mode so there is no need to care about THD::locked_tables_root here. */ - mysql_unlock_tables(thd, thd->locked_tables); - thd->locked_tables= 0; + mysql_unlock_tables(thd, thd->lock); thd->options&= ~(OPTION_TABLE_LOCK); DBUG_RETURN(-1); } @@ -5486,8 +5472,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, and was marked as occupied during open_tables() as free for reuse. */ mark_real_tables_as_free_for_reuse(first_not_own); - DBUG_PRINT("info",("prelocked_mode= PRELOCKED")); - thd->prelocked_mode= PRELOCKED; + DBUG_PRINT("info",("locked_tables_mode= PRELOCKED")); + thd->locked_tables_mode= LTM_PRELOCKED; } } else @@ -5512,7 +5498,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, In a stored function or trigger we should ensure that we won't change a table that is already used by the calling statement. */ - if (thd->prelocked_mode && + if (thd->locked_tables_mode >= LTM_PRELOCKED && table->lock_type >= TL_WRITE_ALLOW_WRITE) { for (TABLE* opentab= thd->open_tables; opentab; opentab= opentab->next) @@ -5540,8 +5526,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, if (thd->lex->requires_prelocking()) { mark_real_tables_as_free_for_reuse(first_not_own); - DBUG_PRINT("info", ("thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES")); - thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES; + DBUG_PRINT("info", + ("thd->locked_tables_mode= LTM_PRELOCKED_UNDER_LOCK_TABLES")); + thd->locked_tables_mode= LTM_PRELOCKED_UNDER_LOCK_TABLES; } } diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index fb370cbd16a..871efeddd43 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1136,7 +1136,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) See also a note on double-check locking usage above. */ - if (thd->locked_tables || query_cache_size == 0) + if (thd->locked_tables_mode || query_cache_size == 0) DBUG_VOID_RETURN; uint8 tables_type= 0; @@ -1364,7 +1364,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) See also a note on double-check locking usage above. */ - if (is_disabled() || thd->locked_tables || + if (is_disabled() || thd->locked_tables_mode || thd->variables.query_cache_type == 0 || query_cache_size == 0) goto err; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 9445f092546..1f0c8321608 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -995,11 +995,7 @@ void THD::cleanup(void) ha_rollback(this); xid_cache_delete(&transaction.xid_state); } - if (locked_tables) - { - lock=locked_tables; locked_tables=0; - close_thread_tables(this); - } + unlock_locked_tables(this); #if defined(ENABLED_DEBUG_SYNC) /* End the Debug Sync Facility. See debug_sync.cc. */ @@ -1742,7 +1738,7 @@ bool select_send::send_eof() ha_release_temporary_latches(thd); /* Unlock tables before sending packet to gain some speed */ - if (thd->lock) + if (thd->lock && ! thd->locked_tables_mode) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; @@ -3036,8 +3032,8 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) */ DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 && handler_tables == 0 && derived_tables == 0 && - lock == 0 && locked_tables == 0 && - prelocked_mode == NON_PRELOCKED && + lock == 0 && + locked_tables_mode == LTM_NONE && m_reprepare_observer == NULL); mdl_context_destroy(&mdl_context); mdl_context_destroy(&handler_mdl_context); @@ -3912,7 +3908,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, If we are in prelocked mode, the flushing will be done inside the top-most close_thread_tables(). */ - if (this->prelocked_mode == NON_PRELOCKED) + if (this->locked_tables_mode <= LTM_LOCK_TABLES) if (int error= binlog_flush_pending_rows_event(TRUE)) DBUG_RETURN(error); diff --git a/sql/sql_class.h b/sql/sql_class.h index 0f7d9d9a8d5..0fa3231e458 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -862,12 +862,17 @@ typedef I_List Item_change_list; /** - Type of prelocked mode. - See comment for THD::prelocked_mode for complete description. + Type of locked tables mode. + See comment for THD::locked_tables_mode for complete description. */ -enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1, - PRELOCKED_UNDER_LOCK_TABLES= 2}; +enum enum_locked_tables_mode +{ + LTM_NONE= 0, + LTM_LOCK_TABLES, + LTM_PRELOCKED, + LTM_PRELOCKED_UNDER_LOCK_TABLES +}; /** @@ -920,19 +925,13 @@ public: statement ends. Manual mode comes into play when a user issues a 'LOCK TABLES' statement. In this mode the user can only use the locked tables. - Trying to use any other tables will give an error. The locked tables are - stored in 'locked_tables' member. Manual locking is described in + Trying to use any other tables will give an error. + The locked tables are also stored in this member, however, + thd->locked_tables_mode is turned on. Manual locking is described in the 'LOCK_TABLES' chapter of the MySQL manual. See also lock_tables() for details. */ MYSQL_LOCK *lock; - /* - Tables that were locked with explicit or implicit LOCK TABLES. - (Implicit LOCK TABLES happens when we are prelocking tables for - execution of statement which uses stored routines. See description - THD::prelocked_mode for more info.) - */ - MYSQL_LOCK *locked_tables; /* CREATE-SELECT keeps an extra lock for the table being @@ -942,29 +941,34 @@ public: MYSQL_LOCK *extra_lock; /* - prelocked_mode_type enum and prelocked_mode member are used for - indicating whenever "prelocked mode" is on, and what type of - "prelocked mode" is it. + Enum enum_locked_tables_mode and locked_tables_mode member are + used to indicate whether the so-called "locked tables mode" is on, + and what kind of mode is active. - Prelocked mode is used for execution of queries which explicitly - or implicitly (via views or triggers) use functions, thus may need - some additional tables (mentioned in query table list) for their - execution. + Locked tables mode is used when it's necessary to open and + lock many tables at once, for usage across multiple + (sub-)statements. + This may be necessary either for queries that use stored functions + and triggers, in which case the statements inside functions and + triggers may be executed many times, or for implementation of + LOCK TABLES, in which case the opened tables are reused by all + subsequent statements until a call to UNLOCK TABLES. - First open_tables() call for such query will analyse all functions - used by it and add all additional tables to table its list. It will - also mark this query as requiring prelocking. After that lock_tables() - will issue implicit LOCK TABLES for the whole table list and change - thd::prelocked_mode to non-0. All queries called in functions invoked - by the main query will use prelocked tables. Non-0 prelocked_mode - will also surpress mentioned analysys in those queries thus saving - cycles. Prelocked mode will be turned off once close_thread_tables() - for the main query will be called. - - Note: Since not all "tables" present in table list are really locked - thd::prelocked_mode does not imply thd::locked_tables. + The kind of locked tables mode employed for stored functions and + triggers is also called "prelocked mode". + In this mode, first open_tables() call to open the tables used + in a statement analyses all functions used by the statement + and adds all indirectly used tables to the list of tables to + open and lock. + It also marks the parse tree of the statement as requiring + prelocking. After that, lock_tables() locks the entire list + of tables and changes THD::locked_tables_modeto LTM_PRELOCKED. + All statements executed inside functions or triggers + use the prelocked tables, instead of opening their own ones. + Prelocked mode is turned off automatically once close_thread_tables() + of the main statement is called. */ - prelocked_mode_type prelocked_mode; + enum enum_locked_tables_mode locked_tables_mode; ulong version; uint current_tablenr; @@ -996,8 +1000,8 @@ public: void reset_open_tables_state(THD *thd) { open_tables= temporary_tables= handler_tables= derived_tables= 0; - extra_lock= lock= locked_tables= 0; - prelocked_mode= NON_PRELOCKED; + extra_lock= lock= 0; + locked_tables_mode= LTM_NONE; state_flags= 0U; m_reprepare_observer= NULL; mdl_context_init(&mdl_context, thd); diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index ffc3fafe55f..098f049704c 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -289,7 +289,6 @@ Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg) Save THD state into cursor. @todo - - XXX: thd->locked_tables is not changed. - What problems can we have with it if cursor is open? - TODO: must be fixed because of the prelocked mode. */ @@ -342,7 +341,6 @@ Sensitive_cursor::post_open(THD *thd) } } /* - XXX: thd->locked_tables is not changed. What problems can we have with it if cursor is open? TODO: must be fixed because of the prelocked mode. */ diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 75ad46f1440..702e1e4f31a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -441,7 +441,7 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type, */ if (specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE) || thd->variables.max_insert_delayed_threads == 0 || - thd->prelocked_mode || + thd->locked_tables_mode > LTM_LOCK_TABLES || thd->lex->uses_stored_routines()) { *lock_type= TL_WRITE; @@ -611,7 +611,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, never be able to get a lock on the table. QQQ: why not upgrade the lock here instead? */ - if (table_list->lock_type == TL_WRITE_DELAYED && thd->locked_tables && + if (table_list->lock_type == TL_WRITE_DELAYED && + thd->locked_tables_mode && find_locked_table(thd->open_tables, table_list->db, table_list->table_name)) { @@ -741,7 +742,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, { if (duplic != DUP_ERROR || ignore) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - if (!thd->prelocked_mode) + /** + This is a simple check for the case when the table has a trigger + that reads from it, or when the statement invokes a stored function + that reads from the table being inserted to. + Engines can't handle a bulk insert in parallel with a read form the + same table in the same connection. + */ + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) table->file->ha_start_bulk_insert(values_list.elements); } @@ -856,7 +864,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, auto_inc values from the delayed_insert thread as they share TABLE. */ table->file->ha_release_auto_increment(); - if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + table->file->ha_end_bulk_insert() && !error) { table->file->print_error(my_errno,MYF(0)); error=1; @@ -3076,7 +3085,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) lex->current_select->join->select_options|= OPTION_BUFFER_RESULT; } else if (!(lex->current_select->options & OPTION_BUFFER_RESULT) && - !thd->prelocked_mode) + thd->locked_tables_mode <= LTM_LOCK_TABLES) { /* We must not yet prepare the result table if it is the same as one of the @@ -3142,7 +3151,7 @@ int select_insert::prepare2(void) { DBUG_ENTER("select_insert::prepare2"); if (thd->lex->current_select->options & OPTION_BUFFER_RESULT && - !thd->prelocked_mode) + thd->locked_tables_mode <= LTM_LOCK_TABLES) table->file->ha_start_bulk_insert((ha_rows) 0); DBUG_RETURN(0); } @@ -3269,7 +3278,8 @@ bool select_insert::send_eof() DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", trans_table, table->file->table_type())); - error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert():0; + error= (thd->locked_tables_mode <= LTM_LOCK_TABLES ? + table->file->ha_end_bulk_insert() : 0); table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); @@ -3349,7 +3359,7 @@ void select_insert::abort() { If we are not in prelocked mode, we end the bulk insert started before. */ - if (!thd->prelocked_mode) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) table->file->ha_end_bulk_insert(); /* @@ -3730,7 +3740,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); if (info.handle_duplicates == DUP_UPDATE) table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE); - if (!thd->prelocked_mode) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) table->file->ha_start_bulk_insert((ha_rows) 0); thd->abort_on_warning= (!info.ignore && (thd->variables.sql_mode & diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 34e899fc536..ef4382c9b4d 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -448,7 +448,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, (!table->triggers || !table->triggers->has_delete_triggers())) table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); - if (!thd->prelocked_mode) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) table->file->ha_start_bulk_insert((ha_rows) 0); table->copy_blobs=1; @@ -469,7 +469,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error= read_sep_field(thd, info, table_list, fields_vars, set_fields, set_values, read_info, *enclosed, skip_lines, ignore); - if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + table->file->ha_end_bulk_insert() && !error) { table->file->print_error(my_errno, MYF(0)); error= 1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 31bd34531e3..95f142bc8a2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -163,7 +163,7 @@ bool end_active_trans(THD *thd) { DBUG_PRINT("info",("options: 0x%llx", thd->options)); /* Safety if one did "drop table" on locked tables */ - if (!thd->locked_tables) + if (!thd->locked_tables_mode) thd->options&= ~OPTION_TABLE_LOCK; thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_commit(thd)) @@ -2211,7 +2211,7 @@ mysql_execute_command(THD *thd) if (res) break; - if (!thd->locked_tables && lex->protect_against_global_read_lock && + if (!thd->locked_tables_mode && lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) break; @@ -2553,7 +2553,7 @@ case SQLCOM_PREPARE: TABLE in the same way. That way we avoid that a new table is created during a gobal read lock. */ - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) { res= 1; @@ -2779,7 +2779,8 @@ end_with_restore_list: To prevent that, refuse SLAVE STOP if the client thread has locked tables */ - if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock) + if (thd->locked_tables_mode || + thd->active_transaction() || thd->global_read_lock) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); @@ -2855,7 +2856,7 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) { res= 1; @@ -3089,7 +3090,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (update_precheck(thd, all_tables)) break; - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) goto error; DBUG_ASSERT(select_lex->offset_limit == 0); @@ -3126,7 +3127,7 @@ end_with_restore_list: Protection might have already been risen if its a fall through from the SQLCOM_UPDATE case above. */ - if (!thd->locked_tables && + if (!thd->locked_tables_mode && lex->sql_command == SQLCOM_UPDATE_MULTI && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) goto error; @@ -3230,7 +3231,7 @@ end_with_restore_list: if ((res= insert_precheck(thd, all_tables))) break; - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) { res= 1; @@ -3270,7 +3271,7 @@ end_with_restore_list: unit->set_limit(select_lex); - if (! thd->locked_tables && + if (! thd->locked_tables_mode && ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1))) { res= 1; @@ -3340,7 +3341,7 @@ end_with_restore_list: Don't allow this within a transaction because we want to use re-generate table */ - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); @@ -3358,7 +3359,7 @@ end_with_restore_list: DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) { res= 1; @@ -3379,7 +3380,7 @@ end_with_restore_list: (TABLE_LIST *)thd->lex->auxiliary_table_list.first; multi_delete *del_result; - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) { res= 1; @@ -3526,7 +3527,7 @@ end_with_restore_list: if (check_one_table_access(thd, privilege, all_tables)) goto error; - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) goto error; @@ -3615,8 +3616,7 @@ end_with_restore_list: if (thd->variables.query_cache_wlock_invalidate) query_cache.invalidate_locked_for_write(first_table); #endif /*HAVE_QUERY_CACHE*/ - thd->locked_tables=thd->lock; - thd->lock=0; + thd->locked_tables_mode= LTM_LOCK_TABLES; my_ok(thd); } else @@ -3707,7 +3707,7 @@ end_with_restore_list: if (check_access(thd,DROP_ACL,lex->name.str,0,1,0, is_schema_db(lex->name.str))) break; - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); @@ -3746,7 +3746,7 @@ end_with_restore_list: res= 1; break; } - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { res= 1; my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, @@ -3786,7 +3786,7 @@ end_with_restore_list: #endif if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str))) break; - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); @@ -4776,7 +4776,7 @@ create_sp_error: xa_state_names[thd->transaction.xid_state.xa_state]); break; } - if (thd->active_transaction() || thd->locked_tables) + if (thd->locked_tables_mode || thd->active_transaction()) { my_error(ER_XAER_OUTSIDE, MYF(0)); break; @@ -7078,7 +7078,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if we have a write locked table as this would lead to a deadlock when trying to reopen (and re-lock) the table after the flush. */ - if (thd->locked_tables) + if (thd->locked_tables_mode) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); return 1; @@ -7103,7 +7103,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } else { - if (thd && thd->locked_tables) + if (thd && thd->locked_tables_mode) { /* If we are under LOCK TABLES we should have a write diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index edc9055fb39..75ab9a73a5b 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6216,7 +6216,7 @@ static void release_log_entries(partition_info *part_info) static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) { int err; - if (lpt->thd->locked_tables) + if (lpt->thd->locked_tables_mode) { /* When we have the table locked, it is necessary to reopen the table diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 857cccde50f..fdf2f5ac155 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -44,7 +44,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) if the user is trying to to do this in a transcation context */ - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fc1a378c371..5320da45322 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1019,7 +1019,7 @@ JOIN::optimize() error= -1; DBUG_RETURN(1); } - if (const_tables && !thd->locked_tables && + if (const_tables && !thd->locked_tables_mode && !(select_options & SELECT_NO_UNLOCK)) mysql_unlock_some_tables(thd, all_tables, const_tables); if (!conds && outer_join) @@ -6930,7 +6930,7 @@ void JOIN::join_free() We are not using tables anymore Unlock all tables. We may be in an INSERT .... SELECT statement. */ - if (can_unlock && lock && thd->lock && + if (can_unlock && lock && thd->lock && ! thd->locked_tables_mode && !(select_options & SELECT_NO_UNLOCK) && !select_lex->subquery_in_having && (select_lex == (thd->lex->unit.fake_select_lex ? diff --git a/sql/sql_table.cc b/sql/sql_table.cc index cac4bab27b7..dd8bd85aab6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1786,7 +1786,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, if (!drop_temporary) { - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) DBUG_RETURN(TRUE); } @@ -1891,7 +1891,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, if (!drop_temporary) { - if (!thd->locked_tables) + if (!thd->locked_tables_mode) { if (lock_table_names(thd, tables)) DBUG_RETURN(1); @@ -1900,7 +1900,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, expel_table_from_cache(0, table->db, table->table_name); pthread_mutex_unlock(&LOCK_open); } - else if (thd->locked_tables) + else { for (table= tables; table; table= table->next_local) if (find_temporary_table(thd, table->db, table->table_name)) @@ -2001,7 +2001,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table_type= table->db_type; if (!drop_temporary) { - if (thd->locked_tables) + if (thd->locked_tables_mode) { if (close_cached_table(thd, table->table)) { @@ -2192,8 +2192,8 @@ err_with_placeholders: locked. Additional check for 'non_temp_tables_count' is to avoid leaving LOCK TABLES mode if we have dropped only temporary tables. */ - if (thd->locked_tables && thd->locked_tables->table_count == 0 && - non_temp_tables_count > 0) + if (thd->locked_tables_mode && + thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) { unlock_locked_tables(thd); goto end; @@ -6571,7 +6571,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if the user is trying to to do this in a transcation context */ - if (thd->locked_tables || thd->active_transaction()) + if (thd->locked_tables_mode || thd->active_transaction()) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); @@ -6620,7 +6620,7 @@ view_err: set of tables from the old table or to open a new TABLE object for an extended list and verify that they belong to locked tables. */ - if (thd->locked_tables && + if (thd->locked_tables_mode && (create_info->used_fields & HA_CREATE_USED_UNION) && (table->s->tmp_table == NO_TMP_TABLE)) { @@ -6847,7 +6847,7 @@ view_err: table_list->table= NULL; // For query cache query_cache_invalidate3(thd, table_list, 0); - if (thd->locked_tables) + if (thd->locked_tables_mode) { /* Under LOCK TABLES we should adjust meta-data locks before finishing @@ -7330,7 +7330,7 @@ view_err: if (table->s->tmp_table != NO_TMP_TABLE) { /* Close lock if this is a transactional table */ - if (thd->lock) + if (thd->lock && ! thd->locked_tables_mode) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; @@ -7467,7 +7467,7 @@ view_err: if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG, create_info)) goto err_with_placeholders; - if (thd->locked_tables) + if (thd->locked_tables_mode) { if (new_name == table_name && new_db == db) { @@ -7489,7 +7489,7 @@ view_err: (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); - if (thd->locked_tables && new_name == table_name && new_db == db) + if (thd->locked_tables_mode && new_name == table_name && new_db == db) { thd->in_lock_tables= 1; error= reopen_tables(thd, 1); @@ -7536,7 +7536,7 @@ view_err: table_list->table=0; // For query cache query_cache_invalidate3(thd, table_list, 0); - if (thd->locked_tables) + if (thd->locked_tables_mode) { if ((new_name != table_name || new_db != db)) { diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 61cd9bffa57..9d47b637886 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -383,7 +383,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) LOCK_open is not enough because global read lock is held without holding LOCK_open). */ - if (!thd->locked_tables && + if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) DBUG_RETURN(TRUE); @@ -444,7 +444,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* Keep consistent with respect to other DDL statements */ mysql_ha_rm_tables(thd, tables); - if (thd->locked_tables) + if (thd->locked_tables_mode) { /* Under LOCK TABLES we must only accept write locked tables. */ if (!(tables->table= find_write_locked_table(thd->open_tables, tables->db, @@ -493,7 +493,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) table->triggers->drop_trigger(thd, tables, &stmt_query)); /* Under LOCK TABLES we must reopen the table to activate the trigger. */ - if (!result && thd->locked_tables) + if (!result && thd->locked_tables_mode) { /* Make table suitable for reopening */ close_data_files_and_leave_as_placeholders(thd, tables->db, @@ -527,7 +527,7 @@ end: locks. Otherwise call to close_thread_tables() will take care about both TABLE instance created by reopen_name_locked_table() and metadata lock. */ - if (thd->locked_tables && tables && tables->table) + if (thd->locked_tables_mode && tables && tables->table) mdl_downgrade_exclusive_lock(&thd->mdl_context, tables->table->mdl_lock_data); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 5cf6ceaa394..7e033bc963a 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -978,7 +978,7 @@ int mysql_multi_update_prepare(THD *thd) count in open_tables() */ uint table_count= lex->table_count; - const bool using_lock_tables= thd->locked_tables != 0; + const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI); bool need_reopen= FALSE; DBUG_ENTER("mysql_multi_update_prepare"); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 9382880ba1b..b370fc79b17 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -384,7 +384,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, alteration of views under LOCK TABLES. */ - if (thd->locked_tables) + if (thd->locked_tables_mode) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); res= TRUE; @@ -1583,7 +1583,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) TABLES we have to simply prohibit dropping of views. */ - if (thd->locked_tables) + if (thd->locked_tables_mode) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); DBUG_RETURN(TRUE); diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 9269b331754..28d11e3d5f8 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1109,7 +1109,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool do_optimize) ha_release_temporary_latches(thd); // Don't lock tables if we have used LOCK TABLE - if (!thd->locked_tables && + if (! thd->locked_tables_mode && mi_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK)) { mi_check_print_error(¶m,ER(ER_CANT_LOCK),my_errno); @@ -1219,7 +1219,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool do_optimize) update_state_info(¶m, file, 0); } thd_proc_info(thd, old_proc_info); - if (!thd->locked_tables) + if (! thd->locked_tables_mode) mi_lock_database(file,F_UNLCK); DBUG_RETURN(error ? HA_ADMIN_FAILED : !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK); From 77be8ba5e000e3d5b1cbc946aa57d08cc7343157 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 17:58:31 +0300 Subject: [PATCH 062/466] Backport of: ------------------------------------------------------------ revno: 2630.4.27 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Mon 2008-06-09 14:01:19 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Changed open_table() to return bool. This allows more easily to distinguish cases when this function succeeds but returns no TABLE instance (in case of view or in case of special kind of open) from cases when we have an error. Pointer to TABLE instance is now always returned in TABLE_LIST::table member. This change allows to get rid of false assumption in open_tables() implementation and makes it more clear. --- sql/mysql_priv.h | 4 +- sql/sql_base.cc | 254 ++++++++++++++++++++++------------------------ sql/sql_insert.cc | 6 +- sql/sql_table.cc | 5 +- 4 files changed, 131 insertions(+), 138 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 94875210d0b..2d043d44afa 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1220,8 +1220,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, uint lock_flags); enum enum_open_table_action {OT_NO_ACTION= 0, OT_BACK_OFF_AND_RETRY, OT_DISCOVER, OT_REPAIR}; -TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, - enum_open_table_action *action, uint flags); +bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, + enum_open_table_action *action, uint flags); bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 09bb05a91c1..e21a7aa31e5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2574,8 +2574,6 @@ void table_share_release_hook(void *share) which will be set according to action which is required to remedy problem appeared during attempt to open table. - If this is a NULL pointer, then the table is not - put in the thread-open-list. flags Bitmap of flags to modify how open works: MYSQL_LOCK_IGNORE_FLUSH - Open table even if someone has done a flush or there is a pending @@ -2597,14 +2595,16 @@ void table_share_release_hook(void *share) "open_type" is TAKE_EXCLUSIVE_MDL. RETURN - NULL Open failed. If refresh is set then one should close - all other tables and retry the open. - # Success. Pointer to TABLE object for open table. + TRUE Open failed. "action" parameter may contain type of action + needed to remedy problem before retrying again. + FALSE Success. Members of TABLE_LIST structure are filled properly (e.g. + TABLE_LIST::table is set for real tables and TABLE_LIST::view is + set for views). */ -TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, - enum_open_table_action *action, uint flags) +bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, + enum_open_table_action *action, uint flags) { reg1 TABLE *table; char key[MAX_DBKEY_LENGTH]; @@ -2622,10 +2622,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, /* an open table operation needs a lot of the stack space */ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias)) - DBUG_RETURN(0); + DBUG_RETURN(TRUE); if (thd->killed) - DBUG_RETURN(0); + DBUG_RETURN(TRUE); key_length= (create_table_def_key(thd, key, table_list, 1) - TMP_TABLE_KEY_EXTRA); @@ -2659,7 +2659,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, (ulong) table->query_id, (uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id)); my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } table->query_id= thd->query_id; thd->thread_specific_used= TRUE; @@ -2672,7 +2672,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (flags & MYSQL_OPEN_TEMPORARY_ONLY) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } /* @@ -2770,7 +2770,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, mem_root, 0)) { DBUG_ASSERT(table_list->view != 0); - DBUG_RETURN(0); // VIEW + DBUG_RETURN(FALSE); // VIEW } } } @@ -2785,7 +2785,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); else my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } /* @@ -2809,7 +2809,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, */ mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } else { @@ -2832,7 +2832,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { if (retry) *action= OT_BACK_OFF_AND_RETRY; - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } } @@ -2854,7 +2854,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, /* Someone did a refresh while thread was opening tables */ *action= OT_BACK_OFF_AND_RETRY; pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) @@ -2867,14 +2867,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (!exists) { pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } /* Table exists. Let us try to open it. */ } else if (table_list->open_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL) { pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_data))) @@ -2921,28 +2921,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); - } - else if (table_list->view) - { - /* - We're trying to open a table for what was a view. - This can only happen during (re-)execution. - At prepared statement prepare the view has been opened and - merged into the statement parse tree. After that, someone - performed a DDL and replaced the view with a base table. - Don't try to open the table inside a prepared statement, - invalidate it instead. - - Note, the assert below is known to fail inside stored - procedures (Bug#27011). - */ - DBUG_ASSERT(thd->m_reprepare_observer); - check_and_update_table_version(thd, table_list, share); - /* Always an error. */ - DBUG_ASSERT(thd->is_error()); - goto err_unlock; + DBUG_RETURN(FALSE); } + /* + Note that situation when we are trying to open a table for what + was a view during previous execution of PS will be handled in by + the caller. Here we should simply open our table even if + TABLE_LIST::view is true. + */ if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) goto err_unlock; @@ -2992,7 +2978,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, *action= OT_BACK_OFF_AND_RETRY; release_table_share(share); pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } /* Force close at once after usage */ thd->version= share->version; @@ -3103,15 +3089,16 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->pos_in_table_list= table_list; table_list->updatable= 1; // It is not derived table nor non-updatable VIEW table->clear_column_bitmaps(); + table_list->table= table; DBUG_ASSERT(table->key_read == 0); - DBUG_RETURN(table); + DBUG_RETURN(FALSE); err_unlock: release_table_share(share); err_unlock2: pthread_mutex_unlock(&LOCK_open); mdl_release_lock(&thd->mdl_context, mdl_lock_data); - DBUG_RETURN(0); + DBUG_RETURN(TRUE); } @@ -4502,6 +4489,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) TABLE_LIST *tables= NULL; enum_open_table_action action; int result=0; + bool error; MEM_ROOT new_frm_mem; /* Also used for indicating that prelocking is need */ TABLE_LIST **query_tables_last_own; @@ -4620,64 +4608,30 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) tables->db, tables->table_name, (long) tables)); (*counter)++; - /* - Not a placeholder: must be a base table or a view, and the table is - not opened yet. Try to open the table. - */ - if (!tables->table) + /* Not a placeholder: must be a base table or a view. Let us open it. */ + DBUG_ASSERT(!tables->table); + + if (tables->prelocking_placeholder) { - if (tables->prelocking_placeholder) - { - /* - For the tables added by the pre-locking code, attempt to open - the table but fail silently if the table does not exist. - The real failure will occur when/if a statement attempts to use - that table. - */ - Prelock_error_handler prelock_handler; - thd->push_internal_handler(& prelock_handler); - tables->table= open_table(thd, tables, &new_frm_mem, &action, flags); - thd->pop_internal_handler(); - safe_to_ignore_table= prelock_handler.safely_trapped_errors(); - } - else - tables->table= open_table(thd, tables, &new_frm_mem, &action, flags); + /* + For the tables added by the pre-locking code, attempt to open + the table but fail silently if the table does not exist. + The real failure will occur when/if a statement attempts to use + that table. + */ + Prelock_error_handler prelock_handler; + thd->push_internal_handler(& prelock_handler); + error= open_table(thd, tables, &new_frm_mem, &action, flags); + thd->pop_internal_handler(); + safe_to_ignore_table= prelock_handler.safely_trapped_errors(); } else - DBUG_PRINT("tcache", ("referenced table: '%s'.'%s' 0x%lx", - tables->db, tables->table_name, - (long) tables->table)); + error= open_table(thd, tables, &new_frm_mem, &action, flags); - if (!tables->table) + free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); + + if (error) { - free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); - - if (tables->view) - { - /* VIEW placeholder */ - (*counter)--; - - /* - tables->next_global list consists of two parts: - 1) Query tables and underlying tables of views. - 2) Tables used by all stored routines that this statement invokes on - execution. - We need to know where the bound between these two parts is. If we've - just opened a view, which was the last table in part #1, and it - has added its base tables after itself, adjust the boundary pointer - accordingly. - */ - if (query_tables_last_own == &(tables->next_global) && - tables->view->query_tables) - query_tables_last_own= tables->view->query_tables_last; - /* - Let us free memory used by 'sroutines' hash here since we never - call destructor for this LEX. - */ - my_hash_free(&tables->view->sroutines); - goto process_view_routines; - } - /* If in a MERGE table open, we need to remove the children list from statement table list before restarting. Otherwise the list @@ -4691,15 +4645,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) parent_l->next_global= *parent_l->table->child_last_l; } - /* - FIXME This is a temporary hack. Actually we need check that will - allow us to differentiate between error while opening/creating - table and successful table creation. - ... - */ - if (tables->open_type) - continue; - if (action) { /* @@ -4742,36 +4687,73 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) result= -1; // Fatal error break; } - else + + /* + We can't rely on simple check for TABLE_LIST::view to determine + that this is a view since during re-execution we might reopen + ordinary table in place of view and thus have TABLE_LIST::view + set from repvious execution and TABLE_LIST::table set from + current. + */ + if (!tables->table && tables->view) { + /* VIEW placeholder */ + (*counter)--; + /* - If we are not already in prelocked mode and extended table list is not - yet built and we have trigger for table being opened then we should - cache all routines used by its triggers and add their tables to - prelocking list. - If we lock table for reading we won't update it so there is no need to - process its triggers since they never will be activated. + tables->next_global list consists of two parts: + 1) Query tables and underlying tables of views. + 2) Tables used by all stored routines that this statement invokes on + execution. + We need to know where the bound between these two parts is. If we've + just opened a view, which was the last table in part #1, and it + has added its base tables after itself, adjust the boundary pointer + accordingly. */ - if (thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking() && - tables->trg_event_map && tables->table->triggers && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) + if (query_tables_last_own == &(tables->next_global) && + tables->view->query_tables) + query_tables_last_own= tables->view->query_tables_last; + /* + Let us free memory used by 'sroutines' hash here since we never + call destructor for this LEX. + */ + my_hash_free(&tables->view->sroutines); + goto process_view_routines; + } + + /* + Special types of open can succeed but still don't set + TABLE_LIST::table to anything. + */ + if (tables->open_type && !tables->table) + continue; + + /* + If we are not already in prelocked mode and extended table list is not + yet built and we have trigger for table being opened then we should + cache all routines used by its triggers and add their tables to + prelocking list. + If we lock table for reading we won't update it so there is no need to + process its triggers since they never will be activated. + */ + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + !thd->lex->requires_prelocking() && + tables->trg_event_map && tables->table->triggers && + tables->lock_type >= TL_WRITE_ALLOW_WRITE) + { + if (!query_tables_last_own) + query_tables_last_own= thd->lex->query_tables_last; + if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, + tables)) { - if (!query_tables_last_own) - query_tables_last_own= thd->lex->query_tables_last; - if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, - tables)) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - result= -1; - goto err; - } + /* + Serious error during reading stored routines from mysql.proc table. + Something's wrong with the table or its contents, and an error has + been emitted; we must abort. + */ + result= -1; + goto err; } - free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); } if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) @@ -4985,6 +4967,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, TABLE *table; enum_open_table_action action; bool refresh; + bool error; DBUG_ENTER("open_ltable"); /* should not be used in a prelocked_mode context, see NOTE above */ @@ -4996,7 +4979,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, table_list->required_type= FRMTYPE_TABLE; retry: - while (!(table= open_table(thd, table_list, thd->mem_root, &action, 0)) && + while ((error= open_table(thd, table_list, thd->mem_root, &action, 0)) && action) { /* @@ -5009,8 +4992,14 @@ retry: break; } - if (table) + if (!error) { + /* + We can't have a view or some special "open_type" in this function + so there should be a TABLE instance. + */ + DBUG_ASSERT(table_list->table); + table= table_list->table; if (table->child_l) { /* A MERGE table must not come here. */ @@ -5023,7 +5012,6 @@ retry: } table_list->lock_type= lock_type; - table_list->table= table; table->grant= table_list->grant; if (thd->locked_tables_mode) { @@ -5047,6 +5035,8 @@ retry: } } } + else + table= 0; end: thd_proc_info(thd, 0); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 702e1e4f31a..00bb013218d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3555,8 +3555,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } else { - if (!(table= open_table(thd, create_table, thd->mem_root, ¬_used2, - MYSQL_OPEN_TEMPORARY_ONLY)) && + if (open_table(thd, create_table, thd->mem_root, ¬_used2, + MYSQL_OPEN_TEMPORARY_ONLY) && !create_info->table_existed) { /* @@ -3566,6 +3566,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, */ drop_temporary_table(thd, create_table); } + else + table= create_table->table; } } reenable_binlog(thd); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index dd8bd85aab6..dcfc9b811b6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7182,8 +7182,9 @@ view_err: tbl.db= new_db; tbl.table_name= tbl.alias= tmp_name; /* Table is in thd->temporary_tables */ - new_table= open_table(thd, &tbl, thd->mem_root, ¬_used, - MYSQL_LOCK_IGNORE_FLUSH); + (void) open_table(thd, &tbl, thd->mem_root, ¬_used, + MYSQL_LOCK_IGNORE_FLUSH); + new_table= tbl.table; } else { From 78460958311d6e0cbcaf91af956891dbf6478fa4 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 18:04:32 +0300 Subject: [PATCH 063/466] Backport of: ------------------------------------------------------------ revno: 2630.9.1 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Sun 2008-06-08 22:13:58 +0400 message: WL#3726 "DDL locking for all metadata objects" After review fixes in progress. Some adjustments to the patch that removes thd->locked_tables --- sql/sp_head.cc | 6 +++--- sql/sql_base.cc | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a0832285742..d905ddcda31 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1257,7 +1257,7 @@ sp_head::execute(THD *thd) Will write this SP statement into binlog separately (TODO: consider changing the condition to "not inside event union") */ - if (thd->locked_tables_mode == LTM_NONE) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) thd->user_var_events_alloc= thd->mem_root; err_status= i->execute(thd, &ip); @@ -1269,7 +1269,7 @@ sp_head::execute(THD *thd) If we've set thd->user_var_events_alloc to mem_root of this SP statement, clean all the events allocated in it. */ - if (thd->locked_tables_mode == LTM_NONE) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) { reset_dynamic(&thd->user_var_events); thd->user_var_events_alloc= NULL;//DEBUG @@ -2740,7 +2740,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, thd->query_id= next_query_id(); pthread_mutex_unlock(&LOCK_thread_count); - if (thd->locked_tables_mode == LTM_NONE) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) { /* This statement will enter/leave prelocked mode on its own. diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e21a7aa31e5..8665cebc4ed 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1375,6 +1375,7 @@ void close_thread_tables(THD *thd, bool skip_mdl) { TABLE *table; + bool clear_table_lock_option= FALSE; DBUG_ENTER("close_thread_tables"); #ifdef EXTRA_DEBUG @@ -1446,6 +1447,11 @@ void close_thread_tables(THD *thd, /* We are under simple LOCK TABLES or we're inside a sub-statement of a prelocked statement, so should not do anything else. + + Note that even if we are in LTM_LOCK_TABLES mode and statement + requires prelocking (e.g. when we are closing tables after + failing ot "open" all tables required for statement execution) + we will exit this function a few lines below. */ if (! thd->lex->requires_prelocking()) DBUG_VOID_RETURN; @@ -1462,7 +1468,7 @@ void close_thread_tables(THD *thd, DBUG_VOID_RETURN; thd->locked_tables_mode= LTM_NONE; - thd->options&= ~OPTION_TABLE_LOCK; + clear_table_lock_option= TRUE; /* Note that we are leaving prelocked mode so we don't need @@ -1502,6 +1508,9 @@ void close_thread_tables(THD *thd, mdl_remove_all_locks(&thd->mdl_context); } + if (clear_table_lock_option) + thd->options&= ~OPTION_TABLE_LOCK; + DBUG_VOID_RETURN; } @@ -5452,6 +5461,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, need to care about THD::locked_tables_root here. */ mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; thd->options&= ~(OPTION_TABLE_LOCK); DBUG_RETURN(-1); } From b474ad262bca9b53b7267bcf4f86c8a963c1d9d8 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 18:20:43 +0300 Subject: [PATCH 064/466] Backport of: ------------------------------------------------------------ revno: 2630.9.2 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Tue 2008-06-10 18:01:56 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. --- sql/mdl.cc | 8 ++++++-- sql/mdl.h | 3 ++- sql/sql_base.cc | 23 ++++++++++++++++------- sql/sql_show.cc | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index dbf08101159..d3d067cfb9b 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -611,6 +611,7 @@ static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) This function must be called after the lock is added to a context. + @param context [in] Context containing request for lock @param lock_data [in] Lock request object for lock to be acquired @param retry [out] Indicates that conflicting lock exists and another attempt should be made after releasing all current @@ -622,16 +623,19 @@ static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) In the latter case "retry" parameter is set to TRUE. */ -bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, + bool *retry) { MDL_LOCK *lock; *retry= FALSE; DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_PENDING); + DBUG_ASSERT(lock_data->ctx == context); + safe_mutex_assert_not_owner(&LOCK_open); - if (lock_data->ctx->has_global_shared_lock && + if (context->has_global_shared_lock && lock_data->type == MDL_SHARED_UPGRADABLE) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); diff --git a/sql/mdl.h b/sql/mdl.h index f99f38d6285..b192980ebaa 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -164,7 +164,8 @@ inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) lock_data->type= lock_type; } -bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry); +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, + bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8665cebc4ed..633184cde79 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2837,7 +2837,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (flags & MYSQL_LOCK_IGNORE_FLUSH) mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); - if (mdl_acquire_shared_lock(mdl_lock_data, &retry)) + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) { if (retry) *action= OT_BACK_OFF_AND_RETRY; @@ -4044,18 +4044,21 @@ end_with_lock_open: /** - Handle failed attempt ot open table by performing requested action. + Recover from failed attempt ot open table by performing requested action. @param thd Thread context @param table Table list element for table that caused problem @param action Type of action requested by failed open_table() call + @pre This function should be called only with "action" != OT_NO_ACTION. + @retval FALSE - Success. One should try to open tables once again. @retval TRUE - Error */ -static bool handle_failed_open_table_attempt(THD *thd, TABLE_LIST *table, - enum_open_table_action action) +static bool +recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, + enum_open_table_action action) { bool result= FALSE; @@ -4678,7 +4681,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) TABLE_LIST element. Altough currently this assumption is valid it may change in future. */ - if (handle_failed_open_table_attempt(thd, tables, action)) + if (recover_from_failed_open_table_attempt(thd, tables, action)) { result= -1; goto err; @@ -4997,7 +5000,7 @@ retry: might have been acquired successfully. */ close_thread_tables(thd, (action == OT_BACK_OFF_AND_RETRY)); - if (handle_failed_open_table_attempt(thd, table_list, action)) + if (recover_from_failed_open_table_attempt(thd, table_list, action)) break; } @@ -8530,7 +8533,13 @@ bool notify_thread_having_shared_lock(THD *thd, THD *in_use) thd_table ; thd_table= thd_table->next) { - /* TODO With new MDL check for db_stat is probably a legacy */ + /* + Check for TABLE::db_stat is needed since in some places we call + handler::close() for table instance (and set TABLE::db_stat to 0) + and do not remove such instances from the THD::open_tables + for some time, during which other thread can see those instances + (e.g. see partitioning code). + */ if (thd_table->db_stat) signalled|= mysql_lock_abort_for_thread(thd, thd_table); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 3d4c0a5aaf7..9df31c7c2ad 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3145,7 +3145,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, */ while (1) { - if (mdl_acquire_shared_lock(&mdl_lock_data, &retry)) + if (mdl_acquire_shared_lock(&thd->mdl_context, &mdl_lock_data, &retry)) { if (!retry || mdl_wait_for_locks(&thd->mdl_context)) { From f7269ba0c7726b94f1dcc1be06dedb8ea2357bb4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 1 Dec 2009 18:28:13 +0000 Subject: [PATCH 065/466] fix build errors --- cmake/dtrace.cmake | 8 +++++++- mysys/CMakeLists.txt | 6 +++--- plugin/semisync/CMakeLists.txt | 12 ++++++++---- sql/CMakeLists.txt | 6 +++--- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index c0f66e4c46c..58fbbba022c 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -86,7 +86,13 @@ MACRO (DTRACE_INSTRUMENT target) -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake WORKING_DIRECTORY ${objdir} ) - SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS "${outfile}") + GET_TARGET_PROPERTY(target_link_flags ${target} LINK_FLAGS) + IF(NOT target_link_flags) + SET(target_link_flags) + ENDIF() + + SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS + "${target_link_flags} ${outfile}") ENDIF() ENDIF() ENDMACRO() diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index b6170170be1..2264b99e75b 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -48,10 +48,10 @@ IF(CMAKE_COMPILER_IS_GNUCC AND NOT HAVE_CXX_NEW) ADD_DEFINITIONS( -DUSE_MYSYS_NEW) ENDIF() -IF(CMAKE_C_COMPILER MATCHES SunPro) +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND CMAKE_C_COMPILER_ID MATCHES "SunPro") # Inline assembly template for rdtsc - SET_SOURCE_FILE_PROPERTIES(my_rdtsc.c - PROPERTIES COMPILE_FLAGS ${CMAKE_CURRENT_SOURCE_DIR}/my_timer_cycles.il) + SET_SOURCE_FILES_PROPERTIES(my_rdtsc.c + PROPERTIES COMPILE_FLAGS "${CMAKE_CURRENT_SOURCE_DIR}/my_timer_cycles.il") ENDIF() IF(HAVE_LARGE_PAGES) diff --git a/plugin/semisync/CMakeLists.txt b/plugin/semisync/CMakeLists.txt index b852bce9463..0d48ad55382 100644 --- a/plugin/semisync/CMakeLists.txt +++ b/plugin/semisync/CMakeLists.txt @@ -15,11 +15,15 @@ -SET(SEMISYNC_MASTER_SOURCES semisync.cc semisync_master.cc semisync_master_plugin.cc +SET(SEMISYNC_MASTER_SOURCES + semisync.cc semisync_master.cc semisync_master_plugin.cc semisync.h semisync_master.h) -MYSQL_ADD_PLUGIN(semisync_master ${SEMISYNC_MASTER_SOURCES} MODULE_ONLY MODULE_OUTPUT_NAME "semisync_master") +MYSQL_ADD_PLUGIN(semisync_master ${SEMISYNC_MASTER_SOURCES} + MODULE_ONLY MODULE_OUTPUT_NAME "semisync_master") -SET(SEMISYNC_SLAVE_SOURCES semisync.cc semisync_slave.cc semisync_slave_plugin.cc semisync.h semisync_slave.h ) -MYSQL_ADD_PLUGIN(semisync_slave ${SEMISYNC_MASTER_SOURCES} MODULE_ONLY MODULE_OUTPUT_NAME "semisync_slave") +SET(SEMISYNC_SLAVE_SOURCES semisync.cc semisync_slave.cc + semisync_slave_plugin.cc semisync.h semisync_slave.h ) +MYSQL_ADD_PLUGIN(semisync_slave ${SEMISYNC_SLAVE_SOURCES} + MODULE_ONLY MODULE_OUTPUT_NAME "semisync_slave") diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 158f0cbc884..8e2fbc0cac3 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -96,8 +96,7 @@ IF(MSVC) OUTPUT mysqld_dummy.cc COMMAND cmake ARGS -E touch mysqld_dummy.cc VERBATIM) - SET(MYSQLD_CORE_LIBS ${MYSQLD_CORE_LIBS} sql) - + SET(SQL_LIB sql) ELSE() ADD_EXECUTABLE(mysqld ${SQL_SOURCE}) DTRACE_INSTRUMENT(mysqld) @@ -129,7 +128,8 @@ ENDIF() SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) -TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} sql mysys dbug strings vio regex ${MYSQLD_CORE_LIBS} +TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} + mysys dbug strings vio regex ${SQL_LIB} ${LIBWRAP_LIBRARY} ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) INSTALL(TARGETS mysqld DESTINATION bin) INSTALL_DEBUG_SYMBOLS(mysqld) From cf4a4ba6fdce6e37c0a97bcf9b6653456a5ff374 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 22:13:01 +0300 Subject: [PATCH 066/466] Backport of: ------------------------------------------------------------ revno: 2630.9.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Wed 2008-06-11 08:33:36 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Changed close_cached_tables() not to flush all unused TABLE instances when flushing individual table. Renamed expel_table_from_cache() to tdc_remove_table() and added enum parameter to be able more explicitly specify type of removal, rewrote its code to be more efficient. ****** Backport of: ------------------------------------------------------------ revno: 2630.9.4 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Wed 2008-06-11 15:53:53 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Minor changes in order to improve code readability and simplify debugging. --- mysql-test/r/ps_ddl.result | 2 +- mysql-test/t/ps_ddl.test | 2 +- sql/mysql_priv.h | 7 +- sql/sql_base.cc | 152 ++++++++++++++++++++----------------- sql/sql_delete.cc | 3 +- sql/sql_rename.cc | 3 +- sql/sql_show.cc | 79 ++++++++++++++----- sql/sql_table.cc | 4 +- sql/sql_trigger.cc | 2 +- 9 files changed, 155 insertions(+), 99 deletions(-) diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index af0ffaf4ae5..f411328ed7c 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -850,7 +850,7 @@ flush table t1; execute stmt; f1() 6 -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS execute stmt; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index 6e771d44b3f..1ba193983b2 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -745,7 +745,7 @@ execute stmt; call p_verify_reprepare_count(1); flush table t1; execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); execute stmt; --echo # Test 18-c: dependent VIEW has changed diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2d043d44afa..b650af8017d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1543,8 +1543,11 @@ char *generate_partition_syntax(partition_info *part_info, #endif bool notify_thread_having_shared_lock(THD *thd, THD *in_use); -void expel_table_from_cache(THD *leave_thd, const char *db, - const char *table_name); + +enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN, + TDC_RT_REMOVE_UNUSED}; +void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, + const char *db, const char *table_name); #define NORMAL_PART_NAME 0 #define TEMP_PART_NAME 1 diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 633184cde79..f7ac1df8b32 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -921,12 +921,17 @@ void free_io_cache(TABLE *table) particular table identified by its share. @param share Table share. + + @pre Caller should have LOCK_open mutex acquired. */ static void kill_delayed_threads_for_table(TABLE_SHARE *share) { I_P_List_iterator it(share->used_tables); TABLE *tab; + + safe_mutex_assert_owner(&LOCK_open); + while ((tab= it++)) { THD *in_use= tab->in_use; @@ -983,6 +988,15 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", refresh_version)); kill_delayed_threads(); + /* + Get rid of all unused TABLE and TABLE_SHARE instances. By doing + this we automatically close all tables which were marked as "old". + */ + while (unused_tables) + free_cache_entry(unused_tables); + /* Free table shares which were not freed implicitly by loop above. */ + while (oldest_unused_share->next) + (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share); } else { @@ -993,8 +1007,10 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, if (share) { - share->version= 0; kill_delayed_threads_for_table(share); + /* tdc_remove_table() also sets TABLE_SHARE::version to 0. */ + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db, + table->table_name); found=1; } } @@ -1002,28 +1018,14 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, wait_for_refresh=0; // Nothing to wait for } - /* - Get rid of all unused TABLE and TABLE_SHARE instances. By doing - this we automatically close all tables which were marked as "old". - - FIXME: Do not close all unused TABLE instances when flushing - particular table. - */ - while (unused_tables) - free_cache_entry(unused_tables); - /* Free table shares */ - while (oldest_unused_share->next) - (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share); + if (!have_lock) + pthread_mutex_unlock(&LOCK_open); if (!wait_for_refresh) - { - if (!have_lock) - pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(result); - } + /* Code below assume that LOCK_open is released. */ DBUG_ASSERT(!have_lock); - pthread_mutex_unlock(&LOCK_open); if (thd->locked_tables_mode) { @@ -2109,27 +2111,6 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, } - /* move table first in unused links */ - -static void relink_unused(TABLE *table) -{ - /* Assert that MERGE children are not attached in unused_tables. */ - DBUG_ASSERT(!table->is_children_attached()); - - if (table != unused_tables) - { - table->prev->next=table->next; /* Remove from unused list */ - table->next->prev=table->prev; - table->next=unused_tables; /* Link in unused tables */ - table->prev=unused_tables->prev; - unused_tables->prev->next=table; - unused_tables->prev=table; - unused_tables=table; - check_unused(); - } -} - - /** Prepare an open merge table for close. @@ -2241,7 +2222,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, } pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str); + tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, + table->s->db.str, table->s->table_name.str); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(FALSE); } @@ -4075,7 +4057,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, if (mdl_acquire_exclusive_locks(&thd->mdl_context)) return TRUE; pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(0, table->db, table->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); ha_create_table_from_engine(thd, table->db, table->table_name); pthread_mutex_unlock(&LOCK_open); @@ -4089,7 +4071,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, if (mdl_acquire_exclusive_locks(&thd->mdl_context)) return TRUE; pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(0, table->db, table->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); @@ -8549,50 +8531,82 @@ bool notify_thread_having_shared_lock(THD *thd, THD *in_use) /** - Remove all instances of the table from cache assuming that current thread - has exclusive meta-data lock on it (optionally leave instances belonging - to the current thread in cache). + Remove all or some (depending on parameter) instances of TABLE and + TABLE_SHARE from the table definition cache. - @param leave_thd 0 If we should remove all instances - non-0 Pointer to current thread context if we should - leave instances belonging to this thread. - @param db Name of database - @param table_name Name of table + @param thd Thread context + @param remove_type Type of removal: + TDC_RT_REMOVE_ALL - remove all TABLE instances and + TABLE_SHARE instance. There + should be no used TABLE objects + and caller should have exclusive + metadata lock on the table. + TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances + except those that belong to + this thread. There should be + no TABLE objects used by other + threads and caller should have + exclusive metadata lock on the + table. + TDC_RT_REMOVE_UNUSED - remove all unused TABLE + instances (if there are no + used instances will also + remove TABLE_SHARE). + @param db Name of database + @param table_name Name of table @note Unlike remove_table_from_cache() it assumes that table instances are already not used by any (other) thread (this should be achieved by using meta-data locks). */ -void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name) +void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, + const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; uint key_length; TABLE *table; TABLE_SHARE *share; + safe_mutex_assert_owner(&LOCK_open); + + DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED || + mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, + db, table_name)); + key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, key_length))) { - I_P_List_iterator it(share->free_tables); - share->version= 0; - - while ((table= it++)) - relink_unused(table); - } - - /* This may destroy share so we have to do new look-up later. */ - while (unused_tables && !unused_tables->s->version) - free_cache_entry(unused_tables); - - if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, - key_length))) - { - DBUG_ASSERT(leave_thd || share->ref_count == 0); - if (share->ref_count == 0) - my_hash_delete(&table_def_cache, (uchar*) share); + if (share->ref_count) + { + I_P_List_iterator it(share->free_tables); +#ifndef DBUG_OFF + if (remove_type == TDC_RT_REMOVE_ALL) + { + DBUG_ASSERT(share->used_tables.is_empty()); + } + else if (remove_type == TDC_RT_REMOVE_NOT_OWN) + { + I_P_List_iterator it2(share->used_tables); + while ((table= it2++)) + if (table->in_use != thd) + { + DBUG_ASSERT(0); + } + } +#endif + /* + Set share's version to zero in order to ensure that it gets + automatically deleted once it is no longer referenced. + */ + share->version= 0; + while ((table= it++)) + free_cache_entry(table); + } + else + (void) my_hash_delete(&table_def_cache, (uchar*) share); } } @@ -8793,7 +8807,7 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_RETURN(1); } pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(lpt->thd, lpt->db, lpt->table_name); + tdc_remove_table(lpt->thd, TDC_RT_REMOVE_NOT_OWN, lpt->db, lpt->table_name); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 612f9d1954d..630cf73076c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1172,7 +1172,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(0, table_list->db, table_list->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, + table_list->table_name); pthread_mutex_unlock(&LOCK_open); } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index fdf2f5ac155..ca1543b32d8 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -139,7 +139,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) pthread_mutex_lock(&LOCK_open); for (ren_table= table_list; ren_table; ren_table= ren_table->next_local) - expel_table_from_cache(0, ren_table->db, ren_table->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db, + ren_table->table_name); error=0; if ((ren_table=rename_tables(thd,table_list,0))) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9df31c7c2ad..f608e233349 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3075,6 +3075,56 @@ uint get_table_open_method(TABLE_LIST *tables, } +/** + Acquire high priority share metadata lock on a table. + + @param thd Thread context. + @param mdl_lock_data Pointer to memory to be used for MDL_LOCK_DATA + object for a lock request. + @param mdlkey Pointer to the buffer for key for the lock request + (should be at least strlen(db) + strlen(name) + 2 + bytes, or, if the lengths are not known, + MAX_DBNAME_LENGTH) + @param table Table list element for the table + + @note This is an auxiliary function to be used in cases when we want to + access table's description by looking up info in TABLE_SHARE without + going through full-blown table open. + @note This function assumes that there are no other metadata lock requests + in the current metadata locking context. + + @retval FALSE Success + @retval TRUE Some error occured (probably thread was killed). +*/ + +static bool +acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_DATA *mdl_lock_data, + char *mdlkey, TABLE_LIST *table) +{ + bool retry; + + mdl_init_lock(mdl_lock_data, mdlkey, 0, table->db, table->table_name); + table->mdl_lock_data= mdl_lock_data; + mdl_add_lock(&thd->mdl_context, mdl_lock_data); + mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + + while (1) + { + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) + { + if (!retry || mdl_wait_for_locks(&thd->mdl_context)) + { + mdl_remove_all_locks(&thd->mdl_context); + return TRUE; + } + continue; + } + break; + } + return FALSE; +} + + /** @brief Fill I_S table with data from FRM file only @@ -3108,7 +3158,6 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; MDL_LOCK_DATA mdl_lock_data; char mdlkey[MAX_DBKEY_LENGTH]; - bool retry; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); @@ -3133,32 +3182,20 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, table_list.db= db_name->str; } - mdl_init_lock(&mdl_lock_data, mdlkey, 0, db_name->str, table_name->str); - table_list.mdl_lock_data= &mdl_lock_data; - mdl_add_lock(&thd->mdl_context, &mdl_lock_data); - mdl_set_lock_type(&mdl_lock_data, MDL_SHARED_HIGH_PRIO); - /* TODO: investigate if in this particular situation we can get by simply obtaining internal lock of data-dictionary (ATM it is LOCK_open) instead of obtaning full-blown metadata lock. */ - while (1) + if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_data, mdlkey, + &table_list)) { - if (mdl_acquire_shared_lock(&thd->mdl_context, &mdl_lock_data, &retry)) - { - if (!retry || mdl_wait_for_locks(&thd->mdl_context)) - { - /* - Some error occured or we have been killed while waiting - for conflicting locks to go away, let the caller to handle - the situation. - */ - return 1; - } - continue; - } - break; + /* + Some error occured (most probably we have been killed while + waiting for conflicting locks to go away), let the caller to + handle the situation. + */ + return 1; } key_length= create_table_def_key(thd, key, &table_list, 0); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index dcfc9b811b6..2c91ac69f9f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1897,7 +1897,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, DBUG_RETURN(1); pthread_mutex_lock(&LOCK_open); for (table= tables; table; table= table->next_local) - expel_table_from_cache(0, table->db, table->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); pthread_mutex_unlock(&LOCK_open); } else @@ -4345,7 +4345,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(0, table->db, table->table_name); + tdc_remove_table(0, TDC_RT_REMOVE_UNUSED, table->db, table->table_name); pthread_mutex_unlock(&LOCK_open); if (my_copy(src_path, dst_path, MYF(MY_WME))) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 9d47b637886..c8f01a56a72 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -469,7 +469,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) goto end; pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(0, tables->db, tables->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tables->db, tables->table_name); if (reopen_name_locked_table(thd, tables)) goto end_unlock; From bcae0d9bab24165bc58090187ba0a51babe0e8ac Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 18:22:15 +0300 Subject: [PATCH 067/466] Backport of: ---------------------------------------------------------- revno: 2630.10.1 committer: Konstantin Osipov branch nick: mysql-6.0-lock-tables-tidyup timestamp: Wed 2008-06-11 15:49:58 +0400 message: WL#3726, review fixes. Now that we have metadata locks, we don't need to keep a crippled TABLE instance in the table cache to indicate that a table is locked. Remove all code that used this technique. Instead, rely on metadata locks and use the standard open_table() and close_thread_table() to manipulate with the table cache tables. Removes a list of functions that have become unused (see the comment for sql_base.cc for details). Under LOCK TABLES, keep a TABLE_LIST instance for each table that may be temporarily closed. For that, implement an own class for LOCK TABLES mode, Locked_tables_list. This is a pre-requisite patch for WL#4144. This is not exactly a backport: there is no new online ALTER table in Celosia, so the old alter table code was changed to work with the new table cache API. --- mysql-test/r/lock.result | 45 + mysql-test/r/trigger-compat.result | 2 - mysql-test/suite/rpl/r/rpl_trigger.result | 2 - mysql-test/t/lock.test | 43 + sql/field.h | 1 - sql/lock.cc | 22 +- sql/mysql_priv.h | 38 +- sql/sp_head.cc | 9 +- sql/sql_acl.cc | 3 +- sql/sql_base.cc | 1347 ++++++--------------- sql/sql_class.cc | 7 +- sql/sql_class.h | 63 +- sql/sql_db.cc | 4 +- sql/sql_insert.cc | 12 +- sql/sql_parse.cc | 51 +- sql/sql_partition.cc | 73 +- sql/sql_servers.cc | 3 +- sql/sql_table.cc | 161 +-- sql/sql_trigger.cc | 77 +- sql/table.h | 3 +- 20 files changed, 767 insertions(+), 1199 deletions(-) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index a60c5c8383f..676a8c41bb6 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -201,3 +201,48 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated UNLOCK TABLES; DROP TABLE t1,t2; End of 5.1 tests. +# +# Ensure that FLUSH TABLES doesn't substitute a base locked table +# with a temporary one. +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +create temporary table t1 (a int); +flush table t1; +drop temporary table t1; +select * from t1; +a +unlock tables; +drop table t1, t2; +# +# Ensure that REPAIR .. USE_FRM works under LOCK TABLES. +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair status OK +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair status OK +select * from t1; +a +select * from t2; +a +repair table t2 use_frm; +Table Op Msg_type Msg_text +test.t2 repair status OK +repair table t2 use_frm; +Table Op Msg_type Msg_text +test.t2 repair status OK +select * from t1; +a +unlock tables; +drop table t1, t2; +# +# End of 6.0 tests. +# diff --git a/mysql-test/r/trigger-compat.result b/mysql-test/r/trigger-compat.result index 14ef4f54f27..14949c227bd 100644 --- a/mysql-test/r/trigger-compat.result +++ b/mysql-test/r/trigger-compat.result @@ -34,8 +34,6 @@ TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATA def mysqltest_db1 wl2818_trg1 INSERT def mysqltest_db1 t1 0 NULL INSERT INTO t2 VALUES(CURRENT_USER()) ROW BEFORE NULL NULL OLD NEW NULL latin1 latin1_swedish_ci latin1_swedish_ci def mysqltest_db1 wl2818_trg2 INSERT def mysqltest_db1 t1 0 NULL INSERT INTO t2 VALUES(CURRENT_USER()) ROW AFTER NULL NULL OLD NEW NULL mysqltest_dfn@localhost latin1 latin1_swedish_ci latin1_swedish_ci DROP TRIGGER wl2818_trg1; -Warnings: -Warning 1454 No definer attribute for trigger 'mysqltest_db1'.'wl2818_trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. DROP TRIGGER wl2818_trg2; use mysqltest_db1; DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_trigger.result b/mysql-test/suite/rpl/r/rpl_trigger.result index 3d7757613a7..01d886c4709 100644 --- a/mysql-test/suite/rpl/r/rpl_trigger.result +++ b/mysql-test/suite/rpl/r/rpl_trigger.result @@ -893,8 +893,6 @@ s @ root@localhost DROP TRIGGER trg1; -Warnings: -Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. DROP TABLE t1; DROP TABLE t2; STOP SLAVE; diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 856ae020492..7effaaeb20d 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -252,3 +252,46 @@ UNLOCK TABLES; DROP TABLE t1,t2; --echo End of 5.1 tests. + +--echo # +--echo # Ensure that FLUSH TABLES doesn't substitute a base locked table +--echo # with a temporary one. +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +create temporary table t1 (a int); +flush table t1; +drop temporary table t1; +select * from t1; +unlock tables; +drop table t1, t2; + +--echo # +--echo # Ensure that REPAIR .. USE_FRM works under LOCK TABLES. +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +repair table t1 use_frm; +repair table t1 use_frm; +select * from t1; +select * from t2; +repair table t2 use_frm; +repair table t2 use_frm; +select * from t1; +unlock tables; +drop table t1, t2; + + +--echo # +--echo # End of 6.0 tests. +--echo # diff --git a/sql/field.h b/sql/field.h index fb6ca34e88e..3dd18b4ffaa 100644 --- a/sql/field.h +++ b/sql/field.h @@ -478,7 +478,6 @@ public: } /* Hash value */ virtual void hash(ulong *nr, ulong *nr2); - friend bool reopen_table(THD *,struct st_table *,bool); friend int cre_myisam(char * name, register TABLE *form, uint options, ulonglong auto_increment_value); friend class Copy_field; diff --git a/sql/lock.cc b/sql/lock.cc index 33c9edcea48..814eebde337 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -512,28 +512,15 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) /** Try to find the table in the list of locked tables. In case of success, unlock the table and remove it from this list. - - @note This function has a legacy side effect: the table is - unlocked even if it is not found in the locked list. - It's not clear if this side effect is intentional or still - desirable. It might lead to unmatched calls to - unlock_external(). Moreover, a discrepancy can be left - unnoticed by the storage engine, because in - unlock_external() we call handler::external_lock(F_UNLCK) only - if table->current_lock is not F_UNLCK. + If a table has more than one lock instance, removes them all. @param thd thread context @param locked list of locked tables @param table the table to unlock - @param always_unlock specify explicitly if the legacy side - effect is desired. */ -void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, - bool always_unlock) +void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) { - if (always_unlock == TRUE) - mysql_unlock_some_tables(thd, &table, /* table count */ 1); if (locked) { reg1 uint i; @@ -547,9 +534,8 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, DBUG_ASSERT(table->lock_position == i); - /* Unlock if not yet unlocked */ - if (always_unlock == FALSE) - mysql_unlock_some_tables(thd, &table, /* table count */ 1); + /* Unlock the table. */ + mysql_unlock_some_tables(thd, &table, /* table count */ 1); /* Decrement table_count in advance, making below expressions easier */ old_tables= --locked->table_count; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b650af8017d..12d7e46e821 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1225,20 +1225,13 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags); -bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list); TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name); TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_name); void detach_merge_children(TABLE *table, bool clear_refs); bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, TABLE_LIST *new_child_list, TABLE_LIST **new_last); -bool reopen_table(TABLE *table); -bool reopen_tables(THD *thd, bool get_locks); thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table); -void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, - const char *table_name); -void close_handle_and_leave_table_as_placeholder(TABLE *table); -void unlock_locked_tables(THD *thd); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); extern Field *not_found_field; @@ -1388,12 +1381,12 @@ void add_join_on(TABLE_LIST *b,Item *expr); void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List *using_fields, SELECT_LEX *lex); bool add_proc_to_list(THD *thd, Item *item); -bool close_cached_table(THD *thd, TABLE *table); bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function); -void unlink_open_table(THD *thd, TABLE *find, bool unlock); void drop_open_table(THD *thd, TABLE *table, const char *db_name, const char *table_name); +void close_all_tables_for_name(THD *thd, TABLE_SHARE *share, + bool remove_from_locked_tables); void update_non_unique_table_error(TABLE_LIST *update, const char *operation, TABLE_LIST *duplicate); @@ -1515,7 +1508,6 @@ void close_temporary_table(THD *thd, TABLE *table, bool free_share, void close_temporary(TABLE *table, bool free_share, bool delete_table); bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); -void remove_db_from_cache(const char *db); void flush_tables(); bool is_equal(const LEX_STRING *a, const LEX_STRING *b); char *make_default_log_name(char *buff,const char* log_ext); @@ -1561,7 +1553,7 @@ void create_subpartition_name(char *out, const char *in1, typedef struct st_lock_param_type { - TABLE_LIST table_list; + TABLE_LIST *table_list; ulonglong copied; ulonglong deleted; THD *thd; @@ -1572,7 +1564,6 @@ typedef struct st_lock_param_type const char *db; const char *table_name; uchar *pack_frm_data; - enum thr_lock_type old_lock_type; uint key_count; uint db_options; size_t pack_frm_len; @@ -2044,12 +2035,31 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 #define MYSQL_LOCK_PERF_SCHEMA 0x0010 #define MYSQL_OPEN_TAKE_UPGRADABLE_MDL 0x0020 +/** + Do not try to acquire a metadata lock on the table: we + already have one. +*/ +#define MYSQL_OPEN_HAS_MDL_LOCK 0x0040 +/** + If in locked tables mode, ignore the locked tables and get + a new instance of the table. +*/ +#define MYSQL_OPEN_GET_NEW_TABLE 0x0080 +/** Don't look up the table in the list of temporary tables. */ +#define MYSQL_OPEN_SKIP_TEMPORARY 0x0100 + +/** Please refer to the internals manual. */ +#define MYSQL_OPEN_REOPEN (MYSQL_LOCK_IGNORE_FLUSH |\ + MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |\ + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\ + MYSQL_OPEN_GET_NEW_TABLE |\ + MYSQL_OPEN_SKIP_TEMPORARY |\ + MYSQL_OPEN_HAS_MDL_LOCK) void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); -void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, - bool always_unlock); +void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); void mysql_lock_downgrade_write(THD *thd, TABLE *table, thr_lock_type new_lock_type); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index d905ddcda31..1d953e773b3 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3985,8 +3985,8 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, - thd->mdl_el_root ? - thd->mdl_el_root : + thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); /* Everyting else should be zeroed */ @@ -4030,8 +4030,9 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->select_lex= lex->current_select; table->cacheable_table= 1; table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, - thd->mdl_el_root ? thd->mdl_el_root : - thd->mem_root); + thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); + lex->add_to_query_tables(table); return table; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b01e7b1049d..aa2c697f221 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -676,7 +676,8 @@ my_bool acl_reload(THD *thd) my_bool return_val= 1; DBUG_ENTER("acl_reload"); - unlock_locked_tables(thd); // Can't have locked tables here + /* Can't have locked tables here. */ + thd->locked_tables_list.unlock_locked_tables(thd); /* To avoid deadlocks we should obtain table locks before diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f7ac1df8b32..61c071d3430 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -116,9 +116,6 @@ static bool table_def_inited= 0; static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables, TABLE_SHARE *table_share); -static bool reopen_table_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, - const char *alias, char *cache_key, - uint cache_key_length); static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry); static bool auto_repair_table(THD *thd, TABLE_LIST *table_list); static void free_cache_entry(TABLE *entry); @@ -405,20 +402,6 @@ static void table_def_unuse_table(TABLE *table) } -/** - Bind used TABLE instance to another table share. - - @note Will go away once we refactor code responsible - for reopening tables under lock tables. -*/ - -static void table_def_change_share(TABLE *table, TABLE_SHARE *new_share) -{ - table->s->used_tables.remove(table); - new_share->used_tables.push_front(table); -} - - /* Get TABLE_SHARE for a table. @@ -726,65 +709,6 @@ static void reference_table_share(TABLE_SHARE *share) } -/** - Close file handle, but leave the table in THD::open_tables list - to allow its future reopening. - - @param table Table handler - - @note THD::killed will be set if we run out of memory - - @note If closing a MERGE child, the calling function has to - take care for closing the parent too, if necessary. - - @todo Get rid of this function once we refactor LOCK TABLES - to keep around TABLE_LIST elements used for opening - of tables until UNLOCK TABLES. -*/ - -void close_handle_and_leave_table_as_placeholder(TABLE *table) -{ - TABLE_SHARE *share, *old_share= table->s; - char *key_buff; - MEM_ROOT *mem_root= &table->mem_root; - DBUG_ENTER("close_handle_and_leave_table_as_lock"); - - DBUG_ASSERT(table->db_stat); - - /* - Make a local copy of the table share and free the current one. - This has to be done to ensure that the table share is removed from - the table defintion cache as soon as the last instance is removed - */ - if (multi_alloc_root(mem_root, - &share, sizeof(*share), - &key_buff, old_share->table_cache_key.length, - NULL)) - { - bzero((char*) share, sizeof(*share)); - share->set_table_cache_key(key_buff, old_share->table_cache_key.str, - old_share->table_cache_key.length); - share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table() - } - - /* - When closing a MERGE parent or child table, detach the children first. - Do not clear child table references to allow for reopen. - */ - if (table->child_l || table->parent) - detach_merge_children(table, FALSE); - table->file->close(); - table->db_stat= 0; // Mark file closed - table_def_change_share(table, share); - release_table_share(table->s); - table->s= share; - table->file->change_table_ptr(table, table->s); - - DBUG_VOID_RETURN; -} - - - /* Create a list for all open tables matching SQL expression @@ -1030,64 +954,31 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, if (thd->locked_tables_mode) { /* - If we are under LOCK TABLES we need to reopen tables without + If we are under LOCK TABLES, we need to reopen the tables without opening a door for any concurrent threads to sneak in and get lock on our tables. To achieve this we use exclusive metadata locks. */ - if (!tables) + TABLE_LIST *tables_to_reopen= (tables ? tables : + thd->locked_tables_list.locked_tables()); + + for (TABLE_LIST *table_list= tables_to_reopen; table_list; + table_list= table_list->next_global) { - for (TABLE *tab= thd->open_tables; tab; tab= tab->next) + /* A check that the table was locked for write is done by the caller. */ + TABLE *table= find_locked_table(thd->open_tables, table_list->db, + table_list->table_name); + + /* May return NULL if this table has already been closed via an alias. */ + if (! table) + continue; + + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) { - /* - Checking TABLE::db_stat is essential in case when we have - several instances of the table open and locked. - */ - if (tab->db_stat) - { - char dbname[NAME_LEN+1]; - char tname[NAME_LEN+1]; - /* - Since close_data_files_and_leave_as_placeholders() frees share's - memroot we need to make copies of database and table names. - */ - strmov(dbname, tab->s->db.str); - strmov(tname, tab->s->table_name.str); - if (wait_while_table_is_used(thd, tab, HA_EXTRA_FORCE_REOPEN)) - { - result= TRUE; - goto err_with_reopen; - } - pthread_mutex_lock(&LOCK_open); - close_data_files_and_leave_as_placeholders(thd, dbname, tname); - pthread_mutex_unlock(&LOCK_open); - } - } - } - else - { - for (TABLE_LIST *table= tables; table; table= table->next_local) - { - /* This should always succeed thanks to check in caller. */ - TABLE *tab= find_write_locked_table(thd->open_tables, table->db, - table->table_name); - /* - Checking TABLE::db_stat is essential in case when we have - several instances of the table open and locked. - */ - if (tab->db_stat) - { - if (wait_while_table_is_used(thd, tab, HA_EXTRA_FORCE_REOPEN)) - { - result= TRUE; - goto err_with_reopen; - } - pthread_mutex_lock(&LOCK_open); - close_data_files_and_leave_as_placeholders(thd, table->db, - table->table_name); - pthread_mutex_unlock(&LOCK_open); - } + result= TRUE; + goto err_with_reopen; } + close_all_tables_for_name(thd, table->s, FALSE); } } @@ -1145,16 +1036,12 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, err_with_reopen: if (thd->locked_tables_mode) { - pthread_mutex_lock(&LOCK_open); /* No other thread has the locked tables open; reopen them and get the old locks. This should always succeed (unless some external process has removed the tables) */ - thd->in_lock_tables=1; - result|= reopen_tables(thd, 1); - thd->in_lock_tables=0; - pthread_mutex_unlock(&LOCK_open); + thd->locked_tables_list.reopen_tables(thd); /* Since mdl_downgrade_exclusive_lock() won't do anything with shared metadata lock it is much simplier to go through all open tables rather @@ -1311,6 +1198,8 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) { for (; table ; table= table->next) { + DBUG_ASSERT(table->pos_in_locked_tables == NULL || + table->pos_in_locked_tables->table == table); if (table->query_id == thd->query_id) { table->query_id= 0; @@ -1355,6 +1244,75 @@ static void close_open_tables(THD *thd) } +/** + Close all open instances of the table but keep the MDL lock, + if any. + + Works both under LOCK TABLES and in the normal mode. + Removes all closed instances of the table from the table cache. + + @param thd thread handle + @param[in] share table share, but is just a handy way to + access the table cache key + + @param[in] remove_from_locked_tables + TRUE if the table is being dropped or renamed. + In that case the documented behaviour is to + implicitly remove the table from LOCK TABLES + list. +*/ + +void +close_all_tables_for_name(THD *thd, TABLE_SHARE *share, + bool remove_from_locked_tables) +{ + char key[MAX_DBKEY_LENGTH]; + uint key_length= share->table_cache_key.length; + + memcpy(key, share->table_cache_key.str, key_length); + + safe_mutex_assert_not_owner(&LOCK_open); + /* + We need to hold LOCK_open while changing the open_tables + list, since another thread may work on it. + @sa notify_thread_having_shared_lock() + */ + pthread_mutex_lock(&LOCK_open); + + for (TABLE **prev= &thd->open_tables; *prev; ) + { + TABLE *table= *prev; + + if (table->s->table_cache_key.length == key_length && + !memcmp(table->s->table_cache_key.str, key, key_length)) + { + /* + Does nothing if the table is not locked. + This allows one to use this function after a table + has been unlocked, e.g. in partition management. + */ + mysql_lock_remove(thd, thd->lock, table); + + thd->locked_tables_list.unlink_from_list(thd, + table->pos_in_locked_tables, + remove_from_locked_tables); + + /* Make sure the table is removed from the cache */ + table->s->version= 0; + close_thread_table(thd, prev); + } + else + { + /* Step to next entry in open_tables list. */ + prev= &table->next; + } + } + /* We have been removing tables from the table cache. */ + broadcast_refresh(); + pthread_mutex_unlock(&LOCK_open); +} + + /* Close all tables used by the current substatement, or all tables used by this thread if we are on the upper level. @@ -1995,7 +1953,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list) If LOCK TABLES list is not empty and contains this table, unlock the table and remove the table from this list. */ - mysql_lock_remove(thd, thd->lock, table, FALSE); + mysql_lock_remove(thd, thd->lock, table); close_temporary_table(thd, table, 1, 1); DBUG_RETURN(0); } @@ -2111,77 +2069,6 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, } -/** - Prepare an open merge table for close. - - @param[in] thd thread context - @param[in] table table to prepare - @param[in,out] prev_pp pointer to pointer of previous table - - @detail - If the table is a MERGE parent, just detach the children. - If the table is a MERGE child, close the parent (incl. detach). -*/ - -static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp) -{ - DBUG_ENTER("unlink_open_merge"); - - if (table->parent) - { - /* - If MERGE child, close parent too. Closing includes detaching. - - This is used for example in ALTER TABLE t1 RENAME TO t5 under - LOCK TABLES where t1 is a MERGE child: - CREATE TABLE t1 (c1 INT); - CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); - LOCK TABLES t1 WRITE, t2 WRITE; - ALTER TABLE t1 RENAME TO t5; - */ - TABLE *parent= table->parent; - TABLE **prv_p; - - /* Find parent in open_tables list. */ - for (prv_p= &thd->open_tables; - *prv_p && (*prv_p != parent); - prv_p= &(*prv_p)->next) {} - if (*prv_p) - { - /* Special treatment required if child follows parent in list. */ - if (*prev_pp == &parent->next) - *prev_pp= prv_p; - /* - Remove parent from open_tables list and close it. - This includes detaching and hence clearing parent references. - */ - close_thread_table(thd, prv_p); - } - } - else if (table->child_l) - { - /* - When closing a MERGE parent, detach the children first. It is - not necessary to clear the child or parent table reference of - this table because the TABLE is freed. But we need to clear - the child or parent references of the other belonging tables - so that they cannot be moved into the unused_tables chain with - these pointers set. - - This is used for example in ALTER TABLE t2 RENAME TO t5 under - LOCK TABLES where t2 is a MERGE parent: - CREATE TABLE t1 (c1 INT); - CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); - LOCK TABLES t1 WRITE, t2 WRITE; - ALTER TABLE t2 RENAME TO t5; - */ - detach_merge_children(table, TRUE); - } - - DBUG_VOID_RETURN; -} - - /** Force all other threads to stop using the table by upgrading metadata lock on it and remove unused TABLE instances from cache. @@ -2230,119 +2117,22 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, /** - Upgrade metadata lock on the table and close all its instances. + Close a and drop a just created table in CREATE TABLE ... SELECT. - @param thd Thread handler - @param table Table to remove from cache + @param thd Thread handle + @param table TABLE object for the table to be dropped + @param db_name Name of database for this table + @param table_name Name of this table - @retval FALSE Success. - @retval TRUE Failure (e.g. because thread was killed). -*/ - -bool close_cached_table(THD *thd, TABLE *table) -{ - DBUG_ENTER("close_cached_table"); - - /* FIXME: check if we pass proper parameters everywhere. */ - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) - DBUG_RETURN(TRUE); - - /* Close lock if this is not got with LOCK TABLES */ - if (! thd->locked_tables_mode) - { - mysql_unlock_tables(thd, thd->lock); - thd->lock=0; // Start locked threads - } - - pthread_mutex_lock(&LOCK_open); - /* Close all copies of 'table'. This also frees all LOCK TABLES lock */ - unlink_open_table(thd, table, TRUE); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(FALSE); -} - - -/** - Remove all instances of table from thread's open list and - table cache. - - @param thd Thread context - @param find Table to remove - @param unlock TRUE - free all locks on tables removed that are - done with LOCK TABLES - FALSE - otherwise - - @note When unlock parameter is FALSE or current thread doesn't have - any tables locked with LOCK TABLES, tables are assumed to be - not locked (for example already unlocked). -*/ - -void unlink_open_table(THD *thd, TABLE *find, bool unlock) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length= find->s->table_cache_key.length; - TABLE *list, **prev; - DBUG_ENTER("unlink_open_table"); - - safe_mutex_assert_owner(&LOCK_open); - - memcpy(key, find->s->table_cache_key.str, key_length); - /* - Note that we need to hold LOCK_open while changing the - open_tables list. Another thread may work on it. - (See: notify_thread_having_shared_lock()) - Closing a MERGE child before the parent would be fatal if the - other thread tries to abort the MERGE lock in between. - */ - for (prev= &thd->open_tables; *prev; ) - { - list= *prev; - - if (list->s->table_cache_key.length == key_length && - !memcmp(list->s->table_cache_key.str, key, key_length)) - { - if (unlock && thd->locked_tables_mode) - mysql_lock_remove(thd, thd->lock, - list->parent ? list->parent : list, TRUE); - - /* Prepare MERGE table for close. Close parent if necessary. */ - unlink_open_merge(thd, list, &prev); - - /* Remove table from open_tables list. */ - *prev= list->next; - /* Close table. */ - free_cache_entry(list); - } - else - { - /* Step to next entry in open_tables list. */ - prev= &list->next; - } - } - - // Notify any 'refresh' threads - broadcast_refresh(); - DBUG_VOID_RETURN; -} - - -/** - Auxiliary routine which closes and drops open table. - - @param thd Thread handle - @param table TABLE object for table to be dropped - @param db_name Name of database for this table - @param table_name Name of this table - - @note This routine assumes that table to be closed is open only - by calling thread so we needn't wait until other threads - will close the table. Also unless called under implicit or - explicit LOCK TABLES mode it assumes that table to be - dropped is already unlocked. In the former case it will - also remove lock on the table. But one should not rely on - this behaviour as it may change in future. - Currently, however, this function is never called for a - table that was locked with LOCK TABLES. + This routine assumes that the table to be closed is open only + by the calling thread, so we needn't wait until other threads + close the table. It also assumes that the table is first + in thd->open_ables and a data lock on it, if any, has been + released. To sum up, it's tuned to work with + CREATE TABLE ... SELECT and CREATE TABLE .. SELECT only. + Note, that currently CREATE TABLE ... SELECT is not supported + under LOCK TABLES. This function, still, can be called in + prelocked mode, e.g. if we do CREATE TABLE .. SELECT f1(); */ void drop_open_table(THD *thd, TABLE *table, const char *db_name, @@ -2352,13 +2142,14 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name, close_temporary_table(thd, table, 1, 1); else { + DBUG_ASSERT(table == thd->open_tables); + handlerton *table_type= table->s->db_type(); + /* Ensure the table is removed from the cache. */ + table->s->version= 0; + pthread_mutex_lock(&LOCK_open); - /* - unlink_open_table() also tells threads waiting for refresh or close - that something has happened. - */ - unlink_open_table(thd, table, FALSE); + close_thread_table(thd, &thd->open_tables); quick_rm_table(table_type, db_name, table_name, 0); pthread_mutex_unlock(&LOCK_open); } @@ -2409,73 +2200,6 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) } -/* - Open table for which this thread has exclusive meta-data lock. - - SYNOPSIS - reopen_name_locked_table() - thd Thread handle - table_list TABLE_LIST object for table to be open. - - NOTE - This function assumes that its caller already acquired LOCK_open mutex. - - RETURN VALUE - FALSE - Success - TRUE - Error -*/ - -bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) -{ - TABLE *table; - TABLE_SHARE *share; - char key[MAX_DBKEY_LENGTH]; - uint key_length; - char *table_name= table_list->table_name; - DBUG_ENTER("reopen_name_locked_table"); - - if (thd->killed) - DBUG_RETURN(TRUE); - - key_length= create_table_def_key(thd, key, table_list, 0); - - if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME)))) - DBUG_RETURN(TRUE); - - if (reopen_table_entry(thd, table, table_list, table_name, key, key_length)) - { - my_free((uchar*)table, MYF(0)); - DBUG_RETURN(TRUE); - } - - share= table->s; - /* - We want to prevent other connections from opening this table until end - of statement as it is likely that modifications of table's metadata are - not yet finished (for example CREATE TRIGGER have to change .TRG file, - or we might want to drop table if CREATE TABLE ... SELECT fails). - This also allows us to assume that no other connection will sneak in - before we will get table-level lock on this table. - */ - share->version=0; - table->in_use = thd; - - table_def_add_used_table(thd, table); - - table->next= thd->open_tables; - thd->open_tables= table; - - table->tablenr=thd->current_tablenr++; - table->used_fields=0; - table->const_table=0; - table->null_row= table->maybe_null= 0; - table->force_index= table->force_index_order= table->force_index_group= 0; - table->status=STATUS_NO_RECORD; - table_list->table= table; - DBUG_RETURN(FALSE); -} - - /** Check that table exists in table definition cache, on disk or in some storage engine. @@ -2554,6 +2278,62 @@ void table_share_release_hook(void *share) } +/* + A helper function that acquires an MDL lock for a table + being opened. +*/ + +static bool +open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, + MDL_LOCK_DATA *mdl_lock_data, + uint flags, + enum_open_table_action *action) +{ + mdl_add_lock(&thd->mdl_context, mdl_lock_data); + + if (table_list->open_type) + { + /* + In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table + may not yet exist. Let's acquire an exclusive lock for that + case. If later it turns out the table existsed, we will + downgrade the lock to shared. Note that, according to the + locking protocol, all exclusive locks must be acquired before + shared locks. This invariant is preserved here and is also + enforced by asserts in metadata locking subsystem. + */ + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + return 1; + } + else + { + bool retry; + + /* + There is no MDL_SHARED_UPGRADABLE_HIGH_PRIO type of metadata lock so we + want to be sure that caller doesn't pass us both flags simultaneously. + */ + DBUG_ASSERT(!(flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL) || + !(flags & MYSQL_LOCK_IGNORE_FLUSH)); + + if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && + table_list->lock_type >= TL_WRITE_ALLOW_WRITE) + mdl_set_lock_type(mdl_lock_data, MDL_SHARED_UPGRADABLE); + if (flags & MYSQL_LOCK_IGNORE_FLUSH) + mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) + { + if (retry) + *action= OT_BACK_OFF_AND_RETRY; + return 1; + } + } + return 0; +} + + /* Open a table. @@ -2628,7 +2408,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, same name. This block implements the behaviour. TODO: move this block into a separate function. */ - if (!table_list->skip_temporary) + if (!table_list->skip_temporary && ! (flags & MYSQL_OPEN_SKIP_TEMPORARY)) { for (table= thd->temporary_tables; table ; table=table->next) { @@ -2673,7 +2453,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, open not pre-opened tables in pre-locked/LOCK TABLES mode. TODO: move this block into a separate function. */ - if (thd->locked_tables_mode) + if (thd->locked_tables_mode && + ! (flags & MYSQL_OPEN_GET_NEW_TABLE)) { // Using table locks TABLE *best_table= 0; int best_distance= INT_MIN; @@ -2785,47 +2566,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, */ mdl_lock_data= table_list->mdl_lock_data; - mdl_add_lock(&thd->mdl_context, mdl_lock_data); - - if (table_list->open_type) + if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - /* - In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table - may not yet exist. Let's acquire an exclusive lock for that - case. If later it turns out the table existsed, we will - downgrade the lock to shared. Note that, according to the - locking protocol, all exclusive locks must be acquired before - shared locks. This invariant is preserved here and is also - enforced by asserts in metadata locking subsystem. - */ - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + if (open_table_get_mdl_lock(thd, table_list, mdl_lock_data, flags, + action)) DBUG_RETURN(TRUE); } - else - { - bool retry; - - /* - There is no MDL_SHARED_UPGRADABLE_HIGH_PRIO type of metadata lock so we - want to be sure that caller doesn't pass us both flags simultaneously. - */ - DBUG_ASSERT(!(flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL) || - !(flags & MYSQL_LOCK_IGNORE_FLUSH)); - - if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && - table_list->lock_type >= TL_WRITE_ALLOW_WRITE) - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_UPGRADABLE); - if (flags & MYSQL_LOCK_IGNORE_FLUSH) - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); - - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) - { - if (retry) - *action= OT_BACK_OFF_AND_RETRY; - DBUG_RETURN(TRUE); - } - } pthread_mutex_lock(&LOCK_open); @@ -3061,7 +2807,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, MYF(MY_WME)); memcpy((char*) table->alias, alias, length); } - /* These variables are also set in reopen_table() */ table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; @@ -3153,427 +2898,264 @@ TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_na } -/* - Reopen an table because the definition has changed. - - SYNOPSIS - reopen_table() - table Table object - - NOTES - The data file for the table is already closed and the share is released - The table has a 'dummy' share that mainly contains database and table name. - - RETURN - 0 ok - 1 error. The old table object is not changed. -*/ - -bool reopen_table(TABLE *table) -{ - TABLE tmp; - bool error= 1; - Field **field; - uint key,part; - TABLE_LIST table_list; - THD *thd= table->in_use; - DBUG_ENTER("reopen_table"); - DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); - - DBUG_ASSERT(table->s->ref_count == 0); - DBUG_ASSERT(!table->sort.io_cache); - DBUG_ASSERT(!table->children_attached); - -#ifdef EXTRA_DEBUG - if (table->db_stat) - sql_print_error("Table %s had a open data handler in reopen_table", - table->alias); -#endif - bzero((char*) &table_list, sizeof(TABLE_LIST)); - table_list.db= table->s->db.str; - table_list.table_name= table->s->table_name.str; - table_list.table= table; - - if (reopen_table_entry(thd, &tmp, &table_list, - table->alias, - table->s->table_cache_key.str, - table->s->table_cache_key.length)) - goto end; - - /* This list copies variables set by open_table */ - tmp.tablenr= table->tablenr; - tmp.used_fields= table->used_fields; - tmp.const_table= table->const_table; - tmp.null_row= table->null_row; - tmp.maybe_null= table->maybe_null; - tmp.status= table->status; - - tmp.s->table_map_id= table->s->table_map_id; - - /* Get state */ - tmp.in_use= thd; - tmp.reginfo.lock_type=table->reginfo.lock_type; - tmp.grant= table->grant; - - /* Replace table in open list */ - tmp.next= table->next; - tmp.prev= table->prev; - - /* Preserve MERGE parent. */ - tmp.parent= table->parent; - /* Fix MERGE child list and check for unchanged union. */ - if ((table->child_l || tmp.child_l) && - fix_merge_after_open(table->child_l, table->child_last_l, - tmp.child_l, tmp.child_last_l)) - { - (void) closefrm(&tmp, 1); // close file, free everything - goto end; - } - tmp.mdl_lock_data= table->mdl_lock_data; - - table_def_change_share(table, tmp.s); - /* Avoid wiping out TABLE's position in new share's used tables list. */ - tmp.share_next= table->share_next; - tmp.share_prev= table->share_prev; - - delete table->triggers; - if (table->file) - (void) closefrm(table, 1); // close file, free everything - - *table= tmp; - table->default_column_bitmaps(); - table->file->change_table_ptr(table, table->s); - - DBUG_ASSERT(table->alias != 0); - for (field=table->field ; *field ; field++) - { - (*field)->table= (*field)->orig_table= table; - (*field)->table_name= &table->alias; - } - for (key=0 ; key < table->s->keys ; key++) - { - for (part=0 ; part < table->key_info[key].usable_key_parts ; part++) - { - table->key_info[key].key_part[part].field->table= table; - table->key_info[key].key_part[part].field->orig_table= table; - } - } - if (table->triggers) - table->triggers->set_table(table); - /* - Do not attach MERGE children here. The children might be reopened - after the parent. Attach children after reopening all tables that - require reopen. See for example reopen_tables(). - */ - - broadcast_refresh(); - error=0; - - end: - DBUG_RETURN(error); -} - +/*********************************************************************** + class Locked_tables_list implementation. Declared in sql_class.h +************************************************************************/ /** - Close all instances of a table open by this thread and replace - them with placeholder in THD::open_tables list for future reopening. + Enter LTM_LOCK_TABLES mode. - @param thd Thread context - @param db Database name for the table to be closed - @param table_name Name of the table to be closed + Enter the LOCK TABLES mode using all the tables that are + currently open and locked in this connection. + Initializes a TABLE_LIST instance for every locked table. - @note This function assumes that if we are not under LOCK TABLES, - then there is only one table open and locked. This means that - the function probably has to be adjusted before it can be used - anywhere outside ALTER TABLE. + @param thd thread handle - @note Must not use TABLE_SHARE::table_name/db of the table being closed, - the strings are used in a loop even after the share may be freed. + @return TRUE if out of memory. */ -void close_data_files_and_leave_as_placeholders(THD *thd, const char *db, - const char *table_name) +bool +Locked_tables_list::init_locked_tables(THD *thd) { - TABLE *table; - DBUG_ENTER("close_data_files_and_leave_as_placeholders"); + DBUG_ASSERT(thd->locked_tables_mode == LTM_NONE); + DBUG_ASSERT(m_locked_tables == NULL); - safe_mutex_assert_owner(&LOCK_open); - - if (! thd->locked_tables_mode) + for (TABLE *table= thd->open_tables; table; table= table->next) { - /* - If we are not under LOCK TABLES we should have only one table - open and locked so it makes sense to remove the lock at once. - */ - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; - } + TABLE_LIST *src_table_list= table->pos_in_table_list; + char *db, *table_name, *alias; + size_t db_len= strlen(src_table_list->db) + 1; + size_t table_name_len= strlen(src_table_list->table_name) + 1; + size_t alias_len= strlen(src_table_list->alias) + 1; + TABLE_LIST *dst_table_list; - for (table=thd->open_tables; table ; table=table->next) - { - if (!strcmp(table->s->table_name.str, table_name) && - !strcmp(table->s->db.str, db)) + if (! multi_alloc_root(&m_locked_tables_root, + &dst_table_list, sizeof(*dst_table_list), + &db, db_len, + &table_name, table_name_len, + &alias, alias_len, + 0)) { - if (thd->locked_tables_mode) - { - if (table->parent) - { - /* - If MERGE child, need to reopen parent too. This means that - the first child to be closed will detach all children from - the parent and close it. OTOH in most cases a MERGE table - won't have multiple children with the same db.table_name. - */ - mysql_lock_remove(thd, thd->lock, table->parent, TRUE); - close_handle_and_leave_table_as_placeholder(table->parent); - } - else - mysql_lock_remove(thd, thd->lock, table, TRUE); - } - table->s->version= 0; - close_handle_and_leave_table_as_placeholder(table); + unlock_locked_tables(0); + return TRUE; } + + /** + Sic: remember the *actual* table level lock type taken, to + acquire the exact same type in reopen_tables(). + E.g. if the table was locked for write, src_table_list->lock_type is + TL_WRITE_DEFAULT, whereas reginfo.lock_type has been updated from + thd->update_lock_default. + */ + dst_table_list->init_one_table(db, table_name, alias, + src_table_list->table->reginfo.lock_type); + dst_table_list->mdl_lock_data= src_table_list->mdl_lock_data; + dst_table_list->table= table; + memcpy(db, src_table_list->db, db_len); + memcpy(table_name, src_table_list->table_name, table_name_len); + memcpy(alias, src_table_list->alias, alias_len); + /* Link last into the list of tables */ + *(dst_table_list->prev_global= m_locked_tables_last)= dst_table_list; + m_locked_tables_last= &dst_table_list->next_global; + table->pos_in_locked_tables= dst_table_list; } - DBUG_VOID_RETURN; + thd->locked_tables_mode= LTM_LOCK_TABLES; + return FALSE; } - /** - Reattach MERGE children after reopen. + Leave LTM_LOCK_TABLES mode if it's been entered. - @param[in] thd thread context - @param[in,out] err_tables_p pointer to pointer of tables in error + Close all locked tables, free memory, and leave the mode. - @return status - @retval FALSE OK, err_tables_p unchanged - @retval TRUE Error, err_tables_p contains table(s) + @note This function is a no-op if we're not in LOCK TABLES. */ -static bool reattach_merge(THD *thd, TABLE **err_tables_p) +void +Locked_tables_list::unlock_locked_tables(THD *thd) + { - TABLE *table; - TABLE *next; - TABLE **prv_p= &thd->open_tables; - bool error= FALSE; - DBUG_ENTER("reattach_merge"); - - for (table= thd->open_tables; table; table= next) - { - next= table->next; - DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx next: 0x%lx", - table->s->db.str, table->s->table_name.str, - (long) table, (long) next)); - /* Reattach children for MERGE tables with "closed data files" only. */ - if (table->child_l && !table->children_attached) - { - DBUG_PRINT("tcache", ("MERGE parent, attach children")); - if(table->file->extra(HA_EXTRA_ATTACH_CHILDREN)) - { - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); - error= TRUE; - /* Remove table from open_tables. */ - *prv_p= next; - if (next) - prv_p= &next->next; - /* Stack table on error list. */ - table->next= *err_tables_p; - *err_tables_p= table; - continue; - } - else - { - table->children_attached= TRUE; - DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx", - table->s->db.str, - table->s->table_name.str, (long) table)); - } - } - prv_p= &table->next; - } - DBUG_RETURN(error); -} - - -/** - Reopen all tables with closed data files. - - @param thd Thread context - @param get_locks Should we get locks after reopening tables ? - - @note Since this function can't properly handle prelocking and - create placeholders it should be used in very special - situations like FLUSH TABLES or ALTER TABLE. In general - case one should just repeat open_tables()/lock_tables() - combination when one needs tables to be reopened (for - example see open_and_lock_tables()). - - @note One should have lock on LOCK_open when calling this. - - @return FALSE in case of success, TRUE - otherwise. -*/ - -bool reopen_tables(THD *thd, bool get_locks) -{ - TABLE *table,*next,**prev; - TABLE **tables,**tables_ptr; // For locks - TABLE *err_tables= NULL, *err_tab_tmp; - bool error=0, not_used; - bool merge_table_found= FALSE; - - DBUG_ENTER("reopen_tables"); - - if (!thd->open_tables) - DBUG_RETURN(0); - - safe_mutex_assert_owner(&LOCK_open); - if (get_locks) + if (thd) { + DBUG_ASSERT(!thd->in_sub_stmt && + !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); /* - The ptr is checked later - Do not handle locks of MERGE children. + Sic: we must be careful to not close open tables if + we're not in LOCK TABLES mode: unlock_locked_tables() is + sometimes called implicitly, expecting no effect on + open tables, e.g. from begin_trans(). */ - uint opens=0; - for (table= thd->open_tables; table ; table=table->next) - if (!table->parent) - opens++; - DBUG_PRINT("tcache", ("open tables to lock: %u", opens)); - tables= (TABLE**) my_alloca(sizeof(TABLE*)*opens); - } - else - tables= &thd->open_tables; - tables_ptr =tables; + if (thd->locked_tables_mode != LTM_LOCK_TABLES) + return; - prev= &thd->open_tables; - for (table=thd->open_tables; table ; table=next) - { - uint db_stat=table->db_stat; - next=table->next; - DBUG_PRINT("tcache", ("open table: '%s'.'%s' 0x%lx " - "parent: 0x%lx db_stat: %u", - table->s->db.str, table->s->table_name.str, - (long) table, (long) table->parent, db_stat)); - if (table->child_l && !db_stat) - merge_table_found= TRUE; - if (!tables || (!db_stat && reopen_table(table))) - { - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); - /* - If we could not allocate 'tables', we may close open tables - here. If a MERGE table is affected, detach the children first. - It is not necessary to clear the child or parent table reference - of this table because the TABLE is freed. But we need to clear - the child or parent references of the other belonging tables so - that they cannot be moved into the unused_tables chain with - these pointers set. - */ - if (table->child_l || table->parent) - detach_merge_children(table, TRUE); - free_cache_entry(table); - error=1; - } - else - { - DBUG_PRINT("tcache", ("opened. need lock: %d", - get_locks && !db_stat && !table->parent)); - *prev= table; - prev= &table->next; - /* Do not handle locks of MERGE children. */ - if (get_locks && !db_stat && !table->parent) - { - *tables_ptr++= table; // need new lock on this - /* - We rely on having exclusive metadata lock on the table to be - able safely re-acquire table locks on it. - */ - DBUG_ASSERT(mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, - table->s->db.str, - table->s->table_name.str)); - } - } - } - *prev=0; - /* - When all tables are open again, we can re-attach MERGE children to - their parents. All TABLE objects are still present. - */ - DBUG_PRINT("tcache", ("re-attaching MERGE tables: %d", merge_table_found)); - if (!error && merge_table_found && reattach_merge(thd, &err_tables)) - { - while (err_tables) - { - err_tab_tmp= err_tables->next; - free_cache_entry(err_tables); - err_tables= err_tab_tmp; - } - } - DBUG_PRINT("tcache", ("open tables to lock: %u", - (uint) (tables_ptr - tables))); - if (tables != tables_ptr) // Should we get back old locks - { - MYSQL_LOCK *lock; - const uint flags= MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | - MYSQL_LOCK_IGNORE_FLUSH; - /* - Since we have exclusive metadata locks on tables which we - are reopening we should always get these locks (We won't - wait on table level locks so can't get aborted and we ignore - other threads that set THD::some_tables_deleted by using - MYSQL_LOCK_IGNORE_FLUSH flag). - */ - thd->some_tables_deleted=0; - if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), - flags, ¬_used))) - { - thd->lock= mysql_lock_merge(thd->lock, lock); - } - else + for (TABLE_LIST *table_list= m_locked_tables; + table_list; table_list= table_list->next_global) { /* - This case should only happen if there is a bug in the reopen logic. - Need to issue error message to have a reply for the application. - Not exactly what happened though, but close enough. + Clear the position in the list, the TABLE object will be + returned to the table cache. */ - my_error(ER_LOCK_DEADLOCK, MYF(0)); - error=1; + table_list->table->pos_in_locked_tables= NULL; } + thd->locked_tables_mode= LTM_NONE; + + close_thread_tables(thd); } - if (get_locks && tables) - { - my_afree((uchar*) tables); - } - broadcast_refresh(); - DBUG_RETURN(error); + /* + After closing tables we can free memory used for storing lock + request for metadata locks and TABLE_LIST elements. + */ + free_root(&m_locked_tables_root, MYF(0)); + m_locked_tables= NULL; + m_locked_tables_last= &m_locked_tables; } /** - Unlock and close tables open and locked by LOCK TABLES statement. + Unlink a locked table from the locked tables list, either + temporarily or permanently. - @param thd Current thread's context. + @param thd thread handle + @param table_list the element of locked tables list. + The implementation assumes that this argument + points to a TABLE_LIST element linked into + the locked tables list. Passing a TABLE_LIST + instance that is not part of locked tables + list will lead to a crash. + @parma remove_from_locked_tables + TRUE if the table is removed from the list + permanently. + + This function is a no-op if we're not under LOCK TABLES. + + @sa Locked_tables_list::reopen_tables() */ -void unlock_locked_tables(THD *thd) -{ - DBUG_ASSERT(!thd->in_sub_stmt && - !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); +void Locked_tables_list::unlink_from_list(THD *thd, + TABLE_LIST *table_list, + bool remove_from_locked_tables) +{ /* - Sic: we must be careful to not close open tables if - we're not in LOCK TABLES mode: unlock_locked_tables() is - sometimes called implicitly, expecting no effect on - open tables, e.g. from begin_trans(). + If mode is not LTM_LOCK_TABLES, we needn't do anything. Moreover, + outside this mode pos_in_locked_tables value is not trustworthy. */ if (thd->locked_tables_mode != LTM_LOCK_TABLES) return; - thd->locked_tables_mode= LTM_NONE; - close_thread_tables(thd); /* - After closing tables we can free memory used for storing lock - request objects for metadata locks + table_list must be set and point to pos_in_locked_tables of some + table. */ - free_root(&thd->locked_tables_root, MYF(MY_MARK_BLOCKS_FREE)); + DBUG_ASSERT(table_list->table->pos_in_locked_tables == table_list); + + /* Clear the pointer, the table will be returned to the table cache. */ + table_list->table->pos_in_locked_tables= NULL; + + /* Mark the table as closed in the locked tables list. */ + table_list->table= NULL; + + /* + If the table is being dropped or renamed, remove it from + the locked tables list (implicitly drop the LOCK TABLES lock + on it). + */ + if (remove_from_locked_tables) + { + *table_list->prev_global= table_list->next_global; + if (table_list->next_global == NULL) + m_locked_tables_last= table_list->prev_global; + else + table_list->next_global->prev_global= table_list->prev_global; + } +} + +/** + This is an attempt to recover (somewhat) in case of an error. + If we failed to reopen a closed table, let's unlink it from the + list and forget about it. From a user perspective that would look + as if the server "lost" the lock on one of the locked tables. + + @note This function is a no-op if we're not under LOCK TABLES. +*/ + +void Locked_tables_list::unlink_all_closed_tables() +{ + for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list= + table_list->next_global) + { + if (table_list->table == NULL) + { + /* Unlink from list. */ + *table_list->prev_global= table_list->next_global; + if (table_list->next_global == NULL) + m_locked_tables_last= table_list->prev_global; + else + table_list->next_global->prev_global= table_list->prev_global; + } + } +} + + +/** + Reopen the tables locked with LOCK TABLES and temporarily closed + by a DDL statement or FLUSH TABLES. + + @note This function is a no-op if we're not under LOCK TABLES. + + @return TRUE if an error reopening the tables. May happen in + case of some fatal system error only, e.g. a disk + corruption, out of memory or a serious bug in the + locking. +*/ + +bool +Locked_tables_list::reopen_tables(THD *thd) +{ + enum enum_open_table_action ot_action_unused; + bool lt_refresh_unused; + + for (TABLE_LIST *table_list= m_locked_tables; + table_list; table_list= table_list->next_global) + { + MYSQL_LOCK *lock; + + if (table_list->table) /* The table was not closed */ + continue; + + /* Links into thd->open_tables upon success */ + if (open_table(thd, table_list, thd->mem_root, &ot_action_unused, + MYSQL_OPEN_REOPEN)) + { + unlink_all_closed_tables(); + return TRUE; + } + table_list->table->pos_in_locked_tables= table_list; + /* See also the comment on lock type in init_locked_tables(). */ + table_list->table->reginfo.lock_type= table_list->lock_type; + thd->in_lock_tables= 1; + lock= mysql_lock_tables(thd, &table_list->table, 1, + MYSQL_OPEN_REOPEN, <_refresh_unused); + thd->in_lock_tables= 0; + if (lock) + lock= mysql_lock_merge(thd->lock, lock); + if (lock == NULL) + { + /* + No one's seen this branch work. Recover and report an + error just in case. + */ + pthread_mutex_lock(&LOCK_open); + close_thread_table(thd, &thd->open_tables); + pthread_mutex_unlock(&LOCK_open); + table_list->table= 0; + unlink_all_closed_tables(); + my_error(ER_LOCK_DEADLOCK, MYF(0)); + return TRUE; + } + thd->lock= lock; + } + return FALSE; } @@ -3759,147 +3341,6 @@ err: } -/** - Load table definition from file and open table while holding exclusive - meta-data lock on it. - - @param thd Thread handle - @param entry Memory for TABLE object to be created - @param table_list TABLE_LIST with db, table_name & belong_to_view - @param alias Alias name - @param cache_key Key for table definition cache - @param cache_key_length Length of cache_key - - @note This auxiliary function is mostly inteded for re-opening table - in situations when we hold exclusive meta-data lock. It is not - intended for normal case in which we have only shared meta-data - lock on the table to be open. - - @note Extra argument for open is taken from thd->open_options. - - @note One must have a lock on LOCK_open as well as exclusive meta-data - lock on the table when calling this function. - - @return FALSE in case of success, TRUE otherwise. -*/ - -static bool reopen_table_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, - const char *alias, char *cache_key, - uint cache_key_length) -{ - int error; - TABLE_SHARE *share; - uint discover_retry_count= 0; - DBUG_ENTER("reopen_table_entry"); - - safe_mutex_assert_owner(&LOCK_open); - DBUG_ASSERT(mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, - table_list->db, - table_list->table_name)); - -retry: - if (!(share= get_table_share_with_create(thd, table_list, cache_key, - cache_key_length, - OPEN_VIEW | - table_list->i_s_requested_object, - &error))) - DBUG_RETURN(1); - - if (share->is_view) - { - /* - If parent_l of the table_list is non null then a merge table - has this view as child table, which is not supported. - */ - if (table_list->parent_l) - { - my_error(ER_WRONG_MRG_TABLE, MYF(0)); - goto err; - } - - /* - This table is a view. Validate its metadata version: in particular, - that it was a view when the statement was prepared. - */ - if (check_and_update_table_version(thd, table_list, share)) - goto err; - if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) - goto err; - /* Attempt to reopen view will bring havoc to upper layers anyway. */ - release_table_share(share); - my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, - "BASE TABLE"); - DBUG_RETURN(1); - } - - if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) - goto err; - - while ((error= open_table_from_share(thd, share, alias, - (uint) (HA_OPEN_KEYFILE | - HA_OPEN_RNDFILE | - HA_GET_INDEX | - HA_TRY_READ_ONLY), - (READ_KEYINFO | COMPUTE_TYPES | - EXTRA_RECORD), - thd->open_options, entry, FALSE))) - { - if (error == 7) // Table def changed - { - share->version= 0; // Mark share as old - if (discover_retry_count++) // Retry once - goto err; - - /* - Since we have exclusive metadata lock on the table here the only - practical case when share->ref_count != 1 is when we have several - instances of the table opened by this thread (i.e we are under LOCK - TABLES). - */ - if (share->ref_count != 1) - goto err; - - release_table_share(share); - - if (ha_create_table_from_engine(thd, table_list->db, - table_list->table_name)) - goto err; - - thd->warning_info->clear_warning_info(thd->query_id); - thd->clear_error(); // Clear error message - goto retry; - } - if (!entry->s || !entry->s->crashed) - goto err; - - entry->s->version= 0; - - /* TODO: We don't need to release share here. */ - release_table_share(share); - pthread_mutex_unlock(&LOCK_open); - error= (int)auto_repair_table(thd, table_list); - pthread_mutex_lock(&LOCK_open); - - if (error) - goto err; - - goto retry; - } - - if (open_table_entry_fini(thd, share, entry)) - { - closefrm(entry, 0); - goto err; - } - - DBUG_RETURN(0); - -err: - release_table_share(share); - DBUG_RETURN(1); -} - - /** Finalize the process of TABLE creation by loading table triggers and taking action if a HEAP table content was emptied implicitly. @@ -8791,24 +8232,10 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b) int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) { - DBUG_ENTER("abort_and_upgrade_locks"); + DBUG_ENTER("abort_and_upgrade_lock"); - lpt->old_lock_type= lpt->table->reginfo.lock_type; - /* If MERGE child, forward lock handling to parent. */ - mysql_lock_abort(lpt->thd, lpt->table->parent ? lpt->table->parent : - lpt->table, TRUE); - if (mdl_upgrade_shared_lock_to_exclusive(&lpt->thd->mdl_context, - lpt->table->mdl_lock_data)) - { - mysql_lock_downgrade_write(lpt->thd, - lpt->table->parent ? lpt->table->parent : - lpt->table, - lpt->old_lock_type); + if (wait_while_table_is_used(lpt->thd, lpt->table, HA_EXTRA_FORCE_REOPEN)) DBUG_RETURN(1); - } - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(lpt->thd, TDC_RT_REMOVE_NOT_OWN, lpt->db, lpt->table_name); - pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 1f0c8321608..d36ea5c52c9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -469,7 +469,7 @@ THD::THD() debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ main_warning_info(0), - mdl_el_root(NULL) + locked_tables_root(NULL) { ulong tmp; @@ -574,8 +574,6 @@ THD::THD() thr_lock_owner_init(&main_lock_id, &lock_info); m_internal_handler= NULL; - - init_sql_alloc(&locked_tables_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); } @@ -995,7 +993,7 @@ void THD::cleanup(void) ha_rollback(this); xid_cache_delete(&transaction.xid_state); } - unlock_locked_tables(this); + locked_tables_list.unlock_locked_tables(this); #if defined(ENABLED_DEBUG_SYNC) /* End the Debug Sync Facility. See debug_sync.cc. */ @@ -1074,7 +1072,6 @@ THD::~THD() #endif free_root(&main_mem_root, MYF(0)); - free_root(&locked_tables_root, MYF(0)); DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 0fa3231e458..68ae3afe931 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1169,6 +1169,58 @@ private: Internal_error_handler *m_err_handler; }; +/** + Tables that were locked with LOCK TABLES statement. + + Encapsulates a list of TABLE_LIST instances for tables + locked by LOCK TABLES statement, memory root for metadata locks, + and, generally, the context of LOCK TABLES statement. + + In LOCK TABLES mode, the locked tables are kept open between + statements. + Therefore, we can't allocate metadata locks on execution memory + root -- as well as tables, the locks need to stay around till + UNLOCK TABLES is called. + The locks are allocated in the memory root encapsulate in this + class. + + Some SQL commands, like FLUSH TABLE or ALTER TABLE, demand that + the tables they operate on are closed, at least temporarily. + This class encapsulates a list of TABLE_LIST instances, one + for each base table from LOCK TABLES list, + which helps conveniently close the TABLEs when it's necessary + and later reopen them. + + Implemented in sql_base.cc +*/ + +class Locked_tables_list +{ +private: + MEM_ROOT m_locked_tables_root; + TABLE_LIST *m_locked_tables; + TABLE_LIST **m_locked_tables_last; +public: + Locked_tables_list() + :m_locked_tables(NULL), + m_locked_tables_last(&m_locked_tables) + { + init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0); + } + void unlock_locked_tables(THD *thd); + ~Locked_tables_list() + { + unlock_locked_tables(0); + } + bool init_locked_tables(THD *thd); + TABLE_LIST *locked_tables() { return m_locked_tables; } + MEM_ROOT *locked_tables_root() { return &m_locked_tables_root; } + void unlink_from_list(THD *thd, TABLE_LIST *table_list, + bool remove_from_locked_tables); + void unlink_all_closed_tables(); + bool reopen_tables(THD *thd); +}; + /** Storage engine specific thread local data. @@ -1810,6 +1862,8 @@ public: */ Parser_state *m_parser_state; + Locked_tables_list locked_tables_list; + #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *work_part_info; #endif @@ -1819,8 +1873,13 @@ public: struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ - MEM_ROOT *mdl_el_root; - MEM_ROOT locked_tables_root; + /** + Points to the memory root of Locked_tables_list if + we're locking the tables for LOCK TABLES. Otherwise is NULL. + This is necessary to ensure that metadata locks allocated for + tables used in triggers will persist after statement end. + */ + MEM_ROOT *locked_tables_root; THD(); ~THD(); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index b5c51601faf..1b61a96d7ff 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1956,8 +1956,8 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) /* Step7: drop the old database. - remove_db_from_cache(olddb) and query_cache_invalidate(olddb) - are done inside mysql_rm_db(), no needs to execute them again. + query_cache_invalidate(olddb) is done inside mysql_rm_db(), no need + to execute them again. mysql_rm_db() also "unuses" if we drop the current database. */ error= mysql_rm_db(thd, old_db->str, 0, 1); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 00bb013218d..e0199fcdf20 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3542,16 +3542,22 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, create_table)) + enum enum_open_table_action ot_action_unused; + /* + Here we open the destination table, on which we already have + an exclusive metadata lock. + */ + if (open_table(thd, create_table, thd->mem_root, + &ot_action_unused, MYSQL_OPEN_REOPEN)) { + pthread_mutex_lock(&LOCK_open); quick_rm_table(create_info->db_type, create_table->db, table_case_name(create_info, create_table->table_name), 0); + pthread_mutex_unlock(&LOCK_open); } else table= create_table->table; - pthread_mutex_unlock(&LOCK_open); } else { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 95f142bc8a2..9de07e90560 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -184,7 +184,7 @@ bool begin_trans(THD *thd) return 1; } - unlock_locked_tables(thd); + thd->locked_tables_list.unlock_locked_tables(thd); if (end_active_trans(thd)) error= -1; @@ -3583,7 +3583,7 @@ end_with_restore_list: done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes false, mysqldump will not work. */ - unlock_locked_tables(thd); + thd->locked_tables_list.unlock_locked_tables(thd); if (thd->options & OPTION_TABLE_LOCK) { end_active_trans(thd); @@ -3594,7 +3594,7 @@ end_with_restore_list: my_ok(thd); break; case SQLCOM_LOCK_TABLES: - unlock_locked_tables(thd); + thd->locked_tables_list.unlock_locked_tables(thd); /* we must end the trasaction first, regardless of anything */ if (end_active_trans(thd)) goto error; @@ -3604,24 +3604,23 @@ end_with_restore_list: if (lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) goto error; - thd->in_lock_tables=1; - thd->options|= OPTION_TABLE_LOCK; - alloc_mdl_locks(all_tables, &thd->locked_tables_root); - thd->mdl_el_root= &thd->locked_tables_root; - if (!(res= open_and_lock_tables_derived(thd, all_tables, FALSE, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) + alloc_mdl_locks(all_tables, thd->locked_tables_list.locked_tables_root()); + + thd->options|= OPTION_TABLE_LOCK; + thd->in_lock_tables=1; + thd->locked_tables_root= thd->locked_tables_list.locked_tables_root(); + + res= (open_and_lock_tables_derived(thd, all_tables, FALSE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL) || + thd->locked_tables_list.init_locked_tables(thd)); + + thd->in_lock_tables= 0; + thd->locked_tables_root= NULL; + + if (res) { -#ifdef HAVE_QUERY_CACHE - if (thd->variables.query_cache_wlock_invalidate) - query_cache.invalidate_locked_for_write(first_table); -#endif /*HAVE_QUERY_CACHE*/ - thd->locked_tables_mode= LTM_LOCK_TABLES; - my_ok(thd); - } - else - { - /* + /* Need to end the current transaction, so the storage engine (InnoDB) can free its locks if LOCK TABLES locked some tables before finding that it can't lock a table in its list @@ -3630,8 +3629,14 @@ end_with_restore_list: end_active_trans(thd); thd->options&= ~(OPTION_TABLE_LOCK); } - thd->in_lock_tables=0; - thd->mdl_el_root= 0; + else + { +#ifdef HAVE_QUERY_CACHE + if (thd->variables.query_cache_wlock_invalidate) + query_cache.invalidate_locked_for_write(first_table); +#endif /*HAVE_QUERY_CACHE*/ + my_ok(thd); + } break; case SQLCOM_CREATE_DB: { @@ -6534,8 +6539,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); ptr->mdl_lock_data= mdl_alloc_lock(0 , ptr->db, ptr->table_name, - thd->mdl_el_root ? thd->mdl_el_root : - thd->mem_root); + thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); DBUG_RETURN(ptr); } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 75ab9a73a5b..f4525db7def 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4314,7 +4314,6 @@ set_engine_all_partitions(partition_info *part_info, static int fast_end_partition(THD *thd, ulonglong copied, ulonglong deleted, - TABLE *table, TABLE_LIST *table_list, bool is_empty, ALTER_PARTITION_PARAM_TYPE *lpt, bool written_bin_log) @@ -4333,11 +4332,7 @@ static int fast_end_partition(THD *thd, ulonglong copied, error= 1; if (error) - { - /* If error during commit, no need to rollback, it's done. */ - table->file->print_error(error, MYF(0)); - DBUG_RETURN(TRUE); - } + DBUG_RETURN(TRUE); /* The error has been reported */ if ((!is_empty) && (!written_bin_log) && (!thd->lex->no_write_to_binlog)) @@ -6215,30 +6210,13 @@ static void release_log_entries(partition_info *part_info) */ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) { - int err; - if (lpt->thd->locked_tables_mode) - { - /* - When we have the table locked, it is necessary to reopen the table - since all table objects were closed and removed as part of the - ALTER TABLE of partitioning structure. - */ - pthread_mutex_lock(&LOCK_open); - lpt->thd->in_lock_tables= 1; - err= reopen_tables(lpt->thd, 1); - lpt->thd->in_lock_tables= 0; - if (err) - { - /* - Issue a warning since we weren't able to regain the lock again. - We also need to unlink table from thread's open list and from - table_cache - */ - unlink_open_table(lpt->thd, lpt->table, FALSE); - sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); - } - pthread_mutex_unlock(&LOCK_open); - } + THD *thd= lpt->thd; + + close_all_tables_for_name(thd, lpt->table->s, FALSE); + lpt->table= 0; + lpt->table_list->table= 0; + if (thd->locked_tables_list.reopen_tables(thd)) + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); } /* @@ -6252,17 +6230,37 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt) { + TABLE_SHARE *share= lpt->table->s; THD *thd= lpt->thd; - const char *db= lpt->db; - const char *table_name= lpt->table_name; + TABLE *table; DBUG_ENTER("alter_close_tables"); /* - We need to also unlock tables and close all handlers. - We set lock to zero to ensure we don't do this twice - and we set db_stat to zero to ensure we don't close twice. + We must keep LOCK_open while manipulating with thd->open_tables. + Another thread may be working on it. */ pthread_mutex_lock(&LOCK_open); - close_data_files_and_leave_as_placeholders(thd, db, table_name); + /* + We can safely remove locks for all tables with the same name: + later they will all be closed anyway in + alter_partition_lock_handling(). + */ + for (table= thd->open_tables; table ; table= table->next) + { + if (!strcmp(table->s->table_name.str, share->table_name.str) && + !strcmp(table->s->db.str, share->db.str)) + { + mysql_lock_remove(thd, thd->lock, table); + table->file->close(); + table->db_stat= 0; // Mark file closed + /* + Ensure that we won't end up with a crippled table instance + in the table cache if an error occurs before we reach + alter_partition_lock_handling() and the table is closed + by close_thread_tables() instead. + */ + table->s->version= 0; + } + } pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -6429,6 +6427,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, DBUG_ENTER("fast_alter_partition_table"); lpt->thd= thd; + lpt->table_list= table_list; lpt->part_info= part_info; lpt->alter_info= alter_info; lpt->create_info= create_info; @@ -6745,7 +6744,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, user */ DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, - table, table_list, FALSE, NULL, + table_list, FALSE, NULL, written_bin_log)); err: close_thread_tables(thd); diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 5251a50cab9..6d25b0c2b59 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -224,7 +224,8 @@ bool servers_reload(THD *thd) bool return_val= TRUE; DBUG_ENTER("servers_reload"); - unlock_locked_tables(thd); // Can't have locked tables here + /* Can't have locked tables here */ + thd->locked_tables_list.unlock_locked_tables(thd); DBUG_PRINT("info", ("locking servers_cache")); rw_wrlock(&THR_LOCK_servers); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2c91ac69f9f..52596f417b2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1966,7 +1966,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, case -1: DBUG_ASSERT(thd->in_sub_stmt); error= 1; - goto err_with_placeholders; + goto err; default: // temporary table not found error= 0; @@ -2003,18 +2003,19 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, { if (thd->locked_tables_mode) { - if (close_cached_table(thd, table->table)) + if (wait_while_table_is_used(thd, table->table, HA_EXTRA_FORCE_REOPEN)) { error= -1; - goto err_with_placeholders; + goto err; } + close_all_tables_for_name(thd, table->table->s, TRUE); table->table= 0; } if (thd->killed) { error= -1; - goto err_with_placeholders; + goto err; } alias= (lower_case_table_names == 2) ? table->alias : table->table_name; /* remove .frm file and engine files */ @@ -2178,7 +2179,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, */ } } -err_with_placeholders: +err: if (!drop_temporary) { /* @@ -2195,7 +2196,7 @@ err_with_placeholders: if (thd->locked_tables_mode && thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) { - unlock_locked_tables(thd); + thd->locked_tables_list.unlock_locked_tables(thd); goto end; } for (table= tables; table; table= table->next_local) @@ -4315,6 +4316,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, HA_CHECK_OPT *check_opt) { MDL_LOCK_DATA *mdl_lock_data= 0; + enum enum_open_table_action ot_action_unused; DBUG_ENTER("prepare_for_restore"); if (table->table) // do not overwrite existing tables on restore @@ -4360,16 +4362,16 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed generating table from .frm file")); } + table->mdl_lock_data= mdl_lock_data; } /* Now we should be able to open the partially restored table to finish the restore in the handler later on */ - pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, table)) + if (open_table(thd, table, thd->mem_root, + &ot_action_unused, MYSQL_OPEN_REOPEN)) { - pthread_mutex_unlock(&LOCK_open); if (mdl_lock_data) mdl_release_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(send_check_errmsg(thd, table, "restore", @@ -4377,7 +4379,6 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, } /* A MERGE table must not come here. */ DBUG_ASSERT(!table->table || !table->table->child_l); - pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -4392,7 +4393,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, const char **ext; MY_STAT stat_info; MDL_LOCK_DATA *mdl_lock_data; + enum enum_open_table_action ot_action_unused; DBUG_ENTER("prepare_for_repair"); + uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH | + MYSQL_OPEN_HAS_MDL_LOCK); if (!(check_opt->sql_flags & TT_USEFRM)) DBUG_RETURN(0); @@ -4428,8 +4432,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); // Out of memory } - table= &tmp_table; pthread_mutex_unlock(&LOCK_open); + table= &tmp_table; + table_list->mdl_lock_data= mdl_lock_data; } else { @@ -4490,8 +4495,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, Table was successfully open in mysql_admin_table(). Now we need to close it, but leave it protected by exclusive metadata lock. */ - if (close_cached_table(thd, table)) + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto end; + close_all_tables_for_name(thd, table_list->table->s, FALSE); table_list->table= 0; } /* @@ -4519,21 +4525,23 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, goto end; } + if (thd->locked_tables_list.reopen_tables(thd)) + goto end; + /* Now we should be able to open the partially repaired table to finish the repair in the handler later on. */ - pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, table_list)) + if (open_table(thd, table_list, thd->mem_root, + &ot_action_unused, reopen_for_repair_flags)) { - pthread_mutex_unlock(&LOCK_open); error= send_check_errmsg(thd, table_list, "repair", "Failed to open partially repaired table"); goto end; } - pthread_mutex_unlock(&LOCK_open); end: + thd->locked_tables_list.unlink_all_closed_tables(); if (table == &tmp_table) { pthread_mutex_lock(&LOCK_open); @@ -5334,6 +5342,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, db, table_name, reg_ext, 0); if (!access(dst_path, F_OK)) goto table_exists; + /* + Make the metadata lock available to open_table() called to + reopen the table down the road. + */ + table->mdl_lock_data= target_lock_data; } DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); @@ -5463,20 +5476,16 @@ binlog: char buf[2048]; String query(buf, sizeof(buf), system_charset_info); query.length(0); // Have to zero it since constructor doesn't - + enum enum_open_table_action ot_action_unused; /* Here we open the destination table, on which we already have - exclusive metada lock. This is needed for store_create_info() - to work. The table will be closed by unlink_open_table() at - the end of this function. + exclusive metadata lock. This is needed for store_create_info() + to work. The table will be closed by close_thread_table() at + the end of this branch. */ - pthread_mutex_lock(&LOCK_open); - if (reopen_name_locked_table(thd, table)) - { - pthread_mutex_unlock(&LOCK_open); + if (open_table(thd, table, thd->mem_root, &ot_action_unused, + MYSQL_OPEN_REOPEN)) goto err; - } - pthread_mutex_unlock(&LOCK_open); int result __attribute__((unused))= store_create_info(thd, table, &query, @@ -5485,8 +5494,14 @@ binlog: DBUG_ASSERT(result == 0); // store_create_info() always return 0 write_bin_log(thd, TRUE, query.ptr(), query.length()); + DBUG_ASSERT(thd->open_tables == table->table); pthread_mutex_lock(&LOCK_open); - unlink_open_table(thd, table->table, FALSE); + /* + When opening the table, we ignored the locked tables + (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without + risking to close some locked table. + */ + close_thread_table(thd, &thd->open_tables); pthread_mutex_unlock(&LOCK_open); } else // Case 1 @@ -6789,13 +6804,14 @@ view_err: /* Then do a 'simple' rename of the table. First we need to close all instances of 'source' table. - Note that if close_cached_table() returns error here (i.e. if + Note that if wait_while_table_is_used() returns error here (i.e. if this thread was killed) then it must be that previous step of - simple rename did nothing and therefore we can safely reture + simple rename did nothing and therefore we can safely return without additional clean-up. */ - if (close_cached_table(thd, table)) + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto err; + close_all_tables_for_name(thd, table->s, TRUE); /* Then, we want check once again that target table does not exist. Actually the order of these two steps does not matter since @@ -7384,11 +7400,12 @@ view_err: if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) goto err_new_table_cleanup; - pthread_mutex_lock(&LOCK_open); - close_data_files_and_leave_as_placeholders(thd, db, table_name); + close_all_tables_for_name(thd, table->s, + new_name != table_name || new_db != db); error=0; + table_list->table= table= 0; /* Safety */ save_old_db_type= old_db_type; /* @@ -7410,6 +7427,7 @@ view_err: /* This type cannot happen in regular ALTER. */ new_db_type= old_db_type= NULL; } + pthread_mutex_lock(&LOCK_open); if (mysql_rename_table(old_db_type, db, table_name, db, old_name, FN_TO_IS_TMP)) { @@ -7433,10 +7451,15 @@ view_err: FN_FROM_IS_TMP); } + if (! error) + (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); + + pthread_mutex_unlock(&LOCK_open); + if (error) { /* This shouldn't happen. But let us play it safe. */ - goto err_with_placeholders; + goto err_with_mdl; } if (need_copy_table == ALTER_TABLE_METADATA_ONLY) @@ -7446,6 +7469,7 @@ view_err: To do this we need to obtain a handler object for it. NO need to tamper with MERGE tables. The real open is done later. */ + enum enum_open_table_action ot_action_unused; TABLE *t_table; if (new_name != table_name || new_db != db) { @@ -7454,51 +7478,39 @@ view_err: table_list->table_name_length= strlen(new_name); table_list->db= new_db; table_list->db_length= strlen(new_db); - if (reopen_name_locked_table(thd, table_list)) - goto err_with_placeholders; - t_table= table_list->table; + table_list->mdl_lock_data= target_lock_data; } else { - if (reopen_table(table)) - goto err_with_placeholders; - t_table= table; + /* + Under LOCK TABLES, we have a different mdl_lock_data + points to a different instance than the one set initially + to request the lock. + */ + table_list->mdl_lock_data= mdl_lock_data; } - /* Tell the handler that a new frm file is in place. */ - if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG, - create_info)) - goto err_with_placeholders; - if (thd->locked_tables_mode) + if (open_table(thd, table_list, thd->mem_root, + &ot_action_unused, MYSQL_OPEN_REOPEN)) { - if (new_name == table_name && new_db == db) - { - /* - We are going to reopen table down on the road, so we have to restore - state of the TABLE object which we used for obtaining of handler - object to make it suitable for reopening. - */ - DBUG_ASSERT(t_table == table); - close_handle_and_leave_table_as_placeholder(table); - } - else - { - /* Unlink the new name from the list of locked tables. */ - unlink_open_table(thd, t_table, FALSE); - } + goto err_with_mdl; } - } + t_table= table_list->table; - (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); + /* Tell the handler that a new frm file is in place. */ + error= t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG, + create_info); + + DBUG_ASSERT(thd->open_tables == t_table); + pthread_mutex_lock(&LOCK_open); + close_thread_table(thd, &thd->open_tables); + pthread_mutex_unlock(&LOCK_open); + table_list->table= 0; - if (thd->locked_tables_mode && new_name == table_name && new_db == db) - { - thd->in_lock_tables= 1; - error= reopen_tables(thd, 1); - thd->in_lock_tables= 0; if (error) - goto err_with_placeholders; + goto err_with_mdl; } - pthread_mutex_unlock(&LOCK_open); + if (thd->locked_tables_list.reopen_tables(thd)) + goto err_with_mdl; thd_proc_info(thd, "end"); @@ -7541,9 +7553,6 @@ view_err: { if ((new_name != table_name || new_db != db)) { - pthread_mutex_lock(&LOCK_open); - unlink_open_table(thd, table, FALSE); - pthread_mutex_unlock(&LOCK_open); mdl_release_lock(&thd->mdl_context, target_lock_data); mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); } @@ -7607,14 +7616,14 @@ err: mdl_release_lock(&thd->mdl_context, target_lock_data); DBUG_RETURN(TRUE); -err_with_placeholders: +err_with_mdl: /* An error happened while we were holding exclusive name metadata lock - on table being altered. To be safe under LOCK TABLES we should remove - placeholders from the list of open tables and relese metadata lock. + on table being altered. To be safe under LOCK TABLES we should + remove all references to the altered table from the list of locked + tables and release the exclusive metadata lock. */ - unlink_open_table(thd, table, FALSE); - pthread_mutex_unlock(&LOCK_open); + thd->locked_tables_list.unlink_all_closed_tables(); if (target_lock_data) mdl_release_lock(&thd->mdl_context, target_lock_data); mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index c8f01a56a72..9a42dd189e7 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -328,6 +328,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) bool result= TRUE; String stmt_query; bool need_start_waiting= FALSE; + bool lock_upgrade_done= FALSE; DBUG_ENTER("mysql_create_or_drop_trigger"); @@ -450,72 +451,54 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (!(tables->table= find_write_locked_table(thd->open_tables, tables->db, tables->table_name))) goto end; - /* - Ensure that table is opened only by this thread and that no other - statement will open this table. - */ - if (wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN)) - goto end; - - pthread_mutex_lock(&LOCK_open); + /* Later on we will need it to downgrade the lock */ + tables->mdl_lock_data= tables->table->mdl_lock_data; } else { - /* - Obtain exlusive meta-data lock on the table and remove TABLE - instances from cache. - */ - if (lock_table_names(thd, tables)) + tables->table= open_n_lock_single_table(thd, tables, + TL_WRITE_ALLOW_READ, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL); + if (! tables->table) goto end; - - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tables->db, tables->table_name); - - if (reopen_name_locked_table(thd, tables)) - goto end_unlock; + tables->table->use_all_columns(); } table= tables->table; + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto end; + + lock_upgrade_done= TRUE; + if (!table->triggers) { if (!create) { my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); - goto end_unlock; + goto end; } if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table))) - goto end_unlock; + goto end; } + pthread_mutex_lock(&LOCK_open); result= (create ? table->triggers->create_trigger(thd, tables, &stmt_query): table->triggers->drop_trigger(thd, tables, &stmt_query)); - - /* Under LOCK TABLES we must reopen the table to activate the trigger. */ - if (!result && thd->locked_tables_mode) - { - /* Make table suitable for reopening */ - close_data_files_and_leave_as_placeholders(thd, tables->db, - tables->table_name); - thd->in_lock_tables= 1; - if (reopen_tables(thd, 1)) - { - /* To be safe remove this table from the set of LOCKED TABLES */ - unlink_open_table(thd, tables->table, FALSE); - - /* - Ignore reopen_tables errors for now. It's better not leave master/slave - in a inconsistent state. - */ - thd->clear_error(); - } - thd->in_lock_tables= 0; - } - -end_unlock: pthread_mutex_unlock(&LOCK_open); + if (result) + goto end; + + close_all_tables_for_name(thd, table->s, FALSE); + /* + Reopen the table if we were under LOCK TABLES. + Ignore the return value for now. It's better to + keep master/slave in consistent state. + */ + thd->locked_tables_list.reopen_tables(thd); + end: if (!result) { @@ -525,11 +508,11 @@ end: /* If we are under LOCK TABLES we should restore original state of meta-data locks. Otherwise call to close_thread_tables() will take care about both - TABLE instance created by reopen_name_locked_table() and metadata lock. + TABLE instance created by open_n_lock_single_table() and metadata lock. */ - if (thd->locked_tables_mode && tables && tables->table) + if (thd->locked_tables_mode && tables && lock_upgrade_done) mdl_downgrade_exclusive_lock(&thd->mdl_context, - tables->table->mdl_lock_data); + tables->mdl_lock_data); if (need_start_waiting) start_waiting_global_read_lock(thd); diff --git a/sql/table.h b/sql/table.h index 48caf962894..432523e5db2 100644 --- a/sql/table.h +++ b/sql/table.h @@ -632,7 +632,6 @@ private: TABLE *share_next, **share_prev; friend struct TABLE_share; - friend bool reopen_table(TABLE *table); public: @@ -679,6 +678,8 @@ public: /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; TABLE_LIST *pos_in_table_list;/* Element referring to this table */ + /* Position in thd->locked_table_list under LOCK TABLES */ + TABLE_LIST *pos_in_locked_tables; ORDER *group; const char *alias; /* alias or table name */ uchar *null_flags; From 7d3ad72e2eba4760d77f64e50ca672d83c249d3f Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 18:33:51 +0300 Subject: [PATCH 068/466] Backport of: ---------------------------------------------------------- revno: 2630.4.30 Konstantin Osipov 2008-06-11 Fix a potential cause of test failures. --- sql/sql_base.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 61c071d3430..b1d2ec41367 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2934,7 +2934,7 @@ Locked_tables_list::init_locked_tables(THD *thd) &db, db_len, &table_name, table_name_len, &alias, alias_len, - 0)) + NullS)) { unlock_locked_tables(0); return TRUE; From bf2aae0487919118e370ab83602a72436f21cc34 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 18:37:10 +0300 Subject: [PATCH 069/466] Backport of: ---------------------------------------------------------- revno: 2630.4.31 committer: Konstantin Osipov branch nick: mysql-6.0-3726 timestamp: Thu 2008-06-12 06:23:08 +0400 message: Extend the signature of TABLE_LIST::init_one_table() to initialize lengths This is part of WL#3726 post-review fixes. --- sql/event_db_repository.cc | 8 ++++---- sql/sql_base.cc | 21 +++++++++++---------- sql/table.h | 4 ++++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index b17785a6be7..e9ee54ff8aa 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -554,7 +554,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE_LIST tables; DBUG_ENTER("Event_db_repository::open_event_table"); - tables.init_one_table("mysql", "event", "event", lock_type); + tables.init_one_table("mysql", 5, "event", 5, "event", lock_type); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) @@ -1109,7 +1109,7 @@ Event_db_repository::check_system_tables(THD *thd) /* Check mysql.db */ - tables.init_one_table("mysql", "db", "db", TL_READ); + tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) @@ -1127,7 +1127,7 @@ Event_db_repository::check_system_tables(THD *thd) close_thread_tables(thd); } /* Check mysql.user */ - tables.init_one_table("mysql", "user", "user", TL_READ); + tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) @@ -1148,7 +1148,7 @@ Event_db_repository::check_system_tables(THD *thd) close_thread_tables(thd); } /* Check mysql.event */ - tables.init_one_table("mysql", "event", "event", TL_READ); + tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ); alloc_mdl_locks(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b1d2ec41367..5227c5e9c72 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2924,16 +2924,16 @@ Locked_tables_list::init_locked_tables(THD *thd) { TABLE_LIST *src_table_list= table->pos_in_table_list; char *db, *table_name, *alias; - size_t db_len= strlen(src_table_list->db) + 1; - size_t table_name_len= strlen(src_table_list->table_name) + 1; - size_t alias_len= strlen(src_table_list->alias) + 1; + size_t db_len= src_table_list->db_length; + size_t table_name_len= src_table_list->table_name_length; + size_t alias_len= strlen(src_table_list->alias); TABLE_LIST *dst_table_list; if (! multi_alloc_root(&m_locked_tables_root, &dst_table_list, sizeof(*dst_table_list), - &db, db_len, - &table_name, table_name_len, - &alias, alias_len, + &db, db_len + 1, + &table_name, table_name_len + 1, + &alias, alias_len + 1, NullS)) { unlock_locked_tables(0); @@ -2947,13 +2947,14 @@ Locked_tables_list::init_locked_tables(THD *thd) TL_WRITE_DEFAULT, whereas reginfo.lock_type has been updated from thd->update_lock_default. */ - dst_table_list->init_one_table(db, table_name, alias, + dst_table_list->init_one_table(db, db_len, table_name, table_name_len, + alias, src_table_list->table->reginfo.lock_type); dst_table_list->mdl_lock_data= src_table_list->mdl_lock_data; dst_table_list->table= table; - memcpy(db, src_table_list->db, db_len); - memcpy(table_name, src_table_list->table_name, table_name_len); - memcpy(alias, src_table_list->alias, alias_len); + memcpy(db, src_table_list->db, db_len + 1); + memcpy(table_name, src_table_list->table_name, table_name_len + 1); + memcpy(alias, src_table_list->alias, alias_len + 1); /* Link last into the list of tables */ *(dst_table_list->prev_global= m_locked_tables_last)= dst_table_list; m_locked_tables_last= &dst_table_list->next_global; diff --git a/sql/table.h b/sql/table.h index 432523e5db2..c30b6e7ad80 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1120,13 +1120,17 @@ struct TABLE_LIST simple_open_and_lock_tables */ inline void init_one_table(const char *db_name_arg, + size_t db_length_arg, const char *table_name_arg, + size_t table_name_length_arg, const char *alias_arg, enum thr_lock_type lock_type_arg) { bzero((char*) this, sizeof(*this)); db= (char*) db_name_arg; + db_length= db_length_arg; table_name= (char*) table_name_arg; + table_name_length= table_name_length_arg; alias= (char*) alias_arg; lock_type= lock_type_arg; } From 0a49fd92d90843f9cd49f98bb23a513b3f82b1b9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 19:15:40 +0300 Subject: [PATCH 070/466] Backport of: ------------------------------------------------------------ revno: 2630.4.32 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Thu 2008-06-19 16:39:58 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Ensure that metadata locking subsystem properly handles out-of-memory conditions. Clarified MDL interface by separating release of locks and removal of lock requests from the context. --- sql/lock.cc | 2 +- sql/mdl.cc | 183 ++++++++++++++++++++++++++++----------------- sql/mdl.h | 13 ++-- sql/sql_base.cc | 25 ++++++- sql/sql_delete.cc | 9 +++ sql/sql_handler.cc | 1 + sql/sql_show.cc | 1 + sql/sql_table.cc | 49 ++++++++++-- 8 files changed, 199 insertions(+), 84 deletions(-) diff --git a/sql/lock.cc b/sql/lock.cc index 814eebde337..199ab354c22 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -965,7 +965,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) lock_table->mdl_lock_data= mdl_lock_data; } if (mdl_acquire_exclusive_locks(&thd->mdl_context)) - return 1; + goto end; return 0; end: diff --git a/sql/mdl.cc b/sql/mdl.cc index d3d067cfb9b..afe3f0eaa7b 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -257,7 +257,7 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) Stores the database name, object name and the type in the key buffer. Initializes mdl_el to point to the key. - We can't simply initialize mdl_el with type, db and name + We can't simply initialize MDL_LOCK_DATA with type, db and name by-pointer because of the underlying HASH implementation requires the key to be a contiguous buffer. @@ -275,7 +275,7 @@ void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, lock_data->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; lock_data->key= key; lock_data->type= MDL_SHARED; - lock_data->state= MDL_PENDING; + lock_data->state= MDL_INITIALIZED; #ifndef DBUG_OFF lock_data->ctx= 0; lock_data->lock= 0; @@ -336,7 +336,7 @@ MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) { DBUG_ENTER("mdl_add_lock"); - DBUG_ASSERT(lock_data->state == MDL_PENDING); + DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); DBUG_ASSERT(!lock_data->ctx); lock_data->ctx= context; context->locks.push_front(lock_data); @@ -344,6 +344,36 @@ void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) } +/** + Remove a lock request from the list of lock requests of the context. + + Disassociates a lock request from the given context. + + @param context The MDL context to remove the lock from. + @param lock_data The lock request to be removed. + + @pre The lock request being removed should correspond to lock which + was released or was not acquired. + + @note Resets lock request for lock released back to its initial state + (i.e. sets type to MDL_SHARED). +*/ + +void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +{ + DBUG_ENTER("mdl_remove_lock"); + DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); + DBUG_ASSERT(context == lock_data->ctx); + /* Reset lock request back to its initial state. */ + lock_data->type= MDL_SHARED; +#ifndef DBUG_OFF + lock_data->ctx= 0; +#endif + context->locks.remove(lock_data); + DBUG_VOID_RETURN; +} + + /** Clear all lock requests in the context (clear the context). @@ -629,7 +659,7 @@ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, MDL_LOCK *lock; *retry= FALSE; - DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_PENDING); + DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_INITIALIZED); DBUG_ASSERT(lock_data->ctx == context); @@ -654,7 +684,11 @@ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, if (!(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, lock_data->key_length))) { - lock= get_lock_object(); + if (!(lock= get_lock_object())) + { + pthread_mutex_unlock(&LOCK_mdl); + return TRUE; + } /* Before inserting MDL_LOCK object into hash we should add at least one MDL_LOCK_DATA to its lists in order to provide key for this element. @@ -662,7 +696,12 @@ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, */ lock->active_shared.push_front(lock_data); lock->lock_data_count= 1; - my_hash_insert(&mdl_locks, (uchar*)lock); + if (my_hash_insert(&mdl_locks, (uchar*)lock)) + { + release_lock_object(lock); + pthread_mutex_unlock(&LOCK_mdl); + return TRUE; + } lock_data->state= MDL_ACQUIRED; lock_data->lock= lock; if (lock_data->type == MDL_SHARED_UPGRADABLE) @@ -702,9 +741,6 @@ static void release_lock(MDL_LOCK_DATA *lock_data); @param context A context containing requests for exclusive locks The context may not have other lock requests. - @note In case of failure (for example, if our thread was killed) - resets lock requests back to their initial state (MDL_SHARED) - @retval FALSE Success @retval TRUE Failure */ @@ -735,25 +771,32 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) while ((lock_data= it++)) { DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_PENDING); + lock_data->state == MDL_INITIALIZED); if (!(lock= (MDL_LOCK *) my_hash_search(&mdl_locks, (uchar*)lock_data->key, lock_data->key_length))) { - lock= get_lock_object(); + if (!(lock= get_lock_object())) + goto err; /* Again before inserting MDL_LOCK into hash provide key for it by adding MDL_LOCK_DATA to one of its lists. */ lock->waiting_exclusive.push_front(lock_data); lock->lock_data_count= 1; - my_hash_insert(&mdl_locks, (uchar*)lock); + if (my_hash_insert(&mdl_locks, (uchar*)lock)) + { + release_lock_object(lock); + goto err; + } lock_data->lock= lock; + lock_data->state= MDL_PENDING; } else { lock->waiting_exclusive.push_front(lock_data); lock->lock_data_count++; lock_data->lock= lock; + lock_data->state= MDL_PENDING; } } @@ -806,23 +849,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); } if (thd->killed) - { - /* Remove our pending lock requests from the locks. */ - it.rewind(); - while ((lock_data= it++)) - { - DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_PENDING); - release_lock(lock_data); - /* Return lock request to its initial state. */ - lock_data->type= MDL_SHARED; - context->locks.remove(lock_data); - } - /* Pending requests for shared locks can be satisfied now. */ - pthread_cond_broadcast(&COND_mdl); - thd->exit_cond(old_msg); - return TRUE; - } + goto err; } it.rewind(); while ((lock_data= it++)) @@ -839,6 +866,22 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ thd->exit_cond(old_msg); return FALSE; + +err: + /* + Remove our pending lock requests from the locks. + Ignore those lock requests which were not made MDL_PENDING. + */ + it.rewind(); + while ((lock_data= it++) && lock_data->state == MDL_PENDING) + { + release_lock(lock_data); + lock_data->state= MDL_INITIALIZED; + } + /* May be some pending requests for shared locks can be satisfied now. */ + pthread_cond_broadcast(&COND_mdl); + thd->exit_cond(old_msg); + return TRUE; } @@ -976,50 +1019,56 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, @param context [in] The context containing the lock request @param lock [in] The lock request + @param conflict [out] Indicates that conflicting lock exists - @retval FALSE the lock was granted - @retval TRUE there were conflicting locks. + @retval TRUE Failure either conflicting lock exists or some error + occured (probably OOM). + @retval FALSE Success, lock was acquired. FIXME: Compared to lock_table_name_if_not_cached() it gives sligthly more false negatives. */ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) + MDL_LOCK_DATA *lock_data, + bool *conflict) { MDL_LOCK *lock; DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_PENDING); + lock_data->state == MDL_INITIALIZED); safe_mutex_assert_not_owner(&LOCK_open); + *conflict= FALSE; + pthread_mutex_lock(&LOCK_mdl); if (!(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, lock_data->key_length))) { - lock= get_lock_object(); + if (!(lock= get_lock_object())) + goto err; lock->active_exclusive.push_front(lock_data); lock->lock_data_count= 1; - my_hash_insert(&mdl_locks, (uchar*)lock); + if (my_hash_insert(&mdl_locks, (uchar*)lock)) + { + release_lock_object(lock); + goto err; + } lock_data->state= MDL_ACQUIRED; lock_data->lock= lock; - lock= 0; global_lock.active_intention_exclusive++; + pthread_mutex_unlock(&LOCK_mdl); + return FALSE; } + + /* There is some lock for the object. */ + *conflict= TRUE; + +err: pthread_mutex_unlock(&LOCK_mdl); - - /* - FIXME: We can't leave pending MDL_EXCLUSIVE lock request in the list since - for such locks we assume that they have MDL_LOCK_DATA::lock properly set. - Long term we should clearly define relation between lock types, - presence in the context lists and MDL_LOCK_DATA::lock values. - */ - if (lock) - context->locks.remove(lock_data); - - return lock; + return TRUE; } @@ -1111,11 +1160,12 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) it.rewind(); while ((lock_data= it++)) { - DBUG_ASSERT(lock_data->state == MDL_PENDING); + DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); if (!can_grant_global_lock(lock_data)) break; /* - To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. + To avoid starvation we don't wait if we have a conflict against + request for MDL_EXCLUSIVE lock. */ if (is_shared(lock_data) && (lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, @@ -1149,6 +1199,9 @@ static void release_lock(MDL_LOCK_DATA *lock_data) DBUG_PRINT("enter", ("db=%s name=%s", lock_data->key + 4, lock_data->key + 4 + strlen(lock_data->key + 4) + 1)); + DBUG_ASSERT(lock_data->state == MDL_PENDING || + lock_data->state == MDL_ACQUIRED); + lock= lock_data->lock; if (lock->has_one_lock_data()) { @@ -1184,7 +1237,6 @@ static void release_lock(MDL_LOCK_DATA *lock_data) } break; default: - /* TODO Really? How about problems during lock upgrade ? */ DBUG_ASSERT(0); } lock->lock_data_count--; @@ -1224,10 +1276,10 @@ void mdl_release_locks(MDL_CONTEXT *context) lists. Allows us to avoid problems in open_tables() in case of back-off */ - if (!(is_shared(lock_data) && lock_data->state == MDL_PENDING)) + if (lock_data->state != MDL_INITIALIZED) { release_lock(lock_data); - lock_data->state= MDL_PENDING; + lock_data->state= MDL_INITIALIZED; #ifndef DBUG_OFF lock_data->lock= 0; #endif @@ -1247,13 +1299,10 @@ void mdl_release_locks(MDL_CONTEXT *context) /** Release a lock. - Removes the lock from the context. @param context Context containing lock in question @param lock_data Lock to be released - @note Resets lock request for lock released back to its initial state - (i.e. sets type to MDL_SHARED). */ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) @@ -1263,13 +1312,9 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) pthread_mutex_lock(&LOCK_mdl); release_lock(lock_data); #ifndef DBUG_OFF - lock_data->ctx= 0; lock_data->lock= 0; #endif - lock_data->state= MDL_PENDING; - /* Return lock request to its initial state. */ - lock_data->type= MDL_SHARED; - context->locks.remove(lock_data); + lock_data->state= MDL_INITIALIZED; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1277,17 +1322,15 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) /** Release all locks in the context which correspond to the same name/ - object as this lock request. + object as this lock request, remove lock requests from the context. @param context Context containing locks in question @param lock_data One of the locks for the name/object for which all locks should be released. - - @see mdl_release_lock() */ -void mdl_release_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) +void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data) { MDL_LOCK *lock; I_P_List_iterator it(context->locks); @@ -1306,7 +1349,10 @@ void mdl_release_all_locks_for_name(MDL_CONTEXT *context, { DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); if (lock_data->lock == lock) + { mdl_release_lock(context, lock_data); + mdl_remove_lock(context, lock_data); + } } } @@ -1418,9 +1464,10 @@ bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, int4store(key, type); key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - while ((lock_data= it++) && (lock_data->key_length != key_length || - memcmp(lock_data->key, key, key_length) || - lock_data->state == MDL_PENDING)) + while ((lock_data= it++) && + (lock_data->key_length != key_length || + memcmp(lock_data->key, key, key_length) || + lock_data->state != MDL_ACQUIRED)) continue; return lock_data; diff --git a/sql/mdl.h b/sql/mdl.h index b192980ebaa..92bd83038e5 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -44,7 +44,8 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, /** States which metadata lock request can have. */ -enum enum_mdl_state {MDL_PENDING=0, MDL_ACQUIRED, MDL_PENDING_UPGRADE}; +enum enum_mdl_state {MDL_INITIALIZED=0, MDL_PENDING, + MDL_ACQUIRED, MDL_PENDING_UPGRADE}; /** @@ -152,6 +153,7 @@ void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, MEM_ROOT *root); void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); +void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); void mdl_remove_all_locks(MDL_CONTEXT *context); /** @@ -160,7 +162,7 @@ void mdl_remove_all_locks(MDL_CONTEXT *context); inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) { - DBUG_ASSERT(lock_data->state == MDL_PENDING); + DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); lock_data->type= lock_type; } @@ -170,14 +172,15 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_DATA *lock_data, + bool *conflict); bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); bool mdl_wait_for_locks(MDL_CONTEXT *context); void mdl_release_locks(MDL_CONTEXT *context); -void mdl_release_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); +void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, + MDL_LOCK_DATA *lock_data); void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5227c5e9c72..6c344ad1d4e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2278,9 +2278,9 @@ void table_share_release_hook(void *share) } -/* - A helper function that acquires an MDL lock for a table - being opened. +/** + A helper function that acquires an MDL lock for a table + being opened. */ static bool @@ -2304,7 +2304,10 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, */ mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + { + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); return 1; + } } else { @@ -2327,6 +2330,8 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, { if (retry) *action= OT_BACK_OFF_AND_RETRY; + else + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); return 1; } } @@ -2833,7 +2838,11 @@ err_unlock: release_table_share(share); err_unlock2: pthread_mutex_unlock(&LOCK_open); - mdl_release_lock(&thd->mdl_context, mdl_lock_data); + if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) + { + mdl_release_lock(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + } DBUG_RETURN(TRUE); } @@ -3497,7 +3506,10 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + { + mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); return TRUE; + } pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); ha_create_table_from_engine(thd, table->db, table->table_name); @@ -3506,18 +3518,23 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, thd->warning_info->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); break; case OT_REPAIR: mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + { + mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); return TRUE; + } pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); break; default: DBUG_ASSERT(0); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 630cf73076c..4827d48e1c5 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1170,7 +1170,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + { + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(TRUE); + } pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, table_list->table_name); @@ -1200,12 +1203,18 @@ end: my_ok(thd); // This should return record count } if (mdl_lock_data) + { mdl_release_lock(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + } } else if (error) { if (mdl_lock_data) + { mdl_release_lock(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + } } DBUG_RETURN(error); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index a2c1f0e3782..9b30d8cec12 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -150,6 +150,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) } pthread_mutex_unlock(&LOCK_open); mdl_release_lock(&thd->handler_mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->handler_mdl_context, mdl_lock_data); } else if (tables->table) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f608e233349..c50d74412bf 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3250,6 +3250,7 @@ err_unlock: err: mdl_release_lock(&thd->mdl_context, &mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, &mdl_lock_data); thd->clear_error(); return res; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 52596f417b2..f6eafaacf61 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2208,7 +2208,8 @@ err: and locked and therefore have to remove several metadata lock requests associated with them. */ - mdl_release_all_locks_for_name(&thd->mdl_context, table->mdl_lock_data); + mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, + table->mdl_lock_data); } } } @@ -4080,13 +4081,27 @@ static bool lock_table_name_if_not_cached(THD *thd, const char *db, const char *table_name, MDL_LOCK_DATA **lock_data) { + bool conflict; + if (!(*lock_data= mdl_alloc_lock(0, db, table_name, thd->mem_root))) return TRUE; mdl_set_lock_type(*lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, *lock_data); - if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_data)) + if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_data, + &conflict)) { - *lock_data= 0; + /* + To simplify our life under LOCK TABLES we remove unsatisfied + lock request from the context. + */ + mdl_remove_lock(&thd->mdl_context, *lock_data); + if (!conflict) + { + /* Probably OOM. */ + return TRUE; + } + else + *lock_data= 0; } return FALSE; } @@ -4157,7 +4172,10 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, unlock: if (target_lock_data) + { mdl_release_lock(&thd->mdl_context, target_lock_data); + mdl_remove_lock(&thd->mdl_context, target_lock_data); + } pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) pthread_cond_signal(&COND_refresh); @@ -4416,7 +4434,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + { + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(0); + } pthread_mutex_lock(&LOCK_open); if (!(share= (get_table_share(thd, table_list, key, key_length, 0, @@ -4550,7 +4571,10 @@ end: } /* In case of a temporary table there will be no metadata lock. */ if (error && mdl_lock_data) + { mdl_release_lock(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + } DBUG_RETURN(error); } @@ -5518,7 +5542,10 @@ binlog: err: if (target_lock_data) + { mdl_release_lock(&thd->mdl_context, target_lock_data); + mdl_remove_lock(&thd->mdl_context, target_lock_data); + } DBUG_RETURN(res); } @@ -6876,7 +6903,9 @@ view_err: if (new_name != table_name || new_db != db) { mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, target_lock_data); + mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, + mdl_lock_data); } else mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data); @@ -7554,7 +7583,9 @@ view_err: if ((new_name != table_name || new_db != db)) { mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, target_lock_data); + mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, + mdl_lock_data); } else mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data); @@ -7613,7 +7644,10 @@ err: thd->abort_on_warning= save_abort_on_warning; } if (target_lock_data) + { mdl_release_lock(&thd->mdl_context, target_lock_data); + mdl_remove_lock(&thd->mdl_context, target_lock_data); + } DBUG_RETURN(TRUE); err_with_mdl: @@ -7625,8 +7659,11 @@ err_with_mdl: */ thd->locked_tables_list.unlink_all_closed_tables(); if (target_lock_data) + { mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, target_lock_data); + } + mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(TRUE); } /* mysql_alter_table */ From 870e78c86cd624180930c6fe7d78805915f23cbc Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 2 Dec 2009 17:17:14 +0100 Subject: [PATCH 071/466] Fix "command install() is not scriptable" cmake error --- scripts/install_scripts.cmake.in | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in index 145c5405de3..8d6d17ce1b5 100644 --- a/scripts/install_scripts.cmake.in +++ b/scripts/install_scripts.cmake.in @@ -109,15 +109,26 @@ IF(WIN32) SET(SH_FILES mysql_convert_table_format mysqld_multi) FOREACH(file ${PLIN_FILES}) - CONFIGURE_FILE(${file}.pl.in + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}.pl.in ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) + + FILE(INSTALL DESTINATION + "${CMAKE_INSTALL_PREFIX}/scripts" TYPE FILE + PERMISSIONS OWNER_READ OWNER_WRITE + OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE FILES + "${CMAKE_CURRENT_BINARY_DIR}/${file}.pl") ENDFOREACH() FOREACH(file ${SH_FILES}) - CONFIGURE_FILE(${file}.sh + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts) + FILE(INSTALL DESTINATION + "${CMAKE_INSTALL_PREFIX}/scripts" TYPE FILE + PERMISSIONS OWNER_READ OWNER_WRITE + OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE FILES + "${CMAKE_CURRENT_BINARY_DIR}/${file}.pl") ENDFOREACH() ELSE() # On Unix, most of the files end up in the bin directory From edddfa0ef45332973e59066ef94075134f48a7ee Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 19:31:57 +0300 Subject: [PATCH 072/466] Backport of: ------------------------------------------------------------ revno: 2630.4.33 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Fri 2008-06-20 17:11:20 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Minimized dependency of mdl.cc on other modules (particularly made it independant of mysql_priv.h) in order to be able write unit tests for metadata locking subsystem. --- sql/ha_ndbcluster_binlog.cc | 2 +- sql/log_event.cc | 2 +- sql/mdl.cc | 136 ++++++++++++++++++++++++------------ sql/mdl.h | 19 +++++ sql/mysql_priv.h | 2 - sql/sql_base.cc | 8 +-- sql/sql_handler.cc | 2 +- sql/sql_show.cc | 4 +- 8 files changed, 120 insertions(+), 55 deletions(-) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 6f0e4498d75..b9ea87aec52 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -141,7 +141,7 @@ static Uint64 *p_latest_trans_gci= 0; static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; static MDL_LOCK_DATA binlog_mdl_lock_data; -static char binlog_mdlkey[MAX_DBKEY_LENGTH]; +static char binlog_mdlkey[MAX_MDLKEY_LENGTH]; /* Helper functions diff --git a/sql/log_event.cc b/sql/log_event.cc index 92de9933181..b3f6fd58f1a 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8074,7 +8074,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, &mdl_lock_data, sizeof(MDL_LOCK_DATA), - &mdlkey, MAX_DBKEY_LENGTH, + &mdlkey, MAX_MDLKEY_LENGTH, NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); diff --git a/sql/mdl.cc b/sql/mdl.cc index afe3f0eaa7b..0a663ad2e11 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -15,13 +15,9 @@ -/* - TODO: Remove this dependency on mysql_priv.h. It's not - trivial step at the moment since currently we access to - some of THD members and use some of its methods here. -*/ -#include "mysql_priv.h" #include "mdl.h" +#include +#include /** @@ -308,7 +304,7 @@ MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, char *key; if (!multi_alloc_root(root, &lock_data, sizeof(MDL_LOCK_DATA), &key, - MAX_DBKEY_LENGTH, NULL)) + MAX_MDLKEY_LENGTH, NULL)) return NULL; mdl_init_lock(lock_data, key, type, db, name); @@ -441,6 +437,58 @@ static bool is_shared(MDL_LOCK_DATA *lock_data) } +/** + Helper functions and macros to be used for killable waiting in metadata + locking subsystem. + + @sa THD::enter_cond()/exit_cond()/killed. + + @note We can't use THD::enter_cond()/exit_cond()/killed directly here + since this will make metadata subsystem dependant on THD class + and thus prevent us from writing unit tests for it. And usage of + wrapper functions to access THD::killed/enter_cond()/exit_cond() + will probably introduce too much overhead. +*/ + +#define MDL_ENTER_COND(A, B) mdl_enter_cond(A, B, __func__, __FILE__, __LINE__) + +static inline const char* mdl_enter_cond(MDL_CONTEXT *context, + st_my_thread_var *mysys_var, + const char *calling_func, + const char *calling_file, + const unsigned int calling_line) +{ + safe_mutex_assert_owner(&LOCK_mdl); + + mysys_var->current_mutex= &LOCK_mdl; + mysys_var->current_cond= &COND_mdl; + + return set_thd_proc_info(context->thd, "Waiting for table", + calling_func, calling_file, calling_line); +} + +#define MDL_EXIT_COND(A, B, C) mdl_exit_cond(A, B, C, __func__, __FILE__, __LINE__) + +static inline void mdl_exit_cond(MDL_CONTEXT *context, + st_my_thread_var *mysys_var, + const char* old_msg, + const char *calling_func, + const char *calling_file, + const unsigned int calling_line) +{ + DBUG_ASSERT(&LOCK_mdl == mysys_var->current_mutex); + + pthread_mutex_unlock(&LOCK_mdl); + pthread_mutex_lock(&mysys_var->mutex); + mysys_var->current_mutex= 0; + mysys_var->current_cond= 0; + pthread_mutex_unlock(&mysys_var->mutex); + + (void) set_thd_proc_info(context->thd, old_msg, calling_func, + calling_file, calling_line); +} + + /** Check if request for the lock on particular object can be satisfied given current state of the global metadata lock. @@ -752,9 +800,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) bool signalled= FALSE; const char *old_msg; I_P_List_iterator it(context->locks); - THD *thd= context->thd; - - DBUG_ASSERT(thd == current_thd); + st_my_thread_var *mysys_var= my_thread_var; safe_mutex_assert_not_owner(&LOCK_open); @@ -766,7 +812,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) pthread_mutex_lock(&LOCK_mdl); - old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + old_msg= MDL_ENTER_COND(context, mysys_var); while ((lock_data= it++)) { @@ -826,8 +872,11 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) !lock->active_shared_waiting_upgrade.is_empty(); while ((conf_lock_data= it++)) + { signalled|= - notify_thread_having_shared_lock(thd, conf_lock_data->ctx->thd); + mysql_notify_thread_having_shared_lock(context->thd, + conf_lock_data->ctx->thd); + } break; } @@ -848,7 +897,7 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) set_timespec(abstime, 10); pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); } - if (thd->killed) + if (mysys_var->abort) goto err; } it.rewind(); @@ -863,8 +912,8 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= NULL; } - /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ - thd->exit_cond(old_msg); + /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ + MDL_EXIT_COND(context, mysys_var, old_msg); return FALSE; err: @@ -880,7 +929,7 @@ err: } /* May be some pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); - thd->exit_cond(old_msg); + MDL_EXIT_COND(context, mysys_var, old_msg); return TRUE; } @@ -907,12 +956,10 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, { MDL_LOCK *lock; const char *old_msg; - THD *thd= context->thd; + st_my_thread_var *mysys_var= my_thread_var; DBUG_ENTER("mdl_upgrade_shared_lock_to_exclusive"); - DBUG_ASSERT(thd == current_thd); - safe_mutex_assert_not_owner(&LOCK_open); DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); @@ -927,7 +974,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, pthread_mutex_lock(&LOCK_mdl); - old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + old_msg= MDL_ENTER_COND(context, mysys_var); lock_data->state= MDL_PENDING_UPGRADE; /* Set type of lock request to the type at which we are aiming. */ @@ -960,8 +1007,11 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, while ((conf_lock_data= it++)) { if (conf_lock_data->ctx != context) - signalled|= notify_thread_having_shared_lock(thd, - conf_lock_data->ctx->thd); + { + signalled|= + mysql_notify_thread_having_shared_lock(context->thd, + conf_lock_data->ctx->thd); + } } if (signalled) @@ -979,7 +1029,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, DBUG_PRINT("info", ("Failed to wake-up from table-level lock ... sleeping")); pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); } - if (thd->killed) + if (mysys_var->abort) { lock_data->state= MDL_ACQUIRED; lock_data->type= MDL_SHARED_UPGRADABLE; @@ -987,7 +1037,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, lock->active_shared.push_front(lock_data); /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); - thd->exit_cond(old_msg); + MDL_EXIT_COND(context, mysys_var, old_msg); DBUG_RETURN(TRUE); } } @@ -999,8 +1049,8 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= 0; - /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ - thd->exit_cond(old_msg); + /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ + MDL_EXIT_COND(context, mysys_var, old_msg); DBUG_RETURN(FALSE); } @@ -1086,32 +1136,31 @@ err: bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) { - THD *thd= context->thd; + st_my_thread_var *mysys_var= my_thread_var; const char *old_msg; safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(thd == current_thd); DBUG_ASSERT(!context->has_global_shared_lock); pthread_mutex_lock(&LOCK_mdl); global_lock.waiting_shared++; - old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + old_msg= MDL_ENTER_COND(context, mysys_var); - while (!thd->killed && global_lock.active_intention_exclusive) + while (!mysys_var->abort && global_lock.active_intention_exclusive) pthread_cond_wait(&COND_mdl, &LOCK_mdl); global_lock.waiting_shared--; - if (thd->killed) + if (mysys_var->abort) { - /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ - thd->exit_cond(old_msg); + /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ + MDL_EXIT_COND(context, mysys_var, old_msg); return TRUE; } global_lock.active_shared++; context->has_global_shared_lock= TRUE; - /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ - thd->exit_cond(old_msg); + /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ + MDL_EXIT_COND(context, mysys_var, old_msg); return FALSE; } @@ -1137,12 +1186,11 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) MDL_LOCK *lock; I_P_List_iterator it(context->locks); const char *old_msg; - THD *thd= context->thd; + st_my_thread_var *mysys_var= my_thread_var; safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(thd == current_thd); - while (!thd->killed) + while (!mysys_var->abort) { /* We have to check if there are some HANDLERs open by this thread @@ -1156,7 +1204,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) */ mysql_ha_flush(context->thd); pthread_mutex_lock(&LOCK_mdl); - old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); + old_msg= MDL_ENTER_COND(context, mysys_var); it.rewind(); while ((lock_data= it++)) { @@ -1179,10 +1227,10 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) break; } pthread_cond_wait(&COND_mdl, &LOCK_mdl); - /* As a side-effect THD::exit_cond() unlocks LOCK_mdl. */ - thd->exit_cond(old_msg); + /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ + MDL_EXIT_COND(context, mysys_var, old_msg); } - return thd->killed; + return mysys_var->abort; } @@ -1422,7 +1470,7 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, const char *name) { - char key[MAX_DBKEY_LENGTH]; + char key[MAX_MDLKEY_LENGTH]; uint key_length; MDL_LOCK_DATA *lock_data; I_P_List_iterator it(context->locks); @@ -1456,7 +1504,7 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, const char *name) { - char key[MAX_DBKEY_LENGTH]; + char key[MAX_MDLKEY_LENGTH]; uint key_length; MDL_LOCK_DATA *lock_data; I_P_List_iterator it(context->locks); diff --git a/sql/mdl.h b/sql/mdl.h index 92bd83038e5..b4b84a9ab24 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -19,6 +19,7 @@ #include "sql_plist.h" #include #include +#include class THD; @@ -148,6 +149,9 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); +/** Maximal length of key for metadata locking subsystem. */ +#define MAX_MDLKEY_LENGTH (4 + NAME_LEN + 1 + NAME_LEN + 1) + void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, const char *db, const char *name); MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, @@ -237,4 +241,19 @@ void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data); void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, mdl_cached_object_release_hook release_hook); + +/* + Functions in the server's kernel used by metadata locking subsystem. +*/ + +extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use); +extern void mysql_ha_flush(THD *thd); +extern "C" const char *set_thd_proc_info(THD *thd, const char *info, + const char *calling_function, + const char *calling_file, + const unsigned int calling_line); +#ifndef DBUG_OFF +extern pthread_mutex_t LOCK_open; +#endif + #endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 12d7e46e821..2f15ecb2b71 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1534,8 +1534,6 @@ char *generate_partition_syntax(partition_info *part_info, Alter_info *alter_info); #endif -bool notify_thread_having_shared_lock(THD *thd, THD *in_use); - enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN, TDC_RT_REMOVE_UNUSED}; void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6c344ad1d4e..7803bd0ba11 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1275,7 +1275,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, /* We need to hold LOCK_open while changing the open_tables list, since another thread may work on it. - @sa notify_thread_having_shared_lock() + @sa mysql_notify_thread_having_shared_lock() */ pthread_mutex_lock(&LOCK_open); @@ -1455,7 +1455,7 @@ void close_thread_tables(THD *thd, /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: notify_thread_having_shared_lock()) + (See: mysql_notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ @@ -7956,7 +7956,7 @@ void flush_tables() rest of the server is broken. */ -bool notify_thread_having_shared_lock(THD *thd, THD *in_use) +bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use) { bool signalled= FALSE; if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && @@ -8501,7 +8501,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: notify_thread_having_shared_lock()) + (See: mysql_notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 9b30d8cec12..49d6cbaa447 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -247,7 +247,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) &name, (uint) namelen, &alias, (uint) aliaslen, &mdl_lock_data, sizeof(MDL_LOCK_DATA), - &mdlkey, MAX_DBKEY_LENGTH, + &mdlkey, MAX_MDLKEY_LENGTH, NullS))) { DBUG_PRINT("exit",("ERROR")); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c50d74412bf..a77ba6264a3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3084,7 +3084,7 @@ uint get_table_open_method(TABLE_LIST *tables, @param mdlkey Pointer to the buffer for key for the lock request (should be at least strlen(db) + strlen(name) + 2 bytes, or, if the lengths are not known, - MAX_DBNAME_LENGTH) + MAX_MDLKEY_LENGTH) @param table Table list element for the table @note This is an auxiliary function to be used in cases when we want to @@ -3157,7 +3157,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; MDL_LOCK_DATA mdl_lock_data; - char mdlkey[MAX_DBKEY_LENGTH]; + char mdlkey[MAX_MDLKEY_LENGTH]; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); From 416a626368b9383a96399ea57dd79f56a665c2a6 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 23:47:23 +0300 Subject: [PATCH 073/466] Backport of: ---------------------------------------------------------- revno: 2630.4.35 committer: Konstantin Osipov branch nick: mysql-6.0-3726 timestamp: Wed 2008-06-25 16:44:00 +0400 message: Fix a MyISAM-specific bug in the new implementation of LOCK TABLES (WL#3726). If more than one instance of a MyISAM table are open in the same connection, all of them must share the same status_param. Otherwise, unlock of a table may lead to lost records. See also comments in thr_lock.c. --- include/thr_lock.h | 2 ++ mysql-test/r/lock.result | 18 +++++++++++++++++ mysql-test/t/lock.test | 18 +++++++++++++++++ mysys/thr_lock.c | 42 ++++++++++++++++++++++++++++++++++------ sql/lock.cc | 2 ++ 5 files changed, 76 insertions(+), 6 deletions(-) diff --git a/include/thr_lock.h b/include/thr_lock.h index fb70c57c0e7..d5a7cd57ea7 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -160,6 +160,8 @@ void thr_unlock(THR_LOCK_DATA *data); enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner); void thr_multi_unlock(THR_LOCK_DATA **data,uint count); +void +thr_lock_merge_status(THR_LOCK_DATA **data, uint count); void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock); my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread); void thr_print_locks(void); /* For debugging */ diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 676a8c41bb6..092c376b34a 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -244,5 +244,23 @@ a unlock tables; drop table t1, t2; # +# Ensure that mi_copy_status is called for two instances +# of the same table when it is reopened after a flush. +# +drop table if exists t1; +drop view if exists v1; +create table t1 (c1 int); +create view v1 as select * from t1; +lock tables t1 write, v1 write; +flush table t1; +insert into t1 values (33); +flush table t1; +select * from t1; +c1 +33 +unlock tables; +drop table t1; +drop view v1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 7effaaeb20d..51900be4df8 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -291,6 +291,24 @@ select * from t1; unlock tables; drop table t1, t2; +--echo # +--echo # Ensure that mi_copy_status is called for two instances +--echo # of the same table when it is reopened after a flush. +--echo # +--disable_warnings +drop table if exists t1; +drop view if exists v1; +--enable_warnings +create table t1 (c1 int); +create view v1 as select * from t1; +lock tables t1 write, v1 write; +flush table t1; +insert into t1 values (33); +flush table t1; +select * from t1; +unlock tables; +drop table t1; +drop view v1; --echo # --echo # End of 6.0 tests. diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 1d724de641c..1288dc36a90 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -1026,12 +1026,43 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) (long) pos[0]->lock, pos[0]->type); fflush(stdout); #endif } - /* - Ensure that all get_locks() have the same status - If we lock the same table multiple times, we must use the same - status_param! - */ + thr_lock_merge_status(data, count); + DBUG_RETURN(THR_LOCK_SUCCESS); +} + + +/** + Ensure that all locks for a given table have the same + status_param. + + This is a MyISAM and possibly Maria specific crutch. MyISAM + engine stores data file length, record count and other table + properties in status_param member of handler. When a table is + locked, connection-local copy is made from a global copy + (myisam_share) by mi_get_status(). When a table is unlocked, + the changed status is transferred back to the global share by + mi_update_status(). + + One thing MyISAM doesn't do is to ensure that when the same + table is opened twice in a connection all instances share the + same status_param. This is necessary, however: for one, to keep + all instances of a connection "on the same page" with regard to + the current state of the table. For other, unless this is done, + myisam_share will always get updated from the last unlocked + instance (in mi_update_status()), and when this instance was not + the one that was used to update data, records may be lost. + + For each table, this function looks up the last lock_data in the + list of acquired locks, and makes sure that all other instances + share status_param with it. +*/ + +void +thr_lock_merge_status(THR_LOCK_DATA **data, uint count) +{ #if !defined(DONT_USE_RW_LOCKS) + THR_LOCK_DATA **pos= data; + THR_LOCK_DATA **end= data + count; if (count > 1) { THR_LOCK_DATA *last_lock= end[-1]; @@ -1073,7 +1104,6 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) } while (pos != data); } #endif - DBUG_RETURN(THR_LOCK_SUCCESS); } /* free all locks */ diff --git a/sql/lock.cc b/sql/lock.cc index 199ab354c22..d19f4b46821 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -687,6 +687,8 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) /* Delete old, not needed locks */ my_free((uchar*) a,MYF(0)); my_free((uchar*) b,MYF(0)); + + thr_lock_merge_status(sql_lock->locks, sql_lock->lock_count); DBUG_RETURN(sql_lock); } From 669258ab54b454e7faee232c5aa6e758afb3c692 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 00:08:51 +0300 Subject: [PATCH 074/466] Backport of: ---------------------------------------------------------- revno: 2630.4.36 committer: Konstantin Osipov branch nick: mysql-6.0-3726 timestamp: Wed 2008-06-25 17:03:37 +0400 message: As per discussion with Serg and Dmitri, remove the code that is trying to emulate LOCK TABLES when locking tables for prelocking. This code was worked around by the engines, and has no effect. We may still have to do something to ensure that statement-based replication is consistent in MVCC engines, or other engines that downgrade table level locks, but this will have to be done somehow differently. --- sql/sql_base.cc | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7803bd0ba11..17b4360c8f9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1335,7 +1335,6 @@ void close_thread_tables(THD *thd, bool skip_mdl) { TABLE *table; - bool clear_table_lock_option= FALSE; DBUG_ENTER("close_thread_tables"); #ifdef EXTRA_DEBUG @@ -1428,7 +1427,6 @@ void close_thread_tables(THD *thd, DBUG_VOID_RETURN; thd->locked_tables_mode= LTM_NONE; - clear_table_lock_option= TRUE; /* Note that we are leaving prelocked mode so we don't need @@ -1467,10 +1465,6 @@ void close_thread_tables(THD *thd, { mdl_remove_all_locks(&thd->mdl_context); } - - if (clear_table_lock_option) - thd->options&= ~OPTION_TABLE_LOCK; - DBUG_VOID_RETURN; } @@ -4842,8 +4836,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, /* We have to emulate LOCK TABLES if we are statement needs prelocking. */ if (thd->lex->requires_prelocking()) { - thd->in_lock_tables=1; - thd->options|= OPTION_TABLE_LOCK; /* A query that modifies autoinc column in sub-statement can make the master and slave inconsistent. @@ -4863,14 +4855,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), flags, need_reopen))) - { - if (thd->lex->requires_prelocking()) - { - thd->options&= ~(OPTION_TABLE_LOCK); - thd->in_lock_tables=0; - } DBUG_RETURN(-1); - } if (thd->lex->requires_prelocking() && thd->lex->sql_command != SQLCOM_LOCK_TABLES) @@ -4880,10 +4865,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, We just have done implicit LOCK TABLES, and now we have to emulate first open_and_lock_tables() after it. - */ - thd->in_lock_tables= 0; - - /* When open_and_lock_tables() is called for a single table out of a table list, the 'next_global' chain is temporarily broken. We may not find 'first_not_own' before the end of the "list". @@ -4906,7 +4887,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, */ mysql_unlock_tables(thd, thd->lock); thd->lock= 0; - thd->options&= ~(OPTION_TABLE_LOCK); DBUG_RETURN(-1); } } From f744a841342aab98adcd01b053e1dc1f8b5a94e3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 00:24:10 +0300 Subject: [PATCH 075/466] Backport of: ------------------------------------------------------------ revno: 2630.4.37 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Wed 2008-06-25 20:28:57 +0400 message: Fix build failure of mysql-6.0-3726 tree on Windows. The failure was caused by the fact that sql/mdl.cc was using __func__ macro without including sql_profile.h header which contained definition of this macro for those platforms that miss it. This patch solves this problem by moving this define to include/my_global.h which makes it available in modules which don't/can't include sql/mysql_priv.h. This is a patch that is part of WL#3726. --- include/my_global.h | 19 +++++++++++++++++++ sql/sql_profile.cc | 3 --- sql/sql_profile.h | 8 -------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 815be622b16..0f2aea62438 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1574,6 +1574,25 @@ inline void operator delete[](void*, void*) { /* Do nothing */ } #define bool In_C_you_should_use_my_bool_instead() #endif +/* Provide __func__ macro definition for platforms that miss it. */ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#elif defined(_MSC_VER) +# if _MSC_VER < 1300 +# define __func__ "" +# else +# define __func__ __FUNCTION__ +# endif +#elif defined(__BORLANDC__) +# define __func__ __FUNC__ +#else +# define __func__ "" +#endif + #ifndef HAVE_RINT /** All integers up to this number can be represented exactly as double precision diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 69e5bc3cbb4..0f67c865d45 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -38,9 +38,6 @@ #define MAX_QUERY_LENGTH 300 -/* Reserved for systems that can't record the function name in source. */ -const char * const _unknown_func_ = ""; - /** Connects Information_Schema and Profiling. */ diff --git a/sql/sql_profile.h b/sql/sql_profile.h index bffe1cb576b..e9c5686efab 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -16,14 +16,6 @@ #ifndef _SQL_PROFILE_H #define _SQL_PROFILE_H -#ifndef __func__ -#ifdef __FUNCTION__ -#define __func__ __FUNCTION__ -#else -#define __func__ "unknown function" -#endif -#endif - extern ST_FIELD_INFO query_profile_statistics_info[]; int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond); int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table); From ef15a335b3c924b779ba986acafffad41ff07ba8 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 02:09:22 +0300 Subject: [PATCH 076/466] Backport of: ---------------------------------------------------------- revno: 2630.4.38 committer: Konstantin Osipov branch nick: mysql-6.0-4144 timestamp: Wed 2008-06-25 22:07:06 +0400 message: WL#4144 - Lock MERGE engine children. Committing a version of the patch merged with WL#3726 on behalf of Ingo. Step #1: Move locking from parent to children. MERGE children are now left in the query list of tables after inserted there in open_tables(). So they are locked by lock_tables() as all other tables are. The MERGE parent does not store locks any more. It appears in a MYSQL_LOCK with zero lock data. This is kind of a "dummy" lock. All other lock handling is also done directly on the children. To protect against parent or child modifications during LOCK TABLES, the children are detached after every statement and attached before every statement, even under LOCK TABLES. The children table list is removed from the query list of tables on every detach and on close of the parent. Step #2: Move MERGE specific functionality from SQL layer into table handler. Functionality moved from SQL layer (mainly sql_base.cc) to the table handler (ha_myisammrg.cc). Unnecessary code is removed from the SQL layer. Step #3: Moved all MERGE specific members from TABLE to ha_myisammrg. Moved members from TABLE to ha_myisammrg. Renamed some mebers. Fixed comments. Step #4: Valgrind and coverage testing Valgrind did not uncover new problems. Added purecov comments. Added a new test for DATA/INDEX DIRECTORY options. Changed handling of ::reset() for non-attached children. Fixed the merge-big test. Step #5: Fixed crashes detected during review Changed detection when to attach/detach. Added new tests. Backport also the fix for Bug#44040 "MySQL allows creating a MERGE table upon VIEWs but crashes when using it" --- include/my_base.h | 5 +- mysql-test/r/merge.result | 285 +++++++++++++- mysql-test/t/disabled.def | 1 - mysql-test/t/merge-big.test | 4 +- mysql-test/t/merge.test | 326 +++++++++++++++- mysys/thr_lock.c | 2 + sql/ha_partition.cc | 68 ++-- sql/mysql_priv.h | 3 - sql/sql_base.cc | 493 ++++-------------------- sql/sql_table.cc | 8 +- sql/table.cc | 18 - sql/table.h | 8 - storage/myisammrg/ha_myisammrg.cc | 620 +++++++++++++++++++----------- storage/myisammrg/ha_myisammrg.h | 4 + storage/myisammrg/myrg_extra.c | 11 +- 15 files changed, 1123 insertions(+), 733 deletions(-) diff --git a/include/my_base.h b/include/my_base.h index 70bd9b5e073..0b8ea79f965 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -191,10 +191,11 @@ enum ha_extra_function { /* Inform handler that we will do a rename */ HA_EXTRA_PREPARE_FOR_RENAME, /* - Orders MERGE handler to attach or detach its child tables. Used at - begin and end of a statement. + Special actions for MERGE tables. */ + HA_EXTRA_ADD_CHILDREN_LIST, HA_EXTRA_ATTACH_CHILDREN, + HA_EXTRA_IS_ATTACHED_CHILDREN, HA_EXTRA_DETACH_CHILDREN }; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 83152a0dd8d..479645744ec 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -578,23 +578,23 @@ select max(b) from t1 where a = 2; max(b) 1 drop table t3,t1,t2; -create table t1 (a int not null); -create table t2 (a int not null); -insert into t1 values (1); -insert into t2 values (2); -create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); -select * from t3; +CREATE TABLE t1 (c1 INT NOT NULL); +CREATE TABLE t2 (c1 INT NOT NULL); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +CREATE TEMPORARY TABLE t3 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t1,t2); +SELECT * FROM t3; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist -create temporary table t4 (a int not null); -create temporary table t5 (a int not null); -insert into t4 values (1); -insert into t5 values (2); -create temporary table t6 (a int not null) ENGINE=MERGE UNION=(t4,t5); -select * from t6; -a -1 -2 -drop table t6, t3, t1, t2, t4, t5; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL); +CREATE TEMPORARY TABLE t5 (c1 INT NOT NULL); +INSERT INTO t4 VALUES (4); +INSERT INTO t5 VALUES (5); +CREATE TEMPORARY TABLE t6 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t4,t5); +SELECT * FROM t6; +c1 +4 +5 +DROP TABLE t6, t3, t1, t2, t4, t5; create temporary table t1 (a int not null); create temporary table t2 (a int not null); insert into t1 values (1); @@ -1258,7 +1258,7 @@ c1 LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; ALTER TABLE t2 RENAME TO t5; SELECT * FROM t3 ORDER BY c1; -ERROR HY000: Table 't3' was not locked with LOCK TABLES +ERROR HY000: Table 't2' was not locked with LOCK TABLES ALTER TABLE t5 RENAME TO t2; ERROR HY000: Table 't5' was not locked with LOCK TABLES UNLOCK TABLES; @@ -1330,9 +1330,9 @@ LOCK TABLES t1 WRITE, t2 WRITE; INSERT INTO t1 VALUES (1); DROP TABLE t1; SELECT * FROM t2; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ERROR HY000: Table 't1' was not locked with LOCK TABLES SELECT * FROM t1; -ERROR 42S02: Table 'test.t1' doesn't exist +ERROR HY000: Table 't1' was not locked with LOCK TABLES UNLOCK TABLES; DROP TABLE t2; # @@ -2256,3 +2256,250 @@ deallocate prepare stmt; # drop table t_parent; set @@global.table_definition_cache=@save_table_definition_cache; +DROP DATABASE IF EXISTS mysql_test1; +CREATE DATABASE mysql_test1; +CREATE TABLE t1 ... DATA DIRECTORY=... INDEX DIRECTORY=... +CREATE TABLE mysql_test1.t2 ... DATA DIRECTORY=... INDEX DIRECTORY=... +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,mysql_test1.t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO mysql_test1.t2 VALUES (2); +SELECT * FROM m1; +c1 +1 +2 +DROP TABLE t1, mysql_test1.t2, m1; +DROP DATABASE mysql_test1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) INSERT_METHOD=FIRST; +CREATE TABLE t3 (c1 INT); +INSERT INTO t3 (c1) VALUES (1); +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM t3); +CREATE VIEW v1 AS SELECT foo.c1 c1, f1() c2, bar.c1 c3, f1() c4 +FROM tm1 foo, tm1 bar, t3; +SELECT * FROM v1; +c1 c2 c3 c4 +1 1 1 1 +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE tm1, t1, t2, t3; +CREATE TEMPORARY TABLE t1 (c1 INT); +CREATE TEMPORARY TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) +INSERT_METHOD=FIRST; +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM tm1); +INSERT INTO tm1 (c1) VALUES (1); +SELECT f1() FROM (SELECT 1) AS c1; +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +CREATE FUNCTION f1() RETURNS INT +BEGIN +CREATE TEMPORARY TABLE t1 (c1 INT); +CREATE TEMPORARY TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2); +INSERT INTO t1 (c1) VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +SELECT f1() FROM (SELECT 1 UNION SELECT 1) c1; +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +CREATE TEMPORARY TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); +CREATE FUNCTION f1() RETURNS INT +BEGIN +CREATE TEMPORARY TABLE t2 (c1 INT); +ALTER TEMPORARY TABLE tm1 UNION=(t1,t2); +INSERT INTO t2 (c1) VALUES (2); +RETURN (SELECT MAX(c1) FROM tm1); +END| +ERROR 0A000: ALTER VIEW is not allowed in stored procedures +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +DROP TABLE tm1, t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +SELECT f1(); +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +UNLOCK TABLES; +DROP TABLE tm1, t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +SELECT f1(); +f1() +1 +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 +FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +c1 +11 +SELECT * FROM t2; +c1 +2 +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +DROP TABLE tm1, t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +SELECT f1(); +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1; +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +UNLOCK TABLES; +DROP TABLE tm1, t1, t9; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +SELECT f1(); +f1() +1 +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t9; +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 +FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +c1 +11 +SELECT * FROM t2; +c1 +2 +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +# +# Don't select MERGE child when trying to get prelocked table. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TRIGGER tm1_ai AFTER INSERT ON tm1 +FOR EACH ROW INSERT INTO t1 VALUES(11); +LOCK TABLE tm1 WRITE, t1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +11 +UNLOCK TABLES; +LOCK TABLE t1 WRITE, tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +11 +1 +11 +UNLOCK TABLES; +DROP TRIGGER tm1_ai; +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t3 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t5 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3,t4,t5) +INSERT_METHOD=LAST; +CREATE TRIGGER t2_au AFTER UPDATE ON t2 +FOR EACH ROW INSERT INTO t3 VALUES(33); +CREATE FUNCTION f1() RETURNS INT +RETURN (SELECT MAX(c1) FROM t4); +LOCK TABLE tm1 WRITE, t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE, t5 WRITE; +INSERT INTO t1 VALUES(1); +INSERT INTO t2 VALUES(2); +INSERT INTO t3 VALUES(3); +INSERT INTO t4 VALUES(4); +INSERT INTO t5 VALUES(5); +UPDATE t2, tm1 SET t2.c1=f1(); +FLUSH TABLES; +FLUSH TABLES; +UNLOCK TABLES; +SELECT * FROM tm1; +c1 +1 +4 +3 +33 +4 +5 +DROP TRIGGER t2_au; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2, t3, t4, t5; +End of 6.0 tests diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index bb2749e902b..ad7617b9403 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -15,4 +15,3 @@ partition_innodb_builtin : Bug#32430 2009-09-25 mattiasj Waiting for push of Inn partition_innodb_plugin : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes innodb-autoinc : Bug#48482 2009-11-02 svoj innodb-autoinc.test fails with results difference rpl_killed_ddl : Bug#45520: rpl_killed_ddl fails sporadically in pb2 -merge : WL#4144 diff --git a/mysql-test/t/merge-big.test b/mysql-test/t/merge-big.test index b687973c9d1..33bd93791f1 100644 --- a/mysql-test/t/merge-big.test +++ b/mysql-test/t/merge-big.test @@ -51,7 +51,7 @@ connection default; #--sleep 8 #SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST; let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE ID = $con1_id AND STATE = 'Locked'; + WHERE ID = $con1_id AND STATE = 'Table lock'; --source include/wait_condition.inc #SELECT NOW(); --echo # Kick INSERT out of thr_multi_lock(). @@ -61,7 +61,7 @@ FLUSH TABLES; #--sleep 8 #SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST; let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE ID = $con1_id AND STATE = 'Waiting for table'; + WHERE ID = $con1_id AND STATE = 'Table lock'; --source include/wait_condition.inc #SELECT NOW(); --echo # Unlock and close table and wait for con1 to close too. diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 63ad5a1e97c..f7ce6ba700b 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -216,20 +216,20 @@ drop table t3,t1,t2; # # temporary merge tables # -create table t1 (a int not null); -create table t2 (a int not null); -insert into t1 values (1); -insert into t2 values (2); -create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +CREATE TABLE t1 (c1 INT NOT NULL); +CREATE TABLE t2 (c1 INT NOT NULL); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +CREATE TEMPORARY TABLE t3 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t1,t2); --error ER_WRONG_MRG_TABLE -select * from t3; -create temporary table t4 (a int not null); -create temporary table t5 (a int not null); -insert into t4 values (1); -insert into t5 values (2); -create temporary table t6 (a int not null) ENGINE=MERGE UNION=(t4,t5); -select * from t6; -drop table t6, t3, t1, t2, t4, t5; +SELECT * FROM t3; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL); +CREATE TEMPORARY TABLE t5 (c1 INT NOT NULL); +INSERT INTO t4 VALUES (4); +INSERT INTO t5 VALUES (5); +CREATE TEMPORARY TABLE t6 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t4,t5); +SELECT * FROM t6; +DROP TABLE t6, t3, t1, t2, t4, t5; # # Bug#19627 - temporary merge table locking # MERGE table and its children must match in temporary type. @@ -556,7 +556,7 @@ CREATE TABLE t1(a INT); SELECT * FROM tm1; CHECK TABLE tm1; CREATE TABLE t2(a BLOB); ---error 1168 +--error ER_WRONG_MRG_TABLE SELECT * FROM tm1; CHECK TABLE tm1; ALTER TABLE t2 MODIFY a INT; @@ -969,9 +969,9 @@ CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) LOCK TABLES t1 WRITE, t2 WRITE; INSERT INTO t1 VALUES (1); DROP TABLE t1; ---error 1168 +--error ER_TABLE_NOT_LOCKED SELECT * FROM t2; ---error ER_NO_SUCH_TABLE +--error ER_TABLE_NOT_LOCKED SELECT * FROM t1; UNLOCK TABLES; DROP TABLE t2; @@ -1407,6 +1407,7 @@ FLUSH TABLES m1, t1; UNLOCK TABLES; DROP TABLE t1, m1; + # # Bug#35068 - Assertion fails when reading from i_s.tables # and there is incorrect merge table @@ -1694,3 +1695,296 @@ while ($1) --enable_query_log drop table t_parent; set @@global.table_definition_cache=@save_table_definition_cache; + +# +# WL#4144 - Lock MERGE engine children +# +# Test DATA/INDEX DIRECTORY +# +--disable_warnings +DROP DATABASE IF EXISTS mysql_test1; +--enable_warnings +CREATE DATABASE mysql_test1; +--disable_query_log +# data/index directory don't work in HAVE_purify builds. Disable +# build-dependent warnings. +--disable_warnings +--echo CREATE TABLE t1 ... DATA DIRECTORY=... INDEX DIRECTORY=... +eval CREATE TABLE t1 (c1 INT) + DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' + INDEX DIRECTORY='$MYSQLTEST_VARDIR/tmp'; +--echo CREATE TABLE mysql_test1.t2 ... DATA DIRECTORY=... INDEX DIRECTORY=... +eval CREATE TABLE mysql_test1.t2 (c1 INT) + DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' + INDEX DIRECTORY='$MYSQLTEST_VARDIR/tmp'; +--enable_query_log +--enable_warnings +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,mysql_test1.t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO mysql_test1.t2 VALUES (2); +SELECT * FROM m1; +#--copy_file $MYSQLTEST_VARDIR/master-data/test/m1.MRG /tmp/mysql-test-m1.MRG +DROP TABLE t1, mysql_test1.t2, m1; +DROP DATABASE mysql_test1; +# +# Review detected Crash #1. Detaching main tables while in sub statement. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) INSERT_METHOD=FIRST; +CREATE TABLE t3 (c1 INT); +INSERT INTO t3 (c1) VALUES (1); +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM t3); +CREATE VIEW v1 AS SELECT foo.c1 c1, f1() c2, bar.c1 c3, f1() c4 + FROM tm1 foo, tm1 bar, t3; +SELECT * FROM v1; +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE tm1, t1, t2, t3; +# +# Review detected Crash #2. Trying to attach temporary table twice. +# +CREATE TEMPORARY TABLE t1 (c1 INT); +CREATE TEMPORARY TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) + INSERT_METHOD=FIRST; +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM tm1); +INSERT INTO tm1 (c1) VALUES (1); +SELECT f1() FROM (SELECT 1) AS c1; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +# +# Review suggested test. DDL in a stored function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + CREATE TEMPORARY TABLE t1 (c1 INT); + CREATE TEMPORARY TABLE t2 (c1 INT); + CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2); + INSERT INTO t1 (c1) VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +SELECT f1() FROM (SELECT 1 UNION SELECT 1) c1; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +# +CREATE TEMPORARY TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); +DELIMITER |; +--error ER_SP_BADSTATEMENT +CREATE FUNCTION f1() RETURNS INT +BEGIN + CREATE TEMPORARY TABLE t2 (c1 INT); + ALTER TEMPORARY TABLE tm1 UNION=(t1,t2); + INSERT INTO t2 (c1) VALUES (2); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +DROP TABLE tm1, t1; +# +# Base table. No LOCK TABLES, no functions/triggers. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +DROP TABLE tm1, t1; +# +# Base table. No LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE tm1, t1; +# +# Base table. LOCK TABLES, no functions/triggers. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +DROP TABLE tm1, t1; +# +# Base table. LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +SELECT f1(); +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1; +# +# Base table. LOCK TABLES statement that locks a table that has a trigger +# that inserts into a merge table, so an attempt is made to lock tables +# of a sub-statement. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 + FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +SELECT * FROM t2; +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +# +# Temporary. No LOCK TABLES, no functions/triggers. +# +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +DROP TABLE tm1, t1; +# +# Temporary. No LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE tm1, t1; +# +# Temporary. LOCK TABLES, no functions/triggers. +# +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +DROP TABLE tm1, t1, t9; +# +# Temporary. LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +SELECT f1(); +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t9; +# +# Temporary. LOCK TABLES statement that locks a table that has a trigger +# that inserts into a merge table, so an attempt is made to lock tables +# of a sub-statement. +# +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 + FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +SELECT * FROM t2; +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +--echo # +--echo # Don't select MERGE child when trying to get prelocked table. +--echo # +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TRIGGER tm1_ai AFTER INSERT ON tm1 + FOR EACH ROW INSERT INTO t1 VALUES(11); +LOCK TABLE tm1 WRITE, t1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +LOCK TABLE t1 WRITE, tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +DROP TRIGGER tm1_ai; +DROP TABLE tm1, t1; + +# Don't resurrect chopped off prelocked tables. +# The problem is not visible by test results; only by debugging. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t3 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t5 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3,t4,t5) + INSERT_METHOD=LAST; +CREATE TRIGGER t2_au AFTER UPDATE ON t2 + FOR EACH ROW INSERT INTO t3 VALUES(33); +CREATE FUNCTION f1() RETURNS INT + RETURN (SELECT MAX(c1) FROM t4); +LOCK TABLE tm1 WRITE, t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE, t5 WRITE; +INSERT INTO t1 VALUES(1); +INSERT INTO t2 VALUES(2); +INSERT INTO t3 VALUES(3); +INSERT INTO t4 VALUES(4); +INSERT INTO t5 VALUES(5); + connect (con1,localhost,root,,); + send UPDATE t2, tm1 SET t2.c1=f1(); +connection default; +# Force reopen in other thread. +#sleep 1; +FLUSH TABLES; +#sleep 1; +FLUSH TABLES; +#sleep 1; +UNLOCK TABLES; + connection con1; + reap; + disconnect con1; +connection default; +SELECT * FROM tm1; +DROP TRIGGER t2_au; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2, t3, t4, t5; + + + +--echo End of 6.0 tests diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 1288dc36a90..e92f68965d8 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -631,6 +631,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, { if (lock->write.data->type == TL_WRITE_ONLY) { + /* purecov: begin tested */ /* Allow lock owner to bypass TL_WRITE_ONLY. */ if (!thr_lock_owner_equal(data->owner, lock->write.data->owner)) { @@ -639,6 +640,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } + /* purecov: end */ } /* diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 1514651c1c5..8760b398ec9 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -5265,34 +5265,35 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, } -/* - General function to prepare handler for certain behavior +/** + General function to prepare handler for certain behavior. - SYNOPSIS - extra() + @param[in] operation operation to execute operation Operation type for extra call - RETURN VALUE - >0 Error code - 0 Success + @return status + @retval 0 success + @retval >0 error code + + @detail - DESCRIPTION extra() is called whenever the server wishes to send a hint to the storage engine. The MyISAM engine implements the most hints. We divide the parameters into the following categories: - 1) Parameters used by most handlers - 2) Parameters used by some non-MyISAM handlers - 3) Parameters used only by MyISAM - 4) Parameters only used by temporary tables for query processing - 5) Parameters only used by MyISAM internally - 6) Parameters not used at all - 7) Parameters only used by federated tables for query processing - 8) Parameters only used by NDB + 1) Operations used by most handlers + 2) Operations used by some non-MyISAM handlers + 3) Operations used only by MyISAM + 4) Operations only used by temporary tables for query processing + 5) Operations only used by MyISAM internally + 6) Operations not used at all + 7) Operations only used by federated tables for query processing + 8) Operations only used by NDB + 9) Operations only used by MERGE The partition handler need to handle category 1), 2) and 3). - 1) Parameters used by most handlers + 1) Operations used by most handlers ----------------------------------- HA_EXTRA_RESET: This option is used by most handlers and it resets the handler state @@ -5331,7 +5332,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, ensure disk based tables are flushed at end of query execution. Currently is never used. - 2) Parameters used by some non-MyISAM handlers + 2) Operations used by some non-MyISAM handlers ---------------------------------------------- HA_EXTRA_KEYREAD_PRESERVE_FIELDS: This is a strictly InnoDB feature that is more or less undocumented. @@ -5350,7 +5351,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, SQL constructs. Not used by MyISAM. - 3) Parameters used only by MyISAM + 3) Operations used only by MyISAM --------------------------------- HA_EXTRA_NORMAL: Only used in MyISAM to reset quick mode, not implemented by any other @@ -5481,7 +5482,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, Only used by MyISAM, called when altering table, closing tables to enforce a reopen of the table files. - 4) Parameters only used by temporary tables for query processing + 4) Operations only used by temporary tables for query processing ---------------------------------------------------------------- HA_EXTRA_RESET_STATE: Same as reset() except that buffers are not released. If there is @@ -5512,7 +5513,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, tables used in query processing. Not handled by partition handler. - 5) Parameters only used by MyISAM internally + 5) Operations only used by MyISAM internally -------------------------------------------- HA_EXTRA_REINIT_CACHE: This call reinitializes the READ CACHE described above if there is one @@ -5547,19 +5548,19 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, HA_EXTRA_CHANGE_KEY_TO_UNIQUE: Only used by MyISAM, never called. - 6) Parameters not used at all + 6) Operations not used at all ----------------------------- HA_EXTRA_KEY_CACHE: HA_EXTRA_NO_KEY_CACHE: This parameters are no longer used and could be removed. - 7) Parameters only used by federated tables for query processing + 7) Operations only used by federated tables for query processing ---------------------------------------------------------------- HA_EXTRA_INSERT_WITH_UPDATE: Inform handler that an "INSERT...ON DUPLICATE KEY UPDATE" will be executed. This condition is unset by HA_EXTRA_NO_IGNORE_DUP_KEY. - 8) Parameters only used by NDB + 8) Operations only used by NDB ------------------------------ HA_EXTRA_DELETE_CANNOT_BATCH: HA_EXTRA_UPDATE_CANNOT_BATCH: @@ -5567,6 +5568,14 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, and should perform them immediately. This may be needed when table has AFTER DELETE/UPDATE triggers which access to subject table. These flags are reset by the handler::extra(HA_EXTRA_RESET) call. + + 9) Operations only used by MERGE + ------------------------------ + HA_EXTRA_ADD_CHILDREN_LIST: + HA_EXTRA_ATTACH_CHILDREN: + HA_EXTRA_IS_ATTACHED_CHILDREN: + HA_EXTRA_DETACH_CHILDREN: + Special actions for MERGE tables. Ignore. */ int ha_partition::extra(enum ha_extra_function operation) @@ -5659,12 +5668,21 @@ int ha_partition::extra(enum ha_extra_function operation) /* Category 7), used by federated handlers */ case HA_EXTRA_INSERT_WITH_UPDATE: DBUG_RETURN(loop_extra(operation)); - /* Category 8) Parameters only used by NDB */ + /* Category 8) Operations only used by NDB */ case HA_EXTRA_DELETE_CANNOT_BATCH: case HA_EXTRA_UPDATE_CANNOT_BATCH: { /* Currently only NDB use the *_CANNOT_BATCH */ break; + } + /* Category 9) Operations only used by MERGE */ + case HA_EXTRA_ADD_CHILDREN_LIST: + case HA_EXTRA_ATTACH_CHILDREN: + case HA_EXTRA_IS_ATTACHED_CHILDREN: + case HA_EXTRA_DETACH_CHILDREN: + { + /* Special actions for MERGE tables. Ignore. */ + break; } /* http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2f15ecb2b71..dc3f59dfec0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1228,9 +1228,6 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name); TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_name); -void detach_merge_children(TABLE *table, bool clear_refs); -bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, - TABLE_LIST *new_child_list, TABLE_LIST **new_last); thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 17b4360c8f9..6751dc763be 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -164,6 +164,11 @@ static void check_unused(void) I_P_List_iterator it(share->free_tables); while ((entry= it++)) { + /* We must not have TABLEs in the free list that have their file closed. */ + DBUG_ASSERT(entry->db_stat && entry->file); + /* Merge children should be detached from a merge parent */ + DBUG_ASSERT(! entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); + if (entry->in_use) { DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */ @@ -372,6 +377,10 @@ static void table_def_use_table(THD *thd, TABLE *table) /* Add table to list of used tables for this share. */ table->s->used_tables.push_front(table); table->in_use= thd; + /* The ex-unused table must be fully functional. */ + DBUG_ASSERT(table->db_stat && table->file); + /* The children must be detached from the table. */ + DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); } @@ -813,9 +822,6 @@ static void free_cache_entry(TABLE *table) { DBUG_ENTER("free_cache_entry"); - /* Assert that MERGE children are not attached before final close. */ - DBUG_ASSERT(!table->is_children_attached()); - /* This should be done before releasing table share. */ table_def_remove_table(table); @@ -1137,14 +1143,11 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd) { table->query_id= 0; table->file->ha_reset(); - /* - Detach temporary MERGE children from temporary parent to allow new - attach at next open. Do not do the detach, if close_thread_tables() - is called from a sub-statement. The temporary table might still be - used in the top-level statement. - */ - if (table->child_l || table->parent) - detach_merge_children(table, TRUE); + + /* Detach temporary MERGE children from temporary parent. */ + DBUG_ASSERT(table->file); + table->file->extra(HA_EXTRA_DETACH_CHILDREN); + /* Reset temporary table lock type to it's default value (TL_WRITE). @@ -1344,6 +1347,20 @@ void close_thread_tables(THD *thd, table->s->table_name.str, (long) table)); #endif + /* Detach MERGE children after every statement. Even under LOCK TABLES. */ + for (table= thd->open_tables; table; table= table->next) + { + /* Table might be in use by some outer statement. */ + DBUG_PRINT("tcache", ("table: '%s' query_id: %lu", + table->s->table_name.str, (ulong) table->query_id)); + if (thd->locked_tables_mode <= LTM_LOCK_TABLES || + table->query_id == thd->query_id) + { + DBUG_ASSERT(table->file); + table->file->extra(HA_EXTRA_DETACH_CHILDREN); + } + } + /* We are assuming here that thd->derived_tables contains ONLY derived tables for this substatement. i.e. instead of approach which uses @@ -1481,12 +1498,6 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) safe_mutex_assert_owner(&LOCK_open); *table_ptr=table->next; - /* - When closing a MERGE parent or child table, detach the children first. - Clear child table references to force new assignment at next open. - */ - if (table->child_l || table->parent) - detach_merge_children(table, TRUE); table->mdl_lock_data= 0; if (table->needs_reopen() || @@ -1497,8 +1508,9 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) } else { - /* Assert that MERGE children are not attached in unused_tables. */ - DBUG_ASSERT(!table->is_children_attached()); + /* Avoid to have MERGE tables with attached children in unused_tables. */ + DBUG_ASSERT(table->file); + table->file->extra(HA_EXTRA_DETACH_CHILDREN); /* Free memory and reset for next loop */ free_field_buffers_larger_than(table,MAX_TDC_BLOB_SIZE); @@ -1964,19 +1976,6 @@ void close_temporary_table(THD *thd, TABLE *table, table->s->db.str, table->s->table_name.str, (long) table, table->alias)); - /* - When closing a MERGE parent or child table, detach the children - first. Clear child table references as MERGE table cannot be - reopened after final close of one of its tables. - - This is necessary here because it is sometimes called with attached - tables and without prior close_thread_tables(). E.g. in - mysql_alter_table(), mysql_rm_table_part2(), mysql_truncate(), - drop_open_table(). - */ - if (table->child_l || table->parent) - detach_merge_children(table, TRUE); - if (table->prev) { table->prev->next= table->next; @@ -2462,15 +2461,10 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (table->s->table_cache_key.length == key_length && !memcmp(table->s->table_cache_key.str, key, key_length)) { - /* - When looking for a usable TABLE, ignore MERGE children, as they - belong to their parent and cannot be used explicitly. - */ if (!my_strcasecmp(system_charset_info, table->alias, alias) && table->query_id != thd->query_id && /* skip tables already used */ (thd->locked_tables_mode == LTM_LOCK_TABLES || - table->query_id == 0) && - !table->parent) + table->query_id == 0)) { int distance= ((int) table->reginfo.lock_type - (int) table_list->lock_type); @@ -2622,6 +2616,16 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (share->is_view) { + /* + If parent_l of the table_list is non null then a merge table + has this view as child table, which is not supported. + */ + if (table_list->parent_l) + { + my_error(ER_WRONG_MRG_TABLE, MYF(0)); + goto err_unlock; + } + /* This table is a view. Validate its metadata version: in particular, that it was a view when the statement was prepared. @@ -2826,6 +2830,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->clear_column_bitmaps(); table_list->table= table; DBUG_ASSERT(table->key_read == 0); + /* Tables may be reused in a sub statement. */ + if (table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)) + table->file->extra(HA_EXTRA_DETACH_CHILDREN); DBUG_RETURN(FALSE); err_unlock: @@ -3537,340 +3544,6 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, } -/** - @brief Add list of MERGE children to a TABLE_LIST list. - - @param[in] tlist the parent TABLE_LIST object just opened - - @return status - @retval 0 OK - @retval != 0 Error - - @detail - When a MERGE parent table has just been opened, insert the - TABLE_LIST chain from the MERGE handle into the table list used for - opening tables for this statement. This lets the children be opened - too. -*/ - -static int add_merge_table_list(TABLE_LIST *tlist) -{ - TABLE *parent= tlist->table; - TABLE_LIST *child_l; - DBUG_ENTER("add_merge_table_list"); - DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str, - parent->s->table_name.str, (long) parent)); - - /* Must not call this with attached children. */ - DBUG_ASSERT(!parent->children_attached); - /* Must not call this with children list in place. */ - DBUG_ASSERT(tlist->next_global != parent->child_l); - /* Prevent inclusion of another MERGE table. Could make infinite recursion. */ - if (tlist->parent_l) - { - my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), tlist->alias); - DBUG_RETURN(1); - } - - /* Fix children.*/ - for (child_l= parent->child_l; ; child_l= child_l->next_global) - { - /* - Note: child_l->table may still be set if this parent was taken - from the unused_tables chain. Ignore this fact here. The - reference will be replaced by the handler in - ::extra(HA_EXTRA_ATTACH_CHILDREN). - */ - - /* Set lock type. */ - child_l->lock_type= tlist->lock_type; - - /* Set parent reference. */ - child_l->parent_l= tlist; - - /* Break when this was the last child. */ - if (&child_l->next_global == parent->child_last_l) - break; - } - - /* Insert children into the table list. */ - *parent->child_last_l= tlist->next_global; - tlist->next_global= parent->child_l; - - /* - Do not fix the prev_global pointers. We will remove the - chain soon anyway. - */ - - DBUG_RETURN(0); -} - - -/** - @brief Attach MERGE children to the parent. - - @param[in] tlist the child TABLE_LIST object just opened - - @return status - @retval 0 OK - @retval != 0 Error - - @note - This is called when the last MERGE child has just been opened, let - the handler attach the MyISAM tables to the MERGE table. Remove - MERGE TABLE_LIST chain from the statement list so that it cannot be - changed or freed. -*/ - -static int attach_merge_children(TABLE_LIST *tlist) -{ - TABLE *parent= tlist->parent_l->table; - int error; - DBUG_ENTER("attach_merge_children"); - DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str, - parent->s->table_name.str, (long) parent)); - - /* Must not call this with attached children. */ - DBUG_ASSERT(!parent->children_attached); - /* Must call this with children list in place. */ - DBUG_ASSERT(tlist->parent_l->next_global == parent->child_l); - - /* Attach MyISAM tables to MERGE table. */ - error= parent->file->extra(HA_EXTRA_ATTACH_CHILDREN); - - /* - Remove children from the table list. Even in case of an error. - This should prevent tampering with them. - */ - tlist->parent_l->next_global= *parent->child_last_l; - - /* - Do not fix the last childs next_global pointer. It is needed for - stepping to the next table in the enclosing loop in open_tables(). - Do not fix prev_global pointers. We did not set them. - */ - - if (error) - { - DBUG_PRINT("error", ("attaching MERGE children failed: %d", my_errno)); - parent->file->print_error(error, MYF(0)); - DBUG_RETURN(1); - } - - parent->children_attached= TRUE; - DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx", parent->s->db.str, - parent->s->table_name.str, (long) parent)); - - /* - Note that we have the cildren in the thd->open_tables list at this - point. - */ - - DBUG_RETURN(0); -} - - -/** - @brief Detach MERGE children from the parent. - - @note - Call this before the first table of a MERGE table (parent or child) - is closed. - - When closing thread tables at end of statement, both parent and - children are in thd->open_tables and will be closed. In most cases - the children will be closed before the parent. They are opened after - the parent and thus stacked into thd->open_tables before it. - - To avoid that we touch a closed children in any way, we must detach - the children from the parent when the first belonging table is - closed (parent or child). - - All references to the children should be removed on handler level - and optionally on table level. - - @note - Assure that you call it for a MERGE parent or child only. - Either table->child_l or table->parent must be set. - - @param[in] table the TABLE object of the parent - @param[in] clear_refs if to clear TABLE references - this must be true when called from - close_thread_tables() to enable fresh - open in open_tables() - it must be false when called in preparation - for reopen_tables() -*/ - -void detach_merge_children(TABLE *table, bool clear_refs) -{ - TABLE_LIST *child_l; - TABLE *parent= table->child_l ? table : table->parent; - bool first_detach; - DBUG_ENTER("detach_merge_children"); - /* - Either table->child_l or table->parent must be set. Parent must have - child_l set. - */ - DBUG_ASSERT(parent && parent->child_l); - DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx clear_refs: %d", - table->s->db.str, table->s->table_name.str, - (long) table, clear_refs)); - DBUG_PRINT("myrg", ("parent: '%s'.'%s' 0x%lx", parent->s->db.str, - parent->s->table_name.str, (long) parent)); - - /* - In a open_tables() loop it can happen that not all tables have their - children attached yet. Also this is called for every child and the - parent from close_thread_tables(). - */ - if ((first_detach= parent->children_attached)) - { - (void) parent->file->extra(HA_EXTRA_DETACH_CHILDREN); - parent->children_attached= FALSE; - DBUG_PRINT("myrg", ("detached parent: '%s'.'%s' 0x%lx", parent->s->db.str, - parent->s->table_name.str, (long) parent)); - } - else - DBUG_PRINT("myrg", ("parent is already detached")); - - if (clear_refs) - { - /* In any case clear the own parent reference. (***) */ - table->parent= NULL; - - /* - On the first detach, clear all references. If this table is the - parent, we still may need to clear the child references. The first - detach might not have done this. - */ - if (first_detach || (table == parent)) - { - /* Clear TABLE references to force new assignment at next open. */ - for (child_l= parent->child_l; ; child_l= child_l->next_global) - { - /* - Do not DBUG_ASSERT(child_l->table); open_tables might be - incomplete. - - Clear the parent reference of the children only on the first - detach. The children might already be closed. They will clear - it themseves when this function is called for them with - 'clear_refs' true. See above "(***)". - */ - if (first_detach && child_l->table) - child_l->table->parent= NULL; - - /* Clear the table reference to force new assignment at next open. */ - child_l->table= NULL; - - /* Break when this was the last child. */ - if (&child_l->next_global == parent->child_last_l) - break; - } - } - } - - DBUG_VOID_RETURN; -} - - -/** - @brief Fix MERGE children after open. - - @param[in] old_child_list first list member from original table - @param[in] old_last pointer to &next_global of last list member - @param[in] new_child_list first list member from freshly opened table - @param[in] new_last pointer to &next_global of last list member - - @return mismatch - @retval FALSE OK, no mismatch - @retval TRUE Error, lists mismatch - - @detail - Main action is to copy TABLE reference for each member of original - child list to new child list. After a fresh open these references - are NULL. Assign the old children to the new table. Some of them - might also be reopened or will be reopened soon. - - Other action is to verify that the table definition with respect to - the UNION list did not change. - - @note - This function terminates the child list if the respective '*_last' - pointer is non-NULL. Do not call it from a place where the list is - embedded in another list and this would break it. - - Terminating the list is required for example in the first - reopen_table() after open_tables(). open_tables() requires the end - of the list not to be terminated because other tables could follow - behind the child list. - - If a '*_last' pointer is NULL, the respective list is assumed to be - NULL terminated. -*/ - -bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, - TABLE_LIST *new_child_list, TABLE_LIST **new_last) -{ - bool mismatch= FALSE; - DBUG_ENTER("fix_merge_after_open"); - DBUG_PRINT("myrg", ("old last addr: 0x%lx new last addr: 0x%lx", - (long) old_last, (long) new_last)); - - /* Terminate the lists for easier check of list end. */ - if (old_last) - *old_last= NULL; - if (new_last) - *new_last= NULL; - - for (;;) - { - DBUG_PRINT("myrg", ("old list item: 0x%lx new list item: 0x%lx", - (long) old_child_list, (long) new_child_list)); - /* Break if one of the list is at its end. */ - if (!old_child_list || !new_child_list) - break; - /* Old table has references to child TABLEs. */ - DBUG_ASSERT(old_child_list->table); - /* New table does not yet have references to child TABLEs. */ - DBUG_ASSERT(!new_child_list->table); - DBUG_PRINT("myrg", ("old table: '%s'.'%s' new table: '%s'.'%s'", - old_child_list->db, old_child_list->table_name, - new_child_list->db, new_child_list->table_name)); - /* Child db.table names must match. */ - if (strcmp(old_child_list->table_name, new_child_list->table_name) || - strcmp(old_child_list->db, new_child_list->db)) - break; - /* - Copy TABLE reference. Child TABLE objects are still in place - though not necessarily open yet. - */ - DBUG_PRINT("myrg", ("old table ref: 0x%lx replaces new table ref: 0x%lx", - (long) old_child_list->table, - (long) new_child_list->table)); - new_child_list->table= old_child_list->table; - /* Step both lists. */ - old_child_list= old_child_list->next_global; - new_child_list= new_child_list->next_global; - } - DBUG_PRINT("myrg", ("end of list, mismatch: %d", mismatch)); - /* - If the list pointers are not both NULL after the loop, then the - lists differ. If the are both identical, but not NULL, then they - have at least one table in common and hence the rest of the list - would be identical too. But in this case the loop woul run until the - list end, where both pointers would become NULL. - */ - if (old_child_list != new_child_list) - mismatch= TRUE; - if (mismatch) - my_error(ER_TABLE_DEF_CHANGED, MYF(0)); - - DBUG_RETURN(mismatch); -} - - /* Return a appropriate read lock type given a table object. @@ -4079,19 +3752,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (error) { - /* - If in a MERGE table open, we need to remove the children list - from statement table list before restarting. Otherwise the list - will be inserted another time. - */ - if (tables->parent_l) - { - TABLE_LIST *parent_l= tables->parent_l; - /* The parent table should be correctly open at this point. */ - DBUG_ASSERT(parent_l->table); - parent_l->next_global= *parent_l->table->child_last_l; - } - if (action) { /* @@ -4132,7 +3792,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } result= -1; // Fatal error - break; + goto err; } /* @@ -4222,16 +3882,15 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) goto err; } - /* Attach MERGE children if not locked already. */ - DBUG_PRINT("tcache", ("is parent: %d is child: %d", - test(tables->table->child_l), - test(tables->parent_l))); - if ((!thd->locked_tables_mode || tables->table->s->tmp_table) && - ((tables->table->child_l && - add_merge_table_list(tables)) || - (tables->parent_l && - (&tables->next_global == tables->parent_l->table->child_last_l) && - attach_merge_children(tables)))) + /* + After opening a MERGE table add the children to the query list of + tables, so that they are opened too. + Note that placeholders don't have the handler open. + */ + /* MERGE tables need to access parent and child TABLE_LISTs. */ + DBUG_ASSERT(tables->table->pos_in_table_list == tables); + /* Non-MERGE tables ignore this call. */ + if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) { result= -1; goto err; @@ -4263,6 +3922,29 @@ process_view_routines: } } + /* + After successful open of all tables, including MERGE parents and + children, attach the children to their parents. At end of statement, + the children are detached. Attaching and detaching are always done, + even under LOCK TABLES. + */ + for (tables= *start; tables; tables= tables->next_global) + { + TABLE *tbl= tables->table; + + /* Schema tables may not have a TABLE object here. */ + if (tbl && tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM) + { + /* MERGE tables need to access parent and child TABLE_LISTs. */ + DBUG_ASSERT(tbl->pos_in_table_list == tables); + if (tbl->file->extra(HA_EXTRA_ATTACH_CHILDREN)) + { + result= -1; + goto err; + } + } + } + err: thd_proc_info(thd, 0); free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block @@ -4272,17 +3954,6 @@ process_view_routines: if (result && tables) { - /* - Some functions determine success as (tables->table != NULL). - tables->table is in thd->open_tables. It won't go lost. If the - error happens on a MERGE child, clear the parents TABLE reference. - */ - if (tables->parent_l) - { - if (tables->parent_l->next_global == tables->parent_l->table->child_l) - tables->parent_l->next_global= *tables->parent_l->table->child_last_l; - tables->parent_l->table= NULL; - } tables->table= NULL; } DBUG_PRINT("tcache", ("returning: %d", result)); @@ -4447,7 +4118,7 @@ retry: */ DBUG_ASSERT(table_list->table); table= table_list->table; - if (table->child_l) + if (table->file->ht->db_type == DB_TYPE_MRG_MYISAM) { /* A MERGE table must not come here. */ /* purecov: begin tested */ @@ -4896,7 +4567,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, and was marked as occupied during open_tables() as free for reuse. */ mark_real_tables_as_free_for_reuse(first_not_own); - DBUG_PRINT("info",("locked_tables_mode= PRELOCKED")); + DBUG_PRINT("info",("locked_tables_mode= LTM_PRELOCKED")); thd->locked_tables_mode= LTM_PRELOCKED; } } @@ -8481,9 +8152,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: mysql_notify_thread_having_shared_lock()) - Closing a MERGE child before the parent would be fatal if the - other thread tries to abort the MERGE lock in between. + (See: notify_thread_having_shared_lock()) */ while (thd->open_tables) found_old_table|= close_thread_table(thd, &thd->open_tables); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f6eafaacf61..289cc77fb27 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4396,7 +4396,8 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, "Failed to open partially restored table")); } /* A MERGE table must not come here. */ - DBUG_ASSERT(!table->table || !table->table->child_l); + DBUG_ASSERT(!table->table || + table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); DBUG_RETURN(0); } @@ -4463,7 +4464,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, } /* A MERGE table must not come here. */ - DBUG_ASSERT(!table->child_l); + DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM); /* REPAIR TABLE ... USE_FRM for temporary tables makes little sense. @@ -7270,7 +7271,8 @@ view_err: } else { - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + if (!table->s->tmp_table && + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto err_new_table_cleanup; thd_proc_info(thd, "manage keys"); alter_table_manage_keys(table, table->file->indexes_are_disabled(), diff --git a/sql/table.cc b/sql/table.cc index 3943dc8e50b..dd39d05733e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4555,24 +4555,6 @@ void TABLE::mark_columns_needed_for_insert() } -/** - @brief Check if this is part of a MERGE table with attached children. - - @return status - @retval TRUE children are attached - @retval FALSE no MERGE part or children not attached - - @detail - A MERGE table consists of a parent TABLE and zero or more child - TABLEs. Each of these TABLEs is called a part of a MERGE table. -*/ - -bool TABLE::is_children_attached(void) -{ - return((child_l && children_attached) || - (parent && parent->children_attached)); -} - /* Cleanup this table for re-execution. diff --git a/sql/table.h b/sql/table.h index c30b6e7ad80..918fb8dfff0 100644 --- a/sql/table.h +++ b/sql/table.h @@ -635,11 +635,6 @@ private: public: - /* For the below MERGE related members see top comment in ha_myisammrg.cc */ - TABLE *parent; /* Set in MERGE child. Ptr to parent */ - TABLE_LIST *child_l; /* Set in MERGE parent. List of children */ - TABLE_LIST **child_last_l; /* Set in MERGE parent. End of list */ - THD *in_use; /* Which thread uses this */ Field **field; /* Pointer to fields */ @@ -809,8 +804,6 @@ public: my_bool insert_or_update; /* Can be used by the handler */ my_bool alias_name_used; /* true if table_name is alias */ my_bool get_fields_in_item_tree; /* Signal to fix_field */ - /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */ - my_bool children_attached; REGINFO reginfo; /* field connections */ MEM_ROOT mem_root; @@ -861,7 +854,6 @@ public: */ inline bool needs_reopen() { return s->version != refresh_version; } - bool is_children_attached(void); }; diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index b2181636492..3d1b2b13c1e 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -33,40 +33,29 @@ and hence through open_tables(). When the parent appears in the list of tables to open, the initial open of the handler does nothing but read the meta file and collect a list of TABLE_LIST objects for the - children. This list is attached to the parent TABLE object as - TABLE::child_l. The end of the children list is saved in - TABLE::child_last_l. + children. This list is attached to the handler object as + ha_myisammrg::children_l. The end of the children list is saved in + ha_myisammrg::children_last_l. - Back in open_tables(), add_merge_table_list() is called. It updates - each list member with the lock type and a back pointer to the parent - TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in - the list of tables to open, right behind the parent. Consequently, - open_tables() opens the children, one after the other. The TABLE - references of the TABLE_LIST objects are implicitly set to the open - tables. The children are opened as independent MyISAM tables, right as - if they are used by the SQL statement. + Back in open_tables(), handler::extra(HA_EXTRA_ADD_CHILDREN_LIST) is + called. It updates each list member with the lock type and a back + pointer to the parent TABLE_LIST object TABLE_LIST::parent_l. The list + is then inserted in the list of tables to open, right behind the + parent. Consequently, open_tables() opens the children, one after the + other. The TABLE references of the TABLE_LIST objects are implicitly + set to the open tables by open_table(). The children are opened as + independent MyISAM tables, right as if they are used by the SQL + statement. - TABLE_LIST::parent_l is required to find the parent 1. when the last - child has been opened and children are to be attached, and 2. when an - error happens during child open and the child list must be removed - from the queuery list. In these cases the current child does not have - TABLE::parent set or does not have a TABLE at all respectively. - - When the last child is open, attach_merge_children() is called. It - removes the list of children from the open list. Then the children are - "attached" to the parent. All required references between parent and + When all tables from the statement query list are open, + handler::extra(HA_EXTRA_ATTACH_CHILDREN) is called. It "attaches" the + children to the parent. All required references between parent and children are set up. The MERGE storage engine sets up an array with references to the low-level MyISAM table objects (MI_INFO). It remembers the state of the table in MYRG_INFO::children_attached. - Every child TABLE::parent references the parent TABLE object. That way - TABLE objects belonging to a MERGE table can be identified. - TABLE::parent is required because the parent and child TABLE objects - can live longer than the parent TABLE_LIST object. So the path - child->pos_in_table_list->parent_l->table can be broken. - If necessary, the compatibility of parent and children is checked. This check is necessary when any of the objects are reopend. This is detected by comparing the current table def version against the @@ -80,14 +69,20 @@ myisammrg_attach_children_callback() sets it ot TRUE if a table def version mismatches the remembered child def version. - Finally the parent TABLE::children_attached is set. + The children chain remains in the statement query list until the table + is closed or the children are detached. This is done so that the + children are locked by lock_tables(). + + At statement end the children are detached. At the next statement + begin the open-add-attach sequence repeats. There is no exception for + LOCK TABLES. The fresh establishment of the parent-child relationship + before every statement catches numerous cases of ALTER/FLUSH/DROP/etc + of parent or children during LOCK TABLES. --- On parent open the storage engine structures are allocated and initialized. They stay with the open table until its final close. - - */ #ifdef USE_PRAGMA_IMPLEMENTATION @@ -118,7 +113,10 @@ static handler *myisammrg_create_handler(handlerton *hton, ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) :handler(hton, table_arg), file(0), is_cloned(0) -{} +{ + init_sql_alloc(&children_mem_root, max(4 * sizeof(TABLE_LIST), FN_REFLEN) + + ALLOC_ROOT_MIN_BLOCK_SIZE, 0); +} /** @@ -126,7 +124,9 @@ ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) */ ha_myisammrg::~ha_myisammrg(void) -{} +{ + free_root(&children_mem_root, MYF(0)); +} static const char *ha_myisammrg_exts[] = { @@ -178,45 +178,48 @@ const char *ha_myisammrg::index_type(uint key_number) /** - @brief Callback function for open of a MERGE parent table. - - @detail This function adds a TABLE_LIST object for a MERGE child table - to a list of tables of the parent TABLE object. It is called for - each child table. - - The list of child TABLE_LIST objects is kept in the TABLE object of - the parent for the whole life time of the MERGE table. It is - inserted in the statement list behind the MERGE parent TABLE_LIST - object when the MERGE table is opened. It is removed from the - statement list after the last child is opened. - - All memeory used for the child TABLE_LIST objects and the strings - referred by it are taken from the parent TABLE::mem_root. Thus they - are all freed implicitly at the final close of the table. - - TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global - # # ^ # ^ - # # | # | - # # +--------- TABLE_LIST::prev_global - # # | - # |<--- TABLE_LIST::prev_global | - # | - TABLE::child_last_l -----------------------------------------+ + Callback function for open of a MERGE parent table. @param[in] callback_param data pointer as given to myrg_parent_open() + this is used to pass the handler handle @param[in] filename file name of MyISAM table without extension. @return status @retval 0 OK @retval != 0 Error + + @detail + + This function adds a TABLE_LIST object for a MERGE child table to a + list of tables in the parent handler object. It is called for each + child table. + + The list of child TABLE_LIST objects is kept in the handler object + of the parent for the whole life time of the MERGE table. It is + inserted in the statement query list behind the MERGE parent + TABLE_LIST object when the MERGE table is opened. It is removed from + the statement query list at end of statement or at children detach. + + All memory used for the child TABLE_LIST objects and the strings + referred by it are taken from the parent + ha_myisammrg::children_mem_root. Thus they are all freed implicitly at + the final close of the table. + + children_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global + # # ^ # ^ + # # | # | + # # +--------- TABLE_LIST::prev_global + # # | + # |<--- TABLE_LIST::prev_global | + # | + children_last_l -----------------------------------------+ */ static int myisammrg_parent_open_callback(void *callback_param, const char *filename) { - ha_myisammrg *ha_myrg; - TABLE *parent; + ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param; TABLE_LIST *child_l; const char *db; const char *table_name; @@ -242,11 +245,8 @@ static int myisammrg_parent_open_callback(void *callback_param, dirlen-= db - dir_path; /* This is now the length of 'db'. */ DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name)); - ha_myrg= (ha_myisammrg*) callback_param; - parent= ha_myrg->table_ptr(); - /* Get a TABLE_LIST object. */ - if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root, + if (!(child_l= (TABLE_LIST*) alloc_root(&ha_myrg->children_mem_root, sizeof(TABLE_LIST)))) { /* purecov: begin inspected */ @@ -258,68 +258,231 @@ static int myisammrg_parent_open_callback(void *callback_param, /* Set database (schema) name. */ child_l->db_length= dirlen; - child_l->db= strmake_root(&parent->mem_root, db, dirlen); + child_l->db= strmake_root(&ha_myrg->children_mem_root, db, dirlen); /* Set table name. */ child_l->table_name_length= strlen(table_name); - child_l->table_name= strmake_root(&parent->mem_root, table_name, + child_l->table_name= strmake_root(&ha_myrg->children_mem_root, table_name, child_l->table_name_length); /* Convert to lowercase if required. */ if (lower_case_table_names && child_l->table_name_length) + { + /* purecov: begin tested */ child_l->table_name_length= my_casedn_str(files_charset_info, child_l->table_name); + /* purecov: end */ + } /* Set alias. */ child_l->alias= child_l->table_name; - /* - FIXME: Actually we should use some other mem-root here. - To be fixed once Ingo pushes his patch for WL4144. - */ - alloc_mdl_locks(child_l, &parent->mem_root); - /* Initialize table map to 'undefined'. */ child_l->init_child_def_version(); - /* Link TABLE_LIST object into the parent list. */ - if (!parent->child_last_l) + /* Link TABLE_LIST object into the children list. */ + if (ha_myrg->children_last_l) + child_l->prev_global= ha_myrg->children_last_l; + else { - /* Initialize parent->child_last_l when handling first child. */ - parent->child_last_l= &parent->child_l; + /* Initialize ha_myrg->children_last_l when handling first child. */ + ha_myrg->children_last_l= &ha_myrg->children_l; } - *parent->child_last_l= child_l; - child_l->prev_global= parent->child_last_l; - parent->child_last_l= &child_l->next_global; + *ha_myrg->children_last_l= child_l; + ha_myrg->children_last_l= &child_l->next_global; DBUG_RETURN(0); } /** - @brief Callback function for attaching a MERGE child table. + Open a MERGE parent table, but not its children. - @detail This function retrieves the MyISAM table handle from the - next child table. It is called for each child table. + @param[in] name MERGE table path name + @param[in] mode read/write mode, unused + @param[in] test_if_locked open flags - @param[in] callback_param data pointer as given to - myrg_attach_children() + @return status + @retval 0 OK + @retval -1 Error, my_errno gives reason + + @detail + This function initializes the MERGE storage engine structures + and adds a child list of TABLE_LIST to the parent handler. +*/ + +int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), + uint test_if_locked) +{ + DBUG_ENTER("ha_myisammrg::open"); + DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table)); + DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked)); + + /* Must not be used when table is open. */ + DBUG_ASSERT(!this->file); + + /* Save for later use. */ + this->test_if_locked= test_if_locked; + + /* In case this handler was open and closed before, free old data. */ + free_root(&this->children_mem_root, MYF(MY_MARK_BLOCKS_FREE)); + + /* + Initialize variables that are used, modified, and/or set by + myisammrg_parent_open_callback(). + 'children_l' is the head of the children chain. + 'children_last_l' points to the end of the children chain. + 'my_errno' is set by myisammrg_parent_open_callback() in + case of an error. + */ + children_l= NULL; + children_last_l= NULL; + my_errno= 0; + + /* retrieve children table list. */ + if (is_cloned) + { + /* + Open and attaches the MyISAM tables,that are under the MERGE table + parent, on the MyISAM storage engine interface directly within the + MERGE engine. The new MyISAM table instances, as well as the MERGE + clone itself, are not visible in the table cache. This is not a + problem because all locking is handled by the original MERGE table + from which this is cloned of. + */ + if (!(file= myrg_open(name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED))) + { + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + } + + file->children_attached= TRUE; + + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + } + else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) + { + /* purecov: begin inspected */ + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + /* purecov: end */ + } + DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx child tables: %u", + (long) file, file->tables)); + DBUG_RETURN(0); +} + + +/** + Add list of MERGE children to a TABLE_LIST chain. + + @return status + @retval 0 OK + @retval != 0 Error + + @detail + When a MERGE parent table has just been opened, insert the + TABLE_LIST chain from the MERGE handle into the table list used for + opening tables for this statement. This lets the children be opened + too. +*/ + +int ha_myisammrg::add_children_list(void) +{ + TABLE_LIST *parent_l= this->table->pos_in_table_list; + TABLE_LIST *child_l; + THD *thd= current_thd; + DBUG_ENTER("ha_myisammrg::add_children_list"); + DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", this->table->s->db.str, + this->table->s->table_name.str, (long) this->table)); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); + + /* Ignore this for empty MERGE tables (UNION=()). */ + if (!this->file->tables) + { + DBUG_PRINT("myrg", ("empty merge table union")); + goto end; + } + + /* Must not call this with attached children. */ + DBUG_ASSERT(!this->file->children_attached); + + /* Must not call this with children list in place. */ + DBUG_ASSERT(parent_l->next_global != this->children_l); + + /* + Prevent inclusion of another MERGE table, which could make infinite + recursion. + */ + if (parent_l->parent_l) + { + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), parent_l->alias); + DBUG_RETURN(1); + } + + /* Fix children. */ + DBUG_ASSERT(this->children_l); + for (child_l= this->children_l; ; child_l= child_l->next_global) + { + DBUG_ASSERT(!child_l->table); + + /* Set lock type. */ + child_l->lock_type= parent_l->lock_type; + + /* Set parent reference. Used to detect MERGE in children list. */ + child_l->parent_l= parent_l; + + /* Copy select_lex. Used in unique_table() at least. */ + child_l->select_lex= parent_l->select_lex; + + child_l->mdl_lock_data= NULL; /* Safety, if alloc_mdl_locks fails. */ + + /* Break when this was the last child. */ + if (&child_l->next_global == this->children_last_l) + break; + } + + alloc_mdl_locks(children_l, + thd->locked_tables_root ? thd->locked_tables_root : + thd->mem_root); + + /* Insert children into the table list. */ + if (parent_l->next_global) + parent_l->next_global->prev_global= this->children_last_l; + *this->children_last_l= parent_l->next_global; + parent_l->next_global= this->children_l; + this->children_l->prev_global= &parent_l->next_global; + +end: + DBUG_RETURN(0); +} + + +/** + Callback function for attaching a MERGE child table. + + @param[in] callback_param data pointer as given to myrg_attach_children() + this is used to pass the handler handle @return pointer to open MyISAM table structure @retval !=NULL OK, returning pointer @retval NULL, my_errno == 0 Ok, no more child tables @retval NULL, my_errno != 0 error + + @detail + This function retrieves the MyISAM table handle from the + next child table. It is called for each child table. */ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) { - ha_myisammrg *ha_myrg; - TABLE *parent; + ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param; + TABLE *parent= ha_myrg->table_ptr(); TABLE *child; TABLE_LIST *child_l; MI_INFO *myisam; DBUG_ENTER("myisammrg_attach_children_callback"); my_errno= 0; - ha_myrg= (ha_myisammrg*) callback_param; - parent= ha_myrg->table_ptr(); /* Get child list item. */ child_l= ha_myrg->next_child_attach; @@ -329,13 +492,11 @@ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) DBUG_RETURN(NULL); } child= child_l->table; - DBUG_PRINT("myrg", ("child table: '%s'.'%s' 0x%lx", child->s->db.str, - child->s->table_name.str, (long) child)); /* Prepare for next child. Used as child_l in next call to this function. We cannot rely on a NULL-terminated chain. */ - if (&child_l->next_global == parent->child_last_l) + if (&child_l->next_global == ha_myrg->children_last_l) { DBUG_PRINT("myrg", ("attaching last child")); ha_myrg->next_child_attach= NULL; @@ -343,9 +504,6 @@ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) else ha_myrg->next_child_attach= child_l->next_global; - /* Set parent reference. */ - child->parent= parent; - /* Do a quick compatibility check. The table def version is set when the table share is created. The child def version is copied @@ -394,63 +552,6 @@ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) } -/** - @brief Open a MERGE parent table, not its children. - - @detail This function initializes the MERGE storage engine structures - and adds a child list of TABLE_LIST to the parent TABLE. - - @param[in] name MERGE table path name - @param[in] mode read/write mode, unused - @param[in] test_if_locked open flags - - @return status - @retval 0 OK - @retval -1 Error, my_errno gives reason -*/ - -int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), - uint test_if_locked) -{ - DBUG_ENTER("ha_myisammrg::open"); - DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table)); - DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked)); - - /* Save for later use. */ - this->test_if_locked= test_if_locked; - - /* retrieve children table list. */ - my_errno= 0; - if (is_cloned) - { - /* - Open and attaches the MyISAM tables,that are under the MERGE table - parent, on the MyISAM storage engine interface directly within the - MERGE engine. The new MyISAM table instances, as well as the MERGE - clone itself, are not visible in the table cache. This is not a - problem because all locking is handled by the original MERGE table - from which this is cloned of. - */ - if (!(file= myrg_open(table->s->normalized_path.str, table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED))) - { - DBUG_PRINT("error", ("my_errno %d", my_errno)); - DBUG_RETURN(my_errno ? my_errno : -1); - } - - file->children_attached= TRUE; - - info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); - } - else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) - { - DBUG_PRINT("error", ("my_errno %d", my_errno)); - DBUG_RETURN(my_errno ? my_errno : -1); - } - DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file)); - DBUG_RETURN(0); -} - /** Returns a cloned instance of the current handler. @@ -502,22 +603,24 @@ handler *ha_myisammrg::clone(MEM_ROOT *mem_root) /** - @brief Attach children to a MERGE table. + Attach children to a MERGE table. - @detail Let the storage engine attach its children through a callback + @return status + @retval 0 OK + @retval != 0 Error, my_errno gives reason + + @detail + Let the storage engine attach its children through a callback function. Check table definitions for consistency. - @note Special thd->open_options may be in effect. We can make use of + @note + Special thd->open_options may be in effect. We can make use of them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names of mismatching child tables. We cannot transport these options in ha_myisammrg::test_if_locked because they may change after the parent is opened. The parent is kept open in the table cache over multiple statements and can be used by other threads. Open options can change over time. - - @return status - @retval 0 OK - @retval != 0 Error, my_errno gives reason */ int ha_myisammrg::attach_children(void) @@ -532,8 +635,27 @@ int ha_myisammrg::attach_children(void) DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str, table->s->table_name.str, (long) table)); DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked)); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); + + /* + A MERGE table with no children (empty union) is always seen as + attached internally. + */ + if (!this->file->tables) + { + DBUG_PRINT("myrg", ("empty merge table union")); + goto end; + } + DBUG_PRINT("myrg", ("child tables: %u", this->file->tables)); + + /* Must not call this with attached children. */ DBUG_ASSERT(!this->file->children_attached); + /* Must call this with children list in place. */ + DBUG_ASSERT(this->table->pos_in_table_list->next_global == this->children_l); + /* Initialize variables that are used, modified, and/or set by myisammrg_attach_children_callback(). @@ -546,7 +668,7 @@ int ha_myisammrg::attach_children(void) 'my_errno' is set by myisammrg_attach_children_callback() in case of an error. */ - next_child_attach= table->child_l; + next_child_attach= this->children_l; need_compat_check= FALSE; my_errno= 0; @@ -555,8 +677,8 @@ int ha_myisammrg::attach_children(void) myisammrg_attach_children_callback, this, (my_bool *) &need_compat_check)) { - DBUG_PRINT("error", ("my_errno %d", my_errno)); - DBUG_RETURN(my_errno ? my_errno : -1); + error= my_errno; + goto err; } DBUG_PRINT("myrg", ("calling myrg_extrafunc")); myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); @@ -583,7 +705,11 @@ int ha_myisammrg::attach_children(void) DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu", table->s->reclength, stats.mean_rec_length)); if (test_if_locked & HA_OPEN_FOR_REPAIR) + { + /* purecov: begin inspected */ myrg_print_wrong_table(file->open_tables->table->filename); + /* purecov: end */ + } error= HA_ERR_WRONG_MRG_TABLE_DEF; goto err; } @@ -614,20 +740,23 @@ int ha_myisammrg::attach_children(void) my_free((uchar*) recinfo, MYF(0)); goto err; } + /* purecov: begin inspected */ myrg_print_wrong_table(u_table->table->filename); + /* purecov: end */ } } my_free((uchar*) recinfo, MYF(0)); if (error == HA_ERR_WRONG_MRG_TABLE_DEF) - goto err; + goto err; /* purecov: inspected */ /* All checks passed so far. Now update child def version. */ - for (child_l= table->child_l; ; child_l= child_l->next_global) + DBUG_ASSERT(this->children_l); + for (child_l= this->children_l; ; child_l= child_l->next_global) { child_l->set_child_def_version( child_l->table->s->get_table_def_version()); - if (&child_l->next_global == table->child_last_l) + if (&child_l->next_global == this->children_last_l) break; } } @@ -640,50 +769,115 @@ int ha_myisammrg::attach_children(void) goto err; } #endif + + end: DBUG_RETURN(0); err: - myrg_detach_children(file); + DBUG_PRINT("error", ("attaching MERGE children failed: %d", error)); + print_error(error, MYF(0)); + detach_children(); DBUG_RETURN(my_errno= error); } /** - @brief Detach all children from a MERGE table. - - @note Detach must not touch the children in any way. - They may have been closed at ths point already. - All references to the children should be removed. + Detach all children from a MERGE table and from the query list of tables. @return status @retval 0 OK @retval != 0 Error, my_errno gives reason + + @note + Detach must not touch the child TABLE objects in any way. + They may have been closed at ths point already. + All references to the children should be removed. */ int ha_myisammrg::detach_children(void) { + TABLE_LIST *child_l; DBUG_ENTER("ha_myisammrg::detach_children"); - DBUG_ASSERT(this->file && this->file->children_attached); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); + + /* A MERGE table with no children (empty union) cannot be detached. */ + if (!this->file->tables) + { + DBUG_PRINT("myrg", ("empty merge table union")); + goto end; + } + + /* Clear TABLE references. */ + DBUG_ASSERT(this->children_l); + for (child_l= this->children_l; ; child_l= child_l->next_global) + { + /* + Do not DBUG_ASSERT(child_l->table); open_tables might be + incomplete. + + Clear the table reference. + */ + child_l->table= NULL; + + /* Break when this was the last child. */ + if (&child_l->next_global == this->children_last_l) + break; + } + + /* + Remove children from the table list. This won't fail if called + twice. The list is terminated after removal. + + If the parent is LEX::query_tables_own_last and pre-locked tables + follow (tables used by stored functions or triggers), the children + are inserted behind the parent and before the pre-locked tables. But + we do not adjust LEX::query_tables_own_last. The pre-locked tables + could have chopped off the list by clearing + *LEX::query_tables_own_last. This did also chop off the children. If + we would copy the reference from *this->children_last_l in this + case, we would put the chopped off pre-locked tables back to the + list. So we refrain from copying it back, if the destination has + been set to NULL meanwhile. + */ + if (this->children_l->prev_global && *this->children_l->prev_global) + *this->children_l->prev_global= *this->children_last_l; + if (*this->children_last_l) + (*this->children_last_l)->prev_global= this->children_l->prev_global; + + /* Terminate child list. So it cannot be tried to remove again. */ + *this->children_last_l= NULL; + this->children_l->prev_global= NULL; + + if (!this->file->children_attached) + { + DBUG_PRINT("myrg", ("merge children are already detached")); + goto end; + } if (myrg_detach_children(this->file)) { /* purecov: begin inspected */ - DBUG_PRINT("error", ("my_errno %d", my_errno)); + print_error(my_errno, MYF(0)); DBUG_RETURN(my_errno ? my_errno : -1); /* purecov: end */ } + + end: DBUG_RETURN(0); } /** - @brief Close a MERGE parent table, not its children. - - @note The children are expected to be closed separately by the caller. + Close a MERGE parent table, but not its children. @return status @retval 0 OK @retval != 0 Error, my_errno gives reason + + @note + The children are expected to be closed separately by the caller. */ int ha_myisammrg::close(void) @@ -691,11 +885,12 @@ int ha_myisammrg::close(void) int rc; DBUG_ENTER("ha_myisammrg::close"); /* - Children must not be attached here. Unless the MERGE table has no - children or the handler instance has been cloned. In these cases - children_attached is always true. + There are cases where children are not explicitly detached before + close. detach_children() protects itself against double detach. */ - DBUG_ASSERT(!this->file->children_attached || !this->file->tables || this->is_cloned); + if (!is_cloned) + detach_children(); + rc= myrg_close(file); file= 0; DBUG_RETURN(rc); @@ -973,13 +1168,23 @@ int ha_myisammrg::info(uint flag) int ha_myisammrg::extra(enum ha_extra_function operation) { - if (operation == HA_EXTRA_ATTACH_CHILDREN) + if (operation == HA_EXTRA_ADD_CHILDREN_LIST) + { + int rc= add_children_list(); + return(rc); + } + else if (operation == HA_EXTRA_ATTACH_CHILDREN) { int rc= attach_children(); if (!rc) (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL return(rc); } + else if (operation == HA_EXTRA_IS_ATTACHED_CHILDREN) + { + /* For the upper layer pretend empty MERGE union is never attached. */ + return(file && file->tables && file->children_attached); + } else if (operation == HA_EXTRA_DETACH_CHILDREN) { /* @@ -1000,6 +1205,7 @@ int ha_myisammrg::extra(enum ha_extra_function operation) int ha_myisammrg::reset(void) { + /* This is normally called with detached children. */ return myrg_reset(file); } @@ -1015,21 +1221,24 @@ int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size) int ha_myisammrg::external_lock(THD *thd, int lock_type) { - DBUG_ASSERT(this->file->children_attached); - return myrg_lock_database(file,lock_type); + /* + This can be called with no children attached. E.g. FLUSH TABLES + unlocks and re-locks tables under LOCK TABLES, but it does not open + them first. So they are detached all the time. But locking of the + children should work anyway because thd->open_tables is not changed + during FLUSH TABLES. + + If this handler instance has been cloned, we still must call + myrg_lock_database(). + */ + if (is_cloned) + return myrg_lock_database(file, lock_type); + return 0; } uint ha_myisammrg::lock_count(void) const { - /* - Return the real lock count even if the children are not attached. - This method is used for allocating memory. If we would return 0 - to another thread (e.g. doing FLUSH TABLE), and attach the children - before the other thread calls store_lock(), then we would return - more locks in store_lock() than we claimed by lock_count(). The - other tread would overrun its memory. - */ - return file->tables; + return 0; } @@ -1037,37 +1246,6 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { - MYRG_TABLE *open_table; - - /* - This method can be called while another thread is attaching the - children. If the processor reorders instructions or write to memory, - 'children_attached' could be set before 'open_tables' has all the - pointers to the children. Use of a mutex here and in - myrg_attach_children() forces consistent data. - */ - pthread_mutex_lock(&this->file->mutex); - - /* - When MERGE table is open, but not yet attached, other threads - could flush it, which means call mysql_lock_abort_for_thread() - on this threads TABLE. 'children_attached' is FALSE in this - situaton. Since the table is not locked, return no lock data. - */ - if (!this->file->children_attached) - goto end; /* purecov: tested */ - - for (open_table=file->open_tables ; - open_table != file->end_table ; - open_table++) - { - *(to++)= &open_table->table->lock; - if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK) - open_table->table->lock.type=lock_type; - } - - end: - pthread_mutex_unlock(&this->file->mutex); return to; } @@ -1156,7 +1334,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form, /* Allocate a table_names array in thread mem_root. */ if (!(table_names= (const char**) thd->alloc((create_info->merge_list.elements+1) * sizeof(char*)))) - DBUG_RETURN(HA_ERR_OUT_OF_MEM); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */ /* Create child path names. */ for (pos= table_names; tables; tables= tables->next_local) @@ -1266,7 +1444,7 @@ bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info, int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt) { - return HA_ADMIN_OK; + return this->file->children_attached ? HA_ADMIN_OK : HA_ADMIN_CORRUPT; } diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h index 790aa15e90a..c3803eb584b 100644 --- a/storage/myisammrg/ha_myisammrg.h +++ b/storage/myisammrg/ha_myisammrg.h @@ -28,6 +28,9 @@ class ha_myisammrg: public handler my_bool is_cloned; /* This instance has been cloned */ public: + MEM_ROOT children_mem_root; /* mem root for children list */ + TABLE_LIST *children_l; /* children list */ + TABLE_LIST **children_last_l; /* children list end */ TABLE_LIST *next_child_attach; /* next child to attach */ uint test_if_locked; /* flags from ::open() */ bool need_compat_check; /* if need compatibility check */ @@ -60,6 +63,7 @@ class ha_myisammrg: public handler { return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; } int open(const char *name, int mode, uint test_if_locked); + int add_children_list(void); int attach_children(void); int detach_children(void); virtual handler *clone(MEM_ROOT *mem_root); diff --git a/storage/myisammrg/myrg_extra.c b/storage/myisammrg/myrg_extra.c index 3d14f6a56e6..0b9c138a188 100644 --- a/storage/myisammrg/myrg_extra.c +++ b/storage/myisammrg/myrg_extra.c @@ -75,12 +75,17 @@ int myrg_reset(MYRG_INFO *info) MYRG_TABLE *file; DBUG_ENTER("myrg_reset"); - if (!info->children_attached) - DBUG_RETURN(1); info->cache_in_use=0; info->current_table=0; info->last_used_table= info->open_tables; - + + /* + This is normally called with detached children. + Return OK as this is the normal case. + */ + if (!info->children_attached) + DBUG_RETURN(0); + for (file=info->open_tables ; file != info->end_table ; file++) { int error; From 45a5d7971336620d37b153526f1d7c9da0f99c1d Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 14:12:53 +0300 Subject: [PATCH 077/466] Backport of: ------------------------------------------------------------ revno: 2630.4.39 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Thu 2008-06-26 13:08:27 +0400 message: Fix warnings about passing pointer to not fully-initialized THD object to constructor of base Open_tables_state classe, which appeared on Windows and were introduced by one of the patches implementing WL#3726 "DDL locking for all metadata objects". --- sql/sql_class.cc | 11 ++++------- sql/sql_class.h | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d36ea5c52c9..ff1b03102a6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -202,12 +202,6 @@ bool foreign_key_prefix(Key *a, Key *b) ** Thread specific functions ****************************************************************************/ -Open_tables_state::Open_tables_state(THD *thd, ulong version_arg) - :version(version_arg), state_flags(0U) -{ - reset_open_tables_state(thd); -} - /* The following functions form part of the C plugin API */ @@ -440,7 +434,7 @@ bool Drop_table_error_handler::handle_condition(THD *thd, THD::THD() :Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION, /* statement id */ 0), - Open_tables_state(this, refresh_version), rli_fake(0), + rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), sql_log_bin_toplevel(false), @@ -542,6 +536,9 @@ THD::THD() command=COM_CONNECT; *scramble= '\0'; + /* Call to init() below requires fully initialized Open_tables_state. */ + init_open_tables_state(this, refresh_version); + init(); #if defined(ENABLED_PROFILING) profiling.set_thd(this); diff --git a/sql/sql_class.h b/sql/sql_class.h index 68ae3afe931..a5236201724 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -984,13 +984,22 @@ public: MDL_CONTEXT mdl_context; MDL_CONTEXT handler_mdl_context; - /* - This constructor serves for creation of Open_tables_state instances - which are used as backup storage. + /** + This constructor initializes Open_tables_state instance which can only + be used as backup storage. To prepare Open_tables_state instance for + operations which open/lock/close tables (e.g. open_table()) one has to + call init_open_tables_state(). */ Open_tables_state() : state_flags(0U) { } - Open_tables_state(THD *thd, ulong version_arg); + /** + Prepare Open_tables_state instance for operations dealing with tables. + */ + void init_open_tables_state(THD *thd, ulong version_arg) + { + reset_open_tables_state(thd); + version= version_arg; + } void set_open_tables_state(Open_tables_state *state) { From 118d772ca809a77bb02eea894f5ee584e53ac2d9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 14:32:29 +0300 Subject: [PATCH 078/466] Backport of (WL#3726) ------------------------------------------------------------ revno: 2630.13.4 committer: Dmitry Lenev branch nick: mysql-6.0-runtime timestamp: Mon 2008-07-07 19:51:20 +0400 message: Fixed outdated comment describing mdl_init_lock() function. --- sql/mdl.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 0a663ad2e11..f28778a1a83 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -245,8 +245,9 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) @param lock_data Pointer to an MDL_LOCK_DATA object to initialize @param key_buff Pointer to the buffer for key for the lock request - (should be at least strlen(db) + strlen(name) - + 2 bytes, or, if the lengths are not known, MAX_DBNAME_LENGTH) + (should be at least 4+ strlen(db) + 1 + strlen(name) + + 1 bytes, or, if the lengths are not known, + MAX_MDLKEY_LENGTH) @param type Id of type of object to be locked @param db Name of database to which the object belongs @param name Name of of the object From 0f9c02d49a52c9faacb328649f9eab23b2d93a6b Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 14:41:29 +0300 Subject: [PATCH 079/466] WL#3726 "DDL locking", post-review fixes. ---------------------------------------------------------- revno: 2630.2.23 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Fri 2008-06-27 21:15:11 +0400 message: Add an assert that we never call COMMIT or ROLLBACK while having a table lock. --- sql/sql_parse.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 55ff9b5c835..62046dddcbd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3989,12 +3989,16 @@ end_with_restore_list: my_ok(thd); break; case SQLCOM_COMMIT: + DBUG_ASSERT(thd->lock == NULL || + thd->locked_tables_mode == LTM_LOCK_TABLES); if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE : lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT)) goto error; my_ok(thd); break; case SQLCOM_ROLLBACK: + DBUG_ASSERT(thd->lock == NULL || + thd->locked_tables_mode == LTM_LOCK_TABLES); if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE : lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK)) goto error; From 37edcc7e26ba924fd516a85c855e6d37859e6d47 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 14:47:05 +0300 Subject: [PATCH 080/466] Backport of: ---------------------------------------------------------- revno: 2630.2.7 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Wed 2008-06-04 15:18:52 +0400 message: Fix a code regression (not observable externally) that I introduced in the fix for Bug#26141 (backporting as part of all patches related to WL#3726) --- sql/sp.cc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/sql/sp.cc b/sql/sp.cc index f6bfba71726..e6999b480f2 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1693,6 +1693,12 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, int type= rt->key.str[0]; sp_head *sp; + /* + Triggers can't happen here: their bodies are always processed + in sp_cache_routines_and_add_tables_for_triggers(). + */ + DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); + if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? &thd->sp_func_cache : &thd->sp_proc_cache), &name))) @@ -1836,11 +1842,6 @@ int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, TABLE_LIST *table) { - int ret= 0; - - Sroutine_hash_entry **last_cached_routine_ptr= - (Sroutine_hash_entry **)lex->sroutines_list.next; - if (static_cast(table->lock_type) >= static_cast(TL_WRITE_ALLOW_WRITE)) { @@ -1857,21 +1858,29 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key, table->belong_to_view)) { + int ret; + /* Sic: excludes the trigger key from processing */ + Sroutine_hash_entry **last_cached_routine_ptr= + (Sroutine_hash_entry **)lex->sroutines_list.next; + trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last, table->belong_to_view); trigger->propagate_attributes(lex); sp_update_stmt_used_routines(thd, lex, &trigger->m_sroutines, table->belong_to_view); + + ret= sp_cache_routines_and_add_tables_aux(thd, lex, + *last_cached_routine_ptr, + FALSE); + if (ret) + return ret; } } } } } - ret= sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, - FALSE); - return ret; + return 0; } From 4ae05129dc51f87f3ff3528640465d2122db96f3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 18:47:20 +0300 Subject: [PATCH 081/466] Backport of: ------------------------------------------------------------ revno: 2630.13.16 committer: Davi Arnaut branch nick: WL#4284 timestamp: Sat 2008-07-26 13:38:20 -0300 message: WL#4284: Transactional DDL locking SQL statements' effect on transactions. Currently the MySQL server and its storage engines are not capable of rolling back operations that define or modify data structures (also known as DDL statements) or operations that alter any of the system tables (the mysql database). Allowing these group of statements to participate in transactions is unfeasible at this time (since rollback has no effect whatsoever on them) and goes against the design of our metadata locking subsystem. The solution is to issue implicit commits before and after those statements execution. This effectively confines each of those statements to its own special transaction and ensures that metadata locks taken during this special transaction are not leaked into posterior statements/transactions. --- mysql-test/include/commit.inc | 8 +- mysql-test/include/implicit_commit_helper.inc | 5 + mysql-test/r/commit_1innodb.result | 8 +- mysql-test/r/implicit_commit.result | 1061 +++++++++++++++ mysql-test/t/implicit_commit.test | 1162 +++++++++++++++++ sql/events.cc | 30 - sql/mysql_priv.h | 2 +- sql/sql_class.h | 47 + sql/sql_delete.cc | 13 +- sql/sql_parse.cc | 245 ++-- sql/sql_table.cc | 2 - tests/mysql_client_test.c | 54 + 12 files changed, 2484 insertions(+), 153 deletions(-) create mode 100644 mysql-test/include/implicit_commit_helper.inc create mode 100644 mysql-test/r/implicit_commit.result create mode 100644 mysql-test/t/implicit_commit.test diff --git a/mysql-test/include/commit.inc b/mysql-test/include/commit.inc index d91ba8291fd..d5f10a6d78e 100644 --- a/mysql-test/include/commit.inc +++ b/mysql-test/include/commit.inc @@ -725,15 +725,15 @@ call p_verify_status_increment(4, 4, 4, 4); alter table t3 add column (b int); call p_verify_status_increment(2, 0, 2, 0); alter table t3 rename t4; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(4, 4, 4, 4); rename table t4 to t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(0, 0, 0, 0); truncate table t3; call p_verify_status_increment(4, 4, 4, 4); create view v1 as select * from t2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 0, 2, 0); check table t1; -call p_verify_status_increment(3, 0, 3, 0); +call p_verify_status_increment(2, 0, 2, 0); --echo # Sic: after this bug is fixed, CHECK leaves no pending transaction commit; call p_verify_status_increment(0, 0, 0, 0); diff --git a/mysql-test/include/implicit_commit_helper.inc b/mysql-test/include/implicit_commit_helper.inc new file mode 100644 index 00000000000..5e87b2db079 --- /dev/null +++ b/mysql-test/include/implicit_commit_helper.inc @@ -0,0 +1,5 @@ +INSERT INTO db1.trans (a) VALUES (1); +--disable_result_log +eval $statement; +--enable_result_log +CALL db1.test_if_commit(); diff --git a/mysql-test/r/commit_1innodb.result b/mysql-test/r/commit_1innodb.result index 51c4ac3002c..bbff677ab3f 100644 --- a/mysql-test/r/commit_1innodb.result +++ b/mysql-test/r/commit_1innodb.result @@ -841,11 +841,11 @@ call p_verify_status_increment(2, 0, 2, 0); SUCCESS alter table t3 rename t4; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(4, 4, 4, 4); SUCCESS rename table t4 to t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(0, 0, 0, 0); SUCCESS truncate table t3; @@ -853,13 +853,13 @@ call p_verify_status_increment(4, 4, 4, 4); SUCCESS create view v1 as select * from t2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 0, 2, 0); SUCCESS check table t1; Table Op Msg_type Msg_text test.t1 check status OK -call p_verify_status_increment(3, 0, 3, 0); +call p_verify_status_increment(2, 0, 2, 0); SUCCESS # Sic: after this bug is fixed, CHECK leaves no pending transaction diff --git a/mysql-test/r/implicit_commit.result b/mysql-test/r/implicit_commit.result new file mode 100644 index 00000000000..8c330550a3b --- /dev/null +++ b/mysql-test/r/implicit_commit.result @@ -0,0 +1,1061 @@ +SET GLOBAL EVENT_SCHEDULER = OFF; +SET BINLOG_FORMAT = STATEMENT; +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1 (a INT, KEY a(a)) ENGINE=INNODB; +INSERT INTO t1 VALUES (1),(2),(3),(4),(5); +CREATE TABLE t3 (a INT) ENGINE=MyISAM; +INSERT INTO t3 SELECT * FROM t1; +CREATE TABLE trans (a INT) ENGINE=INNODB; +CREATE PROCEDURE test_if_commit() +BEGIN +ROLLBACK; +SELECT IF (COUNT(*) > 0, "YES", "NO") AS "IMPLICIT COMMIT" FROM trans; +DELETE FROM trans; +COMMIT; +END| +SET AUTOCOMMIT = FALSE; +# +# SQLCOM_SELECT +# +INSERT INTO db1.trans (a) VALUES (1); +select 1 as res from t1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_TABLE LIKE +# +INSERT INTO db1.trans (a) VALUES (1); +create table t2 like t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE +# +INSERT INTO db1.trans (a) VALUES (1); +show create table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +drop table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_TABLE TEMPORARY +# +INSERT INTO db1.trans (a) VALUES (1); +create temporary table t2 as select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_TABLE TEMPORARY +# +INSERT INTO db1.trans (a) VALUES (1); +drop temporary table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +create table t2 as select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_UPDATE +# +INSERT INTO db1.trans (a) VALUES (1); +update t2 set a=a+1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_INSERT +# +INSERT INTO db1.trans (a) VALUES (1); +insert into t2 set a=((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_INSERT_SELECT +# +INSERT INTO db1.trans (a) VALUES (1); +insert into t2 select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_REPLACE +# +INSERT INTO db1.trans (a) VALUES (1); +replace t2 set a=((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_REPLACE_SELECT +# +INSERT INTO db1.trans (a) VALUES (1); +replace t2 select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DELETE +# +INSERT INTO db1.trans (a) VALUES (1); +delete from t2 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DELETE_MULTI +# +INSERT INTO db1.trans (a) VALUES (1); +delete t2, t3 from t2, t3 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_UPDATE_MULTI +# +select * from t2; +a +INSERT INTO db1.trans (a) VALUES (1); +update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_LOAD +# +create table t4 (a varchar(100)); +INSERT INTO db1.trans (a) VALUES (1); +load data infile '../../std_data/words.dat' into table t4; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +drop table t4; +# +# SQLCOM_SHOW_DATABASES +# +INSERT INTO db1.trans (a) VALUES (1); +show databases where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +show tables where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_FIELDS +# +INSERT INTO db1.trans (a) VALUES (1); +show fields from t1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_KEYS +# +INSERT INTO db1.trans (a) VALUES (1); +show keys from t1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_VARIABLES +# +INSERT INTO db1.trans (a) VALUES (1); +show variables where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS +# +INSERT INTO db1.trans (a) VALUES (1); +show status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_ENGINE_MUTEX +# +INSERT INTO db1.trans (a) VALUES (1); +show engine all mutex; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PROCESSLIST +# +INSERT INTO db1.trans (a) VALUES (1); +show processlist; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_ENGINE_LOGS +# +INSERT INTO db1.trans (a) VALUES (1); +show engine all logs; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_ENGINE_STATUS +# +INSERT INTO db1.trans (a) VALUES (1); +show engine all status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_CHARSETS +# +INSERT INTO db1.trans (a) VALUES (1); +show charset where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_COLLATIONS +# +INSERT INTO db1.trans (a) VALUES (1); +show collation where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_TABLE_STATUS +# +INSERT INTO db1.trans (a) VALUES (1); +show table status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_TRIGGERS +# +INSERT INTO db1.trans (a) VALUES (1); +show triggers where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_OPEN_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +show open tables where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_PROC +# +INSERT INTO db1.trans (a) VALUES (1); +show procedure status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_FUNC +# +INSERT INTO db1.trans (a) VALUES (1); +show function status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SET_OPTION +# +INSERT INTO db1.trans (a) VALUES (1); +set @a=((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DO +# +INSERT INTO db1.trans (a) VALUES (1); +do ((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CALL +# +create procedure p1(a int) begin end; +INSERT INTO db1.trans (a) VALUES (1); +call p1((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +drop procedure p1; +# +# SQLCOM_CREATE_VIEW +# +INSERT INTO db1.trans (a) VALUES (1); +create view v1 as select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_VIEW +# +INSERT INTO db1.trans (a) VALUES (1); +alter view v1 as select 2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_DROP_VIEW +# +INSERT INTO db1.trans (a) VALUES (1); +drop view v1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_INDEX +# +INSERT INTO db1.trans (a) VALUES (1); +create index idx1 on t1(a); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_DROP_INDEX +# +INSERT INTO db1.trans (a) VALUES (1); +drop index idx1 on t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 add column b int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 change b c int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 drop column c; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_TABLE TEMPORARY +# +create temporary table t4 (a int); +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 add column b int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 change b c int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 drop column c; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +drop table t4; +# +# SQLCOM_TRUNCATE +# +insert into t2 select * from t1; +INSERT INTO db1.trans (a) VALUES (1); +truncate table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +insert into t2 select * from t1; +# +# SQLCOM_TRUNCATE TEMPORARY +# +create temporary table t4 as select * from t1; +INSERT INTO db1.trans (a) VALUES (1); +truncate table t4; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +drop temporary table t4; +# +# SQLCOM_SHOW_MASTER_STAT +# +INSERT INTO db1.trans (a) VALUES (1); +show master status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_SLAVE_STAT +# +INSERT INTO db1.trans (a) VALUES (1); +show slave status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_GRANT +# +INSERT INTO db1.trans (a) VALUES (1); +grant all on test.t1 to mysqltest_2@localhost with grant option; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_REVOKE +# +INSERT INTO db1.trans (a) VALUES (1); +revoke select on test.t1 from mysqltest_2@localhost; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_REVOKE_ALL +# +INSERT INTO db1.trans (a) VALUES (1); +revoke all on test.t1 from mysqltest_2@localhost; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +drop user mysqltest_2@localhost; +# +# SQLCOM_SHOW_GRANTS +# +INSERT INTO db1.trans (a) VALUES (1); +show grants; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +INSERT INTO db1.trans (a) VALUES (1); +show grants for current_user(); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_LOCK_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +lock tables t1 write, trans write; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_UNLOCK_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +unlock tables; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_DB +# +INSERT INTO db1.trans (a) VALUES (1); +create database db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHANGE_DB +# +create table db2.t1 (a int); +insert into db2.t1 values (1); +commit; +INSERT INTO db1.trans (a) VALUES (1); +use db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_CREATE_DB +# +INSERT INTO db1.trans (a) VALUES (1); +show create database db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_ALTER_DB +# +# +# SQLCOM_ALTER_DB_UPGRADE +# +# +# SQLCOM_DROP_DB +# +use db1; +INSERT INTO db1.trans (a) VALUES (1); +drop database db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_REPAIR +# +INSERT INTO db1.trans (a) VALUES (1); +repair table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +repair table t2 use_frm; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_OPTIMIZE +# +INSERT INTO db1.trans (a) VALUES (1); +optimize table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHECK +# +INSERT INTO db1.trans (a) VALUES (1); +check table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +check table t1 extended; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ASSIGN_TO_KEYCACHE +# +set global keycache.key_buffer_size=128*1024; +INSERT INTO db1.trans (a) VALUES (1); +cache index t3 in keycache; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +set global keycache.key_buffer_size=0; +# +# SQLCOM_PRELOAD_KEYS +# +INSERT INTO db1.trans (a) VALUES (1); +load index into cache t3; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_FLUSH +# +INSERT INTO db1.trans (a) VALUES (1); +flush local privileges; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +flush privileges; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_KILL +# +# +# SQLCOM_ANALYZE +# +INSERT INTO db1.trans (a) VALUES (1); +analyze table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ROLLBACK +# +INSERT INTO db1.trans (a) VALUES (1); +rollback; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_ROLLBACK_TO_SAVEPOINT +# +# +# SQLCOM_COMMIT +# +INSERT INTO db1.trans (a) VALUES (1); +commit; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SAVEPOINT +# +INSERT INTO db1.trans (a) VALUES (1); +savepoint sp1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_RELEASE_SAVEPOINT +# +# +# SQLCOM_SLAVE_START +# +# +# SQLCOM_SLAVE_STOP +# +# +# SQLCOM_BEGIN +# +INSERT INTO db1.trans (a) VALUES (1); +begin; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHANGE_MASTER +# +# +# SQLCOM_RENAME_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +rename table t3 to t4; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +rename table t4 to t3; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_RESET +# +# +# SQLCOM_PURGE +# +# +# SQLCOM_PURGE_BEFORE +# +# +# SQLCOM_SHOW_BINLOGS +# +# +# SQLCOM_HA_OPEN +# +INSERT INTO db1.trans (a) VALUES (1); +handler t1 open as ha1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_HA_READ +# +INSERT INTO db1.trans (a) VALUES (1); +handler ha1 read a first; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_HA_CLOSE +# +INSERT INTO db1.trans (a) VALUES (1); +handler ha1 close; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_SLAVE_HOSTS +# +INSERT INTO db1.trans (a) VALUES (1); +show slave hosts; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_BINLOG_EVENTS +# +INSERT INTO db1.trans (a) VALUES (1); +show binlog events; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_NEW_MASTER +# +# +# SQLCOM_SHOW_WARNS +# +INSERT INTO db1.trans (a) VALUES (1); +show warnings; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_EMPTY_QUERY +# +# +# SQLCOM_SHOW_ERRORS +# +INSERT INTO db1.trans (a) VALUES (1); +show errors; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STORAGE_ENGINES +# +INSERT INTO db1.trans (a) VALUES (1); +show engines; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PRIVILEGES +# +INSERT INTO db1.trans (a) VALUES (1); +show privileges; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_HELP +# +INSERT INTO db1.trans (a) VALUES (1); +help 'foo'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_USER +# +INSERT INTO db1.trans (a) VALUES (1); +create user trxusr1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_RENAME_USER +# +INSERT INTO db1.trans (a) VALUES (1); +rename user 'trxusr1' to 'trxusr2'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_DROP_USER +# +INSERT INTO db1.trans (a) VALUES (1); +drop user trxusr2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHECKSUM +# +INSERT INTO db1.trans (a) VALUES (1); +checksum table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_PROCEDURE +# +INSERT INTO db1.trans (a) VALUES (1); +create procedure p1(a int) begin end; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_PROCEDURE +# +INSERT INTO db1.trans (a) VALUES (1); +alter procedure p1 comment 'foobar'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_PROC +# +INSERT INTO db1.trans (a) VALUES (1); +show create procedure p1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_PROC +# +INSERT INTO db1.trans (a) VALUES (1); +show procedure status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PROC_CODE +# +INSERT INTO db1.trans (a) VALUES (1); +show procedure code p1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_PROCEDURE +# +INSERT INTO db1.trans (a) VALUES (1); +drop procedure p1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_FUNCTION +# +# +# SQLCOM_DROP_FUNCTION +# +# +# SQLCOM_CREATE_SPFUNCTION +# +INSERT INTO db1.trans (a) VALUES (1); +create function f1() returns int return 69; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_FUNCTION +# +INSERT INTO db1.trans (a) VALUES (1); +alter function f1 comment 'comment'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_FUNC +# +INSERT INTO db1.trans (a) VALUES (1); +show create function f1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_FUNC +# +INSERT INTO db1.trans (a) VALUES (1); +show function status like '%f%'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_FUNC_CODE +# +INSERT INTO db1.trans (a) VALUES (1); +show function code f1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_PREPARE +# +INSERT INTO db1.trans (a) VALUES (1); +prepare stmt1 from "insert into t1 values (5)"; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_EXECUTE +# +INSERT INTO db1.trans (a) VALUES (1); +execute stmt1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DEALLOCATE_PREPARE +# +INSERT INTO db1.trans (a) VALUES (1); +deallocate prepare stmt1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_TRIGGER +# +INSERT INTO db1.trans (a) VALUES (1); +create trigger trg1 before insert on t1 for each row set @a:=1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_TRIGGER +# +INSERT INTO db1.trans (a) VALUES (1); +show create trigger trg1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_TRIGGER +# +INSERT INTO db1.trans (a) VALUES (1); +drop trigger trg1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_XA_START +# +# +# SQLCOM_XA_END +# +# +# SQLCOM_XA_PREPARE +# +# +# SQLCOM_XA_COMMIT +# +# +# SQLCOM_XA_ROLLBACK +# +# +# SQLCOM_XA_RECOVER +# +# +# SQLCOM_ALTER_TABLESPACE +# +# +# SQLCOM_INSTALL_PLUGIN +# +# +# SQLCOM_SHOW_PLUGINS +# +# +# SQLCOM_UNINSTALL_PLUGIN +# +# +# SQLCOM_SHOW_AUTHORS +# +INSERT INTO db1.trans (a) VALUES (1); +show authors; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_BINLOG_BASE64_EVENT +# +# +# SQLCOM_SHOW_CONTRIBUTORS +# +INSERT INTO db1.trans (a) VALUES (1); +show contributors; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_SERVER +# +# +# SQLCOM_ALTER_SERVER +# +# +# SQLCOM_DROP_SERVER +# +# +# SQLCOM_CREATE_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +create event ev1 on schedule every 1 second do insert into t1 values (6); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +alter event ev1 rename to ev2 disable; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +show create event ev2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_EVENTS +# +INSERT INTO db1.trans (a) VALUES (1); +show events; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +drop event ev2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_BACKUP +# +# +# SQLCOM_SHOW_ARCHIVE +# +# +# SQLCOM_RESTORE +# +# +# SQLCOM_BACKUP_TEST +# +# +# SQLCOM_SHOW_PROFILE +# +INSERT INTO db1.trans (a) VALUES (1); +show profile memory; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PROFILES +# +INSERT INTO db1.trans (a) VALUES (1); +show profiles; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +USE test; +DROP DATABASE db1; +End of tests diff --git a/mysql-test/t/implicit_commit.test b/mysql-test/t/implicit_commit.test new file mode 100644 index 00000000000..ed451877c77 --- /dev/null +++ b/mysql-test/t/implicit_commit.test @@ -0,0 +1,1162 @@ +source include/have_innodb.inc; +source include/have_log_bin.inc; + +SET GLOBAL EVENT_SCHEDULER = OFF; +SET BINLOG_FORMAT = STATEMENT; + +LET $OLD_DB= `SELECT DATABASE()`; + +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1 (a INT, KEY a(a)) ENGINE=INNODB; +INSERT INTO t1 VALUES (1),(2),(3),(4),(5); +CREATE TABLE t3 (a INT) ENGINE=MyISAM; +INSERT INTO t3 SELECT * FROM t1; +CREATE TABLE trans (a INT) ENGINE=INNODB; + +DELIMITER |; + +CREATE PROCEDURE test_if_commit() +BEGIN + ROLLBACK; + SELECT IF (COUNT(*) > 0, "YES", "NO") AS "IMPLICIT COMMIT" FROM trans; + DELETE FROM trans; + COMMIT; +END| + +DELIMITER ;| + +SET AUTOCOMMIT = FALSE; + +--echo # +--echo # SQLCOM_SELECT +--echo # + +let $statement= + select 1 as res from t1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TABLE LIKE +--echo # + +let $statement= + create table t2 like t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE +--echo # + +let $statement= + show create table t2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_TABLE +--echo # + +let $statement= + drop table t2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TABLE TEMPORARY +--echo # + +let $statement= + create temporary table t2 as select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_TABLE TEMPORARY +--echo # + +let $statement= + drop temporary table t2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TABLE +--echo # + +let $statement= + create table t2 as select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_UPDATE +--echo # + +let $statement= + update t2 set a=a+1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_INSERT +--echo # + +let $statement= + insert into t2 set a=((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_INSERT_SELECT +--echo # + +let $statement= + insert into t2 select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REPLACE +--echo # + +let $statement= + replace t2 set a=((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REPLACE_SELECT +--echo # + +let $statement= + replace t2 select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DELETE +--echo # + +let $statement= + delete from t2 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DELETE_MULTI +--echo # + +let $statement= + delete t2, t3 from t2, t3 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_UPDATE_MULTI +--echo # + +select * from t2; +let $statement= + update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_LOAD +--echo # + +create table t4 (a varchar(100)); + +let $statement= + load data infile '../../std_data/words.dat' into table t4; +source include/implicit_commit_helper.inc; + +drop table t4; + +--echo # +--echo # SQLCOM_SHOW_DATABASES +--echo # + +let $statement= + show databases where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_TABLES +--echo # + +let $statement= + show tables where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_FIELDS +--echo # + +let $statement= + show fields from t1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_KEYS +--echo # + +let $statement= + show keys from t1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_VARIABLES +--echo # + +let $statement= + show variables where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS +--echo # + +let $statement= + show status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_MUTEX +--echo # + +let $statement= + show engine all mutex; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PROCESSLIST +--echo # + +let $statement= + show processlist; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_LOGS +--echo # + +let $statement= + show engine all logs; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_STATUS +--echo # + +let $statement= + show engine all status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CHARSETS +--echo # + +let $statement= + show charset where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_COLLATIONS +--echo # + +let $statement= + show collation where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_TABLE_STATUS +--echo # + +let $statement= + show table status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_TRIGGERS +--echo # + +let $statement= + show triggers where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_OPEN_TABLES +--echo # + +let $statement= + show open tables where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_PROC +--echo # + +let $statement= + show procedure status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_FUNC +--echo # + +let $statement= + show function status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SET_OPTION +--echo # + +let $statement= + set @a=((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DO +--echo # + +let $statement= + do ((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CALL +--echo # + +create procedure p1(a int) begin end; + +let $statement= + call p1((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +drop procedure p1; + +--echo # +--echo # SQLCOM_CREATE_VIEW +--echo # + +let $statement= + create view v1 as select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_VIEW +--echo # + +let $statement= + alter view v1 as select 2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_VIEW +--echo # + +let $statement= + drop view v1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_INDEX +--echo # + +let $statement= + create index idx1 on t1(a); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_INDEX +--echo # + +let $statement= + drop index idx1 on t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_TABLE +--echo # + +let $statement= + alter table t1 add column b int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 change b c int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 drop column c; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_TABLE TEMPORARY +--echo # + +create temporary table t4 (a int); + +let $statement= + alter table t1 add column b int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 change b c int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 drop column c; +source include/implicit_commit_helper.inc; + +drop table t4; + +--echo # +--echo # SQLCOM_TRUNCATE +--echo # + +insert into t2 select * from t1; +let $statement= + truncate table t2; +source include/implicit_commit_helper.inc; +insert into t2 select * from t1; + +--echo # +--echo # SQLCOM_TRUNCATE TEMPORARY +--echo # + +create temporary table t4 as select * from t1; +let $statement= + truncate table t4; +source include/implicit_commit_helper.inc; +drop temporary table t4; + +--echo # +--echo # SQLCOM_SHOW_MASTER_STAT +--echo # + +let $statement= + show master status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_SLAVE_STAT +--echo # + +let $statement= + show slave status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_GRANT +--echo # + +let $statement= + grant all on test.t1 to mysqltest_2@localhost with grant option; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REVOKE +--echo # +let $statement= + revoke select on test.t1 from mysqltest_2@localhost; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REVOKE_ALL +--echo # + +let $statement= + revoke all on test.t1 from mysqltest_2@localhost; +source include/implicit_commit_helper.inc; + +drop user mysqltest_2@localhost; + +--echo # +--echo # SQLCOM_SHOW_GRANTS +--echo # + +let $statement= + show grants; +source include/implicit_commit_helper.inc; + +let $statement= + show grants for current_user(); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_LOCK_TABLES +--echo # + +let $statement= + lock tables t1 write, trans write; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_UNLOCK_TABLES +--echo # + +let $statement= + unlock tables; +source include/implicit_commit_helper.inc; + +# +# Missing test for lock tables transactional. +# + +--echo # +--echo # SQLCOM_CREATE_DB +--echo # + +let $statement= + create database db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHANGE_DB +--echo # + +create table db2.t1 (a int); +insert into db2.t1 values (1); +commit; + +let $statement= + use db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_DB +--echo # + +let $statement= + show create database db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_DB +--echo # + +#let $statement= +# alter database db2 character set koi8r; +#source include/implicit_commit_helper.inc; + +#let $statement= +# alter database db2 collate cp1251_general_cs; +#source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_DB_UPGRADE +--echo # + +#let $statement= +# alter database `#mysql50#db3` upgrade data directory name; +#source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_DB +--echo # + +use db1; + +let $statement= + drop database db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REPAIR +--echo # + +let $statement= + repair table t2; +source include/implicit_commit_helper.inc; + +let $statement= + repair table t2 use_frm; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_OPTIMIZE +--echo # + +let $statement= + optimize table t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHECK +--echo # + +let $statement= + check table t1; +source include/implicit_commit_helper.inc; + +let $statement= + check table t1 extended; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ASSIGN_TO_KEYCACHE +--echo # + +set global keycache.key_buffer_size=128*1024; + +let $statement= + cache index t3 in keycache; +source include/implicit_commit_helper.inc; + +set global keycache.key_buffer_size=0; + +--echo # +--echo # SQLCOM_PRELOAD_KEYS +--echo # + +let $statement= + load index into cache t3; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_FLUSH +--echo # + +let $statement= + flush local privileges; +source include/implicit_commit_helper.inc; + +let $statement= + flush privileges; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_KILL +--echo # + +--echo # +--echo # SQLCOM_ANALYZE +--echo # + +let $statement= + analyze table t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ROLLBACK +--echo # + +let $statement= + rollback; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ROLLBACK_TO_SAVEPOINT +--echo # + + +--echo # +--echo # SQLCOM_COMMIT +--echo # + +let $statement= + commit; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SAVEPOINT +--echo # + +let $statement= + savepoint sp1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_RELEASE_SAVEPOINT +--echo # + +--echo # +--echo # SQLCOM_SLAVE_START +--echo # + +--echo # +--echo # SQLCOM_SLAVE_STOP +--echo # + +--echo # +--echo # SQLCOM_BEGIN +--echo # + +let $statement= + begin; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHANGE_MASTER +--echo # + +--echo # +--echo # SQLCOM_RENAME_TABLE +--echo # + +let $statement= + rename table t3 to t4; +source include/implicit_commit_helper.inc; + +let $statement= + rename table t4 to t3; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_RESET +--echo # + +--echo # +--echo # SQLCOM_PURGE +--echo # + +--echo # +--echo # SQLCOM_PURGE_BEFORE +--echo # + +--echo # +--echo # SQLCOM_SHOW_BINLOGS +--echo # + +--echo # +--echo # SQLCOM_HA_OPEN +--echo # + +let $statement= + handler t1 open as ha1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_HA_READ +--echo # + +let $statement= + handler ha1 read a first; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_HA_CLOSE +--echo # + +let $statement= + handler ha1 close; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_SLAVE_HOSTS +--echo # + +let $statement= + show slave hosts; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_BINLOG_EVENTS +--echo # + +let $statement= + show binlog events; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_NEW_MASTER +--echo # + +--echo # +--echo # SQLCOM_SHOW_WARNS +--echo # + +let $statement= + show warnings; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_EMPTY_QUERY +--echo # + +--echo # +--echo # SQLCOM_SHOW_ERRORS +--echo # + +let $statement= + show errors; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STORAGE_ENGINES +--echo # + +let $statement= + show engines; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PRIVILEGES +--echo # + +let $statement= + show privileges; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_HELP +--echo # + +let $statement= + help 'foo'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_USER +--echo # + +let $statement= + create user trxusr1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_RENAME_USER +--echo # + +let $statement= + rename user 'trxusr1' to 'trxusr2'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_USER +--echo # + +let $statement= + drop user trxusr2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHECKSUM +--echo # + +let $statement= + checksum table t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_PROCEDURE +--echo # + +let $statement= + create procedure p1(a int) begin end; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_PROCEDURE +--echo # + +let $statement= + alter procedure p1 comment 'foobar'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_PROC +--echo # + +let $statement= + show create procedure p1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_PROC +--echo # + +let $statement= + show procedure status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PROC_CODE +--echo # + +# +# Available only on servers with debugging support. +# + +--disable_abort_on_error +let $statement= + show procedure code p1; +source include/implicit_commit_helper.inc; +--enable_abort_on_error + +--echo # +--echo # SQLCOM_DROP_PROCEDURE +--echo # + +let $statement= + drop procedure p1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_FUNCTION +--echo # + +--echo # +--echo # SQLCOM_DROP_FUNCTION +--echo # + +--echo # +--echo # SQLCOM_CREATE_SPFUNCTION +--echo # + +let $statement= + create function f1() returns int return 69; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_FUNCTION +--echo # + +let $statement= + alter function f1 comment 'comment'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_FUNC +--echo # + +let $statement= + show create function f1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_FUNC +--echo # + +let $statement= + show function status like '%f%'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_FUNC_CODE +--echo # + +# +# Available only on servers with debugging support. +# + +--disable_abort_on_error +let $statement= + show function code f1; +source include/implicit_commit_helper.inc; +--enable_abort_on_error + +--echo # +--echo # SQLCOM_PREPARE +--echo # + +let $statement= + prepare stmt1 from "insert into t1 values (5)"; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_EXECUTE +--echo # + +let $statement= + execute stmt1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DEALLOCATE_PREPARE +--echo # + +let $statement= + deallocate prepare stmt1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TRIGGER +--echo # + +let $statement= + create trigger trg1 before insert on t1 for each row set @a:=1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_TRIGGER +--echo # + +let $statement= + show create trigger trg1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_TRIGGER +--echo # + +let $statement= + drop trigger trg1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_XA_START +--echo # + +--echo # +--echo # SQLCOM_XA_END +--echo # + +--echo # +--echo # SQLCOM_XA_PREPARE +--echo # + +--echo # +--echo # SQLCOM_XA_COMMIT +--echo # + +--echo # +--echo # SQLCOM_XA_ROLLBACK +--echo # + +--echo # +--echo # SQLCOM_XA_RECOVER +--echo # + +--echo # +--echo # SQLCOM_ALTER_TABLESPACE +--echo # + +--echo # +--echo # SQLCOM_INSTALL_PLUGIN +--echo # + +--echo # +--echo # SQLCOM_SHOW_PLUGINS +--echo # + +--echo # +--echo # SQLCOM_UNINSTALL_PLUGIN +--echo # + +--echo # +--echo # SQLCOM_SHOW_AUTHORS +--echo # + +let $statement= + show authors; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_BINLOG_BASE64_EVENT +--echo # + +--echo # +--echo # SQLCOM_SHOW_CONTRIBUTORS +--echo # + +let $statement= + show contributors; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_SERVER +--echo # + +--echo # +--echo # SQLCOM_ALTER_SERVER +--echo # + +--echo # +--echo # SQLCOM_DROP_SERVER +--echo # + +--echo # +--echo # SQLCOM_CREATE_EVENT +--echo # + +let $statement= + create event ev1 on schedule every 1 second do insert into t1 values (6); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_EVENT +--echo # + +let $statement= + alter event ev1 rename to ev2 disable; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_EVENT +--echo # + +let $statement= + show create event ev2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_EVENTS +--echo # + +let $statement= + show events; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_EVENT +--echo # + +let $statement= + drop event ev2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_BACKUP +--echo # + +#create database backup_db; +# +#let $statement= +# backup database db1 to 'backup_db1.ba'; +#source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ARCHIVE +--echo # + +# +# --error ER_NOT_ALLOWED_COMMAND +# +#let $statement= +# show backup 'backup_db1.ba'; +#source include/implicit_commit_helper.inc; +# + +--echo # +--echo # SQLCOM_RESTORE +--echo # + +#let $statement= +# restore from 'backup_db1.ba'; +#source include/implicit_commit_helper.inc; + +#--remove_file $MYSQLTEST_VARDIR/master-data/backup_db1.ba +# +#drop database backup_db; + +--echo # +--echo # SQLCOM_BACKUP_TEST +--echo # + +# BACKUP_TEST + +--echo # +--echo # SQLCOM_SHOW_PROFILE +--echo # + +let $statement= + show profile memory; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PROFILES +--echo # + +let $statement= + show profiles; +source include/implicit_commit_helper.inc; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +eval USE $OLD_DB; +DROP DATABASE db1; + +--echo End of tests diff --git a/sql/events.cc b/sql/events.cc index f7ff2b0ccf1..8c9e60e73e8 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -391,15 +391,6 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, int ret; DBUG_ENTER("Events::create_event"); - /* - Let's commit the transaction first - MySQL manual specifies - that a DDL issues an implicit commit, and it doesn't say "successful - DDL", so that an implicit commit is a property of any successfully - parsed DDL statement. - */ - if (end_active_trans(thd)) - DBUG_RETURN(TRUE); - if (check_if_system_tables_error()) DBUG_RETURN(TRUE); @@ -512,13 +503,6 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, DBUG_ENTER("Events::update_event"); - /* - For consistency, implicit COMMIT should be the first thing in the - execution chain. - */ - if (end_active_trans(thd)) - DBUG_RETURN(TRUE); - if (check_if_system_tables_error()) DBUG_RETURN(TRUE); @@ -635,20 +619,6 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) int ret; DBUG_ENTER("Events::drop_event"); - /* - In MySQL, DDL must always commit: since mysql.* tables are - non-transactional, we must modify them outside a transaction - to not break atomicity. - But the second and more important reason to commit here - regardless whether we're actually changing mysql.event table - or not is replication: end_active_trans syncs the binary log, - and unless we run DDL in it's own transaction it may simply - never appear on the slave in case the outside transaction - rolls back. - */ - if (end_active_trans(thd)) - DBUG_RETURN(TRUE); - if (check_if_system_tables_error()) DBUG_RETURN(TRUE); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 38a55e9ec4c..72bc20238fc 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2014,8 +2014,8 @@ extern struct my_option my_long_options[]; extern const LEX_STRING view_type; extern scheduler_functions thread_scheduler; extern TYPELIB thread_handling_typelib; -extern uint8 uc_update_queries[SQLCOM_END+1]; extern uint sql_command_flags[]; +extern uint server_command_flags[]; extern TYPELIB log_output_typelib; /* optional things, have_* variables */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 3a6da64ca4a..bef8dff5759 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3152,6 +3152,36 @@ public: joins are currently prohibited in these statements. */ #define CF_REEXECUTION_FRAGILE (1U << 5) +/** + Implicitly commit before the SQL statement is executed. + + Statements marked with this flag will cause any active + transaction to end (commit) before proceeding with the + command execution. + + This flag should be set for statements that probably can't + be rolled back or that do not expect any previously metadata + locked tables. +*/ +#define CF_IMPLICT_COMMIT_BEGIN (1U << 6) +/** + Implicitly commit after the SQL statement. + + Statements marked with this flag are automatically committed + at the end of the statement. + + This flag should be set for statements that will implicitly + open and take metadata locks on system tables that should not + be carried for the whole duration of a active transaction. +*/ +#define CF_IMPLICIT_COMMIT_END (1U << 7) +/** + CF_IMPLICT_COMMIT_BEGIN and CF_IMPLICIT_COMMIT_END are used + to ensure that the active transaction is implicitly committed + before and after every DDL statement and any statement that + modifies our currently non-transactional system tables. +*/ +#define CF_AUTO_COMMIT_TRANS (CF_IMPLICT_COMMIT_BEGIN | CF_IMPLICIT_COMMIT_END) /** Diagnostic statement. @@ -3163,6 +3193,23 @@ public: */ #define CF_DIAGNOSTIC_STMT (1U << 8) +/* Bits in server_command_flags */ + +/** + Skip the increase of the global query id counter. Commonly set for + commands that are stateless (won't cause any change on the server + internal states). +*/ +#define CF_SKIP_QUERY_ID (1U << 0) + +/** + Skip the increase of the number of statements that clients have + sent to the server. Commonly used for commands that will cause + a statement to be executed but the statement might have not been + sent by the user (ie: stored procedure). +*/ +#define CF_SKIP_QUESTIONS (1U << 1) + /* Functions in sql_class.cc */ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 4827d48e1c5..c0162605e2f 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1062,9 +1062,18 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) table_list->lock_type= TL_WRITE; mysql_init_select(thd->lex); thd->clear_current_stmt_binlog_row_based(); + /* Delete all rows from table */ error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE); - ha_autocommit_or_rollback(thd, error); - end_trans(thd, error ? ROLLBACK : COMMIT); + /* + All effects of a TRUNCATE TABLE operation are rolled back if a row by row + deletion fails. Otherwise, operation is automatically committed at the end. + */ + if (error) + { + DBUG_ASSERT(thd->stmt_da->is_error()); + ha_autocommit_or_rollback(thd, TRUE); + end_active_trans(thd); + } thd->current_stmt_binlog_row_based= save_binlog_row_based; DBUG_RETURN(error); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 62046dddcbd..660241e8e2c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -221,6 +221,50 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) } +/* + Implicitly commit a active transaction if statement requires so. + + @param thd Thread handle. + @param mask Bitmask used for the SQL command match. + +*/ +static bool opt_implicit_commit(THD *thd, uint mask) +{ + LEX *lex= thd->lex; + bool res= FALSE, skip= FALSE; + DBUG_ENTER("opt_implicit_commit"); + + if (!(sql_command_flags[lex->sql_command] & mask)) + DBUG_RETURN(FALSE); + + switch (lex->sql_command) { + case SQLCOM_DROP_TABLE: + skip= lex->drop_temporary; + break; + case SQLCOM_ALTER_TABLE: + case SQLCOM_CREATE_TABLE: + /* If CREATE TABLE of non-temporary table, do implicit commit */ + skip= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); + break; + case SQLCOM_SET_OPTION: + skip= lex->autocommit ? FALSE : TRUE; + break; + default: + break; + } + + if (!skip) + { + /* Commit or rollback the statement transaction. */ + ha_autocommit_or_rollback(thd, thd->is_error()); + /* Commit the normal transaction if one is active. */ + res= end_active_trans(thd); + } + + DBUG_RETURN(res); +} + + /** Mark all commands that somehow changes a table. @@ -235,26 +279,44 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) */ uint sql_command_flags[SQLCOM_END+1]; +uint server_command_flags[COM_END+1]; void init_update_queries(void) { - bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags)); + /* Initialize the server command flags array. */ + memset(server_command_flags, 0, sizeof(server_command_flags)); - sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; - sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; - sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA; + server_command_flags[COM_STATISTICS]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS; + server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS; + server_command_flags[COM_STMT_PREPARE]= CF_SKIP_QUESTIONS; + server_command_flags[COM_STMT_CLOSE]= CF_SKIP_QUESTIONS; + server_command_flags[COM_STMT_RESET]= CF_SKIP_QUESTIONS; + + /* Initialize the sql command flags array. */ + memset(sql_command_flags, 0, sizeof(sql_command_flags)); + + sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | + CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | + CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | + CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | + CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; @@ -273,7 +335,8 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | + CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; @@ -337,9 +400,29 @@ void init_update_queries(void) The following admin table operations are allowed on log tables. */ - sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND; - sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND; - sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | + CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND | + CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | + CF_AUTO_COMMIT_TRANS; + + sql_command_flags[SQLCOM_CREATE_USER]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_USER]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_RENAME_USER]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REVOKE]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_GRANT]= CF_AUTO_COMMIT_TRANS; + + sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS; + + sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CHECK]= CF_AUTO_COMMIT_TRANS; } @@ -903,28 +986,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); pthread_mutex_lock(&LOCK_thread_count); thd->query_id= global_query_id; - - switch( command ) { - /* Ignore these statements. */ - case COM_STATISTICS: - case COM_PING: - break; - /* Only increase id on these statements but don't count them. */ - case COM_STMT_PREPARE: - case COM_STMT_CLOSE: - case COM_STMT_RESET: + if (!(server_command_flags[command] & CF_SKIP_QUERY_ID)) next_query_id(); - break; - /* Increase id and count all other statements. */ - default: - statistic_increment(thd->status_var.questions, &LOCK_status); - next_query_id(); - } - thread_running++; - /* TODO: set thd->lex->sql_command to SQLCOM_END here */ pthread_mutex_unlock(&LOCK_thread_count); + if (!(server_command_flags[command] & CF_SKIP_QUESTIONS)) + statistic_increment(thd->status_var.questions, &LOCK_status); + /** Clear the set of flags that are expected to be cleared at the beginning of each command. @@ -1277,6 +1346,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, bool not_used; status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]); ulong options= (ulong) (uchar) packet[0]; + if (end_active_trans(thd)) + break; if (check_global_access(thd,RELOAD_ACL)) break; general_log_print(thd, command, NullS); @@ -1296,13 +1367,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, res= reload_acl_and_cache(NULL, options | REFRESH_FAST, NULL, ¬_used); my_pthread_setspecific_ptr(THR_THD, thd); - if (!res) - my_ok(thd); - break; + if (res) + break; } + else #endif - if (!reload_acl_and_cache(thd, options, NULL, ¬_used)) - my_ok(thd); + if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used)) + break; + if (end_active_trans(thd)) + break; + my_ok(thd); break; } #ifndef EMBEDDED_LIBRARY @@ -2037,10 +2111,20 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION } /* endif unlikely slave */ #endif + status_var_increment(thd->status_var.com_stat[lex->sql_command]); DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE); - + + /* + End a active transaction so that this command will have it's + own transaction and will also sync the binary log. If a DDL is + not run in it's own transaction it may simply never appear on + the slave in case the outside transaction rolls back. + */ + if (opt_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN)) + goto error; + switch (lex->sql_command) { case SQLCOM_SHOW_EVENTS: @@ -2315,15 +2399,6 @@ case SQLCOM_PREPARE: } case SQLCOM_CREATE_TABLE: { - /* If CREATE TABLE of non-temporary table, do implicit commit */ - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - { - if (end_active_trans(thd)) - { - res= -1; - break; - } - } DBUG_ASSERT(first_table == all_tables && first_table != 0); bool link_to_local; // Skip first table, which is the table we are creating @@ -2576,8 +2651,6 @@ 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 */ - if (end_active_trans(thd)) - goto error; /* Currently CREATE INDEX or DROP INDEX cause a full table rebuild and thus classify as slow administrative statements just like @@ -2690,9 +2763,6 @@ end_with_restore_list: WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; - /* ALTER TABLE ends previous transaction */ - if (end_active_trans(thd)) - goto error; if (!thd->locked_tables_mode && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -2738,7 +2808,7 @@ end_with_restore_list: goto error; } - if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0)) + if (mysql_rename_tables(thd, first_table, 0)) goto error; break; } @@ -3168,11 +3238,6 @@ end_with_restore_list: break; } case SQLCOM_TRUNCATE: - if (end_active_trans(thd)) - { - res= -1; - break; - } DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_one_table_access(thd, DROP_ACL, all_tables)) goto error; @@ -3280,8 +3345,6 @@ end_with_restore_list: { if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - if (end_active_trans(thd)) - goto error; } else { @@ -3380,9 +3443,6 @@ end_with_restore_list: { List *lex_var_list= &lex->var_list; - if (lex->autocommit && end_active_trans(thd)) - goto error; - if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables))) goto error; @@ -3485,11 +3545,6 @@ end_with_restore_list: prepared statement- safe. */ HA_CREATE_INFO create_info(lex->create_info); - if (end_active_trans(thd)) - { - res= -1; - break; - } char *alias; if (!(alias=thd->strmake(lex->name.str, lex->name.length)) || check_db_name(&lex->name)) @@ -3522,11 +3577,6 @@ end_with_restore_list: } case SQLCOM_DROP_DB: { - if (end_active_trans(thd)) - { - res= -1; - break; - } if (check_db_name(&lex->name)) { my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); @@ -3563,11 +3613,6 @@ end_with_restore_list: case SQLCOM_ALTER_DB_UPGRADE: { LEX_STRING *db= & lex->name; - if (end_active_trans(thd)) - { - res= 1; - break; - } #ifdef HAVE_REPLICATION if (thd->slave_thread && (!rpl_filter->db_ok(db->str) || @@ -3730,8 +3775,6 @@ end_with_restore_list: if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; - if (end_active_trans(thd)) - goto error; /* Conditionally writes to binlog */ if (!(res= mysql_create_user(thd, lex->users_list))) my_ok(thd); @@ -3742,8 +3785,6 @@ end_with_restore_list: if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; - if (end_active_trans(thd)) - goto error; /* Conditionally writes to binlog */ if (!(res= mysql_drop_user(thd, lex->users_list))) my_ok(thd); @@ -3754,8 +3795,6 @@ end_with_restore_list: if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; - if (end_active_trans(thd)) - goto error; /* Conditionally writes to binlog */ if (!(res= mysql_rename_user(thd, lex->users_list))) my_ok(thd); @@ -3763,8 +3802,6 @@ end_with_restore_list: } case SQLCOM_REVOKE_ALL: { - if (end_active_trans(thd)) - goto error; if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; @@ -3776,9 +3813,6 @@ end_with_restore_list: case SQLCOM_REVOKE: case SQLCOM_GRANT: { - if (end_active_trans(thd)) - goto error; - if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, first_table ? first_table->db : select_lex->db, first_table ? &first_table->grant.privilege : 0, @@ -4134,9 +4168,6 @@ end_with_restore_list: is_schema_db(lex->sphead->m_db.str))) goto create_sp_error; - if (end_active_trans(thd)) - goto create_sp_error; - name= lex->sphead->name(&namelen); #ifdef HAVE_DLOPEN if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) @@ -4360,8 +4391,6 @@ create_sp_error: lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) goto error; - if (end_active_trans(thd)) - goto error; memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics)); if ((sp->m_type == TYPE_ENUM_FUNCTION) && !trust_function_creators && mysql_bin_log.is_open() && @@ -4566,16 +4595,12 @@ create_sp_error: Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands as specified through the thd->lex->create_view_mode flag. */ - if (end_active_trans(thd)) - goto error; - res= mysql_create_view(thd, first_table, thd->lex->create_view_mode); break; } case SQLCOM_DROP_VIEW: { - if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE) - || end_active_trans(thd)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog. */ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); @@ -4583,9 +4608,6 @@ create_sp_error: } case SQLCOM_CREATE_TRIGGER: { - if (end_active_trans(thd)) - goto error; - /* Conditionally writes to binlog. */ res= mysql_create_or_drop_trigger(thd, all_tables, 1); @@ -4593,9 +4615,6 @@ create_sp_error: } case SQLCOM_DROP_TRIGGER: { - if (end_active_trans(thd)) - goto error; - /* Conditionally writes to binlog. */ res= mysql_create_or_drop_trigger(thd, all_tables, 0); break; @@ -4922,6 +4941,12 @@ finish: */ start_waiting_global_read_lock(thd); } + + /* If commit fails, we should be able to reset the OK status. */ + thd->stmt_da->can_overwrite_status= TRUE; + opt_implicit_commit(thd, CF_IMPLICIT_COMMIT_END); + thd->stmt_da->can_overwrite_status= FALSE; + DBUG_RETURN(res || thd->is_error()); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 18605916789..9f486768043 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4565,8 +4565,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, int result_code; DBUG_ENTER("mysql_admin_table"); - if (end_active_trans(thd)) - DBUG_RETURN(1); field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2)); item->maybe_null = 1; field_list.push_back(item = new Item_empty_string("Op", 10)); diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 02241dd92f6..c9edd210c32 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18436,6 +18436,59 @@ static void test_bug36004() DBUG_VOID_RETURN; } +/** + Test that COM_REFRESH issues a implicit commit. +*/ + +static void test_wl4284_1() +{ + int rc; + MYSQL_ROW row; + MYSQL_RES *result; + + DBUG_ENTER("test_wl4284_1"); + myheader("test_wl4284_1"); + + /* set AUTOCOMMIT to OFF */ + rc= mysql_autocommit(mysql, FALSE); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS trans"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE trans (a INT) ENGINE= InnoDB"); + myquery(rc); + + rc= mysql_query(mysql, "INSERT INTO trans VALUES(1)"); + myquery(rc); + + rc= mysql_refresh(mysql, REFRESH_GRANT | REFRESH_TABLES); + myquery(rc); + + rc= mysql_rollback(mysql); + myquery(rc); + + rc= mysql_query(mysql, "SELECT * FROM trans"); + myquery(rc); + + result= mysql_use_result(mysql); + mytest(result); + + row= mysql_fetch_row(result); + mytest(row); + + mysql_free_result(result); + + /* set AUTOCOMMIT to OFF */ + rc= mysql_autocommit(mysql, FALSE); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE trans"); + myquery(rc); + + DBUG_VOID_RETURN; +} + static void test_bug38486(void) { @@ -19197,6 +19250,7 @@ static struct my_tests_st my_tests[]= { { "test_wl4166_3", test_wl4166_3 }, { "test_wl4166_4", test_wl4166_4 }, { "test_bug36004", test_bug36004 }, + { "test_wl4284_1", test_wl4284_1 }, { "test_wl4435", test_wl4435 }, { "test_wl4435_2", test_wl4435_2 }, { "test_bug38486", test_bug38486 }, From 3a8a7509199b15c26a88bab5ed97ec093a29f387 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 18:52:21 +0300 Subject: [PATCH 082/466] Backport of: ------------------------------------------------------------ revno: 2630.13.17 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Sun 2008-07-27 10:14:46 -0300 message: Post-merge fixes: Remove dependency on binlog, require not embedded as test uses the event scheduler and disable abort on error for syntax only available on servers built with debugging support. This is a patch in scope of WL#4284 "Transactional DDL locking" --- mysql-test/t/implicit_commit.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/implicit_commit.test b/mysql-test/t/implicit_commit.test index ed451877c77..d8ffd6e9452 100644 --- a/mysql-test/t/implicit_commit.test +++ b/mysql-test/t/implicit_commit.test @@ -1,5 +1,5 @@ source include/have_innodb.inc; -source include/have_log_bin.inc; +source include/not_embedded.inc; SET GLOBAL EVENT_SCHEDULER = OFF; SET BINLOG_FORMAT = STATEMENT; From c43f894c51bb7b091dd668f069fb5e5e44dcb0b1 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 21:37:38 +0300 Subject: [PATCH 083/466] Backport of: ------------------------------------------------------------ revno: 2630.22.3 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Thu 2008-08-07 22:33:43 -0300 message: WL#4284: Transactional DDL locking Make transaction management more modular through a new interface. The overall objective of this change is to provide groundwork for the design of transactional DDL locking by cleaning up the transaction high level API to better distinguish operations implicit and explicit, and single statement transaction from operations on the normal transaction. Having a a high-level interface for transaction management provides a better base for implementing transactional concepts that are not always tied to storage engines and also makes it easier to interect with other higher level modules of the server. --- client/Makefile.am | 3 +- libmysqld/CMakeLists.txt | 2 +- libmysqld/Makefile.am | 3 +- sql/CMakeLists.txt | 1 + sql/Makefile.am | 4 +- sql/handler.cc | 66 +--- sql/handler.h | 7 +- sql/log_event.cc | 10 +- sql/log_event_old.cc | 3 +- sql/mysql_priv.h | 9 - sql/rpl_injector.cc | 7 +- sql/rpl_rli.cc | 5 +- sql/set_var.cc | 3 +- sql/slave.cc | 3 +- sql/sql_base.cc | 3 +- sql/sql_class.cc | 4 +- sql/sql_delete.cc | 5 +- sql/sql_do.cc | 3 +- sql/sql_insert.cc | 9 +- sql/sql_parse.cc | 512 +++---------------------------- sql/sql_partition.cc | 5 +- sql/sql_table.cc | 39 +-- sql/transaction.cc | 648 +++++++++++++++++++++++++++++++++++++++ sql/transaction.h | 46 +++ 24 files changed, 819 insertions(+), 581 deletions(-) create mode 100644 sql/transaction.cc create mode 100644 sql/transaction.h diff --git a/client/Makefile.am b/client/Makefile.am index ccd0d8aada0..cfc322c2531 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -107,7 +107,8 @@ sql_src=log_event.h mysql_priv.h rpl_constants.h \ rpl_utility.h rpl_tblmap.h rpl_tblmap.cc \ log_event.cc my_decimal.h my_decimal.cc \ log_event_old.h log_event_old.cc \ - rpl_record_old.h rpl_record_old.cc + rpl_record_old.h rpl_record_old.cc \ + transaction.h strings_src=decimal.c link_sources: diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 655082f0304..053f277ffbb 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -133,7 +133,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/partition_info.cc ../sql/sql_connect.cc ../sql/scheduler.cc ../sql/event_parse_data.cc ../sql/sql_signal.cc ../sql/rpl_handler.cc - ../sql/mdl.cc + ../sql/mdl.cc ../sql/transaction.cc ${GEN_SOURCES} ${LIB_SOURCES}) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 3a7fa934778..824145a462a 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -75,8 +75,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ - debug_sync.cc \ - sql_tablespace.cc \ + debug_sync.cc sql_tablespace.cc transaction.cc \ rpl_injector.cc my_user.c partition_info.cc \ sql_servers.cc event_parse_data.cc sql_signal.cc \ rpl_handler.cc mdl.cc diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index a40cf04f37c..38c6adf755b 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -77,6 +77,7 @@ SET (SQL_SOURCE sql_connect.cc scheduler.cc sql_profile.cc event_parse_data.cc sql_signal.cc rpl_handler.cc mdl.cc + transaction.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h diff --git a/sql/Makefile.am b/sql/Makefile.am index 95864dfbb1b..a7d897be3f0 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -113,7 +113,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sql_partition.h partition_info.h partition_element.h \ contributors.h sql_servers.h sql_signal.h records.h \ sql_prepare.h rpl_handler.h replication.h mdl.h \ - sql_plist.h + sql_plist.h transaction.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -159,7 +159,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_servers.cc event_parse_data.cc sql_signal.cc \ - rpl_handler.cc mdl.cc + rpl_handler.cc mdl.cc transaction.cc nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c diff --git a/sql/handler.cc b/sql/handler.cc index 2ccb87591dc..66b0450d8d6 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -27,6 +27,7 @@ #include "rpl_handler.h" #include "rpl_filter.h" #include +#include "transaction.h" #include #include "probes_mysql.h" @@ -888,16 +889,16 @@ void ha_close_connection(THD* thd) a transaction in a given engine is read-write and will not involve the two-phase commit protocol! - At the end of a statement, server call - ha_autocommit_or_rollback() is invoked. This call in turn - invokes handlerton::prepare() for every involved engine. - Prepare is followed by a call to handlerton::commit_one_phase() - If a one-phase commit will suffice, handlerton::prepare() is not - invoked and the server only calls handlerton::commit_one_phase(). - At statement commit, the statement-related read-write engine - flag is propagated to the corresponding flag in the normal - transaction. When the commit is complete, the list of registered - engines is cleared. + At the end of a statement, server call trans_commit_stmt is + invoked. This call in turn invokes handlerton::prepare() + for every involved engine. Prepare is followed by a call + to handlerton::commit_one_phase() If a one-phase commit + will suffice, handlerton::prepare() is not invoked and + the server only calls handlerton::commit_one_phase(). + At statement commit, the statement-related read-write + engine flag is propagated to the corresponding flag in the + normal transaction. When the commit is complete, the list + of registered engines is cleared. Rollback is handled in a similar fashion. @@ -908,7 +909,7 @@ void ha_close_connection(THD* thd) do not "register" in thd->transaction lists, and thus do not modify the transaction state. Besides, each DDL in MySQL is prefixed with an implicit normal transaction commit - (a call to end_active_trans()), and thus leaves nothing + (a call to trans_commit_implicit()), and thus leaves nothing to modify. However, as it has been pointed out with CREATE TABLE .. SELECT, some DDL statements can start a *new* transaction. @@ -1370,47 +1371,6 @@ int ha_rollback_trans(THD *thd, bool all) DBUG_RETURN(error); } -/** - This is used to commit or rollback a single statement depending on - the value of error. - - @note - Note that if the autocommit is on, then the following call inside - InnoDB will commit or rollback the whole transaction (= the statement). The - autocommit mechanism built into InnoDB is based on counting locks, but if - the user has used LOCK TABLES then that mechanism does not know to do the - commit. -*/ -int ha_autocommit_or_rollback(THD *thd, int error) -{ - DBUG_ENTER("ha_autocommit_or_rollback"); - - if (thd->transaction.stmt.ha_list) - { - if (!error) - { - if (ha_commit_trans(thd, 0)) - error=1; - } - else - { - (void) ha_rollback_trans(thd, 0); - if (thd->transaction_rollback_request && !thd->in_sub_stmt) - (void) ha_rollback(thd); - } - - thd->variables.tx_isolation=thd->session_tx_isolation; - } - else - { - if (!error) - RUN_HOOK(transaction, after_commit, (thd, FALSE)); - else - RUN_HOOK(transaction, after_rollback, (thd, FALSE)); - } - DBUG_RETURN(error); -} - struct xahton_st { XID *xid; @@ -3496,7 +3456,7 @@ int ha_enable_transaction(THD *thd, bool on) So, let's commit an open transaction (if any) now. */ if (!(error= ha_commit_trans(thd, 0))) - error= end_trans(thd, COMMIT); + error= trans_commit_implicit(thd); } DBUG_RETURN(error); } diff --git a/sql/handler.h b/sql/handler.h index 7e64a08700f..1bcf6a73b33 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1945,10 +1945,6 @@ extern TYPELIB tx_isolation_typelib; extern TYPELIB myisam_stats_method_typelib; extern ulong total_ha, total_ha_2pc; - /* Wrapper functions */ -#define ha_commit(thd) (ha_commit_trans((thd), TRUE)) -#define ha_rollback(thd) (ha_rollback_trans((thd), TRUE)) - /* lookups */ handlerton *ha_default_handlerton(THD *thd); plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name); @@ -2025,13 +2021,12 @@ int ha_release_temporary_latches(THD *thd); int ha_start_consistent_snapshot(THD *thd); int ha_commit_or_rollback_by_xid(XID *xid, bool commit); int ha_commit_one_phase(THD *thd, bool all); +int ha_commit_trans(THD *thd, bool all); int ha_rollback_trans(THD *thd, bool all); int ha_prepare(THD *thd); int ha_recover(HASH *commit_list); /* transactions: these functions never call handlerton functions directly */ -int ha_commit_trans(THD *thd, bool all); -int ha_autocommit_or_rollback(THD *thd, int error); int ha_enable_transaction(THD *thd, bool on); /* savepoints */ diff --git a/sql/log_event.cc b/sql/log_event.cc index b3f6fd58f1a..9c3a2ce2131 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -31,6 +31,7 @@ #include "rpl_filter.h" #include "rpl_utility.h" #include "rpl_record.h" +#include "transaction.h" #include #endif /* MYSQL_CLIENT */ @@ -3231,7 +3232,7 @@ Default database: '%s'. Query: '%s'", them back here. */ if (expected_error && expected_error == actual_error) - ha_autocommit_or_rollback(thd, TRUE); + trans_rollback_stmt(thd); } /* If we expected a non-zero error code and get nothing and, it is a concurrency @@ -3240,7 +3241,8 @@ Default database: '%s'. Query: '%s'", else if (expected_error && !actual_error && (concurrency_error_code(expected_error) || ignored_error_code(expected_error))) - ha_autocommit_or_rollback(thd, TRUE); + trans_rollback_stmt(thd); + /* Other cases: mostly we expected no error and get one. */ @@ -5315,7 +5317,7 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli) /* For a slave Xid_log_event is COMMIT */ general_log_print(thd, COM_QUERY, "COMMIT /* implicit, from Xid_log_event */"); - return end_trans(thd, COMMIT); + return trans_commit(thd); } Log_event::enum_skip_reason @@ -7607,7 +7609,7 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd) are involved, commit the transaction and flush the pending event to the binlog. */ - error= ha_autocommit_or_rollback(thd, 0); + error= trans_commit_stmt(thd); /* Now what if this is not a transactional engine? we still need to diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 3dcf19f6b32..90ea8b6cefe 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -6,6 +6,7 @@ #endif #include "log_event_old.h" #include "rpl_record_old.h" +#include "transaction.h" #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -1795,7 +1796,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) are involved, commit the transaction and flush the pending event to the binlog. */ - if ((error= ha_autocommit_or_rollback(thd, 0))) + if ((error= trans_commit_stmt(thd))) rli->report(ERROR_LEVEL, error, "Error in %s event: commit of row events failed, " "table `%s`.`%s`", diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 72bc20238fc..3f6ed2b1cb0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -883,15 +883,6 @@ bool parse_sql(THD *thd, Parser_state *parser_state, Object_creation_ctx *creation_ctx); -enum enum_mysql_completiontype { - ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7, - COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6 -}; - -bool begin_trans(THD *thd); -bool end_active_trans(THD *thd); -int end_trans(THD *thd, enum enum_mysql_completiontype completion); - Item *negate_expression(THD *thd, Item *expr); /* log.cc */ diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 684655d1c3b..738341cc034 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -15,6 +15,7 @@ #include "mysql_priv.h" #include "rpl_injector.h" +#include "transaction.h" /* injector::transaction - member definitions @@ -35,7 +36,7 @@ injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd) m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0)); m_start_pos.m_file_pos= log_info.pos; - begin_trans(m_thd); + trans_begin(m_thd); thd->set_current_stmt_binlog_row_based(); } @@ -81,8 +82,8 @@ int injector::transaction::commit() is committed by committing the statement transaction explicitly. */ - ha_autocommit_or_rollback(m_thd, 0); - end_trans(m_thd, COMMIT); + trans_commit_stmt(m_thd); + trans_commit(m_thd); DBUG_RETURN(0); } diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 42cd12bd66c..0a703475aa7 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -20,6 +20,7 @@ #include // For MY_STAT #include "sql_repl.h" // For check_binlog_magic #include "rpl_utility.h" +#include "transaction.h" static int count_relay_log_space(Relay_log_info* rli); @@ -1183,8 +1184,8 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) */ if (error) { - ha_autocommit_or_rollback(thd, 1); // if a "statement transaction" - end_trans(thd, ROLLBACK); // if a "real transaction" + trans_rollback_stmt(thd); // if a "statement transaction" + trans_rollback(thd); // if a "real transaction" } m_table_map.clear_tables(); slave_close_thread_tables(thd); diff --git a/sql/set_var.cc b/sql/set_var.cc index 82ecb6b71cf..266cdd9ad6d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -61,6 +61,7 @@ #include #include "events.h" +#include "transaction.h" /* WITH_NDBCLUSTER_STORAGE_ENGINE */ #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE @@ -3190,7 +3191,7 @@ static bool set_option_autocommit(THD *thd, set_var *var) */ if (var->save_result.ulong_value != 0 && (thd->options & OPTION_NOT_AUTOCOMMIT) && - ha_commit(thd)) + trans_commit(thd)) return 1; if (var->save_result.ulong_value != 0) diff --git a/sql/slave.cc b/sql/slave.cc index 99a7fc8d3eb..ed722305b29 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -34,6 +34,7 @@ #include "sql_repl.h" #include "rpl_filter.h" #include "repl_failsafe.h" +#include "transaction.h" #include #include #include @@ -2430,7 +2431,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) else { exec_res= 0; - end_trans(thd, ROLLBACK); + trans_rollback(thd); /* chance for concurrent connection to get more locks */ safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE), (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a84891c0ab7..0bd1acaa08e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -22,6 +22,7 @@ #include "sp_head.h" #include "sp.h" #include "sql_trigger.h" +#include "transaction.h" #include "sql_prepare.h" #include #include @@ -1402,7 +1403,7 @@ void close_thread_tables(THD *thd, if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) { thd->stmt_da->can_overwrite_status= TRUE; - ha_autocommit_or_rollback(thd, thd->is_error()); + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); thd->stmt_da->can_overwrite_status= FALSE; /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ff1b03102a6..de2ef98e993 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -42,6 +42,7 @@ #include "sp_rcontext.h" #include "sp_cache.h" +#include "transaction.h" #include "debug_sync.h" /* @@ -987,7 +988,8 @@ void THD::cleanup(void) } #endif { - ha_rollback(this); + transaction.xid_state.xa_state= XA_NOTR; + trans_rollback(this); xid_cache_delete(&transaction.xid_state); } locked_tables_list.unlock_locked_tables(this); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c0162605e2f..13c331af95c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -23,6 +23,7 @@ #include "sql_select.h" #include "sp_head.h" #include "sql_trigger.h" +#include "transaction.h" /** Implement DELETE SQL word. @@ -1071,8 +1072,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) if (error) { DBUG_ASSERT(thd->stmt_da->is_error()); - ha_autocommit_or_rollback(thd, TRUE); - end_active_trans(thd); + trans_rollback_stmt(thd); + trans_rollback(thd); } thd->current_stmt_binlog_row_based= save_binlog_row_based; DBUG_RETURN(error); diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 8406a9eaf45..0f3a7e1ecef 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -17,6 +17,7 @@ /* Execute DO statement */ #include "mysql_priv.h" +#include "transaction.h" bool mysql_do(THD *thd, List &values) { @@ -36,7 +37,7 @@ bool mysql_do(THD *thd, List &values) will clear the error and the rollback in the end of dispatch_command() won't work. */ - ha_autocommit_or_rollback(thd, thd->is_error()); + trans_rollback_stmt(thd); thd->clear_error(); // DO always is OK } my_ok(thd); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e0199fcdf20..16e9c1ded38 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -61,6 +61,7 @@ #include "sql_show.h" #include "slave.h" #include "rpl_mi.h" +#include "transaction.h" #ifndef EMBEDDED_LIBRARY static bool delayed_get_table(THD *thd, TABLE_LIST *table_list); @@ -2539,7 +2540,7 @@ pthread_handler_t handle_delayed_insert(void *arg) */ di->table->file->ha_release_auto_increment(); mysql_unlock_tables(thd, lock); - ha_autocommit_or_rollback(thd, 0); + trans_commit_stmt(thd); di->group_count=0; pthread_mutex_lock(&di->mutex); } @@ -2559,7 +2560,7 @@ pthread_handler_t handle_delayed_insert(void *arg) first call to ha_*_row() instead. Remove code that are used to cover for the case outlined above. */ - ha_autocommit_or_rollback(thd, 1); + trans_rollback_stmt(thd); DBUG_LEAVE; } @@ -3863,8 +3864,8 @@ bool select_create::send_eof() */ if (!table->s->tmp_table) { - ha_autocommit_or_rollback(thd, 0); - end_active_trans(thd); + trans_commit_stmt(thd); + trans_commit_implicit(thd); } table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 660241e8e2c..2df66a44fa6 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -28,6 +28,7 @@ #include "sp_cache.h" #include "events.h" #include "sql_trigger.h" +#include "transaction.h" #include "sql_prepare.h" #include "probes_mysql.h" @@ -88,113 +89,6 @@ const char *xa_state_names[]={ "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY" }; -/** - Mark a XA transaction as rollback-only if the RM unilaterally - rolled back the transaction branch. - - @note If a rollback was requested by the RM, this function sets - the appropriate rollback error code and transits the state - to XA_ROLLBACK_ONLY. - - @return TRUE if transaction was rolled back or if the transaction - state is XA_ROLLBACK_ONLY. FALSE otherwise. -*/ -static bool xa_trans_rolled_back(XID_STATE *xid_state) -{ - if (xid_state->rm_error) - { - switch (xid_state->rm_error) { - case ER_LOCK_WAIT_TIMEOUT: - my_error(ER_XA_RBTIMEOUT, MYF(0)); - break; - case ER_LOCK_DEADLOCK: - my_error(ER_XA_RBDEADLOCK, MYF(0)); - break; - default: - my_error(ER_XA_RBROLLBACK, MYF(0)); - } - xid_state->xa_state= XA_ROLLBACK_ONLY; - } - - return (xid_state->xa_state == XA_ROLLBACK_ONLY); -} - -/** - Rollback work done on behalf of at ransaction branch. -*/ -static bool xa_trans_rollback(THD *thd) -{ - /* - Resource Manager error is meaningless at this point, as we perform - explicit rollback request by user. We must reset rm_error before - calling ha_rollback(), so thd->transaction.xid structure gets reset - by ha_rollback()/THD::transaction::cleanup(). - */ - thd->transaction.xid_state.rm_error= 0; - - bool status= test(ha_rollback(thd)); - - thd->options&= ~(ulong) OPTION_BEGIN; - thd->transaction.all.modified_non_trans_table= FALSE; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state= XA_NOTR; - - return status; -} - -bool end_active_trans(THD *thd) -{ - int error=0; - DBUG_ENTER("end_active_trans"); - if (unlikely(thd->in_sub_stmt)) - { - my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - DBUG_RETURN(1); - } - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - DBUG_RETURN(1); - } - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | - OPTION_TABLE_LOCK)) - { - DBUG_PRINT("info",("options: 0x%llx", thd->options)); - /* Safety if one did "drop table" on locked tables */ - if (!thd->locked_tables_mode) - thd->options&= ~OPTION_TABLE_LOCK; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - if (ha_commit(thd)) - error=1; - } - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - DBUG_RETURN(error); -} - - -bool begin_trans(THD *thd) -{ - int error=0; - if (unlikely(thd->in_sub_stmt)) - { - my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - return 1; - } - - thd->locked_tables_list.unlock_locked_tables(thd); - - if (end_active_trans(thd)) - error= -1; - else - { - thd->options|= OPTION_BEGIN; - thd->server_status|= SERVER_STATUS_IN_TRANS; - } - return error; -} #ifdef HAVE_REPLICATION /** @@ -256,9 +150,9 @@ static bool opt_implicit_commit(THD *thd, uint mask) if (!skip) { /* Commit or rollback the statement transaction. */ - ha_autocommit_or_rollback(thd, thd->is_error()); + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); /* Commit the normal transaction if one is active. */ - res= end_active_trans(thd); + res= trans_commit_implicit(thd); } DBUG_RETURN(res); @@ -691,80 +585,6 @@ void cleanup_items(Item *item) DBUG_VOID_RETURN; } -/** - Ends the current transaction and (maybe) begin the next. - - @param thd Current thread - @param completion Completion type - - @retval - 0 OK -*/ - -int end_trans(THD *thd, enum enum_mysql_completiontype completion) -{ - bool do_release= 0; - int res= 0; - DBUG_ENTER("end_trans"); - - if (unlikely(thd->in_sub_stmt)) - { - my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - DBUG_RETURN(1); - } - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - DBUG_RETURN(1); - } - switch (completion) { - case COMMIT: - /* - We don't use end_active_trans() here to ensure that this works - even if there is a problem with the OPTION_AUTO_COMMIT flag - (Which of course should never happen...) - */ - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - res= ha_commit(thd); - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - break; - case COMMIT_RELEASE: - do_release= 1; /* fall through */ - case COMMIT_AND_CHAIN: - res= end_active_trans(thd); - if (!res && completion == COMMIT_AND_CHAIN) - res= begin_trans(thd); - break; - case ROLLBACK_RELEASE: - do_release= 1; /* fall through */ - case ROLLBACK: - case ROLLBACK_AND_CHAIN: - { - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - if (ha_rollback(thd)) - res= -1; - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - if (!res && (completion == ROLLBACK_AND_CHAIN)) - res= begin_trans(thd); - break; - } - default: - res= -1; - my_error(ER_UNKNOWN_COM_ERROR, MYF(0)); - DBUG_RETURN(-1); - } - - if (res < 0) - my_error(thd->killed_errno(), MYF(0)); - else if ((res == 0) && do_release) - thd->killed= THD::KILL_CONNECTION; - - DBUG_RETURN(res); -} - #ifndef EMBEDDED_LIBRARY /** @@ -1346,7 +1166,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, bool not_used; status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]); ulong options= (ulong) (uchar) packet[0]; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) break; if (check_global_access(thd,RELOAD_ACL)) break; @@ -1374,7 +1194,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used)) break; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) break; my_ok(thd); break; @@ -1532,7 +1352,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* If commit fails, we should be able to reset the OK status. */ thd->stmt_da->can_overwrite_status= TRUE; - ha_autocommit_or_rollback(thd, thd->is_error()); + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); thd->stmt_da->can_overwrite_status= FALSE; thd->transaction.stmt.reset(); @@ -3485,7 +3305,7 @@ end_with_restore_list: thd->locked_tables_list.unlock_locked_tables(thd); if (thd->options & OPTION_TABLE_LOCK) { - end_active_trans(thd); + trans_commit_implicit(thd); thd->options&= ~(OPTION_TABLE_LOCK); } if (thd->global_read_lock) @@ -3495,7 +3315,7 @@ end_with_restore_list: case SQLCOM_LOCK_TABLES: thd->locked_tables_list.unlock_locked_tables(thd); /* we must end the trasaction first, regardless of anything */ - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) @@ -3524,8 +3344,8 @@ end_with_restore_list: can free its locks if LOCK TABLES locked some tables before finding that it can't lock a table in its list */ - ha_autocommit_or_rollback(thd, 1); - end_active_trans(thd); + trans_rollback_stmt(thd); + trans_commit_implicit(thd); thd->options&= ~(OPTION_TABLE_LOCK); } else @@ -4007,132 +3827,50 @@ end_with_restore_list: break; case SQLCOM_BEGIN: - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (begin_trans(thd)) + if (trans_begin(thd, lex->start_transaction_opt)) goto error; - if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) - { - if (ha_start_consistent_snapshot(thd)) - goto error; - } my_ok(thd); break; case SQLCOM_COMMIT: DBUG_ASSERT(thd->lock == NULL || thd->locked_tables_mode == LTM_LOCK_TABLES); - if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE : - lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT)) + if (trans_commit(thd)) goto error; + /* Begin transaction with the same isolation level. */ + if (lex->tx_chain && trans_begin(thd)) + goto error; + /* Disconnect the current client connection. */ + if (lex->tx_release) + thd->killed= THD::KILL_CONNECTION; my_ok(thd); break; case SQLCOM_ROLLBACK: DBUG_ASSERT(thd->lock == NULL || thd->locked_tables_mode == LTM_LOCK_TABLES); - if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE : - lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK)) + if (trans_rollback(thd)) goto error; + /* Begin transaction with the same isolation level. */ + if (lex->tx_chain && trans_begin(thd)) + goto error; + /* Disconnect the current client connection. */ + if (lex->tx_release) + thd->killed= THD::KILL_CONNECTION; my_ok(thd); break; case SQLCOM_RELEASE_SAVEPOINT: - { - SAVEPOINT *sv; - for (sv=thd->transaction.savepoints; sv; sv=sv->prev) - { - if (my_strnncoll(system_charset_info, - (uchar *)lex->ident.str, lex->ident.length, - (uchar *)sv->name, sv->length) == 0) - break; - } - if (sv) - { - if (ha_release_savepoint(thd, sv)) - res= TRUE; // cannot happen - else - my_ok(thd); - thd->transaction.savepoints=sv->prev; - } - else - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str); + if (trans_release_savepoint(thd, lex->ident)) + goto error; + my_ok(thd); break; - } case SQLCOM_ROLLBACK_TO_SAVEPOINT: - { - SAVEPOINT *sv; - for (sv=thd->transaction.savepoints; sv; sv=sv->prev) - { - if (my_strnncoll(system_charset_info, - (uchar *)lex->ident.str, lex->ident.length, - (uchar *)sv->name, sv->length) == 0) - break; - } - if (sv) - { - if (ha_rollback_to_savepoint(thd, sv)) - res= TRUE; // cannot happen - else - { - if (((thd->options & OPTION_KEEP_LOG) || - thd->transaction.all.modified_non_trans_table) && - !thd->slave_thread) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARNING_NOT_COMPLETE_ROLLBACK, - ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); - my_ok(thd); - } - thd->transaction.savepoints=sv; - } - else - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str); + if (trans_rollback_to_savepoint(thd, lex->ident)) + goto error; + my_ok(thd); break; - } case SQLCOM_SAVEPOINT: - if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) || - thd->in_sub_stmt) || !opt_using_transactions) - my_ok(thd); - else - { - SAVEPOINT **sv, *newsv; - for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev) - { - if (my_strnncoll(system_charset_info, - (uchar *)lex->ident.str, lex->ident.length, - (uchar *)(*sv)->name, (*sv)->length) == 0) - break; - } - if (*sv) /* old savepoint of the same name exists */ - { - newsv=*sv; - ha_release_savepoint(thd, *sv); // it cannot fail - *sv=(*sv)->prev; - } - else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root, - savepoint_alloc_size)) == 0) - { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - break; - } - newsv->name=strmake_root(&thd->transaction.mem_root, - lex->ident.str, lex->ident.length); - newsv->length=lex->ident.length; - /* - if we'll get an error here, don't add new savepoint to the list. - we'll lose a little bit of memory in transaction mem_root, but it'll - be free'd when transaction ends anyway - */ - if (ha_savepoint(thd, newsv)) - res= TRUE; - else - { - newsv->prev=thd->transaction.savepoints; - thd->transaction.savepoints=newsv; - my_ok(thd); - } - } + if (trans_savepoint(thd, lex->ident)) + goto error; + my_ok(thd); break; case SQLCOM_CREATE_PROCEDURE: case SQLCOM_CREATE_SPFUNCTION: @@ -4456,7 +4194,7 @@ create_sp_error: lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) goto error; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) goto error; #ifndef NO_EMBEDDED_ACCESS_CHECKS if (sp_automatic_privileges && !opt_noacl && @@ -4620,185 +4358,29 @@ create_sp_error: break; } case SQLCOM_XA_START: - if (thd->transaction.xid_state.xa_state == XA_IDLE && - thd->lex->xa_opt == XA_RESUME) - { - if (! thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - my_error(ER_XAER_NOTA, MYF(0)); - break; - } - thd->transaction.xid_state.xa_state=XA_ACTIVE; - my_ok(thd); - break; - } - if (thd->lex->xa_opt != XA_NONE) - { // JOIN is not supported yet. TODO - my_error(ER_XAER_INVAL, MYF(0)); - break; - } - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (thd->locked_tables_mode || thd->active_transaction()) - { - my_error(ER_XAER_OUTSIDE, MYF(0)); - break; - } - if (xid_cache_search(thd->lex->xid)) - { - my_error(ER_XAER_DUPID, MYF(0)); - break; - } - DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); - thd->transaction.xid_state.xa_state=XA_ACTIVE; - thd->transaction.xid_state.rm_error= 0; - thd->transaction.xid_state.xid.set(thd->lex->xid); - xid_cache_insert(&thd->transaction.xid_state); - thd->transaction.all.modified_non_trans_table= FALSE; - thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); - thd->server_status|= SERVER_STATUS_IN_TRANS; + if (trans_xa_start(thd)) + goto error; my_ok(thd); break; case SQLCOM_XA_END: - /* fake it */ - if (thd->lex->xa_opt != XA_NONE) - { // SUSPEND and FOR MIGRATE are not supported yet. TODO - my_error(ER_XAER_INVAL, MYF(0)); - break; - } - if (thd->transaction.xid_state.xa_state != XA_ACTIVE) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - my_error(ER_XAER_NOTA, MYF(0)); - break; - } - if (xa_trans_rolled_back(&thd->transaction.xid_state)) - break; - thd->transaction.xid_state.xa_state=XA_IDLE; + if (trans_xa_end(thd)) + goto error; my_ok(thd); break; case SQLCOM_XA_PREPARE: - if (thd->transaction.xid_state.xa_state != XA_IDLE) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - my_error(ER_XAER_NOTA, MYF(0)); - break; - } - if (ha_prepare(thd)) - { - my_error(ER_XA_RBROLLBACK, MYF(0)); - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; - break; - } - thd->transaction.xid_state.xa_state=XA_PREPARED; + if (trans_xa_prepare(thd)) + goto error; my_ok(thd); break; case SQLCOM_XA_COMMIT: - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - XID_STATE *xs=xid_cache_search(thd->lex->xid); - if (!xs || xs->in_thd) - my_error(ER_XAER_NOTA, MYF(0)); - else if (xa_trans_rolled_back(xs)) - { - ha_commit_or_rollback_by_xid(thd->lex->xid, 0); - xid_cache_delete(xs); - break; - } - else - { - ha_commit_or_rollback_by_xid(thd->lex->xid, 1); - xid_cache_delete(xs); - my_ok(thd); - } - break; - } - if (xa_trans_rolled_back(&thd->transaction.xid_state)) - { - xa_trans_rollback(thd); - break; - } - if (thd->transaction.xid_state.xa_state == XA_IDLE && - thd->lex->xa_opt == XA_ONE_PHASE) - { - int r; - if ((r= ha_commit(thd))) - my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); - else - my_ok(thd); - } - else if (thd->transaction.xid_state.xa_state == XA_PREPARED && - thd->lex->xa_opt == XA_NONE) - { - if (wait_if_global_read_lock(thd, 0, 0)) - { - ha_rollback(thd); - my_error(ER_XAER_RMERR, MYF(0)); - } - else - { - if (ha_commit_one_phase(thd, 1)) - my_error(ER_XAER_RMERR, MYF(0)); - else - my_ok(thd); - start_waiting_global_read_lock(thd); - } - } - else - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; + if (trans_xa_commit(thd)) + goto error; + my_ok(thd); break; case SQLCOM_XA_ROLLBACK: - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - XID_STATE *xs=xid_cache_search(thd->lex->xid); - if (!xs || xs->in_thd) - my_error(ER_XAER_NOTA, MYF(0)); - else - { - bool ok= !xa_trans_rolled_back(xs); - ha_commit_or_rollback_by_xid(thd->lex->xid, 0); - xid_cache_delete(xs); - if (ok) - my_ok(thd); - } - break; - } - if (thd->transaction.xid_state.xa_state != XA_IDLE && - thd->transaction.xid_state.xa_state != XA_PREPARED && - thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (xa_trans_rollback(thd)) - my_error(ER_XAER_RMERR, MYF(0)); - else - my_ok(thd); + if (trans_xa_rollback(thd)) + goto error; + my_ok(thd); break; case SQLCOM_XA_RECOVER: res= mysql_xa_recover(thd); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index f4525db7def..e0461533dde 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -50,6 +50,7 @@ #include #include #include "my_md5.h" +#include "transaction.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -4327,8 +4328,8 @@ static int fast_end_partition(THD *thd, ulonglong copied, if (!is_empty) query_cache_invalidate3(thd, table_list, 0); - error= ha_autocommit_or_rollback(thd, 0); - if (end_active_trans(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error= 1; if (error) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9f486768043..d6a592c4799 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -22,6 +22,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "sql_show.h" +#include "transaction.h" #ifdef __WIN__ #include @@ -4668,8 +4669,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, DBUG_PRINT("admin", ("calling prepare_func")); switch ((*prepare_func)(thd, table, check_opt)) { case 1: // error, message written to net - ha_autocommit_or_rollback(thd, 1); - end_trans(thd, ROLLBACK); + trans_rollback_stmt(thd); + trans_rollback(thd); close_thread_tables(thd); DBUG_PRINT("admin", ("simple error, admin next table")); continue; @@ -4740,8 +4741,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name); protocol->store(buff, length, system_charset_info); - ha_autocommit_or_rollback(thd, 0); - end_trans(thd, COMMIT); + trans_commit_stmt(thd); + trans_commit(thd); close_thread_tables(thd); lex->reset_query_tables_list(FALSE); table->table=0; // For query cache @@ -4790,7 +4791,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_ADMIN_NEEDS_ALTER)) { DBUG_PRINT("admin", ("recreating table")); - ha_autocommit_or_rollback(thd, 1); + trans_rollback_stmt(thd); close_thread_tables(thd); tmp_disable_binlog(thd); // binlogging is done by caller if wanted result_code= mysql_recreate_table(thd, table); @@ -4910,7 +4911,7 @@ send_result_message: system_charset_info); if (protocol->write()) goto err; - ha_autocommit_or_rollback(thd, 0); + trans_commit_stmt(thd); close_thread_tables(thd); DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze...")); TABLE_LIST *save_next_local= table->next_local, @@ -4927,7 +4928,7 @@ send_result_message: */ if (thd->stmt_da->is_ok()) thd->stmt_da->reset_diagnostics_area(); - ha_autocommit_or_rollback(thd, 0); + trans_commit_stmt(thd); close_thread_tables(thd); if (!result_code) // recreation went ok { @@ -5022,8 +5023,8 @@ send_result_message: query_cache_invalidate3(thd, table->table, 0); } } - ha_autocommit_or_rollback(thd, 0); - end_trans(thd, COMMIT); + trans_commit_stmt(thd); + trans_commit_implicit(thd); close_thread_tables(thd); table->table=0; // For query cache if (protocol->write()) @@ -5034,8 +5035,8 @@ send_result_message: DBUG_RETURN(FALSE); err: - ha_autocommit_or_rollback(thd, 1); - end_trans(thd, ROLLBACK); + trans_rollback_stmt(thd); + trans_rollback(thd); close_thread_tables(thd); // Shouldn't be needed if (table) table->table=0; @@ -5550,15 +5551,15 @@ mysql_discard_or_import_tablespace(THD *thd, query_cache_invalidate3(thd, table_list, 0); /* The ALTER TABLE is always in its own transaction */ - error = ha_autocommit_or_rollback(thd, 0); - if (end_active_trans(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error=1; if (error) goto err; write_bin_log(thd, FALSE, thd->query(), thd->query_length()); err: - ha_autocommit_or_rollback(thd, error); + trans_rollback_stmt(thd); thd->tablespace_op=FALSE; if (error == 0) @@ -7208,8 +7209,8 @@ view_err: thd_proc_info(thd, "manage keys"); alter_table_manage_keys(table, table->file->indexes_are_disabled(), alter_info->keys_onoff); - error= ha_autocommit_or_rollback(thd, 0); - if (end_active_trans(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error= 1; } thd->count_cuted_fields= CHECK_FIELD_IGNORE; @@ -7297,7 +7298,7 @@ view_err: /* Need to commit before a table is unlocked (NDB requirement). */ DBUG_PRINT("info", ("Committing before unlocking table")); - if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd)) + if (trans_commit_stmt(thd) || trans_commit_implicit(thd)) goto err_new_table_cleanup; committed= 1; } @@ -7803,9 +7804,9 @@ copy_data_between_tables(TABLE *from,TABLE *to, Ensure that the new table is saved properly to disk so that we can do a rename */ - if (ha_autocommit_or_rollback(thd, 0)) + if (trans_commit_stmt(thd)) error=1; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) error=1; err: diff --git a/sql/transaction.cc b/sql/transaction.cc new file mode 100644 index 00000000000..f4a616ead6b --- /dev/null +++ b/sql/transaction.cc @@ -0,0 +1,648 @@ +/* Copyright (C) 2008 Sun/MySQL + + 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 */ + + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "transaction.h" +#include "rpl_handler.h" + +/* Conditions under which the transaction state must not change. */ +static bool trans_check(THD *thd) +{ + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_check"); + + if (unlikely(thd->in_sub_stmt)) + my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); + if (xa_state != XA_NOTR) + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + else + DBUG_RETURN(FALSE); + + DBUG_RETURN(TRUE); +} + + +/** + Mark a XA transaction as rollback-only if the RM unilaterally + rolled back the transaction branch. + + @note If a rollback was requested by the RM, this function sets + the appropriate rollback error code and transits the state + to XA_ROLLBACK_ONLY. + + @return TRUE if transaction was rolled back or if the transaction + state is XA_ROLLBACK_ONLY. FALSE otherwise. +*/ +static bool xa_trans_rolled_back(XID_STATE *xid_state) +{ + if (xid_state->rm_error) + { + switch (xid_state->rm_error) { + case ER_LOCK_WAIT_TIMEOUT: + my_error(ER_XA_RBTIMEOUT, MYF(0)); + break; + case ER_LOCK_DEADLOCK: + my_error(ER_XA_RBDEADLOCK, MYF(0)); + break; + default: + my_error(ER_XA_RBROLLBACK, MYF(0)); + } + xid_state->xa_state= XA_ROLLBACK_ONLY; + } + + return (xid_state->xa_state == XA_ROLLBACK_ONLY); +} + + +/** + Begin a new transaction. + + @note Beginning a transaction implicitly commits any current + transaction and releases existing locks. + + @param thd Current thread + @param flags Transaction flags + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_begin(THD *thd, uint flags) +{ + int res= FALSE; + DBUG_ENTER("trans_begin"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + thd->locked_tables_list.unlock_locked_tables(thd); + + DBUG_ASSERT(!thd->locked_tables_mode); + + if (trans_commit_implicit(thd)) + DBUG_RETURN(TRUE); + + thd->options|= OPTION_BEGIN; + thd->server_status|= SERVER_STATUS_IN_TRANS; + + if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + res= ha_start_consistent_snapshot(thd); + + DBUG_RETURN(test(res)); +} + + +/** + Commit the current transaction, making its changes permanent. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_commit(THD *thd) +{ + int res; + DBUG_ENTER("trans_commit"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= ha_commit_trans(thd, TRUE); + if (res) + /* + if res is non-zero, then ha_commit_trans has rolled back the + transaction, so the hooks for rollback will be called. + */ + RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + else + RUN_HOOK(transaction, after_commit, (thd, FALSE)); + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->lex->start_transaction_opt= 0; + + DBUG_RETURN(test(res)); +} + + +/** + Implicitly commit the current transaction. + + @note A implicit commit does not releases existing table locks. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_commit_implicit(THD *thd) +{ + bool res= FALSE; + DBUG_ENTER("trans_commit_implicit"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | + OPTION_TABLE_LOCK)) + { + /* Safety if one did "drop table" on locked tables */ + if (!thd->locked_tables_mode) + thd->options&= ~OPTION_TABLE_LOCK; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= test(ha_commit_trans(thd, TRUE)); + } + + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + + DBUG_RETURN(res); +} + + +/** + Rollback the current transaction, canceling its changes. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_rollback(THD *thd) +{ + int res; + DBUG_ENTER("trans_rollback"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= ha_rollback_trans(thd, TRUE); + RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->lex->start_transaction_opt= 0; + + DBUG_RETURN(test(res)); +} + + +/** + Commit the single statement transaction. + + @note Note that if the autocommit is on, then the following call + inside InnoDB will commit or rollback the whole transaction + (= the statement). The autocommit mechanism built into InnoDB + is based on counting locks, but if the user has used LOCK + TABLES then that mechanism does not know to do the commit. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_commit_stmt(THD *thd) +{ + DBUG_ENTER("trans_commit_stmt"); + int res= FALSE; + if (thd->transaction.stmt.ha_list) + res= ha_commit_trans(thd, FALSE); + + if (res) + /* + if res is non-zero, then ha_commit_trans has rolled back the + transaction, so the hooks for rollback will be called. + */ + RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + else + RUN_HOOK(transaction, after_commit, (thd, FALSE)); + DBUG_RETURN(test(res)); +} + + +/** + Rollback the single statement transaction. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ +bool trans_rollback_stmt(THD *thd) +{ + DBUG_ENTER("trans_rollback_stmt"); + + if (thd->transaction.stmt.ha_list) + { + ha_rollback_trans(thd, FALSE); + if (thd->transaction_rollback_request && !thd->in_sub_stmt) + ha_rollback_trans(thd, TRUE); + } + + RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + + DBUG_RETURN(FALSE); +} + +/* Find a named savepoint in the current transaction. */ +static SAVEPOINT ** +find_savepoint(THD *thd, LEX_STRING name) +{ + SAVEPOINT **sv= &thd->transaction.savepoints; + + while (*sv) + { + if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length, + (uchar *) (*sv)->name, (*sv)->length) == 0) + break; + sv= &(*sv)->prev; + } + + return sv; +} + + +/** + Set a named transaction savepoint. + + @param thd Current thread + @param name Savepoint name + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_savepoint(THD *thd, LEX_STRING name) +{ + SAVEPOINT **sv, *newsv; + DBUG_ENTER("trans_savepoint"); + + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) || + thd->in_sub_stmt) || !opt_using_transactions) + DBUG_RETURN(FALSE); + + sv= find_savepoint(thd, name); + + if (*sv) /* old savepoint of the same name exists */ + { + newsv= *sv; + ha_release_savepoint(thd, *sv); + *sv= (*sv)->prev; + } + else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root, + savepoint_alloc_size)) == NULL) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + DBUG_RETURN(TRUE); + } + + newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length); + newsv->length= name.length; + + /* + if we'll get an error here, don't add new savepoint to the list. + we'll lose a little bit of memory in transaction mem_root, but it'll + be free'd when transaction ends anyway + */ + if (ha_savepoint(thd, newsv)) + DBUG_RETURN(TRUE); + + newsv->prev= thd->transaction.savepoints; + thd->transaction.savepoints= newsv; + + DBUG_RETURN(FALSE); +} + + +/** + Rollback a transaction to the named savepoint. + + @note Modifications that the current transaction made to + rows after the savepoint was set are undone in the + rollback. + + @note Savepoints that were set at a later time than the + named savepoint are deleted. + + @param thd Current thread + @param name Savepoint name + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) +{ + int res= FALSE; + SAVEPOINT *sv= *find_savepoint(thd, name); + DBUG_ENTER("trans_rollback_to_savepoint"); + + if (sv == NULL) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str); + DBUG_RETURN(TRUE); + } + + if (ha_rollback_to_savepoint(thd, sv)) + res= TRUE; + else if (((thd->options & OPTION_KEEP_LOG) || + thd->transaction.all.modified_non_trans_table) && + !thd->slave_thread) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NOT_COMPLETE_ROLLBACK, + ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); + + thd->transaction.savepoints= sv; + + DBUG_RETURN(test(res)); +} + + +/** + Remove the named savepoint from the set of savepoints of + the current transaction. + + @note No commit or rollback occurs. It is an error if the + savepoint does not exist. + + @param thd Current thread + @param name Savepoint name + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_release_savepoint(THD *thd, LEX_STRING name) +{ + int res= FALSE; + SAVEPOINT *sv= *find_savepoint(thd, name); + DBUG_ENTER("trans_release_savepoint"); + + if (sv == NULL) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str); + DBUG_RETURN(TRUE); + } + + if (ha_release_savepoint(thd, sv)) + res= TRUE; + + thd->transaction.savepoints= sv->prev; + + DBUG_RETURN(test(res)); +} + + +/** + Starts an XA transaction with the given xid value. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_start(THD *thd) +{ + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_xa_start"); + + if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME) + { + bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid); + if (not_equal) + my_error(ER_XAER_NOTA, MYF(0)); + else + thd->transaction.xid_state.xa_state= XA_ACTIVE; + DBUG_RETURN(not_equal); + } + + /* TODO: JOIN is not supported yet. */ + if (thd->lex->xa_opt != XA_NONE) + my_error(ER_XAER_INVAL, MYF(0)); + else if (xa_state != XA_NOTR) + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + else if (thd->locked_tables_mode || thd->active_transaction()) + my_error(ER_XAER_OUTSIDE, MYF(0)); + else if (xid_cache_search(thd->lex->xid)) + my_error(ER_XAER_DUPID, MYF(0)); + else if (!trans_begin(thd)) + { + DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); + thd->transaction.xid_state.xa_state= XA_ACTIVE; + thd->transaction.xid_state.rm_error= 0; + thd->transaction.xid_state.xid.set(thd->lex->xid); + xid_cache_insert(&thd->transaction.xid_state); + DBUG_RETURN(FALSE); + } + + DBUG_RETURN(TRUE); +} + + +/** + Put a XA transaction in the IDLE state. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_end(THD *thd) +{ + DBUG_ENTER("trans_xa_end"); + + /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */ + if (thd->lex->xa_opt != XA_NONE) + my_error(ER_XAER_INVAL, MYF(0)); + else if (thd->transaction.xid_state.xa_state != XA_ACTIVE) + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + my_error(ER_XAER_NOTA, MYF(0)); + else if (!xa_trans_rolled_back(&thd->transaction.xid_state)) + thd->transaction.xid_state.xa_state= XA_IDLE; + + DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_IDLE); +} + + +/** + Put a XA transaction in the PREPARED state. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_prepare(THD *thd) +{ + DBUG_ENTER("trans_xa_prepare"); + + if (thd->transaction.xid_state.xa_state != XA_IDLE) + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + my_error(ER_XAER_NOTA, MYF(0)); + else if (ha_prepare(thd)) + { + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + my_error(ER_XA_RBROLLBACK, MYF(0)); + } + else + thd->transaction.xid_state.xa_state= XA_PREPARED; + + DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_PREPARED); +} + + +/** + Commit and terminate the a XA transaction. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_commit(THD *thd) +{ + bool res= TRUE; + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_xa_commit"); + + if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + { + XID_STATE *xs= xid_cache_search(thd->lex->xid); + res= !xs || xs->in_thd; + if (res) + my_error(ER_XAER_NOTA, MYF(0)); + else + { + res= xa_trans_rolled_back(xs); + ha_commit_or_rollback_by_xid(thd->lex->xid, !res); + xid_cache_delete(xs); + } + DBUG_RETURN(res); + } + + if (xa_trans_rolled_back(&thd->transaction.xid_state)) + { + if ((res= test(ha_rollback_trans(thd, TRUE)))) + my_error(ER_XAER_RMERR, MYF(0)); + } + else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) + { + int r= ha_commit_trans(thd, TRUE); + if ((res= test(r))) + my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); + } + else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE) + { + if (wait_if_global_read_lock(thd, 0, 0)) + { + ha_rollback_trans(thd, TRUE); + my_error(ER_XAER_RMERR, MYF(0)); + } + else + { + res= test(ha_commit_one_phase(thd, 1)); + if (res) + my_error(ER_XAER_RMERR, MYF(0)); + start_waiting_global_read_lock(thd); + } + } + else + { + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + DBUG_RETURN(TRUE); + } + + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + + DBUG_RETURN(res); +} + + +/** + Roll back and terminate a XA transaction. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_rollback(THD *thd) +{ + bool res= TRUE; + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_xa_rollback"); + + if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + { + XID_STATE *xs= xid_cache_search(thd->lex->xid); + if (!xs || xs->in_thd) + my_error(ER_XAER_NOTA, MYF(0)); + else + { + xa_trans_rolled_back(xs); + ha_commit_or_rollback_by_xid(thd->lex->xid, 0); + xid_cache_delete(xs); + } + DBUG_RETURN(thd->stmt_da->is_error()); + } + + if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY) + { + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + DBUG_RETURN(TRUE); + } + + /* + Resource Manager error is meaningless at this point, as we perform + explicit rollback request by user. We must reset rm_error before + calling ha_rollback(), so thd->transaction.xid structure gets reset + by ha_rollback()/THD::transaction::cleanup(). + */ + thd->transaction.xid_state.rm_error= 0; + if ((res= test(ha_rollback_trans(thd, TRUE)))) + my_error(ER_XAER_RMERR, MYF(0)); + + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + + DBUG_RETURN(res); +} diff --git a/sql/transaction.h b/sql/transaction.h new file mode 100644 index 00000000000..135e6cae73f --- /dev/null +++ b/sql/transaction.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2008 Sun/MySQL + + 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 TRANSACTION_H +#define TRANSACTION_H + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#include +#include + +class THD; + +bool trans_begin(THD *thd, uint flags= 0); +bool trans_commit(THD *thd); +bool trans_commit_implicit(THD *thd); +bool trans_rollback(THD *thd); + +bool trans_commit_stmt(THD *thd); +bool trans_rollback_stmt(THD *thd); + +bool trans_savepoint(THD *thd, LEX_STRING name); +bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name); +bool trans_release_savepoint(THD *thd, LEX_STRING name); + +bool trans_xa_start(THD *thd); +bool trans_xa_end(THD *thd); +bool trans_xa_prepare(THD *thd); +bool trans_xa_commit(THD *thd); +bool trans_xa_rollback(THD *thd); + +#endif /* TRANSACTION_H */ From 36897c862b2d46a13794fd305fa9a29fb2ac51b7 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 21:59:52 +0300 Subject: [PATCH 084/466] Backport of: ----------------------------------------------------------- 2497.479.10 Sergei Golubchik 2008-08-27 proc_info_hook, mysys access to thd->proc_info This patch is necessary for backport of WL#4284 "Transactional DDL locking". --- include/my_global.h | 2 +- include/my_sys.h | 3 +++ mysys/my_static.c | 13 +++++++++++++ sql/mysqld.cc | 4 ++++ sql/sql_class.cc | 11 +++++++---- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 0f2aea62438..f95c8aa2441 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1561,7 +1561,7 @@ inline void operator delete[](void*, void*) { /* Do nothing */ } #if !defined(max) #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) -#endif +#endif /* Only Linux is known to need an explicit sync of the directory to make sure a file creation/deletion/renaming in(from,to) this directory durable. diff --git a/include/my_sys.h b/include/my_sys.h index 9121f0f249e..bbbc89cb9f8 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -235,6 +235,9 @@ extern void (*fatal_error_handler_hook)(uint my_err, const char *str, extern uint my_file_limit; extern ulong my_thread_stack_size; +extern const char *(*proc_info_hook)(void *, const char *, const char *, + const char *, const unsigned int); + #ifdef HAVE_LARGE_PAGES extern my_bool my_use_large_pages; extern uint my_large_page_size; diff --git a/mysys/my_static.c b/mysys/my_static.c index 62e6d402315..62caeee89c4 100644 --- a/mysys/my_static.c +++ b/mysys/my_static.c @@ -92,6 +92,19 @@ void (*error_handler_hook)(uint error,const char *str,myf MyFlags)= void (*fatal_error_handler_hook)(uint error,const char *str,myf MyFlags)= my_message_no_curses; +static const char *proc_info_dummy(void *a __attribute__((unused)), + const char *b __attribute__((unused)), + const char *c __attribute__((unused)), + const char *d __attribute__((unused)), + const unsigned int e __attribute__((unused))) +{ + return 0; +} + +/* this is to be able to call set_thd_proc_info from the C code */ +const char *(*proc_info_hook)(void *, const char *, const char *, const char *, + const unsigned int)= proc_info_dummy; + #if defined(ENABLED_DEBUG_SYNC) /** Global pointer to be set if callback function is defined diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ee341314cd6..1055d5fed8b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3828,6 +3828,10 @@ static int init_server_components() } } + proc_info_hook= (const char *(*)(void *, const char *, const char *, + const char *, const unsigned int)) + set_thd_proc_info; + if (xid_cache_init()) { sql_print_error("Out of memory"); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index de2ef98e993..ee6612acfe2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -247,13 +247,16 @@ int thd_tablespace_op(const THD *thd) extern "C" -const char *set_thd_proc_info(THD *thd, const char *info, - const char *calling_function, - const char *calling_file, +const char *set_thd_proc_info(THD *thd, const char *info, + const char *calling_function, + const char *calling_file, const unsigned int calling_line) { + if (!thd) + thd= current_thd; + const char *old_info= thd->proc_info; - DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, + DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, (info != NULL) ? info : "(null)")); #if defined(ENABLED_PROFILING) thd->profiling.status_change(info, calling_function, calling_file, calling_line); From c44665aeb1b45bed1f21bce8faeca7525e73d672 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 23:08:27 +0300 Subject: [PATCH 085/466] Backport of: ------------------------------------------------------------ revno: 3035.4.1 committer: Davi Arnaut branch nick: 39897-6.0 timestamp: Thu 2009-01-15 12:17:57 -0200 message: Bug#39897: lock_multi fails in pushbuild: timeout waiting for processlist The problem is that relying on the "Table lock" thread state in its current position to detect that a thread is waiting on a lock is race prone. The "Table lock" state change happens before the thread actually tries to grab a lock on a table. The solution is to move the "Table lock" state so that its set only when a thread is actually going to wait for a lock. The state change happens after the thread fails to grab the lock (because it is owned by other thread) and proceeds to wait on a condition. This is considered part of work related to WL#4284 "Transactional DDL locking" Warning: this patch contains an incompatible change. When waiting on a lock in thr_lock.c, the server used to display "Locked" processlist state. After this patch, the state is "Table lock". The new state was actually intended to be display since year 2002, when Monty added it. But up until removal of thd->locked boolean member, this state was ignored by SHOW PROCESSLIST code. --- mysql-test/r/lock_multi.result | 2 +- mysql-test/r/sp-threads.result | 2 +- mysql-test/t/delayed.test | 2 +- mysql-test/t/insert_notembedded.test | 2 +- mysql-test/t/lock_multi.test | 28 ++++++++++++++-------------- mysql-test/t/lock_sync.test | 2 +- mysql-test/t/multi_update.test | 4 ++-- mysql-test/t/query_cache_28249.test | 6 +++--- mysql-test/t/sp_notembedded.test | 2 +- mysql-test/t/status.test | 2 +- mysys/thr_lock.c | 7 +++++++ sql/lock.cc | 1 - 12 files changed, 33 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 3f1165fd069..930983bae27 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -203,7 +203,7 @@ drop table if exists t1,t2; create table t1 (a int); flush status; lock tables t1 read; -insert into t1 values(1);; +insert into t1 values(1); unlock tables; drop table t1; select @tlwa < @tlwb; diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index 953830ecc87..d974cfb9605 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -35,7 +35,7 @@ call bug9486(); show processlist; Id User Host db Command Time State Info # root localhost test Sleep # NULL -# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2 +# root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2 # root localhost test Query # NULL show processlist # root localhost test Sleep # NULL unlock tables; diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index ee658d70d16..86311784b6a 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -307,7 +307,7 @@ connection update; connection default; let $wait_condition= select count(*) = 1 from information_schema.processlist - where command = "Delayed insert" and state = "upgrading lock"; + where command = "Delayed insert" and state = "Table lock"; --source include/wait_condition.inc connect (select,localhost,root,,); --echo connection: select diff --git a/mysql-test/t/insert_notembedded.test b/mysql-test/t/insert_notembedded.test index 2950acff3cc..510dc56e8f7 100644 --- a/mysql-test/t/insert_notembedded.test +++ b/mysql-test/t/insert_notembedded.test @@ -174,7 +174,7 @@ connection default; # we must wait till the insert opens and locks the table let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and id = $ID; + where state = "Table lock" and id = $ID; --source include/wait_condition.inc connect (select,localhost,root,,); --echo connection: select diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 58f46941920..c82e351dfef 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -22,7 +22,7 @@ connection reader; # Sleep a bit till the update of connection writer is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "update low_priority t1 set n = 4"; + where state = "Table lock" and info = "update low_priority t1 set n = 4"; --source include/wait_condition.inc send select n from t1; @@ -30,7 +30,7 @@ connection locker; # Sleep a bit till the select of connection reader is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "select n from t1"; + where state = "Table lock" and info = "select n from t1"; --source include/wait_condition.inc unlock tables; connection writer; @@ -50,7 +50,7 @@ connection reader; # Sleep a bit till the update of connection writer is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "update low_priority t1 set n = 4"; + where state = "Table lock" and info = "update low_priority t1 set n = 4"; --source include/wait_condition.inc select n from t1; connection locker; @@ -95,7 +95,7 @@ insert t1 select * from t2; connection locker; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "insert t1 select * from t2"; + where state = "Table lock" and info = "insert t1 select * from t2"; --source include/wait_condition.inc drop table t2; connection reader; @@ -119,7 +119,7 @@ connection locker; # Sleep a bit till the insert of connection reader is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "insert t1 select * from t2"; + where state = "Table lock" and info = "insert t1 select * from t2"; --source include/wait_condition.inc drop table t2; connection reader; @@ -163,8 +163,8 @@ SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; connection locker; # Sleep a bit till the select of connection reader is in work and hangs let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Table lock" AND info = "SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1"; --source include/wait_condition.inc # Make test case independent from earlier grants. @@ -298,7 +298,7 @@ connection reader; # Wait till connection writer is blocked let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "alter table t1 auto_increment=0"; + where state = "Table lock" and info = "alter table t1 auto_increment=0"; --source include/wait_condition.inc send alter table t1 auto_increment=0; @@ -306,7 +306,7 @@ connection locker; # Wait till connection reader is blocked let $wait_condition= select count(*) = 2 from information_schema.processlist - where state = "Locked" and info = "alter table t1 auto_increment=0"; + where state = "Table lock" and info = "alter table t1 auto_increment=0"; --source include/wait_condition.inc unlock tables; connection writer; @@ -461,16 +461,16 @@ update t1 set i= 10; connection reader; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "update t1 set i= 10"; + where state = "Table lock" and info = "update t1 set i= 10"; --source include/wait_condition.inc send select * from t1; connection default; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "select * from t1"; + where state = "Table lock" and info = "select * from t1"; --source include/wait_condition.inc -let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`; +let $ID= `select id from information_schema.processlist where state = "Table lock" and info = "update t1 set i= 10"`; --replace_result $ID ID eval kill query $ID; connection reader; @@ -613,11 +613,11 @@ lock tables t1 read; let $tlwa= `show status like 'Table_locks_waited'`; connect (waiter,localhost,root,,); connection waiter; ---send insert into t1 values(1); +send insert into t1 values(1); connection default; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "insert into t1 values(1)"; + where state = "Table lock" and info = "insert into t1 values(1)"; --source include/wait_condition.inc let $tlwb= `show status like 'Table_locks_waited'`; unlock tables; diff --git a/mysql-test/t/lock_sync.test b/mysql-test/t/lock_sync.test index 73289685114..de8a1d7e43e 100644 --- a/mysql-test/t/lock_sync.test +++ b/mysql-test/t/lock_sync.test @@ -71,7 +71,7 @@ set debug_sync= 'now WAIT_FOR parked'; connection default; --echo # Wait until this LOCK TABLES statement starts waiting for table lock. let $wait_condition= select count(*)= 1 from information_schema.processlist - where state= 'Locked' and + where state= 'Table lock' and info='lock table t1 write'; --source include/wait_condition.inc --echo # Allow SELECT ... FOR UPDATE to resume. diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index fc37fd6a27d..6f8cc94e6b7 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -496,7 +496,7 @@ connection updater; # Wait till "alter table t1 ..." of session changer is in work. # = There is one session is in state "Locked". let $wait_condition= select count(*)= 1 from information_schema.processlist - where state= 'Locked'; + where state= 'Table lock'; --source include/wait_condition.inc send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a; @@ -507,7 +507,7 @@ connection locker; # are in work. # = There are two session is in state "Locked". let $wait_condition= select count(*)= 2 from information_schema.processlist - where state= 'Locked'; + where state= 'Table lock'; --source include/wait_condition.inc unlock tables; diff --git a/mysql-test/t/query_cache_28249.test b/mysql-test/t/query_cache_28249.test index 390a1ce6e3d..c95d7553988 100644 --- a/mysql-test/t/query_cache_28249.test +++ b/mysql-test/t/query_cache_28249.test @@ -58,18 +58,18 @@ connection user3; # Typical information_schema.processlist content after sufficient sleep time # ID USER COMMAND TIME STATE INFO # .... -# 2 root Query 5 Locked SELECT *, (SELECT COUNT(*) FROM t2) FROM t1 +# 2 root Query 5 Table lock SELECT *, (SELECT COUNT(*) FROM t2) FROM t1 # .... # XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # The values marked with 'X' must be reached. --echo # Poll till the select of connection user1 is blocked by the write lock on t1. let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist -WHERE state = 'Locked' +WHERE state = 'Table lock' AND info = '$select_for_qc'; --source include/wait_condition.inc eval SELECT user,command,state,info FROM information_schema.processlist -WHERE state = 'Locked' +WHERE state = 'Table lock' AND info = '$select_for_qc'; INSERT INTO t1 VALUES (4); diff --git a/mysql-test/t/sp_notembedded.test b/mysql-test/t/sp_notembedded.test index f593e184ad2..f7984952e33 100644 --- a/mysql-test/t/sp_notembedded.test +++ b/mysql-test/t/sp_notembedded.test @@ -322,7 +322,7 @@ set session low_priority_updates=on; connection rl_wait; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and + where state = "Table lock" and info = "update t1 set value='updated' where value='old'"; --source include/wait_condition.inc diff --git a/mysql-test/t/status.test b/mysql-test/t/status.test index f951218f5c8..cca54f6c762 100644 --- a/mysql-test/t/status.test +++ b/mysql-test/t/status.test @@ -58,7 +58,7 @@ let $ID= `select connection_id()`; connection con2; --echo # Switched to connection: con2 # wait for the other query to start executing -let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Locked"; +let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Table lock"; --source include/wait_condition.inc unlock tables; diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index e92f68965d8..6433c04a96f 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; my_bool can_deadlock= test(data->owner->info->n_cursors); + const char *old_proc_info; DBUG_ENTER("wait_for_lock"); /* @@ -434,6 +435,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, thread_var->current_cond= cond; data->cond= cond; + old_proc_info= proc_info_hook(NULL, "Table lock", + __func__, __FILE__, __LINE__); + if (can_deadlock) set_timespec(wait_timeout, table_lock_wait_timeout); while (!thread_var->abort || in_wait_list) @@ -504,6 +508,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, thread_var->current_mutex= 0; thread_var->current_cond= 0; pthread_mutex_unlock(&thread_var->mutex); + + proc_info_hook(NULL, old_proc_info, __func__, __FILE__, __LINE__); + DBUG_RETURN(result); } diff --git a/sql/lock.cc b/sql/lock.cc index d19f4b46821..a46fffc6180 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -310,7 +310,6 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, break; } DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); - thd_proc_info(thd, "Locked"); /* Copy the lock data array. thr_multi_lock() reorders its contens. */ memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks, sql_lock->lock_count * sizeof(*sql_lock->locks)); From 67c1b06f1257744d587779132e8434cbeb5fd343 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 01:46:14 +0300 Subject: [PATCH 086/466] ------------------------------------------------------------ revno: 2617.22.4 committer: Davi Arnaut branch nick: mysql-6.0-runtime timestamp: Mon 2009-01-26 15:19:14 -0200 message: Move checks for OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN to a separate helper function. --- sql/ha_ndbcluster.cc | 25 ++++++++++--------------- sql/log.cc | 7 +++---- sql/sql_cache.cc | 13 +++++-------- sql/sql_class.cc | 3 +-- sql/sql_class.h | 15 +++++++++++++++ sql/sql_parse.cc | 2 +- sql/transaction.cc | 7 +++---- 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index a7c4cbf5d20..c7dbe5ad36c 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -4512,7 +4512,7 @@ int ha_ndbcluster::start_statement(THD *thd, trans_register_ha(thd, FALSE, ndbcluster_hton); if (!thd_ndb->trans) { - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + if (thd->in_multi_stmt_transaction()) trans_register_ha(thd, TRUE, ndbcluster_hton); DBUG_PRINT("trans",("Starting transaction")); thd_ndb->trans= ndb->startTransaction(); @@ -4582,7 +4582,7 @@ int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb) } #endif - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + if (thd->in_multi_stmt_transaction()) { const void *key= m_table; HASH_SEARCH_STATE state; @@ -4666,7 +4666,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) if (ndb_cache_check_time && m_rows_changed) { DBUG_PRINT("info", ("Rows has changed and util thread is running")); - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + if (thd->in_multi_stmt_transaction()) { DBUG_PRINT("info", ("Add share to list of tables to be invalidated")); /* NOTE push_back allocates memory using transactions mem_root! */ @@ -4685,7 +4685,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) DBUG_PRINT("trans", ("Last external_lock")); PRINT_OPTION_FLAGS(thd); - if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + if (!thd->in_multi_stmt_transaction()) { if (thd_ndb->trans) { @@ -4795,8 +4795,7 @@ static int ndbcluster_commit(handlerton *hton, THD *thd, bool all) PRINT_OPTION_FLAGS(thd); DBUG_PRINT("enter", ("Commit %s", (all ? "all" : "stmt"))); thd_ndb->start_stmt_count= 0; - if (trans == NULL || (!all && - thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + if (trans == NULL || (!all && thd->in_multi_stmt_transaction())) { /* An odditity in the handler interface is that commit on handlerton @@ -4866,7 +4865,7 @@ static int ndbcluster_rollback(handlerton *hton, THD *thd, bool all) DBUG_ASSERT(ndb); thd_ndb->start_stmt_count= 0; if (trans == NULL || (!all && - thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + thd->in_multi_stmt_transaction())) { /* Ignore end-of-statement until real rollback or commit is called */ DBUG_PRINT("info", ("Rollback before start or end-of-statement only")); @@ -8041,17 +8040,15 @@ ndbcluster_cache_retrieval_allowed(THD *thd, ulonglong *engine_data) { Uint64 commit_count; - bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); char *dbname= full_name; char *tabname= dbname+strlen(dbname)+1; #ifndef DBUG_OFF char buff[22], buff2[22]; #endif DBUG_ENTER("ndbcluster_cache_retrieval_allowed"); - DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d", - dbname, tabname, is_autocommit)); + DBUG_PRINT("enter", ("dbname: %s, tabname: %s", dbname, tabname)); - if (!is_autocommit) + if (thd->in_multi_stmt_transaction()) { DBUG_PRINT("exit", ("No, don't use cache in transaction")); DBUG_RETURN(FALSE); @@ -8116,12 +8113,10 @@ ha_ndbcluster::register_query_cache_table(THD *thd, #ifndef DBUG_OFF char buff[22]; #endif - bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); DBUG_ENTER("ha_ndbcluster::register_query_cache_table"); - DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d", - m_dbname, m_tabname, is_autocommit)); + DBUG_PRINT("enter",("dbname: %s, tabname: %s", m_dbname, m_tabname)); - if (!is_autocommit) + if (thd->in_multi_stmt_transaction()) { DBUG_PRINT("exit", ("Can't register table during transaction")); DBUG_RETURN(FALSE); diff --git a/sql/log.cc b/sql/log.cc index ea06f71b569..48e509e9275 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1503,7 +1503,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, transaction cache to remove the statement. */ thd->binlog_remove_pending_rows_event(TRUE); - if (all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) + if (all || !thd->in_multi_stmt_transaction()) { if (trx_data->has_incident()) mysql_bin_log.write_incident(thd, TRUE); @@ -1571,8 +1571,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) Otherwise, we accumulate the statement */ - ulonglong const in_transaction= - thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN); + bool const in_transaction= thd->in_multi_stmt_transaction(); DBUG_PRINT("debug", ("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s", all, @@ -3903,7 +3902,7 @@ THD::binlog_start_trans_and_stmt() trx_data->before_stmt_pos == MY_OFF_T_UNDEF) { this->binlog_set_stmt_begin(); - if (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + if (in_multi_stmt_transaction()) trans_register_ha(this, TRUE, binlog_hton); trans_register_ha(this, FALSE, binlog_hton); /* diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 871efeddd43..9d2a55f0f3a 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1517,7 +1517,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", } DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query)); - if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && + if (thd->in_multi_stmt_transaction() && (query->tables_type() & HA_CACHE_TBL_TRANSACT)) { DBUG_PRINT("qcache", @@ -1674,8 +1674,7 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, if (is_disabled()) DBUG_VOID_RETURN; - using_transactions= using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + using_transactions= using_transactions && thd->in_multi_stmt_transaction(); for (; tables_used; tables_used= tables_used->next_local) { DBUG_ASSERT(!using_transactions || tables_used->table!=0); @@ -1759,8 +1758,7 @@ void Query_cache::invalidate(THD *thd, TABLE *table, if (is_disabled()) DBUG_VOID_RETURN; - using_transactions= using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + using_transactions= using_transactions && thd->in_multi_stmt_transaction(); if (using_transactions && (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT)) thd->add_changed_table(table); @@ -1778,8 +1776,7 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, if (is_disabled()) DBUG_VOID_RETURN; - using_transactions= using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + using_transactions= using_transactions && thd->in_multi_stmt_transaction(); if (using_transactions) // used for innodb => has_transactions() is TRUE thd->add_changed_table(key, key_length); else @@ -3549,7 +3546,7 @@ Query_cache::is_cacheable(THD *thd, size_t query_len, const char *query, tables_type))) DBUG_RETURN(0); - if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && + if (thd->in_multi_stmt_transaction() && ((*tables_type)&HA_CACHE_TBL_TRANSACT)) { DBUG_PRINT("qcache", ("not in autocommin mode")); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ee6612acfe2..4f1777c152c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1412,8 +1412,7 @@ void THD::add_changed_table(TABLE *table) { DBUG_ENTER("THD::add_changed_table(table)"); - DBUG_ASSERT((options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && - table->file->has_transactions()); + DBUG_ASSERT(in_multi_stmt_transaction() && table->file->has_transactions()); add_changed_table(table->s->table_cache_key.str, (long) table->s->table_cache_key.length); DBUG_VOID_RETURN; diff --git a/sql/sql_class.h b/sql/sql_class.h index bef8dff5759..34ba1386a89 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2008,6 +2008,21 @@ public: { return server_status & SERVER_STATUS_IN_TRANS; } + /** + Returns TRUE if session is in a multi-statement transaction mode. + + OPTION_NOT_AUTOCOMMIT: When autocommit is off, a multi-statement + transaction is implicitly started on the first statement after a + previous transaction has been ended. + + OPTION_BEGIN: Regardless of the autocommit status, a multi-statement + transaction can be explicitly started with the statements "START + TRANSACTION", "BEGIN [WORK]", "[COMMIT | ROLLBACK] AND CHAIN", etc. + */ + inline bool in_multi_stmt_transaction() + { + return options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN); + } inline bool fill_derived_tables() { return !stmt_arena->is_stmt_prepare() && !lex->only_view_structure(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2df66a44fa6..83e45904816 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5306,7 +5306,7 @@ void mysql_reset_thd_for_next_command(THD *thd) OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings in ha_rollback_trans() about some tables couldn't be rolled back. */ - if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + if (!thd->in_multi_stmt_transaction()) { thd->options&= ~OPTION_KEEP_LOG; thd->transaction.all.modified_non_trans_table= FALSE; diff --git a/sql/transaction.cc b/sql/transaction.cc index f4a616ead6b..7bfaf4846cf 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -163,8 +163,7 @@ bool trans_commit_implicit(THD *thd) if (trans_check(thd)) DBUG_RETURN(TRUE); - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | - OPTION_TABLE_LOCK)) + if (thd->in_multi_stmt_transaction() || (thd->options & OPTION_TABLE_LOCK)) { /* Safety if one did "drop table" on locked tables */ if (!thd->locked_tables_mode) @@ -299,8 +298,8 @@ bool trans_savepoint(THD *thd, LEX_STRING name) SAVEPOINT **sv, *newsv; DBUG_ENTER("trans_savepoint"); - if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) || - thd->in_sub_stmt) || !opt_using_transactions) + if (!(thd->in_multi_stmt_transaction() || thd->in_sub_stmt) || + !opt_using_transactions) DBUG_RETURN(FALSE); sv= find_savepoint(thd, name); From a2312bf2c652d2ddc4ed53b808c1fc74e8870ba3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 01:48:14 +0300 Subject: [PATCH 087/466] Backport of: ---------------------------------------------------------- revno: 2617.22.7 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Tue 2009-01-27 16:41:58 +0300 message: WL#4284 "Transactional DDL locking" review. Improve a comment. --- sql/mdl.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index f28778a1a83..78a85f49952 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1320,10 +1320,11 @@ void mdl_release_locks(MDL_CONTEXT *context) { DBUG_PRINT("info", ("found lock to release lock_data=%p", lock_data)); /* - We should not release locks which pending shared locks as these - are not associated with lock object and don't present in its - lists. Allows us to avoid problems in open_tables() in case of - back-off + Don't call release_lock() for a shared lock if has not been + granted. Lock state in this case is MDL_INITIALIZED. + We have pending and granted shared locks in the same context + when this function is called from the "back-off" path of + open_tables(). */ if (lock_data->state != MDL_INITIALIZED) { From 9d52bd52494eccc4bedc2b218f495780275dcb16 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 01:55:34 +0300 Subject: [PATCH 088/466] WL#4284, "Transactional DDL locking". Add two more files to .bzrignore. --- .bzrignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bzrignore b/.bzrignore index bc3ca7b0c24..0a30ac2d9b7 100644 --- a/.bzrignore +++ b/.bzrignore @@ -3071,3 +3071,5 @@ libmysqld/debug_sync.cc libmysqld/rpl_handler.cc dbug/tests libmysqld/mdl.cc +client/transaction.h +libmysqld/transaction.cc From 8582b4de5d2a44f64de2f6aaa15638d288953939 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 01:55:58 +0300 Subject: [PATCH 089/466] WL#4284, "Transactional DDL locking". Add two more files to .bzrignore. From 911c673edf45616137d71f43f472be410ecb7511 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:29:40 +0300 Subject: [PATCH 090/466] Backport of: ------------------------------------------------------------ revno: 2617.23.18 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Mon 2009-03-02 18:18:26 -0300 message: Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order WL#4284: Transactional DDL locking This is a prerequisite patch: These changes are intended to split lock requests from granted locks and to allow the memory and lifetime of granted locks to be managed within the MDL subsystem. Furthermore, tickets can now be shared and therefore are used to satisfy multiple lock requests, but only shared locks can be recursive. The problem is that the MDL subsystem morphs lock requests into granted locks locks but does not manage the memory and lifetime of lock requests, and hence, does not manage the memory of granted locks either. This can be problematic because it puts the burden of tracking references on the users of the subsystem and it can't be easily done in transactional contexts where the locks have to be kept around for the duration of a transaction. Another issue is that recursive locks (when the context trying to acquire a lock already holds a lock on the same object) requires that each time the lock is granted, a unique lock request/granted lock structure structure must be kept around until the lock is released. This can lead to memory leaks in transactional contexts as locks taken during the transaction should only be released at the end of the transaction. This also leads to unnecessary wake ups (broadcasts) in the MDL subsystem if the context still holds a equivalent of the lock being released. These issues are exacerbated due to the fact that WL#4284 low-level design says that the implementation should "2) Store metadata locks in transaction memory root, rather than statement memory root" but this is not possible because a memory root, as implemented in mysys, requires all objects allocated from it to be freed all at once. This patch combines review input and significant code contributions from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev). --- mysql-test/r/mdl_sync.result | 21 + mysql-test/t/mdl_sync.test | 69 ++ sql/event_db_repository.cc | 8 +- sql/ha_ndbcluster_binlog.cc | 8 +- sql/lock.cc | 20 +- sql/log_event.cc | 13 +- sql/mdl.cc | 1020 ++++++++++++++++------------- sql/mdl.h | 300 +++++---- sql/sp_head.cc | 15 +- sql/sql_acl.cc | 16 +- sql/sql_base.cc | 104 +-- sql/sql_class.h | 2 + sql/sql_delete.cc | 24 +- sql/sql_handler.cc | 25 +- sql/sql_insert.cc | 2 +- sql/sql_parse.cc | 10 +- sql/sql_plist.h | 1 + sql/sql_plugin.cc | 6 +- sql/sql_servers.cc | 8 +- sql/sql_show.cc | 34 +- sql/sql_table.cc | 124 ++-- sql/sql_trigger.cc | 9 +- sql/sql_udf.cc | 6 +- sql/table.cc | 7 +- sql/table.h | 9 +- storage/myisammrg/ha_myisammrg.cc | 7 +- 26 files changed, 1046 insertions(+), 822 deletions(-) create mode 100644 mysql-test/r/mdl_sync.result create mode 100644 mysql-test/t/mdl_sync.test diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result new file mode 100644 index 00000000000..37f18746c57 --- /dev/null +++ b/mysql-test/r/mdl_sync.result @@ -0,0 +1,21 @@ +SET DEBUG_SYNC= 'RESET'; +drop table if exists t1,t2,t3; +create table t1 (i int); +create table t2 (i int); +connection: default +lock tables t2 read; +connection: con1 +set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +alter table t1 rename t3; +connection: default +set debug_sync= 'now WAIT_FOR parked'; +connection: con2 +set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +drop table t1,t2; +connection: con1 +connection: default +unlock tables; +connection: con2 +ERROR 42S02: Unknown table 't1' +drop table t3; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test new file mode 100644 index 00000000000..ed03a9a5cb0 --- /dev/null +++ b/mysql-test/t/mdl_sync.test @@ -0,0 +1,69 @@ +# +# We need the Debug Sync Facility. +# +--source include/have_debug_sync.inc + +# Clean up resources used in this test case. +--disable_warnings +SET DEBUG_SYNC= 'RESET'; +--enable_warnings + +# +# Test the case of when a exclusive lock request waits for a +# shared lock being upgraded to a exclusive lock. +# + +connect (con1,localhost,root,,test,,); +connect (con2,localhost,root,,test,,); +connect (con3,localhost,root,,test,,); + +connection default; + +--disable_warnings +drop table if exists t1,t2,t3; +--enable_warnings + +create table t1 (i int); +create table t2 (i int); + +--echo connection: default +lock tables t2 read; + +connection con1; +--echo connection: con1 +set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +--send alter table t1 rename t3 + +connection default; +--echo connection: default +set debug_sync= 'now WAIT_FOR parked'; + +connection con2; +--echo connection: con2 +set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +--send drop table t1,t2 + +connection con1; +--echo connection: con1 +--reap + +connection default; +--echo connection: default +unlock tables; + +connection con2; +--echo connection: con2 +--error ER_BAD_TABLE_ERROR +--reap + +connection default; +drop table t3; + +disconnect con1; +disconnect con2; +disconnect con3; + +# Clean up resources used in this test case. +--disable_warnings +SET DEBUG_SYNC= 'RESET'; +--enable_warnings diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index e9ee54ff8aa..134c8059e13 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -555,7 +555,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, DBUG_ENTER("Event_db_repository::open_event_table"); tables.init_one_table("mysql", 5, "event", 5, "event", lock_type); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1110,7 +1110,7 @@ Event_db_repository::check_system_tables(THD *thd) /* Check mysql.db */ tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1128,7 +1128,7 @@ Event_db_repository::check_system_tables(THD *thd) } /* Check mysql.user */ tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1149,7 +1149,7 @@ Event_db_repository::check_system_tables(THD *thd) } /* Check mysql.event */ tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index b9ea87aec52..aa1c50eaf5b 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -140,8 +140,7 @@ static Uint64 *p_latest_trans_gci= 0; */ static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; -static MDL_LOCK_DATA binlog_mdl_lock_data; -static char binlog_mdlkey[MAX_MDLKEY_LENGTH]; +static MDL_LOCK_REQUEST binlog_mdl_lock_request; /* Helper functions @@ -2343,9 +2342,8 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) tables->alias= tables->table_name= reptable; tables->lock_type= TL_WRITE; thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE; - mdl_init_lock(&binlog_mdl_lock_data, binlog_mdlkey, 0, tables->db, - tables->table_name); - tables->mdl_lock_data= &binlog_mdl_lock_data; + mdl_request_init(&binlog_mdl_lock_request, 0, tables->db, tables->table_name); + tables->mdl_lock_request= &binlog_mdl_lock_request; tables->required_type= FRMTYPE_TABLE; uint counter; thd->clear_error(); diff --git a/sql/lock.cc b/sql/lock.cc index a46fffc6180..40bceae4a99 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -953,24 +953,24 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, bool lock_table_names(THD *thd, TABLE_LIST *table_list) { TABLE_LIST *lock_table; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_req; for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - if (!(mdl_lock_data= mdl_alloc_lock(0, lock_table->db, - lock_table->table_name, - thd->mem_root))) + mdl_lock_req= mdl_request_alloc(0, lock_table->db, lock_table->table_name, + thd->mem_root); + if (!mdl_lock_req) goto end; - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock_data); - lock_table->mdl_lock_data= mdl_lock_data; + mdl_request_set_type(mdl_lock_req, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_req); + lock_table->mdl_lock_request= mdl_lock_req; } if (mdl_acquire_exclusive_locks(&thd->mdl_context)) goto end; return 0; end: - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); return 1; } @@ -986,8 +986,8 @@ end: void unlock_table_names(THD *thd) { DBUG_ENTER("unlock_table_names"); - mdl_release_locks(&thd->mdl_context); - mdl_remove_all_locks(&thd->mdl_context); + mdl_ticket_release_all(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); DBUG_VOID_RETURN; } diff --git a/sql/log_event.cc b/sql/log_event.cc index 9c3a2ce2131..9f4369b901e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8059,8 +8059,8 @@ Table_map_log_event::~Table_map_log_event() int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; - char *db_mem, *tname_mem, *mdlkey; - MDL_LOCK_DATA *mdl_lock_data; + char *db_mem, *tname_mem; + MDL_LOCK_REQUEST *mdl_lock_request; size_t dummy_len; void *memory; DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); @@ -8075,8 +8075,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) &table_list, (uint) sizeof(RPL_TABLE_LIST), &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, - &mdl_lock_data, sizeof(MDL_LOCK_DATA), - &mdlkey, MAX_MDLKEY_LENGTH, + &mdl_lock_request, sizeof(MDL_LOCK_REQUEST), NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -8089,9 +8088,9 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) table_list->updating= 1; strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); strmov(table_list->table_name, m_tblnam); - mdl_init_lock(mdl_lock_data, mdlkey, 0, table_list->db, - table_list->table_name); - table_list->mdl_lock_data= mdl_lock_data; + mdl_request_init(mdl_lock_request, 0, table_list->db, + table_list->table_name); + table_list->mdl_lock_request= mdl_lock_request; int error= 0; diff --git a/sql/mdl.cc b/sql/mdl.cc index 78a85f49952..18f0debdbb5 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -16,6 +16,7 @@ #include "mdl.h" +#include "debug_sync.h" #include #include @@ -29,39 +30,43 @@ struct MDL_LOCK { - I_P_List active_shared; + typedef I_P_List > + Ticket_list; + + typedef Ticket_list::Iterator Ticket_iterator; + + /** The type of lock (shared or exclusive). */ + enum + { + SHARED, + EXCLUSIVE, + } type; + /** The key of the object (data) being protected. */ + MDL_KEY key; + /** List of granted tickets for this lock. */ + Ticket_list granted; /* There can be several upgraders and active exclusive belonging to the same context. */ - I_P_List active_shared_waiting_upgrade; - I_P_List active_exclusive; - I_P_List waiting_exclusive; - /** - Number of MDL_LOCK_DATA objects associated with this MDL_LOCK instance - and therefore present in one of above lists. Note that this number - doesn't account for pending requests for shared lock since we don't - associate them with MDL_LOCK and don't keep them in any list. - */ - uint lock_data_count; + Ticket_list waiting; void *cached_object; mdl_cached_object_release_hook cached_object_release_hook; - MDL_LOCK() : cached_object(0), cached_object_release_hook(0) {} - - MDL_LOCK_DATA *get_key_owner() + MDL_LOCK(const MDL_KEY *mdl_key) + : type(SHARED), cached_object(0), cached_object_release_hook(0) { - return !active_shared.is_empty() ? - active_shared.head() : - (!active_shared_waiting_upgrade.is_empty() ? - active_shared_waiting_upgrade.head() : - (!active_exclusive.is_empty() ? - active_exclusive.head() : waiting_exclusive.head())); + key.mdl_key_init(mdl_key); + granted.empty(); + waiting.empty(); } - bool has_one_lock_data() + bool is_empty() { - return (lock_data_count == 1); + return (granted.is_empty() && waiting.is_empty()); } }; @@ -86,12 +91,13 @@ struct MDL_GLOBAL_LOCK } global_lock; -extern "C" uchar *mdl_locks_key(const uchar *record, size_t *length, - my_bool not_used __attribute__((unused))) +extern "C" uchar * +mdl_locks_key(const uchar *record, size_t *length, + my_bool not_used __attribute__((unused))) { MDL_LOCK *entry=(MDL_LOCK*) record; - *length= entry->get_key_owner()->key_length; - return (uchar*) entry->get_key_owner()->key; + *length= entry->key.length(); + return (uchar*) entry->key.ptr(); } @@ -148,7 +154,8 @@ void mdl_destroy() void mdl_context_init(MDL_CONTEXT *context, THD *thd) { - context->locks.empty(); + context->requests.empty(); + context->tickets.empty(); context->thd= thd; context->has_global_shared_lock= FALSE; } @@ -168,7 +175,8 @@ void mdl_context_init(MDL_CONTEXT *context, THD *thd) void mdl_context_destroy(MDL_CONTEXT *context) { - DBUG_ASSERT(context->locks.is_empty()); + DBUG_ASSERT(context->requests.is_empty()); + DBUG_ASSERT(context->tickets.is_empty()); DBUG_ASSERT(!context->has_global_shared_lock); } @@ -185,8 +193,10 @@ void mdl_context_destroy(MDL_CONTEXT *context) void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) { - backup->locks.empty(); - ctx->locks.swap(backup->locks); + backup->requests.empty(); + backup->tickets.empty(); + ctx->requests.swap(backup->requests); + ctx->tickets.swap(backup->tickets); } @@ -196,8 +206,10 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) { - DBUG_ASSERT(ctx->locks.is_empty()); - ctx->locks.swap(backup->locks); + DBUG_ASSERT(ctx->requests.is_empty()); + DBUG_ASSERT(ctx->tickets.is_empty()); + ctx->requests.swap(backup->requests); + ctx->tickets.swap(backup->tickets); } @@ -207,20 +219,29 @@ void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) { - MDL_LOCK_DATA *lock_data; + MDL_LOCK_TICKET *ticket; + MDL_LOCK_REQUEST *lock_req; DBUG_ASSERT(dst->thd == src->thd); - if (!src->locks.is_empty()) + if (!src->requests.is_empty()) { - I_P_List_iterator it(src->locks); - while ((lock_data= it++)) + MDL_CONTEXT::Request_iterator it(src->requests); + while ((lock_req= it++)) + dst->requests.push_front(lock_req); + src->requests.empty(); + } + + if (!src->tickets.is_empty()) + { + MDL_CONTEXT::Ticket_iterator it(src->tickets); + while ((ticket= it++)) { - DBUG_ASSERT(lock_data->ctx); - lock_data->ctx= dst; - dst->locks.push_front(lock_data); + DBUG_ASSERT(ticket->ctx); + ticket->ctx= dst; + dst->tickets.push_front(ticket); } - src->locks.empty(); + src->tickets.empty(); } } @@ -230,53 +251,36 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) This is to be used for every lock request. - Note that initialization and allocation are split - into two calls. This is to allow flexible memory management - of lock requests. Normally a lock request is stored - in statement memory (e.g. is a member of struct TABLE_LIST), - but we would also like to allow allocation of lock - requests in other memory roots, for example in the grant - subsystem, to lock privilege tables. + Note that initialization and allocation are split into two + calls. This is to allow flexible memory management of lock + requests. Normally a lock request is stored in statement memory + (e.g. is a member of struct TABLE_LIST), but we would also like + to allow allocation of lock requests in other memory roots, + for example in the grant subsystem, to lock privilege tables. - The MDL subsystem does not own or manage memory of lock - requests. Instead it assumes that the life time of every lock - request encloses calls to mdl_acquire_shared_lock() and - mdl_release_locks(). + The MDL subsystem does not own or manage memory of lock requests. + Instead it assumes that the life time of every lock request (including + encompassed members db/name) encloses calls to mdl_request_add() + and mdl_request_remove() or mdl_request_remove_all(). - @param lock_data Pointer to an MDL_LOCK_DATA object to initialize - @param key_buff Pointer to the buffer for key for the lock request - (should be at least 4+ strlen(db) + 1 + strlen(name) - + 1 bytes, or, if the lengths are not known, - MAX_MDLKEY_LENGTH) + @param lock_req Pointer to an MDL_LOCK_REQUEST object to initialize @param type Id of type of object to be locked @param db Name of database to which the object belongs @param name Name of of the object - Stores the database name, object name and the type in the key - buffer. Initializes mdl_el to point to the key. - We can't simply initialize MDL_LOCK_DATA with type, db and name - by-pointer because of the underlying HASH implementation - requires the key to be a contiguous buffer. - The initialized lock request will have MDL_SHARED type. Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 - Note that tables and views have the same lock type, since + Note that tables and views must have the same lock type, since they share the same name space in the SQL standard. */ -void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, - const char *db, const char *name) +void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, + const char *db, const char *name) { - int4store(key, type); - lock_data->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - lock_data->key= key; - lock_data->type= MDL_SHARED; - lock_data->state= MDL_INITIALIZED; -#ifndef DBUG_OFF - lock_data->ctx= 0; - lock_data->lock= 0; -#endif + lock_req->key.mdl_key_init(type, db, name); + lock_req->type= MDL_SHARED; + lock_req->ticket= NULL; } @@ -298,19 +302,19 @@ void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, @retval non-0 Pointer to an object representing a lock request */ -MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root) +MDL_LOCK_REQUEST * +mdl_request_alloc(unsigned char type, const char *db, + const char *name, MEM_ROOT *root) { - MDL_LOCK_DATA *lock_data; - char *key; + MDL_LOCK_REQUEST *lock_req; - if (!multi_alloc_root(root, &lock_data, sizeof(MDL_LOCK_DATA), &key, - MAX_MDLKEY_LENGTH, NULL)) + if (!(lock_req= (MDL_LOCK_REQUEST*) alloc_root(root, + sizeof(MDL_LOCK_REQUEST)))) return NULL; - mdl_init_lock(lock_data, key, type, db, name); + mdl_request_init(lock_req, type, db, name); - return lock_data; + return lock_req; } @@ -318,113 +322,113 @@ MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, Add a lock request to the list of lock requests of the context. The procedure to acquire metadata locks is: - - allocate and initialize lock requests (mdl_alloc_lock()) - - associate them with a context (mdl_add_lock()) - - call mdl_acquire_shared_lock()/mdl_release_lock() (maybe repeatedly). + - allocate and initialize lock requests (mdl_request_alloc()) + - associate them with a context (mdl_request_add()) + - call mdl_acquire_shared_lock()/mdl_ticket_release() (maybe repeatedly). Associates a lock request with the given context. @param context The MDL context to associate the lock with. There should be no more than one context per connection, to avoid deadlocks. - @param lock_data The lock request to be added. + @param lock_req The lock request to be added. */ -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) { - DBUG_ENTER("mdl_add_lock"); - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - DBUG_ASSERT(!lock_data->ctx); - lock_data->ctx= context; - context->locks.push_front(lock_data); + DBUG_ENTER("mdl_request_add"); + DBUG_ASSERT(lock_req->ticket == NULL); + context->requests.push_front(lock_req); DBUG_VOID_RETURN; } /** - Remove a lock request from the list of lock requests of the context. + Remove a lock request from the list of lock requests. Disassociates a lock request from the given context. @param context The MDL context to remove the lock from. - @param lock_data The lock request to be removed. + @param lock_req The lock request to be removed. - @pre The lock request being removed should correspond to lock which + @pre The lock request being removed should correspond to a ticket that was released or was not acquired. - @note Resets lock request for lock released back to its initial state + @note Resets lock request back to its initial state (i.e. sets type to MDL_SHARED). */ -void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) { - DBUG_ENTER("mdl_remove_lock"); - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - DBUG_ASSERT(context == lock_data->ctx); + DBUG_ENTER("mdl_request_remove"); /* Reset lock request back to its initial state. */ - lock_data->type= MDL_SHARED; -#ifndef DBUG_OFF - lock_data->ctx= 0; -#endif - context->locks.remove(lock_data); + lock_req->type= MDL_SHARED; + lock_req->ticket= NULL; + context->requests.remove(lock_req); DBUG_VOID_RETURN; } /** - Clear all lock requests in the context (clear the context). - + Clear all lock requests in the context. Disassociates lock requests from the context. - All granted locks must be released prior to calling this - function. - - In other words, the expected procedure to release locks is: - - mdl_release_locks(); - - mdl_remove_all_locks(); - - We could possibly merge mdl_remove_all_locks() and mdl_release_locks(), - but this function comes in handy when we need to back off: in that case - we release all the locks acquired so-far but do not free them, since - we know that the respective lock requests will be used again. Also resets lock requests back to their initial state (i.e. MDL_SHARED). @param context Context to be cleared. */ -void mdl_remove_all_locks(MDL_CONTEXT *context) +void mdl_request_remove_all(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); - while ((lock_data= it++)) + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it(context->requests); + while ((lock_req= it++)) { /* Reset lock request back to its initial state. */ - lock_data->type= MDL_SHARED; -#ifndef DBUG_OFF - lock_data->ctx= 0; -#endif + lock_req->type= MDL_SHARED; + lock_req->ticket= NULL; } - context->locks.empty(); + context->requests.empty(); } /** - Auxiliary functions needed for creation/destruction of MDL_LOCK + Auxiliary functions needed for creation/destruction of MDL_LOCK objects. + + @todo This naive implementation should be replaced with one that saves + on memory allocation by reusing released objects. +*/ + +static MDL_LOCK* alloc_lock_object(const MDL_KEY *mdl_key) +{ + return new MDL_LOCK(mdl_key); +} + + +static void free_lock_object(MDL_LOCK *lock) +{ + delete lock; +} + + +/** + Auxiliary functions needed for creation/destruction of MDL_LOCK_TICKET objects. @todo This naive implementation should be replaced with one that saves on memory allocation by reusing released objects. */ -static MDL_LOCK* get_lock_object(void) +static MDL_LOCK_TICKET* alloc_ticket_object(MDL_CONTEXT *context) { - return new MDL_LOCK(); + MDL_LOCK_TICKET *ticket= new MDL_LOCK_TICKET; + return ticket; } -static void release_lock_object(MDL_LOCK *lock) +static void free_ticket_object(MDL_LOCK_TICKET *ticket) { - delete lock; + delete ticket; } @@ -432,7 +436,8 @@ static void release_lock_object(MDL_LOCK *lock) Helper functions which simplifies writing various checks and asserts. */ -static bool is_shared(MDL_LOCK_DATA *lock_data) +template +static inline bool is_shared(T *lock_data) { return (lock_data->type < MDL_EXCLUSIVE); } @@ -498,7 +503,7 @@ static inline void mdl_exit_cond(MDL_CONTEXT *context, request, implying a form of lock on the global metadata, is compatible with the current state of the global metadata lock. - @param lock_data Request for lock on an individual object, implying a + @param lock_req Request for lock on an individual object, implying a certain kind of global metadata lock. @retval TRUE - Lock request can be satisfied @@ -528,9 +533,9 @@ static inline void mdl_exit_cond(MDL_CONTEXT *context, type of locks we don't even have any accounting for them. */ -static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) +static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) { - switch (lock_data->type) + switch (type) { case MDL_SHARED: case MDL_SHARED_HIGH_PRIO: @@ -549,7 +554,7 @@ static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) return TRUE; break; case MDL_EXCLUSIVE: - if (lock_data->state == MDL_PENDING_UPGRADE) + if (is_upgrade) { /* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. @@ -586,7 +591,7 @@ static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) Check if request for the lock can be satisfied given current state of lock. @param lock Lock. - @param lock_data Request for lock. + @param lock_req Request for lock. @retval TRUE Lock request can be satisfied @retval FALSE There is some conflicting lock. @@ -612,68 +617,96 @@ static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) being upgraded. */ -static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) +static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, + enum_mdl_type type, bool is_upgrade) { - switch (lock_data->type) - { + bool can_grant= FALSE; + + switch (type) { case MDL_SHARED: case MDL_SHARED_UPGRADABLE: case MDL_SHARED_HIGH_PRIO: - if ((lock->active_exclusive.is_empty() && - (lock_data->type == MDL_SHARED_HIGH_PRIO || - lock->waiting_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.is_empty())) || - (!lock->active_exclusive.is_empty() && - lock->active_exclusive.head()->ctx == lock_data->ctx)) + if (lock->type == MDL_LOCK::SHARED) + { + /* Pending exclusive locks have higher priority over shared locks. */ + if (lock->waiting.is_empty() || type == MDL_SHARED_HIGH_PRIO) + can_grant= TRUE; + } + else if (lock->granted.head()->ctx == ctx) { /* When exclusive lock comes from the same context we can satisfy our shared lock. This is required for CREATE TABLE ... SELECT ... and ALTER VIEW ... AS .... */ - return TRUE; + can_grant= TRUE; } - else - return FALSE; break; case MDL_EXCLUSIVE: - if (lock_data->state == MDL_PENDING_UPGRADE) + if (is_upgrade) { /* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. */ - MDL_LOCK_DATA *conf_lock_data; - I_P_List_iterator it(lock->active_shared); + MDL_LOCK_TICKET *conf_lock_ticket; + MDL_LOCK::Ticket_iterator it(lock->granted); /* There should be no active exclusive locks since we own shared lock on the object. */ - DBUG_ASSERT(lock->active_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.head() == lock_data); + DBUG_ASSERT(lock->type == MDL_LOCK::SHARED); - while ((conf_lock_data= it++)) + while ((conf_lock_ticket= it++)) { /* When upgrading shared lock to exclusive one we can have other shared locks for the same object in the same context, e.g. in case when several instances of TABLE are open. */ - if (conf_lock_data->ctx != lock_data->ctx) - return FALSE; + if (conf_lock_ticket->ctx != ctx) + break; } - return TRUE; + /* Grant lock if there are no conflicting shared locks. */ + if (conf_lock_ticket == NULL) + can_grant= TRUE; + break; } - else + else if (lock->type == MDL_LOCK::SHARED) { - return (lock->active_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.is_empty() && - lock->active_shared.is_empty()); + can_grant= lock->granted.is_empty(); } break; default: DBUG_ASSERT(0); } - return FALSE; + return can_grant; +} + + +/** + Check whether the context already holds a compatible lock ticket + on a object. Only shared locks can be recursive. + + @param lock_req Lock request object for lock to be acquired + + @return A pointer to the lock ticket for the object or NULL otherwise. +*/ + +static MDL_LOCK_TICKET * +mdl_context_find_ticket(MDL_CONTEXT *ctx, MDL_LOCK_REQUEST *lock_req) +{ + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(ctx->tickets); + + DBUG_ASSERT(is_shared(lock_req)); + + while ((ticket= it++)) + { + if (lock_req->type == ticket->type && + lock_req->key.is_equal(&ticket->lock->key)) + break; + } + + return ticket; } @@ -691,7 +724,7 @@ static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) This function must be called after the lock is added to a context. @param context [in] Context containing request for lock - @param lock_data [in] Lock request object for lock to be acquired + @param lock_req [in] Lock request object for lock to be acquired @param retry [out] Indicates that conflicting lock exists and another attempt should be made after releasing all current locks and waiting for conflicting lock go away @@ -702,81 +735,111 @@ static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) In the latter case "retry" parameter is set to TRUE. */ -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, bool *retry) { MDL_LOCK *lock; + MDL_KEY *key= &lock_req->key; + MDL_LOCK_TICKET *ticket; *retry= FALSE; - DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_INITIALIZED); - - DBUG_ASSERT(lock_data->ctx == context); + DBUG_ASSERT(is_shared(lock_req) && lock_req->ticket == NULL); safe_mutex_assert_not_owner(&LOCK_open); if (context->has_global_shared_lock && - lock_data->type == MDL_SHARED_UPGRADABLE) + lock_req->type == MDL_SHARED_UPGRADABLE) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; } + /* + Check whether the context already holds a shared lock on the object, + and if so, grant the request. + */ + if ((ticket= mdl_context_find_ticket(context, lock_req))) + { + DBUG_ASSERT(ticket->state == MDL_ACQUIRED); + lock_req->ticket= ticket; + return FALSE; + } + pthread_mutex_lock(&LOCK_mdl); - if (!can_grant_global_lock(lock_data)) + if (!can_grant_global_lock(lock_req->type, FALSE)) { pthread_mutex_unlock(&LOCK_mdl); *retry= TRUE; return TRUE; } - if (!(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length))) + if (!(ticket= alloc_ticket_object(context))) { - if (!(lock= get_lock_object())) + pthread_mutex_unlock(&LOCK_mdl); + return TRUE; + } + + if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + key->ptr(), key->length()))) + { + /* Default lock type is MDL_LOCK::SHARED */ + lock= alloc_lock_object(key); + if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { + free_lock_object(lock); + free_ticket_object(ticket); pthread_mutex_unlock(&LOCK_mdl); return TRUE; } - /* - Before inserting MDL_LOCK object into hash we should add at least one - MDL_LOCK_DATA to its lists in order to provide key for this element. - Thus we can't merge two branches of the above if-statement. - */ - lock->active_shared.push_front(lock_data); - lock->lock_data_count= 1; - if (my_hash_insert(&mdl_locks, (uchar*)lock)) - { - release_lock_object(lock); - pthread_mutex_unlock(&LOCK_mdl); - return TRUE; - } - lock_data->state= MDL_ACQUIRED; - lock_data->lock= lock; - if (lock_data->type == MDL_SHARED_UPGRADABLE) + } + + if (can_grant_lock(context, lock, lock_req->type, FALSE)) + { + lock->granted.push_front(ticket); + context->tickets.push_front(ticket); + ticket->state= MDL_ACQUIRED; + lock_req->ticket= ticket; + ticket->lock= lock; + ticket->type= lock_req->type; + ticket->ctx= context; + if (lock_req->type == MDL_SHARED_UPGRADABLE) global_lock.active_intention_exclusive++; } else { - if (can_grant_lock(lock, lock_data)) - { - lock->active_shared.push_front(lock_data); - lock->lock_data_count++; - lock_data->state= MDL_ACQUIRED; - lock_data->lock= lock; - if (lock_data->type == MDL_SHARED_UPGRADABLE) - global_lock.active_intention_exclusive++; - } - else - *retry= TRUE; + /* We can't get here if we allocated a new lock. */ + DBUG_ASSERT(! lock->is_empty()); + *retry= TRUE; + free_ticket_object(ticket); } + pthread_mutex_unlock(&LOCK_mdl); return *retry; } -static void release_lock(MDL_LOCK_DATA *lock_data); +static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); + + +/** + Notify a thread holding a shared metadata lock of a pending exclusive lock. + + @param thd Current thread context + @param conf_lock_ticket Conflicting metadata lock + + @retval TRUE A thread was woken up + @retval FALSE Lock is not a shared one or no thread was woken up +*/ + +static bool notify_shared_lock(THD *thd, MDL_LOCK_TICKET *conf_lock_ticket) +{ + bool woke= FALSE; + if (conf_lock_ticket->type != MDL_EXCLUSIVE) + woke= mysql_notify_thread_having_shared_lock(thd, conf_lock_ticket->ctx->thd); + return woke; +} /** @@ -796,12 +859,13 @@ static void release_lock(MDL_LOCK_DATA *lock_data); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; MDL_LOCK *lock; bool signalled= FALSE; const char *old_msg; - I_P_List_iterator it(context->locks); + MDL_LOCK_REQUEST *lock_req; + MDL_LOCK_TICKET *ticket; st_my_thread_var *mysys_var= my_thread_var; + MDL_CONTEXT::Request_iterator it(context->requests); safe_mutex_assert_not_owner(&LOCK_open); @@ -815,46 +879,44 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) old_msg= MDL_ENTER_COND(context, mysys_var); - while ((lock_data= it++)) + while ((lock_req= it++)) { - DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_INITIALIZED); - if (!(lock= (MDL_LOCK *) my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length))) + MDL_KEY *key= &lock_req->key; + DBUG_ASSERT(lock_req->type == MDL_EXCLUSIVE && + lock_req->ticket == NULL); + + /* Early allocation: ticket is used as a shortcut to the lock. */ + if (!(ticket= alloc_ticket_object(context))) + goto err; + + if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + key->ptr(), key->length()))) { - if (!(lock= get_lock_object())) - goto err; - /* - Again before inserting MDL_LOCK into hash provide key for - it by adding MDL_LOCK_DATA to one of its lists. - */ - lock->waiting_exclusive.push_front(lock_data); - lock->lock_data_count= 1; - if (my_hash_insert(&mdl_locks, (uchar*)lock)) + lock= alloc_lock_object(key); + if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - release_lock_object(lock); + free_ticket_object(ticket); + free_lock_object(lock); goto err; } - lock_data->lock= lock; - lock_data->state= MDL_PENDING; - } - else - { - lock->waiting_exclusive.push_front(lock_data); - lock->lock_data_count++; - lock_data->lock= lock; - lock_data->state= MDL_PENDING; } + + lock_req->ticket= ticket; + ticket->state= MDL_PENDING; + ticket->ctx= context; + ticket->lock= lock; + ticket->type= lock_req->type; + lock->waiting.push_front(ticket); } while (1) { it.rewind(); - while ((lock_data= it++)) + while ((lock_req= it++)) { - lock= lock_data->lock; + lock= lock_req->ticket->lock; - if (!can_grant_global_lock(lock_data)) + if (!can_grant_global_lock(lock_req->type, FALSE)) { /* There is an active or pending global shared lock so we have @@ -863,27 +925,25 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) signalled= TRUE; break; } - else if (!can_grant_lock(lock, lock_data)) + else if (!can_grant_lock(context, lock, lock_req->type, FALSE)) { - MDL_LOCK_DATA *conf_lock_data; - I_P_List_iterator it(lock->active_shared); + MDL_LOCK_TICKET *conf_lock_ticket; + MDL_LOCK::Ticket_iterator it(lock->granted); - signalled= !lock->active_exclusive.is_empty() || - !lock->active_shared_waiting_upgrade.is_empty(); + signalled= (lock->type == MDL_LOCK::EXCLUSIVE); - while ((conf_lock_data= it++)) - { - signalled|= - mysql_notify_thread_having_shared_lock(context->thd, - conf_lock_data->ctx->thd); - } + while ((conf_lock_ticket= it++)) + signalled|= notify_shared_lock(context->thd, conf_lock_ticket); break; } } - if (!lock_data) + if (!lock_req) break; + + /* There is a shared or exclusive lock on the object. */ + DEBUG_SYNC(context->thd, "mdl_acquire_exclusive_locks_wait"); + if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); else @@ -902,13 +962,16 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) goto err; } it.rewind(); - while ((lock_data= it++)) + while ((lock_req= it++)) { global_lock.active_intention_exclusive++; - lock= lock_data->lock; - lock->waiting_exclusive.remove(lock_data); - lock->active_exclusive.push_front(lock_data); - lock_data->state= MDL_ACQUIRED; + ticket= lock_req->ticket; + lock= ticket->lock; + lock->type= MDL_LOCK::EXCLUSIVE; + lock->waiting.remove(ticket); + lock->granted.push_front(ticket); + context->tickets.push_front(ticket); + ticket->state= MDL_ACQUIRED; if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= NULL; @@ -923,10 +986,20 @@ err: Ignore those lock requests which were not made MDL_PENDING. */ it.rewind(); - while ((lock_data= it++) && lock_data->state == MDL_PENDING) + while ((lock_req= it++) && lock_req->ticket) { - release_lock(lock_data); - lock_data->state= MDL_INITIALIZED; + ticket= lock_req->ticket; + DBUG_ASSERT(ticket->state == MDL_PENDING); + lock= ticket->lock; + free_ticket_object(ticket); + lock->waiting.remove(ticket); + /* Reset lock request back to its initial state. */ + lock_req->ticket= NULL; + if (lock->is_empty()) + { + my_hash_delete(&mdl_locks, (uchar *)lock); + free_lock_object(lock); + } } /* May be some pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); @@ -942,77 +1015,61 @@ err: new definition has been constructed. @param context Context to which shared lock belongs - @param lock_data Satisfied request for shared lock to be upgraded + @param ticket Ticket for shared lock to be upgraded @note In case of failure to upgrade lock (e.g. because upgrader was killed) leaves lock in its original state (locked in shared mode). + @note There can be only one upgrader for a lock or we will have deadlock. + This invariant is ensured by code outside of metadata subsystem usually + by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, + TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. + @retval FALSE Success @retval TRUE Failure (thread was killed) */ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) + MDL_LOCK_TICKET *ticket) { - MDL_LOCK *lock; + MDL_LOCK *lock= ticket->lock; const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; DBUG_ENTER("mdl_upgrade_shared_lock_to_exclusive"); + DEBUG_SYNC(context->thd, "mdl_upgrade_shared_lock_to_exclusive"); safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - /* Allow this function to be called twice for the same lock request. */ - if (lock_data->type == MDL_EXCLUSIVE) + if (ticket->type == MDL_EXCLUSIVE) DBUG_RETURN(FALSE); - DBUG_ASSERT(lock_data->type == MDL_SHARED_UPGRADABLE); - - lock= lock_data->lock; - pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(context, mysys_var); - lock_data->state= MDL_PENDING_UPGRADE; - /* Set type of lock request to the type at which we are aiming. */ - lock_data->type= MDL_EXCLUSIVE; - lock->active_shared.remove(lock_data); - /* - There can be only one upgrader for this lock or we will have deadlock. - This invariant is ensured by code outside of metadata subsystem usually - by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, - TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. - */ - DBUG_ASSERT(lock->active_shared_waiting_upgrade.is_empty()); - lock->active_shared_waiting_upgrade.push_front(lock_data); /* - Since we should have been already acquired intention exclusive global lock - this call is only enforcing asserts. + Since we should have already acquired an intention exclusive + global lock this call is only enforcing asserts. */ - DBUG_ASSERT(can_grant_global_lock(lock_data)); + DBUG_ASSERT(can_grant_global_lock(MDL_EXCLUSIVE, TRUE)); while (1) { - if (can_grant_lock(lock, lock_data)) + if (can_grant_lock(context, lock, MDL_EXCLUSIVE, TRUE)) break; bool signalled= FALSE; - MDL_LOCK_DATA *conf_lock_data; - I_P_List_iterator it(lock->active_shared); + MDL_LOCK_TICKET *conf_lock_ticket; + MDL_LOCK::Ticket_iterator it(lock->granted); - while ((conf_lock_data= it++)) + while ((conf_lock_ticket= it++)) { - if (conf_lock_data->ctx != context) - { - signalled|= - mysql_notify_thread_having_shared_lock(context->thd, - conf_lock_data->ctx->thd); - } + if (conf_lock_ticket->ctx != context) + signalled|= notify_shared_lock(context->thd, conf_lock_ticket); } if (signalled) @@ -1032,10 +1089,6 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, } if (mysys_var->abort) { - lock_data->state= MDL_ACQUIRED; - lock_data->type= MDL_SHARED_UPGRADABLE; - lock->active_shared_waiting_upgrade.remove(lock_data); - lock->active_shared.push_front(lock_data); /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); MDL_EXIT_COND(context, mysys_var, old_msg); @@ -1043,9 +1096,9 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, } } - lock->active_shared_waiting_upgrade.remove(lock_data); - lock->active_exclusive.push_front(lock_data); - lock_data->state= MDL_ACQUIRED; + lock->type= MDL_LOCK::EXCLUSIVE; + /* Set the new type of lock in the ticket. */ + ticket->type= MDL_EXCLUSIVE; if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= 0; @@ -1069,7 +1122,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, block and wait for the lock if the table already exists. @param context [in] The context containing the lock request - @param lock [in] The lock request + @param lock_req [in] The lock request @param conflict [out] Indicates that conflicting lock exists @retval TRUE Failure either conflicting lock exists or some error @@ -1081,13 +1134,15 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, */ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data, + MDL_LOCK_REQUEST *lock_req, bool *conflict) { MDL_LOCK *lock; + MDL_LOCK_TICKET *ticket; + MDL_KEY *key= &lock_req->key; - DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_INITIALIZED); + DBUG_ASSERT(lock_req->type == MDL_EXCLUSIVE && + lock_req->ticket == NULL); safe_mutex_assert_not_owner(&LOCK_open); @@ -1095,20 +1150,25 @@ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, pthread_mutex_lock(&LOCK_mdl); - if (!(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length))) + if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + key->ptr(), key->length()))) { - if (!(lock= get_lock_object())) - goto err; - lock->active_exclusive.push_front(lock_data); - lock->lock_data_count= 1; - if (my_hash_insert(&mdl_locks, (uchar*)lock)) + ticket= alloc_ticket_object(context); + lock= alloc_lock_object(key); + if (!ticket || !lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - release_lock_object(lock); + free_ticket_object(ticket); + free_lock_object(lock); goto err; } - lock_data->state= MDL_ACQUIRED; - lock_data->lock= lock; + lock->type= MDL_LOCK::EXCLUSIVE; + lock->granted.push_front(ticket); + context->tickets.push_front(ticket); + ticket->state= MDL_ACQUIRED; + lock_req->ticket= ticket; + ticket->ctx= context; + ticket->lock= lock; + ticket->type= lock_req->type; global_lock.active_intention_exclusive++; pthread_mutex_unlock(&LOCK_mdl); return FALSE; @@ -1183,9 +1243,9 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) bool mdl_wait_for_locks(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; MDL_LOCK *lock; - I_P_List_iterator it(context->locks); + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it(context->requests); const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; @@ -1207,22 +1267,23 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(context, mysys_var); it.rewind(); - while ((lock_data= it++)) + while ((lock_req= it++)) { - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - if (!can_grant_global_lock(lock_data)) + MDL_KEY *key= &lock_req->key; + DBUG_ASSERT(lock_req->ticket == NULL); + if (!can_grant_global_lock(lock_req->type, FALSE)) break; /* To avoid starvation we don't wait if we have a conflict against request for MDL_EXCLUSIVE lock. */ - if (is_shared(lock_data) && - (lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length)) && - !can_grant_lock(lock, lock_data)) + if (is_shared(lock_req) && + (lock= (MDL_LOCK*) my_hash_search(&mdl_locks, key->ptr(), + key->length())) && + !can_grant_lock(context, lock, lock_req->type, FALSE)) break; } - if (!lock_data) + if (!lock_req) { pthread_mutex_unlock(&LOCK_mdl); break; @@ -1237,58 +1298,48 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) /** Auxiliary function which allows to release particular lock - ownership of which is represented by lock request object. + ownership of which is represented by a lock ticket object. */ -static void release_lock(MDL_LOCK_DATA *lock_data) +static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) { - MDL_LOCK *lock; + MDL_LOCK *lock= ticket->lock; + DBUG_ENTER("release_ticket"); + DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(), + lock->key.table_name())); - DBUG_ENTER("release_lock"); - DBUG_PRINT("enter", ("db=%s name=%s", lock_data->key + 4, - lock_data->key + 4 + strlen(lock_data->key + 4) + 1)); + safe_mutex_assert_owner(&LOCK_mdl); - DBUG_ASSERT(lock_data->state == MDL_PENDING || - lock_data->state == MDL_ACQUIRED); + context->tickets.remove(ticket); - lock= lock_data->lock; - if (lock->has_one_lock_data()) + switch (ticket->type) + { + case MDL_SHARED_UPGRADABLE: + global_lock.active_intention_exclusive--; + /* Fallthrough. */ + case MDL_SHARED: + case MDL_SHARED_HIGH_PRIO: + lock->granted.remove(ticket); + break; + case MDL_EXCLUSIVE: + lock->type= MDL_LOCK::SHARED; + lock->granted.remove(ticket); + global_lock.active_intention_exclusive--; + break; + default: + DBUG_ASSERT(0); + } + + free_ticket_object(ticket); + + if (lock->is_empty()) { my_hash_delete(&mdl_locks, (uchar *)lock); DBUG_PRINT("info", ("releasing cached_object cached_object=%p", lock->cached_object)); if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); - release_lock_object(lock); - if (lock_data->state == MDL_ACQUIRED && - (lock_data->type == MDL_EXCLUSIVE || - lock_data->type == MDL_SHARED_UPGRADABLE)) - global_lock.active_intention_exclusive--; - } - else - { - switch (lock_data->type) - { - case MDL_SHARED_UPGRADABLE: - global_lock.active_intention_exclusive--; - /* Fallthrough. */ - case MDL_SHARED: - case MDL_SHARED_HIGH_PRIO: - lock->active_shared.remove(lock_data); - break; - case MDL_EXCLUSIVE: - if (lock_data->state == MDL_PENDING) - lock->waiting_exclusive.remove(lock_data); - else - { - lock->active_exclusive.remove(lock_data); - global_lock.active_intention_exclusive--; - } - break; - default: - DBUG_ASSERT(0); - } - lock->lock_data_count--; + free_lock_object(lock); } DBUG_VOID_RETURN; @@ -1307,42 +1358,38 @@ static void release_lock(MDL_LOCK_DATA *lock_data) are associated. */ -void mdl_release_locks(MDL_CONTEXT *context) +void mdl_ticket_release_all(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); - DBUG_ENTER("mdl_release_locks"); + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(context->tickets); + DBUG_ENTER("mdl_ticket_release_all"); safe_mutex_assert_not_owner(&LOCK_open); - pthread_mutex_lock(&LOCK_mdl); - while ((lock_data= it++)) + /* Detach lock tickets from the requests for back off. */ { - DBUG_PRINT("info", ("found lock to release lock_data=%p", lock_data)); - /* - Don't call release_lock() for a shared lock if has not been - granted. Lock state in this case is MDL_INITIALIZED. - We have pending and granted shared locks in the same context - when this function is called from the "back-off" path of - open_tables(). - */ - if (lock_data->state != MDL_INITIALIZED) - { - release_lock(lock_data); - lock_data->state= MDL_INITIALIZED; -#ifndef DBUG_OFF - lock_data->lock= 0; -#endif - } - /* - We will return lock request to its initial state only in - mdl_remove_all_locks() since we need to know type of lock - request in mdl_wait_for_locks(). - */ + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it(context->requests); + + while ((lock_req= it++)) + lock_req->ticket= NULL; + } + + if (context->tickets.is_empty()) + DBUG_VOID_RETURN; + + pthread_mutex_lock(&LOCK_mdl); + while ((ticket= it++)) + { + DBUG_PRINT("info", ("found lock to release ticket=%p", ticket)); + release_ticket(context, ticket); } /* Inefficient but will do for a while */ pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); + + context->tickets.empty(); + DBUG_VOID_RETURN; } @@ -1351,20 +1398,17 @@ void mdl_release_locks(MDL_CONTEXT *context) Release a lock. @param context Context containing lock in question - @param lock_data Lock to be released + @param ticket Lock to be released */ -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) { + DBUG_ASSERT(context == ticket->ctx); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - release_lock(lock_data); -#ifndef DBUG_OFF - lock_data->lock= 0; -#endif - lock_data->state= MDL_INITIALIZED; + release_ticket(context, ticket); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1375,34 +1419,43 @@ void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) object as this lock request, remove lock requests from the context. @param context Context containing locks in question - @param lock_data One of the locks for the name/object for which all + @param ticket One of the locks for the name/object for which all locks should be released. */ -void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) +void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, + MDL_LOCK_TICKET *ticket) { MDL_LOCK *lock; - I_P_List_iterator it(context->locks); - - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); /* - We can use MDL_LOCK_DATA::lock here to identify other locks for the same - object since even altough MDL_LOCK object might be reused for different + We can use MDL_LOCK_TICKET::lock here to identify other locks for the same + object since even though MDL_LOCK object might be reused for different lock after the first lock for this object have been released we can't have references to this other MDL_LOCK object in this context. */ - lock= lock_data->lock; + lock= ticket->lock; - while ((lock_data= it++)) + /* Remove matching lock requests from the context. */ + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it_lock_req(context->requests); + + while ((lock_req= it_lock_req++)) { - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - if (lock_data->lock == lock) - { - mdl_release_lock(context, lock_data); - mdl_remove_lock(context, lock_data); - } + DBUG_ASSERT(lock_req->ticket && lock_req->ticket->state == MDL_ACQUIRED); + if (lock_req->ticket->lock == lock) + mdl_request_remove(context, lock_req); + } + + /* Remove matching lock tickets from the context. */ + MDL_LOCK_TICKET *lock_tkt; + MDL_CONTEXT::Ticket_iterator it_lock_tkt(context->tickets); + + while ((lock_tkt= it_lock_tkt++)) + { + DBUG_ASSERT(lock_tkt->state == MDL_ACQUIRED); + if (lock_tkt->lock == lock) + mdl_ticket_release(context, lock_tkt); } } @@ -1411,27 +1464,26 @@ void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, Downgrade an exclusive lock to shared metadata lock. @param context A context to which exclusive lock belongs - @param lock_data Satisfied request for exclusive lock to be downgraded + @param ticket Ticket for exclusive lock to be downgraded */ void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) + MDL_LOCK_TICKET *ticket) { MDL_LOCK *lock; + DBUG_ASSERT(context == ticket->ctx); + safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - - if (is_shared(lock_data)) + if (is_shared(ticket)) return; - lock= lock_data->lock; + lock= ticket->lock; pthread_mutex_lock(&LOCK_mdl); - lock->active_exclusive.remove(lock_data); - lock_data->type= MDL_SHARED_UPGRADABLE; - lock->active_shared.push_front(lock_data); + lock->type= MDL_LOCK::SHARED; + ticket->type= MDL_SHARED_UPGRADABLE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1469,30 +1521,29 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) FALSE otherwise. */ -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, const char *db, const char *name) { - char key[MAX_MDLKEY_LENGTH]; - uint key_length; - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); + MDL_KEY key; + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(context->tickets); - int4store(key, type); - key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + key.mdl_key_init(type, db, name); - while ((lock_data= it++) && - (lock_data->key_length != key_length || - memcmp(lock_data->key, key, key_length) || - !(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_ACQUIRED))) - continue; - return lock_data; + while ((ticket= it++)) + { + if (ticket->lock->type == MDL_LOCK::EXCLUSIVE && + ticket->lock->key.is_equal(&key)) + break; + } + + return ticket; } /** - Auxiliary function which allows to check if we some kind of lock on - the object. + Auxiliary function which allows to check if we have some kind of lock on + a object. @param context Current context @param type Id of object type @@ -1503,24 +1554,22 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, FALSE otherwise. */ -bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name) +bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name) { - char key[MAX_MDLKEY_LENGTH]; - uint key_length; - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); + MDL_KEY key; + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(context->tickets); - int4store(key, type); - key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + key.mdl_key_init(type, db, name); - while ((lock_data= it++) && - (lock_data->key_length != key_length || - memcmp(lock_data->key, key, key_length) || - lock_data->state != MDL_ACQUIRED)) - continue; + while ((ticket= it++)) + { + if (ticket->lock->key.is_equal(&key)) + break; + } - return lock_data; + return ticket; } @@ -1528,21 +1577,20 @@ bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, Check if we have any pending exclusive locks which conflict with existing shared lock. - @param lock_data Shared lock against which check should be performed. + @param ticket Shared lock against which check should be performed. @return TRUE if there are any conflicting locks, FALSE otherwise. */ -bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data) +bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) { bool result; - DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_ACQUIRED); + DBUG_ASSERT(is_shared(ticket)); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - result= !(lock_data->lock->waiting_exclusive.is_empty() && - lock_data->lock->active_shared_waiting_upgrade.is_empty()); + result= !ticket->lock->waiting.is_empty(); pthread_mutex_unlock(&LOCK_mdl); return result; } @@ -1551,8 +1599,8 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data) /** Associate pointer to an opaque object with a lock. - @param lock_data Lock request for the lock with which the - object should be associated. + @param ticket Lock ticket for the lock with which the object + should be associated. @param cached_object Pointer to the object @param release_hook Cleanup function to be called when MDL subsystem decides to remove lock or associate another object. @@ -1576,27 +1624,24 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data) lock on this name is released. */ -void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, +void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, mdl_cached_object_release_hook release_hook) { + MDL_LOCK *lock= ticket->lock; DBUG_ENTER("mdl_set_cached_object"); - DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", lock_data->key + 4, - lock_data->key + 4 + strlen(lock_data->key + 4) + 1, - cached_object)); - - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED || - lock_data->state == MDL_PENDING_UPGRADE); - + DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", + lock->key.db_name(), lock->key.table_name(), + cached_object)); /* TODO: This assumption works now since we do mdl_get_cached_object() and mdl_set_cached_object() in the same critical section. Once this becomes false we will have to call release_hook here and use additional mutex protecting 'cached_object' member. */ - DBUG_ASSERT(!lock_data->lock->cached_object); + DBUG_ASSERT(!lock->cached_object); - lock_data->lock->cached_object= cached_object; - lock_data->lock->cached_object_release_hook= release_hook; + lock->cached_object= cached_object; + lock->cached_object_release_hook= release_hook; DBUG_VOID_RETURN; } @@ -1605,15 +1650,46 @@ void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, /** Get a pointer to an opaque object that associated with the lock. - @param lock_data Lock request for the lock with which the object is - associated. + @param ticket Lock ticket for the lock which the object is associated to. @return Pointer to an opaque object associated with the lock. */ -void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data) +void* mdl_get_cached_object(MDL_LOCK_TICKET *ticket) { - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED || - lock_data->state == MDL_PENDING_UPGRADE); - return lock_data->lock->cached_object; + return ticket->lock->cached_object; } + + + +/** + Releases metadata locks that were acquired after a specific savepoint. + + @note Used to release tickets acquired during a savepoint unit. + @note It's safe to iterate and unlock any locks after taken after this + savepoint because other statements that take other special locks + cause a implicit commit (ie LOCK TABLES). + + @param thd Current thread + @param sv Savepoint +*/ + +void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, + MDL_LOCK_TICKET *mdl_savepoint) +{ + MDL_LOCK_TICKET *mdl_lock_ticket; + MDL_CONTEXT::Ticket_iterator it(ctx->tickets); + DBUG_ENTER("mdl_rollback_to_savepoint"); + + while ((mdl_lock_ticket= it++)) + { + /* Stop when lock was acquired before this savepoint. */ + if (mdl_lock_ticket == mdl_savepoint) + break; + mdl_ticket_release(ctx, mdl_lock_ticket); + } + + DBUG_VOID_RETURN; +} + + diff --git a/sql/mdl.h b/sql/mdl.h index b4b84a9ab24..103ab8130ba 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -23,7 +23,8 @@ class THD; -struct MDL_LOCK_DATA; +struct MDL_LOCK_REQUEST; +struct MDL_LOCK_TICKET; struct MDL_LOCK; struct MDL_CONTEXT; @@ -43,87 +44,147 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, MDL_SHARED_UPGRADABLE, MDL_EXCLUSIVE}; -/** States which metadata lock request can have. */ +/** States which a metadata lock ticket can have. */ -enum enum_mdl_state {MDL_INITIALIZED=0, MDL_PENDING, - MDL_ACQUIRED, MDL_PENDING_UPGRADE}; +enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; + + +/** Maximal length of key for metadata locking subsystem. */ +#define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) /** - A pending lock request or a granted metadata lock. A lock is requested - or granted based on a fully qualified name and type. E.g. for a table - the key consists of <0 (=table)>++
. - Later in this document this triple will be referred to simply as - "key" or "name". + Metadata lock object key. + + A lock is requested or granted based on a fully qualified name and type. + E.g. They key for a table consists of <0 (=table)>++
. + Elsewhere in the comments this triple will be referred to simply as "key" + or "name". */ -struct MDL_LOCK_DATA +class MDL_KEY { - char *key; - uint key_length; - enum enum_mdl_type type; - enum enum_mdl_state state; +public: + const uchar *ptr() const { return (uchar*) m_ptr; } + uint length() const { return m_length; } + const char *db_name() const { return m_ptr + 1; } + uint db_name_length() const { return m_db_name_length; } + + const char *table_name() const { return m_ptr + m_db_name_length + 2; } + uint table_name_length() const { return m_length - m_db_name_length - 3; } + + /** + Construct a metadata lock key from a triplet (type, database and name). + + @remark The key for a table is <0 (=table)>++
+ + @param type Id of type of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object + @param key Where to store the the MDL key. + */ + void mdl_key_init(char type, const char *db, const char *name) + { + m_ptr[0]= type; + m_db_name_length= (uint) (strmov(m_ptr + 1, db) - m_ptr - 1); + m_length= (uint) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); + } + void mdl_key_init(const MDL_KEY *rhs) + { + memcpy(m_ptr, rhs->m_ptr, rhs->m_length); + m_length= rhs->m_length; + m_db_name_length= rhs->m_db_name_length; + } + bool is_equal(const MDL_KEY *rhs) const + { + return (m_length == rhs->m_length && + memcmp(m_ptr, rhs->m_ptr, m_length) == 0); + } private: + char m_ptr[MAX_MDLKEY_LENGTH]; + uint m_length; + uint m_db_name_length; +}; + + + +/** + Hook class which via its methods specifies which members + of T should be used for participating in MDL lists. +*/ + +template +struct I_P_List_adapter +{ + static inline T **next_ptr(T *el) { return &(el->*next); } + + static inline T ***prev_ptr(T *el) { return &(el->*prev); } +}; + + +/** + A pending metadata lock request. + A pending lock request or a granted metadata lock share the same abstract + base but are presented individually because they have different allocation + sites and hence different lifetimes. The allocation of lock requests is + controlled from outside of the MDL subsystem, while allocation of granted + locks (tickets) is controlled within the MDL subsystem. +*/ + +struct MDL_LOCK_REQUEST +{ + /** Type of metadata lock. */ + enum enum_mdl_type type; + /** Pointers for participating in the list of lock requests for this context. */ - MDL_LOCK_DATA *next_context; - MDL_LOCK_DATA **prev_context; + MDL_LOCK_REQUEST *next_in_context; + MDL_LOCK_REQUEST **prev_in_context; + /** A lock is requested based on a fully qualified name and type. */ + MDL_KEY key; + + /** + Pointer to the lock ticket object for this lock request. + Valid only if this lock request is satisfied. + */ + MDL_LOCK_TICKET *ticket; +}; + + +/** + A granted metadata lock. + + @warning MDL_LOCK_TICKET members are private to the MDL subsystem. + + @note Multiple shared locks on a same object are represented by a + single ticket. The same does not apply for other lock types. +*/ + +struct MDL_LOCK_TICKET +{ + /** Type of metadata lock. */ + enum enum_mdl_type type; + /** State of the metadata lock ticket. */ + enum enum_mdl_state state; + + /** + Pointers for participating in the list of lock requests for this context. + */ + MDL_LOCK_TICKET *next_in_context; + MDL_LOCK_TICKET **prev_in_context; /** Pointers for participating in the list of satisfied/pending requests for the lock. */ - MDL_LOCK_DATA *next_lock; - MDL_LOCK_DATA **prev_lock; + MDL_LOCK_TICKET *next_in_lock; + MDL_LOCK_TICKET **prev_in_lock; + /** Context of the owner of the metadata lock ticket. */ + MDL_CONTEXT *ctx; - friend struct MDL_LOCK_DATA_context; - friend struct MDL_LOCK_DATA_lock; - -public: - /* - Pointer to the lock object for this lock request. Valid only if this lock - request is satisified or is present in the list of pending lock requests - for particular lock. - */ - MDL_LOCK *lock; - MDL_CONTEXT *ctx; -}; - - -/** - Helper class which specifies which members of MDL_LOCK_DATA are used for - participation in the list lock requests belonging to one context. -*/ - -struct MDL_LOCK_DATA_context -{ - static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) - { - return &l->next_context; - } - static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) - { - return &l->prev_context; - } -}; - - -/** - Helper class which specifies which members of MDL_LOCK_DATA are used for - participation in the list of satisfied/pending requests for the lock. -*/ - -struct MDL_LOCK_DATA_lock -{ - static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) - { - return &l->next_lock; - } - static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) - { - return &l->prev_lock; - } + /** Pointer to the lock object for this lock ticket. */ + MDL_LOCK *lock; }; @@ -134,7 +195,24 @@ struct MDL_LOCK_DATA_lock struct MDL_CONTEXT { - I_P_List locks; + typedef I_P_List > + Request_list; + + typedef Request_list::Iterator Request_iterator; + + typedef I_P_List > + Ticket_list; + + typedef Ticket_list::Iterator Ticket_iterator; + + Request_list requests; + Ticket_list tickets; bool has_global_shared_lock; THD *thd; }; @@ -149,96 +227,80 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); -/** Maximal length of key for metadata locking subsystem. */ -#define MAX_MDLKEY_LENGTH (4 + NAME_LEN + 1 + NAME_LEN + 1) - -void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, - const char *db, const char *name); -MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root); -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_remove_all_locks(MDL_CONTEXT *context); +void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, + const char *db, const char *name); +MDL_LOCK_REQUEST *mdl_request_alloc(unsigned char type, const char *db, + const char *name, MEM_ROOT *root); +void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); +void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); +void mdl_request_remove_all(MDL_CONTEXT *context); /** Set type of lock request. Can be only applied to pending locks. */ -inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) +inline void mdl_request_set_type(MDL_LOCK_REQUEST *lock_req, enum_mdl_type lock_type) { - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - lock_data->type= lock_type; + DBUG_ASSERT(lock_req->ticket == NULL); + lock_req->type= lock_type; } -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_TICKET *ticket); bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data, + MDL_LOCK_REQUEST *lock_req, bool *conflict); bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); bool mdl_wait_for_locks(MDL_CONTEXT *context); -void mdl_release_locks(MDL_CONTEXT *context); -void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); +void mdl_ticket_release_all(MDL_CONTEXT *context); +void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, + MDL_LOCK_TICKET *ticket); +void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_TICKET *ticket); void mdl_release_global_shared_lock(MDL_CONTEXT *context); -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name); -bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name); +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name); +bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name); -bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data); +bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket); inline bool mdl_has_locks(MDL_CONTEXT *context) { - return !context->locks.is_empty(); + return !context->tickets.is_empty(); } +inline MDL_LOCK_TICKET *mdl_savepoint(MDL_CONTEXT *ctx) +{ + return ctx->tickets.head(); +} + +void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, + MDL_LOCK_TICKET *mdl_savepoint); /** Get iterator for walking through all lock requests in the context. */ -inline I_P_List_iterator -mdl_get_locks(MDL_CONTEXT *ctx) +inline MDL_CONTEXT::Request_iterator +mdl_get_requests(MDL_CONTEXT *ctx) { - I_P_List_iterator result(ctx->locks); + MDL_CONTEXT::Request_iterator result(ctx->requests); return result; } -/** - Give metadata lock request object for the table get table definition - cache key corresponding to it. - - @param lock_data [in] Lock request object for the table. - @param key [out] LEX_STRING object where table definition cache key - should be put. - - @note This key will have the same life-time as this lock request object. - - @note This is yet another place where border between MDL subsystem and the - rest of the server is broken. OTOH it allows to save some CPU cycles - and memory by avoiding generating these TDC keys from table list. -*/ - -inline void mdl_get_tdc_key(MDL_LOCK_DATA *lock_data, LEX_STRING *key) -{ - key->str= lock_data->key + 4; - key->length= lock_data->key_length - 4; -} - +void mdl_get_tdc_key(MDL_LOCK_TICKET *ticket, LEX_STRING *key); typedef void (* mdl_cached_object_release_hook)(void *); -void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data); -void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, +void *mdl_get_cached_object(MDL_LOCK_TICKET *ticket); +void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, mdl_cached_object_release_hook release_hook); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 9f672653266..5732d7b7079 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3981,10 +3981,10 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : - thd->mem_root); + table->mdl_lock_request= mdl_request_alloc(0, table->db, table->table_name, + thd->locked_tables_root ? + thd->locked_tables_root : + thd->mem_root); /* Everyting else should be zeroed */ @@ -4026,9 +4026,10 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + table->mdl_lock_request= mdl_request_alloc(0, table->db, table->table_name, + thd->locked_tables_root ? + thd->locked_tables_root : + thd->mem_root); lex->add_to_query_tables(table); return table; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 817a5e982ee..aa96483cf09 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -693,7 +693,7 @@ my_bool acl_reload(THD *thd) tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= tables[2].skip_temporary= TRUE; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); if (simple_open_n_lock_tables(thd, tables)) { @@ -1598,7 +1598,7 @@ bool change_password(THD *thd, const char *host, const char *user, bzero((char*) &tables, sizeof(tables)); tables.alias= tables.table_name= (char*) "user"; tables.db= (char*) "mysql"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); #ifdef HAVE_REPLICATION /* @@ -3110,7 +3110,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, ? tables+2 : 0); tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3328,7 +3328,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3467,7 +3467,7 @@ bool mysql_grant(THD *thd, const char *db, List &list, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3801,7 +3801,7 @@ static my_bool grant_reload_procs_priv(THD *thd) table.db= (char *) "mysql"; table.lock_type= TL_READ; table.skip_temporary= 1; - alloc_mdl_locks(&table, thd->mem_root); + alloc_mdl_requests(&table, thd->mem_root); if (simple_open_n_lock_tables(thd, &table)) { @@ -3868,7 +3868,7 @@ my_bool grant_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type= tables[1].lock_type= TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= TRUE; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* To avoid deadlocks we should obtain table locks before @@ -5215,7 +5215,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) (tables+4)->lock_type= TL_WRITE; tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db= (tables+4)->db= (char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); #ifdef HAVE_REPLICATION /* diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0bd1acaa08e..f4f07ad08f7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1055,7 +1055,7 @@ err_with_reopen: than picking only those tables that were flushed. */ for (TABLE *tab= thd->open_tables; tab; tab= tab->next) - mdl_downgrade_exclusive_lock(&thd->mdl_context, tab->mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, tab->mdl_lock_ticket); } DBUG_RETURN(result); } @@ -1478,10 +1478,10 @@ void close_thread_tables(THD *thd, if (thd->open_tables) close_open_tables(thd); - mdl_release_locks(&thd->mdl_context); + mdl_ticket_release_all(&thd->mdl_context); if (!skip_mdl) { - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); } DBUG_VOID_RETURN; } @@ -1500,7 +1500,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) *table_ptr=table->next; - table->mdl_lock_data= 0; + table->mdl_lock_ticket= NULL; if (table->needs_reopen() || thd->version != refresh_version || !table->db_stat) { @@ -2096,7 +2096,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, - table->mdl_lock_data)) + table->mdl_lock_ticket)) { mysql_lock_downgrade_write(thd, table, old_lock_type); DBUG_RETURN(TRUE); @@ -2279,11 +2279,11 @@ void table_share_release_hook(void *share) static bool open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, - MDL_LOCK_DATA *mdl_lock_data, + MDL_LOCK_REQUEST *mdl_lock_request, uint flags, enum_open_table_action *action) { - mdl_add_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (table_list->open_type) { @@ -2296,10 +2296,10 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, shared locks. This invariant is preserved here and is also enforced by asserts in metadata locking subsystem. */ - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) { - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return 1; } } @@ -2316,16 +2316,16 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && table_list->lock_type >= TL_WRITE_ALLOW_WRITE) - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_UPGRADABLE); + mdl_request_set_type(mdl_lock_request, MDL_SHARED_UPGRADABLE); if (flags & MYSQL_LOCK_IGNORE_FLUSH) - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + mdl_request_set_type(mdl_lock_request, MDL_SHARED_HIGH_PRIO); - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_request, &retry)) { if (retry) *action= OT_BACK_OFF_AND_RETRY; else - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return 1; } } @@ -2380,7 +2380,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, char key[MAX_DBKEY_LENGTH]; uint key_length; char *alias= table_list->alias; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_request; + MDL_LOCK_TICKET *mdl_lock_ticket; int error; TABLE_SHARE *share; DBUG_ENTER("open_table"); @@ -2559,14 +2560,21 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, This is the normal use case. */ - mdl_lock_data= table_list->mdl_lock_data; + mdl_lock_request= table_list->mdl_lock_request; if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - if (open_table_get_mdl_lock(thd, table_list, mdl_lock_data, flags, + if (open_table_get_mdl_lock(thd, table_list, mdl_lock_request, flags, action)) DBUG_RETURN(TRUE); } + /* + Grab reference to the granted MDL lock ticket. Must be done after + open_table_get_mdl_lock as the lock on the table might have been + acquired previously (MYSQL_OPEN_HAS_MDL_LOCK). + */ + mdl_lock_ticket= mdl_lock_request->ticket; + pthread_mutex_lock(&LOCK_open); /* @@ -2608,7 +2616,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(FALSE); } - if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_data))) + if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_ticket))) { if (!(share= get_table_share_with_create(thd, table_list, key, key_length, OPEN_VIEW, @@ -2679,7 +2687,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, so we need to increase reference counter; */ reference_table_share(share); - mdl_set_cached_object(mdl_lock_data, share, table_share_release_hook); + mdl_set_cached_object(mdl_lock_ticket, share, table_share_release_hook); } else { @@ -2788,9 +2796,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, lock on this table to shared metadata lock. */ if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) - mdl_downgrade_exclusive_lock(&thd->mdl_context, table_list->mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); - table->mdl_lock_data= mdl_lock_data; + table->mdl_lock_ticket= mdl_lock_ticket; table->next=thd->open_tables; /* Link into simple list */ thd->open_tables=table; @@ -2842,8 +2850,8 @@ err_unlock2: pthread_mutex_unlock(&LOCK_open); if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - mdl_release_lock(&thd->mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } DBUG_RETURN(TRUE); } @@ -2961,7 +2969,7 @@ Locked_tables_list::init_locked_tables(THD *thd) dst_table_list->init_one_table(db, db_len, table_name, table_name_len, alias, src_table_list->table->reginfo.lock_type); - dst_table_list->mdl_lock_data= src_table_list->mdl_lock_data; + dst_table_list->mdl_lock_request= src_table_list->mdl_lock_request; dst_table_list->table= table; memcpy(db, src_table_list->db, db_len + 1); memcpy(table_name, src_table_list->table_name, table_name_len + 1); @@ -3012,6 +3020,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd) thd->locked_tables_mode= LTM_NONE; close_thread_tables(thd); + + mdl_ticket_release_all(&thd->mdl_context); } /* After closing tables we can free memory used for storing lock @@ -3496,20 +3506,21 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, enum_open_table_action action) { bool result= FALSE; + MDL_LOCK_REQUEST *mdl_lock_request= table->mdl_lock_request; switch (action) { case OT_BACK_OFF_AND_RETRY: result= (mdl_wait_for_locks(&thd->mdl_context) || tdc_wait_for_old_versions(thd, &thd->mdl_context)); - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); break; case OT_DISCOVER: - mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) { - mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return TRUE; } pthread_mutex_lock(&LOCK_open); @@ -3519,15 +3530,15 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, thd->warning_info->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message - mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); break; case OT_REPAIR: - mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) { - mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return TRUE; } pthread_mutex_lock(&LOCK_open); @@ -3535,8 +3546,8 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); - mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); break; default: DBUG_ASSERT(0); @@ -7730,10 +7741,9 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; TABLE_SHARE *share; const char *old_msg; - LEX_STRING key; + MDL_LOCK_REQUEST *lock_req; while (!thd->killed) { @@ -7746,18 +7756,16 @@ static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) mysql_ha_flush(thd); pthread_mutex_lock(&LOCK_open); - I_P_List_iterator it= mdl_get_locks(context); - while ((lock_data= it++)) + MDL_CONTEXT::Request_iterator it= mdl_get_requests(context); + while ((lock_req= it++)) { - mdl_get_tdc_key(lock_data, &key); - if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key.str, - key.length)) && + if ((share= get_cached_table_share(lock_req->key.db_name(), + lock_req->key.table_name())) && share->version != refresh_version && !share->used_tables.is_empty()) break; } - if (!lock_data) + if (!lock_req) { pthread_mutex_unlock(&LOCK_open); break; @@ -7971,7 +7979,7 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, DBUG_ENTER("open_system_tables_for_read"); - alloc_mdl_locks(table_list, thd->mem_root); + alloc_mdl_requests(table_list, thd->mem_root); /* Besides using new Open_tables_state for opening system tables, @@ -8046,7 +8054,7 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table) { DBUG_ENTER("open_system_table_for_update"); - alloc_mdl_locks(one_table, thd->mem_root); + alloc_mdl_requests(one_table, thd->mem_root); TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0); if (table) @@ -8084,7 +8092,7 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table, thd->reset_n_backup_open_tables_state(backup); - alloc_mdl_locks(one_table, thd->mem_root); + alloc_mdl_requests(one_table, thd->mem_root); if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) { DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE); @@ -8161,8 +8169,8 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) pthread_mutex_unlock(&LOCK_open); - mdl_release_locks(&thd->mdl_context); - mdl_remove_all_locks(&thd->mdl_context); + mdl_ticket_release_all(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); thd->restore_backup_open_tables_state(backup); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 34ba1386a89..80817fa4c12 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -779,6 +779,8 @@ struct st_savepoint { char *name; uint length; Ha_trx_info *ha_list; + /** Last acquired lock before this savepoint was set. */ + MDL_LOCK_TICKET *mdl_savepoint; }; enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 13c331af95c..c96594a8032 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_LOCK_DATA *mdl_lock_data= 0; + MDL_LOCK_REQUEST *mdl_lock_request= NULL; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1175,13 +1175,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock_data); + mdl_lock_request= mdl_request_alloc(0, table_list->db, + table_list->table_name, thd->mem_root); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) { - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); DBUG_RETURN(TRUE); } pthread_mutex_lock(&LOCK_open); @@ -1212,18 +1212,18 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_lock_data) + if (mdl_lock_request) { - mdl_release_lock(&thd->mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } } else if (error) { - if (mdl_lock_data) + if (mdl_lock_request) { - mdl_release_lock(&thd->mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } } DBUG_RETURN(error); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 49d6cbaa447..83c5c60dc01 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -125,7 +125,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) { TABLE **table_ptr; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_TICKET *mdl_lock_ticket; /* Though we could take the table pointer from hash_tables->table, @@ -141,7 +141,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) if (*table_ptr) { (*table_ptr)->file->ha_index_or_rnd_end(); - mdl_lock_data= (*table_ptr)->mdl_lock_data; + mdl_lock_ticket= (*table_ptr)->mdl_lock_ticket; pthread_mutex_lock(&LOCK_open); if (close_thread_table(thd, table_ptr)) { @@ -149,8 +149,8 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) broadcast_refresh(); } pthread_mutex_unlock(&LOCK_open); - mdl_release_lock(&thd->handler_mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->handler_mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->handler_mdl_context, mdl_lock_ticket); + mdl_request_remove(&thd->handler_mdl_context, tables->mdl_lock_request); } else if (tables->table) { @@ -190,12 +190,12 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { TABLE_LIST *hash_tables = NULL; - MDL_LOCK_DATA *mdl_lock_data; - char *db, *name, *alias, *mdlkey; + char *db, *name, *alias; uint dblen, namelen, aliaslen, counter; int error; TABLE *backup_open_tables; MDL_CONTEXT backup_mdl_context; + MDL_LOCK_REQUEST *mdl_lock_request; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -246,8 +246,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) &db, (uint) dblen, &name, (uint) namelen, &alias, (uint) aliaslen, - &mdl_lock_data, sizeof(MDL_LOCK_DATA), - &mdlkey, MAX_MDLKEY_LENGTH, + &mdl_lock_request, sizeof(MDL_LOCK_REQUEST), NullS))) { DBUG_PRINT("exit",("ERROR")); @@ -261,8 +260,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - mdl_init_lock(mdl_lock_data, mdlkey, 0, db, name); - hash_tables->mdl_lock_data= mdl_lock_data; + mdl_request_init(mdl_lock_request, 0, db, name); + hash_tables->mdl_lock_request= mdl_lock_request; /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) @@ -801,11 +800,11 @@ void mysql_ha_flush(THD *thd) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); /* - TABLE::mdl_lock_data is 0 for temporary tables so we need extra check. + TABLE::mdl_lock_ticket is 0 for temporary tables so we need extra check. */ if (hash_tables->table && - (hash_tables->table->mdl_lock_data && - mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock_data) || + (hash_tables->table->mdl_lock_ticket && + mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock_ticket) || hash_tables->table->needs_reopen())) mysql_ha_close_table(thd, hash_tables); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 16e9c1ded38..8553db8cf2b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2399,7 +2399,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->lex->set_stmt_unsafe(); thd->set_current_stmt_binlog_row_based_if_mixed(); - alloc_mdl_locks(&di->table_list, thd->mem_root); + alloc_mdl_requests(&di->table_list, thd->mem_root); if (di->open_and_lock_table()) goto err; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 83e45904816..4166267c5a9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1115,7 +1115,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, select_lex.table_list.link_in_list((uchar*) &table_list, (uchar**) &table_list.next_local); thd->lex->add_to_query_tables(&table_list); - alloc_mdl_locks(&table_list, thd->mem_root); + alloc_mdl_requests(&table_list, thd->mem_root); /* switch on VIEW optimisation: do not fill temporary tables */ thd->lex->sql_command= SQLCOM_SHOW_FIELDS; @@ -3324,7 +3324,7 @@ end_with_restore_list: !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) goto error; - alloc_mdl_locks(all_tables, thd->locked_tables_list.locked_tables_root()); + alloc_mdl_requests(all_tables, thd->locked_tables_list.locked_tables_root()); thd->options|= OPTION_TABLE_LOCK; thd->in_lock_tables=1; @@ -5977,9 +5977,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_lock_data= mdl_alloc_lock(0 , ptr->db, ptr->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + ptr->mdl_lock_request= + mdl_request_alloc(0, ptr->db, ptr->table_name, thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); DBUG_RETURN(ptr); } diff --git a/sql/sql_plist.h b/sql/sql_plist.h index b05a6318f0f..91fa9ef52bb 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -90,6 +90,7 @@ public: #ifndef _lint friend class I_P_List_iterator; #endif + typedef I_P_List_iterator Iterator; }; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index e9f4152f861..77b5552d977 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1366,7 +1366,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) tables.alias= tables.table_name= (char*)"plugin"; tables.lock_type= TL_READ; tables.db= new_thd->db; - alloc_mdl_locks(&tables, tmp_root); + alloc_mdl_requests(&tables, tmp_root); #ifdef EMBEDDED_LIBRARY /* @@ -1660,7 +1660,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) @@ -1735,7 +1735,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 6d25b0c2b59..40cdfeed946 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -234,7 +234,7 @@ bool servers_reload(THD *thd) tables[0].alias= tables[0].table_name= (char*) "servers"; tables[0].db= (char*) "mysql"; tables[0].lock_type= TL_READ; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); if (simple_open_n_lock_tables(thd, tables)) { @@ -365,7 +365,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server) bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; tables.alias= tables.table_name= (char*) "servers"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) @@ -584,7 +584,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; tables.alias= tables.table_name= (char*) "servers"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); rw_wrlock(&THR_LOCK_servers); @@ -709,7 +709,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) bzero((char*) &tables, sizeof(tables)); tables.db= (char*)"mysql"; tables.alias= tables.table_name= (char*)"servers"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d9da11b79fd..440efbf7c6d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3050,12 +3050,8 @@ uint get_table_open_method(TABLE_LIST *tables, Acquire high priority share metadata lock on a table. @param thd Thread context. - @param mdl_lock_data Pointer to memory to be used for MDL_LOCK_DATA + @param mdl_lock_req Pointer to memory to be used for MDL_LOCK_REQUEST object for a lock request. - @param mdlkey Pointer to the buffer for key for the lock request - (should be at least strlen(db) + strlen(name) + 2 - bytes, or, if the lengths are not known, - MAX_MDLKEY_LENGTH) @param table Table list element for the table @note This is an auxiliary function to be used in cases when we want to @@ -3069,23 +3065,23 @@ uint get_table_open_method(TABLE_LIST *tables, */ static bool -acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_DATA *mdl_lock_data, - char *mdlkey, TABLE_LIST *table) +acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_REQUEST *mdl_lock_req, + TABLE_LIST *table) { bool retry; - mdl_init_lock(mdl_lock_data, mdlkey, 0, table->db, table->table_name); - table->mdl_lock_data= mdl_lock_data; - mdl_add_lock(&thd->mdl_context, mdl_lock_data); - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + mdl_request_init(mdl_lock_req, 0, table->db, table->table_name); + table->mdl_lock_request= mdl_lock_req; + mdl_request_add(&thd->mdl_context, mdl_lock_req); + mdl_request_set_type(mdl_lock_req, MDL_SHARED_HIGH_PRIO); while (1) { - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_req, &retry)) { if (!retry || mdl_wait_for_locks(&thd->mdl_context)) { - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); return TRUE; } continue; @@ -3127,8 +3123,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, char key[MAX_DBKEY_LENGTH]; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; - MDL_LOCK_DATA mdl_lock_data; - char mdlkey[MAX_MDLKEY_LENGTH]; + MDL_LOCK_REQUEST mdl_lock_request; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); @@ -3158,8 +3153,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, simply obtaining internal lock of data-dictionary (ATM it is LOCK_open) instead of obtaning full-blown metadata lock. */ - if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_data, mdlkey, - &table_list)) + if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_request, &table_list)) { /* Some error occured (most probably we have been killed while @@ -3220,8 +3214,8 @@ err_unlock: pthread_mutex_unlock(&LOCK_open); err: - mdl_release_lock(&thd->mdl_context, &mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, &mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request.ticket); + mdl_request_remove(&thd->mdl_context, &mdl_lock_request); thd->clear_error(); return res; } @@ -7317,7 +7311,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ - alloc_mdl_locks(lst, thd->mem_root); + alloc_mdl_requests(lst, thd->mem_root); if (open_tables(thd, &lst, &num_tables, 0)) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d6a592c4799..54f06935d3b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1910,7 +1910,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, Since we don't acquire metadata lock if we have found temporary table, we should do something to avoid releasing it at the end. */ - table->mdl_lock_data= 0; + table->mdl_lock_request= NULL; } else { @@ -1923,7 +1923,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->table_name); if (!table->table) DBUG_RETURN(1); - table->mdl_lock_data= table->table->mdl_lock_data; + table->mdl_lock_request->ticket= table->table->mdl_lock_ticket; } } } @@ -2202,15 +2202,15 @@ err: } for (table= tables; table; table= table->next_local) { - if (table->mdl_lock_data) + if (table->mdl_lock_request) { /* Under LOCK TABLES we may have several instances of table open and locked and therefore have to remove several metadata lock requests associated with them. */ - mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, - table->mdl_lock_data); + mdl_ticket_release_all_for_name(&thd->mdl_context, + table->mdl_lock_request->ticket); } } } @@ -4108,29 +4108,28 @@ warn: static bool lock_table_name_if_not_cached(THD *thd, const char *db, const char *table_name, - MDL_LOCK_DATA **lock_data) + MDL_LOCK_REQUEST **lock_req) { bool conflict; - if (!(*lock_data= mdl_alloc_lock(0, db, table_name, thd->mem_root))) + if (!(*lock_req= mdl_request_alloc(0, db, table_name, thd->mem_root))) return TRUE; - mdl_set_lock_type(*lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, *lock_data); - if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_data, - &conflict)) + mdl_request_set_type(*lock_req, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, *lock_req); + if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_req, &conflict)) { /* To simplify our life under LOCK TABLES we remove unsatisfied lock request from the context. */ - mdl_remove_lock(&thd->mdl_context, *lock_data); + mdl_request_remove(&thd->mdl_context, *lock_req); if (!conflict) { /* Probably OOM. */ return TRUE; } else - *lock_data= 0; + *lock_req= NULL; } return FALSE; } @@ -4146,7 +4145,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, bool internal_tmp_table, uint select_field_count) { - MDL_LOCK_DATA *target_lock_data= 0; + MDL_LOCK_REQUEST *target_lock_req= NULL; bool result; DBUG_ENTER("mysql_create_table"); @@ -4169,12 +4168,12 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_req)) { result= TRUE; goto unlock; } - if (!target_lock_data) + if (!target_lock_req) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) { @@ -4200,10 +4199,10 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, select_field_count); unlock: - if (target_lock_data) + if (target_lock_req) { - mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_remove_lock(&thd->mdl_context, target_lock_data); + mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); + mdl_request_remove(&thd->mdl_context, target_lock_req); } pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) @@ -4368,7 +4367,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, char from[FN_REFLEN],tmp[FN_REFLEN+32]; const char **ext; MY_STAT stat_info; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_request= NULL; enum enum_open_table_action ot_action_unused; DBUG_ENTER("prepare_for_repair"); uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH | @@ -4387,13 +4386,13 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); - mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock_data); + mdl_lock_request= mdl_request_alloc(0, table_list->db, + table_list->table_name, thd->mem_root); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) { - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); DBUG_RETURN(0); } @@ -4413,11 +4412,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, } pthread_mutex_unlock(&LOCK_open); table= &tmp_table; - table_list->mdl_lock_data= mdl_lock_data; - } - else - { - mdl_lock_data= table->mdl_lock_data; + table_list->mdl_lock_request= mdl_lock_request; } /* A MERGE table must not come here. */ @@ -4528,10 +4523,10 @@ end: pthread_mutex_unlock(&LOCK_open); } /* In case of a temporary table there will be no metadata lock. */ - if (error && mdl_lock_data) + if (error && mdl_lock_request) { - mdl_release_lock(&thd->mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } DBUG_RETURN(error); } @@ -5234,7 +5229,7 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { - MDL_LOCK_DATA *target_lock_data= 0; + MDL_LOCK_REQUEST *target_lock_req= NULL; char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1]; uint dst_path_length; char *db= table->db; @@ -5291,9 +5286,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_req)) goto err; - if (!target_lock_data) + if (!target_lock_req) goto table_exists; dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); @@ -5303,7 +5298,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Make the metadata lock available to open_table() called to reopen the table down the road. */ - table->mdl_lock_data= target_lock_data; + table->mdl_lock_request= target_lock_req; } DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); @@ -5474,10 +5469,10 @@ binlog: res= FALSE; err: - if (target_lock_data) + if (target_lock_req) { - mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_remove_lock(&thd->mdl_context, target_lock_data); + mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); + mdl_request_remove(&thd->mdl_context, target_lock_req); } DBUG_RETURN(res); } @@ -6416,7 +6411,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint order_num, ORDER *order, bool ignore) { TABLE *table, *new_table= 0; - MDL_LOCK_DATA *mdl_lock_data, *target_lock_data= 0; + MDL_LOCK_TICKET *mdl_lock_ticket; + MDL_LOCK_REQUEST *target_lock_req= NULL; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6587,7 +6583,7 @@ view_err: MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) DBUG_RETURN(TRUE); table->use_all_columns(); - mdl_lock_data= table->mdl_lock_data; + mdl_lock_ticket= table->mdl_lock_ticket; /* Prohibit changing of the UNION list of a non-temporary MERGE table @@ -6640,9 +6636,9 @@ view_err: else { if (lock_table_name_if_not_cached(thd, new_db, new_name, - &target_lock_data)) + &target_lock_req)) DBUG_RETURN(TRUE); - if (!target_lock_data) + if (!target_lock_req) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); DBUG_RETURN(TRUE); @@ -6835,13 +6831,12 @@ view_err: */ if (new_name != table_name || new_db != db) { - mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_remove_lock(&thd->mdl_context, target_lock_data); - mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, - mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); + mdl_request_remove(&thd->mdl_context, target_lock_req); + mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket); } else - mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); } DBUG_RETURN(error); } @@ -7074,7 +7069,7 @@ view_err: #ifdef WITH_PARTITION_STORAGE_ENGINE if (fast_alter_partition) { - DBUG_ASSERT(!target_lock_data); + DBUG_ASSERT(!target_lock_req); DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, db, table_name, @@ -7441,16 +7436,16 @@ view_err: table_list->table_name_length= strlen(new_name); table_list->db= new_db; table_list->db_length= strlen(new_db); - table_list->mdl_lock_data= target_lock_data; + table_list->mdl_lock_request= target_lock_req; } else { /* - Under LOCK TABLES, we have a different mdl_lock_data + Under LOCK TABLES, we have a different mdl_lock_ticket points to a different instance than the one set initially to request the lock. */ - table_list->mdl_lock_data= mdl_lock_data; + table_list->mdl_lock_request->ticket= mdl_lock_ticket; } if (open_table(thd, table_list, thd->mem_root, &ot_action_unused, MYSQL_OPEN_REOPEN)) @@ -7516,13 +7511,12 @@ view_err: { if ((new_name != table_name || new_db != db)) { - mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_remove_lock(&thd->mdl_context, target_lock_data); - mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, - mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); + mdl_request_remove(&thd->mdl_context, target_lock_req); + mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket); } else - mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); } end_temporary: @@ -7577,10 +7571,10 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (target_lock_data) + if (target_lock_req) { - mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_remove_lock(&thd->mdl_context, target_lock_data); + mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); + mdl_request_remove(&thd->mdl_context, target_lock_req); } DBUG_RETURN(TRUE); @@ -7592,12 +7586,12 @@ err_with_mdl: tables and release the exclusive metadata lock. */ thd->locked_tables_list.unlink_all_closed_tables(); - if (target_lock_data) + if (target_lock_req) { - mdl_release_lock(&thd->mdl_context, target_lock_data); - mdl_remove_lock(&thd->mdl_context, target_lock_data); + mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); + mdl_request_remove(&thd->mdl_context, target_lock_req); } - mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket); DBUG_RETURN(TRUE); } /* mysql_alter_table */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 9a42dd189e7..894204fec58 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -329,6 +329,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) String stmt_query; bool need_start_waiting= FALSE; bool lock_upgrade_done= FALSE; + MDL_LOCK_TICKET *mdl_lock_ticket= NULL; DBUG_ENTER("mysql_create_or_drop_trigger"); @@ -451,8 +452,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (!(tables->table= find_write_locked_table(thd->open_tables, tables->db, tables->table_name))) goto end; - /* Later on we will need it to downgrade the lock */ - tables->mdl_lock_data= tables->table->mdl_lock_data; } else { @@ -465,6 +464,9 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) } table= tables->table; + /* Later on we will need it to downgrade the lock */ + mdl_lock_ticket= table->mdl_lock_ticket; + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto end; @@ -511,8 +513,7 @@ end: TABLE instance created by open_n_lock_single_table() and metadata lock. */ if (thd->locked_tables_mode && tables && lock_upgrade_done) - mdl_downgrade_exclusive_lock(&thd->mdl_context, - tables->mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); if (need_start_waiting) start_waiting_global_read_lock(thd); diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 25a0db2fbb8..7e557c3ce68 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -142,7 +142,7 @@ void udf_init() tables.alias= tables.table_name= (char*) "func"; tables.lock_type = TL_READ; tables.db= db; - alloc_mdl_locks(&tables, new_thd->mem_root); + alloc_mdl_requests(&tables, new_thd->mem_root); if (simple_open_n_lock_tables(new_thd, &tables)) { @@ -486,7 +486,7 @@ int mysql_create_function(THD *thd,udf_func *udf) bzero((char*) &tables,sizeof(tables)); tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* Allow creation of functions even if we can't open func table */ if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; @@ -565,7 +565,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) bzero((char*) &tables,sizeof(tables)); tables.db=(char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); diff --git a/sql/table.cc b/sql/table.cc index dd39d05733e..b1988faf722 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4811,12 +4811,11 @@ size_t max_row_length(TABLE *table, const uchar *data) objects for all elements of table list. */ -void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root) +void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_lock_data= mdl_alloc_lock(0, table_list->db, - table_list->table_name, - root); + table_list->mdl_lock_request= + mdl_request_alloc(0, table_list->db, table_list->table_name, root); } diff --git a/sql/table.h b/sql/table.h index 918fb8dfff0..3b125217338 100644 --- a/sql/table.h +++ b/sql/table.h @@ -30,7 +30,8 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; -struct MDL_LOCK_DATA; +struct MDL_LOCK_REQUEST; +struct MDL_LOCK_TICKET; /*************************************************************************/ @@ -813,7 +814,7 @@ public: partition_info *part_info; /* Partition related information */ bool no_partitions_used; /* If true, all partitions have been pruned away */ #endif - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_TICKET *mdl_lock_ticket; bool fill_item_list(List *item_list) const; void reset_item_list(List *item_list) const; @@ -1416,7 +1417,7 @@ struct TABLE_LIST uint table_open_method; enum enum_schema_table_state schema_table_state; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_request; void calc_md5(char *buffer); void set_underlying_merge(); @@ -1785,6 +1786,6 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, size_t max_row_length(TABLE *table, const uchar *data); -void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root); +void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root); #endif /* TABLE_INCLUDED */ diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 3d1b2b13c1e..b6992256d5a 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -434,16 +434,15 @@ int ha_myisammrg::add_children_list(void) /* Copy select_lex. Used in unique_table() at least. */ child_l->select_lex= parent_l->select_lex; - child_l->mdl_lock_data= NULL; /* Safety, if alloc_mdl_locks fails. */ + child_l->mdl_lock_request= NULL; /* Safety, if alloc_mdl_requests fails. */ /* Break when this was the last child. */ if (&child_l->next_global == this->children_last_l) break; } - alloc_mdl_locks(children_l, - thd->locked_tables_root ? thd->locked_tables_root : - thd->mem_root); + alloc_mdl_requests(children_l, thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); /* Insert children into the table list. */ if (parent_l->next_global) From 195adcd201b78357ebdf8a30777d3f867829418a Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:34:19 +0300 Subject: [PATCH 091/466] Backport of: ---------------------------------------------------------- revno: 2617.23.19 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Tue 2009-03-03 01:20:44 +0300 message: Metadata locking: realign comments. No semantical changes, only enforce a bit of the coding style. This is a review fix for WL#4284 "Transactional DDL locking". --- sql/mdl.cc | 609 ++++++++++++++++++++++++++--------------------------- sql/mdl.h | 62 +++--- 2 files changed, 335 insertions(+), 336 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 18f0debdbb5..e3f285f3a55 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -22,10 +22,10 @@ /** - The lock context. Created internally for an acquired lock. - For a given name, there exists only one MDL_LOCK instance, - and it exists only when the lock has been granted. - Can be seen as an MDL subsystem's version of TABLE_SHARE. + The lock context. Created internally for an acquired lock. + For a given name, there exists only one MDL_LOCK instance, + and it exists only when the lock has been granted. + Can be seen as an MDL subsystem's version of TABLE_SHARE. */ struct MDL_LOCK @@ -48,7 +48,7 @@ struct MDL_LOCK MDL_KEY key; /** List of granted tickets for this lock. */ Ticket_list granted; - /* + /** There can be several upgraders and active exclusive belonging to the same context. */ @@ -76,11 +76,11 @@ pthread_cond_t COND_mdl; HASH mdl_locks; /** - Structure implementing global metadata lock. The only types - of locks which are supported at the moment are shared and - intention exclusive locks. Note that the latter type of global - lock acquired automatically when one tries to acquire exclusive - or shared upgradable lock on particular object. + Structure implementing global metadata lock. The only types + of locks which are supported at the moment are shared and + intention exclusive locks. Note that the latter type of global + lock acquired automatically when one tries to acquire exclusive + or shared upgradable lock on particular object. */ struct MDL_GLOBAL_LOCK @@ -102,21 +102,21 @@ mdl_locks_key(const uchar *record, size_t *length, /** - Initialize the metadata locking subsystem. + Initialize the metadata locking subsystem. - This function is called at server startup. + This function is called at server startup. - In particular, initializes the new global mutex and - the associated condition variable: LOCK_mdl and COND_mdl. - These locking primitives are implementation details of the MDL - subsystem and are private to it. + In particular, initializes the new global mutex and + the associated condition variable: LOCK_mdl and COND_mdl. + These locking primitives are implementation details of the MDL + subsystem and are private to it. - Note, that even though the new implementation adds acquisition - of a new global mutex to the execution flow of almost every SQL - statement, the design capitalizes on that to later save on - look ups in the table definition cache. This leads to reduced - contention overall and on LOCK_open in particular. - Please see the description of mdl_acquire_shared_lock() for details. + Note, that even though the new implementation adds acquisition + of a new global mutex to the execution flow of almost every SQL + statement, the design capitalizes on that to later save on + look ups in the table definition cache. This leads to reduced + contention overall and on LOCK_open in particular. + Please see the description of mdl_acquire_shared_lock() for details. */ void mdl_init() @@ -131,10 +131,10 @@ void mdl_init() /** - Release resources of metadata locking subsystem. + Release resources of metadata locking subsystem. - Destroys the global mutex and the condition variable. - Called at server shutdown. + Destroys the global mutex and the condition variable. + Called at server shutdown. */ void mdl_destroy() @@ -147,9 +147,9 @@ void mdl_destroy() /** - Initialize a metadata locking context. + Initialize a metadata locking context. - This is to be called when a new server connection is created. + This is to be called when a new server connection is created. */ void mdl_context_init(MDL_CONTEXT *context, THD *thd) @@ -162,15 +162,15 @@ void mdl_context_init(MDL_CONTEXT *context, THD *thd) /** - Destroy metadata locking context. + Destroy metadata locking context. - Assumes and asserts that there are no active or pending locks - associated with this context at the time of the destruction. + Assumes and asserts that there are no active or pending locks + associated with this context at the time of the destruction. - Currently does nothing. Asserts that there are no pending - or satisfied lock requests. The pending locks must be released - prior to destruction. This is a new way to express the assertion - that all tables are closed before a connection is destroyed. + Currently does nothing. Asserts that there are no pending + or satisfied lock requests. The pending locks must be released + prior to destruction. This is a new way to express the assertion + that all tables are closed before a connection is destroyed. */ void mdl_context_destroy(MDL_CONTEXT *context) @@ -182,13 +182,13 @@ void mdl_context_destroy(MDL_CONTEXT *context) /** - Backup and reset state of meta-data locking context. + Backup and reset state of meta-data locking context. - mdl_context_backup_and_reset(), mdl_context_restore() and - mdl_context_merge() are used by HANDLER implementation which - needs to open table for new HANDLER independently of already - open HANDLERs and add this table/metadata lock to the set of - tables open/metadata locks for HANDLERs afterwards. + mdl_context_backup_and_reset(), mdl_context_restore() and + mdl_context_merge() are used by HANDLER implementation which + needs to open table for new HANDLER independently of already + open HANDLERs and add this table/metadata lock to the set of + tables open/metadata locks for HANDLERs afterwards. */ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) @@ -201,7 +201,7 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) /** - Restore state of meta-data locking context from backup. + Restore state of meta-data locking context from backup. */ void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) @@ -214,7 +214,7 @@ void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) /** - Merge meta-data locks from one context into another. + Merge meta-data locks from one context into another. */ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) @@ -247,32 +247,32 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) /** - Initialize a lock request. + Initialize a lock request. - This is to be used for every lock request. + This is to be used for every lock request. - Note that initialization and allocation are split into two - calls. This is to allow flexible memory management of lock - requests. Normally a lock request is stored in statement memory - (e.g. is a member of struct TABLE_LIST), but we would also like - to allow allocation of lock requests in other memory roots, - for example in the grant subsystem, to lock privilege tables. + Note that initialization and allocation are split into two + calls. This is to allow flexible memory management of lock + requests. Normally a lock request is stored in statement memory + (e.g. is a member of struct TABLE_LIST), but we would also like + to allow allocation of lock requests in other memory roots, + for example in the grant subsystem, to lock privilege tables. - The MDL subsystem does not own or manage memory of lock requests. - Instead it assumes that the life time of every lock request (including - encompassed members db/name) encloses calls to mdl_request_add() - and mdl_request_remove() or mdl_request_remove_all(). + The MDL subsystem does not own or manage memory of lock requests. + Instead it assumes that the life time of every lock request (including + encompassed members db/name) encloses calls to mdl_request_add() + and mdl_request_remove() or mdl_request_remove_all(). - @param lock_req Pointer to an MDL_LOCK_REQUEST object to initialize - @param type Id of type of object to be locked - @param db Name of database to which the object belongs - @param name Name of of the object + @param lock_req Pointer to an MDL_LOCK_REQUEST object to initialize + @param type Id of type of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object - The initialized lock request will have MDL_SHARED type. + The initialized lock request will have MDL_SHARED type. - Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 - Note that tables and views must have the same lock type, since - they share the same name space in the SQL standard. + Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 + Note that tables and views must have the same lock type, since + they share the same name space in the SQL standard. */ void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, @@ -285,21 +285,21 @@ void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, /** - Allocate and initialize one lock request. + Allocate and initialize one lock request. - Same as mdl_init_lock(), but allocates the lock and the key buffer - on a memory root. Necessary to lock ad-hoc tables, e.g. - mysql.* tables of grant and data dictionary subsystems. + Same as mdl_init_lock(), but allocates the lock and the key buffer + on a memory root. Necessary to lock ad-hoc tables, e.g. + mysql.* tables of grant and data dictionary subsystems. - @param type Id of type of object to be locked - @param db Name of database to which object belongs - @param name Name of of object - @param root MEM_ROOT on which object should be allocated + @param type Id of type of object to be locked + @param db Name of database to which object belongs + @param name Name of of object + @param root MEM_ROOT on which object should be allocated - @note The allocated lock request will have MDL_SHARED type. + @note The allocated lock request will have MDL_SHARED type. - @retval 0 Error - @retval non-0 Pointer to an object representing a lock request + @retval 0 Error if out of memory + @retval non-0 Pointer to an object representing a lock request */ MDL_LOCK_REQUEST * @@ -319,19 +319,19 @@ mdl_request_alloc(unsigned char type, const char *db, /** - Add a lock request to the list of lock requests of the context. + Add a lock request to the list of lock requests of the context. - The procedure to acquire metadata locks is: - - allocate and initialize lock requests (mdl_request_alloc()) - - associate them with a context (mdl_request_add()) - - call mdl_acquire_shared_lock()/mdl_ticket_release() (maybe repeatedly). + The procedure to acquire metadata locks is: + - allocate and initialize lock requests (mdl_request_alloc()) + - associate them with a context (mdl_request_add()) + - call mdl_acquire_shared_lock()/mdl_ticket_release() (maybe repeatedly). - Associates a lock request with the given context. + Associates a lock request with the given context. - @param context The MDL context to associate the lock with. - There should be no more than one context per - connection, to avoid deadlocks. - @param lock_req The lock request to be added. + @param context The MDL context to associate the lock with. + There should be no more than one context per + connection, to avoid deadlocks. + @param lock_req The lock request to be added. */ void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) @@ -344,18 +344,18 @@ void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) /** - Remove a lock request from the list of lock requests. + Remove a lock request from the list of lock requests. - Disassociates a lock request from the given context. + Disassociates a lock request from the given context. - @param context The MDL context to remove the lock from. - @param lock_req The lock request to be removed. + @param context The MDL context to remove the lock from. + @param lock_req The lock request to be removed. - @pre The lock request being removed should correspond to a ticket that - was released or was not acquired. + @pre The lock request being removed should correspond to a ticket that + was released or was not acquired. - @note Resets lock request back to its initial state - (i.e. sets type to MDL_SHARED). + @note Resets lock request back to its initial state + (i.e. sets type to MDL_SHARED). */ void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) @@ -370,12 +370,12 @@ void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) /** - Clear all lock requests in the context. - Disassociates lock requests from the context. + Clear all lock requests in the context. + Disassociates lock requests from the context. - Also resets lock requests back to their initial state (i.e. MDL_SHARED). + Also resets lock requests back to their initial state (i.e. MDL_SHARED). - @param context Context to be cleared. + @param context Context to be cleared. */ void mdl_request_remove_all(MDL_CONTEXT *context) @@ -393,10 +393,10 @@ void mdl_request_remove_all(MDL_CONTEXT *context) /** - Auxiliary functions needed for creation/destruction of MDL_LOCK objects. + Auxiliary functions needed for creation/destruction of MDL_LOCK objects. - @todo This naive implementation should be replaced with one that saves - on memory allocation by reusing released objects. + @todo This naive implementation should be replaced with one that saves + on memory allocation by reusing released objects. */ static MDL_LOCK* alloc_lock_object(const MDL_KEY *mdl_key) @@ -412,11 +412,11 @@ static void free_lock_object(MDL_LOCK *lock) /** - Auxiliary functions needed for creation/destruction of MDL_LOCK_TICKET - objects. + Auxiliary functions needed for creation/destruction of MDL_LOCK_TICKET + objects. - @todo This naive implementation should be replaced with one that saves - on memory allocation by reusing released objects. + @todo This naive implementation should be replaced with one that saves + on memory allocation by reusing released objects. */ static MDL_LOCK_TICKET* alloc_ticket_object(MDL_CONTEXT *context) @@ -433,7 +433,7 @@ static void free_ticket_object(MDL_LOCK_TICKET *ticket) /** - Helper functions which simplifies writing various checks and asserts. + Helper functions which simplifies writing various checks and asserts. */ template @@ -444,16 +444,16 @@ static inline bool is_shared(T *lock_data) /** - Helper functions and macros to be used for killable waiting in metadata - locking subsystem. + Helper functions and macros to be used for killable waiting in metadata + locking subsystem. - @sa THD::enter_cond()/exit_cond()/killed. + @sa THD::enter_cond()/exit_cond()/killed. - @note We can't use THD::enter_cond()/exit_cond()/killed directly here - since this will make metadata subsystem dependant on THD class - and thus prevent us from writing unit tests for it. And usage of - wrapper functions to access THD::killed/enter_cond()/exit_cond() - will probably introduce too much overhead. + @note We can't use THD::enter_cond()/exit_cond()/killed directly here + since this will make metadata subsystem dependant on THD class + and thus prevent us from writing unit tests for it. And usage of + wrapper functions to access THD::killed/enter_cond()/exit_cond() + will probably introduce too much overhead. */ #define MDL_ENTER_COND(A, B) mdl_enter_cond(A, B, __func__, __FILE__, __LINE__) @@ -496,41 +496,41 @@ static inline void mdl_exit_cond(MDL_CONTEXT *context, /** - Check if request for the lock on particular object can be satisfied given - current state of the global metadata lock. + Check if request for the lock on particular object can be satisfied given + current state of the global metadata lock. - @note In other words, we're trying to check that the individual lock - request, implying a form of lock on the global metadata, is - compatible with the current state of the global metadata lock. + @note In other words, we're trying to check that the individual lock + request, implying a form of lock on the global metadata, is + compatible with the current state of the global metadata lock. - @param lock_req Request for lock on an individual object, implying a - certain kind of global metadata lock. + @param lock_req Request for lock on an individual object, implying a + certain kind of global metadata lock. - @retval TRUE - Lock request can be satisfied - @retval FALSE - There is some conflicting lock + @retval TRUE - Lock request can be satisfied + @retval FALSE - There is some conflicting lock - Here is a compatibility matrix defined by this function: + Here is a compatibility matrix defined by this function: - | | Satisfied or pending requests - | | for global metadata lock - ----------------+-------------+-------------------------------------------- - Type of request | Correspond. | - for indiv. lock | global lock | Active-S Pending-S Active-IS(**) Active-IX - ----------------+-------------+-------------------------------------------- - S, high-prio S | IS | + + + + - upgradable S | IX | - - + + - X | IX | - - + + - S upgraded to X | IX (*) | 0 + + + + | | Satisfied or pending requests + | | for global metadata lock + ----------------+-------------+-------------------------------------------- + Type of request | Correspond. | + for indiv. lock | global lock | Active-S Pending-S Active-IS(**) Active-IX + ----------------+-------------+-------------------------------------------- + S, high-prio S | IS | + + + + + upgradable S | IX | - - + + + X | IX | - - + + + S upgraded to X | IX (*) | 0 + + + - Here: "+" -- means that request can be satisfied - "-" -- means that request can't be satisfied and should wait - "0" -- means impossible situation which will trigger assert + Here: "+" -- means that request can be satisfied + "-" -- means that request can't be satisfied and should wait + "0" -- means impossible situation which will trigger assert - (*) Since for upgradable shared locks we always take intention exclusive - global lock at the same time when obtaining the shared lock, there - is no need to obtain such lock during the upgrade itself. - (**) Since intention shared global locks are compatible with all other - type of locks we don't even have any accounting for them. + (*) Since for upgradable shared locks we always take intention exclusive + global lock at the same time when obtaining the shared lock, there + is no need to obtain such lock during the upgrade itself. + (**) Since intention shared global locks are compatible with all other + type of locks we don't even have any accounting for them. */ static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) @@ -588,33 +588,33 @@ static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) /** - Check if request for the lock can be satisfied given current state of lock. + Check if request for the lock can be satisfied given current state of lock. - @param lock Lock. - @param lock_req Request for lock. + @param lock Lock. + @param lock_req Request for lock. - @retval TRUE Lock request can be satisfied - @retval FALSE There is some conflicting lock. + @retval TRUE Lock request can be satisfied + @retval FALSE There is some conflicting lock. - This function defines the following compatibility matrix for metadata locks: + This function defines the following compatibility matrix for metadata locks: - | Satisfied or pending requests which we have in MDL_LOCK - ----------------+--------------------------------------------------------- - Current request | Active-S Pending-X Active-X Act-S-pend-upgrade-to-X - ----------------+--------------------------------------------------------- - S, upgradable S | + - - (*) - - High-prio S | + + - + - X | - + - - - S upgraded to X | - (**) + 0 0 + | Satisfied or pending requests which we have in MDL_LOCK + ----------------+--------------------------------------------------------- + Current request | Active-S Pending-X Active-X Act-S-pend-upgrade-to-X + ----------------+--------------------------------------------------------- + S, upgradable S | + - - (*) - + High-prio S | + + - + + X | - + - - + S upgraded to X | - (**) + 0 0 - Here: "+" -- means that request can be satisfied - "-" -- means that request can't be satisfied and should wait - "0" -- means impossible situation which will trigger assert + Here: "+" -- means that request can be satisfied + "-" -- means that request can't be satisfied and should wait + "0" -- means impossible situation which will trigger assert - (*) Unless active exclusive lock belongs to the same context as shared - lock being requested. - (**) Unless all active shared locks belong to the same context as one - being upgraded. + (*) Unless active exclusive lock belongs to the same context as shared + lock being requested. + (**) Unless all active shared locks belong to the same context as one + being upgraded. */ static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, @@ -683,12 +683,12 @@ static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, /** - Check whether the context already holds a compatible lock ticket - on a object. Only shared locks can be recursive. + Check whether the context already holds a compatible lock ticket + on a object. Only shared locks can be recursive. - @param lock_req Lock request object for lock to be acquired + @param lock_req Lock request object for lock to be acquired - @return A pointer to the lock ticket for the object or NULL otherwise. + @return A pointer to the lock ticket for the object or NULL otherwise. */ static MDL_LOCK_TICKET * @@ -711,28 +711,28 @@ mdl_context_find_ticket(MDL_CONTEXT *ctx, MDL_LOCK_REQUEST *lock_req) /** - Try to acquire one shared lock. + Try to acquire one shared lock. - Unlike exclusive locks, shared locks are acquired one by - one. This is interface is chosen to simplify introduction of - the new locking API to the system. mdl_acquire_shared_lock() - is currently used from open_table(), and there we have only one - table to work with. + Unlike exclusive locks, shared locks are acquired one by + one. This is interface is chosen to simplify introduction of + the new locking API to the system. mdl_acquire_shared_lock() + is currently used from open_table(), and there we have only one + table to work with. - In future we may consider allocating multiple shared locks at once. + In future we may consider allocating multiple shared locks at once. - This function must be called after the lock is added to a context. + This function must be called after the lock is added to a context. - @param context [in] Context containing request for lock - @param lock_req [in] Lock request object for lock to be acquired - @param retry [out] Indicates that conflicting lock exists and another - attempt should be made after releasing all current - locks and waiting for conflicting lock go away - (using mdl_wait_for_locks()). + @param context [in] Context containing request for lock + @param lock_req [in] Lock request object for lock to be acquired + @param retry [out] Indicates that conflicting lock exists and another + attempt should be made after releasing all current + locks and waiting for conflicting lock go away + (using mdl_wait_for_locks()). - @retval FALSE Success. - @retval TRUE Failure. Either error occured or conflicting lock exists. - In the latter case "retry" parameter is set to TRUE. + @retval FALSE Success. + @retval TRUE Failure. Either error occured or conflicting lock exists. + In the latter case "retry" parameter is set to TRUE. */ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, @@ -824,13 +824,13 @@ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); /** - Notify a thread holding a shared metadata lock of a pending exclusive lock. + Notify a thread holding a shared metadata lock of a pending exclusive lock. - @param thd Current thread context - @param conf_lock_ticket Conflicting metadata lock + @param thd Current thread context + @param conf_lock_ticket Conflicting metadata lock - @retval TRUE A thread was woken up - @retval FALSE Lock is not a shared one or no thread was woken up + @retval TRUE A thread was woken up + @retval FALSE Lock is not a shared one or no thread was woken up */ static bool notify_shared_lock(THD *thd, MDL_LOCK_TICKET *conf_lock_ticket) @@ -843,18 +843,18 @@ static bool notify_shared_lock(THD *thd, MDL_LOCK_TICKET *conf_lock_ticket) /** - Acquire exclusive locks. The context must contain the list of - locks to be acquired. There must be no granted locks in the - context. + Acquire exclusive locks. The context must contain the list of + locks to be acquired. There must be no granted locks in the + context. - This is a replacement of lock_table_names(). It is used in - RENAME, DROP and other DDL SQL statements. + This is a replacement of lock_table_names(). It is used in + RENAME, DROP and other DDL SQL statements. - @param context A context containing requests for exclusive locks - The context may not have other lock requests. + @param context A context containing requests for exclusive locks + The context may not have other lock requests. - @retval FALSE Success - @retval TRUE Failure + @retval FALSE Success + @retval TRUE Failure */ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) @@ -1009,25 +1009,25 @@ err: /** - Upgrade a shared metadata lock to exclusive. + Upgrade a shared metadata lock to exclusive. - Used in ALTER TABLE, when a copy of the table with the - new definition has been constructed. + Used in ALTER TABLE, when a copy of the table with the + new definition has been constructed. - @param context Context to which shared lock belongs - @param ticket Ticket for shared lock to be upgraded + @param context Context to which shared lock belongs + @param ticket Ticket for shared lock to be upgraded - @note In case of failure to upgrade lock (e.g. because upgrader - was killed) leaves lock in its original state (locked in - shared mode). + @note In case of failure to upgrade lock (e.g. because upgrader + was killed) leaves lock in its original state (locked in + shared mode). - @note There can be only one upgrader for a lock or we will have deadlock. - This invariant is ensured by code outside of metadata subsystem usually - by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, - TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. + @note There can be only one upgrader for a lock or we will have deadlock. + This invariant is ensured by code outside of metadata subsystem usually + by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, + TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. - @retval FALSE Success - @retval TRUE Failure (thread was killed) + @retval FALSE Success + @retval TRUE Failure (thread was killed) */ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, @@ -1110,27 +1110,27 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, /** - Try to acquire an exclusive lock on the object if there are - no conflicting locks. + Try to acquire an exclusive lock on the object if there are + no conflicting locks. - Similar to the previous function, but returns - immediately without any side effect if encounters a lock - conflict. Otherwise takes the lock. + Similar to the previous function, but returns + immediately without any side effect if encounters a lock + conflict. Otherwise takes the lock. - This function is used in CREATE TABLE ... LIKE to acquire a lock - on the table to be created. In this statement we don't want to - block and wait for the lock if the table already exists. + This function is used in CREATE TABLE ... LIKE to acquire a lock + on the table to be created. In this statement we don't want to + block and wait for the lock if the table already exists. - @param context [in] The context containing the lock request - @param lock_req [in] The lock request - @param conflict [out] Indicates that conflicting lock exists + @param context [in] The context containing the lock request + @param lock_req [in] The lock request + @param conflict [out] Indicates that conflicting lock exists - @retval TRUE Failure either conflicting lock exists or some error - occured (probably OOM). - @retval FALSE Success, lock was acquired. + @retval TRUE Failure either conflicting lock exists or some error + occured (probably OOM). + @retval FALSE Success, lock was acquired. - FIXME: Compared to lock_table_name_if_not_cached() - it gives sligthly more false negatives. + FIXME: Compared to lock_table_name_if_not_cached() + it gives sligthly more false negatives. */ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, @@ -1184,15 +1184,15 @@ err: /** - Acquire global shared metadata lock. + Acquire global shared metadata lock. - Holding this lock will block all requests for exclusive locks - and shared locks which can be potentially upgraded to exclusive. + Holding this lock will block all requests for exclusive locks + and shared locks which can be potentially upgraded to exclusive. - @param context Current metadata locking context. + @param context Current metadata locking context. - @retval FALSE Success -- the lock was granted. - @retval TRUE Failure -- our thread was killed. + @retval FALSE Success -- the lock was granted. + @retval TRUE Failure -- our thread was killed. */ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) @@ -1227,18 +1227,18 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) /** - Wait until there will be no locks that conflict with lock requests - in the context. + Wait until there will be no locks that conflict with lock requests + in the context. - This is a part of the locking protocol and must be used by the - acquirer of shared locks after a back-off. + This is a part of the locking protocol and must be used by the + acquirer of shared locks after a back-off. - Does not acquire the locks! + Does not acquire the locks! - @param context Context with which lock requests are associated. + @param context Context with which lock requests are associated. - @retval FALSE Success. One can try to obtain metadata locks. - @retval TRUE Failure (thread was killed) + @retval FALSE Success. One can try to obtain metadata locks. + @retval TRUE Failure (thread was killed) */ bool mdl_wait_for_locks(MDL_CONTEXT *context) @@ -1297,8 +1297,8 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) /** - Auxiliary function which allows to release particular lock - ownership of which is represented by a lock ticket object. + Auxiliary function which allows to release particular lock + ownership of which is represented by a lock ticket object. */ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) @@ -1347,15 +1347,15 @@ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) /** - Release all locks associated with the context, but leave them - in the context as lock requests. + Release all locks associated with the context, but leave them + in the context as lock requests. - This function is used to back off in case of a lock conflict. - It is also used to release shared locks in the end of an SQL - statement. + This function is used to back off in case of a lock conflict. + It is also used to release shared locks in the end of an SQL + statement. - @param context The context with which the locks to be released - are associated. + @param context The context with which the locks to be released + are associated. */ void mdl_ticket_release_all(MDL_CONTEXT *context) @@ -1395,11 +1395,10 @@ void mdl_ticket_release_all(MDL_CONTEXT *context) /** - Release a lock. - - @param context Context containing lock in question - @param ticket Lock to be released + Release a lock. + @param context Context containing lock in question + @param ticket Lock to be released */ void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) @@ -1415,12 +1414,12 @@ void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) /** - Release all locks in the context which correspond to the same name/ - object as this lock request, remove lock requests from the context. + Release all locks in the context which correspond to the same name/ + object as this lock request, remove lock requests from the context. - @param context Context containing locks in question - @param ticket One of the locks for the name/object for which all - locks should be released. + @param context Context containing locks in question + @param ticket One of the locks for the name/object for which all + locks should be released. */ void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, @@ -1461,10 +1460,10 @@ void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, /** - Downgrade an exclusive lock to shared metadata lock. + Downgrade an exclusive lock to shared metadata lock. - @param context A context to which exclusive lock belongs - @param ticket Ticket for exclusive lock to be downgraded + @param context A context to which exclusive lock belongs + @param ticket Ticket for exclusive lock to be downgraded */ void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, @@ -1490,9 +1489,9 @@ void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, /** - Release global shared metadata lock. + Release global shared metadata lock. - @param context Current context + @param context Current context */ void mdl_release_global_shared_lock(MDL_CONTEXT *context) @@ -1509,16 +1508,16 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) /** - Auxiliary function which allows to check if we have exclusive lock - on the object. + Auxiliary function which allows to check if we have exclusive lock + on the object. - @param context Current context - @param type Id of object type - @param db Name of the database - @param name Name of the object + @param context Current context + @param type Id of object type + @param db Name of the database + @param name Name of the object - @return TRUE if current context contains exclusive lock for the object, - FALSE otherwise. + @return TRUE if current context contains exclusive lock for the object, + FALSE otherwise. */ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, @@ -1542,16 +1541,16 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, /** - Auxiliary function which allows to check if we have some kind of lock on - a object. + Auxiliary function which allows to check if we have some kind of lock on + a object. - @param context Current context - @param type Id of object type - @param db Name of the database - @param name Name of the object + @param context Current context + @param type Id of object type + @param db Name of the database + @param name Name of the object - @return TRUE if current context contains satisfied lock for the object, - FALSE otherwise. + @return TRUE if current context contains satisfied lock for the object, + FALSE otherwise. */ bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, @@ -1574,12 +1573,12 @@ bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, /** - Check if we have any pending exclusive locks which conflict with - existing shared lock. + Check if we have any pending exclusive locks which conflict with + existing shared lock. - @param ticket Shared lock against which check should be performed. + @param ticket Shared lock against which check should be performed. - @return TRUE if there are any conflicting locks, FALSE otherwise. + @return TRUE if there are any conflicting locks, FALSE otherwise. */ bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) @@ -1597,31 +1596,31 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) /** - Associate pointer to an opaque object with a lock. + Associate pointer to an opaque object with a lock. - @param ticket Lock ticket for the lock with which the object - should be associated. - @param cached_object Pointer to the object - @param release_hook Cleanup function to be called when MDL subsystem - decides to remove lock or associate another object. + @param ticket Lock ticket for the lock with which the object + should be associated. + @param cached_object Pointer to the object + @param release_hook Cleanup function to be called when MDL subsystem + decides to remove lock or associate another object. - This is used to cache a pointer to TABLE_SHARE in the lock - structure. Such caching can save one acquisition of LOCK_open - and one table definition cache lookup for every table. + This is used to cache a pointer to TABLE_SHARE in the lock + structure. Such caching can save one acquisition of LOCK_open + and one table definition cache lookup for every table. - Since the pointer may be stored only inside an acquired lock, - the caching is only effective when there is more than one lock - granted on a given table. + Since the pointer may be stored only inside an acquired lock, + the caching is only effective when there is more than one lock + granted on a given table. - This function has the following usage pattern: - - try to acquire an MDL lock - - when done, call for mdl_get_cached_object(). If it returns NULL, our - thread has the only lock on this table. - - look up TABLE_SHARE in the table definition cache - - call mdl_set_cache_object() to assign the share to the opaque pointer. + This function has the following usage pattern: + - try to acquire an MDL lock + - when done, call for mdl_get_cached_object(). If it returns NULL, our + thread has the only lock on this table. + - look up TABLE_SHARE in the table definition cache + - call mdl_set_cache_object() to assign the share to the opaque pointer. - The release hook is invoked when the last shared metadata - lock on this name is released. + The release hook is invoked when the last shared metadata + lock on this name is released. */ void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, @@ -1648,11 +1647,11 @@ void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, /** - Get a pointer to an opaque object that associated with the lock. + Get a pointer to an opaque object that associated with the lock. - @param ticket Lock ticket for the lock which the object is associated to. + @param ticket Lock ticket for the lock which the object is associated to. - @return Pointer to an opaque object associated with the lock. + @return Pointer to an opaque object associated with the lock. */ void* mdl_get_cached_object(MDL_LOCK_TICKET *ticket) diff --git a/sql/mdl.h b/sql/mdl.h index 103ab8130ba..b5d05b06144 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -29,15 +29,15 @@ struct MDL_LOCK; struct MDL_CONTEXT; /** - Type of metadata lock request. + Type of metadata lock request. - - High-priority shared locks differ from ordinary shared locks by - that they ignore pending requests for exclusive locks. - - Upgradable shared locks can be later upgraded to exclusive - (because of that their acquisition involves implicit - acquisition of global intention-exclusive lock). + - High-priority shared locks differ from ordinary shared locks by + that they ignore pending requests for exclusive locks. + - Upgradable shared locks can be later upgraded to exclusive + (because of that their acquisition involves implicit + acquisition of global intention-exclusive lock). - @see Comments for can_grant_lock() and can_grant_global_lock() for details. + @see Comments for can_grant_lock() and can_grant_global_lock() for details. */ enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, @@ -54,12 +54,12 @@ enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; /** - Metadata lock object key. + Metadata lock object key. - A lock is requested or granted based on a fully qualified name and type. - E.g. They key for a table consists of <0 (=table)>++
. - Elsewhere in the comments this triple will be referred to simply as "key" - or "name". + A lock is requested or granted based on a fully qualified name and type. + E.g. They key for a table consists of <0 (=table)>++
. + Elsewhere in the comments this triple will be referred to simply as "key" + or "name". */ class MDL_KEY @@ -110,8 +110,8 @@ private: /** - Hook class which via its methods specifies which members - of T should be used for participating in MDL lists. + Hook class which via its methods specifies which members + of T should be used for participating in MDL lists. */ template @@ -124,12 +124,12 @@ struct I_P_List_adapter /** - A pending metadata lock request. - A pending lock request or a granted metadata lock share the same abstract - base but are presented individually because they have different allocation - sites and hence different lifetimes. The allocation of lock requests is - controlled from outside of the MDL subsystem, while allocation of granted - locks (tickets) is controlled within the MDL subsystem. + A pending metadata lock request. + A pending lock request or a granted metadata lock share the same abstract + base but are presented individually because they have different allocation + sites and hence different lifetimes. The allocation of lock requests is + controlled from outside of the MDL subsystem, while allocation of granted + locks (tickets) is controlled within the MDL subsystem. */ struct MDL_LOCK_REQUEST @@ -138,7 +138,7 @@ struct MDL_LOCK_REQUEST enum enum_mdl_type type; /** - Pointers for participating in the list of lock requests for this context. + Pointers for participating in the list of lock requests for this context. */ MDL_LOCK_REQUEST *next_in_context; MDL_LOCK_REQUEST **prev_in_context; @@ -154,12 +154,12 @@ struct MDL_LOCK_REQUEST /** - A granted metadata lock. + A granted metadata lock. - @warning MDL_LOCK_TICKET members are private to the MDL subsystem. + @warning MDL_LOCK_TICKET members are private to the MDL subsystem. - @note Multiple shared locks on a same object are represented by a - single ticket. The same does not apply for other lock types. + @note Multiple shared locks on a same object are represented by a + single ticket. The same does not apply for other lock types. */ struct MDL_LOCK_TICKET @@ -170,13 +170,13 @@ struct MDL_LOCK_TICKET enum enum_mdl_state state; /** - Pointers for participating in the list of lock requests for this context. + Pointers for participating in the list of lock requests for this context. */ MDL_LOCK_TICKET *next_in_context; MDL_LOCK_TICKET **prev_in_context; /** - Pointers for participating in the list of satisfied/pending requests - for the lock. + Pointers for participating in the list of satisfied/pending requests + for the lock. */ MDL_LOCK_TICKET *next_in_lock; MDL_LOCK_TICKET **prev_in_lock; @@ -189,8 +189,8 @@ struct MDL_LOCK_TICKET /** - Context of the owner of metadata locks. I.e. each server - connection has such a context. + Context of the owner of metadata locks. I.e. each server + connection has such a context. */ struct MDL_CONTEXT @@ -286,7 +286,7 @@ void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, MDL_LOCK_TICKET *mdl_savepoint); /** - Get iterator for walking through all lock requests in the context. + Get iterator for walking through all lock requests in the context. */ inline MDL_CONTEXT::Request_iterator From a3a23ec4d3b9d9c22b8bdbc9adf2cd0f157c6a3f Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:52:05 +0300 Subject: [PATCH 092/466] Backport of: ---------------------------------------------------------- revno: 2617.23.20 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Wed 2009-03-04 16:31:31 +0300 message: WL#4284 "Transactional DDL locking" Review comments: "Objectify" the MDL API. MDL_request and MDL_context still need manual construction and destruction, since they are used in environment that is averse to constructors/destructors. --- sql/ha_ndbcluster_binlog.cc | 6 +- sql/lock.cc | 28 +- sql/log_event.cc | 9 +- sql/mdl.cc | 916 +++++++++++++++--------------- sql/mdl.h | 274 +++++---- sql/rpl_rli.cc | 2 +- sql/sp_head.cc | 16 +- sql/sql_base.cc | 128 ++--- sql/sql_class.cc | 8 +- sql/sql_class.h | 10 +- sql/sql_delete.cc | 26 +- sql/sql_handler.cc | 30 +- sql/sql_parse.cc | 6 +- sql/sql_plist.h | 1 + sql/sql_show.cc | 27 +- sql/sql_table.cc | 117 ++-- sql/sql_trigger.cc | 8 +- sql/table.cc | 4 +- sql/table.h | 8 +- storage/myisammrg/ha_myisammrg.cc | 2 +- 20 files changed, 829 insertions(+), 797 deletions(-) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index aa1c50eaf5b..08abb88e768 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -140,7 +140,7 @@ static Uint64 *p_latest_trans_gci= 0; */ static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; -static MDL_LOCK_REQUEST binlog_mdl_lock_request; +static MDL_request binlog_mdl_request; /* Helper functions @@ -2342,8 +2342,8 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) tables->alias= tables->table_name= reptable; tables->lock_type= TL_WRITE; thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE; - mdl_request_init(&binlog_mdl_lock_request, 0, tables->db, tables->table_name); - tables->mdl_lock_request= &binlog_mdl_lock_request; + binlog_mdl_request.init(0, tables->db, tables->table_name); + tables->mdl_request= &binlog_mdl_request; tables->required_type= FRMTYPE_TABLE; uint counter; thd->clear_error(); diff --git a/sql/lock.cc b/sql/lock.cc index 40bceae4a99..170007d8f66 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -944,7 +944,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, @note This function assumes that no metadata locks were acquired before calling it. Also it cannot be called while holding LOCK_open mutex. Both these invariants are enforced by asserts - in mdl_acquire_exclusive_locks() functions. + in MDL_context::acquire_exclusive_locks(). @retval FALSE Success. @retval TRUE Failure (OOM or thread was killed). @@ -953,24 +953,24 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, bool lock_table_names(THD *thd, TABLE_LIST *table_list) { TABLE_LIST *lock_table; - MDL_LOCK_REQUEST *mdl_lock_req; + MDL_request *mdl_request; for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - mdl_lock_req= mdl_request_alloc(0, lock_table->db, lock_table->table_name, - thd->mem_root); - if (!mdl_lock_req) + mdl_request= MDL_request::create(0, lock_table->db, lock_table->table_name, + thd->mem_root); + if (!mdl_request) goto end; - mdl_request_set_type(mdl_lock_req, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, mdl_lock_req); - lock_table->mdl_lock_request= mdl_lock_req; + mdl_request->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(mdl_request); + lock_table->mdl_request= mdl_request; } - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + if (thd->mdl_context.acquire_exclusive_locks()) goto end; return 0; end: - mdl_request_remove_all(&thd->mdl_context); + thd->mdl_context.remove_all_requests(); return 1; } @@ -986,8 +986,8 @@ end: void unlock_table_names(THD *thd) { DBUG_ENTER("unlock_table_names"); - mdl_ticket_release_all(&thd->mdl_context); - mdl_request_remove_all(&thd->mdl_context); + thd->mdl_context.release_all_locks(); + thd->mdl_context.remove_all_requests(); DBUG_VOID_RETURN; } @@ -1147,7 +1147,7 @@ bool lock_global_read_lock(THD *thd) redundancy between metadata locks, global read lock and DDL blocker (see WL#4399 and WL#4400). */ - if (mdl_acquire_global_shared_lock(&thd->mdl_context)) + if (thd->mdl_context.acquire_global_shared_lock()) { /* Our thread was killed -- return back to initial state. */ pthread_mutex_lock(&LOCK_global_read_lock); @@ -1181,7 +1181,7 @@ void unlock_global_read_lock(THD *thd) ("global_read_lock: %u global_read_lock_blocks_commit: %u", global_read_lock, global_read_lock_blocks_commit)); - mdl_release_global_shared_lock(&thd->mdl_context); + thd->mdl_context.release_global_shared_lock(); pthread_mutex_lock(&LOCK_global_read_lock); tmp= --global_read_lock; diff --git a/sql/log_event.cc b/sql/log_event.cc index 9f4369b901e..e41bb690e8a 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8060,7 +8060,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; - MDL_LOCK_REQUEST *mdl_lock_request; + MDL_request *mdl_request; size_t dummy_len; void *memory; DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); @@ -8075,7 +8075,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) &table_list, (uint) sizeof(RPL_TABLE_LIST), &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, - &mdl_lock_request, sizeof(MDL_LOCK_REQUEST), + &mdl_request, sizeof(MDL_request), NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -8088,9 +8088,8 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) table_list->updating= 1; strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); strmov(table_list->table_name, m_tblnam); - mdl_request_init(mdl_lock_request, 0, table_list->db, - table_list->table_name); - table_list->mdl_lock_request= mdl_lock_request; + mdl_request->init(0, table_list->db, table_list->table_name); + table_list->mdl_request= mdl_request; int error= 0; diff --git a/sql/mdl.cc b/sql/mdl.cc index e3f285f3a55..101d90d7e42 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -14,26 +14,27 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include "mdl.h" #include "debug_sync.h" #include #include +static bool mdl_initialized= 0; /** The lock context. Created internally for an acquired lock. - For a given name, there exists only one MDL_LOCK instance, + For a given name, there exists only one MDL_lock instance, and it exists only when the lock has been granted. Can be seen as an MDL subsystem's version of TABLE_SHARE. */ -struct MDL_LOCK +class MDL_lock { - typedef I_P_List > +public: + typedef I_P_List > Ticket_list; typedef Ticket_list::Iterator Ticket_iterator; @@ -45,60 +46,82 @@ struct MDL_LOCK EXCLUSIVE, } type; /** The key of the object (data) being protected. */ - MDL_KEY key; + MDL_key key; /** List of granted tickets for this lock. */ Ticket_list granted; /** There can be several upgraders and active exclusive - belonging to the same context. + locks belonging to the same context. E.g. + in case of RENAME t1 to t2, t2 to t3, we attempt to + exclusively lock t2 twice. */ Ticket_list waiting; void *cached_object; mdl_cached_object_release_hook cached_object_release_hook; - MDL_LOCK(const MDL_KEY *mdl_key) - : type(SHARED), cached_object(0), cached_object_release_hook(0) - { - key.mdl_key_init(mdl_key); - granted.empty(); - waiting.empty(); - } - - bool is_empty() + bool is_empty() const { return (granted.is_empty() && waiting.is_empty()); } + + bool can_grant_lock(const MDL_context *requestor_ctx, + enum_mdl_type type, bool is_upgrade); + + inline static MDL_lock *create(const MDL_key *key); + inline static void destroy(MDL_lock *lock); +private: + MDL_lock(const MDL_key *key_arg) + : type(SHARED), + key(key_arg), + cached_object(NULL), + cached_object_release_hook(NULL) + { + } }; -pthread_mutex_t LOCK_mdl; -pthread_cond_t COND_mdl; -HASH mdl_locks; +static pthread_mutex_t LOCK_mdl; +static pthread_cond_t COND_mdl; +static HASH mdl_locks; /** - Structure implementing global metadata lock. The only types - of locks which are supported at the moment are shared and - intention exclusive locks. Note that the latter type of global - lock acquired automatically when one tries to acquire exclusive - or shared upgradable lock on particular object. + An implementation of the global metadata lock. The only + locking modes which are supported at the moment are SHARED and + INTENTION EXCLUSIVE. Note, that SHARED global metadata lock + is acquired automatically when one tries to acquire an EXCLUSIVE + or UPGRADABLE SHARED metadata lock on an individual object. */ -struct MDL_GLOBAL_LOCK +class MDL_global_lock { +public: uint waiting_shared; uint active_shared; uint active_intention_exclusive; -} global_lock; + + bool is_empty() const + { + return (waiting_shared == 0 && active_shared == 0 && + active_intention_exclusive == 0); + } + bool is_lock_type_compatible(enum_mdl_type type, bool is_upgrade) const; +}; -extern "C" uchar * +static MDL_global_lock global_lock; + + +extern "C" +{ +static uchar * mdl_locks_key(const uchar *record, size_t *length, my_bool not_used __attribute__((unused))) { - MDL_LOCK *entry=(MDL_LOCK*) record; - *length= entry->key.length(); - return (uchar*) entry->key.ptr(); + MDL_lock *lock=(MDL_lock*) record; + *length= lock->key.length(); + return (uchar*) lock->key.ptr(); } +} /* extern "C" */ /** @@ -116,17 +139,20 @@ mdl_locks_key(const uchar *record, size_t *length, statement, the design capitalizes on that to later save on look ups in the table definition cache. This leads to reduced contention overall and on LOCK_open in particular. - Please see the description of mdl_acquire_shared_lock() for details. + Please see the description of MDL_context::acquire_shared_lock() + for details. */ void mdl_init() { + DBUG_ASSERT(! mdl_initialized); + mdl_initialized= TRUE; pthread_mutex_init(&LOCK_mdl, NULL); pthread_cond_init(&COND_mdl, NULL); my_hash_init(&mdl_locks, &my_charset_bin, 16 /* FIXME */, 0, 0, mdl_locks_key, 0, 0); - global_lock.waiting_shared= global_lock.active_shared= 0; - global_lock.active_intention_exclusive= 0; + /* The global lock is zero-initialized by the loader. */ + DBUG_ASSERT(global_lock.is_empty()); } @@ -139,10 +165,15 @@ void mdl_init() void mdl_destroy() { - DBUG_ASSERT(!mdl_locks.records); - pthread_mutex_destroy(&LOCK_mdl); - pthread_cond_destroy(&COND_mdl); - my_hash_free(&mdl_locks); + if (mdl_initialized) + { + mdl_initialized= FALSE; + DBUG_ASSERT(!mdl_locks.records); + DBUG_ASSERT(global_lock.is_empty()); + pthread_mutex_destroy(&LOCK_mdl); + pthread_cond_destroy(&COND_mdl); + my_hash_free(&mdl_locks); + } } @@ -152,12 +183,19 @@ void mdl_destroy() This is to be called when a new server connection is created. */ -void mdl_context_init(MDL_CONTEXT *context, THD *thd) +void MDL_context::init(THD *thd_arg) { - context->requests.empty(); - context->tickets.empty(); - context->thd= thd; - context->has_global_shared_lock= FALSE; + m_has_global_shared_lock= FALSE; + m_thd= thd_arg; + /* + FIXME: In reset_n_backup_open_tables_state, + we abuse "init" as a reset, i.e. call it on an already + constructed non-empty object. This is why we can't + rely here on the default constructors of I_P_List + to empty the list. + */ + m_requests.empty(); + m_tickets.empty(); } @@ -173,11 +211,11 @@ void mdl_context_init(MDL_CONTEXT *context, THD *thd) that all tables are closed before a connection is destroyed. */ -void mdl_context_destroy(MDL_CONTEXT *context) +void MDL_context::destroy() { - DBUG_ASSERT(context->requests.is_empty()); - DBUG_ASSERT(context->tickets.is_empty()); - DBUG_ASSERT(!context->has_global_shared_lock); + DBUG_ASSERT(m_requests.is_empty()); + DBUG_ASSERT(m_tickets.is_empty()); + DBUG_ASSERT(! m_has_global_shared_lock); } @@ -191,12 +229,22 @@ void mdl_context_destroy(MDL_CONTEXT *context) tables open/metadata locks for HANDLERs afterwards. */ -void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) +void MDL_context::backup_and_reset(MDL_context *backup) { - backup->requests.empty(); - backup->tickets.empty(); - ctx->requests.swap(backup->requests); - ctx->tickets.swap(backup->tickets); + DBUG_ASSERT(backup->m_requests.is_empty()); + DBUG_ASSERT(backup->m_tickets.is_empty()); + + m_requests.swap(backup->m_requests); + m_tickets.swap(backup->m_tickets); + + backup->m_has_global_shared_lock= m_has_global_shared_lock; + /* + When the main context is swapped out, one can not take + the global shared lock, and one can not rely on it: + the functionality in this mode is reduced, since it exists as + a temporary hack to support ad-hoc opening of system tables. + */ + m_has_global_shared_lock= FALSE; } @@ -204,12 +252,15 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) Restore state of meta-data locking context from backup. */ -void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) +void MDL_context::restore_from_backup(MDL_context *backup) { - DBUG_ASSERT(ctx->requests.is_empty()); - DBUG_ASSERT(ctx->tickets.is_empty()); - ctx->requests.swap(backup->requests); - ctx->tickets.swap(backup->tickets); + DBUG_ASSERT(m_requests.is_empty()); + DBUG_ASSERT(m_tickets.is_empty()); + DBUG_ASSERT(m_has_global_shared_lock == FALSE); + + m_requests.swap(backup->m_requests); + m_tickets.swap(backup->m_tickets); + m_has_global_shared_lock= backup->m_has_global_shared_lock; } @@ -217,32 +268,37 @@ void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) Merge meta-data locks from one context into another. */ -void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) +void MDL_context::merge(MDL_context *src) { - MDL_LOCK_TICKET *ticket; - MDL_LOCK_REQUEST *lock_req; + MDL_ticket *ticket; + MDL_request *mdl_request; - DBUG_ASSERT(dst->thd == src->thd); + DBUG_ASSERT(m_thd == src->m_thd); - if (!src->requests.is_empty()) + if (!src->m_requests.is_empty()) { - MDL_CONTEXT::Request_iterator it(src->requests); - while ((lock_req= it++)) - dst->requests.push_front(lock_req); - src->requests.empty(); + Request_iterator it(src->m_requests); + while ((mdl_request= it++)) + m_requests.push_front(mdl_request); + src->m_requests.empty(); } - if (!src->tickets.is_empty()) + if (!src->m_tickets.is_empty()) { - MDL_CONTEXT::Ticket_iterator it(src->tickets); + Ticket_iterator it(src->m_tickets); while ((ticket= it++)) { - DBUG_ASSERT(ticket->ctx); - ticket->ctx= dst; - dst->tickets.push_front(ticket); + DBUG_ASSERT(ticket->m_ctx); + ticket->m_ctx= this; + m_tickets.push_front(ticket); } - src->tickets.empty(); + src->m_tickets.empty(); } + /* + MDL_context::merge() is a hack used in one place only: to open + an SQL handler. We never acquire the global shared lock there. + */ + DBUG_ASSERT(! src->m_has_global_shared_lock); } @@ -260,10 +316,9 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) The MDL subsystem does not own or manage memory of lock requests. Instead it assumes that the life time of every lock request (including - encompassed members db/name) encloses calls to mdl_request_add() - and mdl_request_remove() or mdl_request_remove_all(). + encompassed members db/name) encloses calls to MDL_context::add_request() + and MDL_context::remove_request() or MDL_context::remove_all_requests(). - @param lock_req Pointer to an MDL_LOCK_REQUEST object to initialize @param type Id of type of object to be locked @param db Name of database to which the object belongs @param name Name of of the object @@ -275,12 +330,13 @@ void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) they share the same name space in the SQL standard. */ -void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, - const char *db, const char *name) +void MDL_request::init(unsigned char type_arg, + const char *db_arg, + const char *name_arg) { - lock_req->key.mdl_key_init(type, db, name); - lock_req->type= MDL_SHARED; - lock_req->ticket= NULL; + key.mdl_key_init(type_arg, db_arg, name_arg); + type= MDL_SHARED; + ticket= NULL; } @@ -302,19 +358,18 @@ void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, @retval non-0 Pointer to an object representing a lock request */ -MDL_LOCK_REQUEST * -mdl_request_alloc(unsigned char type, const char *db, - const char *name, MEM_ROOT *root) +MDL_request * +MDL_request::create(unsigned char type, const char *db, + const char *name, MEM_ROOT *root) { - MDL_LOCK_REQUEST *lock_req; + MDL_request *mdl_request; - if (!(lock_req= (MDL_LOCK_REQUEST*) alloc_root(root, - sizeof(MDL_LOCK_REQUEST)))) + if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request)))) return NULL; - mdl_request_init(lock_req, type, db, name); + mdl_request->init(type, db, name); - return lock_req; + return mdl_request; } @@ -322,23 +377,24 @@ mdl_request_alloc(unsigned char type, const char *db, Add a lock request to the list of lock requests of the context. The procedure to acquire metadata locks is: - - allocate and initialize lock requests (mdl_request_alloc()) - - associate them with a context (mdl_request_add()) - - call mdl_acquire_shared_lock()/mdl_ticket_release() (maybe repeatedly). + - allocate and initialize lock requests + (MDL_request::create()) + - associate them with a context (MDL_context::add_request()) + - call MDL_context::acquire_shared_lock() and + MDL_context::release_lock() (maybe repeatedly). Associates a lock request with the given context. + There should be no more than one context per connection, to + avoid deadlocks. - @param context The MDL context to associate the lock with. - There should be no more than one context per - connection, to avoid deadlocks. - @param lock_req The lock request to be added. + @param mdl_request The lock request to be added. */ -void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) +void MDL_context::add_request(MDL_request *mdl_request) { - DBUG_ENTER("mdl_request_add"); - DBUG_ASSERT(lock_req->ticket == NULL); - context->requests.push_front(lock_req); + DBUG_ENTER("MDL_context::add_request"); + DBUG_ASSERT(mdl_request->ticket == NULL); + m_requests.push_front(mdl_request); DBUG_VOID_RETURN; } @@ -348,8 +404,7 @@ void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) Disassociates a lock request from the given context. - @param context The MDL context to remove the lock from. - @param lock_req The lock request to be removed. + @param mdl_request The lock request to be removed. @pre The lock request being removed should correspond to a ticket that was released or was not acquired. @@ -358,13 +413,13 @@ void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) (i.e. sets type to MDL_SHARED). */ -void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) +void MDL_context::remove_request(MDL_request *mdl_request) { - DBUG_ENTER("mdl_request_remove"); + DBUG_ENTER("MDL_context::remove_request"); /* Reset lock request back to its initial state. */ - lock_req->type= MDL_SHARED; - lock_req->ticket= NULL; - context->requests.remove(lock_req); + mdl_request->type= MDL_SHARED; + mdl_request->ticket= NULL; + m_requests.remove(mdl_request); DBUG_VOID_RETURN; } @@ -374,75 +429,61 @@ void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) Disassociates lock requests from the context. Also resets lock requests back to their initial state (i.e. MDL_SHARED). - - @param context Context to be cleared. */ -void mdl_request_remove_all(MDL_CONTEXT *context) +void MDL_context::remove_all_requests() { - MDL_LOCK_REQUEST *lock_req; - MDL_CONTEXT::Request_iterator it(context->requests); - while ((lock_req= it++)) + MDL_request *mdl_request; + Request_iterator it(m_requests); + while ((mdl_request= it++)) { /* Reset lock request back to its initial state. */ - lock_req->type= MDL_SHARED; - lock_req->ticket= NULL; + mdl_request->type= MDL_SHARED; + mdl_request->ticket= NULL; } - context->requests.empty(); + m_requests.empty(); } /** - Auxiliary functions needed for creation/destruction of MDL_LOCK objects. + Auxiliary functions needed for creation/destruction of MDL_lock objects. @todo This naive implementation should be replaced with one that saves on memory allocation by reusing released objects. */ -static MDL_LOCK* alloc_lock_object(const MDL_KEY *mdl_key) +inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key) { - return new MDL_LOCK(mdl_key); + return new MDL_lock(mdl_key); } -static void free_lock_object(MDL_LOCK *lock) +void MDL_lock::destroy(MDL_lock *lock) { delete lock; } /** - Auxiliary functions needed for creation/destruction of MDL_LOCK_TICKET + Auxiliary functions needed for creation/destruction of MDL_ticket objects. @todo This naive implementation should be replaced with one that saves on memory allocation by reusing released objects. */ -static MDL_LOCK_TICKET* alloc_ticket_object(MDL_CONTEXT *context) +MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg) { - MDL_LOCK_TICKET *ticket= new MDL_LOCK_TICKET; - return ticket; + return new MDL_ticket(ctx_arg, type_arg); } -static void free_ticket_object(MDL_LOCK_TICKET *ticket) +void MDL_ticket::destroy(MDL_ticket *ticket) { delete ticket; } -/** - Helper functions which simplifies writing various checks and asserts. -*/ - -template -static inline bool is_shared(T *lock_data) -{ - return (lock_data->type < MDL_EXCLUSIVE); -} - - /** Helper functions and macros to be used for killable waiting in metadata locking subsystem. @@ -458,7 +499,7 @@ static inline bool is_shared(T *lock_data) #define MDL_ENTER_COND(A, B) mdl_enter_cond(A, B, __func__, __FILE__, __LINE__) -static inline const char* mdl_enter_cond(MDL_CONTEXT *context, +static inline const char *mdl_enter_cond(THD *thd, st_my_thread_var *mysys_var, const char *calling_func, const char *calling_file, @@ -469,13 +510,15 @@ static inline const char* mdl_enter_cond(MDL_CONTEXT *context, mysys_var->current_mutex= &LOCK_mdl; mysys_var->current_cond= &COND_mdl; - return set_thd_proc_info(context->thd, "Waiting for table", + DEBUG_SYNC(thd, "mdl_enter_cond"); + + return set_thd_proc_info(thd, "Waiting for table", calling_func, calling_file, calling_line); } #define MDL_EXIT_COND(A, B, C) mdl_exit_cond(A, B, C, __func__, __FILE__, __LINE__) -static inline void mdl_exit_cond(MDL_CONTEXT *context, +static inline void mdl_exit_cond(THD *thd, st_my_thread_var *mysys_var, const char* old_msg, const char *calling_func, @@ -490,7 +533,9 @@ static inline void mdl_exit_cond(MDL_CONTEXT *context, mysys_var->current_cond= 0; pthread_mutex_unlock(&mysys_var->mutex); - (void) set_thd_proc_info(context->thd, old_msg, calling_func, + DEBUG_SYNC(thd, "mdl_exit_cond"); + + (void) set_thd_proc_info(thd, old_msg, calling_func, calling_file, calling_line); } @@ -503,8 +548,8 @@ static inline void mdl_exit_cond(MDL_CONTEXT *context, request, implying a form of lock on the global metadata, is compatible with the current state of the global metadata lock. - @param lock_req Request for lock on an individual object, implying a - certain kind of global metadata lock. + @param mdl_request Request for lock on an individual object, implying a + certain kind of global metadata lock. @retval TRUE - Lock request can be satisfied @retval FALSE - There is some conflicting lock @@ -533,7 +578,9 @@ static inline void mdl_exit_cond(MDL_CONTEXT *context, type of locks we don't even have any accounting for them. */ -static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) +bool +MDL_global_lock::is_lock_type_compatible(enum_mdl_type type, + bool is_upgrade) const { switch (type) { @@ -542,7 +589,7 @@ static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) return TRUE; break; case MDL_SHARED_UPGRADABLE: - if (global_lock.active_shared || global_lock.waiting_shared) + if (active_shared || waiting_shared) { /* We are going to obtain intention exclusive global lock and @@ -562,13 +609,12 @@ static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) There should be no conflicting global locks since for each upgradable shared lock we obtain intention exclusive global lock first. */ - DBUG_ASSERT(global_lock.active_shared == 0 && - global_lock.active_intention_exclusive); + DBUG_ASSERT(active_shared == 0 && active_intention_exclusive); return TRUE; } else { - if (global_lock.active_shared || global_lock.waiting_shared) + if (active_shared || waiting_shared) { /* We are going to obtain intention exclusive global lock and @@ -590,15 +636,15 @@ static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) /** Check if request for the lock can be satisfied given current state of lock. - @param lock Lock. - @param lock_req Request for lock. + @param lock Lock. + @param mdl_request Request for lock. @retval TRUE Lock request can be satisfied @retval FALSE There is some conflicting lock. This function defines the following compatibility matrix for metadata locks: - | Satisfied or pending requests which we have in MDL_LOCK + | Satisfied or pending requests which we have in MDL_lock ----------------+--------------------------------------------------------- Current request | Active-S Pending-X Active-X Act-S-pend-upgrade-to-X ----------------+--------------------------------------------------------- @@ -617,22 +663,23 @@ static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) being upgraded. */ -static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, - enum_mdl_type type, bool is_upgrade) +bool +MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_arg, + bool is_upgrade) { bool can_grant= FALSE; - switch (type) { + switch (type_arg) { case MDL_SHARED: case MDL_SHARED_UPGRADABLE: case MDL_SHARED_HIGH_PRIO: - if (lock->type == MDL_LOCK::SHARED) + if (type == MDL_lock::SHARED) { /* Pending exclusive locks have higher priority over shared locks. */ - if (lock->waiting.is_empty() || type == MDL_SHARED_HIGH_PRIO) + if (waiting.is_empty() || type_arg == MDL_SHARED_HIGH_PRIO) can_grant= TRUE; } - else if (lock->granted.head()->ctx == ctx) + else if (granted.head()->get_ctx() == requestor_ctx) { /* When exclusive lock comes from the same context we can satisfy our @@ -646,33 +693,33 @@ static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, if (is_upgrade) { /* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. */ - MDL_LOCK_TICKET *conf_lock_ticket; - MDL_LOCK::Ticket_iterator it(lock->granted); + MDL_ticket *conflicting_ticket; + MDL_lock::Ticket_iterator it(granted); /* There should be no active exclusive locks since we own shared lock on the object. */ - DBUG_ASSERT(lock->type == MDL_LOCK::SHARED); + DBUG_ASSERT(type == MDL_lock::SHARED); - while ((conf_lock_ticket= it++)) + while ((conflicting_ticket= it++)) { /* When upgrading shared lock to exclusive one we can have other shared locks for the same object in the same context, e.g. in case when several instances of TABLE are open. */ - if (conf_lock_ticket->ctx != ctx) + if (conflicting_ticket->get_ctx() != requestor_ctx) break; } /* Grant lock if there are no conflicting shared locks. */ - if (conf_lock_ticket == NULL) + if (conflicting_ticket == NULL) can_grant= TRUE; break; } - else if (lock->type == MDL_LOCK::SHARED) + else if (type == MDL_lock::SHARED) { - can_grant= lock->granted.is_empty(); + can_grant= granted.is_empty(); } break; default: @@ -686,23 +733,23 @@ static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, Check whether the context already holds a compatible lock ticket on a object. Only shared locks can be recursive. - @param lock_req Lock request object for lock to be acquired + @param mdl_request Lock request object for lock to be acquired @return A pointer to the lock ticket for the object or NULL otherwise. */ -static MDL_LOCK_TICKET * -mdl_context_find_ticket(MDL_CONTEXT *ctx, MDL_LOCK_REQUEST *lock_req) +MDL_ticket * +MDL_context::find_ticket(MDL_request *mdl_request) { - MDL_LOCK_TICKET *ticket; - MDL_CONTEXT::Ticket_iterator it(ctx->tickets); + MDL_ticket *ticket; + Ticket_iterator it(m_tickets); - DBUG_ASSERT(is_shared(lock_req)); + DBUG_ASSERT(mdl_request->is_shared()); while ((ticket= it++)) { - if (lock_req->type == ticket->type && - lock_req->key.is_equal(&ticket->lock->key)) + if (mdl_request->type == ticket->m_type && + mdl_request->key.is_equal(&ticket->m_lock->key)) break; } @@ -715,7 +762,7 @@ mdl_context_find_ticket(MDL_CONTEXT *ctx, MDL_LOCK_REQUEST *lock_req) Unlike exclusive locks, shared locks are acquired one by one. This is interface is chosen to simplify introduction of - the new locking API to the system. mdl_acquire_shared_lock() + the new locking API to the system. MDL_context::acquire_shared_lock() is currently used from open_table(), and there we have only one table to work with. @@ -723,32 +770,31 @@ mdl_context_find_ticket(MDL_CONTEXT *ctx, MDL_LOCK_REQUEST *lock_req) This function must be called after the lock is added to a context. - @param context [in] Context containing request for lock - @param lock_req [in] Lock request object for lock to be acquired + @param mdl_request [in] Lock request object for lock to be acquired @param retry [out] Indicates that conflicting lock exists and another attempt should be made after releasing all current locks and waiting for conflicting lock go away - (using mdl_wait_for_locks()). + (using MDL_context::wait_for_locks()). @retval FALSE Success. - @retval TRUE Failure. Either error occured or conflicting lock exists. + @retval TRUE Failure. Either error occurred or conflicting lock exists. In the latter case "retry" parameter is set to TRUE. */ -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, - bool *retry) +bool +MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry) { - MDL_LOCK *lock; - MDL_KEY *key= &lock_req->key; - MDL_LOCK_TICKET *ticket; + MDL_lock *lock; + MDL_key *key= &mdl_request->key; + MDL_ticket *ticket; *retry= FALSE; - DBUG_ASSERT(is_shared(lock_req) && lock_req->ticket == NULL); + DBUG_ASSERT(mdl_request->is_shared() && mdl_request->ticket == NULL); safe_mutex_assert_not_owner(&LOCK_open); - if (context->has_global_shared_lock && - lock_req->type == MDL_SHARED_UPGRADABLE) + if (m_has_global_shared_lock && + mdl_request->type == MDL_SHARED_UPGRADABLE) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; @@ -758,52 +804,50 @@ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, Check whether the context already holds a shared lock on the object, and if so, grant the request. */ - if ((ticket= mdl_context_find_ticket(context, lock_req))) + if ((ticket= find_ticket(mdl_request))) { - DBUG_ASSERT(ticket->state == MDL_ACQUIRED); - lock_req->ticket= ticket; + DBUG_ASSERT(ticket->m_state == MDL_ACQUIRED); + mdl_request->ticket= ticket; return FALSE; } pthread_mutex_lock(&LOCK_mdl); - if (!can_grant_global_lock(lock_req->type, FALSE)) + if (!global_lock.is_lock_type_compatible(mdl_request->type, FALSE)) { pthread_mutex_unlock(&LOCK_mdl); *retry= TRUE; return TRUE; } - if (!(ticket= alloc_ticket_object(context))) + if (!(ticket= MDL_ticket::create(this, mdl_request->type))) { pthread_mutex_unlock(&LOCK_mdl); return TRUE; } - if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + if (!(lock= (MDL_lock*) my_hash_search(&mdl_locks, key->ptr(), key->length()))) { - /* Default lock type is MDL_LOCK::SHARED */ - lock= alloc_lock_object(key); + /* Default lock type is MDL_lock::SHARED */ + lock= MDL_lock::create(key); if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - free_lock_object(lock); - free_ticket_object(ticket); + MDL_lock::destroy(lock); + MDL_ticket::destroy(ticket); pthread_mutex_unlock(&LOCK_mdl); return TRUE; } } - if (can_grant_lock(context, lock, lock_req->type, FALSE)) + if (lock->can_grant_lock(this, mdl_request->type, FALSE)) { + mdl_request->ticket= ticket; lock->granted.push_front(ticket); - context->tickets.push_front(ticket); - ticket->state= MDL_ACQUIRED; - lock_req->ticket= ticket; - ticket->lock= lock; - ticket->type= lock_req->type; - ticket->ctx= context; - if (lock_req->type == MDL_SHARED_UPGRADABLE) + m_tickets.push_front(ticket); + ticket->m_state= MDL_ACQUIRED; + ticket->m_lock= lock; + if (mdl_request->type == MDL_SHARED_UPGRADABLE) global_lock.active_intention_exclusive++; } else @@ -811,7 +855,7 @@ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, /* We can't get here if we allocated a new lock. */ DBUG_ASSERT(! lock->is_empty()); *retry= TRUE; - free_ticket_object(ticket); + MDL_ticket::destroy(ticket); } pthread_mutex_unlock(&LOCK_mdl); @@ -820,24 +864,25 @@ bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, } -static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); - - /** - Notify a thread holding a shared metadata lock of a pending exclusive lock. + Notify a thread holding a shared metadata lock which + conflicts with a pending exclusive lock. @param thd Current thread context - @param conf_lock_ticket Conflicting metadata lock + @param conflicting_ticket Conflicting metadata lock @retval TRUE A thread was woken up @retval FALSE Lock is not a shared one or no thread was woken up */ -static bool notify_shared_lock(THD *thd, MDL_LOCK_TICKET *conf_lock_ticket) +static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) { bool woke= FALSE; - if (conf_lock_ticket->type != MDL_EXCLUSIVE) - woke= mysql_notify_thread_having_shared_lock(thd, conf_lock_ticket->ctx->thd); + if (conflicting_ticket->is_shared()) + { + THD *conflicting_thd= conflicting_ticket->get_ctx()->get_thd(); + woke= mysql_notify_thread_having_shared_lock(thd, conflicting_thd); + } return woke; } @@ -850,26 +895,26 @@ static bool notify_shared_lock(THD *thd, MDL_LOCK_TICKET *conf_lock_ticket) This is a replacement of lock_table_names(). It is used in RENAME, DROP and other DDL SQL statements. - @param context A context containing requests for exclusive locks - The context may not have other lock requests. + @note The MDL context may not have non-exclusive lock requests + or acquired locks. @retval FALSE Success @retval TRUE Failure */ -bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) +bool MDL_context::acquire_exclusive_locks() { - MDL_LOCK *lock; + MDL_lock *lock; bool signalled= FALSE; const char *old_msg; - MDL_LOCK_REQUEST *lock_req; - MDL_LOCK_TICKET *ticket; + MDL_request *mdl_request; + MDL_ticket *ticket; st_my_thread_var *mysys_var= my_thread_var; - MDL_CONTEXT::Request_iterator it(context->requests); + Request_iterator it(m_requests); safe_mutex_assert_not_owner(&LOCK_open); - if (context->has_global_shared_lock) + if (m_has_global_shared_lock) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; @@ -877,79 +922,76 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) pthread_mutex_lock(&LOCK_mdl); - old_msg= MDL_ENTER_COND(context, mysys_var); + old_msg= MDL_ENTER_COND(m_thd, mysys_var); - while ((lock_req= it++)) + while ((mdl_request= it++)) { - MDL_KEY *key= &lock_req->key; - DBUG_ASSERT(lock_req->type == MDL_EXCLUSIVE && - lock_req->ticket == NULL); + MDL_key *key= &mdl_request->key; + DBUG_ASSERT(mdl_request->type == MDL_EXCLUSIVE && + mdl_request->ticket == NULL); /* Early allocation: ticket is used as a shortcut to the lock. */ - if (!(ticket= alloc_ticket_object(context))) + if (!(ticket= MDL_ticket::create(this, mdl_request->type))) goto err; - if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + if (!(lock= (MDL_lock*) my_hash_search(&mdl_locks, key->ptr(), key->length()))) { - lock= alloc_lock_object(key); + lock= MDL_lock::create(key); if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - free_ticket_object(ticket); - free_lock_object(lock); + MDL_ticket::destroy(ticket); + MDL_lock::destroy(lock); goto err; } } - lock_req->ticket= ticket; - ticket->state= MDL_PENDING; - ticket->ctx= context; - ticket->lock= lock; - ticket->type= lock_req->type; + mdl_request->ticket= ticket; lock->waiting.push_front(ticket); + ticket->m_lock= lock; } while (1) { it.rewind(); - while ((lock_req= it++)) + while ((mdl_request= it++)) { - lock= lock_req->ticket->lock; + lock= mdl_request->ticket->m_lock; - if (!can_grant_global_lock(lock_req->type, FALSE)) + if (!global_lock.is_lock_type_compatible(mdl_request->type, FALSE)) { /* - There is an active or pending global shared lock so we have - to wait until it goes away. + Someone owns or wants to acquire the global shared lock so + we have to wait until he goes away. */ signalled= TRUE; break; } - else if (!can_grant_lock(context, lock, lock_req->type, FALSE)) + else if (!lock->can_grant_lock(this, mdl_request->type, FALSE)) { - MDL_LOCK_TICKET *conf_lock_ticket; - MDL_LOCK::Ticket_iterator it(lock->granted); + MDL_ticket *conflicting_ticket; + MDL_lock::Ticket_iterator it(lock->granted); - signalled= (lock->type == MDL_LOCK::EXCLUSIVE); + signalled= (lock->type == MDL_lock::EXCLUSIVE); - while ((conf_lock_ticket= it++)) - signalled|= notify_shared_lock(context->thd, conf_lock_ticket); + while ((conflicting_ticket= it++)) + signalled|= notify_shared_lock(m_thd, conflicting_ticket); break; } } - if (!lock_req) + if (!mdl_request) break; /* There is a shared or exclusive lock on the object. */ - DEBUG_SYNC(context->thd, "mdl_acquire_exclusive_locks_wait"); + DEBUG_SYNC(m_thd, "mdl_acquire_exclusive_locks_wait"); if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); else { /* - Another thread obtained shared MDL-lock on some table but + Another thread obtained a shared MDL lock on some table but has not yet opened it and/or tried to obtain data lock on it. In this case we need to wait until this happens and try to abort this thread once again. @@ -962,22 +1004,22 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) goto err; } it.rewind(); - while ((lock_req= it++)) + while ((mdl_request= it++)) { global_lock.active_intention_exclusive++; - ticket= lock_req->ticket; - lock= ticket->lock; - lock->type= MDL_LOCK::EXCLUSIVE; + ticket= mdl_request->ticket; + lock= ticket->m_lock; + lock->type= MDL_lock::EXCLUSIVE; lock->waiting.remove(ticket); lock->granted.push_front(ticket); - context->tickets.push_front(ticket); - ticket->state= MDL_ACQUIRED; + m_tickets.push_front(ticket); + ticket->m_state= MDL_ACQUIRED; if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= NULL; } /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(m_thd, mysys_var, old_msg); return FALSE; err: @@ -986,24 +1028,24 @@ err: Ignore those lock requests which were not made MDL_PENDING. */ it.rewind(); - while ((lock_req= it++) && lock_req->ticket) + while ((mdl_request= it++) && mdl_request->ticket) { - ticket= lock_req->ticket; - DBUG_ASSERT(ticket->state == MDL_PENDING); - lock= ticket->lock; - free_ticket_object(ticket); + ticket= mdl_request->ticket; + DBUG_ASSERT(ticket->m_state == MDL_PENDING); + lock= ticket->m_lock; lock->waiting.remove(ticket); + MDL_ticket::destroy(ticket); /* Reset lock request back to its initial state. */ - lock_req->ticket= NULL; + mdl_request->ticket= NULL; if (lock->is_empty()) { my_hash_delete(&mdl_locks, (uchar *)lock); - free_lock_object(lock); + MDL_lock::destroy(lock); } } /* May be some pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(m_thd, mysys_var, old_msg); return TRUE; } @@ -1014,9 +1056,6 @@ err: Used in ALTER TABLE, when a copy of the table with the new definition has been constructed. - @param context Context to which shared lock belongs - @param ticket Ticket for shared lock to be upgraded - @note In case of failure to upgrade lock (e.g. because upgrader was killed) leaves lock in its original state (locked in shared mode). @@ -1030,46 +1069,46 @@ err: @retval TRUE Failure (thread was killed) */ -bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_TICKET *ticket) +bool +MDL_ticket::upgrade_shared_lock_to_exclusive() { - MDL_LOCK *lock= ticket->lock; const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; + THD *thd= m_ctx->get_thd(); - DBUG_ENTER("mdl_upgrade_shared_lock_to_exclusive"); - DEBUG_SYNC(context->thd, "mdl_upgrade_shared_lock_to_exclusive"); + DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive"); + DEBUG_SYNC(thd, "mdl_upgrade_shared_lock_to_exclusive"); safe_mutex_assert_not_owner(&LOCK_open); /* Allow this function to be called twice for the same lock request. */ - if (ticket->type == MDL_EXCLUSIVE) + if (m_type == MDL_EXCLUSIVE) DBUG_RETURN(FALSE); pthread_mutex_lock(&LOCK_mdl); - old_msg= MDL_ENTER_COND(context, mysys_var); + old_msg= MDL_ENTER_COND(thd, mysys_var); /* Since we should have already acquired an intention exclusive global lock this call is only enforcing asserts. */ - DBUG_ASSERT(can_grant_global_lock(MDL_EXCLUSIVE, TRUE)); + DBUG_ASSERT(global_lock.is_lock_type_compatible(MDL_EXCLUSIVE, TRUE)); while (1) { - if (can_grant_lock(context, lock, MDL_EXCLUSIVE, TRUE)) + if (m_lock->can_grant_lock(m_ctx, MDL_EXCLUSIVE, TRUE)) break; bool signalled= FALSE; - MDL_LOCK_TICKET *conf_lock_ticket; - MDL_LOCK::Ticket_iterator it(lock->granted); + MDL_ticket *conflicting_ticket; + MDL_lock::Ticket_iterator it(m_lock->granted); - while ((conf_lock_ticket= it++)) + while ((conflicting_ticket= it++)) { - if (conf_lock_ticket->ctx != context) - signalled|= notify_shared_lock(context->thd, conf_lock_ticket); + if (conflicting_ticket->m_ctx != m_ctx) + signalled|= notify_shared_lock(thd, conflicting_ticket); } if (signalled) @@ -1077,7 +1116,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, else { /* - Another thread obtained shared MDL-lock on some table but + Another thread obtained a shared MDL lock on some table but has not yet opened it and/or tried to obtain data lock on it. In this case we need to wait until this happens and try to abort this thread once again. @@ -1091,20 +1130,20 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, { /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(thd, mysys_var, old_msg); DBUG_RETURN(TRUE); } } - lock->type= MDL_LOCK::EXCLUSIVE; + m_lock->type= MDL_lock::EXCLUSIVE; /* Set the new type of lock in the ticket. */ - ticket->type= MDL_EXCLUSIVE; - if (lock->cached_object) - (*lock->cached_object_release_hook)(lock->cached_object); - lock->cached_object= 0; + m_type= MDL_EXCLUSIVE; + if (m_lock->cached_object) + (*m_lock->cached_object_release_hook)(m_lock->cached_object); + m_lock->cached_object= 0; /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(thd, mysys_var, old_msg); DBUG_RETURN(FALSE); } @@ -1121,28 +1160,27 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, on the table to be created. In this statement we don't want to block and wait for the lock if the table already exists. - @param context [in] The context containing the lock request - @param lock_req [in] The lock request - @param conflict [out] Indicates that conflicting lock exists + @param mdl_request [in] The lock request + @param conflict [out] Indicates that conflicting lock exists @retval TRUE Failure either conflicting lock exists or some error occured (probably OOM). @retval FALSE Success, lock was acquired. FIXME: Compared to lock_table_name_if_not_cached() - it gives sligthly more false negatives. + it gives slightly more false negatives. */ -bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_REQUEST *lock_req, - bool *conflict) +bool +MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request, + bool *conflict) { - MDL_LOCK *lock; - MDL_LOCK_TICKET *ticket; - MDL_KEY *key= &lock_req->key; + MDL_lock *lock; + MDL_ticket *ticket; + MDL_key *key= &mdl_request->key; - DBUG_ASSERT(lock_req->type == MDL_EXCLUSIVE && - lock_req->ticket == NULL); + DBUG_ASSERT(mdl_request->type == MDL_EXCLUSIVE && + mdl_request->ticket == NULL); safe_mutex_assert_not_owner(&LOCK_open); @@ -1150,25 +1188,23 @@ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, pthread_mutex_lock(&LOCK_mdl); - if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + if (!(lock= (MDL_lock*) my_hash_search(&mdl_locks, key->ptr(), key->length()))) { - ticket= alloc_ticket_object(context); - lock= alloc_lock_object(key); + ticket= MDL_ticket::create(this, mdl_request->type); + lock= MDL_lock::create(key); if (!ticket || !lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - free_ticket_object(ticket); - free_lock_object(lock); + MDL_ticket::destroy(ticket); + MDL_lock::destroy(lock); goto err; } - lock->type= MDL_LOCK::EXCLUSIVE; + mdl_request->ticket= ticket; + lock->type= MDL_lock::EXCLUSIVE; lock->granted.push_front(ticket); - context->tickets.push_front(ticket); - ticket->state= MDL_ACQUIRED; - lock_req->ticket= ticket; - ticket->ctx= context; - ticket->lock= lock; - ticket->type= lock_req->type; + m_tickets.push_front(ticket); + ticket->m_state= MDL_ACQUIRED; + ticket->m_lock= lock; global_lock.active_intention_exclusive++; pthread_mutex_unlock(&LOCK_mdl); return FALSE; @@ -1184,29 +1220,27 @@ err: /** - Acquire global shared metadata lock. + Acquire the global shared metadata lock. Holding this lock will block all requests for exclusive locks and shared locks which can be potentially upgraded to exclusive. - @param context Current metadata locking context. - @retval FALSE Success -- the lock was granted. @retval TRUE Failure -- our thread was killed. */ -bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) +bool MDL_context::acquire_global_shared_lock() { st_my_thread_var *mysys_var= my_thread_var; const char *old_msg; safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(!context->has_global_shared_lock); + DBUG_ASSERT(!m_has_global_shared_lock); pthread_mutex_lock(&LOCK_mdl); global_lock.waiting_shared++; - old_msg= MDL_ENTER_COND(context, mysys_var); + old_msg= MDL_ENTER_COND(m_thd, mysys_var); while (!mysys_var->abort && global_lock.active_intention_exclusive) pthread_cond_wait(&COND_mdl, &LOCK_mdl); @@ -1215,13 +1249,13 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) if (mysys_var->abort) { /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(m_thd, mysys_var, old_msg); return TRUE; } global_lock.active_shared++; - context->has_global_shared_lock= TRUE; + m_has_global_shared_lock= TRUE; /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(m_thd, mysys_var, old_msg); return FALSE; } @@ -1235,17 +1269,16 @@ bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context) Does not acquire the locks! - @param context Context with which lock requests are associated. - @retval FALSE Success. One can try to obtain metadata locks. @retval TRUE Failure (thread was killed) */ -bool mdl_wait_for_locks(MDL_CONTEXT *context) +bool +MDL_context::wait_for_locks() { - MDL_LOCK *lock; - MDL_LOCK_REQUEST *lock_req; - MDL_CONTEXT::Request_iterator it(context->requests); + MDL_lock *lock; + MDL_request *mdl_request; + Request_iterator it(m_requests); const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; @@ -1263,34 +1296,34 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) TODO: investigate situations in which we need to broadcast on COND_mdl because of above scenario. */ - mysql_ha_flush(context->thd); + mysql_ha_flush(m_thd); pthread_mutex_lock(&LOCK_mdl); - old_msg= MDL_ENTER_COND(context, mysys_var); + old_msg= MDL_ENTER_COND(m_thd, mysys_var); it.rewind(); - while ((lock_req= it++)) + while ((mdl_request= it++)) { - MDL_KEY *key= &lock_req->key; - DBUG_ASSERT(lock_req->ticket == NULL); - if (!can_grant_global_lock(lock_req->type, FALSE)) + MDL_key *key= &mdl_request->key; + DBUG_ASSERT(mdl_request->ticket == NULL); + if (!global_lock.is_lock_type_compatible(mdl_request->type, FALSE)) break; /* To avoid starvation we don't wait if we have a conflict against request for MDL_EXCLUSIVE lock. */ - if (is_shared(lock_req) && - (lock= (MDL_LOCK*) my_hash_search(&mdl_locks, key->ptr(), + if (mdl_request->is_shared() && + (lock= (MDL_lock*) my_hash_search(&mdl_locks, key->ptr(), key->length())) && - !can_grant_lock(context, lock, lock_req->type, FALSE)) + !lock->can_grant_lock(this, mdl_request->type, FALSE)) break; } - if (!lock_req) + if (!mdl_request) { pthread_mutex_unlock(&LOCK_mdl); break; } pthread_cond_wait(&COND_mdl, &LOCK_mdl); /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ - MDL_EXIT_COND(context, mysys_var, old_msg); + MDL_EXIT_COND(m_thd, mysys_var, old_msg); } return mysys_var->abort; } @@ -1301,18 +1334,18 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) ownership of which is represented by a lock ticket object. */ -static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) +void MDL_context::release_ticket(MDL_ticket *ticket) { - MDL_LOCK *lock= ticket->lock; + MDL_lock *lock= ticket->m_lock; DBUG_ENTER("release_ticket"); DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(), lock->key.table_name())); safe_mutex_assert_owner(&LOCK_mdl); - context->tickets.remove(ticket); + m_tickets.remove(ticket); - switch (ticket->type) + switch (ticket->m_type) { case MDL_SHARED_UPGRADABLE: global_lock.active_intention_exclusive--; @@ -1322,7 +1355,7 @@ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) lock->granted.remove(ticket); break; case MDL_EXCLUSIVE: - lock->type= MDL_LOCK::SHARED; + lock->type= MDL_lock::SHARED; lock->granted.remove(ticket); global_lock.active_intention_exclusive--; break; @@ -1330,7 +1363,7 @@ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) DBUG_ASSERT(0); } - free_ticket_object(ticket); + MDL_ticket::destroy(ticket); if (lock->is_empty()) { @@ -1339,7 +1372,7 @@ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) lock->cached_object)); if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); - free_lock_object(lock); + MDL_lock::destroy(lock); } DBUG_VOID_RETURN; @@ -1353,42 +1386,39 @@ static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) This function is used to back off in case of a lock conflict. It is also used to release shared locks in the end of an SQL statement. - - @param context The context with which the locks to be released - are associated. */ -void mdl_ticket_release_all(MDL_CONTEXT *context) +void MDL_context::release_all_locks() { - MDL_LOCK_TICKET *ticket; - MDL_CONTEXT::Ticket_iterator it(context->tickets); - DBUG_ENTER("mdl_ticket_release_all"); + MDL_ticket *ticket; + Ticket_iterator it(m_tickets); + DBUG_ENTER("MDL_context::release_all_locks"); safe_mutex_assert_not_owner(&LOCK_open); /* Detach lock tickets from the requests for back off. */ { - MDL_LOCK_REQUEST *lock_req; - MDL_CONTEXT::Request_iterator it(context->requests); + MDL_request *mdl_request; + Request_iterator it(m_requests); - while ((lock_req= it++)) - lock_req->ticket= NULL; + while ((mdl_request= it++)) + mdl_request->ticket= NULL; } - if (context->tickets.is_empty()) + if (m_tickets.is_empty()) DBUG_VOID_RETURN; pthread_mutex_lock(&LOCK_mdl); while ((ticket= it++)) { DBUG_PRINT("info", ("found lock to release ticket=%p", ticket)); - release_ticket(context, ticket); + release_ticket(ticket); } /* Inefficient but will do for a while */ pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); - context->tickets.empty(); + m_tickets.empty(); DBUG_VOID_RETURN; } @@ -1397,17 +1427,16 @@ void mdl_ticket_release_all(MDL_CONTEXT *context) /** Release a lock. - @param context Context containing lock in question @param ticket Lock to be released */ -void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) +void MDL_context::release_lock(MDL_ticket *ticket) { - DBUG_ASSERT(context == ticket->ctx); + DBUG_ASSERT(this == ticket->m_ctx); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - release_ticket(context, ticket); + release_ticket(ticket); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1417,91 +1446,76 @@ void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) Release all locks in the context which correspond to the same name/ object as this lock request, remove lock requests from the context. - @param context Context containing locks in question @param ticket One of the locks for the name/object for which all locks should be released. */ -void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, - MDL_LOCK_TICKET *ticket) +void MDL_context::release_all_locks_for_name(MDL_ticket *name) { - MDL_LOCK *lock; - - /* - We can use MDL_LOCK_TICKET::lock here to identify other locks for the same - object since even though MDL_LOCK object might be reused for different - lock after the first lock for this object have been released we can't - have references to this other MDL_LOCK object in this context. - */ - lock= ticket->lock; + /* Use MDL_ticket::lock to identify other locks for the same object. */ + MDL_lock *lock= name->m_lock; /* Remove matching lock requests from the context. */ - MDL_LOCK_REQUEST *lock_req; - MDL_CONTEXT::Request_iterator it_lock_req(context->requests); + MDL_request *mdl_request; + Request_iterator it_mdl_request(m_requests); - while ((lock_req= it_lock_req++)) + while ((mdl_request= it_mdl_request++)) { - DBUG_ASSERT(lock_req->ticket && lock_req->ticket->state == MDL_ACQUIRED); - if (lock_req->ticket->lock == lock) - mdl_request_remove(context, lock_req); + DBUG_ASSERT(mdl_request->ticket && + mdl_request->ticket->m_state == MDL_ACQUIRED); + + if (mdl_request->ticket->m_lock == lock) + remove_request(mdl_request); } /* Remove matching lock tickets from the context. */ - MDL_LOCK_TICKET *lock_tkt; - MDL_CONTEXT::Ticket_iterator it_lock_tkt(context->tickets); + MDL_ticket *ticket; + Ticket_iterator it_ticket(m_tickets); - while ((lock_tkt= it_lock_tkt++)) + while ((ticket= it_ticket++)) { - DBUG_ASSERT(lock_tkt->state == MDL_ACQUIRED); - if (lock_tkt->lock == lock) - mdl_ticket_release(context, lock_tkt); + DBUG_ASSERT(ticket->m_state == MDL_ACQUIRED); + /* + We rarely have more than one ticket in this loop, + let's not bother saving on pthread_cond_broadcast(). + */ + if (ticket->m_lock == lock) + release_lock(ticket); } } /** Downgrade an exclusive lock to shared metadata lock. - - @param context A context to which exclusive lock belongs - @param ticket Ticket for exclusive lock to be downgraded */ -void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_TICKET *ticket) +void MDL_ticket::downgrade_exclusive_lock() { - MDL_LOCK *lock; - - DBUG_ASSERT(context == ticket->ctx); - safe_mutex_assert_not_owner(&LOCK_open); - if (is_shared(ticket)) + if (is_shared()) return; - lock= ticket->lock; - pthread_mutex_lock(&LOCK_mdl); - lock->type= MDL_LOCK::SHARED; - ticket->type= MDL_SHARED_UPGRADABLE; + m_lock->type= MDL_lock::SHARED; + m_type= MDL_SHARED_UPGRADABLE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } /** - Release global shared metadata lock. - - @param context Current context + Release the global shared metadata lock. */ -void mdl_release_global_shared_lock(MDL_CONTEXT *context) +void MDL_context::release_global_shared_lock() { safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(context->has_global_shared_lock); + DBUG_ASSERT(m_has_global_shared_lock); pthread_mutex_lock(&LOCK_mdl); global_lock.active_shared--; - context->has_global_shared_lock= FALSE; + m_has_global_shared_lock= FALSE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1511,7 +1525,6 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) Auxiliary function which allows to check if we have exclusive lock on the object. - @param context Current context @param type Id of object type @param db Name of the database @param name Name of the object @@ -1520,19 +1533,18 @@ void mdl_release_global_shared_lock(MDL_CONTEXT *context) FALSE otherwise. */ -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, - const char *db, const char *name) +bool +MDL_context::is_exclusive_lock_owner(unsigned char type, + const char *db, const char *name) { - MDL_KEY key; - MDL_LOCK_TICKET *ticket; - MDL_CONTEXT::Ticket_iterator it(context->tickets); - - key.mdl_key_init(type, db, name); + MDL_key key(type, db, name); + MDL_ticket *ticket; + MDL_context::Ticket_iterator it(m_tickets); while ((ticket= it++)) { - if (ticket->lock->type == MDL_LOCK::EXCLUSIVE && - ticket->lock->key.is_equal(&key)) + if (ticket->m_lock->type == MDL_lock::EXCLUSIVE && + ticket->m_lock->key.is_equal(&key)) break; } @@ -1544,7 +1556,6 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, Auxiliary function which allows to check if we have some kind of lock on a object. - @param context Current context @param type Id of object type @param db Name of the database @param name Name of the object @@ -1553,18 +1564,17 @@ bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, FALSE otherwise. */ -bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, - const char *db, const char *name) +bool +MDL_context::is_lock_owner(unsigned char type, + const char *db, const char *name) { - MDL_KEY key; - MDL_LOCK_TICKET *ticket; - MDL_CONTEXT::Ticket_iterator it(context->tickets); - - key.mdl_key_init(type, db, name); + MDL_key key(type, db, name); + MDL_ticket *ticket; + MDL_context::Ticket_iterator it(m_tickets); while ((ticket= it++)) { - if (ticket->lock->key.is_equal(&key)) + if (ticket->m_lock->key.is_equal(&key)) break; } @@ -1576,20 +1586,22 @@ bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, Check if we have any pending exclusive locks which conflict with existing shared lock. + @pre The ticket must match an acquired lock. + @param ticket Shared lock against which check should be performed. @return TRUE if there are any conflicting locks, FALSE otherwise. */ -bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) +bool MDL_ticket::has_pending_conflicting_lock() const { bool result; - DBUG_ASSERT(is_shared(ticket)); + DBUG_ASSERT(is_shared()); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - result= !ticket->lock->waiting.is_empty(); + result= !m_lock->waiting.is_empty(); pthread_mutex_unlock(&LOCK_mdl); return result; } @@ -1598,8 +1610,6 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) /** Associate pointer to an opaque object with a lock. - @param ticket Lock ticket for the lock with which the object - should be associated. @param cached_object Pointer to the object @param release_hook Cleanup function to be called when MDL subsystem decides to remove lock or associate another object. @@ -1623,24 +1633,24 @@ bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) lock on this name is released. */ -void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, - mdl_cached_object_release_hook release_hook) +void +MDL_ticket::set_cached_object(void *cached_object, + mdl_cached_object_release_hook release_hook) { - MDL_LOCK *lock= ticket->lock; DBUG_ENTER("mdl_set_cached_object"); DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", - lock->key.db_name(), lock->key.table_name(), + m_lock->key.db_name(), m_lock->key.table_name(), cached_object)); /* - TODO: This assumption works now since we do mdl_get_cached_object() - and mdl_set_cached_object() in the same critical section. Once + TODO: This assumption works now since we do get_cached_object() + and set_cached_object() in the same critical section. Once this becomes false we will have to call release_hook here and use additional mutex protecting 'cached_object' member. */ - DBUG_ASSERT(!lock->cached_object); + DBUG_ASSERT(!m_lock->cached_object); - lock->cached_object= cached_object; - lock->cached_object_release_hook= release_hook; + m_lock->cached_object= cached_object; + m_lock->cached_object_release_hook= release_hook; DBUG_VOID_RETURN; } @@ -1654,13 +1664,12 @@ void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, @return Pointer to an opaque object associated with the lock. */ -void* mdl_get_cached_object(MDL_LOCK_TICKET *ticket) +void *MDL_ticket::get_cached_object() { - return ticket->lock->cached_object; + return m_lock->cached_object; } - /** Releases metadata locks that were acquired after a specific savepoint. @@ -1669,23 +1678,22 @@ void* mdl_get_cached_object(MDL_LOCK_TICKET *ticket) savepoint because other statements that take other special locks cause a implicit commit (ie LOCK TABLES). - @param thd Current thread - @param sv Savepoint + @param mdl_savepont The last acquired MDL lock when the + savepoint was set. */ -void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, - MDL_LOCK_TICKET *mdl_savepoint) +void MDL_context::rollback_to_savepoint(MDL_ticket *mdl_savepoint) { - MDL_LOCK_TICKET *mdl_lock_ticket; - MDL_CONTEXT::Ticket_iterator it(ctx->tickets); - DBUG_ENTER("mdl_rollback_to_savepoint"); + MDL_ticket *ticket; + Ticket_iterator it(m_tickets); + DBUG_ENTER("MDL_context::rollback_to_savepoint"); - while ((mdl_lock_ticket= it++)) + while ((ticket= it++)) { /* Stop when lock was acquired before this savepoint. */ - if (mdl_lock_ticket == mdl_savepoint) + if (ticket == mdl_savepoint) break; - mdl_ticket_release(ctx, mdl_lock_ticket); + release_lock(ticket); } DBUG_VOID_RETURN; diff --git a/sql/mdl.h b/sql/mdl.h index b5d05b06144..cfa3e671706 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -23,10 +23,9 @@ class THD; -struct MDL_LOCK_REQUEST; -struct MDL_LOCK_TICKET; -struct MDL_LOCK; -struct MDL_CONTEXT; +class MDL_context; +class MDL_lock; +class MDL_ticket; /** Type of metadata lock request. @@ -62,7 +61,7 @@ enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; or "name". */ -class MDL_KEY +class MDL_key { public: const uchar *ptr() const { return (uchar*) m_ptr; } @@ -90,21 +89,34 @@ public: m_db_name_length= (uint) (strmov(m_ptr + 1, db) - m_ptr - 1); m_length= (uint) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); } - void mdl_key_init(const MDL_KEY *rhs) + void mdl_key_init(const MDL_key *rhs) { memcpy(m_ptr, rhs->m_ptr, rhs->m_length); m_length= rhs->m_length; m_db_name_length= rhs->m_db_name_length; } - bool is_equal(const MDL_KEY *rhs) const + bool is_equal(const MDL_key *rhs) const { return (m_length == rhs->m_length && memcmp(m_ptr, rhs->m_ptr, m_length) == 0); } + MDL_key(const MDL_key *rhs) + { + mdl_key_init(rhs); + } + MDL_key(char type_arg, const char *db_arg, const char *name_arg) + { + mdl_key_init(type_arg, db_arg, name_arg); + } + MDL_key() {} /* To use when part of MDL_request. */ + private: char m_ptr[MAX_MDLKEY_LENGTH]; uint m_length; uint m_db_name_length; +private: + MDL_key(const MDL_key &); /* not implemented */ + MDL_key &operator=(const MDL_key &); /* not implemented */ }; @@ -125,14 +137,18 @@ struct I_P_List_adapter /** A pending metadata lock request. - A pending lock request or a granted metadata lock share the same abstract - base but are presented individually because they have different allocation + + A lock request and a granted metadata lock are represented by + different classes because they have different allocation sites and hence different lifetimes. The allocation of lock requests is controlled from outside of the MDL subsystem, while allocation of granted locks (tickets) is controlled within the MDL subsystem. + + MDL_request is a C structure, you don't need to call a constructor + or destructor for it. */ -struct MDL_LOCK_REQUEST +struct MDL_request { /** Type of metadata lock. */ enum enum_mdl_type type; @@ -140,51 +156,94 @@ struct MDL_LOCK_REQUEST /** Pointers for participating in the list of lock requests for this context. */ - MDL_LOCK_REQUEST *next_in_context; - MDL_LOCK_REQUEST **prev_in_context; + MDL_request *next_in_context; + MDL_request **prev_in_context; /** A lock is requested based on a fully qualified name and type. */ - MDL_KEY key; + MDL_key key; + + void init(unsigned char type_arg, const char *db_arg, const char *name_arg); + /** Set type of lock request. Can be only applied to pending locks. */ + inline void set_type(enum_mdl_type type_arg) + { + DBUG_ASSERT(ticket == NULL); + type= type_arg; + } + bool is_shared() const { return type < MDL_EXCLUSIVE; } /** Pointer to the lock ticket object for this lock request. Valid only if this lock request is satisfied. */ - MDL_LOCK_TICKET *ticket; + MDL_ticket *ticket; + + static MDL_request *create(unsigned char type, const char *db, + const char *name, MEM_ROOT *root); + }; +typedef void (*mdl_cached_object_release_hook)(void *); + /** A granted metadata lock. - @warning MDL_LOCK_TICKET members are private to the MDL subsystem. + @warning MDL_ticket members are private to the MDL subsystem. @note Multiple shared locks on a same object are represented by a single ticket. The same does not apply for other lock types. */ -struct MDL_LOCK_TICKET +class MDL_ticket { - /** Type of metadata lock. */ - enum enum_mdl_type type; - /** State of the metadata lock ticket. */ - enum enum_mdl_state state; - +public: /** Pointers for participating in the list of lock requests for this context. */ - MDL_LOCK_TICKET *next_in_context; - MDL_LOCK_TICKET **prev_in_context; + MDL_ticket *next_in_context; + MDL_ticket **prev_in_context; /** Pointers for participating in the list of satisfied/pending requests for the lock. */ - MDL_LOCK_TICKET *next_in_lock; - MDL_LOCK_TICKET **prev_in_lock; + MDL_ticket *next_in_lock; + MDL_ticket **prev_in_lock; +public: + bool has_pending_conflicting_lock() const; + + void *get_cached_object(); + void set_cached_object(void *cached_object, + mdl_cached_object_release_hook release_hook); + const MDL_context *get_ctx() const { return m_ctx; } + bool is_shared() const { return m_type < MDL_EXCLUSIVE; } + bool upgrade_shared_lock_to_exclusive(); + void downgrade_exclusive_lock(); +private: + friend class MDL_context; + + MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg) + : m_type(type_arg), + m_state(MDL_PENDING), + m_ctx(ctx_arg), + m_lock(NULL) + {} + + + static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg); + static void destroy(MDL_ticket *ticket); +private: + /** Type of metadata lock. */ + enum enum_mdl_type m_type; + /** State of the metadata lock ticket. */ + enum enum_mdl_state m_state; + /** Context of the owner of the metadata lock ticket. */ - MDL_CONTEXT *ctx; + MDL_context *m_ctx; /** Pointer to the lock object for this lock ticket. */ - MDL_LOCK *lock; + MDL_lock *m_lock; +private: + MDL_ticket(const MDL_ticket &); /* not implemented */ + MDL_ticket &operator=(const MDL_ticket &); /* not implemented */ }; @@ -193,116 +252,87 @@ struct MDL_LOCK_TICKET connection has such a context. */ -struct MDL_CONTEXT +class MDL_context { - typedef I_P_List > +public: + typedef I_P_List > Request_list; typedef Request_list::Iterator Request_iterator; - typedef I_P_List > + typedef I_P_List > Ticket_list; typedef Ticket_list::Iterator Ticket_iterator; - Request_list requests; - Ticket_list tickets; - bool has_global_shared_lock; - THD *thd; + void init(THD *thd); + void destroy(); + + void backup_and_reset(MDL_context *backup); + void restore_from_backup(MDL_context *backup); + void merge(MDL_context *source); + + void add_request(MDL_request *mdl_request); + void remove_request(MDL_request *mdl_request); + void remove_all_requests(); + + bool acquire_shared_lock(MDL_request *mdl_request, bool *retry); + bool acquire_exclusive_locks(); + bool try_acquire_exclusive_lock(MDL_request *mdl_request, bool *conflict); + bool acquire_global_shared_lock(); + + bool wait_for_locks(); + + void release_all_locks(); + void release_all_locks_for_name(MDL_ticket *ticket); + void release_lock(MDL_ticket *ticket); + void release_global_shared_lock(); + + bool is_exclusive_lock_owner(unsigned char type, + const char *db, + const char *name); + bool is_lock_owner(unsigned char type, const char *db, const char *name); + + inline bool has_locks() const + { + return !m_tickets.is_empty(); + } + + inline MDL_ticket *mdl_savepoint() + { + return m_tickets.head(); + } + + void rollback_to_savepoint(MDL_ticket *mdl_savepoint); + + /** + Get iterator for walking through all lock requests in the context. + */ + inline Request_iterator get_requests() + { + return Request_iterator(m_requests); + } + inline THD *get_thd() const { return m_thd; } +private: + Request_list m_requests; + Ticket_list m_tickets; + bool m_has_global_shared_lock; + THD *m_thd; +private: + void release_ticket(MDL_ticket *ticket); + MDL_ticket *find_ticket(MDL_request *mdl_req); }; void mdl_init(); void mdl_destroy(); -void mdl_context_init(MDL_CONTEXT *context, THD *thd); -void mdl_context_destroy(MDL_CONTEXT *context); -void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); -void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); -void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); - -void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, - const char *db, const char *name); -MDL_LOCK_REQUEST *mdl_request_alloc(unsigned char type, const char *db, - const char *name, MEM_ROOT *root); -void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); -void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); -void mdl_request_remove_all(MDL_CONTEXT *context); - -/** - Set type of lock request. Can be only applied to pending locks. -*/ - -inline void mdl_request_set_type(MDL_LOCK_REQUEST *lock_req, enum_mdl_type lock_type) -{ - DBUG_ASSERT(lock_req->ticket == NULL); - lock_req->type= lock_type; -} - -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, - bool *retry); -bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); -bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_TICKET *ticket); -bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_REQUEST *lock_req, - bool *conflict); -bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); - -bool mdl_wait_for_locks(MDL_CONTEXT *context); - -void mdl_ticket_release_all(MDL_CONTEXT *context); -void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, - MDL_LOCK_TICKET *ticket); -void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); -void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_TICKET *ticket); -void mdl_release_global_shared_lock(MDL_CONTEXT *context); - -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, - const char *db, const char *name); -bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, - const char *db, const char *name); - -bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket); - -inline bool mdl_has_locks(MDL_CONTEXT *context) -{ - return !context->tickets.is_empty(); -} - -inline MDL_LOCK_TICKET *mdl_savepoint(MDL_CONTEXT *ctx) -{ - return ctx->tickets.head(); -} - -void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, - MDL_LOCK_TICKET *mdl_savepoint); - -/** - Get iterator for walking through all lock requests in the context. -*/ - -inline MDL_CONTEXT::Request_iterator -mdl_get_requests(MDL_CONTEXT *ctx) -{ - MDL_CONTEXT::Request_iterator result(ctx->requests); - return result; -} - - -void mdl_get_tdc_key(MDL_LOCK_TICKET *ticket, LEX_STRING *key); -typedef void (* mdl_cached_object_release_hook)(void *); -void *mdl_get_cached_object(MDL_LOCK_TICKET *ticket); -void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, - mdl_cached_object_release_hook release_hook); - /* Functions in the server's kernel used by metadata locking subsystem. diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 0a703475aa7..695b160fc01 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1205,7 +1205,7 @@ void Relay_log_info::clear_tables_to_lock() meta-data locks are stored. So we want to be sure that we don't have any references to this memory left. */ - DBUG_ASSERT(!mdl_has_locks(&(current_thd->mdl_context))); + DBUG_ASSERT(!current_thd->mdl_context.has_locks()); while (tables_to_lock) { diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 5732d7b7079..92019299f6c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3981,10 +3981,10 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_lock_request= mdl_request_alloc(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : - thd->mem_root); + table->mdl_request= MDL_request::create(0, table->db, table->table_name, + thd->locked_tables_root ? + thd->locked_tables_root : + thd->mem_root); /* Everyting else should be zeroed */ @@ -4026,10 +4026,10 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_lock_request= mdl_request_alloc(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : - thd->mem_root); + table->mdl_request= MDL_request::create(0, table->db, table->table_name, + thd->locked_tables_root ? + thd->locked_tables_root : + thd->mem_root); lex->add_to_query_tables(table); return table; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f4f07ad08f7..0441458510f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -124,7 +124,7 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); -static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context); +static bool tdc_wait_for_old_versions(THD *thd, MDL_context *context); static bool has_write_table_with_auto_increment(TABLE_LIST *tables); @@ -449,8 +449,8 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, To be able perform any operation on table we should own some kind of metadata lock on it. */ - DBUG_ASSERT(mdl_is_lock_owner(&thd->mdl_context, 0, table_list->db, - table_list->table_name)); + DBUG_ASSERT(thd->mdl_context.is_lock_owner(0, table_list->db, + table_list->table_name)); /* Read table definition from cache */ if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, @@ -1050,12 +1050,12 @@ err_with_reopen: */ thd->locked_tables_list.reopen_tables(thd); /* - Since mdl_downgrade_exclusive_lock() won't do anything with shared - metadata lock it is much simplier to go through all open tables rather + Since downgrade_exclusive_lock() won't do anything with shared + metadata lock it is much simpler to go through all open tables rather than picking only those tables that were flushed. */ for (TABLE *tab= thd->open_tables; tab; tab= tab->next) - mdl_downgrade_exclusive_lock(&thd->mdl_context, tab->mdl_lock_ticket); + tab->mdl_ticket->downgrade_exclusive_lock(); } DBUG_RETURN(result); } @@ -1336,7 +1336,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, */ void close_thread_tables(THD *thd, - bool skip_mdl) + bool is_back_off) { TABLE *table; DBUG_ENTER("close_thread_tables"); @@ -1478,10 +1478,10 @@ void close_thread_tables(THD *thd, if (thd->open_tables) close_open_tables(thd); - mdl_ticket_release_all(&thd->mdl_context); - if (!skip_mdl) + thd->mdl_context.release_all_locks(); + if (!is_back_off) { - mdl_request_remove_all(&thd->mdl_context); + thd->mdl_context.remove_all_requests(); } DBUG_VOID_RETURN; } @@ -1500,7 +1500,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) *table_ptr=table->next; - table->mdl_lock_ticket= NULL; + table->mdl_ticket= NULL; if (table->needs_reopen() || thd->version != refresh_version || !table->db_stat) { @@ -2095,8 +2095,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, old_lock_type= table->reginfo.lock_type; mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ - if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, - table->mdl_lock_ticket)) + if (table->mdl_ticket->upgrade_shared_lock_to_exclusive()) { mysql_lock_downgrade_write(thd, table, old_lock_type); DBUG_RETURN(TRUE); @@ -2279,11 +2278,11 @@ void table_share_release_hook(void *share) static bool open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, - MDL_LOCK_REQUEST *mdl_lock_request, + MDL_request *mdl_request, uint flags, enum_open_table_action *action) { - mdl_request_add(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.add_request(mdl_request); if (table_list->open_type) { @@ -2296,10 +2295,10 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, shared locks. This invariant is preserved here and is also enforced by asserts in metadata locking subsystem. */ - mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + mdl_request->set_type(MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_exclusive_locks()) { - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); return 1; } } @@ -2316,16 +2315,16 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && table_list->lock_type >= TL_WRITE_ALLOW_WRITE) - mdl_request_set_type(mdl_lock_request, MDL_SHARED_UPGRADABLE); + mdl_request->set_type(MDL_SHARED_UPGRADABLE); if (flags & MYSQL_LOCK_IGNORE_FLUSH) - mdl_request_set_type(mdl_lock_request, MDL_SHARED_HIGH_PRIO); + mdl_request->set_type(MDL_SHARED_HIGH_PRIO); - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_request, &retry)) + if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry)) { if (retry) *action= OT_BACK_OFF_AND_RETRY; else - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); return 1; } } @@ -2380,8 +2379,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, char key[MAX_DBKEY_LENGTH]; uint key_length; char *alias= table_list->alias; - MDL_LOCK_REQUEST *mdl_lock_request; - MDL_LOCK_TICKET *mdl_lock_ticket; + MDL_request *mdl_request; + MDL_ticket *mdl_ticket; int error; TABLE_SHARE *share; DBUG_ENTER("open_table"); @@ -2517,8 +2516,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, TABLES breaks metadata locking protocol (potentially can lead to deadlocks) it should be disallowed. */ - if (mdl_is_lock_owner(&thd->mdl_context, 0, table_list->db, - table_list->table_name)) + if (thd->mdl_context.is_lock_owner(0, table_list->db, + table_list->table_name)) { char path[FN_REFLEN + 1]; enum legacy_db_type not_used; @@ -2560,10 +2559,10 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, This is the normal use case. */ - mdl_lock_request= table_list->mdl_lock_request; + mdl_request= table_list->mdl_request; if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - if (open_table_get_mdl_lock(thd, table_list, mdl_lock_request, flags, + if (open_table_get_mdl_lock(thd, table_list, mdl_request, flags, action)) DBUG_RETURN(TRUE); } @@ -2573,7 +2572,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, open_table_get_mdl_lock as the lock on the table might have been acquired previously (MYSQL_OPEN_HAS_MDL_LOCK). */ - mdl_lock_ticket= mdl_lock_request->ticket; + mdl_ticket= mdl_request->ticket; pthread_mutex_lock(&LOCK_open); @@ -2616,7 +2615,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(FALSE); } - if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_ticket))) + if (!(share= (TABLE_SHARE *) mdl_ticket->get_cached_object())) { if (!(share= get_table_share_with_create(thd, table_list, key, key_length, OPEN_VIEW, @@ -2687,7 +2686,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, so we need to increase reference counter; */ reference_table_share(share); - mdl_set_cached_object(mdl_lock_ticket, share, table_share_release_hook); + mdl_ticket->set_cached_object(share, table_share_release_hook); } else { @@ -2796,9 +2795,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, lock on this table to shared metadata lock. */ if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) - mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); + mdl_ticket->downgrade_exclusive_lock(); - table->mdl_lock_ticket= mdl_lock_ticket; + table->mdl_ticket= mdl_ticket; table->next=thd->open_tables; /* Link into simple list */ thd->open_tables=table; @@ -2850,8 +2849,8 @@ err_unlock2: pthread_mutex_unlock(&LOCK_open); if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - mdl_ticket_release(&thd->mdl_context, mdl_lock_ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_ticket); + thd->mdl_context.remove_request(mdl_request); } DBUG_RETURN(TRUE); } @@ -2969,7 +2968,7 @@ Locked_tables_list::init_locked_tables(THD *thd) dst_table_list->init_one_table(db, db_len, table_name, table_name_len, alias, src_table_list->table->reginfo.lock_type); - dst_table_list->mdl_lock_request= src_table_list->mdl_lock_request; + dst_table_list->mdl_request= src_table_list->mdl_request; dst_table_list->table= table; memcpy(db, src_table_list->db, db_len + 1); memcpy(table_name, src_table_list->table_name, table_name_len + 1); @@ -3020,8 +3019,6 @@ Locked_tables_list::unlock_locked_tables(THD *thd) thd->locked_tables_mode= LTM_NONE; close_thread_tables(thd); - - mdl_ticket_release_all(&thd->mdl_context); } /* After closing tables we can free memory used for storing lock @@ -3506,21 +3503,21 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, enum_open_table_action action) { bool result= FALSE; - MDL_LOCK_REQUEST *mdl_lock_request= table->mdl_lock_request; + MDL_request *mdl_request= table->mdl_request; switch (action) { case OT_BACK_OFF_AND_RETRY: - result= (mdl_wait_for_locks(&thd->mdl_context) || + result= (thd->mdl_context.wait_for_locks() || tdc_wait_for_old_versions(thd, &thd->mdl_context)); - mdl_request_remove_all(&thd->mdl_context); + thd->mdl_context.remove_all_requests(); break; case OT_DISCOVER: - mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, mdl_lock_request); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + mdl_request->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(mdl_request); + if (thd->mdl_context.acquire_exclusive_locks()) { - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); return TRUE; } pthread_mutex_lock(&LOCK_open); @@ -3530,15 +3527,15 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, thd->warning_info->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); break; case OT_REPAIR: - mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, mdl_lock_request); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + mdl_request->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(mdl_request); + if (thd->mdl_context.acquire_exclusive_locks()) { - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); return TRUE; } pthread_mutex_lock(&LOCK_open); @@ -3546,8 +3543,8 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); break; default: DBUG_ASSERT(0); @@ -4652,7 +4649,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, */ -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl) +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off) { /* If table list consists only from tables from prelocking set, table list @@ -4664,7 +4661,7 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl) sp_remove_not_own_routines(thd->lex); for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) tmp->table= 0; - close_thread_tables(thd, skip_mdl); + close_thread_tables(thd, is_back_off); } @@ -7691,8 +7688,7 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, safe_mutex_assert_owner(&LOCK_open); DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED || - mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, - db, table_name)); + thd->mdl_context.is_exclusive_lock_owner(0, db, table_name)); key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; @@ -7739,11 +7735,11 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, @param context Metadata locking context with locks. */ -static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) +static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context) { TABLE_SHARE *share; const char *old_msg; - MDL_LOCK_REQUEST *lock_req; + MDL_request *mdl_request; while (!thd->killed) { @@ -7756,16 +7752,16 @@ static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) mysql_ha_flush(thd); pthread_mutex_lock(&LOCK_open); - MDL_CONTEXT::Request_iterator it= mdl_get_requests(context); - while ((lock_req= it++)) + MDL_context::Request_iterator it= mdl_context->get_requests(); + while ((mdl_request= it++)) { - if ((share= get_cached_table_share(lock_req->key.db_name(), - lock_req->key.table_name())) && + if ((share= get_cached_table_share(mdl_request->key.db_name(), + mdl_request->key.table_name())) && share->version != refresh_version && !share->used_tables.is_empty()) break; } - if (!lock_req) + if (!mdl_request) { pthread_mutex_unlock(&LOCK_open); break; @@ -8169,8 +8165,8 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) pthread_mutex_unlock(&LOCK_open); - mdl_ticket_release_all(&thd->mdl_context); - mdl_request_remove_all(&thd->mdl_context); + thd->mdl_context.release_all_locks(); + thd->mdl_context.remove_all_requests(); thd->restore_backup_open_tables_state(backup); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4f1777c152c..654d7ad718d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1050,8 +1050,8 @@ THD::~THD() if (!cleanup_done) cleanup(); - mdl_context_destroy(&mdl_context); - mdl_context_destroy(&handler_mdl_context); + mdl_context.destroy(); + handler_mdl_context.destroy(); ha_close_connection(this); plugin_thdvar_cleanup(this); @@ -3033,8 +3033,8 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) lock == 0 && locked_tables_mode == LTM_NONE && m_reprepare_observer == NULL); - mdl_context_destroy(&mdl_context); - mdl_context_destroy(&handler_mdl_context); + mdl_context.destroy(); + handler_mdl_context.destroy(); set_open_tables_state(backup); DBUG_VOID_RETURN; diff --git a/sql/sql_class.h b/sql/sql_class.h index 80817fa4c12..92b9f9f4611 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -780,7 +780,7 @@ struct st_savepoint { uint length; Ha_trx_info *ha_list; /** Last acquired lock before this savepoint was set. */ - MDL_LOCK_TICKET *mdl_savepoint; + MDL_ticket *mdl_savepoint; }; enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; @@ -983,8 +983,8 @@ public: */ uint state_flags; - MDL_CONTEXT mdl_context; - MDL_CONTEXT handler_mdl_context; + MDL_context mdl_context; + MDL_context handler_mdl_context; /** This constructor initializes Open_tables_state instance which can only @@ -1015,8 +1015,8 @@ public: locked_tables_mode= LTM_NONE; state_flags= 0U; m_reprepare_observer= NULL; - mdl_context_init(&mdl_context, thd); - mdl_context_init(&handler_mdl_context, thd); + mdl_context.init(thd); + handler_mdl_context.init(thd); } }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c96594a8032..fc92dbee0a1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_LOCK_REQUEST *mdl_lock_request= NULL; + MDL_request *mdl_request= NULL; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1175,13 +1175,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - mdl_lock_request= mdl_request_alloc(0, table_list->db, - table_list->table_name, thd->mem_root); - mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, mdl_lock_request); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + mdl_request= MDL_request::create(0, table_list->db, + table_list->table_name, thd->mem_root); + mdl_request->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(mdl_request); + if (thd->mdl_context.acquire_exclusive_locks()) { - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); DBUG_RETURN(TRUE); } pthread_mutex_lock(&LOCK_open); @@ -1212,18 +1212,18 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_lock_request) + if (mdl_request) { - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); } } else if (error) { - if (mdl_lock_request) + if (mdl_request) { - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); } } DBUG_RETURN(error); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 83c5c60dc01..4b07a00c779 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -125,7 +125,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) { TABLE **table_ptr; - MDL_LOCK_TICKET *mdl_lock_ticket; + MDL_ticket *mdl_ticket; /* Though we could take the table pointer from hash_tables->table, @@ -141,7 +141,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) if (*table_ptr) { (*table_ptr)->file->ha_index_or_rnd_end(); - mdl_lock_ticket= (*table_ptr)->mdl_lock_ticket; + mdl_ticket= (*table_ptr)->mdl_ticket; pthread_mutex_lock(&LOCK_open); if (close_thread_table(thd, table_ptr)) { @@ -149,8 +149,8 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) broadcast_refresh(); } pthread_mutex_unlock(&LOCK_open); - mdl_ticket_release(&thd->handler_mdl_context, mdl_lock_ticket); - mdl_request_remove(&thd->handler_mdl_context, tables->mdl_lock_request); + thd->handler_mdl_context.release_lock(mdl_ticket); + thd->handler_mdl_context.remove_request(tables->mdl_request); } else if (tables->table) { @@ -194,8 +194,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) uint dblen, namelen, aliaslen, counter; int error; TABLE *backup_open_tables; - MDL_CONTEXT backup_mdl_context; - MDL_LOCK_REQUEST *mdl_lock_request; + MDL_context backup_mdl_context; + MDL_request *mdl_request; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -246,7 +246,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) &db, (uint) dblen, &name, (uint) namelen, &alias, (uint) aliaslen, - &mdl_lock_request, sizeof(MDL_LOCK_REQUEST), + &mdl_request, sizeof(MDL_request), NullS))) { DBUG_PRINT("exit",("ERROR")); @@ -260,8 +260,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - mdl_request_init(mdl_lock_request, 0, db, name); - hash_tables->mdl_lock_request= mdl_lock_request; + mdl_request->init(0, db, name); + hash_tables->mdl_request= mdl_request; /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) @@ -289,7 +289,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) */ backup_open_tables= thd->open_tables; thd->open_tables= NULL; - mdl_context_backup_and_reset(&thd->mdl_context, &backup_mdl_context); + thd->mdl_context.backup_and_reset(&backup_mdl_context); /* open_tables() will set 'hash_tables->table' if successful. @@ -328,10 +328,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) thd->handler_tables= thd->open_tables; } } - mdl_context_merge(&thd->handler_mdl_context, &thd->mdl_context); + thd->handler_mdl_context.merge(&thd->mdl_context); thd->open_tables= backup_open_tables; - mdl_context_restore(&thd->mdl_context, &backup_mdl_context); + thd->mdl_context.restore_from_backup(&backup_mdl_context); if (error) goto err; @@ -800,11 +800,11 @@ void mysql_ha_flush(THD *thd) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); /* - TABLE::mdl_lock_ticket is 0 for temporary tables so we need extra check. + TABLE::mdl_ticket is 0 for temporary tables so we need extra check. */ if (hash_tables->table && - (hash_tables->table->mdl_lock_ticket && - mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock_ticket) || + (hash_tables->table->mdl_ticket && + hash_tables->table->mdl_ticket->has_pending_conflicting_lock() || hash_tables->table->needs_reopen())) mysql_ha_close_table(thd, hash_tables); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4166267c5a9..579fbcea4ea 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5977,9 +5977,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_lock_request= - mdl_request_alloc(0, ptr->db, ptr->table_name, thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + ptr->mdl_request= + MDL_request::create(0, ptr->db, ptr->table_name, thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); DBUG_RETURN(ptr); } diff --git a/sql/sql_plist.h b/sql/sql_plist.h index 91fa9ef52bb..b0a0bb016d0 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -79,6 +79,7 @@ public: **B::prev_ptr(a)= next; } inline T* head() { return first; } + inline const T *head() const { return first; } void swap(I_P_List &rhs) { swap_variables(T *, first, rhs.first); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 440efbf7c6d..9727498cb2e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3050,7 +3050,7 @@ uint get_table_open_method(TABLE_LIST *tables, Acquire high priority share metadata lock on a table. @param thd Thread context. - @param mdl_lock_req Pointer to memory to be used for MDL_LOCK_REQUEST + @param mdl_request Pointer to memory to be used for MDL_request object for a lock request. @param table Table list element for the table @@ -3065,23 +3065,23 @@ uint get_table_open_method(TABLE_LIST *tables, */ static bool -acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_REQUEST *mdl_lock_req, +acquire_high_prio_shared_mdl_lock(THD *thd, MDL_request *mdl_request, TABLE_LIST *table) { bool retry; - mdl_request_init(mdl_lock_req, 0, table->db, table->table_name); - table->mdl_lock_request= mdl_lock_req; - mdl_request_add(&thd->mdl_context, mdl_lock_req); - mdl_request_set_type(mdl_lock_req, MDL_SHARED_HIGH_PRIO); + mdl_request->init(0, table->db, table->table_name); + table->mdl_request= mdl_request; + thd->mdl_context.add_request(mdl_request); + mdl_request->set_type(MDL_SHARED_HIGH_PRIO); while (1) { - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_req, &retry)) + if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry)) { - if (!retry || mdl_wait_for_locks(&thd->mdl_context)) + if (!retry || thd->mdl_context.wait_for_locks()) { - mdl_request_remove_all(&thd->mdl_context); + thd->mdl_context.remove_all_requests(); return TRUE; } continue; @@ -3123,7 +3123,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, char key[MAX_DBKEY_LENGTH]; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; - MDL_LOCK_REQUEST mdl_lock_request; + MDL_request mdl_request; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); @@ -3153,7 +3153,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, simply obtaining internal lock of data-dictionary (ATM it is LOCK_open) instead of obtaning full-blown metadata lock. */ - if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_request, &table_list)) + if (acquire_high_prio_shared_mdl_lock(thd, &mdl_request, &table_list)) { /* Some error occured (most probably we have been killed while @@ -3213,9 +3213,8 @@ err_share: err_unlock: pthread_mutex_unlock(&LOCK_open); -err: - mdl_ticket_release(&thd->mdl_context, mdl_lock_request.ticket); - mdl_request_remove(&thd->mdl_context, &mdl_lock_request); + thd->mdl_context.release_lock(mdl_request.ticket); + thd->mdl_context.remove_request(&mdl_request); thd->clear_error(); return res; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 54f06935d3b..42ae0a89458 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1910,7 +1910,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, Since we don't acquire metadata lock if we have found temporary table, we should do something to avoid releasing it at the end. */ - table->mdl_lock_request= NULL; + table->mdl_request= NULL; } else { @@ -1923,7 +1923,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->table_name); if (!table->table) DBUG_RETURN(1); - table->mdl_lock_request->ticket= table->table->mdl_lock_ticket; + table->mdl_request->ticket= table->table->mdl_ticket; } } } @@ -2202,15 +2202,14 @@ err: } for (table= tables; table; table= table->next_local) { - if (table->mdl_lock_request) + if (table->mdl_request) { /* Under LOCK TABLES we may have several instances of table open and locked and therefore have to remove several metadata lock requests associated with them. */ - mdl_ticket_release_all_for_name(&thd->mdl_context, - table->mdl_lock_request->ticket); + thd->mdl_context.release_all_locks_for_name(table->mdl_request->ticket); } } } @@ -4108,28 +4107,28 @@ warn: static bool lock_table_name_if_not_cached(THD *thd, const char *db, const char *table_name, - MDL_LOCK_REQUEST **lock_req) + MDL_request **mdl_request) { bool conflict; - if (!(*lock_req= mdl_request_alloc(0, db, table_name, thd->mem_root))) + if (!(*mdl_request= MDL_request::create(0, db, table_name, thd->mem_root))) return TRUE; - mdl_request_set_type(*lock_req, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, *lock_req); - if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_req, &conflict)) + (*mdl_request)->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(*mdl_request); + if (thd->mdl_context.try_acquire_exclusive_lock(*mdl_request, &conflict)) { /* To simplify our life under LOCK TABLES we remove unsatisfied lock request from the context. */ - mdl_request_remove(&thd->mdl_context, *lock_req); + thd->mdl_context.remove_request(*mdl_request); if (!conflict) { /* Probably OOM. */ return TRUE; } else - *lock_req= NULL; + *mdl_request= NULL; } return FALSE; } @@ -4145,7 +4144,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, bool internal_tmp_table, uint select_field_count) { - MDL_LOCK_REQUEST *target_lock_req= NULL; + MDL_request *target_mdl_request= NULL; bool result; DBUG_ENTER("mysql_create_table"); @@ -4168,12 +4167,12 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_req)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request)) { result= TRUE; goto unlock; } - if (!target_lock_req) + if (!target_mdl_request) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) { @@ -4199,10 +4198,10 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, select_field_count); unlock: - if (target_lock_req) + if (target_mdl_request) { - mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); - mdl_request_remove(&thd->mdl_context, target_lock_req); + thd->mdl_context.release_lock(target_mdl_request->ticket); + thd->mdl_context.remove_request(target_mdl_request); } pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) @@ -4367,7 +4366,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, char from[FN_REFLEN],tmp[FN_REFLEN+32]; const char **ext; MY_STAT stat_info; - MDL_LOCK_REQUEST *mdl_lock_request= NULL; + MDL_request *mdl_request= NULL; enum enum_open_table_action ot_action_unused; DBUG_ENTER("prepare_for_repair"); uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH | @@ -4386,13 +4385,13 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); - mdl_lock_request= mdl_request_alloc(0, table_list->db, - table_list->table_name, thd->mem_root); - mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, mdl_lock_request); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + mdl_request= MDL_request::create(0, table_list->db, + table_list->table_name, thd->mem_root); + mdl_request->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(mdl_request); + if (thd->mdl_context.acquire_exclusive_locks()) { - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); DBUG_RETURN(0); } @@ -4412,7 +4411,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, } pthread_mutex_unlock(&LOCK_open); table= &tmp_table; - table_list->mdl_lock_request= mdl_lock_request; + table_list->mdl_request= mdl_request; } /* A MERGE table must not come here. */ @@ -4523,10 +4522,10 @@ end: pthread_mutex_unlock(&LOCK_open); } /* In case of a temporary table there will be no metadata lock. */ - if (error && mdl_lock_request) + if (error && mdl_request) { - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); } DBUG_RETURN(error); } @@ -5229,7 +5228,7 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { - MDL_LOCK_REQUEST *target_lock_req= NULL; + MDL_request *target_mdl_request= NULL; char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1]; uint dst_path_length; char *db= table->db; @@ -5286,9 +5285,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_req)) + if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request)) goto err; - if (!target_lock_req) + if (!target_mdl_request) goto table_exists; dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); @@ -5298,7 +5297,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Make the metadata lock available to open_table() called to reopen the table down the road. */ - table->mdl_lock_request= target_lock_req; + table->mdl_request= target_mdl_request; } DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); @@ -5469,10 +5468,10 @@ binlog: res= FALSE; err: - if (target_lock_req) + if (target_mdl_request) { - mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); - mdl_request_remove(&thd->mdl_context, target_lock_req); + thd->mdl_context.release_lock(target_mdl_request->ticket); + thd->mdl_context.remove_request(target_mdl_request); } DBUG_RETURN(res); } @@ -6411,8 +6410,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint order_num, ORDER *order, bool ignore) { TABLE *table, *new_table= 0; - MDL_LOCK_TICKET *mdl_lock_ticket; - MDL_LOCK_REQUEST *target_lock_req= NULL; + MDL_ticket *mdl_ticket; + MDL_request *target_mdl_request= NULL; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6583,7 +6582,7 @@ view_err: MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) DBUG_RETURN(TRUE); table->use_all_columns(); - mdl_lock_ticket= table->mdl_lock_ticket; + mdl_ticket= table->mdl_ticket; /* Prohibit changing of the UNION list of a non-temporary MERGE table @@ -6636,9 +6635,9 @@ view_err: else { if (lock_table_name_if_not_cached(thd, new_db, new_name, - &target_lock_req)) + &target_mdl_request)) DBUG_RETURN(TRUE); - if (!target_lock_req) + if (!target_mdl_request) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); DBUG_RETURN(TRUE); @@ -6831,12 +6830,12 @@ view_err: */ if (new_name != table_name || new_db != db) { - mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); - mdl_request_remove(&thd->mdl_context, target_lock_req); - mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket); + thd->mdl_context.release_lock(target_mdl_request->ticket); + thd->mdl_context.remove_request(target_mdl_request); + thd->mdl_context.release_all_locks_for_name(mdl_ticket); } else - mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); + mdl_ticket->downgrade_exclusive_lock(); } DBUG_RETURN(error); } @@ -7069,7 +7068,7 @@ view_err: #ifdef WITH_PARTITION_STORAGE_ENGINE if (fast_alter_partition) { - DBUG_ASSERT(!target_lock_req); + DBUG_ASSERT(!target_mdl_request); DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, db, table_name, @@ -7436,7 +7435,7 @@ view_err: table_list->table_name_length= strlen(new_name); table_list->db= new_db; table_list->db_length= strlen(new_db); - table_list->mdl_lock_request= target_lock_req; + table_list->mdl_request= target_mdl_request; } else { @@ -7445,7 +7444,7 @@ view_err: points to a different instance than the one set initially to request the lock. */ - table_list->mdl_lock_request->ticket= mdl_lock_ticket; + table_list->mdl_request->ticket= mdl_ticket; } if (open_table(thd, table_list, thd->mem_root, &ot_action_unused, MYSQL_OPEN_REOPEN)) @@ -7511,12 +7510,12 @@ view_err: { if ((new_name != table_name || new_db != db)) { - mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); - mdl_request_remove(&thd->mdl_context, target_lock_req); - mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket); + thd->mdl_context.release_lock(target_mdl_request->ticket); + thd->mdl_context.remove_request(target_mdl_request); + thd->mdl_context.release_all_locks_for_name(mdl_ticket); } else - mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); + mdl_ticket->downgrade_exclusive_lock(); } end_temporary: @@ -7571,10 +7570,10 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (target_lock_req) + if (target_mdl_request) { - mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); - mdl_request_remove(&thd->mdl_context, target_lock_req); + thd->mdl_context.release_lock(target_mdl_request->ticket); + thd->mdl_context.remove_request(target_mdl_request); } DBUG_RETURN(TRUE); @@ -7586,12 +7585,12 @@ err_with_mdl: tables and release the exclusive metadata lock. */ thd->locked_tables_list.unlink_all_closed_tables(); - if (target_lock_req) + if (target_mdl_request) { - mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket); - mdl_request_remove(&thd->mdl_context, target_lock_req); + thd->mdl_context.release_lock(target_mdl_request->ticket); + thd->mdl_context.remove_request(target_mdl_request); } - mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket); + thd->mdl_context.release_all_locks_for_name(mdl_ticket); DBUG_RETURN(TRUE); } /* mysql_alter_table */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 894204fec58..93b89b869d6 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -329,7 +329,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) String stmt_query; bool need_start_waiting= FALSE; bool lock_upgrade_done= FALSE; - MDL_LOCK_TICKET *mdl_lock_ticket= NULL; + MDL_ticket *mdl_ticket= NULL; DBUG_ENTER("mysql_create_or_drop_trigger"); @@ -465,7 +465,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) table= tables->table; /* Later on we will need it to downgrade the lock */ - mdl_lock_ticket= table->mdl_lock_ticket; + mdl_ticket= table->mdl_ticket; if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto end; @@ -513,7 +513,7 @@ end: TABLE instance created by open_n_lock_single_table() and metadata lock. */ if (thd->locked_tables_mode && tables && lock_upgrade_done) - mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); + mdl_ticket->downgrade_exclusive_lock(); if (need_start_waiting) start_waiting_global_read_lock(thd); @@ -1882,7 +1882,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, In the future, only an exclusive metadata lock will be enough. */ #ifndef DBUG_OFF - if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table)) + if (thd->mdl_context.is_exclusive_lock_owner(0, db, old_table)) safe_mutex_assert_owner(&LOCK_open); #endif diff --git a/sql/table.cc b/sql/table.cc index b1988faf722..8d28514e912 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4814,8 +4814,8 @@ size_t max_row_length(TABLE *table, const uchar *data) void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_lock_request= - mdl_request_alloc(0, table_list->db, table_list->table_name, root); + table_list->mdl_request= + MDL_request::create(0, table_list->db, table_list->table_name, root); } diff --git a/sql/table.h b/sql/table.h index 3b125217338..02492ea4e81 100644 --- a/sql/table.h +++ b/sql/table.h @@ -30,8 +30,8 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; -struct MDL_LOCK_REQUEST; -struct MDL_LOCK_TICKET; +struct MDL_request; +struct MDL_ticket; /*************************************************************************/ @@ -814,7 +814,7 @@ public: partition_info *part_info; /* Partition related information */ bool no_partitions_used; /* If true, all partitions have been pruned away */ #endif - MDL_LOCK_TICKET *mdl_lock_ticket; + MDL_ticket *mdl_ticket; bool fill_item_list(List *item_list) const; void reset_item_list(List *item_list) const; @@ -1417,7 +1417,7 @@ struct TABLE_LIST uint table_open_method; enum enum_schema_table_state schema_table_state; - MDL_LOCK_REQUEST *mdl_lock_request; + MDL_request *mdl_request; void calc_md5(char *buffer); void set_underlying_merge(); diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index b6992256d5a..21a7e2c43d3 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -434,7 +434,7 @@ int ha_myisammrg::add_children_list(void) /* Copy select_lex. Used in unique_table() at least. */ child_l->select_lex= parent_l->select_lex; - child_l->mdl_lock_request= NULL; /* Safety, if alloc_mdl_requests fails. */ + child_l->mdl_request= NULL; /* Safety, if alloc_mdl_requests fails. */ /* Break when this was the last child. */ if (&child_l->next_global == this->children_last_l) From fea5e79941436ce4e4cdd81c849f25ff9b0342ba Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:55:26 +0300 Subject: [PATCH 093/466] Backport of (WL#4284): ------------------------------------------------------------ revno: 2617.23.23 committer: Davi Arnaut branch nick: mysql-6.0-runtime timestamp: Thu 2009-03-05 18:39:58 -0300 message: Fix for broken build: SHARED is defined by Solaris headers. --- sql/mdl.cc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 101d90d7e42..1d591bb2244 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -42,8 +42,8 @@ public: /** The type of lock (shared or exclusive). */ enum { - SHARED, - EXCLUSIVE, + MDL_LOCK_SHARED, + MDL_LOCK_EXCLUSIVE, } type; /** The key of the object (data) being protected. */ MDL_key key; @@ -71,7 +71,7 @@ public: inline static void destroy(MDL_lock *lock); private: MDL_lock(const MDL_key *key_arg) - : type(SHARED), + : type(MDL_LOCK_SHARED), key(key_arg), cached_object(NULL), cached_object_release_hook(NULL) @@ -673,7 +673,7 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar case MDL_SHARED: case MDL_SHARED_UPGRADABLE: case MDL_SHARED_HIGH_PRIO: - if (type == MDL_lock::SHARED) + if (type == MDL_lock::MDL_LOCK_SHARED) { /* Pending exclusive locks have higher priority over shared locks. */ if (waiting.is_empty() || type_arg == MDL_SHARED_HIGH_PRIO) @@ -700,7 +700,7 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar There should be no active exclusive locks since we own shared lock on the object. */ - DBUG_ASSERT(type == MDL_lock::SHARED); + DBUG_ASSERT(type == MDL_lock::MDL_LOCK_SHARED); while ((conflicting_ticket= it++)) { @@ -717,7 +717,7 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar can_grant= TRUE; break; } - else if (type == MDL_lock::SHARED) + else if (type == MDL_lock::MDL_LOCK_SHARED) { can_grant= granted.is_empty(); } @@ -829,7 +829,7 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry) if (!(lock= (MDL_lock*) my_hash_search(&mdl_locks, key->ptr(), key->length()))) { - /* Default lock type is MDL_lock::SHARED */ + /* Default lock type is MDL_lock::MDL_LOCK_SHARED */ lock= MDL_lock::create(key); if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { @@ -972,7 +972,7 @@ bool MDL_context::acquire_exclusive_locks() MDL_ticket *conflicting_ticket; MDL_lock::Ticket_iterator it(lock->granted); - signalled= (lock->type == MDL_lock::EXCLUSIVE); + signalled= (lock->type == MDL_lock::MDL_LOCK_EXCLUSIVE); while ((conflicting_ticket= it++)) signalled|= notify_shared_lock(m_thd, conflicting_ticket); @@ -1009,7 +1009,7 @@ bool MDL_context::acquire_exclusive_locks() global_lock.active_intention_exclusive++; ticket= mdl_request->ticket; lock= ticket->m_lock; - lock->type= MDL_lock::EXCLUSIVE; + lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE; lock->waiting.remove(ticket); lock->granted.push_front(ticket); m_tickets.push_front(ticket); @@ -1135,7 +1135,7 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() } } - m_lock->type= MDL_lock::EXCLUSIVE; + m_lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE; /* Set the new type of lock in the ticket. */ m_type= MDL_EXCLUSIVE; if (m_lock->cached_object) @@ -1200,7 +1200,7 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request, goto err; } mdl_request->ticket= ticket; - lock->type= MDL_lock::EXCLUSIVE; + lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE; lock->granted.push_front(ticket); m_tickets.push_front(ticket); ticket->m_state= MDL_ACQUIRED; @@ -1355,7 +1355,7 @@ void MDL_context::release_ticket(MDL_ticket *ticket) lock->granted.remove(ticket); break; case MDL_EXCLUSIVE: - lock->type= MDL_lock::SHARED; + lock->type= MDL_lock::MDL_LOCK_SHARED; lock->granted.remove(ticket); global_lock.active_intention_exclusive--; break; @@ -1497,7 +1497,7 @@ void MDL_ticket::downgrade_exclusive_lock() return; pthread_mutex_lock(&LOCK_mdl); - m_lock->type= MDL_lock::SHARED; + m_lock->type= MDL_lock::MDL_LOCK_SHARED; m_type= MDL_SHARED_UPGRADABLE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); @@ -1543,7 +1543,7 @@ MDL_context::is_exclusive_lock_owner(unsigned char type, while ((ticket= it++)) { - if (ticket->m_lock->type == MDL_lock::EXCLUSIVE && + if (ticket->m_lock->type == MDL_lock::MDL_LOCK_EXCLUSIVE && ticket->m_lock->key.is_equal(&key)) break; } From 6513332096ca8a261c15d4c43ca91632d9990f5d Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:57:01 +0300 Subject: [PATCH 094/466] Backport of: ---------------------------------------------------------- revno: 2617.23.22 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Wed 2009-03-04 23:29:16 +0300 message: WL#4284, "Transactional DDL locking": fix a Windows compilation warning. --- sql/mdl.h | 3 ++- sql/table.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sql/mdl.h b/sql/mdl.h index cfa3e671706..e3e41652bf4 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -148,8 +148,9 @@ struct I_P_List_adapter or destructor for it. */ -struct MDL_request +class MDL_request { +public: /** Type of metadata lock. */ enum enum_mdl_type type; diff --git a/sql/table.h b/sql/table.h index 02492ea4e81..b6ea372b41e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -30,8 +30,8 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; -struct MDL_request; -struct MDL_ticket; +class MDL_request; +class MDL_ticket; /*************************************************************************/ From 0b39c189baf40a9f0f9bc1588a31947cb941bb1a Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Sat, 5 Dec 2009 02:02:48 +0300 Subject: [PATCH 095/466] Backport of revno ## 2617.31.1, 2617.31.3, 2617.31.4, 2617.31.5, 2617.31.12, 2617.31.15, 2617.31.15, 2617.31.16, 2617.43.1 - initial changeset that introduced the fix for Bug#989 and follow up fixes for all test suite failures introduced in the initial changeset. ------------------------------------------------------------ revno: 2617.31.1 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Fri 2009-03-06 19:17:00 -0300 message: Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order WL#4284: Transactional DDL locking Currently the MySQL server does not keep metadata locks on schema objects for the duration of a transaction, thus failing to guarantee the integrity of the schema objects being used during the transaction and to protect then from concurrent DDL operations. This also poses a problem for replication as a DDL operation might be replicated even thought there are active transactions using the object being modified. The solution is to defer the release of metadata locks until a active transaction is either committed or rolled back. This prevents other statements from modifying the table for the entire duration of the transaction. This provides commitment ordering for guaranteeing serializability across multiple transactions. - Incompatible change: If MySQL's metadata locking system encounters a lock conflict, the usual schema is to use the try and back-off technique to avoid deadlocks -- this schema consists in releasing all locks and trying to acquire them all in one go. But in a transactional context this algorithm can't be utilized as its not possible to release locks acquired during the course of the transaction without breaking the transaction commitments. To avoid deadlocks in this case, the ER_LOCK_DEADLOCK will be returned if a lock conflict is encountered during a transaction. Let's consider an example: A transaction has two statements that modify table t1, then table t2, and then commits. The first statement of the transaction will acquire a shared metadata lock on table t1, and it will be kept utill COMMIT to ensure serializability. At the moment when the second statement attempts to acquire a shared metadata lock on t2, a concurrent ALTER or DROP statement might have locked t2 exclusively. The prescription of the current locking protocol is that the acquirer of the shared lock backs off -- gives up all his current locks and retries. This implies that the entire multi-statement transaction has to be rolled back. - Incompatible change: FLUSH commands such as FLUSH PRIVILEGES and FLUSH TABLES WITH READ LOCK won't cause locked tables to be implicitly unlocked anymore. --- mysql-test/extra/binlog_tests/drop_table.test | 34 ++++++++ .../mix_innodb_myisam_binlog.test | 4 + mysql-test/include/mix1.inc | 1 + mysql-test/include/mix2.inc | 2 + mysql-test/r/flush_block_commit.result | 18 ++-- .../r/flush_block_commit_notembedded.result | 15 +++- mysql-test/r/innodb.result | 3 +- mysql-test/r/innodb_mysql.result | 1 + mysql-test/r/lock.result | 22 ++++- mysql-test/r/mix2_myisam.result | 2 + mysql-test/r/not_embedded_server.result | 16 +++- .../r/partition_innodb_semi_consistent.result | 2 +- mysql-test/r/partition_sync.result | 27 +----- mysql-test/r/ps.result | 28 +++++- mysql-test/r/read_only_innodb.result | 6 +- .../suite/binlog/r/binlog_row_drop_tbl.result | 16 ++++ .../r/binlog_row_mix_innodb_myisam.result | 1 + .../suite/binlog/r/binlog_stm_drop_tbl.result | 13 +++ .../r/binlog_stm_mix_innodb_myisam.result | 1 + .../suite/binlog/r/binlog_unsafe.result | 20 ++--- .../suite/binlog/t/binlog_row_drop_tbl.test | 5 ++ .../suite/binlog/t/binlog_stm_drop_tbl.test | 5 ++ mysql-test/suite/binlog/t/binlog_stm_row.test | 2 +- .../suite/ndb/r/ndb_index_ordered.result | 15 ---- mysql-test/suite/ndb/t/disabled.def | 1 + mysql-test/suite/ndb/t/ndb_index_ordered.test | 36 +++++--- mysql-test/suite/rpl/t/disabled.def | 2 + .../suite/sys_vars/r/autocommit_func.result | 2 + .../suite/sys_vars/t/autocommit_func.test | 4 + mysql-test/t/flush_block_commit.test | 36 +++++--- .../t/flush_block_commit_notembedded.test | 27 +++++- mysql-test/t/innodb.test | 10 ++- mysql-test/t/lock.test | 37 +++++++- mysql-test/t/not_embedded_server.test | 23 +++-- .../t/partition_innodb_semi_consistent.test | 2 +- mysql-test/t/partition_sync.test | 65 +++++++------- mysql-test/t/ps.test | 41 ++++++++- mysql-test/t/read_only_innodb.test | 14 ++- mysql-test/t/xa.test | 3 +- sql/ha_ndbcluster_binlog.cc | 37 ++------ sql/log_event.cc | 9 +- sql/mdl.cc | 6 +- sql/mysql_priv.h | 2 +- sql/rpl_injector.cc | 8 +- sql/rpl_rli.cc | 15 +--- sql/set_var.cc | 12 ++- sql/slave.cc | 3 + sql/sql_acl.cc | 10 ++- sql/sql_base.cc | 55 +++++++++--- sql/sql_class.cc | 10 +++ sql/sql_parse.cc | 85 ++++++++++++++----- sql/sql_plugin.cc | 4 +- sql/sql_prepare.cc | 25 +++++- sql/sql_servers.cc | 7 +- sql/sql_table.cc | 19 ++++- sql/sql_view.cc | 3 +- sql/table.h | 38 ++++++--- sql/transaction.cc | 17 ++++ tests/mysql_client_test.c | 6 +- 59 files changed, 671 insertions(+), 262 deletions(-) create mode 100644 mysql-test/extra/binlog_tests/drop_table.test create mode 100644 mysql-test/suite/binlog/r/binlog_row_drop_tbl.result create mode 100644 mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result create mode 100644 mysql-test/suite/binlog/t/binlog_row_drop_tbl.test create mode 100644 mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test diff --git a/mysql-test/extra/binlog_tests/drop_table.test b/mysql-test/extra/binlog_tests/drop_table.test new file mode 100644 index 00000000000..c55cbb67560 --- /dev/null +++ b/mysql-test/extra/binlog_tests/drop_table.test @@ -0,0 +1,34 @@ +# +# Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order +# + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +connection con1; +RESET MASTER; +CREATE TABLE t1 (a INT); +SET AUTOCOMMIT=OFF; +BEGIN; +INSERT INTO t1 VALUES(1); + +connection con2; +--send DROP TABLE t1; + +connection con1; +COMMIT; + +connection con2; +--reap + +connection default; + +--disconnect con1 +--disconnect con2 + +let $VERSION=`select version()`; +source include/show_binlog_events.inc; diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test index da0b77fbc23..7c928b565dd 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test @@ -204,6 +204,10 @@ select (@after:=unix_timestamp())*0; # always give repeatable output # the bug, the reap would return immediately after the insert into t2. select (@after-@before) >= 2; +connection con3; +commit; + +connection con2; drop table t1,t2; commit; diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index 194d9e41108..3eaaf37cd83 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -1392,6 +1392,7 @@ SELECT * FROM t1; connection con2; --reap SELECT * FROM t1; +COMMIT; --echo # Switch to connection con1 connection con1; diff --git a/mysql-test/include/mix2.inc b/mysql-test/include/mix2.inc index b4c4a9b8836..001d4cf44d4 100644 --- a/mysql-test/include/mix2.inc +++ b/mysql-test/include/mix2.inc @@ -1994,6 +1994,7 @@ commit; connection b; set autocommit = 0; update t1 set b = 5 where a = 2; +commit; connection a; delimiter |; create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | @@ -2056,6 +2057,7 @@ update t2 set b = b + 5 where a = 1; update t3 set b = b + 5 where a = 1; update t4 set b = b + 5 where a = 1; insert into t5(a) values(20); +commit; connection b; set autocommit = 0; insert into t1(a) values(7); diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index d2197beaaab..da09d07b813 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -1,3 +1,4 @@ +# Save the initial number of concurrent sessions # Establish connection con1 (user=root) # Establish connection con2 (user=root) # Establish connection con3 (user=root) @@ -8,13 +9,15 @@ BEGIN; INSERT INTO t1 VALUES(1); # Switch to connection con2 FLUSH TABLES WITH READ LOCK; -SELECT * FROM t1; -a # Switch to connection con1 +# Sending: COMMIT; # Switch to connection con2 +# Wait until COMMIT gets blocked. +# Verify that 'con1' was blocked and data did not move. SELECT * FROM t1; a +1 UNLOCK TABLES; # Switch to connection con1 # Switch to connection con1 @@ -32,6 +35,7 @@ COMMIT; # Switch to connection con2 a 1 +COMMIT; # Switch to connection con3 UNLOCK TABLES; # Switch to connection con2 @@ -40,8 +44,6 @@ COMMIT; BEGIN; INSERT INTO t1 VALUES(10); FLUSH TABLES WITH READ LOCK; -COMMIT; -UNLOCK TABLES; # Switch to connection con2 FLUSH TABLES WITH READ LOCK; UNLOCK TABLES; @@ -53,5 +55,11 @@ a SHOW CREATE DATABASE test; Database Create Database test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */ -DROP TABLE t1; +COMMIT; +# Cleanup # Switch to connection default and close connections con1, con2, con3 +# We commit open transactions when we disconnect: only then we can +# drop the table. +DROP TABLE t1; +# End of 4.1 tests +# Wait till all disconnects are completed diff --git a/mysql-test/r/flush_block_commit_notembedded.result b/mysql-test/r/flush_block_commit_notembedded.result index 4348dbd67e5..6d8af3f5864 100644 --- a/mysql-test/r/flush_block_commit_notembedded.result +++ b/mysql-test/r/flush_block_commit_notembedded.result @@ -1,17 +1,20 @@ +# Save the initial number of concurrent sessions # Establish connection con1 (user=root) # Establish connection con2 (user=root) # Switch to connection con1 CREATE TABLE t1 (a INT) ENGINE=innodb; RESET MASTER; SET AUTOCOMMIT=0; -INSERT t1 VALUES (1); +SELECT 1; +1 +1 # Switch to connection con2 FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 107 # Switch to connection con1 -COMMIT; +INSERT INTO t1 VALUES (1); # Switch to connection con2 SHOW MASTER STATUS; File Position Binlog_Do_DB Binlog_Ignore_DB @@ -20,4 +23,12 @@ UNLOCK TABLES; # Switch to connection con1 DROP TABLE t1; SET AUTOCOMMIT=1; +create table t1 (a int) engine=innodb; +flush tables with read lock; +begin; +insert into t1 values (1);; +unlock tables; +commit; +drop table t1; # Switch to connection default and close connections con1 and con2 +# Wait till all disconnects are completed diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 17b679d99e4..268bc839483 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2836,10 +2836,10 @@ t2 CREATE TABLE `t2` ( DROP TABLE t2,t1; create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb; insert into t1(a) values (1),(2),(3); +create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | commit; set autocommit = 0; update t1 set b = 5 where a = 2; -create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | set autocommit = 0; insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100), (11),(21),(31),(41),(51),(61),(71),(81),(91),(101), @@ -2887,6 +2887,7 @@ insert into t2(a) values(8); delete from t2 where a = 3; update t4 set b = b + 1 where a = 3; commit; +commit; drop trigger t1t; drop trigger t2t; drop trigger t3t; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index d6aa6a02468..0fe704c13f6 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1618,6 +1618,7 @@ a b SELECT * FROM t1; a b 1 init+con1+con2 +COMMIT; # Switch to connection con1 # 3. test for updated key column: TRUNCATE t1; diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 092c376b34a..c60ec4bc1bd 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -1,4 +1,4 @@ -drop table if exists t1,t2; +drop table if exists t1,t2,t3; CREATE TABLE t1 ( `id` int(11) NOT NULL default '0', `id2` int(11) NOT NULL default '0', `id3` int(11) NOT NULL default '0', `dummy1` char(30) default NULL, PRIMARY KEY (`id`,`id2`), KEY `index_id3` (`id3`)) ENGINE=MyISAM; insert into t1 (id,id2) values (1,1),(1,2),(1,3); LOCK TABLE t1 WRITE; @@ -262,5 +262,25 @@ unlock tables; drop table t1; drop view v1; # +# WL#4284: Transactional DDL locking +# +drop table if exists t1; +create table t1 (a int); +set autocommit= 0; +insert into t1 values (1); +lock table t1 write; +# Disconnect +# Ensure that metadata locks will be released if there is an open +# transaction (autocommit=off) in conjunction with lock tables. +drop table t1; +# Same problem but now for BEGIN +drop table if exists t1; +create table t1 (a int); +begin; +insert into t1 values (1); +# Disconnect +# Ensure that metadata locks held by the transaction are released. +drop table t1; +# # End of 6.0 tests. # diff --git a/mysql-test/r/mix2_myisam.result b/mysql-test/r/mix2_myisam.result index cabc4de8d21..99596c7774d 100644 --- a/mysql-test/r/mix2_myisam.result +++ b/mysql-test/r/mix2_myisam.result @@ -2063,6 +2063,7 @@ insert into t1(a) values (1),(2),(3); commit; set autocommit = 0; update t1 set b = 5 where a = 2; +commit; create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | set autocommit = 0; insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100), @@ -2105,6 +2106,7 @@ update t2 set b = b + 5 where a = 1; update t3 set b = b + 5 where a = 1; update t4 set b = b + 5 where a = 1; insert into t5(a) values(20); +commit; set autocommit = 0; insert into t1(a) values(7); insert into t2(a) values(8); diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index 60c92bd0196..fac38624695 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -1,6 +1,16 @@ -select 1; -1 -1 +call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES"); SHOW VARIABLES like 'slave_skip_errors'; Variable_name Value slave_skip_errors OFF +# +# WL#4284: Transactional DDL locking +# +# FLUSH PRIVILEGES should not implicitly unlock locked tables. +# +drop table if exists t1; +create table t1 (c1 int); +lock tables t1 read; +flush privileges; +ERROR HY000: Table 'host' was not locked with LOCK TABLES +unlock tables; +drop table t1; diff --git a/mysql-test/r/partition_innodb_semi_consistent.result b/mysql-test/r/partition_innodb_semi_consistent.result index 471da4c1c2e..48a1bb3d258 100644 --- a/mysql-test/r/partition_innodb_semi_consistent.result +++ b/mysql-test/r/partition_innodb_semi_consistent.result @@ -102,7 +102,7 @@ a b # Switch to connection con1 # 3. test for updated key column: TRUNCATE t1; -TRUNCATE t2; +DELETE FROM t2; INSERT INTO t1 VALUES (1,'init'); BEGIN; UPDATE t1 SET a = 2, b = CONCAT(b, '+con1') WHERE a = 1; diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result index 31cf0569464..41ca19426fe 100644 --- a/mysql-test/r/partition_sync.result +++ b/mysql-test/r/partition_sync.result @@ -1,25 +1,2 @@ -# -# Bug #43867 ALTER TABLE on a partitioned table -# causes unnecessary deadlocks -# -CREATE TABLE t1 (a int) PARTITION BY RANGE (a) -(PARTITION p0 VALUES LESS THAN (1), -PARTITION p1 VALUES LESS THAN (2)); -INSERT INTO t1 VALUES (0),(1); -# Connection 2 -BEGIN; -SELECT * FROM t1; -a -0 -1 -# Connection 1 -ALTER TABLE t1 DROP PARTITION p3; -ERROR HY000: Error in list of partitions to DROP -# Connection 2 -# This failed with deadlock and should not do so. -SELECT * FROM t1; -a -0 -1 -# Connection 1 -DROP TABLE t1; +# Disabled until Bug#46654 False deadlock on concurrent DML/DDL +# with partitions, inconsistent behavior is backported diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 6c7e83134d7..2526049c539 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -3086,5 +3086,29 @@ DROP PROCEDURE p1; DROP PROCEDURE p2; # End of WL#4435. - -End of 6.0 tests. +# +# WL#4284: Transactional DDL locking +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +BEGIN; +SELECT * FROM t1; +a +# Test that preparing a CREATE TABLE does not take a exclusive metdata lock. +PREPARE stmt1 FROM "CREATE TABLE t1 AS SELECT 1"; +EXECUTE stmt1; +ERROR 42S01: Table 't1' already exists +DEALLOCATE PREPARE stmt1; +DROP TABLE t1; +# +# WL#4284: Transactional DDL locking +# +# Test that metadata locks taken during prepare are released. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +BEGIN; +PREPARE stmt1 FROM "SELECT * FROM t1"; +DROP TABLE t1; +# +# End of 6.0 tests. diff --git a/mysql-test/r/read_only_innodb.result b/mysql-test/r/read_only_innodb.result index 690de085bf9..4cba98900a1 100644 --- a/mysql-test/r/read_only_innodb.result +++ b/mysql-test/r/read_only_innodb.result @@ -7,12 +7,10 @@ insert into table_11733 values(11733); set global read_only=1; select @@global.read_only; @@global.read_only -1 +0 select * from table_11733 ; -a -11733 +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction COMMIT; -ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement set global read_only=0; drop table table_11733 ; drop user test@localhost; diff --git a/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result b/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result new file mode 100644 index 00000000000..8b32e9e5a45 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result @@ -0,0 +1,16 @@ +DROP TABLE IF EXISTS t1; +RESET MASTER; +CREATE TABLE t1 (a INT); +SET AUTOCOMMIT=OFF; +BEGIN; +INSERT INTO t1 VALUES(1); +DROP TABLE t1;; +COMMIT; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT) +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # use `test`; DROP TABLE t1 diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result index 4ccc3b5e797..c1254643a18 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result @@ -238,6 +238,7 @@ select (@after:=unix_timestamp())*0; select (@after-@before) >= 2; (@after-@before) >= 2 1 +commit; drop table t1,t2; commit; begin; diff --git a/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result b/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result new file mode 100644 index 00000000000..f4596b808cf --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS t1; +RESET MASTER; +CREATE TABLE t1 (a INT); +SET AUTOCOMMIT=OFF; +BEGIN; +INSERT INTO t1 VALUES(1); +DROP TABLE t1;; +COMMIT; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT) +master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES(1) +master-bin.000001 # Query # # use `test`; DROP TABLE t1 diff --git a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result index ea081183cd1..f05e184976c 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result @@ -207,6 +207,7 @@ select (@after:=unix_timestamp())*0; select (@after-@before) >= 2; (@after-@before) >= 2 1 +commit; drop table t1,t2; commit; begin; diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 58738a0d97c..3885acd079b 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -320,10 +320,10 @@ INSERT INTO t2 SET a = func_modify_t1(); SHOW BINLOG EVENTS FROM 12283; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 12283 Query 1 12351 BEGIN -master-bin.000001 12351 Table_map 1 12393 table_id: 44 (test.t2) -master-bin.000001 12393 Table_map 1 12435 table_id: 45 (test.t1) -master-bin.000001 12435 Write_rows 1 12473 table_id: 45 -master-bin.000001 12473 Write_rows 1 12511 table_id: 44 flags: STMT_END_F +master-bin.000001 12351 Table_map 1 12393 table_id: 41 (test.t2) +master-bin.000001 12393 Table_map 1 12435 table_id: 42 (test.t1) +master-bin.000001 12435 Write_rows 1 12473 table_id: 42 +master-bin.000001 12473 Write_rows 1 12511 table_id: 41 flags: STMT_END_F master-bin.000001 12511 Query 1 12580 COMMIT DROP TABLE t1,t2; DROP FUNCTION func_modify_t1; @@ -347,12 +347,12 @@ INSERT INTO t1 SET a = 2; SHOW BINLOG EVENTS FROM 13426; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 13426 Query 1 13494 BEGIN -master-bin.000001 13494 Table_map 1 13535 table_id: 47 (test.t1) -master-bin.000001 13535 Table_map 1 13577 table_id: 48 (test.t3) -master-bin.000001 13577 Table_map 1 13619 table_id: 49 (test.t2) -master-bin.000001 13619 Write_rows 1 13657 table_id: 49 -master-bin.000001 13657 Write_rows 1 13695 table_id: 48 -master-bin.000001 13695 Write_rows 1 13729 table_id: 47 flags: STMT_END_F +master-bin.000001 13494 Table_map 1 13535 table_id: 44 (test.t1) +master-bin.000001 13535 Table_map 1 13577 table_id: 45 (test.t3) +master-bin.000001 13577 Table_map 1 13619 table_id: 46 (test.t2) +master-bin.000001 13619 Write_rows 1 13657 table_id: 46 +master-bin.000001 13657 Write_rows 1 13695 table_id: 45 +master-bin.000001 13695 Write_rows 1 13729 table_id: 44 flags: STMT_END_F master-bin.000001 13729 Query 1 13798 COMMIT DROP TABLE t1,t2,t3; "End of tests" diff --git a/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test b/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test new file mode 100644 index 00000000000..06854900612 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test @@ -0,0 +1,5 @@ +# This is a wrapper for drop_table.test so that the same test case can be used +# For both statement and row based bin logs + +-- source include/have_binlog_format_row.inc +-- source extra/binlog_tests/drop_table.test diff --git a/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test b/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test new file mode 100644 index 00000000000..f2b07bb69f6 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test @@ -0,0 +1,5 @@ +# This is a wrapper for drop_table.test so that the same test case can be used +# For both statement and row based bin logs + +-- source include/have_binlog_format_mixed_or_statement.inc +-- source extra/binlog_tests/drop_table.test diff --git a/mysql-test/suite/binlog/t/binlog_stm_row.test b/mysql-test/suite/binlog/t/binlog_stm_row.test index e923faae940..c501df324e8 100644 --- a/mysql-test/suite/binlog/t/binlog_stm_row.test +++ b/mysql-test/suite/binlog/t/binlog_stm_row.test @@ -57,7 +57,7 @@ let $wait_condition= --echo # con1 let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE - state = "Locked" and info = "INSERT INTO t2 VALUES (3)"; + state = "Table lock" and info = "INSERT INTO t2 VALUES (3)"; --source include/wait_condition.inc SELECT RELEASE_LOCK('Bug#34306'); --connection con2 diff --git a/mysql-test/suite/ndb/r/ndb_index_ordered.result b/mysql-test/suite/ndb/r/ndb_index_ordered.result index a29b5343d7c..c99db354314 100644 --- a/mysql-test/suite/ndb/r/ndb_index_ordered.result +++ b/mysql-test/suite/ndb/r/ndb_index_ordered.result @@ -637,21 +637,6 @@ select count(*)- 4 from t1 use index (v) where v > 0000965.00042; count(*)- 4 0 drop table t1; -create table t1(a int primary key, b int not null, index(b)); -insert into t1 values (1,1), (2,2); -set autocommit=0; -begin; -select count(*) from t1; -count(*) -2 -ALTER TABLE t1 ADD COLUMN c int; -select a from t1 where b = 2; -a -2 -show tables; -Tables_in_test -t1 -drop table t1; create table t1 (a int, c varchar(10), primary key using hash (a), index(c)) engine=ndb; insert into t1 (a, c) values (1,'aaa'),(3,'bbb'); diff --git a/mysql-test/suite/ndb/t/disabled.def b/mysql-test/suite/ndb/t/disabled.def index 0fc9a5d3ad6..b2aa3e515be 100644 --- a/mysql-test/suite/ndb/t/disabled.def +++ b/mysql-test/suite/ndb/t/disabled.def @@ -13,3 +13,4 @@ ndb_partition_error2 : Bug#40989 ndb_partition_error2 needs maintenance # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open +ndb_alter_table3 : Bug#45621 2009-06-10 alik A few test files are disabled due to WL#4284 diff --git a/mysql-test/suite/ndb/t/ndb_index_ordered.test b/mysql-test/suite/ndb/t/ndb_index_ordered.test index 782f17ca5b2..c8dfc1de59f 100644 --- a/mysql-test/suite/ndb/t/ndb_index_ordered.test +++ b/mysql-test/suite/ndb/t/ndb_index_ordered.test @@ -333,21 +333,29 @@ select count(*)- 4 from t1 use index (v) where v > 0000965.00042; drop table t1; +# +# Disabled due to WL#4284 +# +# Needs to be reworked. It's not possible anymore to do a non-fast alter table +# on a table that is being used by a pending transaction (transaction holds a +# metadata lock on the table). +# # bug#7798 -create table t1(a int primary key, b int not null, index(b)); -insert into t1 values (1,1), (2,2); -connect (con1,localhost,root,,test); -connect (con2,localhost,root,,test); -connection con1; -set autocommit=0; -begin; -select count(*) from t1; -connection con2; -ALTER TABLE t1 ADD COLUMN c int; -connection con1; -select a from t1 where b = 2; -show tables; -drop table t1; +# create table t1(a int primary key, b int not null, c int, index(b)); +# insert into t1 values (1,1,1), (2,2,2); +# connect (con1,localhost,root,,test); +# connect (con2,localhost,root,,test); +# connection con1; +# set autocommit=0; +# begin; +# select count(*) from t1; +# connection con2; +# ALTER TABLE t1 ADD COLUMN c int +# connection con1; +# select a from t1 where b = 2; +# show tables; +# drop table t1; +# # mysqld 5.0.13 crash, no bug# create table t1 (a int, c varchar(10), diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index 485ba229257..6bd4e7e833a 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -12,3 +12,5 @@ rpl_cross_version : BUG#43913 2009-10-22 luis rpl_cross_version fails with symptom in described in bug report rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux +rpl_failed_optimize : WL#4284: Can't optimize table used by a pending transaction (there is metadata lock on the table). +rpl_read_only : WL#4284: Setting Read only won't succeed until all metadata locks are released. diff --git a/mysql-test/suite/sys_vars/r/autocommit_func.result b/mysql-test/suite/sys_vars/r/autocommit_func.result index 47c2c921022..cb59c9a7b32 100644 --- a/mysql-test/suite/sys_vars/r/autocommit_func.result +++ b/mysql-test/suite/sys_vars/r/autocommit_func.result @@ -104,6 +104,8 @@ id name 2 Record_2 4 Record_4 5 Record_5 +## Commit changes +COMMIT; ## Dropping table t1 ## DROP table t1; ## Disconnecting both connections ## diff --git a/mysql-test/suite/sys_vars/t/autocommit_func.test b/mysql-test/suite/sys_vars/t/autocommit_func.test index 07e15ce40da..716189bb7be 100644 --- a/mysql-test/suite/sys_vars/t/autocommit_func.test +++ b/mysql-test/suite/sys_vars/t/autocommit_func.test @@ -153,6 +153,10 @@ SELECT * from t1; CONNECTION test_con2; SELECT * from t1; +--echo ## Commit changes +CONNECTION test_con1; +COMMIT; + --echo ## Dropping table t1 ## DROP table t1; diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test index 74892def63f..98bca8cdad7 100644 --- a/mysql-test/t/flush_block_commit.test +++ b/mysql-test/t/flush_block_commit.test @@ -6,7 +6,7 @@ # And it requires InnoDB --source include/have_innodb.inc -# Save the initial number of concurrent sessions +--echo # Save the initial number of concurrent sessions --source include/count_sessions.inc --echo # Establish connection con1 (user=root) @@ -29,19 +29,26 @@ BEGIN; INSERT INTO t1 VALUES(1); --echo # Switch to connection con2 connection con2; -FLUSH TABLES WITH READ LOCK; -SELECT * FROM t1; +--send FLUSH TABLES WITH READ LOCK --echo # Switch to connection con1 connection con1; -send COMMIT; # blocked by con2 -sleep 1; +--echo # Sending: +COMMIT; --echo # Switch to connection con2 connection con2; -SELECT * FROM t1; # verify con1 was blocked and data did not move +--reap +--echo # Wait until COMMIT gets blocked. +#let $wait_condition= +# select count(*) = 1 from information_schema.processlist +# where state = "Waiting for release of readlock" and info = "COMMIT"; +#--source include/wait_condition.inc +--echo # Verify that 'con1' was blocked and data did not move. +SELECT * FROM t1; UNLOCK TABLES; --echo # Switch to connection con1 connection con1; -reap; +#--echo # Reaping COMMIT +#--reap # No deadlock ? @@ -63,6 +70,7 @@ COMMIT; # should not be blocked by con3 --echo # Switch to connection con2 connection con2; reap; +COMMIT; --echo # Switch to connection con3 connection con3; reap; @@ -79,8 +87,6 @@ connection con1; BEGIN; INSERT INTO t1 VALUES(10); FLUSH TABLES WITH READ LOCK; -COMMIT; -UNLOCK TABLES; --echo # Switch to connection con2 connection con2; FLUSH TABLES WITH READ LOCK; # bug caused hang here @@ -91,19 +97,21 @@ UNLOCK TABLES; BEGIN; SELECT * FROM t1; SHOW CREATE DATABASE test; - -DROP TABLE t1; +COMMIT; -# Cleanup +--echo # Cleanup --echo # Switch to connection default and close connections con1, con2, con3 connection default; disconnect con1; disconnect con2; disconnect con3; -# End of 4.1 tests +--echo # We commit open transactions when we disconnect: only then we can +--echo # drop the table. +DROP TABLE t1; +--echo # End of 4.1 tests -# Wait till all disconnects are completed +--echo # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/flush_block_commit_notembedded.test b/mysql-test/t/flush_block_commit_notembedded.test index aea38250218..d7ffbd475b4 100644 --- a/mysql-test/t/flush_block_commit_notembedded.test +++ b/mysql-test/t/flush_block_commit_notembedded.test @@ -9,7 +9,7 @@ --source include/have_log_bin.inc --source include/have_innodb.inc -# Save the initial number of concurrent sessions +--echo # Save the initial number of concurrent sessions --source include/count_sessions.inc @@ -24,14 +24,14 @@ connection con1; CREATE TABLE t1 (a INT) ENGINE=innodb; RESET MASTER; SET AUTOCOMMIT=0; -INSERT t1 VALUES (1); +SELECT 1; --echo # Switch to connection con2 connection con2; FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS; --echo # Switch to connection con1 connection con1; -send COMMIT; +send INSERT INTO t1 VALUES (1); --echo # Switch to connection con2 connection con2; sleep 1; @@ -43,11 +43,30 @@ reap; DROP TABLE t1; SET AUTOCOMMIT=1; +# GLR blocks new transactions +create table t1 (a int) engine=innodb; +connection con1; +flush tables with read lock; +connection con2; +begin; +--send insert into t1 values (1); +connection con1; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for release of readlock" and + info = "insert into t1 values (1)"; +--source include/wait_condition.inc +unlock tables; +connection con2; +--reap +commit; +drop table t1; + --echo # Switch to connection default and close connections con1 and con2 connection default; disconnect con1; disconnect con2; -# Wait till all disconnects are completed +--echo # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 2879a4c0b9f..6e7d9b5e780 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1849,16 +1849,15 @@ connect (b,localhost,root,,); connection a; create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb; insert into t1(a) values (1),(2),(3); +delimiter |; +create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | +delimiter ;| commit; connection b; set autocommit = 0; update t1 set b = 5 where a = 2; connection a; -delimiter |; -create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | -delimiter ;| set autocommit = 0; -connection a; insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100), (11),(21),(31),(41),(51),(61),(71),(81),(91),(101), (12),(22),(32),(42),(52),(62),(72),(82),(92),(102), @@ -1922,6 +1921,9 @@ insert into t2(a) values(8); delete from t2 where a = 3; update t4 set b = b + 1 where a = 3; commit; +connection a; +commit; +connection b; drop trigger t1t; drop trigger t2t; drop trigger t3t; diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 51900be4df8..c2aeac05cd1 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -3,7 +3,7 @@ # --disable_warnings -drop table if exists t1,t2; +drop table if exists t1,t2,t3; --enable_warnings CREATE TABLE t1 ( `id` int(11) NOT NULL default '0', `id2` int(11) NOT NULL default '0', `id3` int(11) NOT NULL default '0', `dummy1` char(30) default NULL, PRIMARY KEY (`id`,`id2`), KEY `index_id3` (`id3`)) ENGINE=MyISAM; insert into t1 (id,id2) values (1,1),(1,2),(1,3); @@ -310,6 +310,41 @@ unlock tables; drop table t1; drop view v1; +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +connect(con1,localhost,root,,); +set autocommit= 0; +insert into t1 values (1); +lock table t1 write; +--echo # Disconnect +--echo # Ensure that metadata locks will be released if there is an open +--echo # transaction (autocommit=off) in conjunction with lock tables. +disconnect con1; +connection default; +drop table t1; + +--echo # Same problem but now for BEGIN + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +connect(con1,localhost,root,,); +begin; +insert into t1 values (1); +--echo # Disconnect +--echo # Ensure that metadata locks held by the transaction are released. +disconnect con1; +connection default; +drop table t1; + + --echo # --echo # End of 6.0 tests. --echo # diff --git a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test index fa2b659ec57..917d5871682 100644 --- a/mysql-test/t/not_embedded_server.test +++ b/mysql-test/t/not_embedded_server.test @@ -4,12 +4,6 @@ -- source include/not_embedded.inc -# -# Produce output -# - -select 1; - # The following fails sporadically because 'check-testcase' runs # queries before this test and there is no way to guarantee that any # previous process finishes. The purpose of the test is not clearly @@ -36,6 +30,8 @@ select 1; #execute stmt1; #deallocate prepare stmt1; +call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES"); + # # Bug#43835: SHOW VARIABLES does not include 0 for slave_skip_errors # @@ -43,3 +39,18 @@ select 1; SHOW VARIABLES like 'slave_skip_errors'; # End of 5.1 tests + +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # +--echo # FLUSH PRIVILEGES should not implicitly unlock locked tables. +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (c1 int); +lock tables t1 read; +--error ER_TABLE_NOT_LOCKED +flush privileges; +unlock tables; +drop table t1; diff --git a/mysql-test/t/partition_innodb_semi_consistent.test b/mysql-test/t/partition_innodb_semi_consistent.test index 6a6a7cf958e..2bf879603a4 100644 --- a/mysql-test/t/partition_innodb_semi_consistent.test +++ b/mysql-test/t/partition_innodb_semi_consistent.test @@ -157,7 +157,7 @@ connection con1; --echo # 3. test for updated key column: TRUNCATE t1; -TRUNCATE t2; +DELETE FROM t2; INSERT INTO t1 VALUES (1,'init'); diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test index a732b35b8b9..5d2b25e87f3 100644 --- a/mysql-test/t/partition_sync.test +++ b/mysql-test/t/partition_sync.test @@ -2,38 +2,41 @@ # Save the initial number of concurrent sessions. --source include/count_sessions.inc ---echo # ---echo # Bug #43867 ALTER TABLE on a partitioned table ---echo # causes unnecessary deadlocks ---echo # +--echo # Disabled until Bug#46654 False deadlock on concurrent DML/DDL +--echo # with partitions, inconsistent behavior is backported -CREATE TABLE t1 (a int) PARTITION BY RANGE (a) -(PARTITION p0 VALUES LESS THAN (1), - PARTITION p1 VALUES LESS THAN (2)); - -INSERT INTO t1 VALUES (0),(1); - -connect(con1,localhost,root); - ---echo # Connection 2 -connection con1; -BEGIN; -SELECT * FROM t1; - ---echo # Connection 1 -connection default; ---error ER_DROP_PARTITION_NON_EXISTENT -ALTER TABLE t1 DROP PARTITION p3; - ---echo # Connection 2 -connection con1; ---echo # This failed with deadlock and should not do so. -SELECT * FROM t1; - ---echo # Connection 1 -connection default; -disconnect con1; -DROP TABLE t1; +#--echo # +#--echo # Bug #43867 ALTER TABLE on a partitioned table +#--echo # causes unnecessary deadlocks +#--echo # +# +#CREATE TABLE t1 (a int) PARTITION BY RANGE (a) +#(PARTITION p0 VALUES LESS THAN (1), +# PARTITION p1 VALUES LESS THAN (2)); +# +#INSERT INTO t1 VALUES (0),(1); +# +#connect(con1,localhost,root); +# +#--echo # Connection 2 +#connection con1; +#BEGIN; +#SELECT * FROM t1; +# +#--echo # Connection 1 +#connection default; +#--error ER_DROP_PARTITION_NON_EXISTENT +#ALTER TABLE t1 DROP PARTITION p3; +# +#--echo # Connection 2 +#connection con1; +#--echo # This failed with deadlock and should not do so. +#SELECT * FROM t1; +# +#--echo # Connection 1 +#connection default; +#disconnect con1; +#DROP TABLE t1; # Check that all connections opened by test cases in this file are really diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 844be582290..6cebbe6c7a1 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -3211,7 +3211,44 @@ DROP PROCEDURE p2; ########################################################################### ---echo ---echo End of 6.0 tests. + +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1 (a INT); +BEGIN; +SELECT * FROM t1; +--echo # Test that preparing a CREATE TABLE does not take a exclusive metdata lock. +PREPARE stmt1 FROM "CREATE TABLE t1 AS SELECT 1"; +--error ER_TABLE_EXISTS_ERROR +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; +DROP TABLE t1; + +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # +--echo # Test that metadata locks taken during prepare are released. +--echo # + +connect(con1,localhost,root,,); +connection default; +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1 (a INT); +connection con1; +BEGIN; +PREPARE stmt1 FROM "SELECT * FROM t1"; +connection default; +DROP TABLE t1; +disconnect con1; + +--echo # +--echo # End of 6.0 tests. ########################################################################### diff --git a/mysql-test/t/read_only_innodb.test b/mysql-test/t/read_only_innodb.test index f8c25fdee1d..98e704e25c7 100644 --- a/mysql-test/t/read_only_innodb.test +++ b/mysql-test/t/read_only_innodb.test @@ -16,6 +16,8 @@ DROP TABLE IF EXISTS table_11733 ; grant CREATE, SELECT, DROP on *.* to test@localhost; connect (con1,localhost,test,,test); +connect (con2,localhost,root,,); + connection default; set global read_only=0; @@ -28,15 +30,22 @@ BEGIN; insert into table_11733 values(11733); connection default; -set global read_only=1; +send set global read_only=1; + +connection con2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Flushing tables" and info = "set global read_only=1"; +--source include/wait_condition.inc connection con1; select @@global.read_only; +--error ER_LOCK_DEADLOCK select * from table_11733 ; --- error ER_OPTION_PREVENTS_STATEMENT COMMIT; connection default; +reap; set global read_only=0; drop table table_11733 ; drop user test@localhost; @@ -81,5 +90,6 @@ DROP TABLE t1; DROP USER test@localhost; disconnect con1; +disconnect con2; --echo echo End of 5.1 tests diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index f84d822170f..0f705ae20c6 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -76,9 +76,10 @@ xa rollback 'testa','testb'; xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'; select * from t1; -drop table t1; disconnect con1; +connection default; +drop table t1; # # Bug#28323: Server crashed in xid cache operations diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 08abb88e768..7ce318394d4 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -2347,7 +2347,7 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) tables->required_type= FRMTYPE_TABLE; uint counter; thd->clear_error(); - if (open_tables(thd, &tables, &counter, MYSQL_LOCK_IGNORE_FLUSH)) + if (simple_open_n_lock_tables(thd, tables)) { if (thd->killed) sql_print_error("NDB Binlog: Opening ndb_binlog_index: killed"); @@ -2381,28 +2381,11 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) ulong saved_options= thd->options; thd->options&= ~(OPTION_BIN_LOG); - for ( ; ; ) /* loop for need_reopen */ + if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index)) { - if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index)) - { - error= -1; - goto add_ndb_binlog_index_err; - } - - if (lock_tables(thd, &binlog_tables, 1, 0, &need_reopen)) - { - if (need_reopen) - { - TABLE_LIST *p_binlog_tables= &binlog_tables; - close_tables_for_reopen(thd, &p_binlog_tables, FALSE); - ndb_binlog_index= 0; - continue; - } - sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index"); - error= -1; - goto add_ndb_binlog_index_err; - } - break; + sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index"); + error= -1; + goto add_ndb_binlog_index_err; } /* @@ -2428,13 +2411,6 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) } - if (! thd->locked_tables_mode) /* Is always TRUE */ - { - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; - } - thd->options= saved_options; - return 0; add_ndb_binlog_index_err: close_thread_tables(thd); ndb_binlog_index= 0; @@ -3905,9 +3881,6 @@ restart: { static char db[]= ""; thd->db= db; - if (ndb_binlog_running) - open_ndb_binlog_index(thd, &ndb_binlog_index); - thd->db= db; } do_ndbcluster_binlog_close_connection= BCCC_running; for ( ; !((ndbcluster_binlog_terminating || diff --git a/sql/log_event.cc b/sql/log_event.cc index e41bb690e8a..aa8dd6e9bff 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5314,10 +5314,17 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int Xid_log_event::do_apply_event(Relay_log_info const *rli) { + bool res; /* For a slave Xid_log_event is COMMIT */ general_log_print(thd, COM_QUERY, "COMMIT /* implicit, from Xid_log_event */"); - return trans_commit(thd); + if (!(res= trans_commit(thd))) + { + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + } + return res; } Log_event::enum_skip_reason diff --git a/sql/mdl.cc b/sql/mdl.cc index 1d591bb2244..eb8fcdb323e 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -491,7 +491,7 @@ void MDL_ticket::destroy(MDL_ticket *ticket) @sa THD::enter_cond()/exit_cond()/killed. @note We can't use THD::enter_cond()/exit_cond()/killed directly here - since this will make metadata subsystem dependant on THD class + since this will make metadata subsystem dependent on THD class and thus prevent us from writing unit tests for it. And usage of wrapper functions to access THD::killed/enter_cond()/exit_cond() will probably introduce too much overhead. @@ -881,6 +881,7 @@ static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) if (conflicting_ticket->is_shared()) { THD *conflicting_thd= conflicting_ticket->get_ctx()->get_thd(); + DBUG_ASSERT(thd != conflicting_thd); /* Self-deadlock */ woke= mysql_notify_thread_having_shared_lock(thd, conflicting_thd); } return woke; @@ -1089,7 +1090,6 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() old_msg= MDL_ENTER_COND(thd, mysys_var); - /* Since we should have already acquired an intention exclusive global lock this call is only enforcing asserts. @@ -1164,7 +1164,7 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() @param conflict [out] Indicates that conflicting lock exists @retval TRUE Failure either conflicting lock exists or some error - occured (probably OOM). + occurred (probably OOM). @retval FALSE Success, lock was acquired. FIXME: Compared to lock_table_name_if_not_cached() diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3f6ed2b1cb0..159613c58a0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -827,7 +827,7 @@ extern my_decimal decimal_zero; void free_items(Item *item); void cleanup_items(Item *item); class THD; -void close_thread_tables(THD *thd, bool skip_mdl= 0); +void close_thread_tables(THD *thd, bool is_back_off= 0); #ifndef NO_EMBEDDED_ACCESS_CHECKS bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 738341cc034..9d82307d2e7 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -83,10 +83,16 @@ int injector::transaction::commit() explicitly. */ trans_commit_stmt(m_thd); - trans_commit(m_thd); + if (!trans_commit(m_thd)) + { + close_thread_tables(m_thd); + if (!m_thd->locked_tables_mode) + m_thd->mdl_context.release_all_locks(); + } DBUG_RETURN(0); } + int injector::transaction::use_table(server_id_type sid, table tbl) { DBUG_ENTER("injector::transaction::use_table"); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 695b160fc01..b4554bb4b6c 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1189,6 +1189,8 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) } m_table_map.clear_tables(); slave_close_thread_tables(thd); + if (error && !thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); clear_flag(IN_STMT); /* Cleanup for the flags that have been set at do_apply_event. @@ -1200,13 +1202,6 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) void Relay_log_info::clear_tables_to_lock() { - /* - Deallocating elements of table list below will also free memory where - meta-data locks are stored. So we want to be sure that we don't have - any references to this memory left. - */ - DBUG_ASSERT(!current_thd->mdl_context.has_locks()); - while (tables_to_lock) { uchar* to_free= reinterpret_cast(tables_to_lock); @@ -1225,12 +1220,6 @@ void Relay_log_info::clear_tables_to_lock() void Relay_log_info::slave_close_thread_tables(THD *thd) { - /* - Since we use same memory chunks for allocation of metadata lock - objects for tables as we use for allocating corresponding elements - of 'tables_to_lock' list, we have to release metadata locks by - closing tables before calling clear_tables_to_lock(). - */ close_thread_tables(thd); clear_tables_to_lock(); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 266cdd9ad6d..dd009541274 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3190,9 +3190,15 @@ static bool set_option_autocommit(THD *thd, set_var *var) need to commit any outstanding transactions. */ if (var->save_result.ulong_value != 0 && - (thd->options & OPTION_NOT_AUTOCOMMIT) && - trans_commit(thd)) - return 1; + (thd->options & OPTION_NOT_AUTOCOMMIT)) + { + if (trans_commit(thd)) + return TRUE; + + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + } if (var->save_result.ulong_value != 0) thd->options&= ~((sys_var_thd_bit*) var->var)->bit_flag; diff --git a/sql/slave.cc b/sql/slave.cc index ed722305b29..0316ae2591d 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2432,6 +2432,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) { exec_res= 0; trans_rollback(thd); + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); /* chance for concurrent connection to get more locks */ safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE), (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index aa96483cf09..f4a182b321f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -30,6 +30,7 @@ #include #include "sp_head.h" #include "sp.h" +#include "transaction.h" time_t mysql_db_table_last_check= 0L; @@ -676,9 +677,6 @@ my_bool acl_reload(THD *thd) my_bool return_val= 1; DBUG_ENTER("acl_reload"); - /* Can't have locked tables here. */ - thd->locked_tables_list.unlock_locked_tables(thd); - /* To avoid deadlocks we should obtain table locks before obtaining acl_cache->lock mutex. @@ -732,7 +730,10 @@ my_bool acl_reload(THD *thd) if (old_initialized) pthread_mutex_unlock(&acl_cache->lock); end: + trans_commit_implicit(thd); close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); DBUG_RETURN(return_val); } @@ -3900,7 +3901,10 @@ my_bool grant_reload(THD *thd) free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); + trans_commit_implicit(thd); close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); /* It is OK failing to load procs_priv table because we may be diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0441458510f..42fc3ba0566 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1478,11 +1478,22 @@ void close_thread_tables(THD *thd, if (thd->open_tables) close_open_tables(thd); - thd->mdl_context.release_all_locks(); if (!is_back_off) { thd->mdl_context.remove_all_requests(); } + + /* + Defer the release of metadata locks until the current transaction + is either committed or rolled back. This prevents other statements + from modifying the table for the entire duration of this transaction. + This provides commitment ordering for guaranteeing serializability + across multiple transactions. + */ + if (!thd->in_multi_stmt_transaction() || + (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) + thd->mdl_context.release_all_locks(); + DBUG_VOID_RETURN; } @@ -2284,7 +2295,7 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, { thd->mdl_context.add_request(mdl_request); - if (table_list->open_type) + if (table_list->lock_strategy) { /* In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table @@ -2358,10 +2369,16 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, IMPLEMENTATION Uses a cache of open tables to find a table not in use. - If table list element for the table to be opened has "open_type" set - to OPEN_OR_CREATE and table does not exist, this function will take - exclusive metadata lock on the table, also it will do this if - "open_type" is TAKE_EXCLUSIVE_MDL. + If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened + only if it exists. If the open strategy is OPEN_STUB, the underlying table + is never opened. In both cases, metadata locks are always taken according + to the lock strategy. + + This function will take a exclusive metadata lock on the table if + TABLE_LIST::lock_strategy is EXCLUSIVE_DOWNGRADABLE_MDL or EXCLUSIVE_MDL. + If the lock strategy is EXCLUSIVE_DOWNGRADABLE_MDL and opening the table + is successful, the exclusive metadata lock is downgraded to a shared + lock. RETURN TRUE Open failed. "action" parameter may contain type of action @@ -2595,7 +2612,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(TRUE); } - if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) + if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS) { bool exists; @@ -2609,7 +2626,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } /* Table exists. Let us try to open it. */ } - else if (table_list->open_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL) + else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB) { pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(FALSE); @@ -2794,7 +2811,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table exists now we should downgrade our exclusive metadata lock on this table to shared metadata lock. */ - if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) + if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL) mdl_ticket->downgrade_exclusive_lock(); table->mdl_ticket= mdl_ticket; @@ -3623,7 +3640,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) /* Also used for indicating that prelocking is need */ TABLE_LIST **query_tables_last_own; bool safe_to_ignore_table; - + bool has_locks= thd->mdl_context.has_locks(); DBUG_ENTER("open_tables"); /* temporary mem_root for new .frm parsing. @@ -3763,6 +3780,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) { if (action) { + /* + We have met a exclusive metadata lock or a old version of table and + we are inside a transaction that already hold locks. We can't follow + the locking protocol in this scenario as it might lead to deadlocks. + */ + if (thd->in_multi_stmt_transaction() && has_locks) + { + my_error(ER_LOCK_DEADLOCK, MYF(0)); + result= -1; + goto err; + } + /* We have met exclusive metadata lock or old version of table. Now we have to close all tables which are not up to date/release metadata @@ -3841,7 +3870,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) Special types of open can succeed but still don't set TABLE_LIST::table to anything. */ - if (tables->open_type && !tables->table) + if (tables->open_strategy && !tables->table) continue; /* @@ -4122,7 +4151,7 @@ retry: if (!error) { /* - We can't have a view or some special "open_type" in this function + We can't have a view or some special "open_strategy" in this function so there should be a TABLE instance. */ DBUG_ASSERT(table_list->table); @@ -4662,6 +4691,8 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off) for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) tmp->table= 0; close_thread_tables(thd, is_back_off); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 654d7ad718d..7252f078f81 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -995,8 +995,18 @@ void THD::cleanup(void) trans_rollback(this); xid_cache_delete(&transaction.xid_state); } + locked_tables_list.unlock_locked_tables(this); + /* + If the thread was in the middle of an ongoing transaction (rolled + back a few lines above) or under LOCK TABLES (unlocked the tables + and left the mode a few lines above), there will be outstanding + metadata locks. Release them. + */ + DBUG_ASSERT(open_tables == NULL); + mdl_context.release_all_locks(); + #if defined(ENABLED_DEBUG_SYNC) /* End the Debug Sync Facility. See debug_sync.cc. */ debug_sync_end_thread(this); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 579fbcea4ea..ddc163072a7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -122,11 +122,11 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) @param mask Bitmask used for the SQL command match. */ -static bool opt_implicit_commit(THD *thd, uint mask) +static bool stmt_causes_implicit_commit(THD *thd, uint mask) { LEX *lex= thd->lex; - bool res= FALSE, skip= FALSE; - DBUG_ENTER("opt_implicit_commit"); + bool skip= FALSE; + DBUG_ENTER("stmt_causes_implicit_commit"); if (!(sql_command_flags[lex->sql_command] & mask)) DBUG_RETURN(FALSE); @@ -147,15 +147,7 @@ static bool opt_implicit_commit(THD *thd, uint mask) break; } - if (!skip) - { - /* Commit or rollback the statement transaction. */ - thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - /* Commit the normal transaction if one is active. */ - res= trans_commit_implicit(thd); - } - - DBUG_RETURN(res); + DBUG_RETURN(!skip); } @@ -1168,6 +1160,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ulong options= (ulong) (uchar) packet[0]; if (trans_commit_implicit(thd)) break; + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); if (check_global_access(thd,RELOAD_ACL)) break; general_log_print(thd, command, NullS); @@ -1196,6 +1191,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; if (trans_commit_implicit(thd)) break; + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); my_ok(thd); break; } @@ -1942,8 +1940,18 @@ mysql_execute_command(THD *thd) not run in it's own transaction it may simply never appear on the slave in case the outside transaction rolls back. */ - if (opt_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN)) - goto error; + if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN)) + { + /* Commit or rollback the statement transaction. */ + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + /* Commit the normal transaction if one is active. */ + if (trans_commit_implicit(thd)) + goto error; + /* Close tables and release metadata locks. */ + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + } switch (lex->sql_command) { @@ -2363,7 +2371,9 @@ case SQLCOM_PREPARE: if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); - create_table->open_type= TABLE_LIST::OPEN_OR_CREATE; + /* Set strategies: reset default or 'prepared' values. */ + create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL; } if (!(res= open_and_lock_tables(thd, lex->query_tables))) @@ -3306,6 +3316,7 @@ end_with_restore_list: if (thd->options & OPTION_TABLE_LOCK) { trans_commit_implicit(thd); + thd->mdl_context.release_all_locks(); thd->options&= ~(OPTION_TABLE_LOCK); } if (thd->global_read_lock) @@ -3317,6 +3328,8 @@ end_with_restore_list: /* we must end the trasaction first, regardless of anything */ if (trans_commit_implicit(thd)) goto error; + /* release transactional metadata locks. */ + thd->mdl_context.release_all_locks(); if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; @@ -3346,6 +3359,13 @@ end_with_restore_list: */ trans_rollback_stmt(thd); trans_commit_implicit(thd); + /* + Close tables and release metadata locks otherwise a later call to + close_thread_tables might not release the locks if autocommit is off. + */ + close_thread_tables(thd); + DBUG_ASSERT(!thd->locked_tables_mode); + thd->mdl_context.release_all_locks(); thd->options&= ~(OPTION_TABLE_LOCK); } else @@ -3836,6 +3856,8 @@ end_with_restore_list: thd->locked_tables_mode == LTM_LOCK_TABLES); if (trans_commit(thd)) goto error; + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); /* Begin transaction with the same isolation level. */ if (lex->tx_chain && trans_begin(thd)) goto error; @@ -3849,6 +3871,8 @@ end_with_restore_list: thd->locked_tables_mode == LTM_LOCK_TABLES); if (trans_rollback(thd)) goto error; + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); /* Begin transaction with the same isolation level. */ if (lex->tx_chain && trans_begin(thd)) goto error; @@ -4196,6 +4220,12 @@ create_sp_error: if (trans_commit_implicit(thd)) goto error; + + close_thread_tables(thd); + + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (sp_automatic_privileges && !opt_noacl && sp_revoke_privileges(thd, db, name, @@ -4375,11 +4405,15 @@ create_sp_error: case SQLCOM_XA_COMMIT: if (trans_xa_commit(thd)) goto error; + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); my_ok(thd); break; case SQLCOM_XA_ROLLBACK: if (trans_xa_rollback(thd)) goto error; + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); my_ok(thd); break; case SQLCOM_XA_RECOVER: @@ -4524,10 +4558,20 @@ finish: start_waiting_global_read_lock(thd); } - /* If commit fails, we should be able to reset the OK status. */ - thd->stmt_da->can_overwrite_status= TRUE; - opt_implicit_commit(thd, CF_IMPLICIT_COMMIT_END); - thd->stmt_da->can_overwrite_status= FALSE; + if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END)) + { + /* If commit fails, we should be able to reset the OK status. */ + thd->stmt_da->can_overwrite_status= TRUE; + /* Commit or rollback the statement transaction. */ + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + /* Commit the normal transaction if one is active. */ + trans_commit_implicit(thd); + /* Close tables and release metadata locks. */ + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + thd->stmt_da->can_overwrite_status= FALSE; + } DBUG_RETURN(res || thd->is_error()); } @@ -6508,6 +6552,9 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, query_cache.flush(); // RESET QUERY CACHE } #endif /*HAVE_QUERY_CACHE*/ + + DBUG_ASSERT(thd->locked_tables_mode || !thd->mdl_context.has_locks()); + /* Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too (see sql_yacc.yy) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 77b5552d977..b7efe13e26e 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1349,6 +1349,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) #ifdef EMBEDDED_LIBRARY bool table_exists; #endif /* EMBEDDED_LIBRARY */ + MDL_request mdl_request; DBUG_ENTER("plugin_load"); if (!(new_thd= new THD)) @@ -1366,7 +1367,8 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) tables.alias= tables.table_name= (char*)"plugin"; tables.lock_type= TL_READ; tables.db= new_thd->db; - alloc_mdl_requests(&tables, tmp_root); + tables.mdl_request= &mdl_request; + mdl_request.init(0, tables.db, tables.table_name); #ifdef EMBEDDED_LIBRARY /* diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 5efa0cea7a9..9c7949eaf1d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1673,7 +1673,13 @@ static bool mysql_test_create_table(Prepared_statement *stmt) if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); - create_table->open_type= TABLE_LIST::OPEN_OR_CREATE; + /* + The open and lock strategies will be set again once the + statement is executed. These values are only meaningful + for the prepare phase. + */ + create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + create_table->lock_strategy= TABLE_LIST::SHARED_MDL; } if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) @@ -3130,6 +3136,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) bool error; Statement stmt_backup; Query_arena *old_stmt_arena; + MDL_ticket *mdl_savepoint= NULL; DBUG_ENTER("Prepared_statement::prepare"); /* If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. @@ -3188,6 +3195,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) */ DBUG_ASSERT(thd->change_list.is_empty()); + /* + Marker used to release metadata locks acquired while the prepared + statement is being checked. + */ + if (thd->in_multi_stmt_transaction()) + mdl_savepoint= thd->mdl_context.mdl_savepoint(); + /* The only case where we should have items in the thd->free_list is after stmt->set_params_from_vars(), which may in some cases create @@ -3211,6 +3225,15 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex_end(lex); cleanup_stmt(); + /* + If not inside a multi-statement transaction, the metadata locks have + already been released and the rollback_to_savepoint is a nop. + Otherwise, release acquired locks -- a NULL mdl_savepoint means that + all locks are going to be released or that the transaction didn't + own any locks. + */ + if (!thd->locked_tables_mode) + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 40cdfeed946..c46a01cd534 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -39,6 +39,7 @@ #include #include "sp_head.h" #include "sp.h" +#include "transaction.h" /* We only use 1 mutex to guard the data structures - THR_LOCK_servers. @@ -224,9 +225,6 @@ bool servers_reload(THD *thd) bool return_val= TRUE; DBUG_ENTER("servers_reload"); - /* Can't have locked tables here */ - thd->locked_tables_list.unlock_locked_tables(thd); - DBUG_PRINT("info", ("locking servers_cache")); rw_wrlock(&THR_LOCK_servers); @@ -252,7 +250,10 @@ bool servers_reload(THD *thd) } end: + trans_commit_implicit(thd); close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); DBUG_PRINT("info", ("unlocking servers_cache")); rw_unlock(&THR_LOCK_servers); DBUG_RETURN(return_val); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 42ae0a89458..8fdaa2cd93a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1974,7 +1974,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, } /* Probably a non-temporary table. */ - non_temp_tables_count++; + if (!drop_temporary) + non_temp_tables_count++; /* If row-based replication is used and the table is not a @@ -4738,6 +4739,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); lex->reset_query_tables_list(FALSE); table->table=0; // For query cache if (protocol->write()) @@ -4786,7 +4789,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, { DBUG_PRINT("admin", ("recreating table")); trans_rollback_stmt(thd); + trans_rollback(thd); close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); tmp_disable_binlog(thd); // binlogging is done by caller if wanted result_code= mysql_recreate_table(thd, table); reenable_binlog(thd); @@ -4899,14 +4905,17 @@ send_result_message: reopen the table and do ha_innobase::analyze() on it. We have to end the row, so analyze could return more rows. */ + trans_commit_stmt(thd); + trans_commit(thd); + close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); protocol->store(STRING_WITH_LEN("note"), system_charset_info); protocol->store(STRING_WITH_LEN( "Table does not support optimize, doing recreate + analyze instead"), system_charset_info); if (protocol->write()) goto err; - trans_commit_stmt(thd); - close_thread_tables(thd); DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze...")); TABLE_LIST *save_next_local= table->next_local, *save_next_global= table->next_global; @@ -4923,7 +4932,10 @@ send_result_message: if (thd->stmt_da->is_ok()) thd->stmt_da->reset_diagnostics_area(); trans_commit_stmt(thd); + trans_commit(thd); close_thread_tables(thd); + if (!thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); if (!result_code) // recreation went ok { if ((table->table= open_ltable(thd, table, lock_type, 0)) && @@ -5017,6 +5029,7 @@ send_result_message: query_cache_invalidate3(thd, table->table, 0); } } + /* Error path, a admin command failed. */ trans_commit_stmt(thd); trans_commit_implicit(thd); close_thread_tables(thd); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index b370fc79b17..b36ff6b6743 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -395,7 +395,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; lex->link_first_table_back(view, link_to_local); - view->open_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL; + view->open_strategy= TABLE_LIST::OPEN_STUB; + view->lock_strategy= TABLE_LIST::EXCLUSIVE_MDL; if (open_and_lock_tables(thd, lex->query_tables)) { diff --git a/sql/table.h b/sql/table.h index b6ea372b41e..1762ae8785d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1350,24 +1350,36 @@ struct TABLE_LIST bool prelocking_placeholder; /** Indicates that if TABLE_LIST object corresponds to the table/view - which requires special handling/meta-data locking. + which requires special handling. */ enum { - /* Normal open, shared metadata lock should be taken. */ - NORMAL_OPEN= 0, + /* Normal open. */ + OPEN_NORMAL= 0, + /* Associate a table share only if the the table exists. */ + OPEN_IF_EXISTS, + /* Don't associate a table share. */ + OPEN_STUB + } open_strategy; + /** + Indicates the locking strategy for the object being opened: + whether the associated metadata lock is shared or exclusive. + */ + enum + { + /* Take a shared metadata lock before the object is opened. */ + SHARED_MDL= 0, /* - It's target table of CREATE TABLE ... SELECT so we should - either open table if it exists (and take shared metadata lock) - or take exclusive metadata lock if it doesn't exist. + Take a exclusive metadata lock before the object is opened. + If opening is successful, downgrade to a shared lock. */ - OPEN_OR_CREATE, - /* - It's target view of CREATE/ALTER VIEW. We should take exclusive - metadata lock for this table list element. - */ - TAKE_EXCLUSIVE_MDL - } open_type; + EXCLUSIVE_DOWNGRADABLE_MDL, + /* Take a exclusive metadata lock before the object is opened. */ + EXCLUSIVE_MDL + } lock_strategy; + /* For transactional locking. */ + int lock_timeout; /* NOWAIT or WAIT [X] */ + bool lock_transactional; /* If transactional lock requested. */ bool internal_tmp_table; /** TRUE if an alias for this table was specified in the SQL. */ bool is_alias; diff --git a/sql/transaction.cc b/sql/transaction.cc index 7bfaf4846cf..d1c7244ba83 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -99,6 +99,12 @@ bool trans_begin(THD *thd, uint flags) if (trans_commit_implicit(thd)) DBUG_RETURN(TRUE); + /* + Release transactional metadata locks only after the + transaction has been committed. + */ + thd->mdl_context.release_all_locks(); + thd->options|= OPTION_BEGIN; thd->server_status|= SERVER_STATUS_IN_TRANS; @@ -331,6 +337,13 @@ bool trans_savepoint(THD *thd, LEX_STRING name) newsv->prev= thd->transaction.savepoints; thd->transaction.savepoints= newsv; + /* + Remember the last acquired lock before the savepoint was set. + This is used as a marker to only release locks acquired after + the setting of this savepoint. + */ + newsv->mdl_savepoint = thd->mdl_context.mdl_savepoint(); + DBUG_RETURN(FALSE); } @@ -375,6 +388,10 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) thd->transaction.savepoints= sv; + /* Release metadata locks that were acquired during this savepoint unit. */ + if (!res && !thd->locked_tables_mode) + thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint); + DBUG_RETURN(test(res)); } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index c9edd210c32..56e1d609754 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18479,8 +18479,8 @@ static void test_wl4284_1() mysql_free_result(result); - /* set AUTOCOMMIT to OFF */ - rc= mysql_autocommit(mysql, FALSE); + /* set AUTOCOMMIT to ON */ + rc= mysql_autocommit(mysql, TRUE); myquery(rc); rc= mysql_query(mysql, "DROP TABLE trans"); @@ -18637,6 +18637,8 @@ static void test_bug40365(void) DIE_UNLESS(tm[i].day == 0); } mysql_stmt_close(stmt); + rc= mysql_commit(mysql); + myquery(rc); DBUG_VOID_RETURN; } From 80d9f0d9ff83a765ed168e27d91c822818b4b88c Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 5 Dec 2009 04:14:48 +0100 Subject: [PATCH 096/466] Fix my_atomic.h on Solaris, do not redefine macros already defined in include/atomic/solaris.h --- include/my_atomic.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/my_atomic.h b/include/my_atomic.h index 85cf87165fb..95868e4b810 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -69,6 +69,7 @@ #include "atomic/nolock.h" #endif +#ifndef MY_ATOMICS_MADE #ifndef make_atomic_cas_body /* nolock.h was not able to generate even a CAS function, fall back */ #include "atomic/rwlock.h" @@ -277,6 +278,7 @@ make_atomic_store(ptr) #undef make_atomic_load_body #undef make_atomic_store_body #undef make_atomic_fas_body +#endif /* MY_ATOMICS_MADE */ #undef intptr /* From b358cc7f82dde7808bda57d4bed72e23c36e658e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 5 Dec 2009 13:16:30 +0000 Subject: [PATCH 097/466] Fix creation of exports file on Unix --- cmake/libutils.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index 0c34754bc75..dc151bc12f2 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -76,7 +76,7 @@ MACRO(CREATE_EXPORT_FILE VAR TARGET API_FUNCTIONS) CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS}) SET(${VAR} ${DUMMY} ${EXPORTS}) ELSE() - SET(EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${target}_exports_file.cc) + SET(EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_exports_file.cc) SET(CONTENT) FOREACH(FUNC ${API_FUNCTIONS}) SET(CONTENT "${CONTENT} extern void* ${FUNC}\;\n") @@ -241,7 +241,9 @@ MACRO(MERGE_LIBRARIES) ENDIF() ENDFOREACH() ENDIF() - CREATE_EXPORT_FILE(SRC ${TARGET} "${EXPORTS}") + + + CREATE_EXPORT_FILE(SRC ${TARGET} "${ARG_EXPORTS}") ADD_LIBRARY(${TARGET} SHARED ${SRC}) TARGET_LINK_LIBRARIES(${TARGET} ${LIBS}) IF(ARG_OUTPUT_NAME) From 456395813a350329e7aa9fc44fada27b6d4f0ac0 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 5 Dec 2009 16:15:17 +0100 Subject: [PATCH 098/466] fdatasync is not documented on Snow Leopard and is not in unistd.h CMake finds it as it is expored by system libraries, though attempts to use it will result bring a warning. Fix by using CHECK_SYMBOL_EXISTS instead of CHECK_FUNCTION_EXISTS --- configure.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configure.cmake b/configure.cmake index d16e19166d5..b34d5b070d2 100644 --- a/configure.cmake +++ b/configure.cmake @@ -21,6 +21,7 @@ INCLUDE (CheckLibraryExists) INCLUDE (CheckFunctionExists) INCLUDE (CheckCCompilerFlag) INCLUDE (CheckCSourceRuns) +INCLUDE (CheckSymbolExists) # Sometimes it is handy to know if PIC option @@ -521,7 +522,7 @@ ENDIF() CHECK_FUNCTION_EXISTS_UNIX (fchmod HAVE_FCHMOD) CHECK_FUNCTION_EXISTS_UNIX (fcntl HAVE_FCNTL) CHECK_FUNCTION_EXISTS_UNIX (fconvert HAVE_FCONVERT) -CHECK_FUNCTION_EXISTS_UNIX (fdatasync HAVE_FDATASYNC) +CHECK_SYMBOL_EXISTS_UNIX(fdatasync "unistd.h" HAVE_FDATASYNC) CHECK_FUNCTION_EXISTS_UNIX (fesetround HAVE_FESETROUND) CHECK_FUNCTION_EXISTS_UNIX (fpsetmask HAVE_FPSETMASK) CHECK_FUNCTION_EXISTS_UNIX (fseeko HAVE_FSEEKO) @@ -657,7 +658,7 @@ CHECK_FUNCTION_EXISTS_UNIX(rdtscll HAVE_RDTSCLL) # # Tests for symbols # -INCLUDE (CheckSymbolExists) + CHECK_SYMBOL_EXISTS_UNIX(sys_errlist "stdio.h" HAVE_SYS_ERRLIST) CHECK_SYMBOL_EXISTS_UNIX(madvise "sys/mman.h" HAVE_DECL_MADVISE) CHECK_SYMBOL_EXISTS_UNIX(tzname "time.h" HAVE_TZNAME) From 555c99c8eb9dc1371df13af029c42e98e59756c0 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 5 Dec 2009 19:59:13 +0100 Subject: [PATCH 099/466] set version and soversionn for client shared library --- libmysql/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 4fb6e10409d..efd6d553be4 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -163,7 +163,11 @@ IF(NOT DISABLE_SHARED) MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) IF(UNIX) # Name of shared library is mysqlclient on Unix - SET_TARGET_PROPERTIES(libmysql PROPERTIES OUTPUT_NAME mysqlclient) + SET_TARGET_PROPERTIES(libmysql PROPERTIES + OUTPUT_NAME mysqlclient + VERSION "${SHARED_LIB_MAJOR_VERSION}.0.0" + SOVERSION "${SHARED_LIB_MAJOR_VERSION}") + # clean direct output needs to be set several targets have the same name #(mysqlclient in this case) SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) From a1ebd4245e9158b8fb242a49e9aeb6a0840fc92e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 02:16:05 +0100 Subject: [PATCH 100/466] On Linux, support -Wl,--no-undefined (only client shared library) and --Wl,--as-needed (all shared modules). The later will remove unused dependencies (also from gcc and C++ runtime) --- cmake/libutils.cmake | 31 +++++++++--- cmake/plugin.cmake | 4 ++ configure.cmake | 102 ++++++++++++++++++++------------------- libmysql/CMakeLists.txt | 9 +++- libmysqld/CMakeLists.txt | 3 +- mysys/CMakeLists.txt | 3 +- sql/CMakeLists.txt | 3 +- vio/CMakeLists.txt | 1 + 8 files changed, 96 insertions(+), 60 deletions(-) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index dc151bc12f2..8f2b66f73c3 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -132,25 +132,31 @@ MACRO(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE) ADD_LIBRARY(${TARGET} STATIC ${SOURCE_FILE}) SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${OUTPUT_NAME}) + SET(OSLIBS) FOREACH(LIB ${LIBS_TO_MERGE}) GET_TARGET_PROPERTY(LIB_LOCATION ${LIB} LOCATION) GET_TARGET_PROPERTY(LIB_TYPE ${LIB} TYPE) IF(NOT LIB_LOCATION) # 3rd party library like libz.so. Make sure that everything # that links to our library links to this one as well. - TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) + LIST(APPEND OSLIBS ${LIB}) ELSE() # This is a target in current project # (can be a static or shared lib) IF(LIB_TYPE STREQUAL "STATIC_LIBRARY") SET(STATIC_LIBS ${STATIC_LIBS} ${LIB_LOCATION}) ADD_DEPENDENCIES(${TARGET} ${LIB}) + # Extract dependend OS libraries + GET_DEPENDEND_OS_LIBS(${LIB} LIB_OSLIBS) + LIST(APPEND OSLIBS ${LIB_OSLIBS}) ELSE() # This is a shared library our static lib depends on. - TARGET_LINK_LIBRARIES(${TARGET} ${LIB}) + LIST(APPEND OSLIBS ${LIB}) ENDIF() ENDIF() ENDFOREACH() + LIST(REMOVE_DUPLICATES OSLIBS) + TARGET_LINK_LIBRARIES(${TARGET} ${OSLIBS}) # Make the generated dummy source file depended on all static input # libs. If input lib changes,the source file is touched @@ -249,16 +255,27 @@ MACRO(MERGE_LIBRARIES) IF(ARG_OUTPUT_NAME) SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME "${ARG_OUTPUT_NAME}") ENDIF() - # Disallow undefined symbols in shared libraries, but allow for modules - # (they export from loading executable) - IF(LIBTYPE MATCHES "SHARED" AND CMAKE_SYSTEM_TYPE MATCHES "Linux") - SET_TARGET_PROPERTIES(${TARGET} PROPERTIES LINK_FLAGS "-Wl,--no-undefined") - ENDIF() ELSE() MESSAGE(FATAL_ERROR "Unknown library type") ENDIF() ENDMACRO() +FUNCTION(GET_DEPENDEND_OS_LIBS target result) + SET(deps ${${target}_LIB_DEPENDS}) + IF(deps) + FOREACH(lib ${deps}) + # Filter out keywords for used for debug vs optimized builds + IF(NOT lib MATCHES "general" AND NOT lib MATCHES "debug" AND NOT lib MATCHES "optimized") + GET_TARGET_PROPERTY(lib_location ${lib} LOCATION) + IF(NOT lib_location) + SET(ret ${ret} ${lib}) + ENDIF() + ENDIF() + ENDFOREACH() + ENDIF() + SET(${result} ${ret} PARENT_SCOPE) +ENDFUNCTION() + MACRO(RESTRICT_SYMBOL_EXPORTS target) IF(CMAKE_COMPILER_IS_GNUCXX AND UNIX) GET_TARGET_PROPERTY(COMPILE_FLAGS ${target} COMPILE_FLAGS) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 3ad52ce4c98..f718563acda 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -125,7 +125,11 @@ MACRO(MYSQL_ADD_PLUGIN) DTRACE_INSTRUMENT(${target}) SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "" COMPILE_DEFINITIONS "MYSQL_DYNAMIC_PLUGIN") + IF(ARG_LINK_LIBRARIES) + TARGET_LINK_LIBRARIES (${target} ${ARG_LINK_LIBRARIES}) + ENDIF() TARGET_LINK_LIBRARIES (${target} mysqlservices) + # Plugin uses symbols defined in mysqld executable. # Some operating systems like Windows and OSX and are pretty strict about # unresolved symbols. Others are less strict and allow unresolved symbols diff --git a/configure.cmake b/configure.cmake index b34d5b070d2..afc1c652115 100644 --- a/configure.cmake +++ b/configure.cmake @@ -94,9 +94,6 @@ IF(CMAKE_COMPILER_IS_GNUCXX) ENDIF() ENDIF(CMAKE_COMPILER_IS_GNUCXX) -IF(WIN32) - SET(CAN_CONVERT_STATIC_TO_SHARED_LIB 1) -ENDIF() # Large files SET(_LARGEFILE_SOURCE 1) @@ -134,13 +131,9 @@ IF(CMAKE_GENERATOR MATCHES "Visual Studio 7") # VS2003 needs the /Op compiler option to disable floating point # optimizations - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Op") - SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Op") - SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Op") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Op") - SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Op") - SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /Op") -ENDIF(CMAKE_GENERATOR MATCHES "Visual Studio 7") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Op") + SET(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /Op") +ENDIF() IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) @@ -148,7 +141,7 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) # HPUX linker crashes building plugins SET(WITHOUT_DYNAMIC_PLUGINS TRUE) ENDIF() - # If not PA-RISC make shared library suffix .so + # If Itanium make shared library suffix .so # OS understands both .sl and .so. CMake would # use .sl, however MySQL prefers .so IF(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "9000") @@ -157,6 +150,24 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) ENDIF() ENDIF() +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Ensure we have clean build for shared libraries + # without extra dependencies and without unresolved symbols + FOREACH(LANG C CXX) + STRING(REPLACE "-rdynamic" "" + CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS + ${CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS} + ) + ENDFOREACH() + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") + SET(LINK_FLAG_NO_UNDEFINED "--Wl,--no-undefined") +ENDIF() + +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET(LINK_FLAG_NO_UNDEFINED "-z defs") +ENDIF() + #Some OS specific hacks IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") ADD_DEFINITIONS(-DNET_RETRY_COUNT=1000000) @@ -244,48 +255,41 @@ IF(WIN32) LINK_LIBRARIES(ws2_32) ENDIF() - - -MACRO(MY_CHECK_LIB func lib found) - SET(${found} 0) +# Searches function in libraries +# if function is found, sets output parameter result to the name of the library +# if function is found in libc, result will be empty +FUNCTION(MY_SEARCH_LIBS func libs result) CHECK_FUNCTION_EXISTS(${func} HAVE_${func}_IN_LIBC) - CHECK_LIBRARY_EXISTS(${lib} ${func} "" HAVE_${func}_IN_${lib}) - IF (HAVE_${func}_IN_${lib}) - SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${lib}) - LINK_LIBRARIES(${lib}) - STRING(TOUPPER ${lib} upper_lib) - SET(HAVE_LIB${upper_lib} 1 CACHE INTERNAL "Library check") - SET(${found} 1) + IF(HAVE_${func}_IN_LIBC) + SET(${result} "" PARENT_SCOPE) + RETURN() ENDIF() -ENDMACRO() - -MACRO(MY_SEARCH_LIBS func lib found) - SET(${found} 0) - CHECK_FUNCTION_EXISTS(${func} HAVE_${func}_IN_LIBC) - IF(NOT HAVE_${func}_IN_LIBC) - MY_CHECK_LIB(${func} ${lib} ${found}) - ELSE() - SET(${found} 1) - ENDIF() -ENDMACRO() + FOREACH(lib ${libs}) + CHECK_LIBRARY_EXISTS(${lib} ${func} "" HAVE_${func}_IN_${lib}) + IF(HAVE_${func}_IN_${lib}) + SET(${result} ${lib} PARENT_SCOPE) + ENDIF() + RETURN() + ENDFOREACH() +ENDFUNCTION() IF(UNIX) - MY_CHECK_LIB(floor m found) - IF(NOT found) - MY_CHECK_LIB( __infinity m found) + MY_SEARCH_LIBS(floor m LIBM) + IF(NOT LIBM) + MY_SEARCH_LIBS(__infinity m LIBM) ENDIF() - MY_CHECK_LIB(gethostbyname_r nsl_r found) - IF (NOT found) - MY_CHECK_LIB(gethostbyname_r nsl found) - ENDIF() - MY_SEARCH_LIBS(bind bind found) - MY_SEARCH_LIBS(crypt crypt found) - MY_SEARCH_LIBS(setsockopt socket found) - MY_SEARCH_LIBS(aio_read rt found) - MY_SEARCH_LIBS(sched_yield posix4 found) - MY_CHECK_LIB(pthread_create pthread found) - MY_SEARCH_LIBS(dlopen dl found) - + MY_SEARCH_LIBS(gethostbyname_r "nsl_r;nsl" LIBNLS) + MY_SEARCH_LIBS(bind bind LIBBIND) + MY_SEARCH_LIBS(crypt crypt LIBCRYPT) + MY_SEARCH_LIBS(setsockopt socket LIBSOCKET) + MY_SEARCH_LIBS(dlopen dl LIBDL) + FIND_PACKAGE(Threads) + + SET(CMAKE_REQUIRED_LIBRARIES + ${LIBNLS} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT}) + + LINK_LIBRARIES(${CMAKE_THREAD_LIBS_INIT}) + OPTION(WITH_LIBWRAP "Compile with tcp wrappers support" OFF) IF(WITH_LIBWRAP) SET(SAVE_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) @@ -303,7 +307,7 @@ IF(UNIX) SET(CMAKE_REQUIRED_LIBRARIES ${SAVE_CMAKE_REQUIRED_LIBRARIES}) IF(HAVE_LIBWRAP) SET(MYSYS_LIBWRAP_SOURCE ${CMAKE_SOURCE_DIR}/mysys/my_libwrap.c) - SET(LIBWRAP_LIBRARY "wrap") + SET(LIBWRAP "wrap") ENDIF() ENDIF() ENDIF() diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index efd6d553be4..db7d2ee2468 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -167,7 +167,14 @@ IF(NOT DISABLE_SHARED) OUTPUT_NAME mysqlclient VERSION "${SHARED_LIB_MAJOR_VERSION}.0.0" SOVERSION "${SHARED_LIB_MAJOR_VERSION}") - + IF(LINK_FLAG_NO_UNDEFINED) + GET_TARGET_PROPERTY(libmysql_link_flags libmysql LINK_FLAGS) + IF(NOT libmysql_link_flag) + SET(libmysql_link_flags) + ENDIF() + SET_TARGET_PROPERTIES(libmysql PROPERTIES LINK_FLAGS + "${libmysql_link_flags} ${LINK_FLAG_NO_UNDEFINED}") + ENDIF() # clean direct output needs to be set several targets have the same name #(mysqlclient in this case) SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 7378d23790d..56e16e6cd1c 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -100,7 +100,8 @@ ENDIF() SET(LIBS dbug strings regex mysys vio - ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBWRAP_LIBRARY} + ${ZLIB_LIBRARY} ${SSL_LIBRARIES} + ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${MYSQLD_STATIC_PLUGIN_LIBS} ${NDB_CLIENT_LIBS} sql_embedded ) diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 2264b99e75b..06d053f1902 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -63,5 +63,6 @@ IF(UNIX) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_port.c) ENDIF() ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES}) -TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY}) +TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY} + ${LIBNLS} ${LIBM}) DTRACE_INSTRUMENT(mysys) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 8e2fbc0cac3..a496b5c6f7c 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -130,7 +130,8 @@ SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} mysys dbug strings vio regex ${SQL_LIB} - ${LIBWRAP_LIBRARY} ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) + ${LIBWRAP} ${LIBCRYPT} ${LIBDL} + ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) INSTALL(TARGETS mysqld DESTINATION bin) INSTALL_DEBUG_SYMBOLS(mysqld) diff --git a/vio/CMakeLists.txt b/vio/CMakeLists.txt index 3470db61c60..dae2ed2bb2f 100755 --- a/vio/CMakeLists.txt +++ b/vio/CMakeLists.txt @@ -20,3 +20,4 @@ ADD_DEFINITIONS(${SSL_DEFINES}) SET(VIO_SOURCES vio.c viosocket.c viossl.c viosslfactories.c) ADD_CONVENIENCE_LIBRARY(vio ${VIO_SOURCES}) +TARGET_LINK_LIBRARIES(vio ${LIBSOCKET}) From db1b3d2d7d1c339827c384da94f04c385735277e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 01:50:54 +0000 Subject: [PATCH 101/466] do not use -z defs on Solaris, it complains about libc.so implicit dependency --- configure.cmake | 3 --- 1 file changed, 3 deletions(-) diff --git a/configure.cmake b/configure.cmake index afc1c652115..4d234ddd8fc 100644 --- a/configure.cmake +++ b/configure.cmake @@ -164,9 +164,6 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") SET(LINK_FLAG_NO_UNDEFINED "--Wl,--no-undefined") ENDIF() -IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") - SET(LINK_FLAG_NO_UNDEFINED "-z defs") -ENDIF() #Some OS specific hacks IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") From 5b4ce3ce2c0de7e28597c5650f5f0c911234a63f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 02:27:49 +0000 Subject: [PATCH 102/466] fix solaris build - inet_aton is in non-default library --- extra/CMakeLists.txt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index bf7232e4b0a..899e5ecf1fd 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -58,17 +58,11 @@ TARGET_LINK_LIBRARIES(perror mysys) ADD_EXECUTABLE(resolveip resolveip.c) TARGET_LINK_LIBRARIES(resolveip mysys) -# On Solaris, inet_aton() function used by resolveip could be in resolv library IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") INCLUDE(CheckFunctionExists) INCLUDE(CheckLibraryExists) - CHECK_FUNCTION_EXISTS(inet_aton HAVE_INET_ATON) - IF(NOT HAVE_INET_ATON) - CHECK_LIBRARY_EXISTS(resolv inet_aton "" HAVE_INET_ATON_IN_RESOLV) - IF(HAVE_INET_ATON_IN_RESOLV) - TARGET_LINK_LIBRARIES(resolveip resolv) - ENDIF() - ENDIF() + MY_SEARCH_LIBS(inet_aton "nsl;socket;resolv" SOLARIS_NSL) + TARGET_LINK_LIBRARIES(resolveip ${SOLARIS_NSL}) ENDIF() From dc798a489fe154f01af9391cfc1db181309c5606 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 12:25:46 +0100 Subject: [PATCH 103/466] Add libm to the lib of required libraries when doing system checks --- configure.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.cmake b/configure.cmake index 4d234ddd8fc..8fd5681972f 100644 --- a/configure.cmake +++ b/configure.cmake @@ -283,7 +283,7 @@ IF(UNIX) FIND_PACKAGE(Threads) SET(CMAKE_REQUIRED_LIBRARIES - ${LIBNLS} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT}) + ${LIBM} ${LIBNLS} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT}) LINK_LIBRARIES(${CMAKE_THREAD_LIBS_INIT}) From 8c09ac87b28b8ba9b28b5e51a7b07f7e2f8834b2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 15:07:26 +0100 Subject: [PATCH 104/466] fix small stuff in mysql_config --- scripts/install_scripts.cmake.in | 39 ++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in index 8d6d17ce1b5..6e43e726be5 100644 --- a/scripts/install_scripts.cmake.in +++ b/scripts/install_scripts.cmake.in @@ -6,12 +6,19 @@ SET(CFLAGS "@CMAKE_C_FLAGS_RELWITHDEBINFO@") SET(CXXFLAGS "@CMAKE_CXX_FLAGS_RELWITHDEBINFO@") SET(LDFLAGS "@CMAKE_SHARED_LIBRARY_LINK_FLAGS@") SET(mysqlclient_LIB_DEPENDS "@mysqlclient_LIB_DEPENDS@") -SET(mysqlclient_LIB_DEPENDS "@mysqlserver_LIB_DEPENDS@") +SET(mysqlserver_LIB_DEPENDS "@mysqlserver_LIB_DEPENDS@") SET(CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@") SET(CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@") SET(CMAKE_COMPILER_IS_GNUCXX "@CMAKE_COMPILER_IS_GNUCXX@") SET(CMAKE_CXX_COMPILER "@CMAKE_CXX_COMPILER@") SET(CMAKE_CXX_COMPILER_ARG1 "@CMAKE_CXX_COMPILER_ARG1@") +SET(VERSION "@VERSION@") +SET(MYSQL_UNIX_ADDR "@MYSQL_UNIX_ADDR@") +SET(MYSQL_TCP_PORT_DEFAULT "@MYSQL_TCP_PORT_DEFAULT@") +SET(MYSQL_TCP_PORT "@MYSQL_TCP_PORT@") +SET(LIBDL "@LIBDL@") +SET(LIBWRAP "@LIBWRAP@") + IF(UNIX) # FIND_PROC and CHECK_PID are used by mysqld_safe @@ -81,17 +88,34 @@ ENDIF() # mysqlclient library (add -l stuff) SET(CLIENT_LIBS "") SET(LIBS "") +LIST(REMOVE_DUPLICATES mysqlclient_LIB_DEPENDS) FOREACH(lib ${mysqlclient_LIB_DEPENDS}) # Filter out "general", it is not a library, just CMake hint IF(NOT lib STREQUAL "general" AND NOT CLIENT_LIBS MATCHES "-l${lib} ") - SET(CLIENT_LIBS "${CLIENT_LIBS}-l${lib} " ) - ENDIF() -ENDFOREACH() -FOREACH(lib ${mysqlserver_LIB_DEPENDS}) - IF(NOT lib STREQUAL "general" AND NOT LIBS MATCHES "-l${lib} ") - SET(LIBS "${LIBS}-l${lib} " ) + IF (lib MATCHES "^\\-l") + SET(CLIENT_LIBS "${CLIENT_LIBS} ${lib} ") + ELSEIF(lib MATCHES "^/") + # Full path, convert to just filename, strip "lib" prefix and extension + GET_FILENAME_COMPONENT(lib "${lib}" NAME_WE) + STRING(REGEX REPLACE "^lib" "" lib "${lib}") + SET(CLIENT_LIBS "${CLIENT_LIBS}-l${lib} " ) + ELSE() + SET(CLIENT_LIBS "${CLIENT_LIBS}-l${lib} " ) + ENDIF() ENDIF() ENDFOREACH() + + +IF(LIBDL AND NOT LIBDL MATCHES "-l") + SET(LIBDL "-l${LIBDL}") +ENDIF() + +IF(LIBWRAP) + SET(WRAPLIBS "-lwrap") +ENDIF() + +SET(LIBS "${CLIENT_LIBS}") + IF(MSVC) STRING(REPLACE "-l" "" CLIENT_LIBS "${CLIENT_LIBS}") STRING(REPLACE "-l" "" LIBS "${LIBS}" ) @@ -106,6 +130,7 @@ IF(WIN32) # Input files with pl.in extension SET(PLIN_FILES mysql_config mysql_secure_installation) # Input files with .sh extension + SET(SH_FILES mysql_convert_table_format mysqld_multi) FOREACH(file ${PLIN_FILES}) From 01b671fbde92449e8d30b4051c61289a5dd56713 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 15:56:15 +0100 Subject: [PATCH 105/466] Deal with empty elements in LIB_DEPENDS list (in some older 2.6 version empty element produces a warning) --- scripts/install_scripts.cmake.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in index 6e43e726be5..25c2f0f237f 100644 --- a/scripts/install_scripts.cmake.in +++ b/scripts/install_scripts.cmake.in @@ -1,3 +1,5 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) + SET(CMAKE_SYSTEM_NAME @CMAKE_SYSTEM_NAME@) SET(UNIX @UNIX@) SET(WIN32 @WIN32@) @@ -88,6 +90,7 @@ ENDIF() # mysqlclient library (add -l stuff) SET(CLIENT_LIBS "") SET(LIBS "") +LIST(REMOVE_ITEM mysqlclient_LIB_DEPENDS "") LIST(REMOVE_DUPLICATES mysqlclient_LIB_DEPENDS) FOREACH(lib ${mysqlclient_LIB_DEPENDS}) # Filter out "general", it is not a library, just CMake hint From 5b1c0f285d512fae7489d2c2704f76f2c628f05c Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 17:42:21 +0100 Subject: [PATCH 106/466] fix cmake warning about included scripts setting their minimum required version --- scripts/install_scripts.cmake.in | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in index 25c2f0f237f..e4f84472fb0 100644 --- a/scripts/install_scripts.cmake.in +++ b/scripts/install_scripts.cmake.in @@ -1,5 +1,3 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) - SET(CMAKE_SYSTEM_NAME @CMAKE_SYSTEM_NAME@) SET(UNIX @UNIX@) SET(WIN32 @WIN32@) From 9b31158e3cc634e9ffc68cb4d98799562cfcb054 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 22:01:03 +0100 Subject: [PATCH 107/466] simplify some tests, silence warning in script install --- CMakeLists.txt | 13 +++-- configure.cmake | 97 +++++--------------------------- scripts/install_scripts.cmake.in | 9 +++ 3 files changed, 33 insertions(+), 86 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bab5595a786..51a50f682ba 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +# Avoid warnings in higher versions +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 2.6) + CMAKE_POLICY(VERSION 2.8) +endif() + # First, decide about build type (debug or release) # If custom compiler flags are set or cmake is invoked with -DCMAKE_BUILD_TYPE, @@ -136,15 +141,15 @@ ENDIF() # Set commonly used variables IF(WIN32) - SET(DEFAULT_MYSQL_HOME "C:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}/" ) + SET(DEFAULT_MYSQL_HOME "C:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}" ) SET(SHAREDIR share) ELSE() SET(DEFAULT_MYSQL_HOME ${CMAKE_INSTALL_PREFIX}) - SET(SHAREDIR ${DEFAULT_MYSQL_HOME}share) + SET(SHAREDIR ${DEFAULT_MYSQL_HOME}/share) ENDIF() SET(DEFAULT_BASEDIR "${DEFAULT_MYSQL_HOME}") -SET(MYSQL_DATADIR "${DEFAULT_MYSQL_HOME}data") +SET(MYSQL_DATADIR "${DEFAULT_MYSQL_HOME}/data") SET(DEFAULT_CHARSET_HOME "${DEFAULT_MYSQL_HOME}") diff --git a/configure.cmake b/configure.cmake index 8fd5681972f..6799490689e 100644 --- a/configure.cmake +++ b/configure.cmake @@ -467,21 +467,8 @@ CHECK_INCLUDE_FILES_UNIX (sys/param.h HAVE_SYS_PARAM_H) CHECK_INCLUDE_FILES_UNIX (sys/vadvise.h HAVE_SYS_VADVISE_H) CHECK_INCLUDE_FILES_UNIX (fnmatch.h HAVE_FNMATCH_H) CHECK_INCLUDE_FILES (stdarg.h HAVE_STDARG_H) +CHECK_INCLUDE_FILES_UNIX("stdlib.h;sys/un.h" HAVE_SYS_UN_H) -# sys/un.h is broken on Linux and Solaris -# It cannot be included alone ( it references types -# defined in stdlib.h, i.e size_t) -CHECK_C_SOURCE_COMPILES_UNIX(" -#include -#include -int main() -{ - return 0; -} -" -HAVE_SYS_UN_H) - -# # Figure out threading library # FIND_PACKAGE (Threads) @@ -676,7 +663,10 @@ IF(HAVE_FINITE_IN_MATH_H) ELSE() CHECK_SYMBOL_EXISTS(finite "ieeefp.h" HAVE_FINITE) ENDIF() -CHECK_SYMBOL_EXISTS(log2 "math.h" HAVE_LOG2) +CHECK_SYMBOL_EXISTS(log2 math.h HAVE_LOG2) +CHECK_SYMBOL_EXISTS(isnan math.h HAVE_ISNAN) +CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) +CHECK_SYMBOL_EXISTS(rint math.h HAVE_RINT) @@ -685,7 +675,8 @@ CHECK_SYMBOL_EXISTS(log2 "math.h" HAVE_LOG2) # INCLUDE(TestBigEndian) IF(APPLE) - # Care for universal binary format + # Can'r run endian test on universal PPC/Intel binaries + # would return inconsistent result. SET(WORDS_BIGENDIAN __BIG_ENDIAN CACHE INTERNAL "big endian test") ELSE() TEST_BIG_ENDIAN(WORDS_BIGENDIAN) @@ -725,8 +716,7 @@ CHECK_TYPE_SIZE(char SIZEOF_CHAR) CHECK_TYPE_SIZE(short SIZEOF_SHORT) CHECK_TYPE_SIZE(int SIZEOF_INT) CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG) -SET(CMAKE_EXTRA_INCLUDE_FILES stdio.h) -SET(CMAKE_EXTRA_INCLUDE_FILES sys/types.h) +SET(CMAKE_EXTRA_INCLUDE_FILES stdio.h sys/types.h) CHECK_TYPE_SIZE(off_t SIZEOF_OFF_T) CHECK_TYPE_SIZE(uchar SIZEOF_UCHAR) CHECK_TYPE_SIZE(uint SIZEOF_UINT) @@ -897,7 +887,7 @@ IF(NOT STACK_DIRECTION) ELSE() SET(STACK_DIRECTION 1 CACHE INTERNAL "Stack grows direction") ENDIF() - MESSAGE(STATUS "Checking stack grows direction : ${STACK_DIRECTION}") + MESSAGE(STATUS "Checking stack direction : ${STACK_DIRECTION}") ENDIF() ENDIF() @@ -922,60 +912,9 @@ ELSE(SIGNAL_RETURN_TYPE_IS_VOID) SET(RETSIGTYPE int) ENDIF(SIGNAL_RETURN_TYPE_IS_VOID) -# -# isnan() is trickier than the usual function test -- it may be a macro -# -CHECK_CXX_SOURCE_COMPILES("#include - int main(int ac, char **av) - { - isnan(0.0); - return 0; - }" - HAVE_ISNAN) - -# -# isinf() is trickier than the usual function test -- it may be a macro -# -CHECK_CXX_SOURCE_COMPILES("#include - int main(int ac, char **av) - { - isinf(0.0); - return 0; - }" - HAVE_ISINF) -# -# rint() is trickier than the usual function test -- it may be a macro -# -CHECK_CXX_SOURCE_COMPILES("#include - int main(int ac, char **av) - { - rint(0.0); - return 0; - }" - HAVE_RINT) - -CHECK_CXX_SOURCE_COMPILES(" - #include - #include - int main() - { - return 0; - }" - TIME_WITH_SYS_TIME) - -IF(UNIX) -CHECK_C_SOURCE_COMPILES(" -#include -#include -int main() -{ - fcntl(0, F_SETFL, O_NONBLOCK); - return 0; -} -" -HAVE_FCNTL_NONBLOCK) -ENDIF() +CHECK_INCLUDE_FILES_UNIX("time.h;sys/time.h" TIME_WITH_SYS_TIME) +CHECK_SYMBOL_EXISTS_UNIX(O_NONBLOCK "unistd.h;fcntl.h" HAVE_FCNTL_NONBLOCK) IF(NOT HAVE_FCNTL_NONBLOCK) SET(NO_FCNTL_NONBLOCK 1) ENDIF() @@ -1020,14 +959,10 @@ IF(NOT CMAKE_CROSSCOMPILING AND NOT MSVC) " HAVE_FAKE_PAUSE_INSTRUCTION) ENDIF() ENDIF() - - - CHECK_SYMBOL_EXISTS_UNIX(tcgetattr "termios.h" HAVE_TCGETATTR 1) CHECK_INCLUDE_FILES_UNIX(sys/ioctl.h HAVE_SYS_IOCTL 1) - # # Check type of signal routines (posix, 4.2bsd, 4.1bsd or v7) # @@ -1130,8 +1065,8 @@ CHECK_CXX_SOURCE_COMPILES_UNIX(" { struct hostent *foo = - gethostbyaddr_r((const char *) 0, - 0, 0, (struct hostent *) 0, (char *) NULL, 0, (int *)0); + gethostbyaddr_r((const char *) 0, + 0, 0, (struct hostent *) 0, (char *) NULL, 0, (int *)0); return 0; } " @@ -1200,8 +1135,8 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qstaticinline") ENDIF() - # The following is required to export all symbols (also with leading underscore - # from libraries and mysqld + # The following is required to export all symbols + # (also with leading underscore) STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}) STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS @@ -1358,5 +1293,3 @@ IF(WIN32) ENDIF() ENDIF(WIN32) - - diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in index e4f84472fb0..f2f64351469 100644 --- a/scripts/install_scripts.cmake.in +++ b/scripts/install_scripts.cmake.in @@ -88,6 +88,15 @@ ENDIF() # mysqlclient library (add -l stuff) SET(CLIENT_LIBS "") SET(LIBS "") + +# Avoid compatibility warning about lists with empty elements +IF(POLICY CMP0011) + CMAKE_POLICY(SET CMP0011 NEW) +ENDIF() +IF(POLICY CMP0007) + CMAKE_POLICY(SET CMP0007 OLD) +ENDIF() + LIST(REMOVE_ITEM mysqlclient_LIB_DEPENDS "") LIST(REMOVE_DUPLICATES mysqlclient_LIB_DEPENDS) FOREACH(lib ${mysqlclient_LIB_DEPENDS}) From a9329d5d3fbfc85510021151f8284a2b3f9a7d17 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Dec 2009 23:31:10 +0100 Subject: [PATCH 108/466] fix some Solaris Sparc errors from recent cleanup --- configure.cmake | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/configure.cmake b/configure.cmake index 6799490689e..8c1e5cf9859 100644 --- a/configure.cmake +++ b/configure.cmake @@ -265,8 +265,8 @@ FUNCTION(MY_SEARCH_LIBS func libs result) CHECK_LIBRARY_EXISTS(${lib} ${func} "" HAVE_${func}_IN_${lib}) IF(HAVE_${func}_IN_${lib}) SET(${result} ${lib} PARENT_SCOPE) + RETURN() ENDIF() - RETURN() ENDFOREACH() ENDFUNCTION() @@ -665,9 +665,16 @@ ELSE() ENDIF() CHECK_SYMBOL_EXISTS(log2 math.h HAVE_LOG2) CHECK_SYMBOL_EXISTS(isnan math.h HAVE_ISNAN) -CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) CHECK_SYMBOL_EXISTS(rint math.h HAVE_RINT) +# isinf() prototype not found on Solaris +CHECK_CXX_SOURCE_COMPILES( +"#include +int main() { + isinf(0.0); + return 0; +}" HAVE_ISINF) + # From b5761e6348e065a796d798b17af5779220287982 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 8 Dec 2009 03:12:23 +0100 Subject: [PATCH 109/466] couple of tweaks for solaris on sparc, link with librt, as it defines sched_yield --- cmake/bison.cmake | 6 ++++++ configure.cmake | 14 ++++++++------ mysys/CMakeLists.txt | 2 +- sql/CMakeLists.txt | 12 ++++++++---- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/cmake/bison.cmake b/cmake/bison.cmake index 9e01ef3d246..1dd5d10cd33 100644 --- a/cmake/bison.cmake +++ b/cmake/bison.cmake @@ -13,6 +13,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + # On Solaris, /opt/csw often contains a newer bison + IF(NOT BISON_EXECUTABLE AND EXISTS /opt/csw/bin/bison) + SET(BISON_EXECUTABLE /opt/csw/bin/bison) + ENDIF() +ENDIF() FIND_PROGRAM(BISON_EXECUTABLE bison DOC "path to the bison executable") MARK_AS_ADVANCED(BISON_EXECUTABLE "") IF(NOT BISON_EXECUTABLE) diff --git a/configure.cmake b/configure.cmake index 8c1e5cf9859..305f4dde454 100644 --- a/configure.cmake +++ b/configure.cmake @@ -92,7 +92,7 @@ IF(CMAKE_COMPILER_IS_GNUCXX) # mininal architecture flags, i486 enables GCC atomics ADD_DEFINITIONS(-march=i486) ENDIF() -ENDIF(CMAKE_COMPILER_IS_GNUCXX) +ENDIF() # Large files @@ -150,9 +150,10 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) ENDIF() ENDIF() +# Ensure we have clean build for shared libraries +# without extra dependencies and without unresolved symbols +# (on system that support it) IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") - # Ensure we have clean build for shared libraries - # without extra dependencies and without unresolved symbols FOREACH(LANG C CXX) STRING(REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS @@ -164,7 +165,6 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") SET(LINK_FLAG_NO_UNDEFINED "--Wl,--no-undefined") ENDIF() - #Some OS specific hacks IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") ADD_DEFINITIONS(-DNET_RETRY_COUNT=1000000) @@ -276,15 +276,17 @@ IF(UNIX) MY_SEARCH_LIBS(__infinity m LIBM) ENDIF() MY_SEARCH_LIBS(gethostbyname_r "nsl_r;nsl" LIBNLS) - MY_SEARCH_LIBS(bind bind LIBBIND) + MY_SEARCH_LIBS(bind "bind;socket" LIBBIND) MY_SEARCH_LIBS(crypt crypt LIBCRYPT) MY_SEARCH_LIBS(setsockopt socket LIBSOCKET) MY_SEARCH_LIBS(dlopen dl LIBDL) + MY_SEARCH_LIBS(sched_yield rt LIBRT) FIND_PACKAGE(Threads) SET(CMAKE_REQUIRED_LIBRARIES - ${LIBM} ${LIBNLS} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT}) + ${LIBM} ${LIBNLS} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT} ${LIBRT}) + LIST(REMOVE_DUPLICATES CMAKE_REQUIRED_LIBRARIES) LINK_LIBRARIES(${CMAKE_THREAD_LIBS_INIT}) OPTION(WITH_LIBWRAP "Compile with tcp wrappers support" OFF) diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 06d053f1902..d055b0af8a9 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -64,5 +64,5 @@ IF(UNIX) ENDIF() ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES}) TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY} - ${LIBNLS} ${LIBM}) + ${LIBNLS} ${LIBM} ${LIBRT}) DTRACE_INSTRUMENT(mysys) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index a496b5c6f7c..d086149831e 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -132,14 +132,18 @@ TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} mysys dbug strings vio regex ${SQL_LIB} ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) + +# Provide plugins with minimal set of libraries +SET(INTERFACE_LIBS ${LIBRT}) +IF(INTERFACE_LIBS) + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_INTERFACE_LIBRARIES + "${INTERFACE_LIBS}") +ENDIF() + INSTALL(TARGETS mysqld DESTINATION bin) INSTALL_DEBUG_SYMBOLS(mysqld) -FIND_PROGRAM(BISON_EXECUTABLE bison DOC "path to the bison executable") -MARK_AS_ADVANCED(BISON_EXECUTABLE "") - - # Handle out-of-source build from source package with possibly broken # bison. Copy bison output to from source to build directory, if not already # there From a7c244e75685496292d2533fc419fc6fb09d5ae8 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 10:39:49 +0300 Subject: [PATCH 110/466] Backport of: ------------------------------------------------------------ revno: 2617.31.7 committer: Davi Arnaut branch nick: mysql-6.0-runtime timestamp: Wed 2009-03-25 19:22:00 -0300 message: WL#4284: Transactional DDL locking Post-merge fixes for test cases. --- mysql-test/include/mix1.inc | 2 ++ mysql-test/r/innodb_mysql.result | 2 ++ mysql-test/suite/parts/r/partition_special_innodb.result | 5 ----- mysql-test/suite/parts/t/partition_special_innodb.test | 3 --- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index 3eaaf37cd83..66648aaf1bf 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -899,6 +899,8 @@ CREATE PROCEDURE p1 () BEGIN DECLARE i INT DEFAULT 50; DECLARE cnt INT; + # Continue even in the presence of ER_LOCK_DEADLOCK. + DECLARE CONTINUE HANDLER FOR 1213 BEGIN END; START TRANSACTION; ALTER TABLE t1 ENGINE=InnoDB; COMMIT; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 0fe704c13f6..54ca46a098b 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1105,6 +1105,8 @@ CREATE PROCEDURE p1 () BEGIN DECLARE i INT DEFAULT 50; DECLARE cnt INT; +# Continue even in the presence of ER_LOCK_DEADLOCK. +DECLARE CONTINUE HANDLER FOR 1213 BEGIN END; START TRANSACTION; ALTER TABLE t1 ENGINE=InnoDB; COMMIT; diff --git a/mysql-test/suite/parts/r/partition_special_innodb.result b/mysql-test/suite/parts/r/partition_special_innodb.result index 26c1ac9356c..3c64c5c3ca4 100644 --- a/mysql-test/suite/parts/r/partition_special_innodb.result +++ b/mysql-test/suite/parts/r/partition_special_innodb.result @@ -214,9 +214,4 @@ INSERT INTO t1 VALUES (NULL, 'first row t2'); SET autocommit=OFF; ALTER TABLE t1 AUTO_INCREMENT = 10; ERROR HY000: Lock wait timeout exceeded; try restarting transaction -INSERT INTO t1 VALUES (NULL, 'second row t2'); -SELECT a,b FROM t1 ORDER BY a; -a b -1 first row t2 -2 second row t2 DROP TABLE t1; diff --git a/mysql-test/suite/parts/t/partition_special_innodb.test b/mysql-test/suite/parts/t/partition_special_innodb.test index eac19f6d588..7583f953b32 100644 --- a/mysql-test/suite/parts/t/partition_special_innodb.test +++ b/mysql-test/suite/parts/t/partition_special_innodb.test @@ -71,9 +71,6 @@ SET autocommit=OFF; --error ER_LOCK_WAIT_TIMEOUT ALTER TABLE t1 AUTO_INCREMENT = 10; ---connection con1 -INSERT INTO t1 VALUES (NULL, 'second row t2'); -SELECT a,b FROM t1 ORDER BY a; --disconnect con2 --disconnect con1 --connection default From 90e88642afeb9abc4aa5bc417306fae00d6a85bd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 10:53:40 +0300 Subject: [PATCH 111/466] Backport of: ------------------------------------------------------------ revno: 2617.43.3 committer: Davi Arnaut branch nick: 40188-6.0 timestamp: Thu 2009-05-07 13:15:54 +0200 message: Sort results as the file list of the database directory is not sorted (MY_DONT_SORT). (This is a follow-up fix for WL#4284). --- mysql-test/r/drop_debug.result | 4 ++++ mysql-test/t/drop_debug.test | 3 +++ 2 files changed, 7 insertions(+) diff --git a/mysql-test/r/drop_debug.result b/mysql-test/r/drop_debug.result index 75346b88bc6..f2c89034451 100644 --- a/mysql-test/r/drop_debug.result +++ b/mysql-test/r/drop_debug.result @@ -7,12 +7,16 @@ DROP DATABASE IF EXISTS mysql_test; CREATE DATABASE mysql_test; CREATE TABLE mysql_test.t1(a INT); +CREATE TABLE mysql_test.t2(b INT); +CREATE TABLE mysql_test.t3(c INT); SET SESSION DEBUG = "+d,bug43138"; DROP DATABASE mysql_test; Warnings: Error 1051 Unknown table 't1' +Error 1051 Unknown table 't2' +Error 1051 Unknown table 't3' SET SESSION DEBUG = "-d,bug43138"; diff --git a/mysql-test/t/drop_debug.test b/mysql-test/t/drop_debug.test index 97ee5847d0a..63c85d9246b 100644 --- a/mysql-test/t/drop_debug.test +++ b/mysql-test/t/drop_debug.test @@ -17,11 +17,14 @@ DROP DATABASE IF EXISTS mysql_test; --echo CREATE DATABASE mysql_test; CREATE TABLE mysql_test.t1(a INT); +CREATE TABLE mysql_test.t2(b INT); +CREATE TABLE mysql_test.t3(c INT); --echo SET SESSION DEBUG = "+d,bug43138"; --echo +--sorted_result DROP DATABASE mysql_test; --echo From b4677ef084b8b4a188026c284400eed0f37c3344 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 11:26:49 +0300 Subject: [PATCH 112/466] Backport of: ------------------------------------------------------------ revno: 2617.65.6 committer: Dmitry Lenev branch nick: mysql-azalea-bg39674 timestamp: Sat 2009-07-25 00:28:43 +0400 message: Fix for bug #39674 "On shutdown mdl_destroy() called before plugin_shutdown()". Attempt to shutdown PBXT engine plugin led to assertion failure caused by using already destroyed mutex in metadata locking subsystem. This problem stemmed from the fact that we MDL subsystem and table definition cache were deinitialized before plugin shutdown while PBXT plugin during its shutdown process accessed tables and therefore expected them to be in working shape. This patch solves this problem by moving deinitialization of these two subsystems after plugins are shut down. No test case is provided since such test case would require using PBXT or other plugin which accesses tables during its shutdown process. --- sql/mysql_priv.h | 1 + sql/mysqld.cc | 5 +++-- sql/sql_base.cc | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 159613c58a0..74c5af92229 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1047,6 +1047,7 @@ bool compare_record(TABLE *table); bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); bool table_def_init(void); +void table_def_start_shutdown(void); void table_def_free(void); void assign_new_table_id(TABLE_SHARE *share); uint cached_open_tables(void); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1055d5fed8b..ed1572853bc 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1294,9 +1294,7 @@ void clean_up(bool print_message) grant_free(); #endif query_cache_destroy(); - table_def_free(); hostname_cache_free(); - mdl_destroy(); item_user_lock_free(); lex_free(); /* Free some memory */ item_create_cleanup(); @@ -1308,12 +1306,15 @@ void clean_up(bool print_message) udf_free(); #endif } + table_def_start_shutdown(); plugin_shutdown(); ha_end(); if (tc_log) tc_log->close(); delegates_destroy(); xid_cache_free(); + table_def_free(); + mdl_destroy(); delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache); multi_keycache_free(); free_status_vars(); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 42fc3ba0566..4f8c6be8e7b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -114,6 +114,7 @@ TABLE *unused_tables; HASH table_def_cache; static TABLE_SHARE *oldest_unused_share, end_of_unused_share; static bool table_def_inited= 0; +static bool table_def_shutdown_in_progress= 0; static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables, TABLE_SHARE *table_share); @@ -279,13 +280,36 @@ bool table_def_init(void) } +/** + Notify table definition cache that process of shutting down server + has started so it has to keep number of TABLE and TABLE_SHARE objects + minimal in order to reduce number of references to pluggable engines. +*/ + +void table_def_start_shutdown(void) +{ + if (table_def_inited) + { + pthread_mutex_lock(&LOCK_open); + /* Free all cached but unused TABLEs and TABLE_SHAREs first. */ + close_cached_tables(NULL, NULL, TRUE, FALSE); + /* + Ensure that TABLE and TABLE_SHARE objects which are created for + tables that are open during process of plugins' shutdown are + immediately released. This keeps number of references to engine + plugins minimal and allows shutdown to proceed smoothly. + */ + table_def_shutdown_in_progress= TRUE; + pthread_mutex_unlock(&LOCK_open); + } +} + + void table_def_free(void) { DBUG_ENTER("table_def_free"); if (table_def_inited) { - /* Free all open TABLEs first. */ - close_cached_tables(NULL, NULL, FALSE, FALSE); table_def_inited= 0; /* Free table definitions. */ my_hash_free(&table_def_cache); @@ -646,7 +670,8 @@ void release_table_share(TABLE_SHARE *share) DBUG_ASSERT(share->ref_count); if (!--share->ref_count) { - if (share->version != refresh_version) + if (share->version != refresh_version || + table_def_shutdown_in_progress) to_be_deleted=1; else { @@ -1513,7 +1538,8 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) table->mdl_ticket= NULL; if (table->needs_reopen() || - thd->version != refresh_version || !table->db_stat) + thd->version != refresh_version || !table->db_stat || + table_def_shutdown_in_progress) { free_cache_entry(table); found_old_table=1; From 4a8a1c568d8785dc608cc111e74e1ed389f1353e Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 11:38:45 +0300 Subject: [PATCH 113/466] Backport of: ---------------------------------------------------------- revno: 2617.69.2 committer: Konstantin Osipov branch nick: 5.4-azalea-bugfixing timestamp: Mon 2009-08-03 19:26:04 +0400 message: A fix and a test case for Bug#45035 "Altering table under LOCK TABLES results in "Error 1213 Deadlock found...". If a user had a table locked with LOCK TABLES for READ and for WRITE in the same connection, ALTER TABLE could fail. Root cause analysis: If a connection issues LOCK TABLE t1 write, t1 a read, t1 b read; the new LOCK TABLES code in 6.0 (part of WL 3726) will create the following list of TABLE_LIST objects (thd->locked_tables_list->m_locked_tables): {"t1" "b" tl_read_no_insert}, {"t1" "a" tl_read_no_insert}, {"t1" "t1" tl_write } Later on, when we try to ALTER table t1, mysql_alter_table() closes all TABLE instances and releases its thr_lock locks, keeping only an exclusive metadata lock on t1. But when ALTER is finished, Locked_table_list::reopen_tables() tries to restore the original list of open and locked tables. Before this patch, it used to do so one by one: Open t1 b, get TL_READ_NO_INSERT lock, Open t1 a, get TL_READ_NO_INSERT lock Open t1, try to get TL_WRITE lock, deadlock. The cause of the deadlock is that thr_lock.c doesn't resolve the situation when the read list only consists of locks taken by the same thread, followed by this very thread trying to take a WRITE lock. Indeed, since thr_lock_multi always gets a sorted list of locks, WRITE locks always precede READ locks in the list to lock. Don't try to fix thr_lock.c deficiency, keep this code simple. Instead, try to take all thr_lock locks at once in ::reopen_tables(). --- mysql-test/r/lock.result | 37 ++++++++++++++ mysql-test/t/lock.test | 40 +++++++++++++++ sql/sql_base.cc | 103 +++++++++++++++++++++++++++++++-------- sql/sql_class.h | 19 ++++++-- sql/sql_table.cc | 4 +- 5 files changed, 178 insertions(+), 25 deletions(-) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index c60ec4bc1bd..46ce618b99b 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -282,5 +282,42 @@ insert into t1 values (1); # Ensure that metadata locks held by the transaction are released. drop table t1; # +# Bug#45035 " Altering table under LOCK TABLES results in +# "Error 1213 Deadlock found..." +# +# When reopening tables under LOCK TABLES after ALTER TABLE, +# 6.0 used to be taking thr_lock locks one by one, and +# that would lead to a lock conflict. +# Check that taking all locks at once works. +# +drop table if exists t1; +create table t1 (i int); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +create temporary table t1 (i int); +# +# This is just for test coverage purposes, +# when this is allowed, remove the --error. +# +lock tables t1 write, t1 as a read, t1 as b read; +ERROR HY000: Can't reopen table: 't1' +alter table t1 add column j int; +unlock tables; +drop table t1; +# +# Separate case for partitioned tables is important +# because each partition has an own thr_lock object. +# +create table t1 (i int) partition by list (i) +(partition p0 values in (1), +partition p1 values in (2,3), +partition p2 values in (4,5)); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index c2aeac05cd1..eaba2693904 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -345,6 +345,46 @@ connection default; drop table t1; +--echo # +--echo # Bug#45035 " Altering table under LOCK TABLES results in +--echo # "Error 1213 Deadlock found..." +--echo # +--echo # When reopening tables under LOCK TABLES after ALTER TABLE, +--echo # 6.0 used to be taking thr_lock locks one by one, and +--echo # that would lead to a lock conflict. +--echo # Check that taking all locks at once works. +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (i int); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +create temporary table t1 (i int); +--echo # +--echo # This is just for test coverage purposes, +--echo # when this is allowed, remove the --error. +--echo # +--error ER_CANT_REOPEN_TABLE +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +--echo # +--echo # Separate case for partitioned tables is important +--echo # because each partition has an own thr_lock object. +--echo # +create table t1 (i int) partition by list (i) + (partition p0 values in (1), + partition p1 values in (2,3), + partition p2 values in (4,5)); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; + --echo # --echo # End of 6.0 tests. --echo # diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4f8c6be8e7b..02871c118ca 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2980,8 +2980,11 @@ Locked_tables_list::init_locked_tables(THD *thd) { DBUG_ASSERT(thd->locked_tables_mode == LTM_NONE); DBUG_ASSERT(m_locked_tables == NULL); + DBUG_ASSERT(m_reopen_array == NULL); + DBUG_ASSERT(m_locked_tables_count == 0); - for (TABLE *table= thd->open_tables; table; table= table->next) + for (TABLE *table= thd->open_tables; table; + table= table->next, m_locked_tables_count++) { TABLE_LIST *src_table_list= table->pos_in_table_list; char *db, *table_name, *alias; @@ -3021,7 +3024,24 @@ Locked_tables_list::init_locked_tables(THD *thd) m_locked_tables_last= &dst_table_list->next_global; table->pos_in_locked_tables= dst_table_list; } + if (m_locked_tables_count) + { + /** + Allocate an auxiliary array to pass to mysql_lock_tables() + in reopen_tables(). reopen_tables() is a critical + path and we don't want to complicate it with extra allocations. + */ + m_reopen_array= (TABLE**)alloc_root(&m_locked_tables_root, + sizeof(TABLE*) * + (m_locked_tables_count+1)); + if (m_reopen_array == NULL) + { + unlock_locked_tables(0); + return TRUE; + } + } thd->locked_tables_mode= LTM_LOCK_TABLES; + return FALSE; } @@ -3070,6 +3090,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd) free_root(&m_locked_tables_root, MYF(0)); m_locked_tables= NULL; m_locked_tables_last= &m_locked_tables; + m_reopen_array= NULL; + m_locked_tables_count= 0; } @@ -3141,8 +3163,39 @@ void Locked_tables_list::unlink_from_list(THD *thd, @note This function is a no-op if we're not under LOCK TABLES. */ -void Locked_tables_list::unlink_all_closed_tables() +void Locked_tables_list:: +unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count) { + /* If we managed to take a lock, unlock tables and free the lock. */ + if (lock) + mysql_unlock_tables(thd, lock); + /* + If a failure happened in reopen_tables(), we may have succeeded + reopening some tables, but not all. + This works when the connection was killed in mysql_lock_tables(). + */ + if (reopen_count) + { + pthread_mutex_lock(&LOCK_open); + while (reopen_count--) + { + /* + When closing the table, we must remove it + from thd->open_tables list. + We rely on the fact that open_table() that was used + in reopen_tables() always links the opened table + to the beginning of the open_tables list. + */ + DBUG_ASSERT(thd->open_tables == m_reopen_array[reopen_count]); + + thd->open_tables->pos_in_locked_tables->table= NULL; + + close_thread_table(thd, &thd->open_tables); + } + broadcast_refresh(); + pthread_mutex_unlock(&LOCK_open); + } + /* Exclude all closed tables from the LOCK TABLES list. */ for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list= table_list->next_global) { @@ -3176,12 +3229,13 @@ Locked_tables_list::reopen_tables(THD *thd) { enum enum_open_table_action ot_action_unused; bool lt_refresh_unused; + size_t reopen_count= 0; + MYSQL_LOCK *lock; + MYSQL_LOCK *merged_lock; for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list= table_list->next_global) { - MYSQL_LOCK *lock; - if (table_list->table) /* The table was not closed */ continue; @@ -3189,33 +3243,42 @@ Locked_tables_list::reopen_tables(THD *thd) if (open_table(thd, table_list, thd->mem_root, &ot_action_unused, MYSQL_OPEN_REOPEN)) { - unlink_all_closed_tables(); + unlink_all_closed_tables(thd, 0, reopen_count); return TRUE; } table_list->table->pos_in_locked_tables= table_list; /* See also the comment on lock type in init_locked_tables(). */ table_list->table->reginfo.lock_type= table_list->lock_type; + + DBUG_ASSERT(reopen_count < m_locked_tables_count); + m_reopen_array[reopen_count++]= table_list->table; + } + if (reopen_count) + { thd->in_lock_tables= 1; - lock= mysql_lock_tables(thd, &table_list->table, 1, + /* + We re-lock all tables with mysql_lock_tables() at once rather + than locking one table at a time because of the case + reported in Bug#45035: when the same table is present + in the list many times, thr_lock.c fails to grant READ lock + on a table that is already locked by WRITE lock, even if + WRITE lock is taken by the same thread. If READ and WRITE + lock are passed to thr_lock.c in the same list, everything + works fine. Patching legacy code of thr_lock.c is risking to + break something else. + */ + lock= mysql_lock_tables(thd, m_reopen_array, reopen_count, MYSQL_OPEN_REOPEN, <_refresh_unused); thd->in_lock_tables= 0; - if (lock) - lock= mysql_lock_merge(thd->lock, lock); - if (lock == NULL) + if (lock == NULL || (merged_lock= + mysql_lock_merge(thd->lock, lock)) == NULL) { - /* - No one's seen this branch work. Recover and report an - error just in case. - */ - pthread_mutex_lock(&LOCK_open); - close_thread_table(thd, &thd->open_tables); - pthread_mutex_unlock(&LOCK_open); - table_list->table= 0; - unlink_all_closed_tables(); - my_error(ER_LOCK_DEADLOCK, MYF(0)); + unlink_all_closed_tables(thd, lock, reopen_count); + if (! thd->killed) + my_error(ER_LOCK_DEADLOCK, MYF(0)); return TRUE; } - thd->lock= lock; + thd->lock= merged_lock; } return FALSE; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 92b9f9f4611..4b6564fb9da 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1192,7 +1192,7 @@ private: Therefore, we can't allocate metadata locks on execution memory root -- as well as tables, the locks need to stay around till UNLOCK TABLES is called. - The locks are allocated in the memory root encapsulate in this + The locks are allocated in the memory root encapsulated in this class. Some SQL commands, like FLUSH TABLE or ALTER TABLE, demand that @@ -1211,10 +1211,21 @@ private: MEM_ROOT m_locked_tables_root; TABLE_LIST *m_locked_tables; TABLE_LIST **m_locked_tables_last; + /** An auxiliary array used only in reopen_tables(). */ + TABLE **m_reopen_array; + /** + Count the number of tables in m_locked_tables list. We can't + rely on thd->lock->table_count because it excludes + non-transactional temporary tables. We need to know + an exact number of TABLE objects. + */ + size_t m_locked_tables_count; public: Locked_tables_list() :m_locked_tables(NULL), - m_locked_tables_last(&m_locked_tables) + m_locked_tables_last(&m_locked_tables), + m_reopen_array(NULL), + m_locked_tables_count(0) { init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0); } @@ -1228,7 +1239,9 @@ public: MEM_ROOT *locked_tables_root() { return &m_locked_tables_root; } void unlink_from_list(THD *thd, TABLE_LIST *table_list, bool remove_from_locked_tables); - void unlink_all_closed_tables(); + void unlink_all_closed_tables(THD *thd, + MYSQL_LOCK *lock, + size_t reopen_count); bool reopen_tables(THD *thd); }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8fdaa2cd93a..527095f2c88 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4515,7 +4515,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, } end: - thd->locked_tables_list.unlink_all_closed_tables(); + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); if (table == &tmp_table) { pthread_mutex_lock(&LOCK_open); @@ -7597,7 +7597,7 @@ err_with_mdl: remove all references to the altered table from the list of locked tables and release the exclusive metadata lock. */ - thd->locked_tables_list.unlink_all_closed_tables(); + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); if (target_mdl_request) { thd->mdl_context.release_lock(target_mdl_request->ticket); From a66a2608ae4d70a3f9b3d41e38cfb97eb616bed3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 12:57:07 +0300 Subject: [PATCH 114/466] Backport of: ---------------------------------------------------------- revno: 2617.69.20 committer: Konstantin Osipov branch nick: 5.4-4284-1-assert timestamp: Thu 2009-08-13 18:29:55 +0400 message: WL#4284 "Transactional DDL locking" A review fix. Since WL#4284 implementation separated MDL_request and MDL_ticket, MDL_request becamse a utility object necessary only to get a ticket. Store it by-value in TABLE_LIST with the intent to merge MDL_request::key with table_list->table_name and table_list->db in future. Change the MDL subsystem to not require MDL_requests to stay around till close_thread_tables(). Remove the list of requests from the MDL context. Requests for shared metadata locks acquired in open_tables() are only used as a list in recover_from_failed_open_table_attempt(), which calls mdl_context.wait_for_locks() for this list. To keep such list for recover_from_failed_open_table_attempt(), introduce a context class (Open_table_context), that collects all requests. A lot of minor cleanups and simplications that became possible with this change. --- sql/event_db_repository.cc | 4 - sql/ha_ndbcluster_binlog.cc | 10 +- sql/lock.cc | 21 +-- sql/log.cc | 42 ++--- sql/log_event.cc | 18 +- sql/log_event_old.cc | 2 +- sql/mdl.cc | 242 +++++++------------------ sql/mdl.h | 85 +++++---- sql/mysql_priv.h | 8 +- sql/sp.cc | 15 +- sql/sp_head.cc | 10 +- sql/sql_acl.cc | 25 ++- sql/sql_base.cc | 283 ++++++++++++++++-------------- sql/sql_class.cc | 3 +- sql/sql_class.h | 53 ++++-- sql/sql_delete.cc | 41 ++--- sql/sql_handler.cc | 8 +- sql/sql_help.cc | 1 + sql/sql_insert.cc | 33 ++-- sql/sql_parse.cc | 10 +- sql/sql_plugin.cc | 19 +- sql/sql_servers.cc | 22 +-- sql/sql_show.cc | 78 ++++---- sql/sql_table.cc | 204 +++++++++------------ sql/sql_udf.cc | 26 +-- sql/sql_update.cc | 4 +- sql/table.cc | 14 +- sql/table.h | 8 +- sql/tztime.cc | 2 + storage/myisammrg/ha_myisammrg.cc | 8 +- 30 files changed, 566 insertions(+), 733 deletions(-) diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 134c8059e13..9f53a73a594 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -555,7 +555,6 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, DBUG_ENTER("Event_db_repository::open_event_table"); tables.init_one_table("mysql", 5, "event", 5, "event", lock_type); - alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1110,7 +1109,6 @@ Event_db_repository::check_system_tables(THD *thd) /* Check mysql.db */ tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ); - alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1128,7 +1126,6 @@ Event_db_repository::check_system_tables(THD *thd) } /* Check mysql.user */ tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ); - alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1149,7 +1146,6 @@ Event_db_repository::check_system_tables(THD *thd) } /* Check mysql.event */ tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ); - alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 7ce318394d4..8b3764367c2 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -140,7 +140,6 @@ static Uint64 *p_latest_trans_gci= 0; */ static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; -static MDL_request binlog_mdl_request; /* Helper functions @@ -2337,13 +2336,10 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) const char *save_proc_info= thd->proc_info; TABLE_LIST *tables= &binlog_tables; - bzero((char*) tables, sizeof(*tables)); - tables->db= repdb; - tables->alias= tables->table_name= reptable; - tables->lock_type= TL_WRITE; + tables->init_one_table(repdb, strlen(repdb), reptable, strlen(reptable), + reptable, TL_WRITE); thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE; - binlog_mdl_request.init(0, tables->db, tables->table_name); - tables->mdl_request= &binlog_mdl_request; + tables->required_type= FRMTYPE_TABLE; uint counter; thd->clear_error(); diff --git a/sql/lock.cc b/sql/lock.cc index 170007d8f66..aea1bfbd0e6 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -952,26 +952,18 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, bool lock_table_names(THD *thd, TABLE_LIST *table_list) { + MDL_request_list mdl_requests; TABLE_LIST *lock_table; - MDL_request *mdl_request; for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - mdl_request= MDL_request::create(0, lock_table->db, lock_table->table_name, - thd->mem_root); - if (!mdl_request) - goto end; - mdl_request->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(mdl_request); - lock_table->mdl_request= mdl_request; + lock_table->mdl_request.init(0, lock_table->db, lock_table->table_name, + MDL_EXCLUSIVE); + mdl_requests.push_front(&lock_table->mdl_request); } - if (thd->mdl_context.acquire_exclusive_locks()) - goto end; + if (thd->mdl_context.acquire_exclusive_locks(&mdl_requests)) + return 1; return 0; - -end: - thd->mdl_context.remove_all_requests(); - return 1; } @@ -987,7 +979,6 @@ void unlock_table_names(THD *thd) { DBUG_ENTER("unlock_table_names"); thd->mdl_context.release_all_locks(); - thd->mdl_context.remove_all_requests(); DBUG_VOID_RETURN; } diff --git a/sql/log.cc b/sql/log.cc index 48e509e9275..a74fc94d09d 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -423,14 +423,10 @@ bool Log_to_csv_event_handler:: save_thd_options= thd->options; thd->options&= ~OPTION_BIN_LOG; - bzero(& table_list, sizeof(TABLE_LIST)); - table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str; - table_list.table_name_length= GENERAL_LOG_NAME.length; - - table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; - - table_list.db= MYSQL_SCHEMA_NAME.str; - table_list.db_length= MYSQL_SCHEMA_NAME.length; + table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length, + GENERAL_LOG_NAME.str, GENERAL_LOG_NAME.length, + GENERAL_LOG_NAME.str, + TL_WRITE_CONCURRENT_INSERT); /* 1) open_performance_schema_table generates an error of the @@ -588,14 +584,10 @@ bool Log_to_csv_event_handler:: */ save_time_zone_used= thd->time_zone_used; - bzero(& table_list, sizeof(TABLE_LIST)); - table_list.alias= table_list.table_name= SLOW_LOG_NAME.str; - table_list.table_name_length= SLOW_LOG_NAME.length; - - table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; - - table_list.db= MYSQL_SCHEMA_NAME.str; - table_list.db_length= MYSQL_SCHEMA_NAME.length; + table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length, + SLOW_LOG_NAME.str, SLOW_LOG_NAME.length, + SLOW_LOG_NAME.str, + TL_WRITE_CONCURRENT_INSERT); if (!(table= open_performance_schema_table(thd, & table_list, & open_tables_backup))) @@ -733,29 +725,25 @@ int Log_to_csv_event_handler:: { TABLE_LIST table_list; TABLE *table; + LEX_STRING *UNINIT_VAR(log_name); int result; Open_tables_state open_tables_backup; DBUG_ENTER("Log_to_csv_event_handler::activate_log"); - bzero(& table_list, sizeof(TABLE_LIST)); - if (log_table_type == QUERY_LOG_GENERAL) { - table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str; - table_list.table_name_length= GENERAL_LOG_NAME.length; + log_name= &GENERAL_LOG_NAME; } else { DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW); - table_list.alias= table_list.table_name= SLOW_LOG_NAME.str; - table_list.table_name_length= SLOW_LOG_NAME.length; + + log_name= &SLOW_LOG_NAME; } - - table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; - - table_list.db= MYSQL_SCHEMA_NAME.str; - table_list.db_length= MYSQL_SCHEMA_NAME.length; + table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length, + log_name->str, log_name->length, log_name->str, + TL_WRITE_CONCURRENT_INSERT); table= open_performance_schema_table(thd, & table_list, & open_tables_backup); diff --git a/sql/log_event.cc b/sql/log_event.cc index aa8dd6e9bff..46d016b2c15 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8067,7 +8067,6 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; - MDL_request *mdl_request; size_t dummy_len; void *memory; DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); @@ -8082,21 +8081,18 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) &table_list, (uint) sizeof(RPL_TABLE_LIST), &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, - &mdl_request, sizeof(MDL_request), NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); - bzero(table_list, sizeof(*table_list)); - table_list->db = db_mem; - table_list->alias= table_list->table_name = tname_mem; - table_list->lock_type= TL_WRITE; - table_list->next_global= table_list->next_local= 0; + strmov(db_mem, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); + strmov(tname_mem, m_tblnam); + + table_list->init_one_table(db_mem, strlen(db_mem), + tname_mem, strlen(tname_mem), + tname_mem, TL_WRITE); + table_list->table_id= m_table_id; table_list->updating= 1; - strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); - strmov(table_list->table_name, m_tblnam); - mdl_request->init(0, table_list->db, table_list->table_name); - table_list->mdl_request= mdl_request; int error= 0; diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 90ea8b6cefe..942396fc3da 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1506,7 +1506,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) */ thd->binlog_flush_pending_rows_event(false); TABLE_LIST *tables= rli->tables_to_lock; - close_tables_for_reopen(thd, &tables, FALSE); + close_tables_for_reopen(thd, &tables); uint tables_count= rli->tables_to_lock_count; if ((error= open_tables(thd, &tables, &tables_count, 0))) diff --git a/sql/mdl.cc b/sql/mdl.cc index eb8fcdb323e..566a7c96b3b 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -194,7 +194,6 @@ void MDL_context::init(THD *thd_arg) rely here on the default constructors of I_P_List to empty the list. */ - m_requests.empty(); m_tickets.empty(); } @@ -213,7 +212,6 @@ void MDL_context::init(THD *thd_arg) void MDL_context::destroy() { - DBUG_ASSERT(m_requests.is_empty()); DBUG_ASSERT(m_tickets.is_empty()); DBUG_ASSERT(! m_has_global_shared_lock); } @@ -231,10 +229,8 @@ void MDL_context::destroy() void MDL_context::backup_and_reset(MDL_context *backup) { - DBUG_ASSERT(backup->m_requests.is_empty()); DBUG_ASSERT(backup->m_tickets.is_empty()); - m_requests.swap(backup->m_requests); m_tickets.swap(backup->m_tickets); backup->m_has_global_shared_lock= m_has_global_shared_lock; @@ -254,11 +250,9 @@ void MDL_context::backup_and_reset(MDL_context *backup) void MDL_context::restore_from_backup(MDL_context *backup) { - DBUG_ASSERT(m_requests.is_empty()); DBUG_ASSERT(m_tickets.is_empty()); DBUG_ASSERT(m_has_global_shared_lock == FALSE); - m_requests.swap(backup->m_requests); m_tickets.swap(backup->m_tickets); m_has_global_shared_lock= backup->m_has_global_shared_lock; } @@ -271,18 +265,9 @@ void MDL_context::restore_from_backup(MDL_context *backup) void MDL_context::merge(MDL_context *src) { MDL_ticket *ticket; - MDL_request *mdl_request; DBUG_ASSERT(m_thd == src->m_thd); - if (!src->m_requests.is_empty()) - { - Request_iterator it(src->m_requests); - while ((mdl_request= it++)) - m_requests.push_front(mdl_request); - src->m_requests.empty(); - } - if (!src->m_tickets.is_empty()) { Ticket_iterator it(src->m_tickets); @@ -315,15 +300,11 @@ void MDL_context::merge(MDL_context *src) for example in the grant subsystem, to lock privilege tables. The MDL subsystem does not own or manage memory of lock requests. - Instead it assumes that the life time of every lock request (including - encompassed members db/name) encloses calls to MDL_context::add_request() - and MDL_context::remove_request() or MDL_context::remove_all_requests(). @param type Id of type of object to be locked @param db Name of database to which the object belongs @param name Name of of the object - - The initialized lock request will have MDL_SHARED type. + @param mdl_type The MDL lock type for the request. Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 Note that tables and views must have the same lock type, since @@ -332,10 +313,11 @@ void MDL_context::merge(MDL_context *src) void MDL_request::init(unsigned char type_arg, const char *db_arg, - const char *name_arg) + const char *name_arg, + enum enum_mdl_type mdl_type_arg) { key.mdl_key_init(type_arg, db_arg, name_arg); - type= MDL_SHARED; + type= mdl_type_arg; ticket= NULL; } @@ -360,91 +342,20 @@ void MDL_request::init(unsigned char type_arg, MDL_request * MDL_request::create(unsigned char type, const char *db, - const char *name, MEM_ROOT *root) + const char *name, enum_mdl_type mdl_type, + MEM_ROOT *root) { MDL_request *mdl_request; if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request)))) return NULL; - mdl_request->init(type, db, name); + mdl_request->init(type, db, name, mdl_type); return mdl_request; } -/** - Add a lock request to the list of lock requests of the context. - - The procedure to acquire metadata locks is: - - allocate and initialize lock requests - (MDL_request::create()) - - associate them with a context (MDL_context::add_request()) - - call MDL_context::acquire_shared_lock() and - MDL_context::release_lock() (maybe repeatedly). - - Associates a lock request with the given context. - There should be no more than one context per connection, to - avoid deadlocks. - - @param mdl_request The lock request to be added. -*/ - -void MDL_context::add_request(MDL_request *mdl_request) -{ - DBUG_ENTER("MDL_context::add_request"); - DBUG_ASSERT(mdl_request->ticket == NULL); - m_requests.push_front(mdl_request); - DBUG_VOID_RETURN; -} - - -/** - Remove a lock request from the list of lock requests. - - Disassociates a lock request from the given context. - - @param mdl_request The lock request to be removed. - - @pre The lock request being removed should correspond to a ticket that - was released or was not acquired. - - @note Resets lock request back to its initial state - (i.e. sets type to MDL_SHARED). -*/ - -void MDL_context::remove_request(MDL_request *mdl_request) -{ - DBUG_ENTER("MDL_context::remove_request"); - /* Reset lock request back to its initial state. */ - mdl_request->type= MDL_SHARED; - mdl_request->ticket= NULL; - m_requests.remove(mdl_request); - DBUG_VOID_RETURN; -} - - -/** - Clear all lock requests in the context. - Disassociates lock requests from the context. - - Also resets lock requests back to their initial state (i.e. MDL_SHARED). -*/ - -void MDL_context::remove_all_requests() -{ - MDL_request *mdl_request; - Request_iterator it(m_requests); - while ((mdl_request= it++)) - { - /* Reset lock request back to its initial state. */ - mdl_request->type= MDL_SHARED; - mdl_request->ticket= NULL; - } - m_requests.empty(); -} - - /** Auxiliary functions needed for creation/destruction of MDL_lock objects. @@ -636,8 +547,10 @@ MDL_global_lock::is_lock_type_compatible(enum_mdl_type type, /** Check if request for the lock can be satisfied given current state of lock. - @param lock Lock. - @param mdl_request Request for lock. + @param requestor_ctx The context that identifies the owner of the request. + @param type_arg The requested lock type. + @param is_upgrade Must be set to TRUE when we are upgrading + a shared upgradable lock to exclusive. @retval TRUE Lock request can be satisfied @retval FALSE There is some conflicting lock. @@ -731,7 +644,7 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar /** Check whether the context already holds a compatible lock ticket - on a object. Only shared locks can be recursive. + on an object. @param mdl_request Lock request object for lock to be acquired @@ -744,8 +657,6 @@ MDL_context::find_ticket(MDL_request *mdl_request) MDL_ticket *ticket; Ticket_iterator it(m_tickets); - DBUG_ASSERT(mdl_request->is_shared()); - while ((ticket= it++)) { if (mdl_request->type == ticket->m_type && @@ -762,35 +673,33 @@ MDL_context::find_ticket(MDL_request *mdl_request) Unlike exclusive locks, shared locks are acquired one by one. This is interface is chosen to simplify introduction of - the new locking API to the system. MDL_context::acquire_shared_lock() + the new locking API to the system. MDL_context::try_acquire_shared_lock() is currently used from open_table(), and there we have only one table to work with. In future we may consider allocating multiple shared locks at once. - This function must be called after the lock is added to a context. + @param mdl_request [in/out] Lock request object for lock to be acquired - @param mdl_request [in] Lock request object for lock to be acquired - @param retry [out] Indicates that conflicting lock exists and another - attempt should be made after releasing all current - locks and waiting for conflicting lock go away - (using MDL_context::wait_for_locks()). - - @retval FALSE Success. - @retval TRUE Failure. Either error occurred or conflicting lock exists. - In the latter case "retry" parameter is set to TRUE. + @retval FALSE Success. The lock may have not been acquired. + Check the ticket, if it's NULL, a conflicting lock + exists and another attempt should be made after releasing + all current locks and waiting for conflicting lock go + away (using MDL_context::wait_for_locks()). + @retval TRUE Out of resources, an error has been reported. */ bool -MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry) +MDL_context::try_acquire_shared_lock(MDL_request *mdl_request) { MDL_lock *lock; MDL_key *key= &mdl_request->key; MDL_ticket *ticket; - *retry= FALSE; DBUG_ASSERT(mdl_request->is_shared() && mdl_request->ticket == NULL); + /* Don't take chances in production. */ + mdl_request->ticket= NULL; safe_mutex_assert_not_owner(&LOCK_open); if (m_has_global_shared_lock && @@ -807,6 +716,8 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry) if ((ticket= find_ticket(mdl_request))) { DBUG_ASSERT(ticket->m_state == MDL_ACQUIRED); + /* Only shared locks can be recursive. */ + DBUG_ASSERT(ticket->is_shared()); mdl_request->ticket= ticket; return FALSE; } @@ -816,8 +727,7 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry) if (!global_lock.is_lock_type_compatible(mdl_request->type, FALSE)) { pthread_mutex_unlock(&LOCK_mdl); - *retry= TRUE; - return TRUE; + return FALSE; } if (!(ticket= MDL_ticket::create(this, mdl_request->type))) @@ -854,13 +764,11 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry) { /* We can't get here if we allocated a new lock. */ DBUG_ASSERT(! lock->is_empty()); - *retry= TRUE; MDL_ticket::destroy(ticket); } - pthread_mutex_unlock(&LOCK_mdl); - return *retry; + return FALSE; } @@ -888,6 +796,19 @@ static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) } +/** + Acquire a single exclusive lock. A convenience + wrapper around the method acquiring a list of locks. +*/ + +bool MDL_context::acquire_exclusive_lock(MDL_request *mdl_request) +{ + MDL_request_list mdl_requests; + mdl_requests.push_front(mdl_request); + return acquire_exclusive_locks(&mdl_requests); +} + + /** Acquire exclusive locks. The context must contain the list of locks to be acquired. There must be no granted locks in the @@ -903,7 +824,7 @@ static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) @retval TRUE Failure */ -bool MDL_context::acquire_exclusive_locks() +bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests) { MDL_lock *lock; bool signalled= FALSE; @@ -911,9 +832,11 @@ bool MDL_context::acquire_exclusive_locks() MDL_request *mdl_request; MDL_ticket *ticket; st_my_thread_var *mysys_var= my_thread_var; - Request_iterator it(m_requests); + MDL_request_list::Iterator it(*mdl_requests); safe_mutex_assert_not_owner(&LOCK_open); + /* Exclusive locks must always be acquired first, all at once. */ + DBUG_ASSERT(! has_locks()); if (m_has_global_shared_lock) { @@ -931,6 +854,9 @@ bool MDL_context::acquire_exclusive_locks() DBUG_ASSERT(mdl_request->type == MDL_EXCLUSIVE && mdl_request->ticket == NULL); + /* Don't take chances in production. */ + mdl_request->ticket= NULL; + /* Early allocation: ticket is used as a shortcut to the lock. */ if (!(ticket= MDL_ticket::create(this, mdl_request->type))) goto err; @@ -1024,10 +950,7 @@ bool MDL_context::acquire_exclusive_locks() return FALSE; err: - /* - Remove our pending lock requests from the locks. - Ignore those lock requests which were not made MDL_PENDING. - */ + /* Remove our pending tickets from the locks. */ it.rewind(); while ((mdl_request= it++) && mdl_request->ticket) { @@ -1163,17 +1086,16 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() @param mdl_request [in] The lock request @param conflict [out] Indicates that conflicting lock exists - @retval TRUE Failure either conflicting lock exists or some error - occurred (probably OOM). - @retval FALSE Success, lock was acquired. + @retval TRUE Failure: some error occurred (probably OOM). + @retval FALSE Success: the lock might have not been acquired, + check request.ticket to find out. FIXME: Compared to lock_table_name_if_not_cached() it gives slightly more false negatives. */ bool -MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request, - bool *conflict) +MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request) { MDL_lock *lock; MDL_ticket *ticket; @@ -1184,7 +1106,7 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request, safe_mutex_assert_not_owner(&LOCK_open); - *conflict= FALSE; + mdl_request->ticket= NULL; pthread_mutex_lock(&LOCK_mdl); @@ -1197,7 +1119,8 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request, { MDL_ticket::destroy(ticket); MDL_lock::destroy(lock); - goto err; + pthread_mutex_unlock(&LOCK_mdl); + return TRUE; } mdl_request->ticket= ticket; lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE; @@ -1206,16 +1129,9 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request, ticket->m_state= MDL_ACQUIRED; ticket->m_lock= lock; global_lock.active_intention_exclusive++; - pthread_mutex_unlock(&LOCK_mdl); - return FALSE; } - - /* There is some lock for the object. */ - *conflict= TRUE; - -err: pthread_mutex_unlock(&LOCK_mdl); - return TRUE; + return FALSE; } @@ -1262,7 +1178,7 @@ bool MDL_context::acquire_global_shared_lock() /** Wait until there will be no locks that conflict with lock requests - in the context. + in the given list. This is a part of the locking protocol and must be used by the acquirer of shared locks after a back-off. @@ -1274,11 +1190,11 @@ bool MDL_context::acquire_global_shared_lock() */ bool -MDL_context::wait_for_locks() +MDL_context::wait_for_locks(MDL_request_list *mdl_requests) { MDL_lock *lock; MDL_request *mdl_request; - Request_iterator it(m_requests); + MDL_request_list::Iterator it(*mdl_requests); const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; @@ -1380,8 +1296,7 @@ void MDL_context::release_ticket(MDL_ticket *ticket) /** - Release all locks associated with the context, but leave them - in the context as lock requests. + Release all locks associated with the context. This function is used to back off in case of a lock conflict. It is also used to release shared locks in the end of an SQL @@ -1396,15 +1311,6 @@ void MDL_context::release_all_locks() safe_mutex_assert_not_owner(&LOCK_open); - /* Detach lock tickets from the requests for back off. */ - { - MDL_request *mdl_request; - Request_iterator it(m_requests); - - while ((mdl_request= it++)) - mdl_request->ticket= NULL; - } - if (m_tickets.is_empty()) DBUG_VOID_RETURN; @@ -1444,7 +1350,7 @@ void MDL_context::release_lock(MDL_ticket *ticket) /** Release all locks in the context which correspond to the same name/ - object as this lock request, remove lock requests from the context. + object as this lock request. @param ticket One of the locks for the name/object for which all locks should be released. @@ -1455,19 +1361,6 @@ void MDL_context::release_all_locks_for_name(MDL_ticket *name) /* Use MDL_ticket::lock to identify other locks for the same object. */ MDL_lock *lock= name->m_lock; - /* Remove matching lock requests from the context. */ - MDL_request *mdl_request; - Request_iterator it_mdl_request(m_requests); - - while ((mdl_request= it_mdl_request++)) - { - DBUG_ASSERT(mdl_request->ticket && - mdl_request->ticket->m_state == MDL_ACQUIRED); - - if (mdl_request->ticket->m_lock == lock) - remove_request(mdl_request); - } - /* Remove matching lock tickets from the context. */ MDL_ticket *ticket; Ticket_iterator it_ticket(m_tickets); @@ -1537,16 +1430,11 @@ bool MDL_context::is_exclusive_lock_owner(unsigned char type, const char *db, const char *name) { - MDL_key key(type, db, name); - MDL_ticket *ticket; - MDL_context::Ticket_iterator it(m_tickets); + MDL_request mdl_request; + mdl_request.init(type, db, name, MDL_EXCLUSIVE); + MDL_ticket *ticket= find_ticket(&mdl_request); - while ((ticket= it++)) - { - if (ticket->m_lock->type == MDL_lock::MDL_LOCK_EXCLUSIVE && - ticket->m_lock->key.is_equal(&key)) - break; - } + DBUG_ASSERT(ticket == NULL || ticket->m_state == MDL_ACQUIRED); return ticket; } diff --git a/sql/mdl.h b/sql/mdl.h index e3e41652bf4..dc6f0a34443 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -109,7 +109,6 @@ public: mdl_key_init(type_arg, db_arg, name_arg); } MDL_key() {} /* To use when part of MDL_request. */ - private: char m_ptr[MAX_MDLKEY_LENGTH]; uint m_length; @@ -157,12 +156,20 @@ public: /** Pointers for participating in the list of lock requests for this context. */ - MDL_request *next_in_context; - MDL_request **prev_in_context; + MDL_request *next_in_list; + MDL_request **prev_in_list; + /** + Pointer to the lock ticket object for this lock request. + Valid only if this lock request is satisfied. + */ + MDL_ticket *ticket; + /** A lock is requested based on a fully qualified name and type. */ MDL_key key; - void init(unsigned char type_arg, const char *db_arg, const char *name_arg); +public: + void init(unsigned char type_arg, const char *db_arg, const char *name_arg, + enum_mdl_type mdl_type_arg); /** Set type of lock request. Can be only applied to pending locks. */ inline void set_type(enum_mdl_type type_arg) { @@ -171,15 +178,37 @@ public: } bool is_shared() const { return type < MDL_EXCLUSIVE; } - /** - Pointer to the lock ticket object for this lock request. - Valid only if this lock request is satisfied. - */ - MDL_ticket *ticket; - static MDL_request *create(unsigned char type, const char *db, - const char *name, MEM_ROOT *root); + const char *name, enum_mdl_type mdl_type, + MEM_ROOT *root); + /* + This is to work around the ugliness of TABLE_LIST + compiler-generated assignment operator. It is currently used + in several places to quickly copy "most" of the members of the + table list. These places currently never assume that the mdl + request is carried over to the new TABLE_LIST, or shared + between lists. + + This method does not initialize the instance being assigned! + Use of init() for initialization after this assignment operator + is mandatory. Can only be used before the request has been + granted. + */ + MDL_request& operator=(const MDL_request &rhs) + { + ticket= NULL; + /* Do nothing, in particular, don't try to copy the key. */ + return *this; + } + /* Another piece of ugliness for TABLE_LIST constructor */ + MDL_request() {} + + MDL_request(const MDL_request *rhs) + :type(rhs->type), + ticket(NULL), + key(&rhs->key) + {} }; @@ -248,6 +277,11 @@ private: }; +typedef I_P_List > + MDL_request_list; + /** Context of the owner of metadata locks. I.e. each server connection has such a context. @@ -256,14 +290,6 @@ private: class MDL_context { public: - typedef I_P_List > - Request_list; - - typedef Request_list::Iterator Request_iterator; - typedef I_P_Listprelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_request= MDL_request::create(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : - thd->mem_root); + table->mdl_request.init(0, table->db, table->table_name, MDL_SHARED); /* Everyting else should be zeroed */ @@ -4026,10 +4023,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_request= MDL_request::create(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : - thd->mem_root); + table->mdl_request.init(0, table->db, table->table_name, MDL_SHARED); lex->add_to_query_tables(table); return table; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f4a182b321f..fdcd68cc2ea 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -691,7 +691,7 @@ my_bool acl_reload(THD *thd) tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= tables[2].skip_temporary= TRUE; - alloc_mdl_requests(tables, thd->mem_root); + init_mdl_requests(tables); if (simple_open_n_lock_tables(thd, tables)) { @@ -1596,10 +1596,7 @@ 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); - bzero((char*) &tables, sizeof(tables)); - tables.alias= tables.table_name= (char*) "user"; - tables.db= (char*) "mysql"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE); #ifdef HAVE_REPLICATION /* @@ -3111,7 +3108,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, ? tables+2 : 0); tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; - alloc_mdl_requests(tables, thd->mem_root); + init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using @@ -3329,7 +3326,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - alloc_mdl_requests(tables, thd->mem_root); + init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using @@ -3468,7 +3465,7 @@ bool mysql_grant(THD *thd, const char *db, List &list, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - alloc_mdl_requests(tables, thd->mem_root); + init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using @@ -3797,12 +3794,10 @@ static my_bool grant_reload_procs_priv(THD *thd) my_bool return_val= FALSE; DBUG_ENTER("grant_reload_procs_priv"); - bzero((char*) &table, sizeof(table)); - table.alias= table.table_name= (char*) "procs_priv"; - table.db= (char *) "mysql"; - table.lock_type= TL_READ; + table.init_one_table("mysql", 5, "procs_priv", + strlen("procs_priv"), "procs_priv", + TL_READ); table.skip_temporary= 1; - alloc_mdl_requests(&table, thd->mem_root); if (simple_open_n_lock_tables(thd, &table)) { @@ -3869,7 +3864,7 @@ my_bool grant_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type= tables[1].lock_type= TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= TRUE; - alloc_mdl_requests(tables, thd->mem_root); + init_mdl_requests(tables); /* To avoid deadlocks we should obtain table locks before @@ -5219,7 +5214,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) (tables+4)->lock_type= TL_WRITE; tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db= (tables+4)->db= (char*) "mysql"; - alloc_mdl_requests(tables, thd->mem_root); + init_mdl_requests(tables); #ifdef HAVE_REPLICATION /* diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 02871c118ca..7650d854efb 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -125,7 +125,8 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); -static bool tdc_wait_for_old_versions(THD *thd, MDL_context *context); +static bool tdc_wait_for_old_versions(THD *thd, + MDL_request_list *mdl_requests); static bool has_write_table_with_auto_increment(TABLE_LIST *tables); @@ -1360,8 +1361,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, leave prelocked mode if needed. */ -void close_thread_tables(THD *thd, - bool is_back_off) +void close_thread_tables(THD *thd) { TABLE *table; DBUG_ENTER("close_thread_tables"); @@ -1471,10 +1471,6 @@ void close_thread_tables(THD *thd, thd->locked_tables_mode= LTM_NONE; - /* - Note that we are leaving prelocked mode so we don't need - to care about THD::locked_tables_root. - */ /* Fallthrough */ } @@ -1503,11 +1499,6 @@ void close_thread_tables(THD *thd, if (thd->open_tables) close_open_tables(thd); - if (!is_back_off) - { - thd->mdl_context.remove_all_requests(); - } - /* Defer the release of metadata locks until the current transaction is either committed or rolled back. This prevents other statements @@ -2316,10 +2307,10 @@ void table_share_release_hook(void *share) static bool open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, MDL_request *mdl_request, - uint flags, - enum_open_table_action *action) + Open_table_context *ot_ctx, + uint flags) { - thd->mdl_context.add_request(mdl_request); + ot_ctx->add_request(mdl_request); if (table_list->lock_strategy) { @@ -2333,16 +2324,13 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, enforced by asserts in metadata locking subsystem. */ mdl_request->set_type(MDL_EXCLUSIVE); - if (thd->mdl_context.acquire_exclusive_locks()) - { - thd->mdl_context.remove_request(mdl_request); + DBUG_ASSERT(! thd->mdl_context.has_locks()); + + if (thd->mdl_context.acquire_exclusive_lock(mdl_request)) return 1; - } } else { - bool retry; - /* There is no MDL_SHARED_UPGRADABLE_HIGH_PRIO type of metadata lock so we want to be sure that caller doesn't pass us both flags simultaneously. @@ -2356,12 +2344,11 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, if (flags & MYSQL_LOCK_IGNORE_FLUSH) mdl_request->set_type(MDL_SHARED_HIGH_PRIO); - if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry)) + if (thd->mdl_context.try_acquire_shared_lock(mdl_request)) + return 1; + if (mdl_request->ticket == NULL) { - if (retry) - *action= OT_BACK_OFF_AND_RETRY; - else - thd->mdl_context.remove_request(mdl_request); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT); return 1; } } @@ -2416,7 +2403,7 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, - enum_open_table_action *action, uint flags) + Open_table_context *ot_ctx, uint flags) { reg1 TABLE *table; char key[MAX_DBKEY_LENGTH]; @@ -2428,11 +2415,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, TABLE_SHARE *share; DBUG_ENTER("open_table"); - /* Parsing of partitioning information from .frm needs thd->lex set up. */ - DBUG_ASSERT(thd->lex->is_lex_started); - - *action= OT_NO_ACTION; - /* an open table operation needs a lot of the stack space */ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias)) DBUG_RETURN(TRUE); @@ -2602,12 +2584,15 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, This is the normal use case. */ - mdl_request= table_list->mdl_request; + mdl_request= &table_list->mdl_request; if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - if (open_table_get_mdl_lock(thd, table_list, mdl_request, flags, - action)) + if (open_table_get_mdl_lock(thd, table_list, mdl_request, ot_ctx, flags)) + { + DEBUG_SYNC(thd, "before_open_table_wait_refresh"); DBUG_RETURN(TRUE); + } + DEBUG_SYNC(thd, "after_open_table_mdl_shared"); } /* @@ -2633,8 +2618,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ! (flags & MYSQL_LOCK_IGNORE_FLUSH)) { /* Someone did a refresh while thread was opening tables */ - *action= OT_BACK_OFF_AND_RETRY; pthread_mutex_unlock(&LOCK_open); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT); DBUG_RETURN(TRUE); } @@ -2766,9 +2751,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, separately in the caller for old table versions to go away (see tdc_wait_for_old_versions()). */ - *action= OT_BACK_OFF_AND_RETRY; release_table_share(share); pthread_mutex_unlock(&LOCK_open); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT); DBUG_RETURN(TRUE); } /* Force close at once after usage */ @@ -2808,12 +2793,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (error == 7) { share->version= 0; - *action= OT_DISCOVER; + (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER); } else if (share->crashed) { share->version= 0; - *action= OT_REPAIR; + (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR); } goto err_unlock; @@ -2891,10 +2876,8 @@ err_unlock: err_unlock2: pthread_mutex_unlock(&LOCK_open); if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) - { thd->mdl_context.release_lock(mdl_ticket); - thd->mdl_context.remove_request(mdl_request); - } + DBUG_RETURN(TRUE); } @@ -3004,6 +2987,9 @@ Locked_tables_list::init_locked_tables(THD *thd) return TRUE; } + memcpy(db, src_table_list->db, db_len + 1); + memcpy(table_name, src_table_list->table_name, table_name_len + 1); + memcpy(alias, src_table_list->alias, alias_len + 1); /** Sic: remember the *actual* table level lock type taken, to acquire the exact same type in reopen_tables(). @@ -3014,11 +3000,9 @@ Locked_tables_list::init_locked_tables(THD *thd) dst_table_list->init_one_table(db, db_len, table_name, table_name_len, alias, src_table_list->table->reginfo.lock_type); - dst_table_list->mdl_request= src_table_list->mdl_request; dst_table_list->table= table; - memcpy(db, src_table_list->db, db_len + 1); - memcpy(table_name, src_table_list->table_name, table_name_len + 1); - memcpy(alias, src_table_list->alias, alias_len + 1); + dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket; + /* Link last into the list of tables */ *(dst_table_list->prev_global= m_locked_tables_last)= dst_table_list; m_locked_tables_last= &dst_table_list->next_global; @@ -3227,7 +3211,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count) bool Locked_tables_list::reopen_tables(THD *thd) { - enum enum_open_table_action ot_action_unused; + Open_table_context ot_ctx_unused(thd); bool lt_refresh_unused; size_t reopen_count= 0; MYSQL_LOCK *lock; @@ -3240,7 +3224,7 @@ Locked_tables_list::reopen_tables(THD *thd) continue; /* Links into thd->open_tables upon success */ - if (open_table(thd, table_list, thd->mem_root, &ot_action_unused, + if (open_table(thd, table_list, thd->mem_root, &ot_ctx_unused, MYSQL_OPEN_REOPEN)) { unlink_all_closed_tables(thd, 0, reopen_count); @@ -3591,6 +3575,43 @@ end_with_lock_open: } +/** Open_table_context */ + +Open_table_context::Open_table_context(THD *thd) + :m_action(OT_NO_ACTION), + m_can_deadlock(thd->in_multi_stmt_transaction() && + thd->mdl_context.has_locks()) +{} + + +/** + Check if we can back-off and set back off action if we can. + Otherwise report and return error. + + @retval TRUE if back-off is impossible. + @retval FALSE if we can back off. Back off action has been set. +*/ + +bool +Open_table_context:: +request_backoff_action(enum_open_table_action action_arg) +{ + /* + We have met a exclusive metadata lock or a old version of + table and we are inside a transaction that already hold locks. + We can't follow the locking protocol in this scenario as it + might lead to deadlocks. + */ + if (m_can_deadlock) + { + my_error(ER_LOCK_DEADLOCK, MYF(0)); + return TRUE; + } + m_action= action_arg; + return FALSE; +} + + /** Recover from failed attempt ot open table by performing requested action. @@ -3604,57 +3625,58 @@ end_with_lock_open: @retval TRUE - Error */ -static bool -recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, - enum_open_table_action action) +bool +Open_table_context:: +recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table) { bool result= FALSE; - MDL_request *mdl_request= table->mdl_request; - - switch (action) + /* Execute the action. */ + switch (m_action) { - case OT_BACK_OFF_AND_RETRY: - result= (thd->mdl_context.wait_for_locks() || - tdc_wait_for_old_versions(thd, &thd->mdl_context)); - thd->mdl_context.remove_all_requests(); + case OT_WAIT: + result= (thd->mdl_context.wait_for_locks(&m_mdl_requests) || + tdc_wait_for_old_versions(thd, &m_mdl_requests)); break; case OT_DISCOVER: - mdl_request->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(mdl_request); - if (thd->mdl_context.acquire_exclusive_locks()) { - thd->mdl_context.remove_request(mdl_request); - return TRUE; - } - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); - ha_create_table_from_engine(thd, table->db, table->table_name); - pthread_mutex_unlock(&LOCK_open); + MDL_request mdl_xlock_request(&table->mdl_request); + mdl_xlock_request.set_type(MDL_EXCLUSIVE); + if ((result= + thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request))) + break; + pthread_mutex_lock(&LOCK_open); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); + ha_create_table_from_engine(thd, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); - thd->warning_info->clear_warning_info(thd->query_id); - thd->clear_error(); // Clear error message - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - break; + thd->warning_info->clear_warning_info(thd->query_id); + thd->clear_error(); // Clear error message + thd->mdl_context.release_lock(mdl_xlock_request.ticket); + break; + } case OT_REPAIR: - mdl_request->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(mdl_request); - if (thd->mdl_context.acquire_exclusive_locks()) { - thd->mdl_context.remove_request(mdl_request); - return TRUE; - } - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); - pthread_mutex_unlock(&LOCK_open); + MDL_request mdl_xlock_request(&table->mdl_request); + mdl_xlock_request.set_type(MDL_EXCLUSIVE); + if ((result= + thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request))) + break; - result= auto_repair_table(thd, table); - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - break; + pthread_mutex_lock(&LOCK_open); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); + pthread_mutex_unlock(&LOCK_open); + + result= auto_repair_table(thd, table); + thd->mdl_context.release_lock(mdl_xlock_request.ticket); + break; + } default: DBUG_ASSERT(0); } + /* Remove all old requests, they will be re-added. */ + m_mdl_requests.empty(); + /* Prepare for possible another back-off. */ + m_action= OT_NO_ACTION; return result; } @@ -3722,14 +3744,13 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) { TABLE_LIST *tables= NULL; - enum_open_table_action action; + Open_table_context ot_ctx(thd); int result=0; bool error; MEM_ROOT new_frm_mem; /* Also used for indicating that prelocking is need */ TABLE_LIST **query_tables_last_own; bool safe_to_ignore_table; - bool has_locks= thd->mdl_context.has_locks(); DBUG_ENTER("open_tables"); /* temporary mem_root for new .frm parsing. @@ -3856,31 +3877,19 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ Prelock_error_handler prelock_handler; thd->push_internal_handler(& prelock_handler); - error= open_table(thd, tables, &new_frm_mem, &action, flags); + error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags); thd->pop_internal_handler(); safe_to_ignore_table= prelock_handler.safely_trapped_errors(); } else - error= open_table(thd, tables, &new_frm_mem, &action, flags); + error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags); free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); if (error) { - if (action) + if (ot_ctx.can_recover_from_failed_open_table()) { - /* - We have met a exclusive metadata lock or a old version of table and - we are inside a transaction that already hold locks. We can't follow - the locking protocol in this scenario as it might lead to deadlocks. - */ - if (thd->in_multi_stmt_transaction() && has_locks) - { - my_error(ER_LOCK_DEADLOCK, MYF(0)); - result= -1; - goto err; - } - /* We have met exclusive metadata lock or old version of table. Now we have to close all tables which are not up to date/release metadata @@ -3897,13 +3906,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ if (query_tables_last_own) thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - close_tables_for_reopen(thd, start, (action == OT_BACK_OFF_AND_RETRY)); + close_tables_for_reopen(thd, start); /* Here we rely on the fact that 'tables' still points to the valid TABLE_LIST element. Altough currently this assumption is valid it may change in future. */ - if (recover_from_failed_open_table_attempt(thd, tables, action)) + if (ot_ctx.recover_from_failed_open_table_attempt(thd, tables)) { result= -1; goto err; @@ -4210,7 +4219,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, uint lock_flags) { TABLE *table; - enum_open_table_action action; + Open_table_context ot_ctx(thd); bool refresh; bool error; DBUG_ENTER("open_ltable"); @@ -4224,16 +4233,18 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, table_list->required_type= FRMTYPE_TABLE; retry: - while ((error= open_table(thd, table_list, thd->mem_root, &action, 0)) && - action) + while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) && + ot_ctx.can_recover_from_failed_open_table()) { /* - Even altough we have failed to open table we still need to - call close_thread_tables() to release metadata locks which + Even though we have failed to open table we still need to + call release_all_locks() to release metadata locks which might have been acquired successfully. */ - close_thread_tables(thd, (action == OT_BACK_OFF_AND_RETRY)); - if (recover_from_failed_open_table_attempt(thd, table_list, action)) + if (! thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + table_list->mdl_request.ticket= 0; + if (ot_ctx.recover_from_failed_open_table_attempt(thd, table_list)) break; } @@ -4272,8 +4283,20 @@ retry: { if (refresh) { - close_thread_tables(thd); - goto retry; + if (ot_ctx.can_deadlock()) + { + my_error(ER_LOCK_DEADLOCK, MYF(0)); + table= 0; + } + else + { + close_thread_tables(thd); + table_list->table= NULL; + table_list->mdl_request.ticket= NULL; + if (! thd->locked_tables_mode) + thd->mdl_context.release_all_locks(); + goto retry; + } } else table= 0; @@ -4283,7 +4306,7 @@ retry: else table= 0; - end: +end: thd_proc_info(thd, 0); DBUG_RETURN(table); } @@ -4320,6 +4343,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, { uint counter; bool need_reopen; + bool has_locks= thd->mdl_context.has_locks(); DBUG_ENTER("open_and_lock_tables_derived"); DBUG_PRINT("enter", ("derived handling: %d", derived)); @@ -4339,7 +4363,12 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, break; if (!need_reopen) DBUG_RETURN(-1); - close_tables_for_reopen(thd, &tables, FALSE); + if (thd->in_multi_stmt_transaction() && has_locks) + { + my_error(ER_LOCK_DEADLOCK, MYF(0)); + DBUG_RETURN(-1); + } + close_tables_for_reopen(thd, &tables); } if (derived && (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -4677,10 +4706,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, table->table->query_id= thd->query_id; if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) { - /* - This was an attempt to enter prelocked mode so there is no - need to care about THD::locked_tables_root here. - */ mysql_unlock_tables(thd, thd->lock); thd->lock= 0; DBUG_RETURN(-1); @@ -4767,7 +4792,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, */ -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off) +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) { /* If table list consists only from tables from prelocking set, table list @@ -4778,8 +4803,11 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off) thd->lex->chop_off_not_own_tables(); sp_remove_not_own_routines(thd->lex); for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) + { tmp->table= 0; - close_thread_tables(thd, is_back_off); + tmp->mdl_request.ticket= NULL; + } + close_thread_tables(thd); if (!thd->locked_tables_mode) thd->mdl_context.release_all_locks(); } @@ -7855,7 +7883,8 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, @param context Metadata locking context with locks. */ -static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context) +static bool +tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) { TABLE_SHARE *share; const char *old_msg; @@ -7872,7 +7901,7 @@ static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context) mysql_ha_flush(thd); pthread_mutex_lock(&LOCK_open); - MDL_context::Request_iterator it= mdl_context->get_requests(); + MDL_request_list::Iterator it(*mdl_requests); while ((mdl_request= it++)) { if ((share= get_cached_table_share(mdl_request->key.db_name(), @@ -8095,8 +8124,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, DBUG_ENTER("open_system_tables_for_read"); - alloc_mdl_requests(table_list, thd->mem_root); - /* Besides using new Open_tables_state for opening system tables, we also have to backup and reset/and then restore part of LEX @@ -8170,8 +8197,6 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table) { DBUG_ENTER("open_system_table_for_update"); - alloc_mdl_requests(one_table, thd->mem_root); - TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0); if (table) { @@ -8208,7 +8233,6 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table, thd->reset_n_backup_open_tables_state(backup); - alloc_mdl_requests(one_table, thd->mem_root); if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) { DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE); @@ -8286,7 +8310,6 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) pthread_mutex_unlock(&LOCK_open); thd->mdl_context.release_all_locks(); - thd->mdl_context.remove_all_requests(); thd->restore_backup_open_tables_state(backup); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7252f078f81..d83b60810ab 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -466,8 +466,7 @@ THD::THD() #if defined(ENABLED_DEBUG_SYNC) debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ - main_warning_info(0), - locked_tables_root(NULL) + main_warning_info(0) { ulong tmp; diff --git a/sql/sql_class.h b/sql/sql_class.h index 4b6564fb9da..9edb5f97713 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1180,6 +1180,49 @@ private: Internal_error_handler *m_err_handler; }; + +/** + A context of open_tables() function, used to recover + from a failed open_table() attempt. + + Implemented in sql_base.cc. +*/ + +class Open_table_context +{ +public: + enum enum_open_table_action + { + OT_NO_ACTION= 0, + OT_WAIT, + OT_DISCOVER, + OT_REPAIR + }; + Open_table_context(THD *thd); + + bool recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *tables); + bool request_backoff_action(enum_open_table_action action_arg); + + void add_request(MDL_request *request) + { m_mdl_requests.push_front(request); } + + bool can_recover_from_failed_open_table() const + { return m_action != OT_NO_ACTION; } + bool can_deadlock() const { return m_can_deadlock; } +private: + /** List of requests for all locks taken so far. Used for waiting on locks. */ + MDL_request_list m_mdl_requests; + /** Back off action. */ + enum enum_open_table_action m_action; + /** + Whether we had any locks when this context was created. + If we did, they are from the previous statement of a transaction, + and we can't safely do back-off (and release them). + */ + bool m_can_deadlock; +}; + + /** Tables that were locked with LOCK TABLES statement. @@ -1236,7 +1279,6 @@ public: } bool init_locked_tables(THD *thd); TABLE_LIST *locked_tables() { return m_locked_tables; } - MEM_ROOT *locked_tables_root() { return &m_locked_tables_root; } void unlink_from_list(THD *thd, TABLE_LIST *table_list, bool remove_from_locked_tables); void unlink_all_closed_tables(THD *thd, @@ -1895,15 +1937,6 @@ public: /* Debug Sync facility. See debug_sync.cc. */ struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ - - /** - Points to the memory root of Locked_tables_list if - we're locking the tables for LOCK TABLES. Otherwise is NULL. - This is necessary to ensure that metadata locks allocated for - tables used in triggers will persist after statement end. - */ - MEM_ROOT *locked_tables_root; - THD(); ~THD(); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fc92dbee0a1..c3e205848de 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_request *mdl_request= NULL; + MDL_request mdl_request; + bool has_mdl_lock= FALSE; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1145,6 +1146,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) if (!dont_send_ok) { enum legacy_db_type table_type; + /* + FIXME: Code of TRUNCATE breaks the meta-data + locking protocol since it tries to find out the table storage + engine and therefore accesses table in some way without holding + any kind of meta-data lock. + */ mysql_frm_type(thd, path, &table_type); if (table_type == DB_TYPE_UNKNOWN) { @@ -1170,20 +1177,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - /* - FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since - tries to get table enging and therefore accesses table in some way - without holding any kind of meta-data lock. - */ - mdl_request= MDL_request::create(0, table_list->db, - table_list->table_name, thd->mem_root); - mdl_request->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(mdl_request); - if (thd->mdl_context.acquire_exclusive_locks()) - { - thd->mdl_context.remove_request(mdl_request); + mdl_request.init(0, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) DBUG_RETURN(TRUE); - } + + has_mdl_lock= TRUE; + pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, table_list->table_name); @@ -1212,19 +1211,13 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_request) - { - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - } + if (has_mdl_lock) + thd->mdl_context.release_lock(mdl_request.ticket); } else if (error) { - if (mdl_request) - { - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - } + if (has_mdl_lock) + thd->mdl_context.release_lock(mdl_request.ticket); } DBUG_RETURN(error); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 4b07a00c779..1b7e45cec5d 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -150,7 +150,6 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) } pthread_mutex_unlock(&LOCK_open); thd->handler_mdl_context.release_lock(mdl_ticket); - thd->handler_mdl_context.remove_request(tables->mdl_request); } else if (tables->table) { @@ -163,6 +162,8 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) /* Mark table as closed, ready for re-open if necessary. */ tables->table= NULL; + /* Safety, cleanup the pointer to satisfy MDL assertions. */ + tables->mdl_request.ticket= NULL; } /* @@ -195,7 +196,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) int error; TABLE *backup_open_tables; MDL_context backup_mdl_context; - MDL_request *mdl_request; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -246,7 +246,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) &db, (uint) dblen, &name, (uint) namelen, &alias, (uint) aliaslen, - &mdl_request, sizeof(MDL_request), NullS))) { DBUG_PRINT("exit",("ERROR")); @@ -260,8 +259,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - mdl_request->init(0, db, name); - hash_tables->mdl_request= mdl_request; + hash_tables->mdl_request.init(0, db, name, MDL_SHARED); /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 003741a7ddc..af67db45b36 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -653,6 +653,7 @@ bool mysqld_help(THD *thd, const char *mask) tables[3].alias= tables[3].table_name= (char*) "help_keyword"; tables[3].lock_type= TL_READ; tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql"; + init_mdl_requests(tables); Open_tables_state open_tables_state_backup; if (open_system_tables_for_read(thd, tables, &open_tables_state_backup)) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8553db8cf2b..e0537c75e07 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2399,7 +2399,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->lex->set_stmt_unsafe(); thd->set_current_stmt_binlog_row_based_if_mixed(); - alloc_mdl_requests(&di->table_list, thd->mem_root); + init_mdl_requests(&di->table_list); if (di->open_and_lock_table()) goto err; @@ -3464,7 +3464,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, Item *item; Field *tmp_field; bool not_used; - enum_open_table_action not_used2; DBUG_ENTER("create_table_from_items"); tmp_table.alias= 0; @@ -3543,13 +3542,13 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - enum enum_open_table_action ot_action_unused; + Open_table_context ot_ctx_unused(thd); /* Here we open the destination table, on which we already have an exclusive metadata lock. */ if (open_table(thd, create_table, thd->mem_root, - &ot_action_unused, MYSQL_OPEN_REOPEN)) + &ot_ctx_unused, MYSQL_OPEN_REOPEN)) { pthread_mutex_lock(&LOCK_open); quick_rm_table(create_info->db_type, create_table->db, @@ -3562,7 +3561,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } else { - if (open_table(thd, create_table, thd->mem_root, ¬_used2, + Open_table_context ot_ctx_unused(thd); + if (open_table(thd, create_table, thd->mem_root, &ot_ctx_unused, MYSQL_OPEN_TEMPORARY_ONLY) && !create_info->table_existed) { @@ -3637,18 +3637,28 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) */ class MY_HOOKS : public TABLEOP_HOOKS { public: - MY_HOOKS(select_create *x, TABLE_LIST *create_table, - TABLE_LIST *select_tables) - : ptr(x), all_tables(*create_table) + MY_HOOKS(select_create *x, TABLE_LIST *create_table_arg, + TABLE_LIST *select_tables_arg) + : ptr(x), + create_table(create_table_arg), + select_tables(select_tables_arg) { - all_tables.next_global= select_tables; } private: virtual int do_postlock(TABLE **tables, uint count) { + int error; THD *thd= const_cast(ptr->get_thd()); - if (int error= decide_logging_format(thd, &all_tables)) + TABLE_LIST *save_next_global= create_table->next_global; + + create_table->next_global= select_tables; + + error= decide_logging_format(thd, create_table); + + create_table->next_global= save_next_global; + + if (error) return error; TABLE const *const table = *tables; @@ -3662,7 +3672,8 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) } select_create *ptr; - TABLE_LIST all_tables; + TABLE_LIST *create_table; + TABLE_LIST *select_tables; }; MY_HOOKS hooks(this, create_table, select_tables); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ddc163072a7..35c0973d103 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1107,7 +1107,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, select_lex.table_list.link_in_list((uchar*) &table_list, (uchar**) &table_list.next_local); thd->lex->add_to_query_tables(&table_list); - alloc_mdl_requests(&table_list, thd->mem_root); + init_mdl_requests(&table_list); /* switch on VIEW optimisation: do not fill temporary tables */ thd->lex->sql_command= SQLCOM_SHOW_FIELDS; @@ -3337,18 +3337,16 @@ end_with_restore_list: !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) goto error; - alloc_mdl_requests(all_tables, thd->locked_tables_list.locked_tables_root()); + init_mdl_requests(all_tables); thd->options|= OPTION_TABLE_LOCK; thd->in_lock_tables=1; - thd->locked_tables_root= thd->locked_tables_list.locked_tables_root(); res= (open_and_lock_tables_derived(thd, all_tables, FALSE, MYSQL_OPEN_TAKE_UPGRADABLE_MDL) || thd->locked_tables_list.init_locked_tables(thd)); thd->in_lock_tables= 0; - thd->locked_tables_root= NULL; if (res) { @@ -6021,9 +6019,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_request= - MDL_request::create(0, ptr->db, ptr->table_name, thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + ptr->mdl_request.init(0, ptr->db, ptr->table_name, MDL_SHARED); DBUG_RETURN(ptr); } diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index b7efe13e26e..7dd7dd0d4b9 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1349,7 +1349,6 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) #ifdef EMBEDDED_LIBRARY bool table_exists; #endif /* EMBEDDED_LIBRARY */ - MDL_request mdl_request; DBUG_ENTER("plugin_load"); if (!(new_thd= new THD)) @@ -1363,12 +1362,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) lex_start(new_thd); new_thd->db= my_strdup("mysql", MYF(0)); new_thd->db_length= 5; - bzero((uchar*)&tables, sizeof(tables)); - tables.alias= tables.table_name= (char*)"plugin"; - tables.lock_type= TL_READ; - tables.db= new_thd->db; - tables.mdl_request= &mdl_request; - mdl_request.init(0, tables.db, tables.table_name); + tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ); #ifdef EMBEDDED_LIBRARY /* @@ -1656,14 +1650,10 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl struct st_plugin_int *tmp; DBUG_ENTER("mysql_install_plugin"); - bzero(&tables, sizeof(tables)); - tables.db= (char *)"mysql"; - tables.table_name= tables.alias= (char *)"plugin"; + tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE); if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); - alloc_mdl_requests(&tables, thd->mem_root); - /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(TRUE); @@ -1734,10 +1724,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) struct st_plugin_int *plugin; DBUG_ENTER("mysql_uninstall_plugin"); - bzero(&tables, sizeof(tables)); - tables.db= (char *)"mysql"; - tables.table_name= tables.alias= (char *)"plugin"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index c46a01cd534..8fafbbd8e70 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -228,11 +228,7 @@ bool servers_reload(THD *thd) DBUG_PRINT("info", ("locking servers_cache")); rw_wrlock(&THR_LOCK_servers); - bzero((char*) tables, sizeof(tables)); - tables[0].alias= tables[0].table_name= (char*) "servers"; - tables[0].db= (char*) "mysql"; - tables[0].lock_type= TL_READ; - alloc_mdl_requests(tables, thd->mem_root); + tables[0].init_one_table("mysql", 5, "servers", 7, "servers", TL_READ); if (simple_open_n_lock_tables(thd, tables)) { @@ -363,10 +359,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server) DBUG_ENTER("insert_server"); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.alias= tables.table_name= (char*) "servers"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE); /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) @@ -582,10 +575,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) DBUG_PRINT("info", ("server name server->server_name %s", server_options->server_name)); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.alias= tables.table_name= (char*) "servers"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE); rw_wrlock(&THR_LOCK_servers); @@ -707,10 +697,8 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) TABLE_LIST tables; DBUG_ENTER("update_server"); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*)"mysql"; - tables.alias= tables.table_name= (char*)"servers"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "servers", 7, "servers", + TL_WRITE); if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9727498cb2e..6a5895f9446 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2934,7 +2934,7 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, table, res, db_name, table_name)); thd->temporary_tables= 0; - close_tables_for_reopen(thd, &show_table_list, FALSE); + close_tables_for_reopen(thd, &show_table_list); DBUG_RETURN(error); } @@ -3065,30 +3065,21 @@ uint get_table_open_method(TABLE_LIST *tables, */ static bool -acquire_high_prio_shared_mdl_lock(THD *thd, MDL_request *mdl_request, - TABLE_LIST *table) +acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table) { - bool retry; - - mdl_request->init(0, table->db, table->table_name); - table->mdl_request= mdl_request; - thd->mdl_context.add_request(mdl_request); - mdl_request->set_type(MDL_SHARED_HIGH_PRIO); - - while (1) + bool error; + table->mdl_request.init(0, table->db, table->table_name, + MDL_SHARED_HIGH_PRIO); + while (!(error= + thd->mdl_context.try_acquire_shared_lock(&table->mdl_request)) && + !table->mdl_request.ticket) { - if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry)) - { - if (!retry || thd->mdl_context.wait_for_locks()) - { - thd->mdl_context.remove_all_requests(); - return TRUE; - } - continue; - } - break; + MDL_request_list mdl_requests; + mdl_requests.push_front(&table->mdl_request); + if ((error= thd->mdl_context.wait_for_locks(&mdl_requests))) + break; } - return FALSE; + return error; } @@ -3123,7 +3114,6 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, char key[MAX_DBKEY_LENGTH]; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; - MDL_request mdl_request; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); @@ -3153,7 +3143,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, simply obtaining internal lock of data-dictionary (ATM it is LOCK_open) instead of obtaning full-blown metadata lock. */ - if (acquire_high_prio_shared_mdl_lock(thd, &mdl_request, &table_list)) + if (acquire_high_prio_shared_mdl_lock(thd, &table_list)) { /* Some error occured (most probably we have been killed while @@ -3213,8 +3203,7 @@ err_share: err_unlock: pthread_mutex_unlock(&LOCK_open); - thd->mdl_context.release_lock(mdl_request.ticket); - thd->mdl_context.remove_request(&mdl_request); + thd->mdl_context.release_lock(table_list.mdl_request.ticket); thd->clear_error(); return res; } @@ -3462,7 +3451,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) res= schema_table->process_table(thd, show_table_list, table, res, &orig_db_name, &tmp_lex_string); - close_tables_for_reopen(thd, &show_table_list, FALSE); + close_tables_for_reopen(thd, &show_table_list); } DBUG_ASSERT(!lex->query_tables_own_last); if (res) @@ -7199,14 +7188,14 @@ static bool show_create_trigger_impl(THD *thd, - do not update Lex::query_tables in add_table_to_list(). */ -static TABLE_LIST *get_trigger_table_impl( - THD *thd, - const sp_name *trg_name) +static +TABLE_LIST *get_trigger_table_impl(THD *thd, const sp_name *trg_name) { char trn_path_buff[FN_REFLEN]; - LEX_STRING trn_path= { trn_path_buff, 0 }; + LEX_STRING db; LEX_STRING tbl_name; + TABLE_LIST *table; build_trn_path(thd, trg_name, &trn_path); @@ -7220,25 +7209,19 @@ static TABLE_LIST *get_trigger_table_impl( return NULL; /* We need to reset statement table list to be PS/SP friendly. */ - - TABLE_LIST *table; - - if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST)))) - { - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(TABLE_LIST)); + if (!(table= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)))) return NULL; - } - table->db_length= trg_name->m_db.length; - table->db= thd->strmake(trg_name->m_db.str, trg_name->m_db.length); + db= trg_name->m_db; - table->table_name_length= tbl_name.length; - table->table_name= thd->strmake(tbl_name.str, tbl_name.length); + db.str= thd->strmake(db.str, db.length); + tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length); - table->alias= thd->strmake(tbl_name.str, tbl_name.length); + if (db.str == NULL || tbl_name.str == NULL) + return NULL; - table->lock_type= TL_IGNORE; - table->cacheable_table= 0; + table->init_one_table(db.str, db.length, tbl_name.str, tbl_name.length, + tbl_name.str, TL_IGNORE); return table; } @@ -7254,7 +7237,8 @@ static TABLE_LIST *get_trigger_table_impl( @return TABLE_LIST object corresponding to the base table. */ -static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) +static +TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) { /* Acquire LOCK_open (stop the server). */ @@ -7310,8 +7294,6 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ - alloc_mdl_requests(lst, thd->mem_root); - if (open_tables(thd, &lst, &num_tables, 0)) { my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0), diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 527095f2c88..c389ef2aef3 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -16,6 +16,7 @@ /* drop and alter of tables */ #include "mysql_priv.h" +#include "debug_sync.h" #include #include #include @@ -1907,14 +1908,25 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, if (find_temporary_table(thd, table->db, table->table_name)) { /* - Since we don't acquire metadata lock if we have found temporary - table, we should do something to avoid releasing it at the end. + A temporary table. + + Don't try to find a corresponding MDL lock or assign it + to table->mdl_request.ticket. There can't be metadata + locks for temporary tables: they are local to the session. + + Later in this function we release the MDL lock only if + table->mdl_requeset.ticket is not NULL. Thus here we + ensure that we won't release the metadata lock on the base + table locked with LOCK TABLES as a side effect of temporary + table drop. */ - table->mdl_request= NULL; + DBUG_ASSERT(table->mdl_request.ticket == NULL); } else { /* + Not a temporary table. + Since 'tables' list can't contain duplicates (this is ensured by parser) it is safe to cache pointer to the TABLE instances in its elements. @@ -1923,7 +1935,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->table_name); if (!table->table) DBUG_RETURN(1); - table->mdl_request->ticket= table->table->mdl_ticket; + table->mdl_request.ticket= table->table->mdl_ticket; } } } @@ -2188,7 +2200,7 @@ err: Under LOCK TABLES we should release meta-data locks on the tables which were dropped. Otherwise we can rely on close_thread_tables() doing this. Unfortunately in this case we are likely to get more - false positives in lock_table_name_if_not_cached() function. So + false positives in try_acquire_exclusive_lock() function. So it makes sense to remove exclusive meta-data locks in all cases. Leave LOCK TABLES mode if we managed to drop all tables which were @@ -2203,14 +2215,14 @@ err: } for (table= tables; table; table= table->next_local) { - if (table->mdl_request) + if (table->mdl_request.ticket) { /* Under LOCK TABLES we may have several instances of table open and locked and therefore have to remove several metadata lock requests associated with them. */ - thd->mdl_context.release_all_locks_for_name(table->mdl_request->ticket); + thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket); } } } @@ -4094,47 +4106,6 @@ warn: } -/** - Auxiliary function which obtains exclusive meta-data lock on the - table if there are no shared or exclusive on it already. - - See mdl_try_acquire_exclusive_lock() function for more info. - - TODO: This function is here mostly to simplify current patch - and probably should be removed. - TODO: Investigate if it is kosher to leave lock request in the - context in the case when we fail to obtain the lock. -*/ - -static bool lock_table_name_if_not_cached(THD *thd, const char *db, - const char *table_name, - MDL_request **mdl_request) -{ - bool conflict; - - if (!(*mdl_request= MDL_request::create(0, db, table_name, thd->mem_root))) - return TRUE; - (*mdl_request)->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(*mdl_request); - if (thd->mdl_context.try_acquire_exclusive_lock(*mdl_request, &conflict)) - { - /* - To simplify our life under LOCK TABLES we remove unsatisfied - lock request from the context. - */ - thd->mdl_context.remove_request(*mdl_request); - if (!conflict) - { - /* Probably OOM. */ - return TRUE; - } - else - *mdl_request= NULL; - } - return FALSE; -} - - /* Database and name-locking aware wrapper for mysql_create_table_no_lock(), */ @@ -4145,7 +4116,8 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, bool internal_tmp_table, uint select_field_count) { - MDL_request *target_mdl_request= NULL; + MDL_request target_mdl_request; + bool has_target_mdl_lock= FALSE; bool result; DBUG_ENTER("mysql_create_table"); @@ -4168,13 +4140,15 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request)) + target_mdl_request.init(0, db, table_name, MDL_EXCLUSIVE); + if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) { result= TRUE; goto unlock; } - if (!target_mdl_request) + if (target_mdl_request.ticket == NULL) { + /* Table exists and is locked by some other thread. */ if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -4191,6 +4165,9 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, } goto unlock; } + /* Got lock. */ + DEBUG_SYNC(thd, "locked_table_name"); + has_target_mdl_lock= TRUE; } result= mysql_create_table_no_lock(thd, db, table_name, create_info, @@ -4199,11 +4176,9 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, select_field_count); unlock: - if (target_mdl_request) - { - thd->mdl_context.release_lock(target_mdl_request->ticket); - thd->mdl_context.remove_request(target_mdl_request); - } + if (has_target_mdl_lock) + thd->mdl_context.release_lock(target_mdl_request.ticket); + pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) pthread_cond_signal(&COND_refresh); @@ -4364,11 +4339,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, int error= 0; TABLE tmp_table, *table; TABLE_SHARE *share; + bool has_mdl_lock= FALSE; char from[FN_REFLEN],tmp[FN_REFLEN+32]; const char **ext; MY_STAT stat_info; - MDL_request *mdl_request= NULL; - enum enum_open_table_action ot_action_unused; + Open_table_context ot_ctx_unused(thd); DBUG_ENTER("prepare_for_repair"); uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH | MYSQL_OPEN_HAS_MDL_LOCK); @@ -4386,15 +4361,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); - mdl_request= MDL_request::create(0, table_list->db, - table_list->table_name, thd->mem_root); - mdl_request->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(mdl_request); - if (thd->mdl_context.acquire_exclusive_locks()) - { - thd->mdl_context.remove_request(mdl_request); + table_list->mdl_request.init(0, table_list->db, table_list->table_name, + MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request)) DBUG_RETURN(0); - } + has_mdl_lock= TRUE; pthread_mutex_lock(&LOCK_open); if (!(share= (get_table_share(thd, table_list, key, key_length, 0, @@ -4412,7 +4383,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, } pthread_mutex_unlock(&LOCK_open); table= &tmp_table; - table_list->mdl_request= mdl_request; } /* A MERGE table must not come here. */ @@ -4507,7 +4477,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, to finish the repair in the handler later on. */ if (open_table(thd, table_list, thd->mem_root, - &ot_action_unused, reopen_for_repair_flags)) + &ot_ctx_unused, reopen_for_repair_flags)) { error= send_check_errmsg(thd, table_list, "repair", "Failed to open partially repaired table"); @@ -4523,11 +4493,9 @@ end: pthread_mutex_unlock(&LOCK_open); } /* In case of a temporary table there will be no metadata lock. */ - if (error && mdl_request) - { - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - } + if (error && has_mdl_lock) + thd->mdl_context.release_lock(table_list->mdl_request.ticket); + DBUG_RETURN(error); } @@ -4938,6 +4906,8 @@ send_result_message: thd->mdl_context.release_all_locks(); if (!result_code) // recreation went ok { + /* Clear the ticket released in close_thread_tables(). */ + table->mdl_request.ticket= NULL; if ((table->table= open_ltable(thd, table, lock_type, 0)) && ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0)) result_code= 0; // analyze went ok @@ -5241,9 +5211,9 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { - MDL_request *target_mdl_request= NULL; - char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1]; + char src_path[FN_REFLEN + 1], dst_path[FN_REFLEN + 1]; uint dst_path_length; + bool has_mdl_lock= FALSE; char *db= table->db; char *table_name= table->table_name; int err; @@ -5298,19 +5268,20 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request)) - goto err; - if (!target_mdl_request) + table->mdl_request.init(0, db, table_name, MDL_EXCLUSIVE); + if (thd->mdl_context.try_acquire_exclusive_lock(&table->mdl_request)) + DBUG_RETURN(TRUE); + + if (table->mdl_request.ticket == NULL) goto table_exists; + + DEBUG_SYNC(thd, "locked_table_name"); + has_mdl_lock= TRUE; + dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1, db, table_name, reg_ext, 0); if (!access(dst_path, F_OK)) goto table_exists; - /* - Make the metadata lock available to open_table() called to - reopen the table down the road. - */ - table->mdl_request= target_mdl_request; } DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); @@ -5440,14 +5411,14 @@ binlog: char buf[2048]; String query(buf, sizeof(buf), system_charset_info); query.length(0); // Have to zero it since constructor doesn't - enum enum_open_table_action ot_action_unused; + Open_table_context ot_ctx_unused(thd); /* Here we open the destination table, on which we already have exclusive metadata lock. This is needed for store_create_info() to work. The table will be closed by close_thread_table() at the end of this branch. */ - if (open_table(thd, table, thd->mem_root, &ot_action_unused, + if (open_table(thd, table, thd->mem_root, &ot_ctx_unused, MYSQL_OPEN_REOPEN)) goto err; @@ -5481,11 +5452,9 @@ binlog: res= FALSE; err: - if (target_mdl_request) - { - thd->mdl_context.release_lock(target_mdl_request->ticket); - thd->mdl_context.remove_request(target_mdl_request); - } + if (has_mdl_lock) + thd->mdl_context.release_lock(table->mdl_request.ticket); + DBUG_RETURN(res); } @@ -5969,7 +5938,6 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, DBUG_RETURN(error); } - /** Prepare column and key definitions for CREATE TABLE in ALTER TABLE. @@ -6424,7 +6392,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { TABLE *table, *new_table= 0; MDL_ticket *mdl_ticket; - MDL_request *target_mdl_request= NULL; + MDL_request target_mdl_request; + bool has_target_mdl_lock= FALSE; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6647,15 +6616,21 @@ view_err: } else { - if (lock_table_name_if_not_cached(thd, new_db, new_name, - &target_mdl_request)) + target_mdl_request.init(0, new_db, new_name, MDL_EXCLUSIVE); + if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) DBUG_RETURN(TRUE); - if (!target_mdl_request) + if (target_mdl_request.ticket == NULL) { + /* Table exists and is locked by some thread. */ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); DBUG_RETURN(TRUE); } - + DEBUG_SYNC(thd, "locked_table_name"); + has_target_mdl_lock= TRUE; + /* + Table maybe does not exist, but we got an exclusive lock + on the name, now we can safely try to find out for sure. + */ build_table_filename(new_name_buff, sizeof(new_name_buff) - 1, new_db, new_name_buff, reg_ext, 0); if (!access(new_name_buff, F_OK)) @@ -6843,8 +6818,7 @@ view_err: */ if (new_name != table_name || new_db != db) { - thd->mdl_context.release_lock(target_mdl_request->ticket); - thd->mdl_context.remove_request(target_mdl_request); + thd->mdl_context.release_lock(target_mdl_request.ticket); thd->mdl_context.release_all_locks_for_name(mdl_ticket); } else @@ -7081,7 +7055,6 @@ view_err: #ifdef WITH_PARTITION_STORAGE_ENGINE if (fast_alter_partition) { - DBUG_ASSERT(!target_mdl_request); DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, db, table_name, @@ -7161,13 +7134,13 @@ view_err: { if (table->s->tmp_table) { - enum_open_table_action not_used; + Open_table_context ot_ctx_unused(thd); TABLE_LIST tbl; bzero((void*) &tbl, sizeof(tbl)); tbl.db= new_db; tbl.table_name= tbl.alias= tmp_name; /* Table is in thd->temporary_tables */ - (void) open_table(thd, &tbl, thd->mem_root, ¬_used, + (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx_unused, MYSQL_LOCK_IGNORE_FLUSH); new_table= tbl.table; } @@ -7439,7 +7412,7 @@ view_err: To do this we need to obtain a handler object for it. NO need to tamper with MERGE tables. The real open is done later. */ - enum enum_open_table_action ot_action_unused; + Open_table_context ot_ctx_unused(thd); TABLE *t_table; if (new_name != table_name || new_db != db) { @@ -7448,7 +7421,7 @@ view_err: table_list->table_name_length= strlen(new_name); table_list->db= new_db; table_list->db_length= strlen(new_db); - table_list->mdl_request= target_mdl_request; + table_list->mdl_request.ticket= target_mdl_request.ticket; } else { @@ -7457,10 +7430,10 @@ view_err: points to a different instance than the one set initially to request the lock. */ - table_list->mdl_request->ticket= mdl_ticket; + table_list->mdl_request.ticket= mdl_ticket; } if (open_table(thd, table_list, thd->mem_root, - &ot_action_unused, MYSQL_OPEN_REOPEN)) + &ot_ctx_unused, MYSQL_OPEN_REOPEN)) { goto err_with_mdl; } @@ -7523,8 +7496,7 @@ view_err: { if ((new_name != table_name || new_db != db)) { - thd->mdl_context.release_lock(target_mdl_request->ticket); - thd->mdl_context.remove_request(target_mdl_request); + thd->mdl_context.release_lock(target_mdl_request.ticket); thd->mdl_context.release_all_locks_for_name(mdl_ticket); } else @@ -7583,11 +7555,9 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (target_mdl_request) - { - thd->mdl_context.release_lock(target_mdl_request->ticket); - thd->mdl_context.remove_request(target_mdl_request); - } + if (has_target_mdl_lock) + thd->mdl_context.release_lock(target_mdl_request.ticket); + DBUG_RETURN(TRUE); err_with_mdl: @@ -7598,11 +7568,9 @@ err_with_mdl: tables and release the exclusive metadata lock. */ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); - if (target_mdl_request) - { - thd->mdl_context.release_lock(target_mdl_request->ticket); - thd->mdl_context.remove_request(target_mdl_request); - } + if (has_target_mdl_lock) + thd->mdl_context.release_lock(target_mdl_request.ticket); + thd->mdl_context.release_all_locks_for_name(mdl_ticket); DBUG_RETURN(TRUE); } @@ -7851,6 +7819,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) uninitialized data. open_tables() could fail. */ table_list->table= NULL; + /* Same applies to MDL ticket. */ + table_list->mdl_request.ticket= NULL; bzero((char*) &create_info, sizeof(create_info)); create_info.row_type=ROW_TYPE_NOT_USED; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 7e557c3ce68..d27473c1959 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -138,11 +138,7 @@ void udf_init() lex_start(new_thd); new_thd->set_db(db, sizeof(db)-1); - bzero((uchar*) &tables,sizeof(tables)); - tables.alias= tables.table_name= (char*) "func"; - tables.lock_type = TL_READ; - tables.db= db; - alloc_mdl_requests(&tables, new_thd->mem_root); + tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ); if (simple_open_n_lock_tables(new_thd, &tables)) { @@ -483,10 +479,7 @@ int mysql_create_function(THD *thd,udf_func *udf) /* create entry in mysql.func table */ - bzero((char*) &tables,sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "func"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); /* Allow creation of functions even if we can't open func table */ if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; @@ -562,10 +555,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle); - bzero((char*) &tables,sizeof(tables)); - tables.db=(char*) "mysql"; - tables.table_name= tables.alias= (char*) "func"; - alloc_mdl_requests(&tables, thd->mem_root); + tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); + if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); @@ -579,15 +570,16 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if ((error = table->file->ha_delete_row(table->record[0]))) table->file->print_error(error, MYF(0)); } - close_thread_tables(thd); - rw_unlock(&THR_LOCK_udf); - /* Binlog the drop function. */ + /* + Binlog the drop function. Keep the table open and locked + while binlogging, to avoid binlog inconsistency. + */ write_bin_log(thd, TRUE, thd->query(), thd->query_length()); DBUG_RETURN(0); - err: +err: rw_unlock(&THR_LOCK_udf); DBUG_RETURN(1); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7e033bc963a..b81fb30ec27 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -226,7 +226,7 @@ int mysql_update(THD *thd, break; if (!need_reopen) DBUG_RETURN(1); - close_tables_for_reopen(thd, &table_list, FALSE); + close_tables_for_reopen(thd, &table_list); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -1149,7 +1149,7 @@ reopen_tables: */ cleanup_items(thd->free_list); - close_tables_for_reopen(thd, &table_list, FALSE); + close_tables_for_reopen(thd, &table_list); goto reopen_tables; } diff --git a/sql/table.cc b/sql/table.cc index 8d28514e912..7fb9bbbd955 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4583,6 +4583,14 @@ void TABLE_LIST::reinit_before_use(THD *thd) } while (parent_embedding && parent_embedding->nested_join->join_list.head() == embedded); + + mdl_request.ticket= NULL; + /* + Not strictly necessary, but we manipulate with the type in open_table(), + so it's "safe" to reset the lock request type to the parser default, to + restore things back to first-execution state. + */ + mdl_request.set_type(MDL_SHARED); } /* @@ -4811,11 +4819,11 @@ size_t max_row_length(TABLE *table, const uchar *data) objects for all elements of table list. */ -void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root) +void init_mdl_requests(TABLE_LIST *table_list) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_request= - MDL_request::create(0, table_list->db, table_list->table_name, root); + table_list->mdl_request.init(0, table_list->db, table_list->table_name, + MDL_SHARED); } diff --git a/sql/table.h b/sql/table.h index 1762ae8785d..82498428a11 100644 --- a/sql/table.h +++ b/sql/table.h @@ -18,6 +18,7 @@ #include "sql_plist.h" +#include "mdl.h" /* Structs that defines the TABLE */ @@ -30,8 +31,6 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; -class MDL_request; -class MDL_ticket; /*************************************************************************/ @@ -1126,6 +1125,7 @@ struct TABLE_LIST table_name_length= table_name_length_arg; alias= (char*) alias_arg; lock_type= lock_type_arg; + mdl_request.init(0, db, table_name, MDL_SHARED); } /* @@ -1429,7 +1429,7 @@ struct TABLE_LIST uint table_open_method; enum enum_schema_table_state schema_table_state; - MDL_request *mdl_request; + MDL_request mdl_request; void calc_md5(char *buffer); void set_underlying_merge(); @@ -1798,6 +1798,6 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, size_t max_row_length(TABLE *table, const uchar *data); -void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root); +void init_mdl_requests(TABLE_LIST *table_list); #endif /* TABLE_INCLUDED */ diff --git a/sql/tztime.cc b/sql/tztime.cc index 9c49c286662..c17b37e27fb 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1637,6 +1637,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) tz_init_table_list(tz_tables+1); tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1]; tz_tables[1].prev_global= &tz_tables[0].next_global; + init_mdl_requests(tz_tables); /* We need to open only mysql.time_zone_leap_second, but we try to @@ -2296,6 +2297,7 @@ my_tz_find(THD *thd, const String *name) Open_tables_state open_tables_state_backup; tz_init_table_list(tz_tables); + init_mdl_requests(tz_tables); if (!open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup)) { diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 21a7e2c43d3..e68971975bc 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -388,7 +388,6 @@ int ha_myisammrg::add_children_list(void) { TABLE_LIST *parent_l= this->table->pos_in_table_list; TABLE_LIST *child_l; - THD *thd= current_thd; DBUG_ENTER("ha_myisammrg::add_children_list"); DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", this->table->s->db.str, this->table->s->table_name.str, (long) this->table)); @@ -434,15 +433,12 @@ int ha_myisammrg::add_children_list(void) /* Copy select_lex. Used in unique_table() at least. */ child_l->select_lex= parent_l->select_lex; - child_l->mdl_request= NULL; /* Safety, if alloc_mdl_requests fails. */ - /* Break when this was the last child. */ if (&child_l->next_global == this->children_last_l) break; } - alloc_mdl_requests(children_l, thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + init_mdl_requests(children_l); /* Insert children into the table list. */ if (parent_l->next_global) @@ -819,6 +815,8 @@ int ha_myisammrg::detach_children(void) Clear the table reference. */ child_l->table= NULL; + /* Similarly, clear the ticket reference. */ + child_l->mdl_request.ticket= NULL; /* Break when this was the last child. */ if (&child_l->next_global == this->children_last_l) From 92ba2f2a4f8fed3d36021face47c51937d10c872 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 14:14:05 +0100 Subject: [PATCH 115/466] Backport of revno: 2617.56.21 Bug #45066 FLUSH TABLES WITH READ LOCK deadlocks against LOCK TABLE Test coverage for combinations of LOCK TABLE READ / WRITE and FLUSH TABLES / FLUSH TABLES WITH READ LOCK added to lock.test. LOCK and FLUSH are executed sequentially from one connection. --- mysql-test/r/lock.result | 22 ++++++++++++++++++++++ mysql-test/t/lock.test | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 46ce618b99b..afb444f8ae9 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -319,5 +319,27 @@ alter table t1 add column j int; unlock tables; drop table t1; # +# Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against +# LOCK TABLE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT); +LOCK TABLE t1 READ; +FLUSH TABLES; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +LOCK TABLE t1 WRITE; +FLUSH TABLES; +# +# If you allow the next combination, you reintroduce bug Bug#45066 +# +LOCK TABLE t1 READ; +FLUSH TABLES WITH READ LOCK; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +LOCK TABLE t1 WRITE; +FLUSH TABLES WITH READ LOCK; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +UNLOCK TABLES; +DROP TABLE t1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index eaba2693904..4d610559077 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -385,6 +385,38 @@ alter table t1 add column j int; unlock tables; drop table t1; +--echo # +--echo # Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against +--echo # LOCK TABLE +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT); + +LOCK TABLE t1 READ; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +FLUSH TABLES; + +LOCK TABLE t1 WRITE; +FLUSH TABLES; + +--echo # +--echo # If you allow the next combination, you reintroduce bug Bug#45066 +--echo # +LOCK TABLE t1 READ; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +FLUSH TABLES WITH READ LOCK; + +LOCK TABLE t1 WRITE; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +FLUSH TABLES WITH READ LOCK; + +UNLOCK TABLES; +DROP TABLE t1; + --echo # --echo # End of 6.0 tests. --echo # From 4f48b423c251de10fd566a83768571c8efba46e1 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 14:22:26 +0100 Subject: [PATCH 116/466] Backport of revno: 2617.62.1 Bug #39675 rename tables on innodb tables with pending transactions causes slave data issue Bug was already fixed as part of patch for Bug#989 (If DROP TABLE while there's an active transaction, wrong binlog order) Test case added to rpl_innodb.test. --- mysql-test/suite/rpl/r/rpl_innodb.result | 45 ++++++++++++++++ mysql-test/suite/rpl/t/rpl_innodb.test | 67 +++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/rpl/r/rpl_innodb.result b/mysql-test/suite/rpl/r/rpl_innodb.result index bf6c3cb8c86..0d83f29d0fb 100644 --- a/mysql-test/suite/rpl/r/rpl_innodb.result +++ b/mysql-test/suite/rpl/r/rpl_innodb.result @@ -82,3 +82,48 @@ FLUSH LOGS; FLUSH LOGS; DROP DATABASE mysqltest1; End of 5.1 tests +# +# Bug#39675 rename tables on innodb tables with pending +# transactions causes slave data issue. +# +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +CREATE TABLE t1 ( +id INT PRIMARY KEY auto_increment, +b INT DEFAULT NULL +) ENGINE=InnoDB; +CREATE TABLE t2 ( +id INT PRIMARY KEY auto_increment, +b INT DEFAULT NULL +) ENGINE=InnoDB; +INSERT INTO t1 (b) VALUES (1),(2),(3); +BEGIN; +INSERT INTO t1(b) VALUES (4); +-------- switch to master1 -------- +RENAME TABLE t1 TO t3, t2 TO t1;; +-------- switch to master -------- +COMMIT; +-------- switch to master1 -------- +-------- switch to master -------- +SELECT * FROM t1; +id b +SELECT * FROM t3; +id b +1 1 +2 2 +3 3 +4 4 +-------- switch to slave -------- +SELECT * FROM t1; +id b +SELECT * FROM t3; +id b +1 1 +2 2 +3 3 +4 4 +-------- switch to master -------- +DROP TABLE t1; +DROP TABLE t3; +End of 6.0 tests diff --git a/mysql-test/suite/rpl/t/rpl_innodb.test b/mysql-test/suite/rpl/t/rpl_innodb.test index 64a85d27c88..7ee65027abc 100644 --- a/mysql-test/suite/rpl/t/rpl_innodb.test +++ b/mysql-test/suite/rpl/t/rpl_innodb.test @@ -120,6 +120,71 @@ connection master; FLUSH LOGS; DROP DATABASE mysqltest1; --- source include/master-slave-end.inc --echo End of 5.1 tests + +--echo # +--echo # Bug#39675 rename tables on innodb tables with pending +--echo # transactions causes slave data issue. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +--enable_warnings + +CREATE TABLE t1 ( + id INT PRIMARY KEY auto_increment, + b INT DEFAULT NULL +) ENGINE=InnoDB; + +CREATE TABLE t2 ( + id INT PRIMARY KEY auto_increment, + b INT DEFAULT NULL +) ENGINE=InnoDB; + +INSERT INTO t1 (b) VALUES (1),(2),(3); + +BEGIN; +INSERT INTO t1(b) VALUES (4); + +--echo -------- switch to master1 -------- +connection master1; +--send RENAME TABLE t1 TO t3, t2 TO t1; + +--echo -------- switch to master -------- +connection master; +# Need to wait until RENAME is received +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE info = "RENAME TABLE t1 TO t3, t2 TO t1" and + state = "Waiting for table"; +--source include/wait_condition.inc + +COMMIT; + +--echo -------- switch to master1 -------- +connection master1; +--reap + +--echo -------- switch to master -------- +connection master; +SELECT * FROM t1; +SELECT * FROM t3; + +sync_slave_with_master; + +--echo -------- switch to slave -------- +connection slave; +SELECT * FROM t1; +SELECT * FROM t3; + +--echo -------- switch to master -------- +connection master; +DROP TABLE t1; +DROP TABLE t3; + +--echo End of 6.0 tests + +--source include/master-slave-end.inc From 502b4ea47f2d1bdc96c34ce77ce6d25d3df9e1dc Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 14:27:33 +0100 Subject: [PATCH 117/466] Backport of revno: 2617.65.1 Bug #22876 Four-way deadlock This bug was fixed as a part of Bug#989 "If DROP TABLE while there's an active transaction, wrong binlog order" A statement which would have caused circular wait will now be aborted with ER_LOCK_DEADLOCK. Test case based on bug description added to innodb_mysql_lock.test. Note that innodb_lock_wait_timeout is set to 5 mins to prevent race conditions in the test. --- mysql-test/r/innodb_mysql_lock.result | 24 ++++++++++ mysql-test/t/innodb_mysql_lock-master.opt | 1 + mysql-test/t/innodb_mysql_lock.test | 58 +++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 mysql-test/r/innodb_mysql_lock.result create mode 100644 mysql-test/t/innodb_mysql_lock-master.opt create mode 100644 mysql-test/t/innodb_mysql_lock.test diff --git a/mysql-test/r/innodb_mysql_lock.result b/mysql-test/r/innodb_mysql_lock.result new file mode 100644 index 00000000000..147267d5550 --- /dev/null +++ b/mysql-test/r/innodb_mysql_lock.result @@ -0,0 +1,24 @@ +# +# Bug #22876 Four-way deadlock +# +DROP TABLE IF EXISTS t1; +# Connection 1 +set @@autocommit=0; +CREATE TABLE t1(s1 INT UNIQUE) ENGINE=innodb; +INSERT INTO t1 VALUES (1); +# Connection 2 +set @@autocommit=0; +INSERT INTO t1 VALUES (2); +INSERT INTO t1 VALUES (1); +# Connection 3 +set @@autocommit=0; +DROP TABLE t1; +# Connection 1 +# Connection 1 is now holding the lock. +# Issuing insert from connection 1 while connection 2&3 +# is waiting for the lock should give a deadlock error. +INSERT INTO t1 VALUES (2); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Cleanup +commit; +commit; diff --git a/mysql-test/t/innodb_mysql_lock-master.opt b/mysql-test/t/innodb_mysql_lock-master.opt new file mode 100644 index 00000000000..0041949b829 --- /dev/null +++ b/mysql-test/t/innodb_mysql_lock-master.opt @@ -0,0 +1 @@ +--innodb_lock_wait_timeout=300 diff --git a/mysql-test/t/innodb_mysql_lock.test b/mysql-test/t/innodb_mysql_lock.test new file mode 100644 index 00000000000..daee94bedb5 --- /dev/null +++ b/mysql-test/t/innodb_mysql_lock.test @@ -0,0 +1,58 @@ +-- source include/have_innodb.inc + +--echo # +--echo # Bug #22876 Four-way deadlock +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +connect (con3,localhost,root,,); + +--echo # Connection 1 +connection con1; +set @@autocommit=0; +CREATE TABLE t1(s1 INT UNIQUE) ENGINE=innodb; +INSERT INTO t1 VALUES (1); + +--echo # Connection 2 +connection con2; +set @@autocommit=0; +INSERT INTO t1 VALUES (2); +--send INSERT INTO t1 VALUES (1) + +--echo # Connection 3 +connection con3; +set @@autocommit=0; +--send DROP TABLE t1 + +--echo # Connection 1 +connection con1; +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE info = "INSERT INTO t1 VALUES (1)" and + state = "update"; +--source include/wait_condition.inc +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE info = "DROP TABLE t1" and + state = "Waiting for table"; +--source include/wait_condition.inc +--echo # Connection 1 is now holding the lock. +--echo # Issuing insert from connection 1 while connection 2&3 +--echo # is waiting for the lock should give a deadlock error. +--error ER_LOCK_DEADLOCK +INSERT INTO t1 VALUES (2); + +--echo # Cleanup +connection con2; +--reap +commit; +connection con1; +commit; +connection con3; +--reap +connection default; From 22630531cbea0bf85217ecba4e2670b7e3e21bbb Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 16:57:25 +0300 Subject: [PATCH 118/466] Backport of revid 2617.69.21, 2617.69.22, 2617.29.23: ---------------------------------------------------------- revno: 2617.69.21 committer: Konstantin Osipov branch nick: 5.4-4284-1-assert timestamp: Thu 2009-08-13 20:13:55 +0400 message: A fix and a test case for Bug#46610 "MySQL 5.4.4: MyISAM MRG engine crash on auto-repair of child". Also fixes Bug#42862 "Crash on failed attempt to open a children of a merge table". MERGE engine needs to extend the global table list with TABLE_LIST elements for child tables, so that they are opened and locked. Previously these table list elements were allocated in memory of ha_myisammrg object (MERGE engine handler). That would lead to access to freed memory in recover_from_failed_open_table_attempt(), which would try to recover a MERGE table child (MyISAM table) and use for that TABLE_LIST of that child. But by the time recover_from_failed_open_table_attempt() is invoked, ha_myisammrg object that owns this TABLE_LIST may be destroyed, and thus TABLE_LIST memory freed. The fix is to ensure that TABLE_LIST elements that are added to the global table list (lex->query_tables) are always allocated in thd->mem_root, which is not destroyed until end of execution. If previously TABLE_LIST elements were allocated at ha_myisammrg::open() (i.e. when the TABLE object was created and added to the table cache), now they are allocated in ha_myisammrg::add_chidlren_list() (i.e. right after "open" of the merge parent in open_tables()). We still create a list of children names at ha_myisammrg::open() to use as a basis for creation of TABLE_LISTs, that allows to avoid reading the merge handler data file on every execution. --- mysql-test/r/merge_recover.result | 103 ++++++++ mysql-test/t/merge_recover-master.opt | 1 + mysql-test/t/merge_recover.test | 113 +++++++++ sql/table.h | 30 +-- storage/myisammrg/ha_myisammrg.cc | 326 +++++++++++++++----------- storage/myisammrg/ha_myisammrg.h | 50 +++- 6 files changed, 459 insertions(+), 164 deletions(-) create mode 100644 mysql-test/r/merge_recover.result create mode 100644 mysql-test/t/merge_recover-master.opt create mode 100644 mysql-test/t/merge_recover.test diff --git a/mysql-test/r/merge_recover.result b/mysql-test/r/merge_recover.result new file mode 100644 index 00000000000..871c12ca4c0 --- /dev/null +++ b/mysql-test/r/merge_recover.result @@ -0,0 +1,103 @@ +# +# Test of MyISAM MRG tables with corrupted children. +# Run with --myisam-recover=force option. +# +# Preparation: we need to make sure that the merge parent +# is never left in the table cache when closed, since this may +# have effect on merge children. +# For that, we set the table cache to minimal size and populate it +# in a concurrent connection. +# +# Switching to connection con1 +# +# +# Minimal values. +# +call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); +call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*root Checking table"); +call mtr.add_suppression(" '\..test.t1'"); +set global table_open_cache=256; +set global table_definition_cache=400; +drop procedure if exists p_create; +create procedure p_create() +begin +declare i int default 1; +set @lock_table_stmt="lock table "; +set @drop_table_stmt="drop table "; +while i < @@global.table_definition_cache + 1 do +set @table_name=concat("t_", i); +set @opt_comma=if(i=1, "", ", "); +set @lock_table_stmt=concat(@lock_table_stmt, @opt_comma, +@table_name, " read"); +set @drop_table_stmt=concat(@drop_table_stmt, @opt_comma, @table_name); +set @create_table_stmt=concat("create table if not exists ", +@table_name, " (a int)"); +prepare stmt from @create_table_stmt; +execute stmt; +deallocate prepare stmt; +set i= i+1; +end while; +end| +call p_create(); +drop procedure p_create; +# +# Switching to connection 'default' +# +# +# We have to disable the ps-protocol, to avoid +# "Prepared statement needs to be re-prepared" errors +# -- table def versions change all the time with full table cache. +# +drop table if exists t1, t1_mrg, t1_copy; +# +# Prepare a MERGE engine table, that refers to a corrupted +# child. +# +create table t1 (a int, key(a)) engine=myisam; +create table t1_mrg (a int) union (t1) engine=merge; +# +# Create a table with a corrupted index file: +# save an old index file, insert more rows, +# overwrite the new index file with the old one. +# +insert into t1 (a) values (1), (2), (3); +flush table t1; +insert into t1 (a) values (4), (5), (6); +flush table t1; +# check table is needed to mark the table as crashed. +check table t1; +Table Op Msg_type Msg_text +test.t1 check warning Size of datafile is: 42 Should be: 21 +test.t1 check error Record-count is not ok; is 6 Should be: 3 +test.t1 check warning Found 6 key parts. Should be: 3 +test.t1 check error Corrupt +# +# At this point we have a merge table t1_mrg pointing to t1, +# and t1 is corrupted, and will be auto-repaired at open. +# Check that this doesn't lead to memory corruption. +# +select * from t1_mrg; +a +1 +2 +3 +4 +5 +6 +Warnings: +Error 145 Table 't1' is marked as crashed and should be repaired +Error 1194 Table 't1' is marked as crashed and should be repaired +Error 1034 Number of rows changed from 3 to 6 +# +# Cleanup +# +drop table t1, t1_mrg; +# +# Switching to connection con1 +# +unlock tables; +prepare stmt from @drop_table_stmt; +execute stmt; +deallocate prepare stmt; +set @@global.table_definition_cache=default; +set @@global.table_open_cache=default; diff --git a/mysql-test/t/merge_recover-master.opt b/mysql-test/t/merge_recover-master.opt new file mode 100644 index 00000000000..875a25ad513 --- /dev/null +++ b/mysql-test/t/merge_recover-master.opt @@ -0,0 +1 @@ +--myisam-recover=force diff --git a/mysql-test/t/merge_recover.test b/mysql-test/t/merge_recover.test new file mode 100644 index 00000000000..f2cb204eeb6 --- /dev/null +++ b/mysql-test/t/merge_recover.test @@ -0,0 +1,113 @@ +--echo # +--echo # Test of MyISAM MRG tables with corrupted children. +--echo # Run with --myisam-recover=force option. +--echo # +--echo # Preparation: we need to make sure that the merge parent +--echo # is never left in the table cache when closed, since this may +--echo # have effect on merge children. +--echo # For that, we set the table cache to minimal size and populate it +--echo # in a concurrent connection. +connect(con1,localhost,root,,test,,); +--echo # +--echo # Switching to connection con1 +--echo # +connection con1; +--echo # +--echo # Minimal values. +--echo # + +call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); +call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*root Checking table"); +call mtr.add_suppression(" '\..test.t1'"); + +set global table_open_cache=256; +set global table_definition_cache=400; +--disable_warnings +drop procedure if exists p_create; +--enable_warnings +delimiter |; +create procedure p_create() +begin + declare i int default 1; + set @lock_table_stmt="lock table "; + set @drop_table_stmt="drop table "; + while i < @@global.table_definition_cache + 1 do + set @table_name=concat("t_", i); + set @opt_comma=if(i=1, "", ", "); + set @lock_table_stmt=concat(@lock_table_stmt, @opt_comma, + @table_name, " read"); + set @drop_table_stmt=concat(@drop_table_stmt, @opt_comma, @table_name); + set @create_table_stmt=concat("create table if not exists ", + @table_name, " (a int)"); + prepare stmt from @create_table_stmt; + execute stmt; + deallocate prepare stmt; + set i= i+1; + end while; +end| +delimiter ;| +call p_create(); +drop procedure p_create; +--disable_query_log +let $lock=`select @lock_table_stmt`; +eval $lock; +--enable_query_log +--echo # +--echo # Switching to connection 'default' +--echo # +connection default; +--echo # +--echo # We have to disable the ps-protocol, to avoid +--echo # "Prepared statement needs to be re-prepared" errors +--echo # -- table def versions change all the time with full table cache. +--echo # +--disable_ps_protocol +--disable_warnings +drop table if exists t1, t1_mrg, t1_copy; +--enable_warnings +let $MYSQLD_DATADIR=`select @@datadir`; +--echo # +--echo # Prepare a MERGE engine table, that refers to a corrupted +--echo # child. +--echo # +create table t1 (a int, key(a)) engine=myisam; +create table t1_mrg (a int) union (t1) engine=merge; +--echo # +--echo # Create a table with a corrupted index file: +--echo # save an old index file, insert more rows, +--echo # overwrite the new index file with the old one. +--echo # +insert into t1 (a) values (1), (2), (3); +flush table t1; +--copy_file $MYSQLD_DATADIR/test/t1.MYI $MYSQLD_DATADIR/test/t1_copy.MYI +insert into t1 (a) values (4), (5), (6); +flush table t1; +--remove_file $MYSQLD_DATADIR/test/t1.MYI +--copy_file $MYSQLD_DATADIR/test/t1_copy.MYI $MYSQLD_DATADIR/test/t1.MYI +--remove_file $MYSQLD_DATADIR/test/t1_copy.MYI +--echo # check table is needed to mark the table as crashed. +check table t1; +--echo # +--echo # At this point we have a merge table t1_mrg pointing to t1, +--echo # and t1 is corrupted, and will be auto-repaired at open. +--echo # Check that this doesn't lead to memory corruption. +--echo # +--replace_regex /'.*[\/\\]/'/ +select * from t1_mrg; +--echo # +--echo # Cleanup +--echo # +drop table t1, t1_mrg; +--echo # +--echo # Switching to connection con1 +--echo # +connection con1; +unlock tables; +prepare stmt from @drop_table_stmt; +execute stmt; +deallocate prepare stmt; +set @@global.table_definition_cache=default; +set @@global.table_open_cache=default; +disconnect con1; +connection default; +--enable_ps_protocol diff --git a/sql/table.h b/sql/table.h index 82498428a11..21e7bd3142c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1486,20 +1486,6 @@ struct TABLE_LIST */ bool process_index_hints(TABLE *table); - /* Access MERGE child def version. See top comment in ha_myisammrg.cc */ - inline ulong get_child_def_version() - { - return child_def_version; - } - inline void set_child_def_version(ulong version) - { - child_def_version= version; - } - inline void init_child_def_version() - { - child_def_version= ~0UL; - } - /** Compare the version of metadata from the previous execution (if any) with values obtained from the current table @@ -1522,9 +1508,14 @@ struct TABLE_LIST */ inline void set_table_ref_id(TABLE_SHARE *s) + { set_table_ref_id(s->get_table_ref_type(), s->get_table_ref_version()); } + + inline + void set_table_ref_id(enum_table_ref_type table_ref_type_arg, + ulong table_ref_version_arg) { - m_table_ref_type= s->get_table_ref_type(); - m_table_ref_version= s->get_table_ref_version(); + m_table_ref_type= table_ref_type_arg; + m_table_ref_version= table_ref_version_arg; } /** @@ -1550,13 +1541,6 @@ struct TABLE_LIST private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); - /* - Cleanup for re-execution in a prepared statement or a stored - procedure. - */ - - /* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */ - ulong child_def_version; /** See comments for set_metadata_id() */ enum enum_table_ref_type m_table_ref_type; /** See comments for set_metadata_id() */ diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index e68971975bc..760639e2c6b 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -114,8 +114,8 @@ static handler *myisammrg_create_handler(handlerton *hton, ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) :handler(hton, table_arg), file(0), is_cloned(0) { - init_sql_alloc(&children_mem_root, max(4 * sizeof(TABLE_LIST), FN_REFLEN) + - ALLOC_ROOT_MIN_BLOCK_SIZE, 0); + init_sql_alloc(&children_mem_root, + FN_REFLEN + ALLOC_ROOT_MIN_BLOCK_SIZE, 0); } @@ -220,10 +220,11 @@ static int myisammrg_parent_open_callback(void *callback_param, const char *filename) { ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param; - TABLE_LIST *child_l; - const char *db; - const char *table_name; - size_t dirlen; + Mrg_child_def *mrg_child_def; + char *db; + char *table_name; + uint dirlen; + uint table_name_length; char dir_path[FN_REFLEN]; DBUG_ENTER("myisammrg_parent_open_callback"); @@ -237,7 +238,7 @@ static int myisammrg_parent_open_callback(void *callback_param, DBUG_RETURN(1); /* purecov: end */ } - table_name= filename + dirlen; + table_name= (char*) filename + dirlen; dirlen--; /* Strip off trailing '/'. */ memcpy(dir_path, filename, dirlen); dir_path[dirlen]= '\0'; @@ -245,49 +246,33 @@ static int myisammrg_parent_open_callback(void *callback_param, dirlen-= db - dir_path; /* This is now the length of 'db'. */ DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name)); - /* Get a TABLE_LIST object. */ - if (!(child_l= (TABLE_LIST*) alloc_root(&ha_myrg->children_mem_root, - sizeof(TABLE_LIST)))) - { - /* purecov: begin inspected */ - DBUG_PRINT("error", ("my_malloc error: %d", my_errno)); - DBUG_RETURN(1); - /* purecov: end */ - } - bzero((char*) child_l, sizeof(TABLE_LIST)); - /* Set database (schema) name. */ - child_l->db_length= dirlen; - child_l->db= strmake_root(&ha_myrg->children_mem_root, db, dirlen); + db= strmake_root(&ha_myrg->children_mem_root, db, dirlen); /* Set table name. */ - child_l->table_name_length= strlen(table_name); - child_l->table_name= strmake_root(&ha_myrg->children_mem_root, table_name, - child_l->table_name_length); + table_name_length= strlen(table_name); + table_name= strmake_root(&ha_myrg->children_mem_root, table_name, + table_name_length); + + if (! db || ! table_name) + DBUG_RETURN(1); + /* Convert to lowercase if required. */ - if (lower_case_table_names && child_l->table_name_length) + if (lower_case_table_names && table_name_length) { /* purecov: begin tested */ - child_l->table_name_length= my_casedn_str(files_charset_info, - child_l->table_name); + table_name_length= my_casedn_str(files_charset_info, table_name); /* purecov: end */ } - /* Set alias. */ - child_l->alias= child_l->table_name; - /* Initialize table map to 'undefined'. */ - child_l->init_child_def_version(); + mrg_child_def= new (&ha_myrg->children_mem_root) + Mrg_child_def(db, dirlen, table_name, table_name_length); - /* Link TABLE_LIST object into the children list. */ - if (ha_myrg->children_last_l) - child_l->prev_global= ha_myrg->children_last_l; - else + if (! mrg_child_def || + ha_myrg->child_def_list.push_back(mrg_child_def, + &ha_myrg->children_mem_root)) { - /* Initialize ha_myrg->children_last_l when handling first child. */ - ha_myrg->children_last_l= &ha_myrg->children_l; + DBUG_RETURN(1); } - *ha_myrg->children_last_l= child_l; - ha_myrg->children_last_l= &child_l->next_global; - DBUG_RETURN(0); } @@ -297,7 +282,7 @@ static int myisammrg_parent_open_callback(void *callback_param, @param[in] name MERGE table path name @param[in] mode read/write mode, unused - @param[in] test_if_locked open flags + @param[in] test_if_locked_arg open flags @return status @retval 0 OK @@ -309,17 +294,17 @@ static int myisammrg_parent_open_callback(void *callback_param, */ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), - uint test_if_locked) + uint test_if_locked_arg) { DBUG_ENTER("ha_myisammrg::open"); DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table)); - DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked)); + DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked_arg)); /* Must not be used when table is open. */ DBUG_ASSERT(!this->file); /* Save for later use. */ - this->test_if_locked= test_if_locked; + test_if_locked= test_if_locked_arg; /* In case this handler was open and closed before, free old data. */ free_root(&this->children_mem_root, MYF(MY_MARK_BLOCKS_FREE)); @@ -334,6 +319,7 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), */ children_l= NULL; children_last_l= NULL; + child_def_list.empty(); my_errno= 0; /* retrieve children table list. */ @@ -379,7 +365,7 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), @detail When a MERGE parent table has just been opened, insert the - TABLE_LIST chain from the MERGE handle into the table list used for + TABLE_LIST chain from the MERGE handler into the table list used for opening tables for this statement. This lets the children be opened too. */ @@ -387,7 +373,9 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), int ha_myisammrg::add_children_list(void) { TABLE_LIST *parent_l= this->table->pos_in_table_list; - TABLE_LIST *child_l; + THD *thd= table->in_use; + List_iterator_fast it(child_def_list); + Mrg_child_def *mrg_child_def; DBUG_ENTER("ha_myisammrg::add_children_list"); DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", this->table->s->db.str, this->table->s->table_name.str, (long) this->table)); @@ -406,7 +394,7 @@ int ha_myisammrg::add_children_list(void) DBUG_ASSERT(!this->file->children_attached); /* Must not call this with children list in place. */ - DBUG_ASSERT(parent_l->next_global != this->children_l); + DBUG_ASSERT(this->children_l == NULL); /* Prevent inclusion of another MERGE table, which could make infinite @@ -418,40 +406,103 @@ int ha_myisammrg::add_children_list(void) DBUG_RETURN(1); } - /* Fix children. */ - DBUG_ASSERT(this->children_l); - for (child_l= this->children_l; ; child_l= child_l->next_global) + while ((mrg_child_def= it++)) { - DBUG_ASSERT(!child_l->table); + TABLE_LIST *child_l; + char *db; + char *table_name; - /* Set lock type. */ - child_l->lock_type= parent_l->lock_type; + child_l= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)); + db= (char*) thd->memdup(mrg_child_def->db.str, mrg_child_def->db.length+1); + table_name= (char*) thd->memdup(mrg_child_def->name.str, + mrg_child_def->name.length+1); + if (child_l == NULL || db == NULL || table_name == NULL) + DBUG_RETURN(1); + + child_l->init_one_table(db, mrg_child_def->db.length, + table_name, mrg_child_def->name.length, + table_name, parent_l->lock_type); /* Set parent reference. Used to detect MERGE in children list. */ child_l->parent_l= parent_l; - /* Copy select_lex. Used in unique_table() at least. */ child_l->select_lex= parent_l->select_lex; - - /* Break when this was the last child. */ - if (&child_l->next_global == this->children_last_l) - break; + /* + Set the expected table version, to not cause spurious re-prepare. + @todo: revise after the fix for Bug#36171 + */ + child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(), + mrg_child_def->get_child_def_version()); + /* Link TABLE_LIST object into the children list. */ + if (this->children_last_l) + child_l->prev_global= this->children_last_l; + else + { + /* Initialize children_last_l when handling first child. */ + this->children_last_l= &this->children_l; + } + *this->children_last_l= child_l; + this->children_last_l= &child_l->next_global; } - init_mdl_requests(children_l); - /* Insert children into the table list. */ if (parent_l->next_global) parent_l->next_global->prev_global= this->children_last_l; *this->children_last_l= parent_l->next_global; parent_l->next_global= this->children_l; this->children_l->prev_global= &parent_l->next_global; + /* + We have to update LEX::query_tables_last if children are added to + the tail of the table list in order to be able correctly add more + elements to it (e.g. as part of prelocking process). + */ + if (thd->lex->query_tables_last == &parent_l->next_global) + thd->lex->query_tables_last= this->children_last_l; end: DBUG_RETURN(0); } +/** + A context of myrg_attach_children() callback. +*/ + +class Mrg_attach_children_callback_param +{ +public: + /** + 'need_compat_check' is set by myisammrg_attach_children_callback() + if a child fails the table def version check. + */ + bool need_compat_check; + /** TABLE_LIST identifying this merge parent. */ + TABLE_LIST *parent_l; + /** Iterator position, the current child to attach. */ + TABLE_LIST *next_child_attach; + List_iterator_fast def_it; + Mrg_child_def *mrg_child_def; +public: + Mrg_attach_children_callback_param(TABLE_LIST *parent_l_arg, + TABLE_LIST *first_child, + List &child_def_list) + :need_compat_check(FALSE), + parent_l(parent_l_arg), + next_child_attach(first_child), + def_it(child_def_list), + mrg_child_def(def_it++) + {} + void next() + { + next_child_attach= next_child_attach->next_global; + if (next_child_attach && next_child_attach->parent_l != parent_l) + next_child_attach= NULL; + if (mrg_child_def) + mrg_child_def= def_it++; + } +}; + + /** Callback function for attaching a MERGE child table. @@ -470,48 +521,38 @@ end: static MI_INFO *myisammrg_attach_children_callback(void *callback_param) { - ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param; - TABLE *parent= ha_myrg->table_ptr(); + Mrg_attach_children_callback_param *param= + (Mrg_attach_children_callback_param*) callback_param; + TABLE *parent= param->parent_l->table; TABLE *child; - TABLE_LIST *child_l; - MI_INFO *myisam; + TABLE_LIST *child_l= param->next_child_attach; + Mrg_child_def *mrg_child_def= param->mrg_child_def; + MI_INFO *myisam= NULL; DBUG_ENTER("myisammrg_attach_children_callback"); - my_errno= 0; - - /* Get child list item. */ - child_l= ha_myrg->next_child_attach; if (!child_l) { DBUG_PRINT("myrg", ("No more children to attach")); - DBUG_RETURN(NULL); + my_errno= 0; /* Ok, no more child tables. */ + goto end; } child= child_l->table; - /* - Prepare for next child. Used as child_l in next call to this function. - We cannot rely on a NULL-terminated chain. - */ - if (&child_l->next_global == ha_myrg->children_last_l) - { - DBUG_PRINT("myrg", ("attaching last child")); - ha_myrg->next_child_attach= NULL; - } - else - ha_myrg->next_child_attach= child_l->next_global; + /* Prepare for next child. */ + param->next(); /* Do a quick compatibility check. The table def version is set when the table share is created. The child def version is copied - from the table def version after a sucessful compatibility check. + from the table def version after a successful compatibility check. We need to repeat the compatibility check only if a child is opened from a different share than last time it was used with this MERGE table. */ DBUG_PRINT("myrg", ("table_def_version last: %lu current: %lu", - (ulong) child_l->get_child_def_version(), + (ulong) mrg_child_def->get_child_def_version(), (ulong) child->s->get_table_def_version())); - if (child_l->get_child_def_version() != child->s->get_table_def_version()) - ha_myrg->need_compat_check= TRUE; + if (mrg_child_def->get_child_def_version() != child->s->get_table_def_version()) + param->need_compat_check= TRUE; /* If parent is temporary, children must be temporary too and vice @@ -527,7 +568,7 @@ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d", parent->s->tmp_table, child->s->tmp_table)); my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; - goto err; + goto end; } /* Extract the MyISAM table structure pointer from the handler object. */ @@ -542,8 +583,8 @@ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d", (long) myisam, my_errno)); - err: - DBUG_RETURN(my_errno ? NULL : myisam); + end: + DBUG_RETURN(myisam); } @@ -625,7 +666,9 @@ int ha_myisammrg::attach_children(void) MI_KEYDEF *keyinfo; uint recs; uint keys= table->s->keys; + TABLE_LIST *parent_l= table->pos_in_table_list; int error; + Mrg_attach_children_callback_param param(parent_l, this->children_l, child_def_list); DBUG_ENTER("ha_myisammrg::attach_children"); DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str, table->s->table_name.str, (long) table)); @@ -652,25 +695,15 @@ int ha_myisammrg::attach_children(void) DBUG_ASSERT(this->table->pos_in_table_list->next_global == this->children_l); /* - Initialize variables that are used, modified, and/or set by - myisammrg_attach_children_callback(). - 'next_child_attach' traverses the chain of TABLE_LIST objects - that has been compiled during myrg_parent_open(). Every call - to myisammrg_attach_children_callback() moves the pointer to - the next object. - 'need_compat_check' is set by myisammrg_attach_children_callback() - if a child fails the table def version check. 'my_errno' is set by myisammrg_attach_children_callback() in case of an error. */ - next_child_attach= this->children_l; - need_compat_check= FALSE; my_errno= 0; if (myrg_attach_children(this->file, this->test_if_locked | current_thd->open_options, - myisammrg_attach_children_callback, this, - (my_bool *) &need_compat_check)) + myisammrg_attach_children_callback, ¶m, + (my_bool *) ¶m.need_compat_check)) { error= my_errno; goto err; @@ -690,8 +723,8 @@ int ha_myisammrg::attach_children(void) always happen at the first attach because the reference child def version is initialized to 'undefined' at open. */ - DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check)); - if (need_compat_check) + DBUG_PRINT("myrg", ("need_compat_check: %d", param.need_compat_check)); + if (param.need_compat_check) { TABLE_LIST *child_l; @@ -744,11 +777,13 @@ int ha_myisammrg::attach_children(void) if (error == HA_ERR_WRONG_MRG_TABLE_DEF) goto err; /* purecov: inspected */ - /* All checks passed so far. Now update child def version. */ + List_iterator_fast def_it(child_def_list); DBUG_ASSERT(this->children_l); for (child_l= this->children_l; ; child_l= child_l->next_global) { - child_l->set_child_def_version( + Mrg_child_def *mrg_child_def= def_it++; + mrg_child_def->set_child_def_version( + child_l->table->s->get_table_ref_type(), child_l->table->s->get_table_def_version()); if (&child_l->next_global == this->children_last_l) @@ -804,49 +839,64 @@ int ha_myisammrg::detach_children(void) goto end; } - /* Clear TABLE references. */ - DBUG_ASSERT(this->children_l); - for (child_l= this->children_l; ; child_l= child_l->next_global) + if (this->children_l) { + THD *thd= table->in_use; + + /* Clear TABLE references. */ + for (child_l= this->children_l; ; child_l= child_l->next_global) + { + /* + Do not DBUG_ASSERT(child_l->table); open_tables might be + incomplete. + + Clear the table reference. + */ + child_l->table= NULL; + /* Similarly, clear the ticket reference. */ + child_l->mdl_request.ticket= NULL; + + /* Break when this was the last child. */ + if (&child_l->next_global == this->children_last_l) + break; + } /* - Do not DBUG_ASSERT(child_l->table); open_tables might be - incomplete. + Remove children from the table list. This won't fail if called + twice. The list is terminated after removal. - Clear the table reference. + If the parent is LEX::query_tables_own_last and pre-locked tables + follow (tables used by stored functions or triggers), the children + are inserted behind the parent and before the pre-locked tables. But + we do not adjust LEX::query_tables_own_last. The pre-locked tables + could have chopped off the list by clearing + *LEX::query_tables_own_last. This did also chop off the children. If + we would copy the reference from *this->children_last_l in this + case, we would put the chopped off pre-locked tables back to the + list. So we refrain from copying it back, if the destination has + been set to NULL meanwhile. */ - child_l->table= NULL; - /* Similarly, clear the ticket reference. */ - child_l->mdl_request.ticket= NULL; + if (this->children_l->prev_global && *this->children_l->prev_global) + *this->children_l->prev_global= *this->children_last_l; + if (*this->children_last_l) + (*this->children_last_l)->prev_global= this->children_l->prev_global; - /* Break when this was the last child. */ - if (&child_l->next_global == this->children_last_l) - break; + /* + If table elements being removed are at the end of table list we + need to adjust LEX::query_tables_last member to point to the + new last element of the list. + */ + if (thd->lex->query_tables_last == this->children_last_l) + thd->lex->query_tables_last= this->children_l->prev_global; + + /* Terminate child list. So it cannot be tried to remove again. */ + *this->children_last_l= NULL; + this->children_l->prev_global= NULL; + + /* Forget about the children, we don't own their memory. */ + this->children_l= NULL; + this->children_last_l= NULL; } - /* - Remove children from the table list. This won't fail if called - twice. The list is terminated after removal. - - If the parent is LEX::query_tables_own_last and pre-locked tables - follow (tables used by stored functions or triggers), the children - are inserted behind the parent and before the pre-locked tables. But - we do not adjust LEX::query_tables_own_last. The pre-locked tables - could have chopped off the list by clearing - *LEX::query_tables_own_last. This did also chop off the children. If - we would copy the reference from *this->children_last_l in this - case, we would put the chopped off pre-locked tables back to the - list. So we refrain from copying it back, if the destination has - been set to NULL meanwhile. - */ - if (this->children_l->prev_global && *this->children_l->prev_global) - *this->children_l->prev_global= *this->children_last_l; - if (*this->children_last_l) - (*this->children_last_l)->prev_global= this->children_l->prev_global; - - /* Terminate child list. So it cannot be tried to remove again. */ - *this->children_last_l= NULL; - this->children_l->prev_global= NULL; - if (!this->file->children_attached) { DBUG_PRINT("myrg", ("merge children are already detached")); diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h index c3803eb584b..4ff24c69071 100644 --- a/storage/myisammrg/ha_myisammrg.h +++ b/storage/myisammrg/ha_myisammrg.h @@ -22,18 +22,62 @@ #include +/** + Represents one name of a MERGE child. + + @todo: Add MYRG_SHARE and store chlidren names in the + share. +*/ + +class Mrg_child_def: public Sql_alloc +{ + /* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */ + enum_table_ref_type m_child_table_ref_type; + ulong m_child_def_version; +public: + LEX_STRING db; + LEX_STRING name; + + /* Access MERGE child def version. See top comment in ha_myisammrg.cc */ + inline enum_table_ref_type get_child_table_ref_type() + { + return m_child_table_ref_type; + } + inline ulong get_child_def_version() + { + return m_child_def_version; + } + inline void set_child_def_version(enum_table_ref_type child_table_ref_type, + ulong version) + { + m_child_table_ref_type= child_table_ref_type; + m_child_def_version= version; + } + + Mrg_child_def(char *db_arg, size_t db_len_arg, + char *table_name_arg, size_t table_name_len_arg) + { + db.str= db_arg; + db.length= db_len_arg; + name.str= table_name_arg; + name.length= table_name_len_arg; + m_child_def_version= ~0UL; + m_child_table_ref_type= TABLE_REF_NULL; + } +}; + + class ha_myisammrg: public handler { MYRG_INFO *file; my_bool is_cloned; /* This instance has been cloned */ - public: +public: MEM_ROOT children_mem_root; /* mem root for children list */ + List child_def_list; TABLE_LIST *children_l; /* children list */ TABLE_LIST **children_last_l; /* children list end */ - TABLE_LIST *next_child_attach; /* next child to attach */ uint test_if_locked; /* flags from ::open() */ - bool need_compat_check; /* if need compatibility check */ ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg); ~ha_myisammrg(); From c20afa6d49d36df2b8609da7c977b852d3bac521 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 17:13:12 +0300 Subject: [PATCH 119/466] Backport of: ---------------------------------------------------------- revno: 2617.69.24 committer: Konstantin Osipov branch nick: 5.4-42546 timestamp: Fri 2009-08-14 19:22:05 +0400 message: A pre-requisite for a fix for Bug#42546 "Backup: RESTORE fails, thinking it finds an existing table" Back-port from WL 148 "Foreign keys" feature tree a patch that introduced Prelocking_strategy class -- a way to parameterize open_tables() behaviour, implemented by Dmitry Lenev. (Part of WL#4284). --- sql/mysql_priv.h | 22 ++- sql/sp.cc | 416 +++++++++++------------------------------- sql/sp.h | 52 ++++-- sql/sp_head.h | 8 +- sql/sql_base.cc | 440 +++++++++++++++++++++++++++++++++++++++------ sql/sql_class.h | 84 +++++++++ sql/sql_lex.h | 17 ++ sql/sql_parse.cc | 11 +- sql/sql_table.cc | 34 +++- sql/sql_trigger.cc | 52 ++++++ sql/sql_trigger.h | 6 +- sql/sql_yacc.yy | 66 +++++-- 12 files changed, 790 insertions(+), 418 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index d65850e0d18..c956a0bfd12 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1474,10 +1474,26 @@ int setup_ftfuncs(SELECT_LEX* select); int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond); -int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags); +int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, + Prelocking_strategy *prelocking_strategy); +inline int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags) +{ + DML_prelocking_strategy prelocking_strategy; + + return open_tables(thd, tables, counter, flags, &prelocking_strategy); +} /* open_and_lock_tables with optional derived handling */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, - uint flags); +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, + bool derived, uint flags, + Prelocking_strategy *prelocking_strategy); +inline int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, + bool derived, uint flags) +{ + DML_prelocking_strategy prelocking_strategy; + + return open_and_lock_tables_derived(thd, tables, derived, flags, + &prelocking_strategy); +} /* simple open_and_lock_tables without derived handling */ inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { diff --git a/sql/sp.cc b/sql/sp.cc index 68c8ad395a1..19fe00594bd 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -17,7 +17,6 @@ #include "sp.h" #include "sp_head.h" #include "sp_cache.h" -#include "sql_trigger.h" #include @@ -1388,32 +1387,6 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name) } -/** - Structure that represents element in the set of stored routines - used by statement or routine. -*/ -struct Sroutine_hash_entry; - -struct Sroutine_hash_entry -{ - /** - Set key consisting of one-byte routine type and quoted routine name. - */ - LEX_STRING key; - /** - Next element in list linking all routines in set. See also comments - for LEX::sroutine/sroutine_list and sp_head::m_sroutines. - */ - Sroutine_hash_entry *next; - /** - Uppermost view which directly or indirectly uses this routine. - 0 if routine is not used in view. Note that it also can be 0 if - statement uses routine both via view and directly. - */ - TABLE_LIST *belong_to_view; -}; - - extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, my_bool first) { @@ -1423,52 +1396,17 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, } -/** - Check if - - current statement (the one in thd->lex) needs table prelocking - - first routine in thd->lex->sroutines_list needs to execute its body in - prelocked mode. - - @param thd Current thread, thd->lex is the statement to be - checked. - @param[out] need_prelocking TRUE - prelocked mode should be activated - before executing the statement; - FALSE - Don't activate prelocking - @param[out] first_no_prelocking TRUE - Tables used by first routine in - thd->lex->sroutines_list should be - prelocked. FALSE - Otherwise. - - @note - This function assumes that for any "CALL proc(...)" statement routines_list - will have 'proc' as first element (it may have several, consider e.g. - "proc(sp_func(...)))". This property is currently guaranted by the parser. -*/ - -void sp_get_prelocking_info(THD *thd, bool *need_prelocking, - bool *first_no_prelocking) -{ - Sroutine_hash_entry *routine; - routine= (Sroutine_hash_entry*)thd->lex->sroutines_list.first; - - DBUG_ASSERT(routine); - bool first_is_procedure= (routine->key.str[0] == TYPE_ENUM_PROCEDURE); - - *first_no_prelocking= first_is_procedure; - *need_prelocking= !first_is_procedure || test(routine->next); -} - - /** Auxilary function that adds new element to the set of stored routines used by statement. In case when statement uses stored routines but does not need prelocking (i.e. it does not use any tables) we will access the - elements of LEX::sroutines set on prepared statement re-execution. - Because of this we have to allocate memory for both hash element - and copy of its key in persistent arena. + elements of Query_tables_list::sroutines set on prepared statement + re-execution. Because of this we have to allocate memory for both + hash element and copy of its key in persistent arena. - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement @param arena Arena in which memory for new element will be allocated @param key Key for the hash representing set @@ -1476,7 +1414,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, (0 if routine is not used by view) @note - Will also add element to end of 'LEX::sroutines_list' list. + Will also add element to end of 'Query_tables_list::sroutines_list' list. @todo When we will got rid of these accesses on re-executions we will be @@ -1491,15 +1429,15 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, the set). */ -static bool add_used_routine(LEX *lex, Query_arena *arena, - const LEX_STRING *key, - TABLE_LIST *belong_to_view) +bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, + const LEX_STRING *key, TABLE_LIST *belong_to_view) { - my_hash_init_opt(&lex->sroutines, system_charset_info, + my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info, Query_tables_list::START_SROUTINES_HASH_SIZE, 0, 0, sp_sroutine_key, 0, 0); - if (!my_hash_search(&lex->sroutines, (uchar *)key->str, key->length)) + if (!my_hash_search(&prelocking_ctx->sroutines, (uchar *)key->str, + key->length)) { Sroutine_hash_entry *rn= (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) + @@ -1509,8 +1447,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, rn->key.length= key->length; rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry); memcpy(rn->key.str, key->str, key->length + 1); - my_hash_insert(&lex->sroutines, (uchar *)rn); - lex->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); + my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn); + prelocking_ctx->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); rn->belong_to_view= belong_to_view; return TRUE; } @@ -1525,24 +1463,25 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, To be friendly towards prepared statements one should pass persistent arena as second argument. - @param lex LEX representing statement - @param arena arena in which memory for new element of the set - will be allocated - @param rt routine name - @param rt_type routine type (one of TYPE_ENUM_PROCEDURE/...) + @param prelocking_ctx Prelocking context of the statement + @param arena Arena in which memory for new element of the set + will be allocated + @param rt Routine name + @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...) @note - Will also add element to end of 'LEX::sroutines_list' list (and will - take into account that this is explicitly used routine). + Will also add element to end of 'Query_tables_list::sroutines_list' list + (and will take into account that this is an explicitly used routine). */ -void sp_add_used_routine(LEX *lex, Query_arena *arena, +void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, sp_name *rt, char rt_type) { rt->set_routine_type(rt_type); - (void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0); - lex->sroutines_list_own_last= lex->sroutines_list.next; - lex->sroutines_list_own_elements= lex->sroutines_list.elements; + (void)sp_add_used_routine(prelocking_ctx, arena, &rt->m_sroutines_key, 0); + prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; + prelocking_ctx->sroutines_list_own_elements= + prelocking_ctx->sroutines_list.elements; } @@ -1550,13 +1489,14 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena, Remove routines which are only indirectly used by statement from the set of routines used by this statement. - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement */ -void sp_remove_not_own_routines(LEX *lex) +void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx) { Sroutine_hash_entry *not_own_rt, *next_rt; - for (not_own_rt= *(Sroutine_hash_entry **)lex->sroutines_list_own_last; + for (not_own_rt= + *(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last; not_own_rt; not_own_rt= next_rt) { /* @@ -1564,12 +1504,13 @@ void sp_remove_not_own_routines(LEX *lex) but we want to be more future-proof. */ next_rt= not_own_rt->next; - my_hash_delete(&lex->sroutines, (uchar *)not_own_rt); + my_hash_delete(&prelocking_ctx->sroutines, (uchar *)not_own_rt); } - *(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL; - lex->sroutines_list.next= lex->sroutines_list_own_last; - lex->sroutines_list.elements= lex->sroutines_list_own_elements; + *(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last= NULL; + prelocking_ctx->sroutines_list.next= prelocking_ctx->sroutines_list_own_last; + prelocking_ctx->sroutines_list.elements= + prelocking_ctx->sroutines_list_own_elements; } @@ -1605,23 +1546,24 @@ void sp_update_sp_used_routines(HASH *dst, HASH *src) routines used by statement. @param thd Thread context - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement @param src Hash representing set from which routines will be added @param belong_to_view Uppermost view which uses these routines, 0 if none - @note - It will also add elements to end of 'LEX::sroutines_list' list. + @note It will also add elements to end of + 'Query_tables_list::sroutines_list' list. */ -static void -sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src, - TABLE_LIST *belong_to_view) +void +sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, + HASH *src, TABLE_LIST *belong_to_view) { for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); - (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view); + (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &rt->key, + belong_to_view); } } @@ -1631,254 +1573,104 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src, routines used by statement. @param thd Thread context - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement @param src List representing set from which routines will be added @param belong_to_view Uppermost view which uses these routines, 0 if none - @note - It will also add elements to end of 'LEX::sroutines_list' list. + @note It will also add elements to end of + 'Query_tables_list::sroutines_list' list. */ -static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src, - TABLE_LIST *belong_to_view) +void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, + SQL_LIST *src, TABLE_LIST *belong_to_view) { for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first; rt; rt= rt->next) - (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view); + (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &rt->key, + belong_to_view); } /** - Cache sub-set of routines used by statement, add tables used by these - routines to statement table list. Do the same for all routines used - by these routines. + Ensure that routine is present in cache by loading it from the mysql.proc + table if needed. Emit an appropriate error if there was a problem during + loading. - @param thd thread context - @param lex LEX representing statement - @param start first routine from the list of routines to be cached - (this list defines mentioned sub-set). - @param first_no_prelock If true, don't add tables or cache routines used by - the body of the first routine (i.e. *start) - will be executed in non-prelocked mode. - @param tabs_changed Set to TRUE some tables were added, FALSE otherwise + @param[in] thd Thread context. + @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE). + @param[in] name Name of routine. + @param[out] sp Pointer to sp_head object for routine, NULL if routine was + not found, - @note - If some function is missing this won't be reported here. - Instead this fact will be discovered during query execution. - - @retval - 0 success - @retval - non-0 failure + @retval 0 Either routine is found and was succesfully loaded into cache + or it does not exist. + @retval non-0 Error while loading routine from mysql,proc table. */ -static int -sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, - Sroutine_hash_entry *start, - bool first_no_prelock) +int sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp) { int ret= 0; - bool first= TRUE; - DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); - for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) - { - sp_name name(thd, rt->key.str, rt->key.length); - int type= rt->key.str[0]; - sp_head *sp; + DBUG_ENTER("sp_cache_routine"); - /* - Triggers can't happen here: their bodies are always processed - in sp_cache_routines_and_add_tables_for_triggers(). - */ - DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); + DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); - if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? + if (!(*sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? &thd->sp_func_cache : &thd->sp_proc_cache), - &name))) + name))) + { + switch ((ret= db_find_routine(thd, type, name, sp))) { - switch ((ret= db_find_routine(thd, type, &name, &sp))) + case SP_OK: + if (type == TYPE_ENUM_FUNCTION) + sp_cache_insert(&thd->sp_func_cache, *sp); + else + sp_cache_insert(&thd->sp_proc_cache, *sp); + break; + case SP_KEY_NOT_FOUND: + ret= SP_OK; + break; + default: + /* Query might have been killed, don't set error. */ + if (thd->killed) + break; + /* + Any error when loading an existing routine is either some problem + with the mysql.proc table, or a parse error because the contents + has been tampered with (in which case we clear that error). + */ + if (ret == SP_PARSE_ERROR) + thd->clear_error(); + /* + If we cleared the parse error, or when db_find_routine() flagged + an error with it's return value without calling my_error(), we + set the generic "mysql.proc table corrupt" error here. + */ + if (! thd->is_error()) { - case SP_OK: - { - if (type == TYPE_ENUM_FUNCTION) - sp_cache_insert(&thd->sp_func_cache, sp); - else - sp_cache_insert(&thd->sp_proc_cache, sp); - } - break; - case SP_KEY_NOT_FOUND: - ret= SP_OK; - break; - default: - /* Query might have been killed, don't set error. */ - if (thd->killed) - break; /* - Any error when loading an existing routine is either some problem - with the mysql.proc table, or a parse error because the contents - has been tampered with (in which case we clear that error). + SP allows full NAME_LEN chars thus he have to allocate enough + size in bytes. Otherwise there is stack overrun could happen + if multibyte sequence is `name`. `db` is still safe because the + rest of the server checks agains NAME_LEN bytes and not chars. + Hence, the overrun happens only if the name is in length > 32 and + uses multibyte (cyrillic, greek, etc.) */ - if (ret == SP_PARSE_ERROR) - thd->clear_error(); - /* - If we cleared the parse error, or when db_find_routine() flagged - an error with it's return value without calling my_error(), we - set the generic "mysql.proc table corrupt" error here. - */ - if (! thd->is_error()) - { - /* - SP allows full NAME_LEN chars thus he have to allocate enough - size in bytes. Otherwise there is stack overrun could happen - if multibyte sequence is `name`. `db` is still safe because the - rest of the server checks agains NAME_LEN bytes and not chars. - Hence, the overrun happens only if the name is in length > 32 and - uses multibyte (cyrillic, greek, etc.) - */ - char n[NAME_LEN*2+2]; + char n[NAME_LEN*2+2]; - /* m_qname.str is not always \0 terminated */ - memcpy(n, name.m_qname.str, name.m_qname.length); - n[name.m_qname.length]= '\0'; - my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); - } - break; + /* m_qname.str is not always \0 terminated */ + memcpy(n, name->m_qname.str, name->m_qname.length); + n[name->m_qname.length]= '\0'; + my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); } + break; } - if (sp) - { - if (!(first && first_no_prelock)) - { - sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines, - rt->belong_to_view); - (void)sp->add_used_tables_to_table_list(thd, &lex->query_tables_last, - rt->belong_to_view); - } - sp->propagate_attributes(lex); - } - first= FALSE; } DBUG_RETURN(ret); } -/** - Cache all routines from the set of used by statement, add tables used - by those routines to statement table list. Do the same for all routines - used by those routines. - - @param thd thread context - @param lex LEX representing statement - @param first_no_prelock If true, don't add tables or cache routines used by - the body of the first routine (i.e. *start) - - @retval - 0 success - @retval - non-0 failure -*/ - -int -sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock) -{ - return sp_cache_routines_and_add_tables_aux(thd, lex, - (Sroutine_hash_entry *)lex->sroutines_list.first, - first_no_prelock); -} - - -/** - Add all routines used by view to the set of routines used by - statement. - - Add tables used by those routines to statement table list. Do the same - for all routines used by these routines. - - @param thd Thread context - @param lex LEX representing statement - @param view Table list element representing view - - @retval - 0 success - @retval - non-0 failure -*/ - -int -sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view) -{ - Sroutine_hash_entry **last_cached_routine_ptr= - (Sroutine_hash_entry **)lex->sroutines_list.next; - sp_update_stmt_used_routines(thd, lex, &view->view->sroutines_list, - view->top_table()); - return sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, FALSE); -} - - -/** - Add triggers for table to the set of routines used by statement. - Add tables used by them to statement table list. Do the same for - all implicitly used routines. - - @param thd thread context - @param lex LEX respresenting statement - @param table Table list element for table with trigger - - @retval - 0 success - @retval - non-0 failure -*/ - -int -sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, - TABLE_LIST *table) -{ - if (static_cast(table->lock_type) >= - static_cast(TL_WRITE_ALLOW_WRITE)) - { - for (int i= 0; i < (int)TRG_EVENT_MAX; i++) - { - if (table->trg_event_map & - static_cast(1 << static_cast(i))) - { - for (int j= 0; j < (int)TRG_ACTION_MAX; j++) - { - /* We can have only one trigger per action type currently */ - sp_head *trigger= table->table->triggers->bodies[i][j]; - if (trigger && - add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key, - table->belong_to_view)) - { - int ret; - /* Sic: excludes the trigger key from processing */ - Sroutine_hash_entry **last_cached_routine_ptr= - (Sroutine_hash_entry **)lex->sroutines_list.next; - - trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last, - table->belong_to_view); - trigger->propagate_attributes(lex); - sp_update_stmt_used_routines(thd, lex, - &trigger->m_sroutines, - table->belong_to_view); - - ret= sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, - FALSE); - if (ret) - return ret; - } - } - } - } - } - return 0; -} - - /** Generates the CREATE... string from the table information. diff --git a/sql/sp.h b/sql/sp.h index 5a190c5480e..cf8f72d2cef 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -42,6 +42,9 @@ sp_head * sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, bool cache_only); +int +sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp); + bool sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any); @@ -60,22 +63,45 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics); int sp_drop_routine(THD *thd, int type, sp_name *name); -/* - Procedures for pre-caching of stored routines and building table list - for prelocking. + +/** + Structure that represents element in the set of stored routines + used by statement or routine. */ -void sp_get_prelocking_info(THD *thd, bool *need_prelocking, - bool *first_no_prelocking); -void sp_add_used_routine(LEX *lex, Query_arena *arena, + +struct Sroutine_hash_entry +{ + /** + Set key consisting of one-byte routine type and quoted routine name. + */ + LEX_STRING key; + /** + Next element in list linking all routines in set. See also comments + for LEX::sroutine/sroutine_list and sp_head::m_sroutines. + */ + Sroutine_hash_entry *next; + /** + Uppermost view which directly or indirectly uses this routine. + 0 if routine is not used in view. Note that it also can be 0 if + statement uses routine both via view and directly. + */ + TABLE_LIST *belong_to_view; +}; + + +/* + Procedures for handling sets of stored routines used by statement or routine. +*/ +void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, sp_name *rt, char rt_type); -void sp_remove_not_own_routines(LEX *lex); +bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, + const LEX_STRING *key, TABLE_LIST *belong_to_view); +void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx); void sp_update_sp_used_routines(HASH *dst, HASH *src); -int sp_cache_routines_and_add_tables(THD *thd, LEX *lex, - bool first_no_prelock); -int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, - TABLE_LIST *view); -int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, - TABLE_LIST *table); +void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, + HASH *src, TABLE_LIST *belong_to_view); +void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, + SQL_LIST *src, TABLE_LIST *belong_to_view); extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, my_bool first); diff --git a/sql/sp_head.h b/sql/sp_head.h index dd11f8693ac..5610ecd2a72 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -454,10 +454,10 @@ public: /* This method is intended for attributes of a routine which need - to propagate upwards to the LEX of the caller (when a property of a - sp_head needs to "taint" the caller). + to propagate upwards to the Query_tables_list of the caller (when + a property of a sp_head needs to "taint" the calling statement). */ - void propagate_attributes(LEX *lex) + void propagate_attributes(Query_tables_list *prelocking_ctx) { /* If this routine needs row-based binary logging, the entire top statement @@ -466,7 +466,7 @@ public: the substatements not). */ if (m_flags & BINLOG_ROW_BASED_IF_MIXED) - lex->set_stmt_unsafe(); + prelocking_ctx->set_stmt_unsafe(); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7650d854efb..4be80f4c8a5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3714,34 +3714,99 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) /* + Perform steps of prelocking algorithm for elements of the + prelocking set other than tables. E.g. cache routines and, if + prelocking strategy prescribes so, extend the prelocking set with + tables and routines used by them. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context. + @param[in] start First element in the list representing + subset of the prelocking set to be + processed. + @param[in] prelocking_strategy Strategy which specifies how the + prelocking set should be extended when + one of its elements is processed. + @param[out] need_prelocking Set to TRUE if it was detected that this + statement will require prelocked mode for + its execution, not touched otherwise. + + @retval FALSE Success. + @retval TRUE Failure (Conflicting metadata lock, OOM, other errors). +*/ + +static bool +open_routines(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *start, + Prelocking_strategy *prelocking_strategy, + bool *need_prelocking) +{ + DBUG_ENTER("open_routines"); + + for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) + { + int type= rt->key.str[0]; + + switch (type) + { + case TYPE_ENUM_FUNCTION: + case TYPE_ENUM_PROCEDURE: + { + sp_name name(thd, rt->key.str, rt->key.length); + sp_head *sp; + + if (sp_cache_routine(thd, type, &name, &sp)) + DBUG_RETURN(TRUE); + + if (sp) + { + prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, + need_prelocking); + } + } + break; + case TYPE_ENUM_TRIGGER: + break; + default: + /* Impossible type value. */ + DBUG_ASSERT(0); + } + } + DBUG_RETURN(FALSE); +} + + +/** Open all tables in list - SYNOPSIS - open_tables() - thd - thread handler - start - list of tables in/out - counter - number of opened tables will be return using this parameter - flags - bitmap of flags to modify how the tables will be open: - MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has - done a flush or namelock on it. + @param[in] thd Thread context. + @param[in,out] start List of tables to be open (it can be adjusted for + statement that uses tables only implicitly, e.g. + for "SELECT f1()"). + @param[out] counter Number of tables which were open. + @param[in] flags Bitmap of flags to modify how the tables will be + open, see open_table() description for details. + @param[in] prelocking_strategy Strategy which specifies how prelocking + algorithm should work for this statement. - NOTE - Unless we are already in prelocked mode, this function will also precache - all SP/SFs explicitly or implicitly (via views and triggers) used by the - query and add tables needed for their execution to table list. If resulting - tables list will be non empty it will mark query as requiring precaching. + @note + Unless we are already in prelocked mode and prelocking strategy prescribes + so this function will also precache all SP/SFs explicitly or implicitly + (via views and triggers) used by the query and add tables needed for their + execution to table list. Statement that uses SFs, invokes triggers or + requires foreign key checks will be marked as requiring prelocking. Prelocked mode will be enabled for such query during lock_tables() call. If query for which we are opening tables is already marked as requiring prelocking it won't do such precaching and will simply reuse table list which is already built. - RETURN - 0 - OK - -1 - error + @retval 0 OK + @retval -1 Error. */ -int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) +int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, + Prelocking_strategy *prelocking_strategy) { TABLE_LIST *tables= NULL; Open_table_context ot_ctx(thd); @@ -3786,13 +3851,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) !thd->lex->requires_prelocking() && thd->lex->uses_stored_routines()) { - bool first_no_prelocking, need_prelocking; + bool need_prelocking= FALSE; TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; DBUG_ASSERT(thd->lex->query_tables == *start); - sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking); - if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking)) + if (open_routines(thd, thd->lex, + (Sroutine_hash_entry *)thd->lex->sroutines_list.first, + prelocking_strategy, &need_prelocking)) { /* Serious error during reading stored routines from mysql.proc table. @@ -3973,27 +4039,54 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) /* If we are not already in prelocked mode and extended table list is not - yet built and we have trigger for table being opened then we should - cache all routines used by its triggers and add their tables to - prelocking list. - If we lock table for reading we won't update it so there is no need to - process its triggers since they never will be activated. + yet built we might have to build the prelocking set for this statement. + + Since currently no prelocking strategy prescribes doing anything for + tables which are only read, we do below checks only if table is going + to be changed. */ if (thd->locked_tables_mode <= LTM_LOCK_TABLES && !thd->lex->requires_prelocking() && - tables->trg_event_map && tables->table->triggers && tables->lock_type >= TL_WRITE_ALLOW_WRITE) { - if (!query_tables_last_own) - query_tables_last_own= thd->lex->query_tables_last; - if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, - tables)) + bool need_prelocking= FALSE; + bool not_used; + TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; + Sroutine_hash_entry **sroutines_next= + (Sroutine_hash_entry **)thd->lex->sroutines_list.next; + + /* + Extend statement's table list and the prelocking set with + tables and routines according to the current prelocking + strategy. + + For example, for DML statements we need to add tables and routines + used by triggers which are going to be invoked for this element of + table list and also add tables required for handling of foreign keys. + */ + error= prelocking_strategy->handle_table(thd, thd->lex, tables, + &need_prelocking); + + if (need_prelocking && ! query_tables_last_own) + query_tables_last_own= save_query_tables_last; + + if (error) + { + result= -1; + goto err; + } + + /* + Process elements of the prelocking set which were added + by the above invocation of Prelocking_strategy method. + + For example, if new element is a routine, cache it and then, if + prelocking strategy prescribes so, add tables it uses to the table + list and routines it might invoke to the prelocking set. + */ + if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, + ¬_used)) { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ result= -1; goto err; } @@ -4039,13 +4132,27 @@ process_view_routines: */ if (tables->view && thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking() && - tables->view->uses_stored_routines()) + !thd->lex->requires_prelocking()) { - /* We have at least one table in TL here. */ - if (!query_tables_last_own) - query_tables_last_own= thd->lex->query_tables_last; - if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables)) + bool need_prelocking= FALSE; + bool not_used; + TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; + Sroutine_hash_entry **sroutines_next= + (Sroutine_hash_entry **)thd->lex->sroutines_list.next; + + error= prelocking_strategy->handle_view(thd, thd->lex, tables, + &need_prelocking); + + if (need_prelocking && ! query_tables_last_own) + query_tables_last_own= save_query_tables_last; + + if (error) + { + result= -1; + goto err; + } + if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, + ¬_used)) { /* Serious error during reading stored routines from mysql.proc table. @@ -4097,6 +4204,220 @@ process_view_routines: } +/** + Defines how prelocking algorithm for DML statements should handle routines: + - For CALL statements we do unrolling (i.e. open and lock tables for each + sub-statement individually). So for such statements prelocking is enabled + only if stored functions are used in parameter list and only for period + during which we calculate values of parameters. Thus in this strategy we + ignore procedure which is directly called by such statement and extend + the prelocking set only with tables/functions used by SF called from the + parameter list. + - For any other statement any routine which is directly or indirectly called + by statement is going to be executed in prelocked mode. So in this case we + simply add all tables and routines used by it to the prelocking set. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context of the statement. + @param[in] rt Prelocking set element describing routine. + @param[in] sp Routine body. + @param[out] need_prelocking Set to TRUE if method detects that prelocking + required, not changed otherwise. + + @retval FALSE Success. + @retval TRUE Failure (OOM). +*/ + +bool DML_prelocking_strategy:: +handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking) +{ + /* + We assume that for any "CALL proc(...)" statement sroutines_list will + have 'proc' as first element (it may have several, consider e.g. + "proc(sp_func(...)))". This property is currently guaranted by the + parser. + */ + + if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first || + rt->key.str[0] != TYPE_ENUM_PROCEDURE) + { + *need_prelocking= TRUE; + sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines, + rt->belong_to_view); + (void)sp->add_used_tables_to_table_list(thd, + &prelocking_ctx->query_tables_last, + rt->belong_to_view); + } + sp->propagate_attributes(prelocking_ctx); + return FALSE; +} + + +/** + Defines how prelocking algorithm for DML statements should handle table list + elements: + - If table has triggers we should add all tables and routines + used by them to the prelocking set. + + We do not need to acquire metadata locks on trigger names + in DML statements, since all DDL statements + that change trigger metadata always lock their + subject tables. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context of the statement. + @param[in] table_list Table list element for table. + @param[in] sp Routine body. + @param[out] need_prelocking Set to TRUE if method detects that prelocking + required, not changed otherwise. + + @retval FALSE Success. + @retval TRUE Failure (OOM). +*/ + +bool DML_prelocking_strategy:: +handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + /* We rely on a caller to check that table is going to be changed. */ + DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE); + + if (table_list->trg_event_map) + { + if (table_list->table->triggers) + { + *need_prelocking= TRUE; + + if (table_list->table->triggers-> + add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list)) + return TRUE; + } + } + + return FALSE; +} + + +/** + Defines how prelocking algorithm for DML statements should handle view - + all view routines should be added to the prelocking set. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context of the statement. + @param[in] table_list Table list element for view. + @param[in] sp Routine body. + @param[out] need_prelocking Set to TRUE if method detects that prelocking + required, not changed otherwise. + + @retval FALSE Success. + @retval TRUE Failure (OOM). +*/ + +bool DML_prelocking_strategy:: +handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + if (table_list->view->uses_stored_routines()) + { + *need_prelocking= TRUE; + + sp_update_stmt_used_routines(thd, prelocking_ctx, + &table_list->view->sroutines_list, + table_list->top_table()); + } + return FALSE; +} + + +/** + Defines how prelocking algorithm for LOCK TABLES statement should handle + table list elements. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context of the statement. + @param[in] table_list Table list element for table. + @param[in] sp Routine body. + @param[out] need_prelocking Set to TRUE if method detects that prelocking + required, not changed otherwise. + + @retval FALSE Success. + @retval TRUE Failure (OOM). +*/ + +bool Lock_tables_prelocking_strategy:: +handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + if (DML_prelocking_strategy::handle_table(thd, prelocking_ctx, table_list, + need_prelocking)) + return TRUE; + + /* We rely on a caller to check that table is going to be changed. */ + DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE); + + return FALSE; +} + + +/** + Defines how prelocking algorithm for ALTER TABLE statement should handle + routines - do nothing as this statement is not supposed to call routines. + + We still can end up in this method when someone tries + to define a foreign key referencing a view, and not just + a simple view, but one that uses stored routines. +*/ + +bool Alter_table_prelocking_strategy:: +handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking) +{ + return FALSE; +} + + +/** + Defines how prelocking algorithm for ALTER TABLE statement should handle + table list elements. + + Unlike in DML, we do not process triggers here. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context of the statement. + @param[in] table_list Table list element for table. + @param[in] sp Routine body. + @param[out] need_prelocking Set to TRUE if method detects that prelocking + required, not changed otherwise. + + + @retval FALSE Success. + @retval TRUE Failure (OOM). +*/ + +bool Alter_table_prelocking_strategy:: +handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + return FALSE; +} + + +/** + Defines how prelocking algorithm for ALTER TABLE statement + should handle view - do nothing. We don't need to add view + routines to the prelocking set in this case as view is not going + to be materialized. +*/ + +bool Alter_table_prelocking_strategy:: +handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + return FALSE; +} + + /* Check that lock is ok for tables; Call start stmt if ok @@ -4312,34 +4633,35 @@ end: } -/* +/** Open all tables in list, locks them and optionally process derived tables. - SYNOPSIS - open_and_lock_tables_derived() - thd - thread handler - tables - list of tables for open&locking - flags - set of options to be used to open and lock tables (see - open_tables() and mysql_lock_tables() for details). - derived - if to handle derived tables + @param thd Thread context. + @param tables List of tables for open and locking. + @param derived If to handle derived tables. + @param flags Bitmap of options to be used to open and lock + tables (see open_tables() and mysql_lock_tables() + for details). + @param prelocking_strategy Strategy which specifies how prelocking algorithm + should work for this statement. - RETURN - FALSE - ok - TRUE - error - - NOTE + @note The lock will automaticaly be freed by close_thread_tables() - NOTE - There are two convenience functions: + @note + There are several convenience functions, e.g. : - simple_open_n_lock_tables(thd, tables) without derived handling - open_and_lock_tables(thd, tables) with derived handling Both inline functions call open_and_lock_tables_derived() with the third argument set appropriately. + + @retval FALSE OK. + @retval TRUE Error */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, - uint flags) +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, + bool derived, uint flags, + Prelocking_strategy *prelocking_strategy) { uint counter; bool need_reopen; @@ -4349,7 +4671,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, for ( ; ; ) { - if (open_tables(thd, &tables, &counter, flags)) + if (open_tables(thd, &tables, &counter, flags, prelocking_strategy)) DBUG_RETURN(-1); DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { diff --git a/sql/sql_class.h b/sql/sql_class.h index 9edb5f97713..67fa6306283 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -38,6 +38,7 @@ class sp_rcontext; class sp_cache; class Parser_state; class Rows_log_event; +class Sroutine_hash_entry; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; @@ -1181,6 +1182,89 @@ private: }; +/** + An abstract class for a strategy specifying how the prelocking + algorithm should extend the prelocking set while processing + already existing elements in the set. +*/ + +class Prelocking_strategy +{ +public: + virtual ~Prelocking_strategy() { } + + virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, + bool *need_prelocking) = 0; + virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) = 0; + virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking)= 0; +}; + + +/** + A Strategy for prelocking algorithm suitable for DML statements. + + Ensures that all tables used by all statement's SF/SP/triggers and + required for foreign key checks are prelocked and SF/SPs used are + cached. +*/ + +class DML_prelocking_strategy : public Prelocking_strategy +{ +public: + virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, + bool *need_prelocking); + virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); + virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); +}; + + +/** + A strategy for prelocking algorithm to be used for LOCK TABLES + statement. +*/ + +class Lock_tables_prelocking_strategy : public DML_prelocking_strategy +{ + virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); +}; + + +/** + Strategy for prelocking algorithm to be used for ALTER TABLE statements. + + Unlike DML or LOCK TABLES strategy, it doesn't + prelock triggers, views or stored routines, since they are not + used during ALTER. +*/ + +class Alter_table_prelocking_strategy : public Prelocking_strategy +{ +public: + + Alter_table_prelocking_strategy(Alter_info *alter_info) + : m_alter_info(alter_info) + {} + + virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, + bool *need_prelocking); + virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); + virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); + +private: + Alter_info *m_alter_info; +}; + + /** A context of open_tables() function, used to recover from a failed open_table() attempt. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6b0c9699032..c10e0fc20fe 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1052,6 +1052,17 @@ public: } } + /** Return a pointer to the last element in query table list. */ + TABLE_LIST *last_table() + { + /* Don't use offsetof() macro in order to avoid warnings. */ + return query_tables ? + (TABLE_LIST*) ((char*) query_tables_last - + ((char*) &(query_tables->next_global) - + (char*) query_tables)) : + 0; + } + /** Has the parser/scanner detected that this statement is unsafe? */ @@ -1751,6 +1762,12 @@ struct LEX: public Query_tables_list bool subqueries, ignore; st_parsing_options parsing_options; Alter_info alter_info; + /* + For CREATE TABLE statement last element of table list which is not + part of SELECT or LIKE part (i.e. either element for table we are + creating or last of tables referenced by foreign keys). + */ + TABLE_LIST *create_last_non_select_table; /* Prepared statements SQL syntax:*/ LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 35c0973d103..2f1753bcdaa 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3342,9 +3342,14 @@ end_with_restore_list: thd->options|= OPTION_TABLE_LOCK; thd->in_lock_tables=1; - res= (open_and_lock_tables_derived(thd, all_tables, FALSE, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL) || - thd->locked_tables_list.init_locked_tables(thd)); + { + Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; + + res= (open_and_lock_tables_derived(thd, all_tables, FALSE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL, + &lock_tables_prelocking_strategy) || + thd->locked_tables_list.init_locked_tables(thd)); + } thd->in_lock_tables= 0; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c389ef2aef3..532cb1465c6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6560,9 +6560,26 @@ view_err: DBUG_RETURN(error); } - if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) + + /* + Code below can handle only base tables so ensure that we won't open a view. + Note that RENAME TABLE the only ALTER clause which is supported for views + has been already processed. + */ + table_list->required_type= FRMTYPE_TABLE; + + Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info); + + error= open_and_lock_tables_derived(thd, table_list, FALSE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL, + &alter_prelocking_strategy); + + if (error) + { DBUG_RETURN(TRUE); + } + + table= table_list->table; table->use_all_columns(); mdl_ticket= table->mdl_ticket; @@ -6572,7 +6589,8 @@ view_err: set of tables from the old table or to open a new TABLE object for an extended list and verify that they belong to locked tables. */ - if (thd->locked_tables_mode && + if ((thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) && (create_info->used_fields & HA_CREATE_USED_UNION) && (table->s->tmp_table == NO_TMP_TABLE)) { @@ -6806,7 +6824,8 @@ view_err: table_list->table= NULL; // For query cache query_cache_invalidate3(thd, table_list, 0); - if (thd->locked_tables_mode) + if ((thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) { /* Under LOCK TABLES we should adjust meta-data locks before finishing @@ -7290,7 +7309,9 @@ view_err: if (table->s->tmp_table != NO_TMP_TABLE) { /* Close lock if this is a transactional table */ - if (thd->lock && ! thd->locked_tables_mode) + if (thd->lock && + ! (thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; @@ -7492,7 +7513,8 @@ view_err: table_list->table=0; // For query cache query_cache_invalidate3(thd, table_list, 0); - if (thd->locked_tables_mode) + if (thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) { if ((new_name != table_name || new_db != db)) { diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 93b89b869d6..b8a4edaa963 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -19,6 +19,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "parse_file.h" +#include "sp.h" /*************************************************************************/ @@ -2017,6 +2018,57 @@ bool Table_triggers_list::process_triggers(THD *thd, } +/** + Add triggers for table to the set of routines used by statement. + Add tables used by them to statement table list. Do the same for + routines used by triggers. + + @param thd Thread context. + @param prelocking_ctx Prelocking context of the statement. + @param table_list Table list element for table with trigger. + + @retval FALSE Success. + @retval TRUE Failure. +*/ + +bool +Table_triggers_list:: +add_tables_and_routines_for_triggers(THD *thd, + Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list) +{ + DBUG_ASSERT(static_cast(table_list->lock_type) >= + static_cast(TL_WRITE_ALLOW_WRITE)); + + for (int i= 0; i < (int)TRG_EVENT_MAX; i++) + { + if (table_list->trg_event_map & + static_cast(1 << static_cast(i))) + { + for (int j= 0; j < (int)TRG_ACTION_MAX; j++) + { + /* We can have only one trigger per action type currently */ + sp_head *trigger= table_list->table->triggers->bodies[i][j]; + + if (trigger && sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + &trigger->m_sroutines_key, + table_list->belong_to_view)) + { + trigger->add_used_tables_to_table_list(thd, + &prelocking_ctx->query_tables_last, + table_list->belong_to_view); + sp_update_stmt_used_routines(thd, prelocking_ctx, + &trigger->m_sroutines, + table_list->belong_to_view); + trigger->propagate_attributes(prelocking_ctx); + } + } + } + } + return FALSE; +} + + /** Mark fields of subject table which we read/set in its triggers as such. diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index b411acf2ac5..85b2dbe5f21 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -144,8 +144,10 @@ public: void mark_fields_used(trg_event_type event); friend class Item_trigger_field; - friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, - TABLE_LIST *table); + + bool add_tables_and_routines_for_triggers(THD *thd, + Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list); private: bool prepare_record1_accessors(TABLE *table); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 24800f805d0..577c60b4c10 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1766,6 +1766,7 @@ create: lex->create_info.default_table_charset= NULL; lex->name.str= 0; lex->name.length= 0; + lex->create_last_non_select_table= lex->last_table(); } create2 { @@ -1788,7 +1789,8 @@ create: lex->sql_command= SQLCOM_CREATE_INDEX; if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL, - TL_OPTION_UPDATING)) + TL_OPTION_UPDATING, + TL_WRITE_ALLOW_READ)) MYSQL_YYABORT; lex->alter_info.reset(); lex->alter_info.flags= ALTER_ADD_INDEX; @@ -3952,7 +3954,7 @@ create2: ; create2a: - field_list ')' opt_create_table_options + create_field_list ')' opt_create_table_options opt_partitioning create3 {} | opt_partitioning @@ -4802,19 +4804,30 @@ create_table_option: Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; } - | UNION_SYM opt_equal '(' opt_table_list ')' + | UNION_SYM opt_equal { - /* Move the union list to the merge_list */ + Lex->select_lex.table_list.save_and_clear(&Lex->save_list); + } + '(' opt_table_list ')' + { + /* + Move the union list to the merge_list and exclude its tables + from the global list. + */ LEX *lex=Lex; - TABLE_LIST *table_list= lex->select_lex.get_table_list(); lex->create_info.merge_list= lex->select_lex.table_list; - lex->create_info.merge_list.elements--; - lex->create_info.merge_list.first= - (uchar*) (table_list->next_local); - lex->select_lex.table_list.elements=1; - lex->select_lex.table_list.next= - (uchar**) &(table_list->next_local); - table_list->next_local= 0; + lex->select_lex.table_list= lex->save_list; + /* + When excluding union list from the global list we assume that + elements of the former immediately follow elements which represent + table being created/altered and parent tables. + */ + TABLE_LIST *last_non_sel_table= lex->create_last_non_select_table; + DBUG_ASSERT(last_non_sel_table->next_global == + (TABLE_LIST *)lex->create_info.merge_list.first); + last_non_sel_table->next_global= 0; + Lex->query_tables_last= &last_non_sel_table->next_global; + lex->create_info.used_fields|= HA_CREATE_USED_UNION; } | default_charset @@ -4952,6 +4965,14 @@ udf_type: | INT_SYM {$$ = (int) INT_RESULT; } ; + +create_field_list: + field_list + { + Lex->create_last_non_select_table= Lex->last_table(); + } + ; + field_list: field_list_item | field_list ',' field_list_item @@ -5743,7 +5764,8 @@ alter: lex->sql_command= SQLCOM_ALTER_TABLE; lex->duplicates= DUP_ERROR; if (!lex->select_lex.add_table_to_list(thd, $4, NULL, - TL_OPTION_UPDATING)) + TL_OPTION_UPDATING, + TL_WRITE_ALLOW_READ)) MYSQL_YYABORT; lex->col_list.empty(); lex->select_lex.init_order(); @@ -5756,6 +5778,7 @@ alter: lex->alter_info.reset(); lex->no_write_to_binlog= 0; lex->create_info.storage_media= HA_SM_DEFAULT; + lex->create_last_non_select_table= lex->last_table(); } alter_commands {} @@ -6139,12 +6162,16 @@ add_column: ; alter_list_item: - add_column column_def opt_place { } + add_column column_def opt_place + { + Lex->create_last_non_select_table= Lex->last_table(); + } | ADD key_def { + Lex->create_last_non_select_table= Lex->last_table(); Lex->alter_info.flags|= ALTER_ADD_INDEX; } - | add_column '(' field_list ')' + | add_column '(' create_field_list ')' { Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX; } @@ -6155,6 +6182,9 @@ alter_list_item: lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } field_spec opt_place + { + Lex->create_last_non_select_table= Lex->last_table(); + } | MODIFY_SYM opt_column field_ident { LEX *lex=Lex; @@ -6177,6 +6207,9 @@ alter_list_item: MYSQL_YYABORT; } opt_place + { + Lex->create_last_non_select_table= Lex->last_table(); + } | DROP opt_column field_ident opt_restrict { LEX *lex=Lex; @@ -9607,7 +9640,8 @@ drop: lex->alter_info.flags= ALTER_DROP_INDEX; lex->alter_info.drop_list.push_back(ad); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, - TL_OPTION_UPDATING)) + TL_OPTION_UPDATING, + TL_WRITE_ALLOW_READ)) MYSQL_YYABORT; } | DROP DATABASE if_exists ident From f28f985922fae2bc49a6b515699d8404f01bc85b Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 15:38:58 +0100 Subject: [PATCH 120/466] Backport of revno: 2617.65.10 Bug #45067 Assertion `stmt_da->is_error()' in Delayed_insert::open_and_lock_table The assert was triggered when delayed insert was killed by another connection using mysql_notify_thread_having_shared_lock(). During handling of thd->killed, thd.fatal_error() was called without a previous call to my_error() which triggered the assert. This patch allows the assert to pass if thd->killed has been set. --- sql/sql_class.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_class.h b/sql/sql_class.h index 67fa6306283..f4504bfed0d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2216,7 +2216,7 @@ public: */ inline void fatal_error() { - DBUG_ASSERT(main_da.is_error()); + DBUG_ASSERT(stmt_da->is_error() || killed); is_fatal_error= 1; DBUG_PRINT("error",("Fatal error set")); } From 9e1cb3fe1b19aad0bd6cb0daf56f3243afd69c13 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 15:56:06 +0100 Subject: [PATCH 121/466] Backport of revno: 2617.68.9 Bug #43272 HANDLER SQL command does not work under LOCK TABLES HANDLER commands are now explicitly disallowed in LOCK TABLES mode. Before, HANDLER OPEN gave the misleading error message: "Table x was not locked with LOCK TABLES". This patch changes HANDLER OPEN/READ/CLOSE to give ER_LOCK_OR_ACTIVE_TRANSACTION "Can't execute the given command because you have active locked tables or an active transaction" in LOCK TABLES mode. Test case added to lock.test. --- mysql-test/r/lock.result | 15 +++++++++++++++ mysql-test/t/lock.test | 24 ++++++++++++++++++++++++ sql/sql_handler.cc | 16 ++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index afb444f8ae9..a542d70c5b9 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -319,6 +319,21 @@ alter table t1 add column j int; unlock tables; drop table t1; # +# Bug #43272 HANDLER SQL command does not work under LOCK TABLES +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +LOCK TABLE t1 WRITE; +# HANDLER commands are not allowed in LOCK TABLES mode +HANDLER t1 OPEN; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +HANDLER t1 READ FIRST; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +HANDLER t1 CLOSE; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +UNLOCK TABLES; +DROP TABLE t1; +# # Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against # LOCK TABLE # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 4d610559077..64003c9d861 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -385,6 +385,30 @@ alter table t1 add column j int; unlock tables; drop table t1; + +--echo # +--echo # Bug #43272 HANDLER SQL command does not work under LOCK TABLES +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (a INT); +LOCK TABLE t1 WRITE; + +--echo # HANDLER commands are not allowed in LOCK TABLES mode +--error ER_LOCK_OR_ACTIVE_TRANSACTION +HANDLER t1 OPEN; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +HANDLER t1 READ FIRST; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +HANDLER t1 CLOSE; + +UNLOCK TABLES; +DROP TABLE t1; + + --echo # --echo # Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against --echo # LOCK TABLE diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 1b7e45cec5d..5bdf8611f5e 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -201,6 +201,11 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) tables->db, tables->table_name, tables->alias, (int) reopen)); + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(TRUE); + } if (tables->schema_table) { my_error(ER_WRONG_USAGE, MYF(0), "HANDLER OPEN", @@ -386,6 +391,11 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) DBUG_PRINT("enter",("'%s'.'%s' as '%s'", tables->db, tables->table_name, tables->alias)); + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(TRUE); + } if ((hash_tables= (TABLE_LIST*) my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias, strlen(tables->alias) + 1))) @@ -448,6 +458,12 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, DBUG_PRINT("enter",("'%s'.'%s' as '%s'", tables->db, tables->table_name, tables->alias)); + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(TRUE); + } + thd->lex->select_lex.context.resolve_in_table_list_only(tables); list.push_front(new Item_field(&thd->lex->select_lex.context, NULL, NULL, "*")); From 962d3fb94af144c716fd84d953ee324cca915ae4 Mon Sep 17 00:00:00 2001 From: vvaintroub Date: Wed, 9 Dec 2009 03:13:38 +0100 Subject: [PATCH 122/466] Use CMAKE_CFG_INTDIR instead of different MSVC and Xcode specific configuration names --- cmake/install_macros.cmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 81fc44c6a15..3a5529efc15 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -30,8 +30,7 @@ MACRO (INSTALL_DEBUG_SYMBOLS targets) STRING(REPLACE ".exe" ".pdb" pdb_location ${location}) STRING(REPLACE ".dll" ".pdb" pdb_location ${pdb_location}) STRING(REPLACE ".lib" ".pdb" pdb_location ${pdb_location}) - STRING(REPLACE "$(OutDir)" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) - STRING(REPLACE "$(ConfigurationName)" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) + STRING(REPLACE "${CMAKE_CFG_INTDIR}" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) INSTALL(FILES ${pdb_location} DESTINATION ${INSTALL_LOCATION}) ENDFOREACH() ENDIF() @@ -62,7 +61,7 @@ IF(UNIX) DEPENDS ${output}) SET_TARGET_PROPERTIES(symlink_${linkbasename}${ext} PROPERTIES CLEAN_DIRECT_OUTPUT 1) # For Xcode, replace project config with install config - STRING(REPLACE "$(CONFIGURATION)" "\${CMAKE_INSTALL_CONFIG_NAME}" output ${output}) + STRING(REPLACE "${CMAKE_CFG_INTDIR}" "\${CMAKE_INSTALL_CONFIG_NAME}" output ${output}) INSTALL(FILES ${output} DESTINATION ${destination}) ENDIF() ENDMACRO() From 8817b0d3af8333afc834ab0f44a1f713241e244b Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 09:32:29 +0100 Subject: [PATCH 123/466] Backport of revno: 2599.169.2 Bug #42074 concurrent optimize table and alter table = Assertion failed: thd->is_error() This assertion could occur if OPTIMIZE TABLE was started on a InnoDB table and the table was altered to different storage engine after OPTIMIZE had started. This allowed OPTIMIZE to pass the initial checks for storage engine support, but fail once it reached "recreate+analyze" if this operation was not supported by the new storage engine. The bug had no consequences for non-debug builds of the server. In detail, the assertion was triggered when ha_analyze() returned HA_ADMIN_NOT_IMPLEMENTED. This led to a code path which included an assert checking for diagnostics area contents. Since this area had not been filled, the assertion was triggered. The diagnostics area is in this case only used to provide more detailed information about why optimize failed. The triggered code path sends this information to the client and clears the diagnostic area. This patch fixed the problem by adding an error message to the diagnostic area if ha_analyze() fails. This error message contains the error code returned by ha_analyze(). Test case added to innodb_mysql_sync.test. --- mysql-test/r/innodb_mysql_sync.result | 26 +++++++++++++++ mysql-test/t/innodb_mysql_sync.test | 48 +++++++++++++++++++++++++++ sql/sql_table.cc | 3 ++ 3 files changed, 77 insertions(+) create mode 100644 mysql-test/r/innodb_mysql_sync.result create mode 100644 mysql-test/t/innodb_mysql_sync.test diff --git a/mysql-test/r/innodb_mysql_sync.result b/mysql-test/r/innodb_mysql_sync.result new file mode 100644 index 00000000000..039d8b74c07 --- /dev/null +++ b/mysql-test/r/innodb_mysql_sync.result @@ -0,0 +1,26 @@ +# +# Bug 42074 concurrent optimize table and +# alter table = Assertion failed: thd->is_error() +# +DROP TABLE IF EXISTS t1; +# Create InnoDB table +CREATE TABLE t1 (id INT) engine=innodb; +# Connection 1 +# Start optimizing table +SET DEBUG_SYNC='ha_admin_try_alter SIGNAL optimize_started WAIT_FOR table_altered'; +OPTIMIZE TABLE t1; +# Connection 2 +# Change table to engine=memory +SET DEBUG_SYNC='now WAIT_FOR optimize_started'; +ALTER TABLE t1 engine=memory; +SET DEBUG_SYNC='now SIGNAL table_altered'; +# Connection 1 +# Complete optimization +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize error Got error -1 from storage engine +test.t1 optimize status Operation failed +Warnings: +Error 1030 Got error -1 from storage engine +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; diff --git a/mysql-test/t/innodb_mysql_sync.test b/mysql-test/t/innodb_mysql_sync.test new file mode 100644 index 00000000000..3f061e30293 --- /dev/null +++ b/mysql-test/t/innodb_mysql_sync.test @@ -0,0 +1,48 @@ +# +# Test file for InnoDB tests that require the debug sync facility +# +--source include/have_innodb.inc +--source include/have_debug_sync.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + + +--echo # +--echo # Bug 42074 concurrent optimize table and +--echo # alter table = Assertion failed: thd->is_error() +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # Create InnoDB table +CREATE TABLE t1 (id INT) engine=innodb; +connect (con2, localhost, root); + +--echo # Connection 1 +--echo # Start optimizing table +connection default; +SET DEBUG_SYNC='ha_admin_try_alter SIGNAL optimize_started WAIT_FOR table_altered'; +--send OPTIMIZE TABLE t1 + +--echo # Connection 2 +--echo # Change table to engine=memory +connection con2; +SET DEBUG_SYNC='now WAIT_FOR optimize_started'; +ALTER TABLE t1 engine=memory; +SET DEBUG_SYNC='now SIGNAL table_altered'; + +--echo # Connection 1 +--echo # Complete optimization +connection default; +--reap + +disconnect con2; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; + + +# Check that all connections opened by test cases in this file are really +# gone so execution of other tests won't be affected by their presence. +--source include/wait_until_count_sessions.inc diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 532cb1465c6..6630331ad00 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4878,6 +4878,7 @@ send_result_message: close_thread_tables(thd); if (!thd->locked_tables_mode) thd->mdl_context.release_all_locks(); + DEBUG_SYNC(thd, "ha_admin_try_alter"); protocol->store(STRING_WITH_LEN("note"), system_charset_info); protocol->store(STRING_WITH_LEN( "Table does not support optimize, doing recreate + analyze instead"), @@ -4911,6 +4912,8 @@ send_result_message: if ((table->table= open_ltable(thd, table, lock_type, 0)) && ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0)) result_code= 0; // analyze went ok + if (result_code) // analyze failed + table->table->file->print_error(result_code, MYF(0)); } /* Start a new row for the final status row */ protocol->prepare_for_resend(); From 4592dd2d8147d54422432afcf0e87530df9a3d59 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 09:51:20 +0100 Subject: [PATCH 124/466] Backport of revno: 2617.69.40 A pre-requisite patch for Bug#30977 "Concurrent statement using stored function and DROP FUNCTION breaks SBR". This patch changes the MDL API by introducing a namespace for lock keys: MDL_TABLE for tables and views and MDL_PROCEDURE for stored procedures and functions. The latter is needed for the fix for Bug#30977. --- sql/lock.cc | 2 +- sql/mdl.cc | 48 +++++++++++++++++++++------------------------- sql/mdl.h | 37 +++++++++++++++++++++-------------- sql/sp_head.cc | 4 ++-- sql/sql_base.cc | 6 +++--- sql/sql_delete.cc | 2 +- sql/sql_handler.cc | 2 +- sql/sql_parse.cc | 2 +- sql/sql_show.cc | 2 +- sql/sql_table.cc | 8 ++++---- sql/sql_trigger.cc | 2 +- sql/table.cc | 2 +- sql/table.h | 2 +- 13 files changed, 62 insertions(+), 57 deletions(-) diff --git a/sql/lock.cc b/sql/lock.cc index aea1bfbd0e6..8d314c4ad19 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -957,7 +957,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - lock_table->mdl_request.init(0, lock_table->db, lock_table->table_name, + lock_table->mdl_request.init(MDL_TABLE, lock_table->db, lock_table->table_name, MDL_EXCLUSIVE); mdl_requests.push_front(&lock_table->mdl_request); } diff --git a/sql/mdl.cc b/sql/mdl.cc index 566a7c96b3b..f9b52b17f5f 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -301,22 +301,18 @@ void MDL_context::merge(MDL_context *src) The MDL subsystem does not own or manage memory of lock requests. - @param type Id of type of object to be locked - @param db Name of database to which the object belongs - @param name Name of of the object - @param mdl_type The MDL lock type for the request. - - Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 - Note that tables and views must have the same lock type, since - they share the same name space in the SQL standard. + @param mdl_namespace Id of namespace of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object + @param mdl_type The MDL lock type for the request. */ -void MDL_request::init(unsigned char type_arg, +void MDL_request::init(enum_mdl_namespace mdl_namespace, const char *db_arg, const char *name_arg, enum enum_mdl_type mdl_type_arg) { - key.mdl_key_init(type_arg, db_arg, name_arg); + key.mdl_key_init(mdl_namespace, db_arg, name_arg); type= mdl_type_arg; ticket= NULL; } @@ -329,10 +325,10 @@ void MDL_request::init(unsigned char type_arg, on a memory root. Necessary to lock ad-hoc tables, e.g. mysql.* tables of grant and data dictionary subsystems. - @param type Id of type of object to be locked - @param db Name of database to which object belongs - @param name Name of of object - @param root MEM_ROOT on which object should be allocated + @param mdl_namespace Id of namespace of object to be locked + @param db Name of database to which object belongs + @param name Name of of object + @param root MEM_ROOT on which object should be allocated @note The allocated lock request will have MDL_SHARED type. @@ -341,7 +337,7 @@ void MDL_request::init(unsigned char type_arg, */ MDL_request * -MDL_request::create(unsigned char type, const char *db, +MDL_request::create(enum_mdl_namespace mdl_namespace, const char *db, const char *name, enum_mdl_type mdl_type, MEM_ROOT *root) { @@ -350,7 +346,7 @@ MDL_request::create(unsigned char type, const char *db, if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request)))) return NULL; - mdl_request->init(type, db, name, mdl_type); + mdl_request->init(mdl_namespace, db, name, mdl_type); return mdl_request; } @@ -1418,20 +1414,20 @@ void MDL_context::release_global_shared_lock() Auxiliary function which allows to check if we have exclusive lock on the object. - @param type Id of object type - @param db Name of the database - @param name Name of the object + @param mdl_namespace Id of object namespace + @param db Name of the database + @param name Name of the object @return TRUE if current context contains exclusive lock for the object, FALSE otherwise. */ bool -MDL_context::is_exclusive_lock_owner(unsigned char type, +MDL_context::is_exclusive_lock_owner(enum_mdl_namespace mdl_namespace, const char *db, const char *name) { MDL_request mdl_request; - mdl_request.init(type, db, name, MDL_EXCLUSIVE); + mdl_request.init(mdl_namespace, db, name, MDL_EXCLUSIVE); MDL_ticket *ticket= find_ticket(&mdl_request); DBUG_ASSERT(ticket == NULL || ticket->m_state == MDL_ACQUIRED); @@ -1444,19 +1440,19 @@ MDL_context::is_exclusive_lock_owner(unsigned char type, Auxiliary function which allows to check if we have some kind of lock on a object. - @param type Id of object type - @param db Name of the database - @param name Name of the object + @param mdl_namespace Id of object namespace + @param db Name of the database + @param name Name of the object @return TRUE if current context contains satisfied lock for the object, FALSE otherwise. */ bool -MDL_context::is_lock_owner(unsigned char type, +MDL_context::is_lock_owner(enum_mdl_namespace mdl_namespace, const char *db, const char *name) { - MDL_key key(type, db, name); + MDL_key key(mdl_namespace, db, name); MDL_ticket *ticket; MDL_context::Ticket_iterator it(m_tickets); diff --git a/sql/mdl.h b/sql/mdl.h index dc6f0a34443..87982bc6af1 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -47,6 +47,14 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; +/** + Object namespaces + + Different types of objects exist in different namespaces + - MDL_TABLE is for tables and views. + - MDL_PROCEDURE is for stored procedures, stored functions and UDFs. +*/ +enum enum_mdl_namespace { MDL_TABLE=0, MDL_PROCEDURE }; /** Maximal length of key for metadata locking subsystem. */ #define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) @@ -74,18 +82,18 @@ public: uint table_name_length() const { return m_length - m_db_name_length - 3; } /** - Construct a metadata lock key from a triplet (type, database and name). + Construct a metadata lock key from a triplet (mdl_namespace, database and name). - @remark The key for a table is <0 (=table)>++
+ @remark The key for a table is ++
- @param type Id of type of object to be locked - @param db Name of database to which the object belongs - @param name Name of of the object - @param key Where to store the the MDL key. + @param mdl_namespace Id of namespace of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object + @param key Where to store the the MDL key. */ - void mdl_key_init(char type, const char *db, const char *name) + void mdl_key_init(enum_mdl_namespace mdl_namespace, const char *db, const char *name) { - m_ptr[0]= type; + m_ptr[0]= (char) mdl_namespace; m_db_name_length= (uint) (strmov(m_ptr + 1, db) - m_ptr - 1); m_length= (uint) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); } @@ -104,11 +112,12 @@ public: { mdl_key_init(rhs); } - MDL_key(char type_arg, const char *db_arg, const char *name_arg) + MDL_key(enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg) { - mdl_key_init(type_arg, db_arg, name_arg); + mdl_key_init(namespace_arg, db_arg, name_arg); } MDL_key() {} /* To use when part of MDL_request. */ + private: char m_ptr[MAX_MDLKEY_LENGTH]; uint m_length; @@ -168,7 +177,7 @@ public: MDL_key key; public: - void init(unsigned char type_arg, const char *db_arg, const char *name_arg, + void init(enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg, enum_mdl_type mdl_type_arg); /** Set type of lock request. Can be only applied to pending locks. */ inline void set_type(enum_mdl_type type_arg) @@ -178,7 +187,7 @@ public: } bool is_shared() const { return type < MDL_EXCLUSIVE; } - static MDL_request *create(unsigned char type, const char *db, + static MDL_request *create(enum_mdl_namespace mdl_namespace, const char *db, const char *name, enum_mdl_type mdl_type, MEM_ROOT *root); @@ -318,10 +327,10 @@ public: void release_lock(MDL_ticket *ticket); void release_global_shared_lock(); - bool is_exclusive_lock_owner(unsigned char type, + bool is_exclusive_lock_owner(enum_mdl_namespace mdl_namespace, const char *db, const char *name); - bool is_lock_owner(unsigned char type, const char *db, const char *name); + bool is_lock_owner(enum_mdl_namespace mdl_namespace, const char *db, const char *name); inline bool has_locks() const { diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ae798d063ea..0744e5be930 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3981,7 +3981,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_request.init(0, table->db, table->table_name, MDL_SHARED); + table->mdl_request.init(MDL_TABLE, table->db, table->table_name, MDL_SHARED); /* Everyting else should be zeroed */ @@ -4023,7 +4023,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_request.init(0, table->db, table->table_name, MDL_SHARED); + table->mdl_request.init(MDL_TABLE, table->db, table->table_name, MDL_SHARED); lex->add_to_query_tables(table); return table; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4be80f4c8a5..6ed97c468ca 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -474,7 +474,7 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, To be able perform any operation on table we should own some kind of metadata lock on it. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(0, table_list->db, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_TABLE, table_list->db, table_list->table_name)); /* Read table definition from cache */ @@ -2541,7 +2541,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, TABLES breaks metadata locking protocol (potentially can lead to deadlocks) it should be disallowed. */ - if (thd->mdl_context.is_lock_owner(0, table_list->db, + if (thd->mdl_context.is_lock_owner(MDL_TABLE, table_list->db, table_list->table_name)) { char path[FN_REFLEN + 1]; @@ -8158,7 +8158,7 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, safe_mutex_assert_owner(&LOCK_open); DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED || - thd->mdl_context.is_exclusive_lock_owner(0, db, table_name)); + thd->mdl_context.is_exclusive_lock_owner(MDL_TABLE, db, table_name)); key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c3e205848de..03be0382150 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1177,7 +1177,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - mdl_request.init(0, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) DBUG_RETURN(TRUE); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 5bdf8611f5e..0bc9623c31c 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -264,7 +264,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - hash_tables->mdl_request.init(0, db, name, MDL_SHARED); + hash_tables->mdl_request.init(MDL_TABLE, db, name, MDL_SHARED); /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2f1753bcdaa..2c5f72c8cf4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6024,7 +6024,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_request.init(0, ptr->db, ptr->table_name, MDL_SHARED); + ptr->mdl_request.init(MDL_TABLE, ptr->db, ptr->table_name, MDL_SHARED); DBUG_RETURN(ptr); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 6a5895f9446..d346bae5258 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3068,7 +3068,7 @@ static bool acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table) { bool error; - table->mdl_request.init(0, table->db, table->table_name, + table->mdl_request.init(MDL_TABLE, table->db, table->table_name, MDL_SHARED_HIGH_PRIO); while (!(error= thd->mdl_context.try_acquire_shared_lock(&table->mdl_request)) && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6630331ad00..ddb53dd3754 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4140,7 +4140,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - target_mdl_request.init(0, db, table_name, MDL_EXCLUSIVE); + target_mdl_request.init(MDL_TABLE, db, table_name, MDL_EXCLUSIVE); if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) { result= TRUE; @@ -4361,7 +4361,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); - table_list->mdl_request.init(0, table_list->db, table_list->table_name, + table_list->mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request)) DBUG_RETURN(0); @@ -5271,7 +5271,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - table->mdl_request.init(0, db, table_name, MDL_EXCLUSIVE); + table->mdl_request.init(MDL_TABLE, db, table_name, MDL_EXCLUSIVE); if (thd->mdl_context.try_acquire_exclusive_lock(&table->mdl_request)) DBUG_RETURN(TRUE); @@ -6637,7 +6637,7 @@ view_err: } else { - target_mdl_request.init(0, new_db, new_name, MDL_EXCLUSIVE); + target_mdl_request.init(MDL_TABLE, new_db, new_name, MDL_EXCLUSIVE); if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) DBUG_RETURN(TRUE); if (target_mdl_request.ticket == NULL) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index b8a4edaa963..f9d99b3c119 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1883,7 +1883,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, In the future, only an exclusive metadata lock will be enough. */ #ifndef DBUG_OFF - if (thd->mdl_context.is_exclusive_lock_owner(0, db, old_table)) + if (thd->mdl_context.is_exclusive_lock_owner(MDL_TABLE, db, old_table)) safe_mutex_assert_owner(&LOCK_open); #endif diff --git a/sql/table.cc b/sql/table.cc index 7fb9bbbd955..c66610e5693 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4822,7 +4822,7 @@ size_t max_row_length(TABLE *table, const uchar *data) void init_mdl_requests(TABLE_LIST *table_list) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_request.init(0, table_list->db, table_list->table_name, + table_list->mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, MDL_SHARED); } diff --git a/sql/table.h b/sql/table.h index 21e7bd3142c..6a2bef4c610 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1125,7 +1125,7 @@ struct TABLE_LIST table_name_length= table_name_length_arg; alias= (char*) alias_arg; lock_type= lock_type_arg; - mdl_request.init(0, db, table_name, MDL_SHARED); + mdl_request.init(MDL_TABLE, db, table_name, MDL_SHARED); } /* From c95448ba11e1827cb7863a6679fbd57907f097c5 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:04:55 +0300 Subject: [PATCH 125/466] Backport of: ---------------------------------------------------------- revno: 2617.69.25 committer: Konstantin Osipov branch nick: 5.4-42546 timestamp: Fri 2009-08-14 23:52:00 +0400 message: A cleanup in open_tables() and lock_tables(): change return type of these functions to bool from int, to follow convention in the rest of the code. (Part of WL#4284 review fixes). --- sql/mysql_priv.h | 17 ++++---- sql/sql_base.cc | 104 ++++++++++++++++++++------------------------- sql/sql_handler.cc | 6 +-- 3 files changed, 58 insertions(+), 69 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index c956a0bfd12..e61c9a923c3 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1474,19 +1474,20 @@ int setup_ftfuncs(SELECT_LEX* select); int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond); -int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, +bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy); -inline int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags) +inline bool +open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags) { DML_prelocking_strategy prelocking_strategy; return open_tables(thd, tables, counter, flags, &prelocking_strategy); } /* open_and_lock_tables with optional derived handling */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, +bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, uint flags, Prelocking_strategy *prelocking_strategy); -inline int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, +inline bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, uint flags) { DML_prelocking_strategy prelocking_strategy; @@ -1495,12 +1496,12 @@ inline int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, &prelocking_strategy); } /* simple open_and_lock_tables without derived handling */ -inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) +inline bool simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { return open_and_lock_tables_derived(thd, tables, FALSE, 0); } /* open_and_lock_tables with derived handling */ -inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) +inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) { return open_and_lock_tables_derived(thd, tables, TRUE, 0); } @@ -1508,9 +1509,9 @@ inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, thr_lock_type lock_type, uint flags); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); -int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags, +bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags, bool *need_reopen); -int decide_logging_format(THD *thd, TABLE_LIST *tables); +bool decide_logging_format(THD *thd, TABLE_LIST *tables); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); bool rm_temporary_table(handlerton *base, char *path); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6ed97c468ca..0a5e89b279f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3801,17 +3801,16 @@ open_routines(THD *thd, Query_tables_list *prelocking_ctx, prelocking it won't do such precaching and will simply reuse table list which is already built. - @retval 0 OK - @retval -1 Error. + @retval FALSE Success. + @retval TRUE Error, reported. */ -int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, +bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) { TABLE_LIST *tables= NULL; Open_table_context ot_ctx(thd); - int result=0; - bool error; + bool error= FALSE; MEM_ROOT new_frm_mem; /* Also used for indicating that prelocking is need */ TABLE_LIST **query_tables_last_own; @@ -3856,16 +3855,16 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, DBUG_ASSERT(thd->lex->query_tables == *start); - if (open_routines(thd, thd->lex, - (Sroutine_hash_entry *)thd->lex->sroutines_list.first, - prelocking_strategy, &need_prelocking)) + error= open_routines(thd, thd->lex, + (Sroutine_hash_entry *)thd->lex->sroutines_list.first, + prelocking_strategy, &need_prelocking); + if (error) { /* Serious error during reading stored routines from mysql.proc table. Something's wrong with the table or its contents, and an error has been emitted; we must abort. */ - result= -1; goto err; } else if (need_prelocking) @@ -3924,7 +3923,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, { continue; } - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", tables->db, tables->table_name, (long) tables)); @@ -3979,10 +3978,9 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, it may change in future. */ if (ot_ctx.recover_from_failed_open_table_attempt(thd, tables)) - { - result= -1; goto err; - } + + error= FALSE; goto restart; } @@ -3990,10 +3988,9 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, { DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", tables->db, tables->alias)); + error= FALSE; continue; } - - result= -1; // Fatal error goto err; } @@ -4071,10 +4068,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, query_tables_last_own= save_query_tables_last; if (error) - { - result= -1; goto err; - } /* Process elements of the prelocking set which were added @@ -4084,12 +4078,10 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, prelocking strategy prescribes so, add tables it uses to the table list and routines it might invoke to the prelocking set. */ - if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, - ¬_used)) - { - result= -1; + error= open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, + ¬_used); + if (error) goto err; - } } if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) @@ -4105,12 +4097,10 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, tables->table->grant= tables->grant; /* Check and update metadata version of a base table. */ - if (check_and_update_table_version(thd, tables, tables->table->s)) - { - result= -1; - goto err; - } + error= check_and_update_table_version(thd, tables, tables->table->s); + if (error) + goto err; /* After opening a MERGE table add the children to the query list of tables, so that they are opened too. @@ -4121,7 +4111,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, /* Non-MERGE tables ignore this call. */ if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) { - result= -1; + error= TRUE; goto err; } @@ -4147,19 +4137,18 @@ process_view_routines: query_tables_last_own= save_query_tables_last; if (error) - { - result= -1; goto err; - } - if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, - ¬_used)) + + error= open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, + ¬_used); + + if (error) { /* Serious error during reading stored routines from mysql.proc table. Something is wrong with the table or its contents, and an error has been emitted; we must abort. */ - result= -1; goto err; } } @@ -4182,25 +4171,25 @@ process_view_routines: DBUG_ASSERT(tbl->pos_in_table_list == tables); if (tbl->file->extra(HA_EXTRA_ATTACH_CHILDREN)) { - result= -1; + error= TRUE; goto err; } } } - err: +err: thd_proc_info(thd, 0); free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block if (query_tables_last_own) thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - if (result && tables) + if (error && tables) { tables->table= NULL; } - DBUG_PRINT("tcache", ("returning: %d", result)); - DBUG_RETURN(result); + DBUG_PRINT("open_tables", ("returning: %d", (int) error)); + DBUG_RETURN(error); } @@ -4659,9 +4648,9 @@ end: @retval TRUE Error */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, - bool derived, uint flags, - Prelocking_strategy *prelocking_strategy) +bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, + bool derived, uint flags, + Prelocking_strategy *prelocking_strategy) { uint counter; bool need_reopen; @@ -4672,8 +4661,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, for ( ; ; ) { if (open_tables(thd, &tables, &counter, flags, prelocking_strategy)) - DBUG_RETURN(-1); - + DBUG_RETURN(TRUE); DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { const char *old_proc_info= thd->proc_info; thd->proc_info= "DBUG sleep"; @@ -4684,11 +4672,11 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, &need_reopen)) break; if (!need_reopen) - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); if (thd->in_multi_stmt_transaction() && has_locks) { my_error(ER_LOCK_DEADLOCK, MYF(0)); - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } close_tables_for_reopen(thd, &tables); } @@ -4697,7 +4685,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, (thd->fill_derived_tables() && mysql_handle_derived(thd->lex, &mysql_derived_filling)))) DBUG_RETURN(TRUE); /* purecov: inspected */ - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -4791,7 +4779,7 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) @param tables Tables involved in the query */ -int decide_logging_format(THD *thd, TABLE_LIST *tables) +bool decide_logging_format(THD *thd, TABLE_LIST *tables) { /* In SBR mode, we are only proceeding if we are binlogging this @@ -4889,7 +4877,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) DBUG_PRINT("info", ("error: %d", error)); if (error) - return -1; + return TRUE; /* We switch to row-based format if we are in mixed mode and one of @@ -4910,7 +4898,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) } } - return 0; + return FALSE; } /* @@ -4942,8 +4930,8 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) -1 Error */ -int lock_tables(THD *thd, TABLE_LIST *tables, uint count, - uint flags, bool *need_reopen) +bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, + uint flags, bool *need_reopen) { TABLE_LIST *table; @@ -4975,7 +4963,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, TABLE **start,**ptr; if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); for (table= tables; table; table= table->next_global) { if (!table->placeholder()) @@ -5002,7 +4990,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), flags, need_reopen))) - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); if (thd->lex->requires_prelocking() && thd->lex->sql_command != SQLCOM_LOCK_TABLES) @@ -5030,7 +5018,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, { mysql_unlock_tables(thd, thd->lock); thd->lock= 0; - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } } } @@ -5075,14 +5063,14 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, { my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0), table->table->s->table_name.str); - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } } } if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) { - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } } /* diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 0bc9623c31c..94f6b248e45 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -193,7 +193,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) TABLE_LIST *hash_tables = NULL; char *db, *name, *alias; uint dblen, namelen, aliaslen, counter; - int error; + bool error; TABLE *backup_open_tables; MDL_context backup_mdl_context; DBUG_ENTER("mysql_ha_open"); @@ -321,8 +321,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) can close a single table only. */ close_thread_tables(thd); - my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); - error= 1; + my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias); + error= TRUE; } else { From 530b54ea7899e72185f30d773bb016a72d80587e Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:17:17 +0300 Subject: [PATCH 126/466] Backpo ---------------------------------------------------------- revno: 2617.69.28 committer: Konstantin Osipov branch nick: 5.4-azalea-bugfixing timestamp: Tue 2009-08-18 15:27:35 +0400 message: An attempt to fix a link failure on Windows -- Sroutine_hash_entry is forward-declared as class. (Part of WL#4284). --- sql/sp.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sp.h b/sql/sp.h index cf8f72d2cef..1de41eb3920 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -69,8 +69,9 @@ sp_drop_routine(THD *thd, int type, sp_name *name); used by statement or routine. */ -struct Sroutine_hash_entry +class Sroutine_hash_entry { +public: /** Set key consisting of one-byte routine type and quoted routine name. */ From 0104ea3ca022629c15d9b59ced959e2e638ac177 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:29:36 +0300 Subject: [PATCH 127/466] ---------------------------------------------------------- revno: 2617.69.33 committer: Konstantin Osipov branch nick: mysql-next-46452 timestamp: Wed 2009-08-19 18:39:31 +0400 message: Bug#46452 "Crash in MDL, HANDLER OPEN + TRUNCATE TABLE". Flush open HANDLER tables before TRUNCATE, which is a DDL. --- mysql-test/r/truncate.result | 14 ++++++++++++++ mysql-test/t/truncate.test | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/mysql-test/r/truncate.result b/mysql-test/r/truncate.result index b194f9b7dc6..8ce2ad9be21 100644 --- a/mysql-test/r/truncate.result +++ b/mysql-test/r/truncate.result @@ -60,3 +60,17 @@ truncate table v1; ERROR 42S02: Table 'test.v1' doesn't exist drop view v1; drop table t1; +# +# Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 AS SELECT 1 AS f1; +HANDLER t1 OPEN; +# Here comes the crash. +TRUNCATE t1; +# Currently TRUNCATE, just like other DDL, implicitly closes +# open HANDLER table. +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +DROP TABLE t1; +# End of 6.0 tests diff --git a/mysql-test/t/truncate.test b/mysql-test/t/truncate.test index ba5364bd324..feec4051e35 100644 --- a/mysql-test/t/truncate.test +++ b/mysql-test/t/truncate.test @@ -69,3 +69,26 @@ drop table t1; # End of 5.0 tests +--echo # +--echo # Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE +--echo # +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 AS SELECT 1 AS f1; + +HANDLER t1 OPEN; +--echo # Here comes the crash. +TRUNCATE t1; + +--echo # Currently TRUNCATE, just like other DDL, implicitly closes +--echo # open HANDLER table. +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +# Cleanup +DROP TABLE t1; + +--echo # End of 6.0 tests + From 7fa9f986671a2b434a5f824baed27fa654ba7150 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:37:54 +0300 Subject: [PATCH 128/466] Backport of: ------------------------------------------------------------ revno: 2617.69.32 committer: Dmitry Lenev branch nick: mysql-next-bg46747 timestamp: Wed 2009-08-19 18:12:27 +0400 message: Fix for bug #46747 "Crash in MDL_ticket::upgrade_shared_lock_to_exclusive on TRIGGER + TEMP table". Server crashed when one tried to drop trigger which had its subject table shadowed by a temporary table with the same name. This problem occured because in such situation DROP TRIGGER has opened temporary table instead of base table on which trigger was defined. Attempt to upgrade metadata lock on this temporary table led to crash (we don't acquire metadata locks for temporary tables). This fix ensures that DROP TRIGGER ignores temporary tables when trying to open table on which trigger to be dropped is defined. --- mysql-test/r/trigger.result | 20 ++++++++++++++++++++ mysql-test/t/trigger.test | 25 +++++++++++++++++++++++++ sql/sql_trigger.cc | 5 +++++ 3 files changed, 50 insertions(+) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 47b50b233b3..5c63604a325 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -2162,3 +2162,23 @@ Warning 1265 Data truncated for column 'trg2' at row 1 DROP TRIGGER trg1; DROP TRIGGER trg2; DROP TABLE t1; +# +# Bug #46747 "Crash in MDL_ticket::upgrade_shared_lock_to_exclusive +# on TRIGGER + TEMP table". +# +drop trigger if exists t1_bi; +drop temporary table if exists t1; +drop table if exists t1; +create table t1 (i int); +create trigger t1_bi before insert on t1 for each row set @a:=1; +# Create temporary table which shadows base table with trigger. +create temporary table t1 (j int); +# Dropping of trigger should succeed. +drop trigger t1_bi; +select trigger_name from information_schema.triggers +where event_object_schema = 'test' and event_object_table = 't1'; +trigger_name +# Clean-up. +drop temporary table t1; +drop table t1; +End of 6.0 tests. diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index f3b9d6bb91e..7ff61bd96e1 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -2489,3 +2489,28 @@ DROP TRIGGER trg1; DROP TRIGGER trg2; DROP TABLE t1; + +--echo # +--echo # Bug #46747 "Crash in MDL_ticket::upgrade_shared_lock_to_exclusive +--echo # on TRIGGER + TEMP table". +--echo # + +--disable_warnings +drop trigger if exists t1_bi; +drop temporary table if exists t1; +drop table if exists t1; +--enable_warnings + +create table t1 (i int); +create trigger t1_bi before insert on t1 for each row set @a:=1; +--echo # Create temporary table which shadows base table with trigger. +create temporary table t1 (j int); +--echo # Dropping of trigger should succeed. +drop trigger t1_bi; +select trigger_name from information_schema.triggers + where event_object_schema = 'test' and event_object_table = 't1'; +--echo # Clean-up. +drop temporary table t1; +drop table t1; + +--echo End of 6.0 tests. diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index f9d99b3c119..015e0d4daa1 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -443,6 +443,11 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* We also don't allow creation of triggers on views. */ tables->required_type= FRMTYPE_TABLE; + /* + Also prevent DROP TRIGGER from opening temporary table which might + shadow base table on which trigger to be dropped is defined. + */ + tables->skip_temporary= TRUE; /* Keep consistent with respect to other DDL statements */ mysql_ha_rm_tables(thd, tables); From e1992b30269fea83c1519e39de700ed51793623f Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 10:44:01 +0100 Subject: [PATCH 129/466] Backport of revno: 2617.68.39 Bug #47249 assert in MDL_global_lock::is_lock_type_compatible This assert could be triggered if LOCK TABLES were used to lock both a table and a view that used the same table. The table would have to be first WRITE locked and then READ locked. So "LOCK TABLES v1 WRITE, t1 READ" would eventually trigger the assert, "LOCK TABLES v1 READ, t1 WRITE" would not. The reason is that the ordering of locks in the interal representation made a difference when executing FLUSH TABLE on the table. During FLUSH TABLE, a lock was upgraded to exclusive. If this lock was of type MDL_SHARED and not MDL_SHARED_UPGRADABLE, an internal counter in the MDL subsystem would get out of sync. This would happen if the *last* mention of the table in LOCK TABLES was a READ lock. The counter in question is the number exclusive locks (active or intention). This is used to make sure a global metadata lock is only taken when the counter is zero (= no conflicts). The counter is increased when a MDL_EXCLUSIVE or MDL_SHARED_UPGRADABLE lock is taken, but not when upgrade_shared_lock_to_exclusive() is used to upgrade directly from MDL_SHARED to MDL_EXCLUSIVE. This patch fixes the problem by searching for a TABLE instance locked with MDL_SHARED_UPGRADABLE or MDL_EXCLUSIVE before calling upgrade_shared_lock_to_exclusive(). The patch also adds an assert checking that only MDL_SHARED_UPGRADABLE locks are upgraded to exclusive. Test case added to lock_multi.test. --- mysql-test/r/lock_multi.result | 31 ++++++++++++++++++ mysql-test/t/lock_multi.test | 58 ++++++++++++++++++++++++++++++++++ sql/mdl.cc | 3 ++ sql/mdl.h | 4 +++ sql/sql_base.cc | 34 ++++++++++++++++++-- 5 files changed, 128 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 930983bae27..ef9292ad8c0 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -219,3 +219,34 @@ flush tables with read lock;; connection: default flush tables; drop table t1; +# +# Bug#47249 assert in MDL_global_lock::is_lock_type_compatible +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +# +# Test 1: LOCK TABLES v1 WRITE, t1 READ; +# +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; +# Connection 2 +LOCK TABLES v1 WRITE, t1 READ; +FLUSH TABLE t1; +# Connection 1 +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; +DROP TABLE t1; +DROP VIEW v1; +# +# Test 2: LOCK TABLES t1 WRITE, v1 READ; +# +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; +# Connection 2 +LOCK TABLES t1 WRITE, v1 READ; +FLUSH TABLE t1; +# Connection 1 +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index c82e351dfef..cbb99c04967 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -664,5 +664,63 @@ connection flush; --reap connection default; disconnect flush; + + +--echo # +--echo # Bug#47249 assert in MDL_global_lock::is_lock_type_compatible +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +--enable_warnings + +--echo # +--echo # Test 1: LOCK TABLES v1 WRITE, t1 READ; +--echo # + +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; + +--echo # Connection 2 +connect (con2,localhost,root); +LOCK TABLES v1 WRITE, t1 READ; +FLUSH TABLE t1; +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection 1 +connection default; +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; # Assertion happened here + +# Cleanup +DROP TABLE t1; +DROP VIEW v1; + +--echo # +--echo # Test 2: LOCK TABLES t1 WRITE, v1 READ; +--echo # + +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; + +--echo # Connection 2 +connect (con2,localhost,root); +LOCK TABLES t1 WRITE, v1 READ; +FLUSH TABLE t1; +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection 1 +connection default; +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; # Assertion happened here + +# Cleanup +DROP TABLE t1; +DROP VIEW v1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/mdl.cc b/sql/mdl.cc index f9b52b17f5f..879b12a4cac 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1005,6 +1005,9 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() if (m_type == MDL_EXCLUSIVE) DBUG_RETURN(FALSE); + /* Only allow upgrades from MDL_SHARED_UPGRADABLE */ + DBUG_ASSERT(m_type == MDL_SHARED_UPGRADABLE); + pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(thd, mysys_var); diff --git a/sql/mdl.h b/sql/mdl.h index 87982bc6af1..03631bb9dd6 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -254,6 +254,10 @@ public: mdl_cached_object_release_hook release_hook); const MDL_context *get_ctx() const { return m_ctx; } bool is_shared() const { return m_type < MDL_EXCLUSIVE; } + bool is_upgradable_or_exclusive() const + { + return m_type == MDL_SHARED_UPGRADABLE || m_type == MDL_EXCLUSIVE; + } bool upgrade_shared_lock_to_exclusive(); void downgrade_exclusive_lock(); private: diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a5e89b279f..140e9e14568 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -130,6 +130,8 @@ static bool tdc_wait_for_old_versions(THD *thd, static bool has_write_table_with_auto_increment(TABLE_LIST *tables); +TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db, + const char *table_name); uint cached_open_tables(void) { @@ -999,8 +1001,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, table_list= table_list->next_global) { /* A check that the table was locked for write is done by the caller. */ - TABLE *table= find_locked_table(thd->open_tables, table_list->db, - table_list->table_name); + TABLE *table= find_table_for_mdl_upgrade(thd->open_tables, table_list->db, + table_list->table_name); /* May return NULL if this table has already been closed via an alias. */ if (! table) @@ -2942,6 +2944,34 @@ TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_na } +/** + Find instance of TABLE with MDL_SHARED_UPGRADABLE or + MDL_EXCLUSIVE lock from the list of open tables. + + @param list List of TABLE objects to be searched + @param db Database name. + @param table_name Name of table. + + @return Pointer to MDL_SHARED_UPGRADABLE or MDL_EXCLUSIVE + TABLE instance, NULL otherwise. +*/ + +TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db, + const char *table_name) +{ + TABLE *tab= find_locked_table(list, db, table_name); + + while (tab != NULL) + { + if (tab->mdl_ticket != NULL && + tab->mdl_ticket->is_upgradable_or_exclusive()) + return tab; + tab= find_locked_table(tab->next, db, table_name); + } + return NULL; +} + + /*********************************************************************** class Locked_tables_list implementation. Declared in sql_class.h ************************************************************************/ From 289d2bf72b7ee99b2f7e5f60e9aabb0e0d41bef3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:44:05 +0300 Subject: [PATCH 130/466] Backport of: ------------------------------------------------------------ revno: 2617.69.37 committer: Dmitry Lenev branch nick: mysql-next-bg46748 timestamp: Fri 2009-08-21 18:17:02 +0400 message: Fix for bug #46748 "Assertion in MDL_context::wait_for_locks() on INSERT + CREATE TRIGGER". Concurrent execution of statements involving stored functions or triggers which were using several tables and DDL statements which affected those tables on debug build of server might have led to assertion failures in MDL_context::wait_for_locks(). Non-debug build was not affected. The problem was that during back-off which happens when open_tables() encounters conflicting metadata lock for one of the tables being open we didn't reset MDL_request::ticket value for requests which correspond to tables from extended prelocking set. Since these requests are part of of list of requests to be waited for in Open_table_context this broke assumption that ticket value for them is 0 in MDL_context::wait_for_locks() and caused assertion failure. This fix ensures that close_tables_for_reopen(), which performs this back-off resets MDL_request::ticket value not only for tables directly used by the statement but also for tables from extended prelocking set, thus satisfying assumption described above. --- mysql-test/r/mdl_sync.result | 43 ++++++++++++++++++++ mysql-test/t/mdl_sync.test | 77 ++++++++++++++++++++++++++++++++++++ sql/sql_base.cc | 16 +++++++- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 37f18746c57..36451985a86 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -19,3 +19,46 @@ connection: con2 ERROR 42S02: Unknown table 't1' drop table t3; SET DEBUG_SYNC= 'RESET'; +# +# Test for bug #46748 "Assertion in MDL_context::wait_for_locks() +# on INSERT + CREATE TRIGGER". +# +drop tables if exists t1, t2, t3, t4, t5; +# Let us simulate scenario in which we open some tables from extended +# part of prelocking set but then encounter conflicting metadata lock, +# so have to back-off and wait for it to go away. +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (l int); +create trigger t1_bi before insert on t1 for each row +insert into t2 values (new.i); +create trigger t2_bi before insert on t2 for each row +insert into t3 values (new.j); +# +# Switching to connection 'con1root'. +lock tables t4 read; +# +# Switching to connection 'con2root'. +# Send : +rename table t3 to t5, t4 to t3;; +# +# Switching to connection 'default'. +# Wait until the above RENAME TABLE adds pending requests for exclusive +# metadata lock on its tables and blocks due to 't4' being used by LOCK +# TABLES. +# Send : +insert into t1 values (1);; +# +# Switching to connection 'con1root'. +# Wait until INSERT statement waits due to encountering pending +# exclusive metadata lock on 't3'. +unlock tables; +# +# Switching to connection 'con2root'. +# Reap RENAME TABLE. +# +# Switching to connection 'default'. +# Reap INSERT. +# Clean-up. +drop tables t1, t2, t3, t5; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index ed03a9a5cb0..124e76bbc1f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -3,6 +3,10 @@ # --source include/have_debug_sync.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + + # Clean up resources used in this test case. --disable_warnings SET DEBUG_SYNC= 'RESET'; @@ -67,3 +71,76 @@ disconnect con3; --disable_warnings SET DEBUG_SYNC= 'RESET'; --enable_warnings + + +--echo # +--echo # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() +--echo # on INSERT + CREATE TRIGGER". +--echo # +--disable_warnings +drop tables if exists t1, t2, t3, t4, t5; +--enable_warnings +--echo # Let us simulate scenario in which we open some tables from extended +--echo # part of prelocking set but then encounter conflicting metadata lock, +--echo # so have to back-off and wait for it to go away. +connect (con1root,localhost,root,,test,,); +connect (con2root,localhost,root,,test,,); +connection default; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (l int); +create trigger t1_bi before insert on t1 for each row + insert into t2 values (new.i); +create trigger t2_bi before insert on t2 for each row + insert into t3 values (new.j); +--echo # +--echo # Switching to connection 'con1root'. +connection con1root; +lock tables t4 read; +--echo # +--echo # Switching to connection 'con2root'. +connection con2root; +--echo # Send : +--send rename table t3 to t5, t4 to t3; +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Wait until the above RENAME TABLE adds pending requests for exclusive +--echo # metadata lock on its tables and blocks due to 't4' being used by LOCK +--echo # TABLES. +let $wait_condition= select count(*)= 1 from information_schema.processlist + where state= 'Waiting for table' and + info='rename table t3 to t5, t4 to t3'; +--source include/wait_condition.inc +--echo # Send : +--send insert into t1 values (1); +--echo # +--echo # Switching to connection 'con1root'. +connection con1root; +--echo # Wait until INSERT statement waits due to encountering pending +--echo # exclusive metadata lock on 't3'. +let $wait_condition= select count(*)= 1 from information_schema.processlist + where state= 'Waiting for table' and + info='insert into t1 values (1)'; +--source include/wait_condition.inc +unlock tables; +--echo # +--echo # Switching to connection 'con2root'. +connection con2root; +--echo # Reap RENAME TABLE. +--reap +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap INSERT. +--reap +--echo # Clean-up. +disconnect con1root; +disconnect con2root; +drop tables t1, t2, t3, t5; + + +# Check that all connections opened by test cases in this file are really +# gone so execution of other tests won't be affected by their presence. +--source include/wait_until_count_sessions.inc diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a5e89b279f..42f987133dd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5104,19 +5104,31 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) { + TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); + TABLE_LIST *tmp; + /* If table list consists only from tables from prelocking set, table list for new attempt should be empty, so we have to update list's root pointer. */ - if (thd->lex->first_not_own_table() == *tables) + if (first_not_own_table == *tables) *tables= 0; thd->lex->chop_off_not_own_tables(); sp_remove_not_own_routines(thd->lex); - for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) + for (tmp= *tables; tmp; tmp= tmp->next_global) { tmp->table= 0; tmp->mdl_request.ticket= NULL; } + /* + Metadata lock requests for tables from extended part of prelocking set + are part of list of requests to be waited for in Open_table_context. + So to satisfy assumptions in MDL_context::wait_for_locks(), which will + performs the waiting, we have to reset MDL_request::ticket values for + them as well. + */ + for (tmp= first_not_own_table; tmp; tmp= tmp->next_global) + tmp->mdl_request.ticket= NULL; close_thread_tables(thd); if (!thd->locked_tables_mode) thd->mdl_context.release_all_locks(); From 4b8db713aaa617ee089eedab42d3ab4598355e3f Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 12:51:45 +0100 Subject: [PATCH 131/466] Backport of revno: 2617.69.34 Bug #45949 Assertion `!tables->table' in open_tables() on ALTER + INSERT DELAYED The assertion was caused by improperly closing tables when INSERT DELAYED needed to reopen tables. This patch replaces the call to close_thread_tables with close_tables_for_reopen which fixes the problem. The only way I was able to trigger the reopen code path and thus the assertion, was if ALTER TABLE killed the delayed insert thread and the delayed insert thread was able to enter the reopen code path before it noticed that thd->killed had been set. Note that in these cases reopen will always fail since open_table() will check thd->killed and return. This patch therefore adds two more thd->killed checks to minimize the chance of entering the reopen code path without hope for success. The patch also changes it so that if the delayed insert is killed using KILL_CONNECTION, the error message that is copied to the connection thread is ER_QUERY_INTERRUPTED rather than ER_SERVER_SHUTDOWN. This means that if INSERT DELAYED fails, the user will now see "Query execution was interrupted" rather than the misleading "Server shutdown in progress". No test case is supplied. This is for two reasons: 1) Unable to reproduce the error without having the delayed insert thread in a killed state which means that reopen is futile and was not supposed to be attempted. 2) Difficulty of using sync points in other threads than the connection thread. The patch has been successfully tested with the RQG and the grammar supplied in the bug description. --- sql/sql_insert.cc | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e0537c75e07..666d6400d32 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1974,6 +1974,11 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) } pthread_mutex_unlock(&di->mutex); thd_proc_info(thd, "got old table"); + if (thd->killed) + { + di->unlock(); + goto end_create; + } if (di->thd.killed) { if (di->thd.is_error()) @@ -1981,20 +1986,19 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) /* Copy the error message. Note that we don't treat fatal errors in the delayed thread as fatal errors in the - main thread. Use of my_message will enable stored - procedures continue handlers. + main thread. If delayed thread was killed, we don't + want to send "Server shutdown in progress" in the + INSERT THREAD. */ - my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(), - MYF(0)); - } - di->unlock(); + if (di->thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN) + my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0)); + else + my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(), + MYF(0)); + } + di->unlock(); goto end_create; } - if (thd->killed) - { - di->unlock(); - goto end_create; - } pthread_mutex_lock(&LOCK_delayed_insert); delayed_threads.append(di); pthread_mutex_unlock(&LOCK_delayed_insert); @@ -2061,8 +2065,11 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) goto error; if (dead) { - my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0)); - goto error; + /* Don't copy over "Server shutdown in progress". */ + if (thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN) + my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0)); + else + my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0)); } } share= table->s; @@ -2412,7 +2419,7 @@ pthread_handler_t handle_delayed_insert(void *arg) for (;;) { - if (thd->killed == THD::KILL_CONNECTION) + if (thd->killed) { uint lock_count; /* @@ -2474,7 +2481,7 @@ pthread_handler_t handle_delayed_insert(void *arg) } thd_proc_info(&(di->thd), 0); - if (di->tables_in_use && ! thd->lock) + if (di->tables_in_use && ! thd->lock && !thd->killed) { bool need_reopen; /* @@ -2491,14 +2498,15 @@ pthread_handler_t handle_delayed_insert(void *arg) MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK, &need_reopen))) { - if (need_reopen) + if (need_reopen && !thd->killed) { /* We were waiting to obtain TL_WRITE_DELAYED (probably due to someone having or requesting TL_WRITE_ALLOW_READ) and got aborted. Try to reopen table and if it fails die. */ - close_thread_tables(thd); + TABLE_LIST *tl_ptr = &di->table_list; + close_tables_for_reopen(thd, &tl_ptr); di->table= 0; if (di->open_and_lock_table()) { From ee8184355d85070a1bc272ecfe9863424fbad26d Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 12:58:52 +0100 Subject: [PATCH 132/466] Backport of revno: 3711.1.1 Bug #48725 Assert !thd->is_error() in delayed_get_table() This bug is a regression introduced by the patch for Bug #45949. If the handler thread for INSERT DELAYED was killed by e.g. FLUSH TABLES, the error message is copied from the handler thread to the connection thread. But the error was not reacted on, so the connection thread continued as normal, leading to an eventual assert. No test case added as it would have required sync points to work for handler threads. The plan is to add this in the scope of Bug #48725 / Bug #48541. The patch has been tested with the non-deterministic test case given in the bug description. --- sql/sql_insert.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 666d6400d32..171c5e2cee0 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2070,6 +2070,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0)); else my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0)); + goto error; } } share= table->s; From 20f19d147f8c7b6a825b7d85541d110ecb265421 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 13:15:35 +0100 Subject: [PATCH 133/466] Backport of revno: 2617.76.2 Bug #47107 assert in notify_shared_lock on incorrect CREATE TABLE , HANDLER Attempts to create a table (using CREATE TABLE, CREATE TABLE LIKE or CREATE TABLE SELECT statements) which already existed and was opened by the same connection through HANDLER statement, led to a stalled connection (for production builds of the server) or to the server being aborted due to an assertion failure (for debug builds of the server). This problem was introduced by the new implementation of a metadata locking subsystem and didn't affect earlier versions of the server. The cause of the problem was that the HANDLER was not closed by CREATE TABLE before CREATE tried to open and lock the table. Acquiring an exclusive MDL lock on the table to be created would therefore fail since HANDLER already had a shared MDL lock. This triggered an assert as the HANDLER and CREATE statements came from the same thread (self-deadlock). This patch resolves the issue by closing any open HANDLERs on tables to be created by CREATE TABLE, similar to what is already done for DROP and ALTER TABLE. Test case added to create.test. --- mysql-test/r/create.result | 18 ++++++++++++++++++ mysql-test/t/create.test | 30 ++++++++++++++++++++++++++++++ sql/sql_parse.cc | 6 ++++++ 3 files changed, 54 insertions(+) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index cf424b8b058..90502f94f44 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -1961,3 +1961,21 @@ END ;| ERROR 42000: This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table' DROP TABLE t1; DROP TABLE B; +# +# Bug #47107 assert in notify_shared_lock on incorrect +# CREATE TABLE , HANDLER +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(f1 integer); +# The following CREATE TABLEs before gave an assert. +HANDLER t1 OPEN AS A; +CREATE TABLE t1 SELECT 1 AS f2; +ERROR 42S01: Table 't1' already exists +HANDLER t1 OPEN AS A; +CREATE TABLE t1(f1 integer); +ERROR 42S01: Table 't1' already exists +CREATE TABLE t2(f1 integer); +HANDLER t1 OPEN AS A; +CREATE TABLE t1 LIKE t2; +ERROR 42S01: Table 't1' already exists +DROP TABLE t2; +DROP TABLE t1; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index c07014bfc19..21778e00ab9 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -1639,3 +1639,33 @@ END ;| DROP TABLE t1; DROP TABLE B; + + +--echo # +--echo # Bug #47107 assert in notify_shared_lock on incorrect +--echo # CREATE TABLE , HANDLER +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(f1 integer); + +--echo # The following CREATE TABLEs before gave an assert. + +HANDLER t1 OPEN AS A; +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1 SELECT 1 AS f2; + +HANDLER t1 OPEN AS A; +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1(f1 integer); + +CREATE TABLE t2(f1 integer); +HANDLER t1 OPEN AS A; +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1 LIKE t2; + +DROP TABLE t2; +DROP TABLE t1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2c5f72c8cf4..056a947f3fa 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2311,6 +2311,12 @@ case SQLCOM_PREPARE: thd->work_part_info= part_info; } #endif + + /* + Close any open handlers for the table + */ + mysql_ha_rm_tables(thd, create_table); + if (select_lex->item_list.elements) // With select { select_result *result; From 81813b1d72f9ab5c2a4355ff2aa01f99d374e1fb Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 13:27:24 +0100 Subject: [PATCH 134/466] Backport of revno: 2617.76.3 Bug#47107 Add missing line in previous change set. --- mysql-test/r/create.result | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 90502f94f44..66439593c7c 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -1964,6 +1964,7 @@ DROP TABLE B; # # Bug #47107 assert in notify_shared_lock on incorrect # CREATE TABLE , HANDLER +# DROP TABLE IF EXISTS t1; CREATE TABLE t1(f1 integer); # The following CREATE TABLEs before gave an assert. From cf1110cd4a807de8e5b4aa7c8655447358f6457c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 14:03:37 +0100 Subject: [PATCH 135/466] Backport of revno: 3702 Bug #48248 assert in MDL_ticket::upgrade_shared_lock_to_exclusive The assert would happen if REPAIR TABLE was used on a table already locked by LOCK TABLES READ. REPAIR mistakenly tried to upgrade the read-lock to exclusive, thereby triggering the assert. The cause of the problem was that REPAIR TABLE ignored errors from opening and locking tables. This is by design, as REPAIR can be used to broken tables that cannot be opened. However, repair also ignored logical errors such as the inability to exclusivly lock a table due to conflicting LOCK TABLES. This patch fixes the problem by not ignoring errors from opening and locking tables if inside LOCK TABLES mode. In LOCK TABLES we already know that the table can be opened, so that the failure to open must be a logical error. Test added to repair.test. --- mysql-test/r/lock.result | 2 +- mysql-test/r/repair.result | 12 ++++++++++++ mysql-test/r/view.result | 6 +++--- mysql-test/t/repair.test | 15 +++++++++++++++ sql/sql_table.cc | 15 +++++++++++++-- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index a542d70c5b9..8f680858fdc 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -41,7 +41,7 @@ lock tables t1 write; check table t2; Table Op Msg_type Msg_text test.t2 check Error Table 't2' was not locked with LOCK TABLES -test.t2 check error Corrupt +test.t2 check status Operation failed insert into t1 select index1,nr from t1; ERROR HY000: Table 't1' was not locked with LOCK TABLES unlock tables; diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index 5bb3dd76fed..77eb927a21f 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -157,3 +157,15 @@ REPAIR TABLE tt1 USE_FRM; Table Op Msg_type Msg_text tt1 repair error Cannot repair temporary table from .frm file DROP TABLE tt1; +# +# Bug #48248 assert in MDL_ticket::upgrade_shared_lock_to_exclusive +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT); +LOCK TABLES t1 READ; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair Error Table 't1' was locked with a READ lock and can't be updated +test.t1 repair status Operation failed +UNLOCK TABLES; +DROP TABLE t1; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 5f16d88a0dc..7e9739173df 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1955,15 +1955,15 @@ CHECK TABLE v1, v2, v3, v4, v5, v6; Table Op Msg_type Msg_text test.v1 check Error FUNCTION test.f1 does not exist test.v1 check Error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v1 check error Corrupt +test.v1 check status Operation failed test.v2 check status OK test.v3 check Error FUNCTION test.f1 does not exist test.v3 check Error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v3 check error Corrupt +test.v3 check status Operation failed test.v4 check status OK test.v5 check Error FUNCTION test.f1 does not exist test.v5 check Error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v5 check error Corrupt +test.v5 check status Operation failed test.v6 check status OK create function f1 () returns int return (select max(col1) from t1); DROP TABLE t1; diff --git a/mysql-test/t/repair.test b/mysql-test/t/repair.test index eb2ca7992a6..ec4c9b3cae8 100644 --- a/mysql-test/t/repair.test +++ b/mysql-test/t/repair.test @@ -158,3 +158,18 @@ CREATE TEMPORARY TABLE tt1 (c1 INT); REPAIR TABLE tt1 USE_FRM; DROP TABLE tt1; + +--echo # +--echo # Bug #48248 assert in MDL_ticket::upgrade_shared_lock_to_exclusive +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT); +LOCK TABLES t1 READ; +REPAIR TABLE t1; + +UNLOCK TABLES; +DROP TABLE t1; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ddb53dd3754..341d2d16757 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4548,6 +4548,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, char table_name[NAME_LEN*2+2]; char* db = table->db; bool fatal_error=0; + bool open_error; DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name)); DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options)); @@ -4575,12 +4576,22 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - open_and_lock_tables_derived(thd, table, TRUE, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL); + open_error= open_and_lock_tables_derived(thd, table, TRUE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL); thd->no_warnings_for_error= 0; table->next_global= save_next_global; table->next_local= save_next_local; thd->open_options&= ~extra_open_options; + /* + Under locked tables, we know that the table can be opened, + so any errors opening the table are logical errors. + In these cases it does not make sense to try to repair. + */ + if (open_error && thd->locked_tables_mode) + { + result_code= HA_ADMIN_FAILED; + goto send_result; + } #ifdef WITH_PARTITION_STORAGE_ENGINE if (table->table) { From fb545cf9d8f2cc38afe35640534f51e6281dee38 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 14:23:31 +0100 Subject: [PATCH 136/466] Backport of revno: 2617.31.30 Bug #21793 Missing CF_CHANGES_DATA and CF_STATUS_COMMAND for handful of commands CF_CHANGES_DATA and CF_STATUS_COMMAND flags added to the commands mentioned in the bug description. With the following two exceptions: 1) 4 commands do not exist: SQLCOM_RENAME_DB SQLCOM_LOAD_MASTER_DATA SQLCOM_LOAD_MASTER_TABLE SQLCOM_SHOW_COLUMN_TYPES 2) All SQLCOM_SHOW_* commands already had CF_STATUS_COMMAND, leaving only SQLCOM_BINLOG_BASE64_EVENT. Further, check_prepared_statement() in sql_prepare.cc has been simplified by taking advantage of the CF_STATUS_COMMAND flag. Note that no test case has been added. --- sql/sql_parse.cc | 91 ++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 056a947f3fa..06f29cd6039 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -197,6 +197,8 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; @@ -221,8 +223,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | - CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; @@ -237,34 +238,35 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_STORAGE_ENGINES]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_PRIVILEGES]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_WARNS]= CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT; - sql_command_flags[SQLCOM_SHOW_ERRORS]= CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT; + sql_command_flags[SQLCOM_SHOW_PRIVILEGES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_WARNS]= CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT; + sql_command_flags[SQLCOM_SHOW_ERRORS]= CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT; sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND | @@ -273,37 +275,52 @@ void init_update_queries(void) CF_SHOW_TABLE_COMMAND | CF_REEXECUTION_FRAGILE); + + sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA; + /* The following is used to preserver CF_ROW_COUNT during the a CALL or EXECUTE statement, so the value generated by the last called (or executed) statement is preserved. See mysql_execute_command() for how CF_ROW_COUNT is used. */ - sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT; /* The following admin table operations are allowed on log tables. */ - sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | - CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND | - CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | - CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_CREATE_USER]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_USER]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_RENAME_USER]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_REVOKE]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_GRANT]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_PROCEDURE]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_SPFUNCTION]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_PROCEDURE]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_FUNCTION]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS; From 1642f67b4019c1191ecd8fa5e53302171ef6aba4 Mon Sep 17 00:00:00 2001 From: "lars-erik.bjork@sun.com" <> Date: Wed, 9 Dec 2009 14:41:56 +0100 Subject: [PATCH 137/466] Backport of revno: 2617.68.36 --------------------------------------------- This is a patch for bug#47098 assert in MDL_context::destroy on HANDLER OPEN. The assert occurs in MDL_context::destroy when the connection is terminated, because all mdl_tickets have not been released. MERGE tables do not support being opened using the HANDLER ... OPEN command, and trying to do so will result in an error. In the event of an error, all tables that are opened, should be closed again. The fix for bug#45781 made sure that this also works for MERGE tables, which causes multiple tables to be opened. This fix extends the fix for bug#45781, by ensuring that also all locks are released, when MERGE tables are involved. --- mysql-test/r/merge.result | 30 ++++++++++++++++++++++++++++++ mysql-test/t/merge.test | 39 ++++++++++++++++++++++++++++++++++++++- sql/sql_handler.cc | 1 + 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 479645744ec..3a6cd4d3f5a 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2502,4 +2502,34 @@ c1 DROP TRIGGER t2_au; DROP FUNCTION f1; DROP TABLE tm1, t1, t2, t3, t4, t5; +# +# Bug47098 assert in MDL_context::destroy on HANDLER +# OPEN +# +# Test that merge tables are closed correctly when opened using +# HANDLER ... OPEN. +# The general case. +DROP TABLE IF EXISTS t1, t2, t3; +# Connection con1. +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +START TRANSACTION; +HANDLER t3 OPEN; +ERROR HY000: Table storage engine for 't3' doesn't have this option +DROP TABLE t1, t2, t3; +# Connection default. +# Disconnecting con1, all mdl_tickets must have been released. +# The bug-specific case. +# Connection con1. +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +DROP TABLE t2; +START TRANSACTION; +HANDLER t3 OPEN; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +DROP TABLE t1, t3; +# Connection default. +# Disconnecting con1, all mdl_tickets must have been released. End of 6.0 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index f7ce6ba700b..2738f79247f 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -1985,6 +1985,43 @@ DROP TRIGGER t2_au; DROP FUNCTION f1; DROP TABLE tm1, t1, t2, t3, t4, t5; - +--echo # +--echo # Bug47098 assert in MDL_context::destroy on HANDLER +--echo # OPEN +--echo # +--echo # Test that merge tables are closed correctly when opened using +--echo # HANDLER ... OPEN. +--echo # The general case. +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3; +--enable_warnings +--echo # Connection con1. +connect (con1,localhost,root,,); +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +START TRANSACTION; +--error ER_ILLEGAL_HA +HANDLER t3 OPEN; +DROP TABLE t1, t2, t3; +--echo # Connection default. +connection default; +--echo # Disconnecting con1, all mdl_tickets must have been released. +disconnect con1; +--echo # The bug-specific case. +--echo # Connection con1. +connect (con1,localhost,root,,); +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +DROP TABLE t2; +START TRANSACTION; +--error ER_WRONG_MRG_TABLE +HANDLER t3 OPEN; +DROP TABLE t1, t3; +--echo # Connection default. +connection default; +--echo # Disconnecting con1, all mdl_tickets must have been released. +disconnect con1; --echo End of 6.0 tests diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 94f6b248e45..cf178342b51 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -321,6 +321,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) can close a single table only. */ close_thread_tables(thd); + thd->mdl_context.release_all_locks(); my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias); error= TRUE; } From 17252c325827beeb9a74acc6c56d2c7515f36ef0 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 15:25:48 +0100 Subject: [PATCH 138/466] Backport of revno: 2617.68.13 Introduce a counter for protection against global read lock on thread level. The functions for protection against global read lock sometimes need a local variable to signal when the protection is set, and hence need to be released. It would be better to control this behaviour via a counter on the THD struct, telling how many times the protection has been claimed by the current thread. A side-effect of the fix is that if protection is claimed twice for a thread, only a simple increment is required for the second claim, instead of a mutex-protected increment of the global variable protect_against_global_read_lock. --- sql/lock.cc | 93 ++++++++++++++++++++++++++++++++++++++++++++++ sql/sql_class.cc | 1 + sql/sql_class.h | 1 + sql/sql_parse.cc | 37 +++++++----------- sql/sql_table.cc | 9 ++--- sql/sql_trigger.cc | 6 +-- 6 files changed, 115 insertions(+), 32 deletions(-) diff --git a/sql/lock.cc b/sql/lock.cc index 8d314c4ad19..420868a25c4 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -1098,6 +1098,19 @@ static volatile uint waiting_for_read_lock=0; #define GOT_GLOBAL_READ_LOCK 1 #define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2 +/** + Take global read lock, wait if there is protection against lock. + + If the global read lock is already taken by this thread, then nothing is done. + + See also "Handling of global read locks" above. + + @param thd Reference to thread. + + @retval False Success, global read lock set, commits are NOT blocked. + @retval True Failure, thread was killed. +*/ + bool lock_global_read_lock(THD *thd) { DBUG_ENTER("lock_global_read_lock"); @@ -1164,6 +1177,16 @@ bool lock_global_read_lock(THD *thd) } +/** + Unlock global read lock. + + Commits may or may not be blocked when this function is called. + + See also "Handling of global read locks" above. + + @param thd Reference to thread. +*/ + void unlock_global_read_lock(THD *thd) { uint tmp; @@ -1190,6 +1213,25 @@ void unlock_global_read_lock(THD *thd) DBUG_VOID_RETURN; } +/** + Wait if the global read lock is set, and optionally seek protection against + global read lock. + + See also "Handling of global read locks" above. + + @param thd Reference to thread. + @param abort_on_refresh If True, abort waiting if a refresh occurs, + do NOT seek protection against GRL. + If False, wait until the GRL is released and seek + protection against GRL. + @param is_not_commit If False, called from a commit operation, + wait only if commit blocking is also enabled. + + @retval False Success, protection against global read lock is set + (if !abort_on_refresh) + @retval True Failure, wait was aborted or thread was killed. +*/ + #define must_wait (global_read_lock && \ (is_not_commit || \ global_read_lock_blocks_commit)) @@ -1201,6 +1243,16 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, bool result= 0, need_exit_cond; DBUG_ENTER("wait_if_global_read_lock"); + /* + If we already have protection against global read lock, + just increment the counter. + */ + if (unlikely(thd->global_read_lock_protection > 0)) + { + if (!abort_on_refresh) + thd->global_read_lock_protection++; + DBUG_RETURN(FALSE); + } /* Assert that we do not own LOCK_open. If we would own it, other threads could not close their tables. This would make a pretty @@ -1237,7 +1289,12 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, result=1; } if (!abort_on_refresh && !result) + { + thd->global_read_lock_protection++; protect_against_global_read_lock++; + DBUG_PRINT("sql_lock", ("protect_against_global_read_lock incr: %u", + protect_against_global_read_lock)); + } /* The following is only true in case of a global read locks (which is rare) and if old_message is set @@ -1250,10 +1307,31 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, } +/** + Release protection against global read lock and restart + global read lock waiters. + + Should only be called if we have protection against global read lock. + + See also "Handling of global read locks" above. + + @param thd Reference to thread. +*/ + void start_waiting_global_read_lock(THD *thd) { bool tmp; DBUG_ENTER("start_waiting_global_read_lock"); + /* + Ignore request if we do not have protection against global read lock. + (Note that this is a violation of the interface contract, hence the assert). + */ + DBUG_ASSERT(thd->global_read_lock_protection > 0); + if (unlikely(thd->global_read_lock_protection == 0)) + DBUG_VOID_RETURN; + /* Decrement local read lock protection counter, return if we still have it */ + if (unlikely(--thd->global_read_lock_protection > 0)) + DBUG_VOID_RETURN; if (unlikely(thd->global_read_lock)) DBUG_VOID_RETURN; (void) pthread_mutex_lock(&LOCK_global_read_lock); @@ -1267,6 +1345,21 @@ void start_waiting_global_read_lock(THD *thd) } +/** + Make global read lock also block commits. + + The scenario is: + - This thread has the global read lock. + - Global read lock blocking of commits is not set. + + See also "Handling of global read locks" above. + + @param thd Reference to thread. + + @retval False Success, global read lock set, commits are blocked. + @retval True Failure, thread was killed. +*/ + bool make_global_read_lock_block_commit(THD *thd) { bool error; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d83b60810ab..d42a06b3811 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -452,6 +452,7 @@ THD::THD() examined_row_count(0), warning_info(&main_warning_info), stmt_da(&main_da), + global_read_lock_protection(0), global_read_lock(0), is_fatal_error(0), transaction_rollback_request(0), diff --git a/sql/sql_class.h b/sql/sql_class.h index f4504bfed0d..889d7c5472b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1874,6 +1874,7 @@ public: ulong rand_saved_seed1, rand_saved_seed2; pthread_t real_id; /* For debugging */ my_thread_id thread_id; + uint global_read_lock_protection;// GRL protection count uint tmp_table, global_read_lock; uint server_status,open_options; enum enum_thread_type system_thread; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 06f29cd6039..211788768f9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1762,7 +1762,6 @@ int mysql_execute_command(THD *thd) { int res= FALSE; - bool need_start_waiting= FALSE; // have protection against global read lock int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ @@ -2039,7 +2038,7 @@ mysql_execute_command(THD *thd) break; if (!thd->locked_tables_mode && lex->protect_against_global_read_lock && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + wait_if_global_read_lock(thd, 0, 1)) break; res= execute_sqlcom_select(thd, all_tables); @@ -2309,10 +2308,9 @@ case SQLCOM_PREPARE: read lock when it succeeds. This needs to be released by start_waiting_global_read_lock(). We protect the normal CREATE TABLE in the same way. That way we avoid that a new table is - created during a gobal read lock. + created during a global read lock. */ - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) { res= 1; goto end_with_restore_list; @@ -2617,8 +2615,7 @@ end_with_restore_list: "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) { res= 1; break; @@ -2852,8 +2849,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (update_precheck(thd, all_tables)) break; - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) goto error; DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); @@ -2891,7 +2887,7 @@ end_with_restore_list: */ if (!thd->locked_tables_mode && lex->sql_command == SQLCOM_UPDATE_MULTI && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + wait_if_global_read_lock(thd, 0, 1)) goto error; res= mysql_multi_update_prepare(thd); @@ -2993,8 +2989,7 @@ end_with_restore_list: if ((res= insert_precheck(thd, all_tables))) break; - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) { res= 1; break; @@ -3033,8 +3028,7 @@ end_with_restore_list: unit->set_limit(select_lex); - if (! thd->locked_tables_mode && - ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) { res= 1; break; @@ -3104,7 +3098,7 @@ end_with_restore_list: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (wait_if_global_read_lock(thd, 0, 1)) goto error; res= mysql_truncate(thd, first_table, 0); break; @@ -3116,8 +3110,7 @@ end_with_restore_list: DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) { res= 1; break; @@ -3137,8 +3130,7 @@ end_with_restore_list: (TABLE_LIST *)thd->lex->auxiliary_table_list.first; multi_delete *del_result; - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) { res= 1; break; @@ -3282,8 +3274,7 @@ end_with_restore_list: if (check_one_table_access(thd, privilege, all_tables)) goto error; - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) goto error; res= mysql_load(thd, lex->exchange, first_table, lex->field_list, @@ -3357,7 +3348,7 @@ end_with_restore_list: FALSE, UINT_MAX, FALSE)) goto error; if (lex->protect_against_global_read_lock && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + wait_if_global_read_lock(thd, 0, 1)) goto error; init_mdl_requests(all_tables); @@ -4575,7 +4566,7 @@ error: res= TRUE; finish: - if (need_start_waiting) + if (thd->global_read_lock_protection > 0) { /* Release the protection against the global read lock and wake diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 341d2d16757..5500bc286b8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1780,16 +1780,16 @@ void write_bin_log(THD *thd, bool clear_error, bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, my_bool drop_temporary) { - bool error= FALSE, need_start_waiting= FALSE; + bool error; Drop_table_error_handler err_handler(thd->get_internal_handler()); + DBUG_ENTER("mysql_rm_table"); /* mark for close and remove all cached entries */ if (!drop_temporary) { - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) DBUG_RETURN(TRUE); } @@ -1797,8 +1797,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0); thd->pop_internal_handler(); - - if (need_start_waiting) + if (thd->global_read_lock_protection > 0) start_waiting_global_read_lock(thd); if (error) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 015e0d4daa1..b3108cae3d9 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -328,7 +328,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) TABLE *table; bool result= TRUE; String stmt_query; - bool need_start_waiting= FALSE; bool lock_upgrade_done= FALSE; MDL_ticket *mdl_ticket= NULL; @@ -386,8 +385,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) LOCK_open is not enough because global read lock is held without holding LOCK_open). */ - if (!thd->locked_tables_mode && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) DBUG_RETURN(TRUE); if (!create) @@ -521,7 +519,7 @@ end: if (thd->locked_tables_mode && tables && lock_upgrade_done) mdl_ticket->downgrade_exclusive_lock(); - if (need_start_waiting) + if (thd->global_read_lock_protection > 0) start_waiting_global_read_lock(thd); if (!result) From 1b5f2b9030e0dc19eaac22de7db429b6a7ba31ae Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 16:13:00 +0100 Subject: [PATCH 139/466] Backport of revno: 2617.68.18 Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB table cause warnings in errlog Concurrent execution of LOCK TABLES ... READ statement and DML statements affecting the same InnoDB table on debug builds of MySQL server might lead to "Found lock of type 6 that is write and read locked" warnings appearing in error log. The problem is that the table-level locking code allows a thread to acquire TL_READ_NO_INSERT lock on a table even if there is another thread which holds TL_WRITE_ALLOW_WRITE lock on the same table. At the same time, the locking code assumes that that such locks are incompatible (for example, see check_locks()). This doesn't lead to any problems other than warnings in error log for debug builds of server since for InnoDB tables TL_READ_NO_INSERT type of lock is only used for LOCK TABLES and for this statement InnoDB also performs its own table-level locking. Unfortunately, the table lock compatibility matrix cannot be updated to disallow TL_READ_NO_INSERT when another thread holds TL_WRITE_ALLOW_WRITE without causing starvation of LOCK TABLE READ in InnoDB under high write load. This patch therefore contains no code changes. The issue will be fixed later when LOCK TABLE READ has been updated to not use table locks. This bug will therefore be marked as "To be fixed later". Code comment in thr_lock.c expanded to clarify the issue and a test case based on the bug description added to innodb_mysql_lock.test. Note that a global suppression rule has been added to both MTR v1 and v2 for the "Found lock of type 6 that is write and read locked" warning. These suppression rules must be removed once this bug is properly fixed. --- mysql-test/include/mtr_warnings.sql | 8 ++++ mysql-test/lib/v1/mtr_report.pl | 3 ++ mysql-test/r/innodb_mysql_lock.result | 32 ++++++++++++++ mysql-test/t/innodb_mysql_lock.test | 63 +++++++++++++++++++++++++++ mysys/thr_lock.c | 32 +++++++++++--- 5 files changed, 131 insertions(+), 7 deletions(-) diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index e03e83efac2..14f1dd97830 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -186,6 +186,14 @@ INSERT INTO global_suppressions VALUES (": The MySQL server is running with the --secure-backup-file-priv option so it cannot execute this statement"), ("Slave: Unknown table 't1' Error_code: 1051"), + /* + BUG#42147 - Concurrent DML and LOCK TABLE ... READ for InnoDB + table cause warnings in errlog + Note: This is a temporary suppression until Bug#42147 can be + fixed properly. See bug page for more information. + */ + ("Found lock of type 6 that is write and read locked"), + ("THE_LAST_SUPPRESSION")|| diff --git a/mysql-test/lib/v1/mtr_report.pl b/mysql-test/lib/v1/mtr_report.pl index 3c78c3ca064..36aba983c34 100644 --- a/mysql-test/lib/v1/mtr_report.pl +++ b/mysql-test/lib/v1/mtr_report.pl @@ -376,6 +376,9 @@ sub mtr_report_stats ($) { /Slave: Can't DROP 'c7'.* 1091/ or /Slave: Key column 'c6'.* 1072/ or + # Warnings generated until bug#42147 is properly resolved + /Found lock of type 6 that is write and read locked/ or + # rpl_idempotency.test produces warnings for the slave. ($testname eq 'rpl.rpl_idempotency' and (/Slave: Can\'t find record in \'t1\' Error_code: 1032/ or diff --git a/mysql-test/r/innodb_mysql_lock.result b/mysql-test/r/innodb_mysql_lock.result index 147267d5550..374f67358eb 100644 --- a/mysql-test/r/innodb_mysql_lock.result +++ b/mysql-test/r/innodb_mysql_lock.result @@ -21,4 +21,36 @@ INSERT INTO t1 VALUES (2); ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # Cleanup commit; +set @@autocommit=1; commit; +set @@autocommit=1; +set @@autocommit=1; +# +# Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB +# table cause warnings in errlog +# +# +# Note that this test for now relies on a global suppression of +# the warning "Found lock of type 6 that is write and read locked" +# This suppression rule can be removed once Bug#42147 is properly +# fixed. See bug page for more info. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i INT) engine= innodb; +# Connection 2 +# Get user-level lock +SELECT get_lock('bug42147_lock', 60); +get_lock('bug42147_lock', 60) +1 +# Connection 1 +INSERT INTO t1 SELECT get_lock('bug42147_lock', 60); +# Connection 2 +LOCK TABLES t1 READ; +SELECT release_lock('bug42147_lock'); +release_lock('bug42147_lock') +1 +# Connection 1 +# Connection 2 +UNLOCK TABLES; +# Connection 1 +DROP TABLE t1; diff --git a/mysql-test/t/innodb_mysql_lock.test b/mysql-test/t/innodb_mysql_lock.test index daee94bedb5..c8c38cd1ab1 100644 --- a/mysql-test/t/innodb_mysql_lock.test +++ b/mysql-test/t/innodb_mysql_lock.test @@ -1,5 +1,8 @@ -- source include/have_innodb.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + --echo # --echo # Bug #22876 Four-way deadlock --echo # @@ -51,8 +54,68 @@ INSERT INTO t1 VALUES (2); connection con2; --reap commit; +set @@autocommit=1; connection con1; commit; +set @@autocommit=1; connection con3; --reap +set @@autocommit=1; connection default; + +disconnect con1; +disconnect con3; + +--echo # +--echo # Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB +--echo # table cause warnings in errlog +--echo # + +--echo # +--echo # Note that this test for now relies on a global suppression of +--echo # the warning "Found lock of type 6 that is write and read locked" +--echo # This suppression rule can be removed once Bug#42147 is properly +--echo # fixed. See bug page for more info. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i INT) engine= innodb; + +--echo # Connection 2 +--echo # Get user-level lock +connection con2; +SELECT get_lock('bug42147_lock', 60); + +--echo # Connection 1 +connection default; +--send INSERT INTO t1 SELECT get_lock('bug42147_lock', 60) + +--echo # Connection 2 +connection con2; +let $wait_condition= + SELECT COUNT(*) > 0 FROM information_schema.processlist + WHERE state = 'User lock' + AND info = 'INSERT INTO t1 SELECT get_lock(\'bug42147_lock\', 60)'; +--source include/wait_condition.inc +LOCK TABLES t1 READ; +SELECT release_lock('bug42147_lock'); + +--echo # Connection 1 +connection default; +--reap + +--echo # Connection 2 +connection con2; +UNLOCK TABLES; + +--echo # Connection 1 +connection default; +disconnect con2; +DROP TABLE t1; + +# Check that all connections opened by test cases in this file are really +# gone so execution of other tests won't be affected by their presence. +--source include/wait_until_count_sessions.inc diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 6433c04a96f..a3f3e9ee080 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -540,13 +540,31 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, /* Request for READ lock */ if (lock->write.data) { - /* We can allow a read lock even if there is already a write lock - on the table in one the following cases: - - This thread alread have a write lock on the table - - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED - and the read lock is TL_READ_HIGH_PRIORITY or TL_READ - - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE - and the read lock is not TL_READ_NO_INSERT + /* + We can allow a read lock even if there is already a + write lock on the table if they are owned by the same + thread or if they satisfy the following lock + compatibility matrix: + + Request + /------- + H|++++ WRITE_ALLOW_WRITE + e|+++- WRITE_ALLOW_READ + l|+++- WRITE_CONCURRENT_INSERT + d|++++ WRITE_DELAYED + |||| + |||\= READ_NO_INSERT + ||\ = READ_HIGH_PRIORITY + |\ = READ_WITH_SHARED_LOCKS + \ = READ + + + = Request can be satisified. + - = Request cannot be satisified. + + READ_NO_INSERT and WRITE_ALLOW_WRITE should in principle + be incompatible. However this will cause starvation of + LOCK TABLE READ in InnoDB under high write load. + See Bug#42147 for more information. */ DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx", From 09c3bb13d4515484afdb9ed25241f4c2c1a6aac3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 9 Dec 2009 16:34:52 +0100 Subject: [PATCH 140/466] Remove -Wl,--as-needed option, broken on Fedora --- configure.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/configure.cmake b/configure.cmake index 305f4dde454..d1fc590a0cb 100644 --- a/configure.cmake +++ b/configure.cmake @@ -160,8 +160,6 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") ${CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS} ) ENDFOREACH() - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") SET(LINK_FLAG_NO_UNDEFINED "--Wl,--no-undefined") ENDIF() From d8af2fe42ce9670e2a8e19ac66b4cd141f86d008 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 18:48:42 +0300 Subject: [PATCH 141/466] Backport of: ------------------------------------------------------------ revno: 2617.68.7 committer: Dmitry Lenev branch nick: mysql-next-bg46044 timestamp: Thu 2009-08-27 10:22:17 +0400 message: Fix for bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY FOR UPDATE". Deadlock occured when during execution of query to I_S we tried to open a table or its .FRM in order to get information about it and had to wait because we have encountered exclusive metadata lock on this table held by a DDL operation from another connection which in its turn waited for some resource currently owned by connection executing this I_S query. For example, this might have happened if one under LOCK TABLES executed I_S query targeted to particular table (which was not among locked) and also concurrently tried to create this table using CREATE TABLE SELECT which had to wait for one of tables locked by the first connection. Another situation in which deadlock might have occured is when I_S query, which was executed as part of transaction, tried to get information about table which just has been dropped by concurrent DROP TABLES executed under LOCK TABLES and this DROP TABLES for its completion also had to wait transaction from the first connection. This problem stemmed from the fact that opening of tables/.FRMs for I_S filling is happening outside of connection's main MDL_context so code which tries to detect deadlocks due to conflicting metadata locks doesn't work in this case. Indeed, this led to deadlocks when during I_S filling we tried to wait for conflicting metadata lock to go away, while its owner was waiting for some resource held by connection executing I_S query. This patch solves this problem by avoiding waiting in such situation. Instead we skip this table and produce warning that information about it was omitted from I_S due to concurrent DDL operation. We still wait for conflicting metadata lock to go away when it is known that deadlock is not possible (i.e. when connection executing I_S query does not hold any metadata or table-level locks). Basically, we apply our standard deadlock avoidance technique for metadata locks to the process of filling of I_S tables but replace ER_LOCK_DEADLOCK error with a warning. Note that this change is supposed to be safe for 'mysqldump' since the only its mode which is affected by this change is --single-transaction mode is not safe in the presence of concurrent DDL anyway (and this fact is documented). Other modes are unaffected because they either use SHOW TABLES/SELECT * FROM I_S.TABLE_NAMES which do not take any metadata locks in the process of I_S table filling and thus cannot skip tables or execute I_S queries for tables which were previously locked by LOCK TABLES (or in the presence of global read lock) which excludes possibility of encountering conflicting metadata lock. --- mysql-test/r/mdl_sync.result | 111 +++++++++++++++++++ mysql-test/t/mdl_sync.test | 202 +++++++++++++++++++++++++++++++++++ sql/mysql_priv.h | 2 + sql/share/errmsg-utf8.txt | 5 +- sql/share/errmsg.txt | 2 + sql/sql_base.cc | 5 +- sql/sql_show.cc | 84 ++++++++++++--- 7 files changed, 391 insertions(+), 20 deletions(-) diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 36451985a86..f63179b893a 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -62,3 +62,114 @@ unlock tables; # Reap INSERT. # Clean-up. drop tables t1, t2, t3, t5; +# +# Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY +# FOR UPDATE" +# +drop tables if exists t1, t2; +create table t1 (i int); +# Let us check that we won't deadlock if during filling +# of I_S table we encounter conflicting metadata lock +# which owner is in its turn waiting for our connection. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# First let us check that SHOW FIELDS/DESCRIBE doesn't +# gets blocked and emits and error. +show fields from t2; +ERROR HY000: Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement +# Now test for I_S query which reads only .FRMs. +# +# Query below should only emit a warning. +select column_name from information_schema.columns +where table_schema='test' and table_name='t2'; +column_name +Warnings: +Warning 1652 Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement +# Finally, test for I_S query which does full-blown table open. +# +# Query below should not be blocked. Warning message should be +# stored in the 'table_comment' column. +select table_name, table_type, auto_increment, table_comment +from information_schema.tables where table_schema='test' and table_name='t2'; +table_name table_type auto_increment table_comment +t2 BASE TABLE NULL Table 'test'.'t2' was skipped since its definition is being modified by concurre +# Switching to connection 'default'. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +drop table t2; +# +# Let us also check that queries to I_S wait for conflicting metadata +# locks to go away instead of skipping table with a warning in cases +# when deadlock is not possible. This is a nice thing from compatibility +# and ease of use points of view. +# +# We check same three queries to I_S in this new situation. +# Switching to connection 'con46044_2'. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# Let us check that SHOW FIELDS/DESCRIBE gets blocked. +# Sending: +show fields from t2;; +# Switching to connection 'con46044_2'. +# Wait until SHOW FIELDS gets blocked. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +# Switching to connection 'default'. +# Reaping SHOW FIELDS ... +Field Type Null Key Default Extra +i int(11) YES NULL +drop table t2; +# Switching to connection 'con46044_2'. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# Check that I_S query which reads only .FRMs gets blocked. +# Sending: +select column_name from information_schema.columns where table_schema='test' and table_name='t2';; +# Switching to connection 'con46044_2'. +# Wait until SELECT COLUMN_NAME FROM I_S.COLUMNS gets blocked. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +# Switching to connection 'default'. +# Reaping SELECT COLUMN_NAME FROM I_S.COLUMNS +column_name +i +drop table t2; +# Switching to connection 'con46044_2'. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# Finally, check that I_S query which does full-blown table open +# also gets blocked. +# Sending: +select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t2';; +# Switching to connection 'con46044_2'. +# Wait until SELECT ... FROM I_S.TABLES gets blocked. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +# Switching to connection 'default'. +# Reaping SELECT ... FROM I_S.TABLES +table_name table_type auto_increment table_comment +t2 BASE TABLE NULL +drop table t2; +# Switching to connection 'default'. +# Clean-up. +drop table t1; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 124e76bbc1f..cc03cc67f95 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -141,6 +141,208 @@ disconnect con2root; drop tables t1, t2, t3, t5; +--echo # +--echo # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY +--echo # FOR UPDATE" +--echo # +--disable_warnings +drop tables if exists t1, t2; +--enable_warnings +connect (con46044, localhost, root,,); +connect (con46044_2, localhost, root,,); +connection default; +create table t1 (i int); + +--echo # Let us check that we won't deadlock if during filling +--echo # of I_S table we encounter conflicting metadata lock +--echo # which owner is in its turn waiting for our connection. +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # First let us check that SHOW FIELDS/DESCRIBE doesn't +--echo # gets blocked and emits and error. +--error ER_WARN_I_S_SKIPPED_TABLE +show fields from t2; + +--echo # Now test for I_S query which reads only .FRMs. +--echo # +--echo # Query below should only emit a warning. +select column_name from information_schema.columns + where table_schema='test' and table_name='t2'; + +--echo # Finally, test for I_S query which does full-blown table open. +--echo # +--echo # Query below should not be blocked. Warning message should be +--echo # stored in the 'table_comment' column. +select table_name, table_type, auto_increment, table_comment + from information_schema.tables where table_schema='test' and table_name='t2'; + +--echo # Switching to connection 'default'. +connection default; +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap +drop table t2; + +--echo # +--echo # Let us also check that queries to I_S wait for conflicting metadata +--echo # locks to go away instead of skipping table with a warning in cases +--echo # when deadlock is not possible. This is a nice thing from compatibility +--echo # and ease of use points of view. +--echo # +--echo # We check same three queries to I_S in this new situation. + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # Let us check that SHOW FIELDS/DESCRIBE gets blocked. +--echo # Sending: +--send show fields from t2; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +--echo # Wait until SHOW FIELDS gets blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "show fields from t2"; +--source include/wait_condition.inc + +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap + +--echo # Switching to connection 'default'. +connection default; +--echo # Reaping SHOW FIELDS ... +--reap +drop table t2; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # Check that I_S query which reads only .FRMs gets blocked. +--echo # Sending: +--send select column_name from information_schema.columns where table_schema='test' and table_name='t2'; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +--echo # Wait until SELECT COLUMN_NAME FROM I_S.COLUMNS gets blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info like "select column_name from information_schema.columns%"; +--source include/wait_condition.inc + +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap + +--echo # Switching to connection 'default'. +connection default; +--echo # Reaping SELECT COLUMN_NAME FROM I_S.COLUMNS +--reap +drop table t2; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # Finally, check that I_S query which does full-blown table open +--echo # also gets blocked. +--echo # Sending: +--send select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t2'; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +--echo # Wait until SELECT ... FROM I_S.TABLES gets blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info like "select table_name, table_type, auto_increment, table_comment from information_schema.tables%"; +--source include/wait_condition.inc + +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap + +--echo # Switching to connection 'default'. +connection default; +--echo # Reaping SELECT ... FROM I_S.TABLES +--reap +drop table t2; + +--echo # Switching to connection 'default'. +connection default; +--echo # Clean-up. +disconnect con46044; +disconnect con46044_2; +drop table t1; + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e61c9a923c3..03a4462f199 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2071,6 +2071,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, #define MYSQL_OPEN_GET_NEW_TABLE 0x0080 /** Don't look up the table in the list of temporary tables. */ #define MYSQL_OPEN_SKIP_TEMPORARY 0x0100 +/** Fail instead of waiting when conficting metadata lock is discovered. */ +#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0200 /** Please refer to the internals manual. */ #define MYSQL_OPEN_REOPEN (MYSQL_LOCK_IGNORE_FLUSH |\ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 4260efdeb56..70fae60d051 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6236,12 +6236,13 @@ ER_COND_ITEM_TOO_LONG eng "Data too long for condition item '%s'" ER_UNKNOWN_LOCALE - eng "Unknown locale: '%-.64s'" - + eng "Unknown locale: '%-.64s'" ER_SLAVE_IGNORE_SERVER_IDS eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id" ER_QUERY_CACHE_DISABLED eng "Query cache is disabled; restart the server with query_cache_type=1 to enable it" +ER_WARN_I_S_SKIPPED_TABLE + eng "Table '%s'.'%s' was skipped since its definition is being modified by concurrent DDL statement" ER_SAME_NAME_PARTITION_FIELD eng "Duplicate partition field name '%-.192s'" ER_PARTITION_COLUMN_LIST_ERROR diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 93385292c24..0f8a43407fa 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6242,6 +6242,8 @@ ER_UNKNOWN_LOCALE ER_SLAVE_IGNORE_SERVER_IDS eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id" +ER_WARN_I_S_SKIPPED_TABLE + eng "Table '%s'.'%s' was skipped since its definition is being modified by concurrent DDL statement" ER_SAME_NAME_PARTITION_FIELD eng "Duplicate partition field name '%-.192s'" ER_PARTITION_COLUMN_LIST_ERROR diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f9514a055ca..d1ce4aefb1f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2350,7 +2350,10 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, return 1; if (mdl_request->ticket == NULL) { - (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT); + if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT) + my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0), table_list->db, table_list->table_name); + else + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT); return 1; } } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d346bae5258..fe941321dcd 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2867,6 +2867,10 @@ make_table_name_list(THD *thd, List *table_names, LEX *lex, @param[in] thd thread handler @param[in] tables TABLE_LIST for I_S table @param[in] schema_table pointer to I_S structure + @param[in] can_deadlock Indicates that deadlocks are possible + due to metadata locks, so to avoid + them we should not wait in case if + conflicting lock is present. @param[in] open_tables_state_backup pointer to Open_tables_state object which is used to save|restore original status of variables related to @@ -2880,6 +2884,7 @@ make_table_name_list(THD *thd, List *table_names, LEX *lex, static int fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, ST_SCHEMA_TABLE *schema_table, + bool can_deadlock, Open_tables_state *open_tables_state_backup) { LEX *lex= thd->lex; @@ -2908,7 +2913,9 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, */ lex->sql_command= SQLCOM_SHOW_FIELDS; res= open_normal_and_derived_tables(thd, show_table_list, - MYSQL_LOCK_IGNORE_FLUSH); + (MYSQL_LOCK_IGNORE_FLUSH | + (can_deadlock ? + MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0))); lex->sql_command= save_sql_command; /* get_all_tables() returns 1 on failure and 0 on success thus @@ -3047,12 +3054,16 @@ uint get_table_open_method(TABLE_LIST *tables, /** - Acquire high priority share metadata lock on a table. + Try acquire high priority share metadata lock on a table (with + optional wait for conflicting locks to go away). @param thd Thread context. @param mdl_request Pointer to memory to be used for MDL_request object for a lock request. @param table Table list element for the table + @param can_deadlock Indicates that deadlocks are possible due to + metadata locks, so to avoid them we should not + wait in case if conflicting lock is present. @note This is an auxiliary function to be used in cases when we want to access table's description by looking up info in TABLE_SHARE without @@ -3060,19 +3071,21 @@ uint get_table_open_method(TABLE_LIST *tables, @note This function assumes that there are no other metadata lock requests in the current metadata locking context. - @retval FALSE Success + @retval FALSE No error, if lock was obtained TABLE_LIST::mdl_request::ticket + is set to non-NULL value. @retval TRUE Some error occured (probably thread was killed). */ static bool -acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table) +try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, + bool can_deadlock) { bool error; table->mdl_request.init(MDL_TABLE, table->db, table->table_name, MDL_SHARED_HIGH_PRIO); while (!(error= thd->mdl_context.try_acquire_shared_lock(&table->mdl_request)) && - !table->mdl_request.ticket) + !table->mdl_request.ticket && !can_deadlock) { MDL_request_list mdl_requests; mdl_requests.push_front(&table->mdl_request); @@ -3092,6 +3105,10 @@ acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table) @param[in] db_name database name @param[in] table_name table name @param[in] schema_table_idx I_S table index + @param[in] can_deadlock Indicates that deadlocks are possible + due to metadata locks, so to avoid + them we should not wait in case if + conflicting lock is present. @return Operation status @retval 0 Table is processed and we can continue @@ -3104,13 +3121,14 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, ST_SCHEMA_TABLE *schema_table, LEX_STRING *db_name, LEX_STRING *table_name, - enum enum_schema_tables schema_table_idx) + enum enum_schema_tables schema_table_idx, + bool can_deadlock) { TABLE_SHARE *share; TABLE tbl; TABLE_LIST table_list; uint res= 0; - int error; + int not_used; char key[MAX_DBKEY_LENGTH]; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; @@ -3143,7 +3161,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, simply obtaining internal lock of data-dictionary (ATM it is LOCK_open) instead of obtaning full-blown metadata lock. */ - if (acquire_high_prio_shared_mdl_lock(thd, &table_list)) + if (try_acquire_high_prio_shared_mdl_lock(thd, &table_list, can_deadlock)) { /* Some error occured (most probably we have been killed while @@ -3153,14 +3171,30 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, return 1; } + if (! table_list.mdl_request.ticket) + { + /* + We are in situation when we have encountered conflicting metadata + lock and deadlocks can occur due to waiting for it to go away. + So instead of waiting skip this table with an appropriate warning. + */ + DBUG_ASSERT(can_deadlock); + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_I_S_SKIPPED_TABLE, + ER(ER_WARN_I_S_SKIPPED_TABLE), + table_list.db, table_list.table_name); + return 0; + } + key_length= create_table_def_key(thd, key, &table_list, 0); pthread_mutex_lock(&LOCK_open); share= get_table_share(thd, &table_list, key, - key_length, OPEN_VIEW, &error); + key_length, OPEN_VIEW, ¬_used); if (!share) { res= 0; - goto err_unlock; + goto end_unlock; } if (share->is_view) @@ -3169,7 +3203,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, { /* skip view processing */ res= 0; - goto err_share; + goto end_share; } else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL) { @@ -3178,7 +3212,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, open_normal_and_derived_tables() */ res= 1; - goto err_share; + goto end_share; } } @@ -3194,15 +3228,16 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, res= schema_table->process_table(thd, &table_list, table, res, db_name, table_name); closefrm(&tbl, true); - goto err_unlock; + goto end_unlock; } -err_share: +end_share: release_table_share(share); -err_unlock: +end_unlock: pthread_mutex_unlock(&LOCK_open); +end: thd->mdl_context.release_lock(table_list.mdl_request.ticket); thd->clear_error(); return res; @@ -3254,8 +3289,20 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) Security_context *sctx= thd->security_ctx; #endif uint table_open_method; + bool can_deadlock; DBUG_ENTER("get_all_tables"); + /* + In cases when SELECT from I_S table being filled by this call is + part of statement which also uses other tables or is being executed + under LOCK TABLES or is part of transaction which also uses other + tables waiting for metadata locks which happens below might result + in deadlocks. + To avoid them we don't wait if conflicting metadata lock is + encountered and skip table with emitting an appropriate warning. + */ + can_deadlock= thd->mdl_context.has_locks(); + lex->view_prepare_mode= TRUE; lex->reset_n_backup_query_tables_list(&query_tables_list_backup); @@ -3275,6 +3322,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (lsel && lsel->table_list.first) { error= fill_schema_show_cols_or_idxs(thd, tables, schema_table, + can_deadlock, &open_tables_state_backup); goto err; } @@ -3390,7 +3438,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) !with_i_schema) { if (!fill_schema_table_from_frm(thd, table, schema_table, db_name, - table_name, schema_table_idx)) + table_name, schema_table_idx, + can_deadlock)) continue; } @@ -3415,7 +3464,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) show_table_list->i_s_requested_object= schema_table->i_s_requested_object; res= open_normal_and_derived_tables(thd, show_table_list, - MYSQL_LOCK_IGNORE_FLUSH); + (MYSQL_LOCK_IGNORE_FLUSH | + (can_deadlock ? MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0))); lex->sql_command= save_sql_command; /* XXX: show_table_list has a flag i_is_requested, From 2c538778957b81ea70de57501eee61403c284e91 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 18:56:34 +0300 Subject: [PATCH 142/466] Backport of: ------------------------------------------------------------ revno: 2617.68.10 committer: Dmitry Lenev branch nick: mysql-next-bg46673 timestamp: Tue 2009-09-01 19:57:05 +0400 message: Fix for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK and DML". Deadlocks occured when one concurrently executed transactions with several statements modifying data and FLUSH TABLES WITH READ LOCK statement or SET READ_ONLY=1 statement. These deadlocks were introduced by the patch for WL 4284: "Transactional DDL locking"/Bug 989: "If DROP TABLE while there's an active transaction, wrong binlog order" which has changed FLUSH TABLES WITH READ LOCK/SET READ_ONLY=1 to wait for pending transactions. What happened was that FLUSH TABLES WITH READ LOCK blocked all further statements changing tables by setting global_read_lock global variable and has started waiting for all pending transactions to complete. Then one of those transactions tried to executed DML, detected that global_read_lock non-zero and tried to wait until global read lock will be released (i.e. global_read_lock becomes 0), indeed, this led to a deadlock. Proper solution for this problem should probably involve full integration of global read lock with metadata locking subsystem (which will allow to implement waiting for pending transactions without blocking DML in them). But since it requires significant changes another, short-term solution for the problem is implemented in this patch. Basically, this patch restores behavior of FLUSH TABLES WITH READ LOCK/ SET READ_ONLY=1 before the patch for WL 4284/bug 989. By ensuring that extra references to TABLE_SHARE are not stored for active metadata locks it changes these statements not to wait for pending transactions. As result deadlock is eliminated. Note that this does not change the fact that active FLUSH TABLES WITH READ LOCK lock or SET READ_ONLY=1 prevent modifications to tables as they also block transaction commits. --- mysql-test/r/flush_block_commit.result | 2 +- mysql-test/r/mdl_sync.result | 21 +++++++++++++++ mysql-test/r/read_only_innodb.result | 6 +++-- mysql-test/t/flush_block_commit.test | 17 ++++++------ mysql-test/t/mdl_sync.test | 37 ++++++++++++++++++++++++++ mysql-test/t/read_only_innodb.test | 14 ++-------- sql/sql_base.cc | 6 +++++ 7 files changed, 79 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index da09d07b813..7062d05c2d7 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -17,9 +17,9 @@ COMMIT; # Verify that 'con1' was blocked and data did not move. SELECT * FROM t1; a -1 UNLOCK TABLES; # Switch to connection con1 +# Reaping COMMIT # Switch to connection con1 BEGIN; SELECT * FROM t1 FOR UPDATE; diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index f63179b893a..d409157a70b 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -173,3 +173,24 @@ drop table t2; # Switching to connection 'default'. # Clean-up. drop table t1; +# +# Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK +# and DML". +# +drop tables if exists t1; +create table t1 (i int); +# Switching to connection 'con46673'. +begin; +insert into t1 values (1); +# Switching to connection 'default'. +# Statement below should not get blocked. And if after some +# changes to code it is there should not be a deadlock between +# it and transaction from connection 'con46673'. +flush tables with read lock; +unlock tables; +# Switching to connection 'con46673'. +delete from t1 where i = 1; +commit; +# Switching to connection 'default'. +# Clean-up +drop table t1; diff --git a/mysql-test/r/read_only_innodb.result b/mysql-test/r/read_only_innodb.result index 4cba98900a1..690de085bf9 100644 --- a/mysql-test/r/read_only_innodb.result +++ b/mysql-test/r/read_only_innodb.result @@ -7,10 +7,12 @@ insert into table_11733 values(11733); set global read_only=1; select @@global.read_only; @@global.read_only -0 +1 select * from table_11733 ; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +a +11733 COMMIT; +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement set global read_only=0; drop table table_11733 ; drop user test@localhost; diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test index 98bca8cdad7..0b3bede1684 100644 --- a/mysql-test/t/flush_block_commit.test +++ b/mysql-test/t/flush_block_commit.test @@ -29,26 +29,25 @@ BEGIN; INSERT INTO t1 VALUES(1); --echo # Switch to connection con2 connection con2; ---send FLUSH TABLES WITH READ LOCK +FLUSH TABLES WITH READ LOCK; --echo # Switch to connection con1 connection con1; --echo # Sending: -COMMIT; +--send COMMIT --echo # Switch to connection con2 connection con2; ---reap --echo # Wait until COMMIT gets blocked. -#let $wait_condition= -# select count(*) = 1 from information_schema.processlist -# where state = "Waiting for release of readlock" and info = "COMMIT"; -#--source include/wait_condition.inc +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for release of readlock" and info = "COMMIT"; +--source include/wait_condition.inc --echo # Verify that 'con1' was blocked and data did not move. SELECT * FROM t1; UNLOCK TABLES; --echo # Switch to connection con1 connection con1; -#--echo # Reaping COMMIT -#--reap +--echo # Reaping COMMIT +--reap # No deadlock ? diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index cc03cc67f95..d50c056fda3 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -343,6 +343,43 @@ disconnect con46044; disconnect con46044_2; drop table t1; + +--echo # +--echo # Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK +--echo # and DML". +--echo # +--disable_warnings +drop tables if exists t1; +--enable_warnings +connect (con46673, localhost, root,,); +connection default; +create table t1 (i int); + +--echo # Switching to connection 'con46673'. +connection con46673; +begin; +insert into t1 values (1); + +--echo # Switching to connection 'default'. +connection default; +--echo # Statement below should not get blocked. And if after some +--echo # changes to code it is there should not be a deadlock between +--echo # it and transaction from connection 'con46673'. +flush tables with read lock; +unlock tables; + +--echo # Switching to connection 'con46673'. +connection con46673; +delete from t1 where i = 1; +commit; + +--echo # Switching to connection 'default'. +connection default; +--echo # Clean-up +disconnect con46673; +drop table t1; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/read_only_innodb.test b/mysql-test/t/read_only_innodb.test index 98e704e25c7..9e001f2b997 100644 --- a/mysql-test/t/read_only_innodb.test +++ b/mysql-test/t/read_only_innodb.test @@ -16,8 +16,6 @@ DROP TABLE IF EXISTS table_11733 ; grant CREATE, SELECT, DROP on *.* to test@localhost; connect (con1,localhost,test,,test); -connect (con2,localhost,root,,); - connection default; set global read_only=0; @@ -30,22 +28,15 @@ BEGIN; insert into table_11733 values(11733); connection default; -send set global read_only=1; - -connection con2; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Flushing tables" and info = "set global read_only=1"; ---source include/wait_condition.inc +set global read_only=1; connection con1; select @@global.read_only; ---error ER_LOCK_DEADLOCK select * from table_11733 ; +--error ER_OPTION_PREVENTS_STATEMENT COMMIT; connection default; -reap; set global read_only=0; drop table table_11733 ; drop user test@localhost; @@ -90,6 +81,5 @@ DROP TABLE t1; DROP USER test@localhost; disconnect con1; -disconnect con2; --echo echo End of 5.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d1ce4aefb1f..ad618eb59fe 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2648,7 +2648,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(FALSE); } +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL if (!(share= (TABLE_SHARE *) mdl_ticket->get_cached_object())) +#endif { if (!(share= get_table_share_with_create(thd, table_list, key, key_length, OPEN_VIEW, @@ -2714,13 +2716,16 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) goto err_unlock; +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL /* We are going to to store extra reference to the share in MDL-subsystem so we need to increase reference counter; */ reference_table_share(share); mdl_ticket->set_cached_object(share, table_share_release_hook); +#endif } +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL else { if (table_list->view) @@ -2741,6 +2746,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, */ reference_table_share(share); } +#endif if (share->version != refresh_version) { From 10db8e79c58ebb646ad777ae8288d6276698b545 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 19:00:46 +0300 Subject: [PATCH 143/466] ------------------------------------------------------------ revno: 2617.68.23 committer: Dmitry Lenev branch nick: mysql-next-bg-pre1 timestamp: Wed 2009-09-16 09:34:42 +0400 message: Pre-requisite patch for fixing bug #30977 "Concurrent statement using stored function and DROP FUNCTION breaks SBR". CREATE TABLE SELECT statements take exclusive metadata lock on table being created. Invariant of metadata locking subsystem states that such lock should be taken before taking any kind of shared locks. Once metadata locks on stored routines are introduced statements like "CREATE TABLE ... SELECT f1()" will break this invariant by taking shared locks on routines before exclusive lock on target table. To avoid this, open_tables() is reworked to process tables which are directly used by the statement before stored routines are processed. --- sql/sql_base.cc | 724 +++++++++++++++++++++++++----------------------- 1 file changed, 380 insertions(+), 344 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ad618eb59fe..20353f0c9f8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3753,16 +3753,13 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) /* - Perform steps of prelocking algorithm for elements of the - prelocking set other than tables. E.g. cache routines and, if - prelocking strategy prescribes so, extend the prelocking set with - tables and routines used by them. + Handle element of prelocking set other than table. E.g. cache routine + and, if prelocking strategy prescribes so, extend the prelocking set + with tables and routines used by it. @param[in] thd Thread context. @param[in] prelocking_ctx Prelocking context. - @param[in] start First element in the list representing - subset of the prelocking set to be - processed. + @param[in] rt Element of prelocking set to be processed. @param[in] prelocking_strategy Strategy which specifies how the prelocking set should be extended when one of its elements is processed. @@ -3775,46 +3772,293 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) */ static bool -open_routines(THD *thd, Query_tables_list *prelocking_ctx, - Sroutine_hash_entry *start, - Prelocking_strategy *prelocking_strategy, - bool *need_prelocking) +open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, + Prelocking_strategy *prelocking_strategy, + bool *need_prelocking) { - DBUG_ENTER("open_routines"); + int type= rt->key.str[0]; - for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) + DBUG_ENTER("open_and_process_routine"); + + switch (type) { - int type= rt->key.str[0]; - - switch (type) + case TYPE_ENUM_FUNCTION: + case TYPE_ENUM_PROCEDURE: { - case TYPE_ENUM_FUNCTION: - case TYPE_ENUM_PROCEDURE: + sp_name name(thd, rt->key.str, rt->key.length); + sp_head *sp; + + if (sp_cache_routine(thd, type, &name, &sp)) + DBUG_RETURN(TRUE); + + if (sp) { - sp_name name(thd, rt->key.str, rt->key.length); - sp_head *sp; - - if (sp_cache_routine(thd, type, &name, &sp)) - DBUG_RETURN(TRUE); - - if (sp) - { - prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, - need_prelocking); - } + prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, + need_prelocking); } - break; - case TYPE_ENUM_TRIGGER: - break; - default: - /* Impossible type value. */ - DBUG_ASSERT(0); } + break; + case TYPE_ENUM_TRIGGER: + break; + default: + /* Impossible type value. */ + DBUG_ASSERT(0); } DBUG_RETURN(FALSE); } +/** + Handle table list element by obtaining metadata lock, opening table or view + and, if prelocking strategy prescribes so, extending the prelocking set with + tables and routines used by it. + + @param[in] thd Thread context. + @param[in] lex LEX structure for statement. + @param[in] tables Table list element to be processed. + @param[in,out] counter Number of tables which are open. + @param[in] flags Bitmap of flags to modify how the tables + will be open, see open_table() description + for details. + @param[in] prelocking_strategy Strategy which specifies how the + prelocking set should be extended + when table or view is processed. + @param[in] has_prelocking_list Indicates that prelocking set/list for + this statement has already been built. + @param[in] ot_ctx Context used to recover from a failed + open_table() attempt. + @param[in] new_frm_mem Temporary MEM_ROOT to be used for + parsing .FRMs for views. + + @retval FALSE Success. + @retval TRUE Error, reported unless there is a chance to recover from it. +*/ + +static bool +open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, + uint *counter, uint flags, + Prelocking_strategy *prelocking_strategy, + bool has_prelocking_list, + Open_table_context *ot_ctx, + MEM_ROOT *new_frm_mem) +{ + bool error= FALSE; + bool safe_to_ignore_table= FALSE; + DBUG_ENTER("open_and_process_table"); + + /* + Ignore placeholders for derived tables. After derived tables + processing, link to created temporary table will be put here. + If this is derived table for view then we still want to process + routines used by this view. + */ + if (tables->derived) + { + if (!tables->view) + goto end; + /* + We restore view's name and database wiped out by derived tables + processing and fall back to standard open process in order to + obtain proper metadata locks and do other necessary steps like + stored routine processing. + */ + tables->db= tables->view_db.str; + tables->db_length= tables->view_db.length; + tables->table_name= tables->view_name.str; + tables->table_name_length= tables->view_name.length; + } + /* + If this TABLE_LIST object is a placeholder for an information_schema + table, create a temporary table to represent the information_schema + table in the query. Do not fill it yet - will be filled during + execution. + */ + if (tables->schema_table) + { + /* + If this information_schema table is merged into a mergeable + view, ignore it for now -- it will be filled when its respective + TABLE_LIST is processed. This code works only during re-execution. + */ + if (tables->view) + goto process_view_routines; + if (!mysql_schema_table(thd, lex, tables) && + !check_and_update_table_version(thd, tables, tables->table->s)) + { + goto end; + } + error= TRUE; + goto end; + } + DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: %p", + tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here + (*counter)++; + + /* Not a placeholder: must be a base table or a view. Let us open it. */ + DBUG_ASSERT(!tables->table); + + if (tables->prelocking_placeholder) + { + /* + For the tables added by the pre-locking code, attempt to open + the table but fail silently if the table does not exist. + The real failure will occur when/if a statement attempts to use + that table. + */ + Prelock_error_handler prelock_handler; + thd->push_internal_handler(& prelock_handler); + error= open_table(thd, tables, new_frm_mem, ot_ctx, flags); + thd->pop_internal_handler(); + safe_to_ignore_table= prelock_handler.safely_trapped_errors(); + } + else + error= open_table(thd, tables, new_frm_mem, ot_ctx, flags); + + free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC)); + + if (error) + { + if (! ot_ctx->can_recover_from_failed_open_table() && safe_to_ignore_table) + { + DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", + tables->db, tables->alias)); + error= FALSE; + } + goto end; + } + + /* + We can't rely on simple check for TABLE_LIST::view to determine + that this is a view since during re-execution we might reopen + ordinary table in place of view and thus have TABLE_LIST::view + set from repvious execution and TABLE_LIST::table set from + current. + */ + if (!tables->table && tables->view) + { + /* VIEW placeholder */ + (*counter)--; + + /* + tables->next_global list consists of two parts: + 1) Query tables and underlying tables of views. + 2) Tables used by all stored routines that this statement invokes on + execution. + We need to know where the bound between these two parts is. If we've + just opened a view, which was the last table in part #1, and it + has added its base tables after itself, adjust the boundary pointer + accordingly. + */ + if (lex->query_tables_own_last == &(tables->next_global) && + tables->view->query_tables) + lex->query_tables_own_last= tables->view->query_tables_last; + /* + Let us free memory used by 'sroutines' hash here since we never + call destructor for this LEX. + */ + my_hash_free(&tables->view->sroutines); + goto process_view_routines; + } + + /* + Special types of open can succeed but still don't set + TABLE_LIST::table to anything. + */ + if (tables->open_strategy && !tables->table) + goto end; + + /* + If we are not already in prelocked mode and extended table list is not + yet built we might have to build the prelocking set for this statement. + + Since currently no prelocking strategy prescribes doing anything for + tables which are only read, we do below checks only if table is going + to be changed. + */ + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + ! has_prelocking_list && + tables->lock_type >= TL_WRITE_ALLOW_WRITE) + { + bool need_prelocking= FALSE; + TABLE_LIST **save_query_tables_last= lex->query_tables_last; + /* + Extend statement's table list and the prelocking set with + tables and routines according to the current prelocking + strategy. + + For example, for DML statements we need to add tables and routines + used by triggers which are going to be invoked for this element of + table list and also add tables required for handling of foreign keys. + */ + error= prelocking_strategy->handle_table(thd, lex, tables, + &need_prelocking); + + if (need_prelocking && ! lex->requires_prelocking()) + lex->mark_as_requiring_prelocking(save_query_tables_last); + + if (error) + goto end; + } + + if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) + { + if (tables->lock_type == TL_WRITE_DEFAULT) + tables->table->reginfo.lock_type= thd->update_lock_default; + else if (tables->lock_type == TL_READ_DEFAULT) + tables->table->reginfo.lock_type= + read_lock_type_for_table(thd, tables->table); + else + tables->table->reginfo.lock_type= tables->lock_type; + } + tables->table->grant= tables->grant; + + /* Check and update metadata version of a base table. */ + error= check_and_update_table_version(thd, tables, tables->table->s); + + if (error) + goto end; + /* + After opening a MERGE table add the children to the query list of + tables, so that they are opened too. + Note that placeholders don't have the handler open. + */ + /* MERGE tables need to access parent and child TABLE_LISTs. */ + DBUG_ASSERT(tables->table->pos_in_table_list == tables); + /* Non-MERGE tables ignore this call. */ + if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) + { + error= TRUE; + goto end; + } + +process_view_routines: + /* + Again we may need cache all routines used by this view and add + tables used by them to table list. + */ + if (tables->view && + thd->locked_tables_mode <= LTM_LOCK_TABLES && + ! has_prelocking_list) + { + bool need_prelocking= FALSE; + TABLE_LIST **save_query_tables_last= lex->query_tables_last; + + error= prelocking_strategy->handle_view(thd, lex, tables, + &need_prelocking); + + if (need_prelocking && ! lex->requires_prelocking()) + lex->mark_as_requiring_prelocking(save_query_tables_last); + + if (error) + goto end; + } + +end: + DBUG_RETURN(error); +} + + /** Open all tables in list @@ -3847,26 +4091,21 @@ open_routines(THD *thd, Query_tables_list *prelocking_ctx, bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) { - TABLE_LIST *tables= NULL; + /* + We use pointers to "next_global" member in the last processed TABLE_LIST + element and to the "next" member in the last processed Sroutine_hash_entry + element as iterators over, correspondingly, the table list and stored routines + list which stay valid and allow to continue iteration when new elements are + added to the tail of the lists. + */ + TABLE_LIST **table_to_open; + Sroutine_hash_entry **sroutine_to_open; + TABLE_LIST *tables; Open_table_context ot_ctx(thd); bool error= FALSE; MEM_ROOT new_frm_mem; - /* Also used for indicating that prelocking is need */ - TABLE_LIST **query_tables_last_own; - bool safe_to_ignore_table; + bool has_prelocking_list= thd->lex->requires_prelocking(); DBUG_ENTER("open_tables"); - /* - temporary mem_root for new .frm parsing. - TODO: variables for size - */ - init_sql_alloc(&new_frm_mem, 8024, 8024); - - thd->current_tablenr= 0; - restart: - *counter= 0; - query_tables_last_own= 0; - thd_proc_info(thd, "Opening tables"); - /* Close HANDLER tables which are marked for flush or against which there are pending exclusive metadata locks. Note that we do this not to avoid @@ -3880,316 +4119,116 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, mysql_ha_flush(thd); /* - If we are not already executing prelocked statement and don't have - statement for which table list for prelocking is already built, let - us cache routines and try to build such table list. + temporary mem_root for new .frm parsing. + TODO: variables for size */ + init_sql_alloc(&new_frm_mem, 8024, 8024); - if (thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking() && - thd->lex->uses_stored_routines()) - { - bool need_prelocking= FALSE; - TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - - DBUG_ASSERT(thd->lex->query_tables == *start); - - error= open_routines(thd, thd->lex, - (Sroutine_hash_entry *)thd->lex->sroutines_list.first, - prelocking_strategy, &need_prelocking); - if (error) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - goto err; - } - else if (need_prelocking) - { - query_tables_last_own= save_query_tables_last; - *start= thd->lex->query_tables; - } - } + thd->current_tablenr= 0; + restart: + table_to_open= start; + sroutine_to_open= (Sroutine_hash_entry**) &thd->lex->sroutines_list.first; + *counter= 0; + thd_proc_info(thd, "Opening tables"); /* - For every table in the list of tables to open, try to find or open - a table. + Perform steps of prelocking algorithm until there are unprocessed + elements in prelocking list/set. */ - for (tables= *start; tables ;tables= tables->next_global) + while (*table_to_open || + (thd->locked_tables_mode <= LTM_LOCK_TABLES && + ! has_prelocking_list && + *sroutine_to_open)) { - safe_to_ignore_table= FALSE; - /* - Ignore placeholders for derived tables. After derived tables - processing, link to created temporary table will be put here. - If this is derived table for view then we still want to process - routines used by this view. - */ - if (tables->derived) - { - if (!tables->view) - continue; - /* - We restore view's name and database wiped out by derived tables - processing and fall back to standard open process in order to - obtain proper metadata locks and do other necessary steps like - stored routine processing. - */ - tables->db= tables->view_db.str; - tables->db_length= tables->view_db.length; - tables->table_name= tables->view_name.str; - tables->table_name_length= tables->view_name.length; - } - /* - If this TABLE_LIST object is a placeholder for an information_schema - table, create a temporary table to represent the information_schema - table in the query. Do not fill it yet - will be filled during - execution. + For every table in the list of tables to open, try to find or open + a table. */ - if (tables->schema_table) + for (tables= *table_to_open; tables; + table_to_open= &tables->next_global, tables= tables->next_global) { - /* - If this information_schema table is merged into a mergeable - view, ignore it for now -- it will be filled when its respective - TABLE_LIST is processed. This code works only during re-execution. - */ - if (tables->view) - goto process_view_routines; - if (!mysql_schema_table(thd, thd->lex, tables) && - !check_and_update_table_version(thd, tables, tables->table->s)) + error= open_and_process_table(thd, thd->lex, tables, counter, + flags, prelocking_strategy, + has_prelocking_list, &ot_ctx, + &new_frm_mem); + + if (error) { - continue; + if (ot_ctx.can_recover_from_failed_open_table()) + { + /* + We have met exclusive metadata lock or old version of table. + Now we have to close all tables and release metadata locks. + We also have to throw away set of prelocked tables (and thus + close tables from this set that were open by now) since it + is possible that one of tables which determined its content + was changed. + + Instead of implementing complex/non-robust logic mentioned + above we simply close and then reopen all tables. + + We have to save pointer to table list element for table which we + have failed to open since closing tables can trigger removal of + elements from the table list (if MERGE tables are involved), + */ + TABLE_LIST *failed_table= *table_to_open; + close_tables_for_reopen(thd, start); + /* + Here we rely on the fact that 'tables' still points to the valid + TABLE_LIST element. Altough currently this assumption is valid + it may change in future. + */ + if (ot_ctx.recover_from_failed_open_table_attempt(thd, failed_table)) + goto err; + + error= FALSE; + goto restart; + } + goto err; } - DBUG_RETURN(TRUE); } - DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", - tables->db, tables->table_name, (long) tables)); - (*counter)++; - /* Not a placeholder: must be a base table or a view. Let us open it. */ - DBUG_ASSERT(!tables->table); - - if (tables->prelocking_placeholder) + /* + If we are not already in prelocked mode and extended table list is + not yet built for our statement we need to cache routines it uses + and build the prelocking list for it. + */ + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list) { + bool need_prelocking= FALSE; + TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; /* - For the tables added by the pre-locking code, attempt to open - the table but fail silently if the table does not exist. - The real failure will occur when/if a statement attempts to use - that table. + Process elements of the prelocking set which are present there + since parsing stage or were added to it by invocations of + Prelocking_strategy methods in the above loop over tables. + + For example, if element is a routine, cache it and then, + if prelocking strategy prescribes so, add tables it uses to the + table list and routines it might invoke to the prelocking set. */ - Prelock_error_handler prelock_handler; - thd->push_internal_handler(& prelock_handler); - error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags); - thd->pop_internal_handler(); - safe_to_ignore_table= prelock_handler.safely_trapped_errors(); - } - else - error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags); - - free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); - - if (error) - { - if (ot_ctx.can_recover_from_failed_open_table()) + for (Sroutine_hash_entry *rt= *sroutine_to_open; rt; + sroutine_to_open= &rt->next, rt= rt->next) { - /* - We have met exclusive metadata lock or old version of table. Now we - have to close all tables which are not up to date/release metadata - locks. We also have to throw away set of prelocked tables (and thus - close tables from this set that were open by now) since it possible - that one of tables which determined its content was changed. + error= open_and_process_routine(thd, thd->lex, rt, + prelocking_strategy, + &need_prelocking); - Instead of implementing complex/non-robust logic mentioned - above we simply close and then reopen all tables. - - In order to prepare for recalculation of set of prelocked tables - we pretend that we have finished calculation which we were doing - currently. - */ - if (query_tables_last_own) - thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - close_tables_for_reopen(thd, start); - /* - Here we rely on the fact that 'tables' still points to the valid - TABLE_LIST element. Altough currently this assumption is valid - it may change in future. - */ - if (ot_ctx.recover_from_failed_open_table_attempt(thd, tables)) + if (error) + { + /* + Serious error during reading stored routines from mysql.proc table. + Something is wrong with the table or its contents, and an error has + been emitted; we must abort. + */ goto err; - - error= FALSE; - goto restart; + } } - if (safe_to_ignore_table) - { - DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", - tables->db, tables->alias)); - error= FALSE; - continue; - } - goto err; - } + if (need_prelocking && ! thd->lex->requires_prelocking()) + thd->lex->mark_as_requiring_prelocking(save_query_tables_last); - /* - We can't rely on simple check for TABLE_LIST::view to determine - that this is a view since during re-execution we might reopen - ordinary table in place of view and thus have TABLE_LIST::view - set from repvious execution and TABLE_LIST::table set from - current. - */ - if (!tables->table && tables->view) - { - /* VIEW placeholder */ - (*counter)--; - - /* - tables->next_global list consists of two parts: - 1) Query tables and underlying tables of views. - 2) Tables used by all stored routines that this statement invokes on - execution. - We need to know where the bound between these two parts is. If we've - just opened a view, which was the last table in part #1, and it - has added its base tables after itself, adjust the boundary pointer - accordingly. - */ - if (query_tables_last_own == &(tables->next_global) && - tables->view->query_tables) - query_tables_last_own= tables->view->query_tables_last; - /* - Let us free memory used by 'sroutines' hash here since we never - call destructor for this LEX. - */ - my_hash_free(&tables->view->sroutines); - goto process_view_routines; - } - - /* - Special types of open can succeed but still don't set - TABLE_LIST::table to anything. - */ - if (tables->open_strategy && !tables->table) - continue; - - /* - If we are not already in prelocked mode and extended table list is not - yet built we might have to build the prelocking set for this statement. - - Since currently no prelocking strategy prescribes doing anything for - tables which are only read, we do below checks only if table is going - to be changed. - */ - if (thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking() && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) - { - bool need_prelocking= FALSE; - bool not_used; - TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - Sroutine_hash_entry **sroutines_next= - (Sroutine_hash_entry **)thd->lex->sroutines_list.next; - - /* - Extend statement's table list and the prelocking set with - tables and routines according to the current prelocking - strategy. - - For example, for DML statements we need to add tables and routines - used by triggers which are going to be invoked for this element of - table list and also add tables required for handling of foreign keys. - */ - error= prelocking_strategy->handle_table(thd, thd->lex, tables, - &need_prelocking); - - if (need_prelocking && ! query_tables_last_own) - query_tables_last_own= save_query_tables_last; - - if (error) - goto err; - - /* - Process elements of the prelocking set which were added - by the above invocation of Prelocking_strategy method. - - For example, if new element is a routine, cache it and then, if - prelocking strategy prescribes so, add tables it uses to the table - list and routines it might invoke to the prelocking set. - */ - error= open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, - ¬_used); - if (error) - goto err; - } - - if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) - { - if (tables->lock_type == TL_WRITE_DEFAULT) - tables->table->reginfo.lock_type= thd->update_lock_default; - else if (tables->lock_type == TL_READ_DEFAULT) - tables->table->reginfo.lock_type= - read_lock_type_for_table(thd, tables->table); - else - tables->table->reginfo.lock_type= tables->lock_type; - } - tables->table->grant= tables->grant; - - /* Check and update metadata version of a base table. */ - error= check_and_update_table_version(thd, tables, tables->table->s); - - if (error) - goto err; - /* - After opening a MERGE table add the children to the query list of - tables, so that they are opened too. - Note that placeholders don't have the handler open. - */ - /* MERGE tables need to access parent and child TABLE_LISTs. */ - DBUG_ASSERT(tables->table->pos_in_table_list == tables); - /* Non-MERGE tables ignore this call. */ - if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) - { - error= TRUE; - goto err; - } - -process_view_routines: - /* - Again we may need cache all routines used by this view and add - tables used by them to table list. - */ - if (tables->view && - thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking()) - { - bool need_prelocking= FALSE; - bool not_used; - TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - Sroutine_hash_entry **sroutines_next= - (Sroutine_hash_entry **)thd->lex->sroutines_list.next; - - error= prelocking_strategy->handle_view(thd, thd->lex, tables, - &need_prelocking); - - if (need_prelocking && ! query_tables_last_own) - query_tables_last_own= save_query_tables_last; - - if (error) - goto err; - - error= open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, - ¬_used); - - if (error) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something is wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - goto err; - } + if (need_prelocking && ! *start) + *start= thd->lex->query_tables; } } @@ -4220,12 +4259,9 @@ err: thd_proc_info(thd, 0); free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block - if (query_tables_last_own) - thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - - if (error && tables) + if (error && *table_to_open) { - tables->table= NULL; + (*table_to_open)->table= NULL; } DBUG_PRINT("open_tables", ("returning: %d", (int) error)); DBUG_RETURN(error); From 4f85df4b95df010fe2fa486002597b40c5e30a6b Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 19:11:26 +0300 Subject: [PATCH 144/466] Backport of: ------------------------------------------------------------ revno: 2617.68.24 committer: Dmitry Lenev branch nick: mysql-next-bg-pre2-2 timestamp: Wed 2009-09-16 17:25:29 +0400 message: Pre-requisite patch for fixing bug #30977 "Concurrent statement using stored function and DROP FUNCTION breaks SBR". Added MDL_request for stored routine as member to Sroutine_hash_entry in order to be able perform metadata locking for stored routines in future (Sroutine_hash_entry is an equivalent of TABLE_LIST class for stored routines). (WL#4284, follow up fixes). --- sql/mdl.cc | 22 ++++++++++++++-- sql/mdl.h | 17 +++++++++--- sql/sp.cc | 32 +++++++++++------------ sql/sp.h | 7 ++--- sql/sp_head.cc | 64 +++++++++++++++++++++------------------------- sql/sp_head.h | 29 +++------------------ sql/sql_base.cc | 19 +++++++------- sql/sql_trigger.cc | 24 +++++++++-------- 8 files changed, 109 insertions(+), 105 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 879b12a4cac..5bda56193f5 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -318,6 +318,24 @@ void MDL_request::init(enum_mdl_namespace mdl_namespace, } +/** + Initialize a lock request using pre-built MDL_key. + + @sa MDL_request::init(namespace, db, name, type). + + @param key_arg The pre-built MDL key for the request. + @param mdl_type_arg The MDL lock type for the request. +*/ + +void MDL_request::init(const MDL_key *key_arg, + enum enum_mdl_type mdl_type_arg) +{ + key.mdl_key_init(key_arg); + type= mdl_type_arg; + ticket= NULL; +} + + /** Allocate and initialize one lock request. @@ -1254,7 +1272,7 @@ void MDL_context::release_ticket(MDL_ticket *ticket) MDL_lock *lock= ticket->m_lock; DBUG_ENTER("release_ticket"); DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(), - lock->key.table_name())); + lock->key.name())); safe_mutex_assert_owner(&LOCK_mdl); @@ -1526,7 +1544,7 @@ MDL_ticket::set_cached_object(void *cached_object, { DBUG_ENTER("mdl_set_cached_object"); DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", - m_lock->key.db_name(), m_lock->key.table_name(), + m_lock->key.db_name(), m_lock->key.name(), cached_object)); /* TODO: This assumption works now since we do get_cached_object() diff --git a/sql/mdl.h b/sql/mdl.h index 03631bb9dd6..868e22d0834 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -52,9 +52,14 @@ enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; Different types of objects exist in different namespaces - MDL_TABLE is for tables and views. - - MDL_PROCEDURE is for stored procedures, stored functions and UDFs. + - MDL_FUNCTION is for stored functions. + - MDL_PROCEDURE is for stored procedures. + - MDL_TRIGGER is for triggers. + Note that although there isn't metadata locking on triggers, + it's necessary to have a separate namespace for them since + MDL_key is also used outside of the MDL subsystem. */ -enum enum_mdl_namespace { MDL_TABLE=0, MDL_PROCEDURE }; +enum enum_mdl_namespace { MDL_TABLE=0, MDL_FUNCTION, MDL_PROCEDURE, MDL_TRIGGER }; /** Maximal length of key for metadata locking subsystem. */ #define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) @@ -78,8 +83,11 @@ public: const char *db_name() const { return m_ptr + 1; } uint db_name_length() const { return m_db_name_length; } - const char *table_name() const { return m_ptr + m_db_name_length + 2; } - uint table_name_length() const { return m_length - m_db_name_length - 3; } + const char *name() const { return m_ptr + m_db_name_length + 2; } + uint name_length() const { return m_length - m_db_name_length - 3; } + + enum_mdl_namespace mdl_namespace() const + { return (enum_mdl_namespace)(m_ptr[0]); } /** Construct a metadata lock key from a triplet (mdl_namespace, database and name). @@ -179,6 +187,7 @@ public: public: void init(enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg, enum_mdl_type mdl_type_arg); + void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg); /** Set type of lock request. Can be only applied to pending locks. */ inline void set_type(enum_mdl_type type_arg) { diff --git a/sql/sp.cc b/sql/sp.cc index 19fe00594bd..93defd1b401 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1391,8 +1391,8 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, my_bool first) { Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr; - *plen= rn->key.length; - return (uchar *)rn->key.str; + *plen= rn->mdl_request.key.length(); + return (uchar *)rn->mdl_request.key.ptr(); } @@ -1430,23 +1430,19 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, */ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - const LEX_STRING *key, TABLE_LIST *belong_to_view) + const MDL_key *key, TABLE_LIST *belong_to_view) { my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info, Query_tables_list::START_SROUTINES_HASH_SIZE, 0, 0, sp_sroutine_key, 0, 0); - if (!my_hash_search(&prelocking_ctx->sroutines, (uchar *)key->str, - key->length)) + if (!my_hash_search(&prelocking_ctx->sroutines, key->ptr(), key->length())) { Sroutine_hash_entry *rn= - (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) + - key->length + 1); + (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry)); if (!rn) // OOM. Error will be reported using fatal_error(). return FALSE; - rn->key.length= key->length; - rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry); - memcpy(rn->key.str, key->str, key->length + 1); + rn->mdl_request.init(key, MDL_SHARED); my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn); prelocking_ctx->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); rn->belong_to_view= belong_to_view; @@ -1477,8 +1473,9 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, sp_name *rt, char rt_type) { - rt->set_routine_type(rt_type); - (void)sp_add_used_routine(prelocking_ctx, arena, &rt->m_sroutines_key, 0); + MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_FUNCTION : MDL_PROCEDURE, + rt->m_db.str, rt->m_name.str); + (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0); prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; prelocking_ctx->sroutines_list_own_elements= prelocking_ctx->sroutines_list.elements; @@ -1535,7 +1532,8 @@ void sp_update_sp_used_routines(HASH *dst, HASH *src) for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); - if (!my_hash_search(dst, (uchar *)rt->key.str, rt->key.length)) + if (!my_hash_search(dst, (uchar *)rt->mdl_request.key.ptr(), + rt->mdl_request.key.length())) my_hash_insert(dst, (uchar *)rt); } } @@ -1562,8 +1560,8 @@ sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); - (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &rt->key, - belong_to_view); + (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + &rt->mdl_request.key, belong_to_view); } } @@ -1587,8 +1585,8 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, { for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first; rt; rt= rt->next) - (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &rt->key, - belong_to_view); + (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + &rt->mdl_request.key, belong_to_view); } diff --git a/sql/sp.h b/sql/sp.h index 1de41eb3920..a66d72d0e9e 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -73,9 +73,10 @@ class Sroutine_hash_entry { public: /** - Set key consisting of one-byte routine type and quoted routine name. + Metadata lock request for routine. + MDL_key in this request is also used as a key for set. */ - LEX_STRING key; + MDL_request mdl_request; /** Next element in list linking all routines in set. See also comments for LEX::sroutine/sroutine_list and sp_head::m_sroutines. @@ -96,7 +97,7 @@ public: void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, sp_name *rt, char rt_type); bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - const LEX_STRING *key, TABLE_LIST *belong_to_view); + const MDL_key *key, TABLE_LIST *belong_to_view); void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx); void sp_update_sp_used_routines(HASH *dst, HASH *src); void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 0744e5be930..c9e13e6b830 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -382,31 +382,34 @@ error: } -/* - * - * sp_name - * - */ +/** + Create temporary sp_name object from MDL key. -sp_name::sp_name(THD *thd, char *key, uint key_len) + @note The lifetime of this object is bound to the lifetime of the MDL_key. + This should be fine as sp_name objects created by this constructor + are mainly used for SP-cache lookups. + + @param key MDL key containing database and routine name. + @param qname_buff Buffer to be used for storing quoted routine name + (should be at least 2*NAME_LEN+1+1 bytes). +*/ + +sp_name::sp_name(const MDL_key *key, char *qname_buff) { - m_sroutines_key.str= key; - m_sroutines_key.length= key_len; - m_qname.str= ++key; - m_qname.length= key_len - 1; - if ((m_name.str= strchr(m_qname.str, '.'))) + m_db.str= (char*)key->db_name(); + m_db.length= key->db_name_length(); + m_name.str= (char*)key->name(); + m_name.length= key->name_length(); + m_qname.str= qname_buff; + if (m_db.length) { - m_db.length= m_name.str - key; - m_db.str= strmake_root(thd->mem_root, key, m_db.length); - m_name.str++; - m_name.length= m_qname.length - m_db.length - 1; + strxmov(qname_buff, m_db.str, ".", m_name.str, NullS); + m_qname.length= m_db.length + 1 + m_name.length; } else { - m_name.str= m_qname.str; - m_name.length= m_qname.length; - m_db.str= 0; - m_db.length= 0; + strmov(qname_buff, m_name.str); + m_qname.length= m_name.length; } m_explicit_name= false; } @@ -419,12 +422,10 @@ void sp_name::init_qname(THD *thd) { const uint dot= !!m_db.length; - /* m_sroutines format: m_type + [database + dot] + name + nul */ - m_sroutines_key.length= 1 + m_db.length + dot + m_name.length; - if (!(m_sroutines_key.str= (char*) thd->alloc(m_sroutines_key.length + 1))) + /* m_qname format: [database + dot] + name + '\0' */ + m_qname.length= m_db.length + dot + m_name.length; + if (!(m_qname.str= (char*) thd->alloc(m_qname.length + 1))) return; - m_qname.length= m_sroutines_key.length - 1; - m_qname.str= m_sroutines_key.str + 1; sprintf(m_qname.str, "%.*s%.*s%.*s", (int) m_db.length, (m_db.length ? m_db.str : ""), dot, ".", @@ -585,9 +586,6 @@ sp_head::init(LEX *lex) m_defstr.str= NULL; m_defstr.length= 0; - m_sroutines_key.str= NULL; - m_sroutines_key.length= 0; - m_return_field_def.charset= NULL; DBUG_VOID_RETURN; @@ -617,14 +615,10 @@ sp_head::init_sp_name(THD *thd, sp_name *spname) if (spname->m_qname.length == 0) spname->init_qname(thd); - m_sroutines_key.length= spname->m_sroutines_key.length; - m_sroutines_key.str= (char*) memdup_root(thd->mem_root, - spname->m_sroutines_key.str, - spname->m_sroutines_key.length + 1); - m_sroutines_key.str[0]= static_cast(m_type); - - m_qname.length= m_sroutines_key.length - 1; - m_qname.str= m_sroutines_key.str + 1; + m_qname.length= spname->m_qname.length; + m_qname.str= (char*) memdup_root(thd->mem_root, + spname->m_qname.str, + spname->m_qname.length + 1); DBUG_VOID_RETURN; } diff --git a/sql/sp_head.h b/sql/sp_head.h index 5610ecd2a72..74fcd03180e 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -109,36 +109,21 @@ public: LEX_STRING m_db; LEX_STRING m_name; LEX_STRING m_qname; - /** - Key representing routine in the set of stored routines used by statement. - Consists of 1-byte routine type and m_qname (which usually refences to - same buffer). Note that one must complete initialization of the key by - calling set_routine_type(). - */ - LEX_STRING m_sroutines_key; bool m_explicit_name; /**< Prepend the db name? */ sp_name(LEX_STRING db, LEX_STRING name, bool use_explicit_name) : m_db(db), m_name(name), m_explicit_name(use_explicit_name) { - m_qname.str= m_sroutines_key.str= 0; - m_qname.length= m_sroutines_key.length= 0; + m_qname.str= 0; + m_qname.length= 0; } - /** - Creates temporary sp_name object from key, used mainly - for SP-cache lookups. - */ - sp_name(THD *thd, char *key, uint key_len); + /** Create temporary sp_name object from MDL key. */ + sp_name(const MDL_key *key, char *qname_buff); // Init. the qualified name from the db and name. void init_qname(THD *thd); // thd for memroot allocation - void set_routine_type(char type) - { - m_sroutines_key.str[0]= type; - } - ~sp_name() {} }; @@ -181,12 +166,6 @@ public: ulong m_sql_mode; ///< For SHOW CREATE and execution LEX_STRING m_qname; ///< db.name bool m_explicit_name; ///< Prepend the db name? */ - /** - Key representing routine in the set of stored routines used by statement. - [routine_type]db.name - @sa sp_name::m_sroutines_key - */ - LEX_STRING m_sroutines_key; LEX_STRING m_db; LEX_STRING m_name; LEX_STRING m_params; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 20353f0c9f8..16b9342889a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3777,17 +3777,18 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, Prelocking_strategy *prelocking_strategy, bool *need_prelocking) { - int type= rt->key.str[0]; - DBUG_ENTER("open_and_process_routine"); - switch (type) + switch (rt->mdl_request.key.mdl_namespace()) { - case TYPE_ENUM_FUNCTION: - case TYPE_ENUM_PROCEDURE: + case MDL_FUNCTION: + case MDL_PROCEDURE: { - sp_name name(thd, rt->key.str, rt->key.length); + char qname_buff[NAME_LEN*2+1+1]; + sp_name name(&rt->mdl_request.key, qname_buff); sp_head *sp; + int type= (rt->mdl_request.key.mdl_namespace() == MDL_FUNCTION) ? + TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE; if (sp_cache_routine(thd, type, &name, &sp)) DBUG_RETURN(TRUE); @@ -3799,7 +3800,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, } } break; - case TYPE_ENUM_TRIGGER: + case MDL_TRIGGER: break; default: /* Impossible type value. */ @@ -4304,7 +4305,7 @@ handle_routine(THD *thd, Query_tables_list *prelocking_ctx, */ if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first || - rt->key.str[0] != TYPE_ENUM_PROCEDURE) + rt->mdl_request.key.mdl_namespace() != MDL_PROCEDURE) { *need_prelocking= TRUE; sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines, @@ -8302,7 +8303,7 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) while ((mdl_request= it++)) { if ((share= get_cached_table_share(mdl_request->key.db_name(), - mdl_request->key.table_name())) && + mdl_request->key.name())) && share->version != refresh_version && !share->used_tables.is_empty()) break; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 015e0d4daa1..676875ecc75 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -2055,17 +2055,21 @@ add_tables_and_routines_for_triggers(THD *thd, /* We can have only one trigger per action type currently */ sp_head *trigger= table_list->table->triggers->bodies[i][j]; - if (trigger && sp_add_used_routine(prelocking_ctx, thd->stmt_arena, - &trigger->m_sroutines_key, - table_list->belong_to_view)) + if (trigger) { - trigger->add_used_tables_to_table_list(thd, - &prelocking_ctx->query_tables_last, - table_list->belong_to_view); - sp_update_stmt_used_routines(thd, prelocking_ctx, - &trigger->m_sroutines, - table_list->belong_to_view); - trigger->propagate_attributes(prelocking_ctx); + MDL_key key(MDL_TRIGGER, trigger->m_db.str, trigger->m_name.str); + + if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + &key, table_list->belong_to_view)) + { + trigger->add_used_tables_to_table_list(thd, + &prelocking_ctx->query_tables_last, + table_list->belong_to_view); + sp_update_stmt_used_routines(thd, prelocking_ctx, + &trigger->m_sroutines, + table_list->belong_to_view); + trigger->propagate_attributes(prelocking_ctx); + } } } } From ddfdb1b1d161b4b0c96071325581ae6f2419a2f2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 10 Dec 2009 03:48:21 +0100 Subject: [PATCH 145/466] fix install_symlink on unixes other than osx --- CMakeLists.txt | 2 ++ cmake/install_macros.cmake | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51a50f682ba..b68bf2ae108 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,3 +239,5 @@ INSTALL(FILES COPYING EXCEPTIONS-CLIENT README DESTINATION .) IF(UNIX) INSTALL(FILES Docs/INSTALL-BINARY DESTINATION .) ENDIF() +GET_TARGET_PROPERTY(mysqld_location mysqld LOCATION) +INSTALL(FILES ${mysqld_location} DESTINATION win) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 3a5529efc15..3925ce292de 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -60,8 +60,11 @@ IF(UNIX) ALL DEPENDS ${output}) SET_TARGET_PROPERTIES(symlink_${linkbasename}${ext} PROPERTIES CLEAN_DIRECT_OUTPUT 1) - # For Xcode, replace project config with install config - STRING(REPLACE "${CMAKE_CFG_INTDIR}" "\${CMAKE_INSTALL_CONFIG_NAME}" output ${output}) + IF(CMAKE_GENERATOR MATCHES "Xcode") + # For Xcode, replace project config with install config + STRING(REPLACE "${CMAKE_CFG_INTDIR}" + "\${CMAKE_INSTALL_CONFIG_NAME}" output ${output}) + ENDIF() INSTALL(FILES ${output} DESTINATION ${destination}) ENDIF() ENDMACRO() From 3a72f357ee84eeeb7511c431472a7c60bef6ddcd Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 10 Dec 2009 04:09:32 +0100 Subject: [PATCH 146/466] Check for clock_gettime in librt --- configure.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.cmake b/configure.cmake index d1fc590a0cb..84a1b5f1dfb 100644 --- a/configure.cmake +++ b/configure.cmake @@ -279,6 +279,9 @@ IF(UNIX) MY_SEARCH_LIBS(setsockopt socket LIBSOCKET) MY_SEARCH_LIBS(dlopen dl LIBDL) MY_SEARCH_LIBS(sched_yield rt LIBRT) + IF(NOT LIBRT) + MY_SEARCH_LIBS(clock_gettime rt LIBRT) + ENDIF() FIND_PACKAGE(Threads) SET(CMAKE_REQUIRED_LIBRARIES From 2e73ea7ea8a56765982ba5c8642ed5b14ef39fde Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 10 Dec 2009 11:21:38 +0300 Subject: [PATCH 147/466] Backport of: ------------------------------------------------------------ revno: 2617.68.25 committer: Dmitry Lenev branch nick: mysql-next-bg-pre2-2 timestamp: Wed 2009-09-16 18:26:50 +0400 message: Follow-up for one of pre-requisite patches for fixing bug #30977 "Concurrent statement using stored function and DROP FUNCTION breaks SBR". Made enum_mdl_namespace enum part of MDL_key class and removed MDL_ prefix from the names of enum members. In order to do the latter changed name of PROCEDURE symbol to PROCEDURE_SYM (otherwise macro which was automatically generated for this symbol conflicted with MDL_key::PROCEDURE enum member). --- sql/lex.h | 2 +- sql/lock.cc | 3 ++- sql/mdl.cc | 8 ++++---- sql/mdl.h | 45 +++++++++++++++++++++++++-------------------- sql/sp.cc | 3 ++- sql/sp_head.cc | 6 ++++-- sql/sql_base.cc | 17 +++++++++-------- sql/sql_delete.cc | 2 +- sql/sql_handler.cc | 2 +- sql/sql_parse.cc | 2 +- sql/sql_show.cc | 2 +- sql/sql_table.cc | 10 ++++++---- sql/sql_trigger.cc | 4 ++-- sql/sql_yacc.yy | 20 ++++++++++---------- sql/table.cc | 3 ++- sql/table.h | 2 +- 16 files changed, 72 insertions(+), 59 deletions(-) diff --git a/sql/lex.h b/sql/lex.h index a12cf0c4b3e..91ef5287a1e 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -416,7 +416,7 @@ static SYMBOL symbols[] = { { "PREV", SYM(PREV_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIVILEGES", SYM(PRIVILEGES)}, - { "PROCEDURE", SYM(PROCEDURE)}, + { "PROCEDURE", SYM(PROCEDURE_SYM)}, { "PROCESS" , SYM(PROCESS)}, { "PROCESSLIST", SYM(PROCESSLIST_SYM)}, { "PROFILE", SYM(PROFILE_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index 8d314c4ad19..648fecf9b69 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -957,7 +957,8 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - lock_table->mdl_request.init(MDL_TABLE, lock_table->db, lock_table->table_name, + lock_table->mdl_request.init(MDL_key::TABLE, + lock_table->db, lock_table->table_name, MDL_EXCLUSIVE); mdl_requests.push_front(&lock_table->mdl_request); } diff --git a/sql/mdl.cc b/sql/mdl.cc index 5bda56193f5..b624b0658ed 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -307,7 +307,7 @@ void MDL_context::merge(MDL_context *src) @param mdl_type The MDL lock type for the request. */ -void MDL_request::init(enum_mdl_namespace mdl_namespace, +void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace, const char *db_arg, const char *name_arg, enum enum_mdl_type mdl_type_arg) @@ -355,7 +355,7 @@ void MDL_request::init(const MDL_key *key_arg, */ MDL_request * -MDL_request::create(enum_mdl_namespace mdl_namespace, const char *db, +MDL_request::create(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name, enum_mdl_type mdl_type, MEM_ROOT *root) { @@ -1444,7 +1444,7 @@ void MDL_context::release_global_shared_lock() */ bool -MDL_context::is_exclusive_lock_owner(enum_mdl_namespace mdl_namespace, +MDL_context::is_exclusive_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name) { MDL_request mdl_request; @@ -1470,7 +1470,7 @@ MDL_context::is_exclusive_lock_owner(enum_mdl_namespace mdl_namespace, */ bool -MDL_context::is_lock_owner(enum_mdl_namespace mdl_namespace, +MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name) { MDL_key key(mdl_namespace, db, name); diff --git a/sql/mdl.h b/sql/mdl.h index 868e22d0834..2758bd3a8e6 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -47,20 +47,6 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; -/** - Object namespaces - - Different types of objects exist in different namespaces - - MDL_TABLE is for tables and views. - - MDL_FUNCTION is for stored functions. - - MDL_PROCEDURE is for stored procedures. - - MDL_TRIGGER is for triggers. - Note that although there isn't metadata locking on triggers, - it's necessary to have a separate namespace for them since - MDL_key is also used outside of the MDL subsystem. -*/ -enum enum_mdl_namespace { MDL_TABLE=0, MDL_FUNCTION, MDL_PROCEDURE, MDL_TRIGGER }; - /** Maximal length of key for metadata locking subsystem. */ #define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) @@ -77,6 +63,23 @@ enum enum_mdl_namespace { MDL_TABLE=0, MDL_FUNCTION, MDL_PROCEDURE, MDL_TRIGGER class MDL_key { public: + /** + Object namespaces + + Different types of objects exist in different namespaces + - TABLE is for tables and views. + - FUNCTION is for stored functions. + - PROCEDURE is for stored procedures. + - TRIGGER is for triggers. + Note that although there isn't metadata locking on triggers, + it's necessary to have a separate namespace for them since + MDL_key is also used outside of the MDL subsystem. + */ + enum enum_mdl_namespace { TABLE=0, + FUNCTION, + PROCEDURE, + TRIGGER }; + const uchar *ptr() const { return (uchar*) m_ptr; } uint length() const { return m_length; } @@ -185,7 +188,8 @@ public: MDL_key key; public: - void init(enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg, + void init(MDL_key::enum_mdl_namespace namespace_arg, + const char *db_arg, const char *name_arg, enum_mdl_type mdl_type_arg); void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg); /** Set type of lock request. Can be only applied to pending locks. */ @@ -196,9 +200,9 @@ public: } bool is_shared() const { return type < MDL_EXCLUSIVE; } - static MDL_request *create(enum_mdl_namespace mdl_namespace, const char *db, - const char *name, enum_mdl_type mdl_type, - MEM_ROOT *root); + static MDL_request *create(MDL_key::enum_mdl_namespace mdl_namespace, + const char *db, const char *name, + enum_mdl_type mdl_type, MEM_ROOT *root); /* This is to work around the ugliness of TABLE_LIST @@ -340,10 +344,11 @@ public: void release_lock(MDL_ticket *ticket); void release_global_shared_lock(); - bool is_exclusive_lock_owner(enum_mdl_namespace mdl_namespace, + bool is_exclusive_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name); - bool is_lock_owner(enum_mdl_namespace mdl_namespace, const char *db, const char *name); + bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, + const char *db, const char *name); inline bool has_locks() const { diff --git a/sql/sp.cc b/sql/sp.cc index 93defd1b401..a8bd419cdcc 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1473,7 +1473,8 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, sp_name *rt, char rt_type) { - MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_FUNCTION : MDL_PROCEDURE, + MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION : + MDL_key::PROCEDURE, rt->m_db.str, rt->m_name.str); (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0); prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index c9e13e6b830..7316bcb2a46 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3975,7 +3975,8 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_request.init(MDL_TABLE, table->db, table->table_name, MDL_SHARED); + table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + MDL_SHARED); /* Everyting else should be zeroed */ @@ -4017,7 +4018,8 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_request.init(MDL_TABLE, table->db, table->table_name, MDL_SHARED); + table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + MDL_SHARED); lex->add_to_query_tables(table); return table; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 16b9342889a..08ec0c8a2ed 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -476,7 +476,7 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, To be able perform any operation on table we should own some kind of metadata lock on it. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_TABLE, table_list->db, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table_list->db, table_list->table_name)); /* Read table definition from cache */ @@ -2546,7 +2546,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, TABLES breaks metadata locking protocol (potentially can lead to deadlocks) it should be disallowed. */ - if (thd->mdl_context.is_lock_owner(MDL_TABLE, table_list->db, + if (thd->mdl_context.is_lock_owner(MDL_key::TABLE, table_list->db, table_list->table_name)) { char path[FN_REFLEN + 1]; @@ -3781,13 +3781,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, switch (rt->mdl_request.key.mdl_namespace()) { - case MDL_FUNCTION: - case MDL_PROCEDURE: + case MDL_key::FUNCTION: + case MDL_key::PROCEDURE: { char qname_buff[NAME_LEN*2+1+1]; sp_name name(&rt->mdl_request.key, qname_buff); sp_head *sp; - int type= (rt->mdl_request.key.mdl_namespace() == MDL_FUNCTION) ? + int type= (rt->mdl_request.key.mdl_namespace() == MDL_key::FUNCTION) ? TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE; if (sp_cache_routine(thd, type, &name, &sp)) @@ -3800,7 +3800,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, } } break; - case MDL_TRIGGER: + case MDL_key::TRIGGER: break; default: /* Impossible type value. */ @@ -4305,7 +4305,7 @@ handle_routine(THD *thd, Query_tables_list *prelocking_ctx, */ if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first || - rt->mdl_request.key.mdl_namespace() != MDL_PROCEDURE) + rt->mdl_request.key.mdl_namespace() != MDL_key::PROCEDURE) { *need_prelocking= TRUE; sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines, @@ -8234,7 +8234,8 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, safe_mutex_assert_owner(&LOCK_open); DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED || - thd->mdl_context.is_exclusive_lock_owner(MDL_TABLE, db, table_name)); + thd->mdl_context.is_exclusive_lock_owner(MDL_key::TABLE, + db, table_name)); key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 03be0382150..1f3621de502 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1177,7 +1177,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) DBUG_RETURN(TRUE); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 94f6b248e45..a7fe5a03f10 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -264,7 +264,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - hash_tables->mdl_request.init(MDL_TABLE, db, name, MDL_SHARED); + hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED); /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2c5f72c8cf4..c49b22d23bd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6024,7 +6024,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_request.init(MDL_TABLE, ptr->db, ptr->table_name, MDL_SHARED); + ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, MDL_SHARED); DBUG_RETURN(ptr); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index fe941321dcd..ccb5875dee5 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3081,7 +3081,7 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, bool can_deadlock) { bool error; - table->mdl_request.init(MDL_TABLE, table->db, table->table_name, + table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, MDL_SHARED_HIGH_PRIO); while (!(error= thd->mdl_context.try_acquire_shared_lock(&table->mdl_request)) && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ddb53dd3754..42d688542b5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4140,7 +4140,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - target_mdl_request.init(MDL_TABLE, db, table_name, MDL_EXCLUSIVE); + target_mdl_request.init(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE); if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) { result= TRUE; @@ -4361,7 +4361,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, uint key_length; key_length= create_table_def_key(thd, key, table_list, 0); - table_list->mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, + table_list->mdl_request.init(MDL_key::TABLE, + table_list->db, table_list->table_name, MDL_EXCLUSIVE); if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request)) DBUG_RETURN(0); @@ -5271,7 +5272,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } else { - table->mdl_request.init(MDL_TABLE, db, table_name, MDL_EXCLUSIVE); + table->mdl_request.init(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE); if (thd->mdl_context.try_acquire_exclusive_lock(&table->mdl_request)) DBUG_RETURN(TRUE); @@ -6637,7 +6638,8 @@ view_err: } else { - target_mdl_request.init(MDL_TABLE, new_db, new_name, MDL_EXCLUSIVE); + target_mdl_request.init(MDL_key::TABLE, new_db, new_name, + MDL_EXCLUSIVE); if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) DBUG_RETURN(TRUE); if (target_mdl_request.ticket == NULL) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 676875ecc75..08d0dacd8e8 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1888,7 +1888,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, In the future, only an exclusive metadata lock will be enough. */ #ifndef DBUG_OFF - if (thd->mdl_context.is_exclusive_lock_owner(MDL_TABLE, db, old_table)) + if (thd->mdl_context.is_exclusive_lock_owner(MDL_key::TABLE, db, old_table)) safe_mutex_assert_owner(&LOCK_open); #endif @@ -2057,7 +2057,7 @@ add_tables_and_routines_for_triggers(THD *thd, if (trigger) { - MDL_key key(MDL_TRIGGER, trigger->m_db.str, trigger->m_name.str); + MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str); if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &key, table_list->belong_to_view)) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 577c60b4c10..826ec63a93e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -922,7 +922,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PREV_SYM %token PRIMARY_SYM /* SQL-2003-R */ %token PRIVILEGES /* SQL-2003-N */ -%token PROCEDURE /* SQL-2003-R */ +%token PROCEDURE_SYM /* SQL-2003-R */ %token PROCESS %token PROCESSLIST_SYM %token PROFILE_SYM @@ -5807,7 +5807,7 @@ alter: lex->sql_command= SQLCOM_ALTER_DB_UPGRADE; lex->name= $3; } - | ALTER PROCEDURE sp_name + | ALTER PROCEDURE_SYM sp_name { LEX *lex= Lex; @@ -9439,7 +9439,7 @@ dec_num: procedure_clause: /* empty */ - | PROCEDURE ident /* Procedure name */ + | PROCEDURE_SYM ident /* Procedure name */ { LEX *lex=Lex; @@ -9695,7 +9695,7 @@ drop: spname->init_qname(thd); lex->spname= spname; } - | DROP PROCEDURE if_exists sp_name + | DROP PROCEDURE_SYM if_exists sp_name { LEX *lex=Lex; if (lex->sphead) @@ -10446,7 +10446,7 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } - | CREATE PROCEDURE sp_name + | CREATE PROCEDURE_SYM sp_name { LEX *lex= Lex; @@ -10466,7 +10466,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER; lex->spname= $3; } - | PROCEDURE STATUS_SYM wild_and_where + | PROCEDURE_SYM STATUS_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_STATUS_PROC; @@ -10480,7 +10480,7 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) MYSQL_YYABORT; } - | PROCEDURE CODE_SYM sp_name + | PROCEDURE_SYM CODE_SYM sp_name { Lex->sql_command= SQLCOM_SHOW_PROC_CODE; Lex->spname= $3; @@ -12671,7 +12671,7 @@ revoke_command: lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_FUNCTION; } - | grant_privileges ON PROCEDURE grant_ident FROM grant_list + | grant_privileges ON PROCEDURE_SYM grant_ident FROM grant_list { LEX *lex= Lex; if (lex->columns.elements) @@ -12713,7 +12713,7 @@ grant_command: lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_FUNCTION; } - | grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list + | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list require_clause grant_options { LEX *lex= Lex; @@ -13721,7 +13721,7 @@ sf_tail: ; sp_tail: - PROCEDURE remember_name sp_name + PROCEDURE_SYM remember_name sp_name { LEX *lex= Lex; sp_head *sp; diff --git a/sql/table.cc b/sql/table.cc index c66610e5693..aef836c330e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4822,7 +4822,8 @@ size_t max_row_length(TABLE *table, const uchar *data) void init_mdl_requests(TABLE_LIST *table_list) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, + table_list->mdl_request.init(MDL_key::TABLE, + table_list->db, table_list->table_name, MDL_SHARED); } diff --git a/sql/table.h b/sql/table.h index 6a2bef4c610..eacf4f6085c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1125,7 +1125,7 @@ struct TABLE_LIST table_name_length= table_name_length_arg; alias= (char*) alias_arg; lock_type= lock_type_arg; - mdl_request.init(MDL_TABLE, db, table_name, MDL_SHARED); + mdl_request.init(MDL_key::TABLE, db, table_name, MDL_SHARED); } /* From 6c25664eb35b3333d9e4cbc0a937f3fa873994f7 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Thu, 10 Dec 2009 10:32:23 +0100 Subject: [PATCH 148/466] Bug #46495 Crash in reload_acl_and_cache on SIGHUP An assert in reload_acl_and_cache didn't account for the case when the function is called with a NULL thd. A null thd is used whenever the function is called from the SIGHUP signal handler. Backported from 6.0-codebase (revid: 2617.69.35) --- sql/sql_parse.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7ea5c81c52b..cafc70dc2ee 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6568,7 +6568,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } #endif /*HAVE_QUERY_CACHE*/ - DBUG_ASSERT(thd->locked_tables_mode || !thd->mdl_context.has_locks()); + DBUG_ASSERT(!thd || thd->locked_tables_mode || !thd->mdl_context.has_locks()); /* Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too From b20a409c38107f2afe49c130b136828fce263336 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 11:53:20 +0100 Subject: [PATCH 149/466] Backport of revno: 2617.71.1 Bug#42546 Backup: RESTORE fails, thinking it finds an existing table The problem occured when a MDL locking conflict happened for a non-existent table between a CREATE and a INSERT statement. The code for CREATE interpreted this lock conflict to mean that the table existed, which meant that the statement failed when it should not have. The problem could occur for CREATE TABLE, CREATE TABLE LIKE and ALTER TABLE RENAME. This patch fixes the problem for CREATE TABLE and CREATE TABLE LIKE. It is based on code backported from the mysql-6.1-fk tree written by Dmitry Lenev. CREATE now uses normal open_and_lock_tables() code to acquire exclusive locks. This means that for the test case in the bug description, CREATE will wait until INSERT completes so that it can get the exclusive lock. This resolves the reported bug. The patch also prohibits CREATE TABLE and CREATE TABLE LIKE under LOCK TABLES. Note that this is an incompatible change and must be reflected in the documentation. Affected test cases have been updated. mdl_sync.test contains tests for CREATE TABLE and CREATE TABLE LIKE. Fixing the issue for ALTER TABLE RENAME is beyond the scope of this patch. ALTER TABLE cannot be prohibited from working under LOCK TABLES as this could seriously impact customers and a proper fix would require a significant rewrite. --- mysql-test/r/lock_multi.result | 3 +- mysql-test/r/mdl_sync.result | 60 ++++++ mysql-test/r/merge.result | 30 ++- mysql-test/r/ps_ddl.result | 17 +- mysql-test/t/lock_multi.test | 3 +- mysql-test/t/mdl_sync.test | 95 +++++++++ mysql-test/t/merge.test | 13 +- mysql-test/t/ps_ddl.test | 11 +- sql/mdl.cc | 3 + sql/mysql_priv.h | 6 +- sql/sql_acl.cc | 7 +- sql/sql_base.cc | 15 +- sql/sql_insert.cc | 75 ++++---- sql/sql_parse.cc | 79 +++++--- sql/sql_partition.cc | 49 ++--- sql/sql_prepare.cc | 36 ++-- sql/sql_table.cc | 338 +++++++++------------------------ sql/sql_trigger.cc | 2 +- sql/sql_view.cc | 2 +- sql/sql_yacc.yy | 20 +- sql/table.h | 16 +- 21 files changed, 465 insertions(+), 415 deletions(-) diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index ef9292ad8c0..5d12e0efd64 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -72,9 +72,10 @@ CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; FLUSH TABLES WITH READ LOCK; CREATE TABLE t2 (c1 int); +ERROR HY000: Table 't2' was not locked with LOCK TABLES UNLOCK TABLES; UNLOCK TABLES; -DROP TABLE t1, t2; +DROP TABLE t1; CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; FLUSH TABLES WITH READ LOCK; diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index d409157a70b..e5447c32b7d 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -63,6 +63,66 @@ unlock tables; # Clean-up. drop tables t1, t2, t3, t5; # +# Bug#42546 - Backup: RESTORE fails, thinking it finds an existing table +# +DROP TABLE IF EXISTS t1; +set @save_log_output=@@global.log_output; +set global log_output=file; +# +# Test 1: CREATE TABLE +# +# Connection 2 +# Start insert on the not-yet existing table +# Wait after taking the MDL lock +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +INSERT INTO t1 VALUES(1,"def"); +# Connection 1 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Now INSERT has a MDL on the non-existent table t1. +# +# Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +# Try to create that table. +CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1)); +# Connection 2 +# Insert fails +ERROR 42S02: Table 'test.t1' doesn't exist +# Connection 1 +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; +Tables_in_test +t1 +DROP TABLE IF EXISTS t1; +# +# Test 2: CREATE TABLE LIKE +# +CREATE TABLE t2 (c1 INT, c2 VARCHAR(100), KEY(c1)); +# Connection 2 +# Start insert on the not-yet existing table +# Wait after taking the MDL +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +INSERT INTO t1 VALUES(1,"def"); +# Connection 1 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Now INSERT has a MDL on the non-existent table t1. +# +# Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +# Try to create that table. +CREATE TABLE t1 LIKE t2; +# Connection 2 +# Insert fails +ERROR 42S02: Table 'test.t1' doesn't exist +# Connection 1 +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; +Tables_in_test +t1 +t2 +DROP TABLE t2; +DROP TABLE IF EXISTS t1; +set global log_output=@save_log_output; +# # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY # FOR UPDATE" # diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 3a6cd4d3f5a..0417b91490e 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -1149,7 +1149,8 @@ SHOW CREATE TABLE t3; ERROR 42S02: Table 'test.t3' doesn't exist DROP TABLE t1, t2; # -# CREATE ... LIKE +# Bug#37371 "CREATE TABLE LIKE merge loses UNION parameter" +# Demonstrate that this is no longer the case. # # 1. Create like. CREATE TABLE t1 (c1 INT); @@ -1164,26 +1165,26 @@ SHOW CREATE TABLE t4; Table Create Table t4 CREATE TABLE `t4` ( `c1` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) INSERT INTO t4 VALUES (4); -ERROR HY000: Table 't4' is read only DROP TABLE t4; # # 1. Create like with locked tables. LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; CREATE TABLE t4 LIKE t3; +ERROR HY000: Table 't4' was not locked with LOCK TABLES SHOW CREATE TABLE t4; ERROR HY000: Table 't4' was not locked with LOCK TABLES INSERT INTO t4 VALUES (4); ERROR HY000: Table 't4' was not locked with LOCK TABLES +CREATE TEMPORARY TABLE t4 LIKE t3; +SHOW CREATE TABLE t4; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +INSERT INTO t4 VALUES (4); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist UNLOCK TABLES; -SHOW CREATE TABLE t4; -Table Create Table -t4 CREATE TABLE `t4` ( - `c1` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT INTO t4 VALUES (4); -ERROR HY000: Table 't4' is read only +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist DROP TABLE t4; # # Rename child. @@ -1210,6 +1211,7 @@ c1 1 2 3 +4 RENAME TABLE t2 TO t5; SELECT * FROM t3 ORDER BY c1; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist @@ -1219,6 +1221,7 @@ c1 1 2 3 +4 # # 3. Normal rename with locked tables. LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; @@ -1227,6 +1230,7 @@ c1 1 2 3 +4 RENAME TABLE t2 TO t5; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1234,6 +1238,7 @@ c1 1 2 3 +4 RENAME TABLE t5 TO t2; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1241,6 +1246,7 @@ c1 1 2 3 +4 UNLOCK TABLES; # # 4. Alter table rename. @@ -1253,6 +1259,7 @@ c1 1 2 3 +4 # # 5. Alter table rename with locked tables. LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; @@ -1268,6 +1275,7 @@ c1 1 2 3 +4 # # Rename parent. # @@ -1278,6 +1286,7 @@ c1 1 2 3 +4 RENAME TABLE t3 TO t5; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1285,6 +1294,7 @@ c1 1 2 3 +4 RENAME TABLE t5 TO t3; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1292,6 +1302,7 @@ c1 1 2 3 +4 # # 5. Alter table rename with locked tables. ALTER TABLE t3 RENAME TO t5; @@ -1306,6 +1317,7 @@ c1 1 2 3 +4 DROP TABLE t1, t2, t3; # # Drop locked tables. diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index f411328ed7c..3d57c8f7332 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -1755,21 +1755,21 @@ SUCCESS drop table t1; deallocate prepare stmt; -# XXX: no validation of the first table in case of -# CREATE TEMPORARY TABLE. This is a shortcoming of the current code, -# but since validation is not strictly necessary, nothing is done -# about it. -# Will be fixed as part of work on Bug#21431 "Incomplete support of -# temporary tables" create table t1 (a int); insert into t1 (a) values (1); prepare stmt from "create temporary table if not exists t2 as select * from t1"; execute stmt; drop table t2; execute stmt; +call p_verify_reprepare_count(0); +SUCCESS + execute stmt; Warnings: Note 1050 Table 't2' already exists +call p_verify_reprepare_count(1); +SUCCESS + select * from t2; a 1 @@ -1777,6 +1777,9 @@ a execute stmt; Warnings: Note 1050 Table 't2' already exists +call p_verify_reprepare_count(0); +SUCCESS + select * from t2; a 1 @@ -1790,7 +1793,7 @@ Note 1050 Table 't2' already exists select * from t2; a 1 -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); SUCCESS drop table t1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index cbb99c04967..31a10f89796 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -199,6 +199,7 @@ let $wait_condition= where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; --source include/wait_condition.inc # This must not block. +--error ER_TABLE_NOT_LOCKED CREATE TABLE t2 (c1 int); UNLOCK TABLES; # @@ -208,7 +209,7 @@ reap; UNLOCK TABLES; # connection default; -DROP TABLE t1, t2; +DROP TABLE t1; # # Test if CREATE TABLE SELECT with LOCK TABLE deadlocks. # diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index d50c056fda3..fd66f6d539d 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -141,6 +141,101 @@ disconnect con2root; drop tables t1, t2, t3, t5; +--echo # +--echo # Bug#42546 - Backup: RESTORE fails, thinking it finds an existing table +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +set @save_log_output=@@global.log_output; +set global log_output=file; + +connect(con2, localhost, root,,); + +--echo # +--echo # Test 1: CREATE TABLE +--echo # + +--echo # Connection 2 +connection con2; +--echo # Start insert on the not-yet existing table +--echo # Wait after taking the MDL lock +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +--send INSERT INTO t1 VALUES(1,"def") + +--echo # Connection 1 +connection default; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Now INSERT has a MDL on the non-existent table t1. + +--echo # +--echo # Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +--echo # Try to create that table. +--send CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1)) + +--echo # Connection 2 +--echo # Insert fails +connection con2; +--error ER_NO_SUCH_TABLE +--reap + +--echo # Connection 1 +connection default; +--reap; +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # +--echo # Test 2: CREATE TABLE LIKE +--echo # + +CREATE TABLE t2 (c1 INT, c2 VARCHAR(100), KEY(c1)); + +--echo # Connection 2 +connection con2; +--echo # Start insert on the not-yet existing table +--echo # Wait after taking the MDL +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +--send INSERT INTO t1 VALUES(1,"def") + +--echo # Connection 1 +connection default; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Now INSERT has a MDL on the non-existent table t1. + +--echo # +--echo # Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +--echo # Try to create that table. +--send CREATE TABLE t1 LIKE t2 + +--echo # Connection 2 +--echo # Insert fails +connection con2; +--error ER_NO_SUCH_TABLE +--reap + +--echo # Connection 1 +connection default; +--reap +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; + +DROP TABLE t2; +disconnect con2; +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +set global log_output=@save_log_output; + + --echo # --echo # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY --echo # FOR UPDATE" diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 2738f79247f..b9e6813a4df 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -846,7 +846,8 @@ SHOW CREATE TABLE t3; DROP TABLE t1, t2; # --echo # ---echo # CREATE ... LIKE +--echo # Bug#37371 "CREATE TABLE LIKE merge loses UNION parameter" +--echo # Demonstrate that this is no longer the case. --echo # --echo # 1. Create like. CREATE TABLE t1 (c1 INT); @@ -858,20 +859,24 @@ INSERT INTO t2 VALUES (2); INSERT INTO t3 VALUES (3); CREATE TABLE t4 LIKE t3; SHOW CREATE TABLE t4; ---error ER_OPEN_AS_READONLY INSERT INTO t4 VALUES (4); DROP TABLE t4; --echo # --echo # 1. Create like with locked tables. LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; +--error ER_TABLE_NOT_LOCKED CREATE TABLE t4 LIKE t3; --error ER_TABLE_NOT_LOCKED SHOW CREATE TABLE t4; --error ER_TABLE_NOT_LOCKED INSERT INTO t4 VALUES (4); -UNLOCK TABLES; +CREATE TEMPORARY TABLE t4 LIKE t3; +--error ER_WRONG_MRG_TABLE SHOW CREATE TABLE t4; ---error ER_OPEN_AS_READONLY +--error ER_WRONG_MRG_TABLE +INSERT INTO t4 VALUES (4); +UNLOCK TABLES; +--error ER_WRONG_MRG_TABLE INSERT INTO t4 VALUES (4); DROP TABLE t4; # diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index 1ba193983b2..fe17bca1eba 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -1493,27 +1493,24 @@ execute stmt; call p_verify_reprepare_count(0); drop table t1; deallocate prepare stmt; ---echo # XXX: no validation of the first table in case of ---echo # CREATE TEMPORARY TABLE. This is a shortcoming of the current code, ---echo # but since validation is not strictly necessary, nothing is done ---echo # about it. ---echo # Will be fixed as part of work on Bug#21431 "Incomplete support of ---echo # temporary tables" create table t1 (a int); insert into t1 (a) values (1); prepare stmt from "create temporary table if not exists t2 as select * from t1"; execute stmt; drop table t2; execute stmt; +call p_verify_reprepare_count(0); execute stmt; +call p_verify_reprepare_count(1); select * from t2; execute stmt; +call p_verify_reprepare_count(0); select * from t2; drop table t2; create temporary table t2 (a varchar(10)); execute stmt; select * from t2; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); drop table t1; create table t1 (x int); execute stmt; diff --git a/sql/mdl.cc b/sql/mdl.cc index b624b0658ed..a883b21423e 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1051,6 +1051,9 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() signalled|= notify_shared_lock(thd, conflicting_ticket); } + /* There is a shared or exclusive lock on the object. */ + DEBUG_SYNC(thd, "mdl_upgrade_shared_lock_to_exclusive_wait"); + if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); else diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 03a4462f199..b6ffcb238b2 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1033,6 +1033,7 @@ bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); +void create_table_set_open_action_and_adjust_tables(LEX *lex); void init_max_user_conn(void); void init_update_queries(void); void free_max_user_conn(void); @@ -1170,10 +1171,9 @@ int prepare_create_field(Create_field *sql_field, longlong table_flags); CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, HA_CREATE_INFO *create_info); -bool mysql_create_table(THD *thd,const char *db, const char *table_name, +bool mysql_create_table(THD *thd, TABLE_LIST *create_table, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool tmp_table, uint select_field_count); + Alter_info *alter_info); bool mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, HA_CREATE_INFO *create_info, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index fdcd68cc2ea..34680855337 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -689,8 +689,7 @@ my_bool acl_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[1].next_local= tables[1].next_global= tables+2; tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; - tables[0].skip_temporary= tables[1].skip_temporary= - tables[2].skip_temporary= TRUE; + tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY; init_mdl_requests(tables); if (simple_open_n_lock_tables(thd, tables)) @@ -3797,7 +3796,7 @@ static my_bool grant_reload_procs_priv(THD *thd) table.init_one_table("mysql", 5, "procs_priv", strlen("procs_priv"), "procs_priv", TL_READ); - table.skip_temporary= 1; + table.open_type= OT_BASE_ONLY; if (simple_open_n_lock_tables(thd, &table)) { @@ -3863,7 +3862,7 @@ my_bool grant_reload(THD *thd) tables[0].db= tables[1].db= (char *) "mysql"; tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type= tables[1].lock_type= TL_READ; - tables[0].skip_temporary= tables[1].skip_temporary= TRUE; + tables[0].open_type= tables[1].open_type= OT_BASE_ONLY; init_mdl_requests(tables); /* diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 08ec0c8a2ed..7a3adc89ea9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2437,7 +2437,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, same name. This block implements the behaviour. TODO: move this block into a separate function. */ - if (!table_list->skip_temporary && ! (flags & MYSQL_OPEN_SKIP_TEMPORARY)) + if (table_list->open_type != OT_BASE_ONLY && + ! (flags & MYSQL_OPEN_SKIP_TEMPORARY)) { for (table= thd->temporary_tables; table ; table=table->next) { @@ -2469,10 +2470,16 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } } - if (flags & MYSQL_OPEN_TEMPORARY_ONLY) + if (table_list->open_type == OT_TEMPORARY_ONLY || + (flags & MYSQL_OPEN_TEMPORARY_ONLY)) { - my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); - DBUG_RETURN(TRUE); + if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); + DBUG_RETURN(TRUE); + } + else + DBUG_RETURN(FALSE); } /* diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 171c5e2cee0..8fd704c4f71 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3415,46 +3415,43 @@ void select_insert::abort() { CREATE TABLE (SELECT) ... ***************************************************************************/ -/* +/** Create table from lists of fields and items (or just return TABLE object for pre-opened existing table). - SYNOPSIS - create_table_from_items() - thd in Thread object - create_info in Create information (like MAX_ROWS, ENGINE or - temporary table flag) - create_table in Pointer to TABLE_LIST object providing database - and name for table to be created or to be open - alter_info in/out Initial list of columns and indexes for the table - to be created - items in List of items which should be used to produce rest - of fields for the table (corresponding fields will - be added to the end of alter_info->create_list) - lock out Pointer to the MYSQL_LOCK object for table created - (or open temporary table) will be returned in this - parameter. Since this table is not included in - THD::lock caller is responsible for explicitly - unlocking this table. - hooks + @param thd [in] Thread object + @param create_info [in] Create information (like MAX_ROWS, ENGINE or + temporary table flag) + @param create_table [in] Pointer to TABLE_LIST object providing database + and name for table to be created or to be open + @param alter_info [in/out] Initial list of columns and indexes for the + table to be created + @param items [in] List of items which should be used to produce + rest of fields for the table (corresponding + fields will be added to the end of + alter_info->create_list) + @param lock [out] Pointer to the MYSQL_LOCK object for table + created will be returned in this parameter. + Since this table is not included in THD::lock + caller is responsible for explicitly unlocking + this table. + @param hooks [in] Hooks to be invoked before and after obtaining + table lock on the table being created. - NOTES - This function behaves differently for base and temporary tables: - - For base table we assume that either table exists and was pre-opened - and locked at open_and_lock_tables() stage (and in this case we just - emit error or warning and return pre-opened TABLE object) or special - placeholder was put in table cache that guarantees that this table - won't be created or opened until the placeholder will be removed - (so there is an exclusive lock on this table). - - We don't pre-open existing temporary table, instead we either open - or create and then open table in this function. + @note + This function assumes that either table exists and was pre-opened and + locked at open_and_lock_tables() stage (and in this case we just emit + error or warning and return pre-opened TABLE object) or an exclusive + metadata lock was acquired on table so we can safely create, open and + lock table in it (we don't acquire metadata lock if this create is + for temporary table). + @note Since this function contains some logic specific to CREATE TABLE ... SELECT it should be changed before it can be used in other contexts. - RETURN VALUES - non-zero Pointer to TABLE object for table created or opened - 0 Error + @retval non-zero Pointer to TABLE object for table created or opened + @retval 0 Error */ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, @@ -3529,14 +3526,12 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, open_table(). */ { - tmp_disable_binlog(thd); if (!mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, create_info, alter_info, 0, select_field_count)) { - if (create_info->table_existed && - !(create_info->options & HA_LEX_CREATE_TMP_TABLE)) + if (create_info->table_existed) { /* This means that someone created table underneath server @@ -3572,8 +3567,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, { Open_table_context ot_ctx_unused(thd); if (open_table(thd, create_table, thd->mem_root, &ot_ctx_unused, - MYSQL_OPEN_TEMPORARY_ONLY) && - !create_info->table_existed) + MYSQL_OPEN_TEMPORARY_ONLY)) { /* This shouldn't happen as creation of temporary table should make @@ -3586,7 +3580,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, table= create_table->table; } } - reenable_binlog(thd); if (!table) // open failed DBUG_RETURN(0); } @@ -3610,9 +3603,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, mysql_unlock_tables(thd, *lock); *lock= 0; } - - if (!create_info->table_existed) - drop_open_table(thd, table, create_table->db, create_table->table_name); + drop_open_table(thd, table, create_table->db, create_table->table_name); DBUG_RETURN(0); } DBUG_RETURN(table); @@ -3704,7 +3695,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000);); - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && create_table->table) + if (create_table->table) { /* Table already exists and was open at open_and_lock_tables() stage. */ if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cafc70dc2ee..e462556c133 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2245,9 +2245,9 @@ case SQLCOM_PREPARE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); bool link_to_local; - // Skip first table, which is the table we are creating - TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); - TABLE_LIST *select_tables= lex->query_tables; + TABLE_LIST *create_table= first_table; + TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global; + /* Code below (especially in mysql_create_table() and select_create methods) may modify HA_CREATE_INFO structure in LEX, so we have to @@ -2327,6 +2327,10 @@ case SQLCOM_PREPARE: } #endif + /* Set strategies: reset default or 'prepared' values. */ + create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL; + /* Close any open handlers for the table */ @@ -2389,15 +2393,8 @@ case SQLCOM_PREPARE: goto end_with_restore_list; } - if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) - { - lex->link_first_table_back(create_table, link_to_local); - /* Set strategies: reset default or 'prepared' values. */ - create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; - create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL; - } - - if (!(res= open_and_lock_tables(thd, lex->query_tables))) + if (!(res= open_and_lock_tables_derived(thd, lex->query_tables, TRUE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL))) { /* Is table which we are changing used somewhere in other parts @@ -2406,7 +2403,6 @@ case SQLCOM_PREPARE: if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { TABLE_LIST *duplicate; - create_table= lex->unlink_first_table(&link_to_local); if ((duplicate= unique_table(thd, create_table, select_tables, 0))) { update_non_unique_table_error(create_table, "CREATE", duplicate); @@ -2432,6 +2428,13 @@ case SQLCOM_PREPARE: } } + /* + Remove target table from main select and name resolution + context. This can't be done earlier as it will break view merging in + statements like "CREATE TABLE IF NOT EXISTS existing_view SELECT". + */ + lex->unlink_first_table(&link_to_local); + /* select_create is currently not re-execution friendly and needs to be created for every execution of a PS/SP. @@ -2451,33 +2454,32 @@ case SQLCOM_PREPARE: res= handle_select(thd, lex, result, 0); delete result; } + + lex->link_first_table_back(create_table, link_to_local); } - else if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) - create_table= lex->unlink_first_table(&link_to_local); - } else { /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ if (create_info.options & HA_LEX_CREATE_TMP_TABLE) thd->options|= OPTION_KEEP_LOG; - /* regular create */ if (create_info.options & HA_LEX_CREATE_TABLE_LIKE) + { + /* CREATE TABLE ... LIKE ... */ res= mysql_create_like_table(thd, create_table, select_tables, &create_info); + } else { - res= mysql_create_table(thd, create_table->db, - create_table->table_name, &create_info, - &alter_info, 0, 0); + /* Regular CREATE TABLE */ + res= mysql_create_table(thd, create_table, + &create_info, &alter_info); } if (!res) - my_ok(thd); + my_ok(thd); } - /* put tables back for PS rexecuting */ end_with_restore_list: - lex->link_first_table_back(create_table, link_to_local); break; } case SQLCOM_CREATE_INDEX: @@ -2705,7 +2707,8 @@ end_with_restore_list: } /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ - first_table->skip_temporary= 1; + first_table->open_type= OT_BASE_ONLY; + } else { @@ -7194,6 +7197,34 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) } +/** + Set proper open mode and table type for element representing target table + of CREATE TABLE statement, also adjust statement table list if necessary. +*/ + +void create_table_set_open_action_and_adjust_tables(LEX *lex) +{ + TABLE_LIST *create_table= lex->query_tables; + + if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) + create_table->open_type= OT_TEMPORARY_ONLY; + else if (!lex->select_lex.item_list.elements) + create_table->open_type= OT_BASE_ONLY; + + if (!lex->select_lex.item_list.elements) + { + /* + Avoid opening and locking target table for ordinary CREATE TABLE + or CREATE TABLE LIKE for write (unlike in CREATE ... SELECT we + won't do any insertions in it anyway). Not doing this causes + problems when running CREATE TABLE IF NOT EXISTS for already + existing log table. + */ + create_table->lock_type= TL_READ; + } +} + + /** CREATE TABLE query pre-check. diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index e0461533dde..718471cc1b6 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4175,39 +4175,22 @@ bool mysql_unpack_partition(THD *thd, ha_resolve_storage_engine_name(default_db_type))); if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE) { - if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) - { - /* - This code is executed when we create table in CREATE TABLE t1 LIKE t2. - old_lex->query_tables contains table list element for t2 and the table - we are opening has name t1. - */ - if (partition_default_handling(table, part_info, FALSE, - old_lex->query_tables->table->s->path.str)) - { - result= TRUE; - goto end; - } - } - else - { - /* - When we come here we are doing a create table. In this case we - have already done some preparatory work on the old part_info - object. We don't really need this new partition_info object. - Thus we go back to the old partition info object. - We need to free any memory objects allocated on item_free_list - by the parser since we are keeping the old info from the first - parser call in CREATE TABLE. - We'll ensure that this object isn't put into table cache also - just to ensure we don't get into strange situations with the - item objects. - */ - thd->free_items(); - part_info= thd->work_part_info; - table->s->version= 0UL; - *work_part_info_used= true; - } + /* + When we come here we are doing a create table. In this case we + have already done some preparatory work on the old part_info + object. We don't really need this new partition_info object. + Thus we go back to the old partition info object. + We need to free any memory objects allocated on item_free_list + by the parser since we are keeping the old info from the first + parser call in CREATE TABLE. + We'll ensure that this object isn't put into table cache also + just to ensure we don't get into strange situations with the + item objects. + */ + thd->free_items(); + part_info= thd->work_part_info; + table->s->version= 0UL; + *work_part_info_used= true; } table->part_info= part_info; table->file->set_part_info(part_info); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9c7949eaf1d..27fdd1e2a8d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1660,39 +1660,35 @@ static bool mysql_test_create_table(Prepared_statement *stmt) LEX *lex= stmt->lex; SELECT_LEX *select_lex= &lex->select_lex; bool res= FALSE; - /* Skip first table, which is the table we are creating */ bool link_to_local; - TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); - TABLE_LIST *tables= lex->query_tables; + TABLE_LIST *create_table= lex->query_tables; + TABLE_LIST *tables= lex->create_last_non_select_table->next_global; if (create_table_precheck(thd, tables, create_table)) DBUG_RETURN(TRUE); + /* + The open and lock strategies will be set again once the + statement is executed. These values are only meaningful + for the prepare phase. + */ + create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + create_table->lock_strategy= TABLE_LIST::SHARED_MDL; + if (select_lex->item_list.elements) { - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - { - lex->link_first_table_back(create_table, link_to_local); - /* - The open and lock strategies will be set again once the - statement is executed. These values are only meaningful - for the prepare phase. - */ - create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; - create_table->lock_strategy= TABLE_LIST::SHARED_MDL; - } - if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) DBUG_RETURN(TRUE); - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - create_table= lex->unlink_first_table(&link_to_local); - select_lex->context.resolve_in_select_list= TRUE; + lex->unlink_first_table(&link_to_local); + res= select_like_stmt_test(stmt, 0, 0); + + lex->link_first_table_back(create_table, &link_to_local); } - else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + else { /* Check that the source table exist, and also record @@ -1704,8 +1700,6 @@ static bool mysql_test_create_table(Prepared_statement *stmt) DBUG_RETURN(TRUE); } - /* put tables back for PS rexecuting */ - lex->link_first_table_back(create_table, link_to_local); DBUG_RETURN(res); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 44a0d21580f..e9474d9add6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4084,7 +4084,6 @@ bool mysql_create_table_no_lock(THD *thd, thd->thread_specific_used= TRUE; } - write_create_table_bin_log(thd, create_info, internal_tmp_table); error= FALSE; unlock_and_end: pthread_mutex_unlock(&LOCK_open); @@ -4109,21 +4108,18 @@ warn: Database and name-locking aware wrapper for mysql_create_table_no_lock(), */ -bool mysql_create_table(THD *thd, const char *db, const char *table_name, +bool mysql_create_table(THD *thd, TABLE_LIST *create_table, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool internal_tmp_table, - uint select_field_count) + Alter_info *alter_info) { - MDL_request target_mdl_request; - bool has_target_mdl_lock= FALSE; bool result; DBUG_ENTER("mysql_create_table"); /* Wait for any database locks */ pthread_mutex_lock(&LOCK_lock_db); while (!thd->killed && - my_hash_search(&lock_db_cache,(uchar*) db, strlen(db))) + my_hash_search(&lock_db_cache, (uchar*)create_table->db, + create_table->db_length)) { wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); pthread_mutex_lock(&LOCK_lock_db); @@ -4137,47 +4133,47 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, creating_table++; pthread_mutex_unlock(&LOCK_lock_db); - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) + /* + Open or obtain an exclusive metadata lock on table being created. + */ + if (open_and_lock_tables_derived(thd, thd->lex->query_tables, FALSE, + MYSQL_OPEN_TAKE_UPGRADABLE_MDL)) { - target_mdl_request.init(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE); - if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) - { - result= TRUE; - goto unlock; - } - if (target_mdl_request.ticket == NULL) - { - /* Table exists and is locked by some other thread. */ - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), - table_name); - create_info->table_existed= 1; - result= FALSE; - write_create_table_bin_log(thd, create_info, internal_tmp_table); - } - else - { - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); - result= TRUE; - } - goto unlock; - } - /* Got lock. */ - DEBUG_SYNC(thd, "locked_table_name"); - has_target_mdl_lock= TRUE; + result= TRUE; + goto unlock; } - result= mysql_create_table_no_lock(thd, db, table_name, create_info, - alter_info, - internal_tmp_table, - select_field_count); + /* Got lock. */ + DEBUG_SYNC(thd, "locked_table_name"); + + result= mysql_create_table_no_lock(thd, create_table->db, + create_table->table_name, create_info, + alter_info, FALSE, 0); + + /* + Don't write statement if: + - Table creation has failed + - Table has already existed + - Row-based logging is used and we are creating a temporary table + Otherwise, the statement shall be binlogged. + */ + if (!result && + !create_info->table_existed && + (!thd->current_stmt_binlog_row_based || + (thd->current_stmt_binlog_row_based && + !(create_info->options & HA_LEX_CREATE_TMP_TABLE)))) + write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + + if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) + { + /* + close_thread_tables() takes care about both closing open tables (which + might be still around in case of error) and releasing metadata locks. + */ + close_thread_tables(thd); + } unlock: - if (has_target_mdl_lock) - thd->mdl_context.release_lock(target_mdl_request.ticket); - pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) pthread_cond_signal(&COND_refresh); @@ -5158,55 +5154,6 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) } - -/** - @brief Create frm file based on I_S table - - @param[in] thd thread handler - @param[in] schema_table I_S table - @param[in] dst_path path where frm should be created - @param[in] create_info Create info - - @return Operation status - @retval 0 success - @retval 1 error -*/ - - -bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, - char *dst_path, HA_CREATE_INFO *create_info) -{ - HA_CREATE_INFO local_create_info; - Alter_info alter_info; - bool tmp_table= (create_info->options & HA_LEX_CREATE_TMP_TABLE); - uint keys= schema_table->table->s->keys; - uint db_options= 0; - DBUG_ENTER("mysql_create_like_schema_frm"); - - bzero((char*) &local_create_info, sizeof(local_create_info)); - local_create_info.db_type= schema_table->table->s->db_type(); - local_create_info.row_type= schema_table->table->s->row_type; - local_create_info.default_table_charset=default_charset_info; - alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE); - schema_table->table->use_all_columns(); - if (mysql_prepare_alter_table(thd, schema_table->table, - &local_create_info, &alter_info)) - DBUG_RETURN(1); - if (mysql_prepare_create_table(thd, &local_create_info, &alter_info, - tmp_table, &db_options, - schema_table->table->file, - &schema_table->table->s->key_info, &keys, 0)) - DBUG_RETURN(1); - local_create_info.max_rows= 0; - if (mysql_create_frm(thd, dst_path, NullS, NullS, - &local_create_info, alter_info.create_list, - keys, schema_table->table->s->key_info, - schema_table->table->file)) - DBUG_RETURN(1); - DBUG_RETURN(0); -} - - /* Create a table identical to the specified table @@ -5225,12 +5172,8 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { - char src_path[FN_REFLEN + 1], dst_path[FN_REFLEN + 1]; - uint dst_path_length; - bool has_mdl_lock= FALSE; - char *db= table->db; - char *table_name= table->table_name; - int err; + HA_CREATE_INFO local_create_info; + Alter_info local_alter_info; bool res= TRUE; uint not_used; #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -5242,161 +5185,63 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, /* - By opening source table and thus acquiring shared metadata lock on it - we guarantee that it exists and no concurrent DDL operation will mess - with it. Later we also take an exclusive metadata lock on target table - name, which makes copying of .frm file, call to ha_create_table() and - binlogging atomic against concurrent DML and DDL operations on target - table. Thus by holding both these "locks" we ensure that our statement - is properly isolated from all concurrent operations which matter. + We the open source table to get its description in HA_CREATE_INFO + and Alter_info objects. This also acquires a shared metadata lock + on this table which ensures that no concurrent DDL operation will + mess with it. + Also in case when we create non-temporary table open_tables() + call obtains an exclusive metadata lock on target table ensuring + that we can safely perform table creation. + Thus by holding both these locks we ensure that our statement is + properly isolated from all concurrent operations which matter. */ - if (open_tables(thd, &src_table, ¬_used, 0)) - DBUG_RETURN(TRUE); - - /* - For bug#25875, Newly created table through CREATE TABLE .. LIKE - has no ndb_dd attributes; - Add something to get possible tablespace info from src table, - it can get valid tablespace name only for disk-base ndb table - */ - if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN))) - { - create_info->tablespace= ts_name; - create_info->storage_media= HA_SM_DISK; - } - - strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS); - - DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000);); - - /* - Check that destination tables does not exist. Note that its name - was already checked when it was added to the table list. - */ - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) - { - if (find_temporary_table(thd, db, table_name)) - goto table_exists; - dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path)); - create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE; - } - else - { - table->mdl_request.init(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE); - if (thd->mdl_context.try_acquire_exclusive_lock(&table->mdl_request)) - DBUG_RETURN(TRUE); - - if (table->mdl_request.ticket == NULL) - goto table_exists; - - DEBUG_SYNC(thd, "locked_table_name"); - has_mdl_lock= TRUE; - - dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1, - db, table_name, reg_ext, 0); - if (!access(dst_path, F_OK)) - goto table_exists; - } - - DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); - - if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE)) - flags|= MY_SYNC; - - /* - Create a new table by copying from source table - and sync the new table if the flag MY_SYNC is set - - TODO: Obtaining LOCK_open mutex here is actually a legacy from the - times when some operations (e.g. I_S implementation) ignored - exclusive metadata lock on target table. Also some engines - (e.g. NDB cluster) require that LOCK_open should be held - during the call to ha_create_table() (See bug #28614 for more - info). So we should double check and probably fix this code - to not acquire this mutex. - */ - pthread_mutex_lock(&LOCK_open); - if (src_table->schema_table) - { - if (mysql_create_like_schema_frm(thd, src_table, dst_path, create_info)) - { - pthread_mutex_unlock(&LOCK_open); - goto err; - } - } - else if (my_copy(src_path, dst_path, flags)) - { - if (my_errno == ENOENT) - my_error(ER_BAD_DB_ERROR,MYF(0),db); - else - my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno); - pthread_mutex_unlock(&LOCK_open); + if (open_tables(thd, &thd->lex->query_tables, ¬_used, 0)) goto err; - } + src_table->table->use_all_columns(); - /* - As mysql_truncate don't work on a new table at this stage of - creation, instead create the table directly (for both normal - and temporary tables). - */ + /* Fill HA_CREATE_INFO and Alter_info with description of source table. */ + bzero((char*) &local_create_info, sizeof(local_create_info)); + local_create_info.db_type= src_table->table->s->db_type(); + local_create_info.row_type= src_table->table->s->row_type; + if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info, + &local_alter_info)) + goto err; #ifdef WITH_PARTITION_STORAGE_ENGINE - /* - For partitioned tables we need to copy the .par file as well since - it is used in open_table_def to even be able to create a new handler. - There is no way to find out here if the original table is a - partitioned table so we copy the file and ignore any errors. - */ - fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT)); - strmov(dst_path, tmp_path); - fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT)); - strmov(src_path, tmp_path); - my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)); + /* Partition info is not handled by mysql_prepare_alter_table() call. */ + if (src_table->table->part_info) + thd->work_part_info= src_table->table->part_info->get_clone(); #endif - DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000);); + /* + Adjust description of source table before using it for creation of + target table. - dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm - if (thd->variables.keep_files_on_create) - create_info->options|= HA_CREATE_KEEP_FILES; - err= ha_create_table(thd, dst_path, db, table_name, create_info, 1); - pthread_mutex_unlock(&LOCK_open); + Similarly to SHOW CREATE TABLE we ignore MAX_ROWS attribute of + temporary table which represents I_S table. + */ + if (src_table->schema_table) + local_create_info.max_rows= 0; + /* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */ + local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS; + /* Replace type of source table with one specified in the statement. */ + local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE; + local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE; + /* Reset auto-increment counter for the new table. */ + local_create_info.auto_increment_value= 0; - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) - { - if (err || !open_temporary_table(thd, dst_path, db, table_name, 1)) - { - (void) rm_temporary_table(create_info->db_type, - dst_path); /* purecov: inspected */ - goto err; /* purecov: inspected */ - } - thd->thread_specific_used= TRUE; - } - else if (err) - { - (void) quick_rm_table(create_info->db_type, db, - table_name, 0); /* purecov: inspected */ - goto err; /* purecov: inspected */ - } - -goto binlog; - -table_exists: - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - { - char warn_buff[MYSQL_ERRMSG_SIZE]; - my_snprintf(warn_buff, sizeof(warn_buff), - ER(ER_TABLE_EXISTS_ERROR), table_name); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR,warn_buff); - } - else - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name, + &local_create_info, &local_alter_info, + FALSE, 0)) || + local_create_info.table_existed) goto err; - } -binlog: - DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000);); + /* + Ensure that we have an exclusive lock on target table if we are creating + non-temporary table. + */ + DBUG_ASSERT((create_info->options & HA_LEX_CREATE_TMP_TABLE) || + thd->mdl_context.is_exclusive_lock_owner(MDL_key::TABLE, table->db, + table->table_name)); /* We have to write the query before we unlock the tables. @@ -5463,12 +5308,7 @@ binlog: else write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - res= FALSE; - err: - if (has_mdl_lock) - thd->mdl_context.release_lock(table->mdl_request.ticket); - DBUG_RETURN(res); } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 823dca5d93f..8719938d85d 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -445,7 +445,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) Also prevent DROP TRIGGER from opening temporary table which might shadow base table on which trigger to be dropped is defined. */ - tables->skip_temporary= TRUE; + tables->open_type= OT_BASE_ONLY; /* Keep consistent with respect to other DDL statements */ mysql_ha_rm_tables(thd, tables); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index b36ff6b6743..0f920bca101 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1259,7 +1259,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, tbl; tbl= (view_tables_tail= tbl)->next_global) { - tbl->skip_temporary= 1; + tbl->open_type= OT_BASE_ONLY; tbl->belong_to_view= top_view; tbl->referencing_view= table; tbl->prelocking_placeholder= table->prelocking_placeholder; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 826ec63a93e..1e6a7c4d50f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1781,6 +1781,7 @@ create: ha_resolve_storage_engine_name(lex->create_info.db_type), $5->table.str); } + create_table_set_open_action_and_adjust_tables(lex); } | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON table_ident @@ -3921,7 +3922,7 @@ size_number: create2: '(' create2a {} | opt_create_table_options - opt_partitioning + opt_create_partitioning create3 {} | LIKE table_ident { @@ -3955,9 +3956,9 @@ create2: create2a: create_field_list ')' opt_create_table_options - opt_partitioning + opt_create_partitioning create3 {} - | opt_partitioning + | opt_create_partitioning create_select ')' { Select->set_braces(1);} union_opt {} @@ -3973,6 +3974,19 @@ create3: union_opt {} ; +opt_create_partitioning: + opt_partitioning + { + /* + Remove all tables used in PARTITION clause from the global table + list. Partitioning with subqueries is not allowed anyway. + */ + TABLE_LIST *last_non_sel_table= Lex->create_last_non_select_table; + last_non_sel_table->next_global= 0; + Lex->query_tables_last= &last_non_sel_table->next_global; + } + ; + /* This part of the parser is about handling of the partition information. diff --git a/sql/table.h b/sql/table.h index eacf4f6085c..b44c2e9f3be 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1069,6 +1069,16 @@ public: }; +/** + Type of table which can be open for an element of table list. +*/ + +enum enum_open_type +{ + OT_TEMPORARY_OR_BASE= 0, OT_TEMPORARY_ONLY, OT_BASE_ONLY +}; + + /* Table reference in the FROM clause. @@ -1330,7 +1340,11 @@ struct TABLE_LIST bool cacheable_table; /* stop PS caching */ /* used in multi-upd/views privilege check */ bool table_in_first_from_clause; - bool skip_temporary; /* this table shouldn't be temporary */ + /** + Specifies which kind of table should be open for this element + of table list. + */ + enum enum_open_type open_type; /* TRUE if this merged view contain auto_increment field */ bool contain_auto_increment; bool multitable_view; /* TRUE iff this is multitable view */ From 9e17ef84ccf4e00648eb7aab9d98dd4eab3b6e5a Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 12:46:16 +0100 Subject: [PATCH 150/466] Backport of revno: 2617.68.3 Followup to Bug#42546 Backup: RESTORE fails, thinking it finds an existing table This patch updates lowercase_table2.test with the changed error message CREATE TABLE produces if it fails because it finds an matching TABLE_SHARE in the TDC even if the .FRM/.MYD has been removed from disk. With the changes introduced in Bug#42546, CREATE TABLE uses open_tables() which will find the TDC entry and fail in open_table_from_share() with ER_FILE_NOT_FOUND. Before, CREATE TABLE would not use open_tables() and fail with ER_TABLE_EXISTS_ERROR upon finding the TDC entry in mysql_create_table_no_lock(). --- mysql-test/r/lowercase_table2.result | 16 +++++++++------- mysql-test/t/lowercase_table2.test | 14 +++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/lowercase_table2.result b/mysql-test/r/lowercase_table2.result index cf87fd1b5a4..b621a466a29 100644 --- a/mysql-test/r/lowercase_table2.result +++ b/mysql-test/r/lowercase_table2.result @@ -226,10 +226,9 @@ drop table t_bug44738_UPPERCASE; create table t_bug44738_UPPERCASE (i int); drop table t_bug44738_UPPERCASE; # Finally, let us check that another issue which was exposed by -# the original test case is solved. I.e. that fuse in CREATE TABLE -# which ensures that table is not created if there is an entry for -# it in TDC even though it was removed from disk uses normalized -# version of the table name. +# the original test case is solved. I.e. that the table is not +# created if there is an entry for it in TDC even though it was +# removed from disk. create table t_bug44738_UPPERCASE (i int) engine = myisam; # Load table definition in TDC. select table_schema, table_name, table_comment from information_schema.tables @@ -237,10 +236,13 @@ where table_schema = 'test' and table_name like 't_bug44738_%'; table_schema table_name table_comment test t_bug44738_UPPERCASE # Simulate manual removal of the table. -# After manual removal of table still there should be an entry for table -# in TDC so attempt to create table with the same name should fail. +# Check that still there is an entry for table in TDC. +show open tables like 't_bug44738_%'; +Database Table In_use Name_locked +test t_bug44738_uppercase 0 0 +# So attempt to create table with the same name should fail. create table t_bug44738_UPPERCASE (i int); -ERROR 42S01: Table 't_bug44738_uppercase' already exists +ERROR HY000: Can't find file: 't_bug44738_uppercase' (errno: 2) # And should succeed after FLUSH TABLES. flush tables; create table t_bug44738_UPPERCASE (i int); diff --git a/mysql-test/t/lowercase_table2.test b/mysql-test/t/lowercase_table2.test index 92add60616a..b8c7f532cde 100644 --- a/mysql-test/t/lowercase_table2.test +++ b/mysql-test/t/lowercase_table2.test @@ -201,10 +201,9 @@ create table t_bug44738_UPPERCASE (i int); drop table t_bug44738_UPPERCASE; --echo # Finally, let us check that another issue which was exposed by ---echo # the original test case is solved. I.e. that fuse in CREATE TABLE ---echo # which ensures that table is not created if there is an entry for ---echo # it in TDC even though it was removed from disk uses normalized ---echo # version of the table name. +--echo # the original test case is solved. I.e. that the table is not +--echo # created if there is an entry for it in TDC even though it was +--echo # removed from disk. create table t_bug44738_UPPERCASE (i int) engine = myisam; --echo # Load table definition in TDC. select table_schema, table_name, table_comment from information_schema.tables @@ -214,9 +213,10 @@ let $MYSQLD_DATADIR= `select @@datadir`; --remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.frm --remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.MYD --remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.MYI ---echo # After manual removal of table still there should be an entry for table ---echo # in TDC so attempt to create table with the same name should fail. ---error ER_TABLE_EXISTS_ERROR +--echo # Check that still there is an entry for table in TDC. +show open tables like 't_bug44738_%'; +--echo # So attempt to create table with the same name should fail. +--error ER_FILE_NOT_FOUND create table t_bug44738_UPPERCASE (i int); --echo # And should succeed after FLUSH TABLES. flush tables; From 3173cf335b1cca8bc2265e060c4e983c038bbd0e Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 13:02:37 +0100 Subject: [PATCH 151/466] Backport of revno: 2617.68.43 Bug #47335 assert in get_table_share The assert would happen if ALTER VIEW was used to alter a view (existing or non-existing) and a temporary table with the same name already existed. The assert is triggered if the current statement does not have a MDL lock on the view to be altered. This would happen because open_table() would open the temporary table instead and MDL locks are not taken for temporary tables (since they are local to one connection). The patch changes open_type for CREATE/ALTER VIEW to OT_BASE_ONLY. This prevents open_table() from trying to open a temporary table with the same name should one exist. Now the view will be altered if it exists or ER_NO_SUCH_TABLE will be reported if it does not. Test case added to view.test --- mysql-test/r/view.result | 17 +++++++++++++++++ mysql-test/t/view.test | 21 +++++++++++++++++++++ sql/sql_view.cc | 1 + 3 files changed, 39 insertions(+) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 7e9739173df..cb53bf462f5 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3959,3 +3959,20 @@ DROP TABLE t1; # ----------------------------------------------------------------- # -- End of 5.1 tests. # ----------------------------------------------------------------- +# +# Bug #47335 assert in get_table_share +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +CREATE TEMPORARY TABLE t1 (id INT); +ALTER VIEW t1 AS SELECT 1 AS f1; +ERROR 42S02: Table 'test.t1' doesn't exist +DROP TABLE t1; +CREATE VIEW v1 AS SELECT 1 AS f1; +CREATE TEMPORARY TABLE v1 (id INT); +ALTER VIEW v1 AS SELECT 2 AS f1; +DROP TABLE v1; +SELECT * FROM v1; +f1 +2 +DROP VIEW v1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index c3ff58880c9..13a557dda60 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3906,3 +3906,24 @@ DROP TABLE t1; --echo # ----------------------------------------------------------------- --echo # -- End of 5.1 tests. --echo # ----------------------------------------------------------------- + +--echo # +--echo # Bug #47335 assert in get_table_share +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +--enable_warnings + +CREATE TEMPORARY TABLE t1 (id INT); +--error ER_NO_SUCH_TABLE +ALTER VIEW t1 AS SELECT 1 AS f1; +DROP TABLE t1; + +CREATE VIEW v1 AS SELECT 1 AS f1; +CREATE TEMPORARY TABLE v1 (id INT); +ALTER VIEW v1 AS SELECT 2 AS f1; +DROP TABLE v1; +SELECT * FROM v1; +DROP VIEW v1; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0f920bca101..6d2836afc0d 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -397,6 +397,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, lex->link_first_table_back(view, link_to_local); view->open_strategy= TABLE_LIST::OPEN_STUB; view->lock_strategy= TABLE_LIST::EXCLUSIVE_MDL; + view->open_type= OT_BASE_ONLY; if (open_and_lock_tables(thd, lex->query_tables)) { From b6fb4dbab23490eca5c181e3fb68ce61bdd36a87 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 13:15:20 +0100 Subject: [PATCH 152/466] Backport of revno: 2617.68.45 Bug #47635 assert in start_waiting_global_read_lock during CREATE VIEW The problem was that CREATE VIEW would trigger an assert if a temporary table with the same name already existed. This bug was fixed by the patch for Bug#47335. CREATE/ALTER VIEW will now ignore temporary tables. See Bug#47335 for more information. Test case added to view.test. --- mysql-test/r/view.result | 15 +++++++++++++++ mysql-test/t/view.test | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index cb53bf462f5..c097b70680f 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3976,3 +3976,18 @@ SELECT * FROM v1; f1 2 DROP VIEW v1; +# +# Bug #47635 assert in start_waiting_global_read_lock +# during CREATE VIEW +# +DROP TABLE IF EXISTS t1, t2; +DROP VIEW IF EXISTS t2; +CREATE TABLE t1 (f1 integer); +CREATE TEMPORARY TABLE IF NOT EXISTS t1 (f1 integer); +CREATE TEMPORARY TABLE t2 (f1 integer); +DROP TABLE t1; +FLUSH TABLES WITH READ LOCK; +CREATE VIEW t2 AS SELECT * FROM t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +UNLOCK TABLES; +DROP TABLE t1, t2; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 13a557dda60..880b4b6a645 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1,3 +1,4 @@ + --disable_warnings drop table if exists t1,t2,t3,t4,t9,`t1a``b`,v1,v2,v3,v4,v5,v6; drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6; @@ -3927,3 +3928,25 @@ ALTER VIEW v1 AS SELECT 2 AS f1; DROP TABLE v1; SELECT * FROM v1; DROP VIEW v1; + + +--echo # +--echo # Bug #47635 assert in start_waiting_global_read_lock +--echo # during CREATE VIEW +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +DROP VIEW IF EXISTS t2; +--enable_warnings + +CREATE TABLE t1 (f1 integer); +CREATE TEMPORARY TABLE IF NOT EXISTS t1 (f1 integer); +CREATE TEMPORARY TABLE t2 (f1 integer); +DROP TABLE t1; +FLUSH TABLES WITH READ LOCK; +--error ER_CANT_UPDATE_WITH_READLOCK +CREATE VIEW t2 AS SELECT * FROM t1; + +UNLOCK TABLES; +DROP TABLE t1, t2; From 1cfcd2d210b8a069496571d3d35bf3b8bd5ee67e Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 13:37:18 +0100 Subject: [PATCH 153/466] Backport of revno: 3673 Bug #47313 assert in check_key_in_view during CALL procedure View definitions are inlined in a stored procedure when the procedure is fist called. This means that if a temporary table is later added with the same name as the view, the stored procedure will still use the view. This happens even if temporary tables normally shadow base tables/views. The reason for the assert was that even if the stored procedure referenced the view, open_table() still tried to open the temporary table. This "half view/half temporary table" state caused the assert. The bug was not present in 5.1 as open_table() is not called for the view there. This code was changed with the introduction of MDL in order to properly lock the view and any objects it refers to. This patch fixes the problem by instructing open_table() to open base tables/views (using OT_BASE_ONLY) when reopening tables/views used by stored procedures. This also means that a prepared statement is no longer invalidated if a temporary table is created with the same name as a view used in the prepared statement. Test case added to sp.test. The test case also demonstrates the effect of sp cache invalidation between CALLs. --- mysql-test/r/ps_ddl.result | 63 ++++++++++++++++++++++++++++- mysql-test/r/sp.result | 59 +++++++++++++++++++++++++++ mysql-test/t/ps_ddl.test | 57 ++++++++++++++++++++++++++- mysql-test/t/sp.test | 81 ++++++++++++++++++++++++++++++++++++++ sql/sql_view.cc | 14 +++++++ 5 files changed, 271 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index 3d57c8f7332..c72d129c8e4 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -707,6 +707,9 @@ deallocate prepare stmt; ===================================================================== Part 16: VIEW -> TEMPORARY TABLE transitions ===================================================================== +# +# Test 1: Merged view +# create table t2 (a int); insert into t2 (a) values (1); create view t1 as select * from t2; @@ -720,16 +723,72 @@ SUCCESS create temporary table t1 (a int); execute stmt; a -call p_verify_reprepare_count(1); +1 +call p_verify_reprepare_count(0); SUCCESS drop view t1; execute stmt; -a +ERROR 42S02: Table 'test.t1' doesn't exist call p_verify_reprepare_count(0); SUCCESS drop table t2; +drop temporary table t1; +deallocate prepare stmt; +# +# Test 2: Materialized view +# +create table t2 (a int); +insert into t2 (a) values (1); +create algorithm = temptable view t1 as select * from t2; +prepare stmt from "select * from t1"; +execute stmt; +a +1 +call p_verify_reprepare_count(0); +SUCCESS + +create temporary table t1 (a int); +execute stmt; +a +1 +call p_verify_reprepare_count(0); +SUCCESS + +drop view t1; +execute stmt; +ERROR 42S02: Table 'test.t1' doesn't exist +call p_verify_reprepare_count(0); +SUCCESS + +drop table t2; +drop temporary table t1; +deallocate prepare stmt; +# +# Test 3: View referencing an Information schema table +# +create view t1 as select table_name from information_schema.views; +prepare stmt from "select * from t1"; +execute stmt; +table_name +t1 +call p_verify_reprepare_count(0); +SUCCESS + +create temporary table t1 (a int); +execute stmt; +table_name +t1 +call p_verify_reprepare_count(0); +SUCCESS + +drop view t1; +execute stmt; +table_name +call p_verify_reprepare_count(0); +SUCCESS + drop temporary table t1; deallocate prepare stmt; ===================================================================== diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bcc738d695c..08c7831c955 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7076,3 +7076,62 @@ SELECT routine_comment FROM information_schema.routines WHERE routine_name = "p1 routine_comment 12345678901234567890123456789012345678901234567890123456789012345678901234567890 DROP PROCEDURE p1; +# +# Bug #47313 assert in check_key_in_view during CALL procedure +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS t1, t2_unrelated; +DROP PROCEDURE IF EXISTS p1; +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; +# t1 refers to the view +CALL p1(1); +ERROR HY000: The target table t1 of the INSERT is not insertable-into +CREATE TEMPORARY TABLE t1 (f1 INT); +# t1 still refers to the view since it was inlined +CALL p1(2); +ERROR HY000: The target table t1 of the INSERT is not insertable-into +DROP VIEW t1; +# t1 now refers to the temporary table +CALL p1(3); +# Check which values were inserted into the temp table. +SELECT * FROM t1; +f1 +3 +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; +# Now test what happens if the sp cache is invalidated. +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; +CREATE VIEW v2_unrelated AS SELECT 1 AS r1; +# Load the procedure into the sp cache +CALL p1(4); +ERROR HY000: The target table t1 of the INSERT is not insertable-into +CREATE TEMPORARY TABLE t1 (f1 int); +ALTER VIEW v2_unrelated AS SELECT 2 AS r1; +# Alter view causes the sp cache to be invalidated. +# Now t1 refers to the temporary table, not the view. +CALL p1(5); +# Check which values were inserted into the temp table. +SELECT * FROM t1; +f1 +5 +DROP TEMPORARY TABLE t1; +DROP VIEW t1, v2_unrelated; +DROP PROCEDURE p1; +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE TEMPORARY TABLE t1 (f1 INT); +# t1 refers to the temporary table +CALL p1(6); +CREATE VIEW t1 AS SELECT 10 AS f1; +# Create view causes the sp cache to be invalidated. +# t1 still refers to the temporary table since it shadows the view. +CALL p1(7); +DROP VIEW t1; +# Check which values were inserted into the temp table. +SELECT * FROM t1; +f1 +6 +7 +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index fe17bca1eba..e00d63aaedc 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -642,6 +642,9 @@ deallocate prepare stmt; --echo Part 16: VIEW -> TEMPORARY TABLE transitions --echo ===================================================================== +--echo # +--echo # Test 1: Merged view +--echo # create table t2 (a int); insert into t2 (a) values (1); create view t1 as select * from t2; @@ -651,9 +654,13 @@ execute stmt; call p_verify_reprepare_count(0); create temporary table t1 (a int); +# t1 still refers to the view - no reprepare has been done. execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); + drop view t1; +# t1 still refers to the, now deleted, view - no reprepare has been done. +--error ER_NO_SUCH_TABLE execute stmt; call p_verify_reprepare_count(0); @@ -661,6 +668,54 @@ drop table t2; drop temporary table t1; deallocate prepare stmt; +--echo # +--echo # Test 2: Materialized view +--echo # +create table t2 (a int); +insert into t2 (a) values (1); +create algorithm = temptable view t1 as select * from t2; + +prepare stmt from "select * from t1"; +execute stmt; +call p_verify_reprepare_count(0); + +create temporary table t1 (a int); +# t1 still refers to the view - no reprepare has been done. +execute stmt; +call p_verify_reprepare_count(0); + +drop view t1; +# t1 still refers to the, now deleted, view - no reprepare has been done. +--error ER_NO_SUCH_TABLE +execute stmt; +call p_verify_reprepare_count(0); + +drop table t2; +drop temporary table t1; +deallocate prepare stmt; + +--echo # +--echo # Test 3: View referencing an Information schema table +--echo # +create view t1 as select table_name from information_schema.views; + +prepare stmt from "select * from t1"; +execute stmt; +call p_verify_reprepare_count(0); + +create temporary table t1 (a int); +# t1 has been substituted with a reference to the IS table +execute stmt; +call p_verify_reprepare_count(0); + +drop view t1; +# Since the IS table has been substituted in, the statement still works +execute stmt; +call p_verify_reprepare_count(0); + +drop temporary table t1; +deallocate prepare stmt; + --echo ===================================================================== --echo Part 17: VIEW -> VIEW (DDL) transitions --echo ===================================================================== diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 7cf2fcf9bb7..a29275eeda4 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -8433,3 +8433,84 @@ SELECT routine_comment FROM information_schema.routines WHERE routine_name = "p1 DROP PROCEDURE p1; + +--echo # +--echo # Bug #47313 assert in check_key_in_view during CALL procedure +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS t1, t2_unrelated; +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; + +--echo # t1 refers to the view +--error ER_NON_INSERTABLE_TABLE +CALL p1(1); + +CREATE TEMPORARY TABLE t1 (f1 INT); + +--echo # t1 still refers to the view since it was inlined +--error ER_NON_INSERTABLE_TABLE +CALL p1(2); + +DROP VIEW t1; + +--echo # t1 now refers to the temporary table +CALL p1(3); + +--echo # Check which values were inserted into the temp table. +SELECT * FROM t1; + +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; + +--echo # Now test what happens if the sp cache is invalidated. + +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; +CREATE VIEW v2_unrelated AS SELECT 1 AS r1; + +--echo # Load the procedure into the sp cache +--error ER_NON_INSERTABLE_TABLE +CALL p1(4); + +CREATE TEMPORARY TABLE t1 (f1 int); + +ALTER VIEW v2_unrelated AS SELECT 2 AS r1; + +--echo # Alter view causes the sp cache to be invalidated. +--echo # Now t1 refers to the temporary table, not the view. +CALL p1(5); + +--echo # Check which values were inserted into the temp table. +SELECT * FROM t1; + +DROP TEMPORARY TABLE t1; +DROP VIEW t1, v2_unrelated; +DROP PROCEDURE p1; + +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE TEMPORARY TABLE t1 (f1 INT); + +--echo # t1 refers to the temporary table +CALL p1(6); + +CREATE VIEW t1 AS SELECT 10 AS f1; + +--echo # Create view causes the sp cache to be invalidated. +--echo # t1 still refers to the temporary table since it shadows the view. +CALL p1(7); + +DROP VIEW t1; + +--echo # Check which values were inserted into the temp table. +SELECT * FROM t1; + +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; + + diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 6d2836afc0d..17ac10ebfb9 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1137,6 +1137,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, table->view_db.length= table->db_length; table->view_name.str= table->table_name; table->view_name.length= table->table_name_length; + /* + We don't invalidate a prepared statement when a view changes, + or when someone creates a temporary table. + Instead, the view is inlined into the body of the statement + upon the first execution. Below, make sure that on + re-execution of a prepared statement we don't prefer + a temporary table to the view, if the view name was shadowed + with a temporary table with the same name. + This assignment ensures that on re-execution open_table() will + not try to call find_temporary_table() for this TABLE_LIST, + but will invoke open_table_from_share(), which will + eventually call this function. + */ + table->open_type= OT_BASE_ONLY; /*TODO: md5 test here and warning if it is differ */ From 28b0eeff28f9ae8c834719bb852094f64f7157da Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 14:15:50 +0100 Subject: [PATCH 154/466] Backport of revno: 3514 Bug#40181 Made use of tdc_remove_table instead of just setting share->version to 0 to make sure all unused table instances go away as part of CREATE/ALTER TABLE. --- mysql-test/r/partition.result | 9 +++++++++ mysql-test/t/partition.test | 13 +++++++++++++ sql/sql_partition.cc | 21 +++++++++++++++------ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 543a70f9a2a..e9bbc011f7b 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -65,6 +65,15 @@ show indexes from t1; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t1 1 a 1 a A 1 NULL NULL YES BTREE drop table t1; +create table t1 (a int) +partition by hash (a); +create index i on t1 (a); +insert into t1 values (1); +insert into t1 select * from t1; +create index i on t1 (a); +ERROR 42000: Duplicate key name 'i' +create index i2 on t1 (a); +drop table t1; CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a)) ENGINE=MyISAM PARTITION BY HASH (a); diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 8ab91f23522..01e885a527c 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -74,6 +74,19 @@ analyze table t1; show indexes from t1; drop table t1; +# +# Bug#40181: hang if create index +# +create table t1 (a int) +partition by hash (a); +create index i on t1 (a); +insert into t1 values (1); +insert into t1 select * from t1; +--error ER_DUP_KEYNAME +create index i on t1 (a); +create index i2 on t1 (a); +drop table t1; + # # Bug#36001: Partitions: spelling and using some error messages # diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 718471cc1b6..52657deed83 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4189,7 +4189,9 @@ bool mysql_unpack_partition(THD *thd, */ thd->free_items(); part_info= thd->work_part_info; - table->s->version= 0UL; + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, + table->s->db.str, + table->s->table_name.str); *work_part_info_used= true; } table->part_info= part_info; @@ -4482,12 +4484,17 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, /* We are going to manipulate the partition info on the table object - so we need to ensure that the data structure of the table object - is freed by setting version to 0. table->s->version= 0 forces a - flush of the table object in close_thread_tables(). + so we need to ensure that the table instances cached and all other + instances are properly closed. */ if (table->part_info) - table->s->version= 0L; + { + pthread_mutex_lock(&LOCK_open); + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, + table->s->db.str, + table->s->table_name.str); + pthread_mutex_unlock(&LOCK_open); + } thd->work_part_info= thd->lex->part_info; if (thd->work_part_info && @@ -6242,7 +6249,9 @@ static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt) alter_partition_lock_handling() and the table is closed by close_thread_tables() instead. */ - table->s->version= 0; + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, + table->s->db.str, + table->s->table_name.str); } } pthread_mutex_unlock(&LOCK_open); From 2945f773f21cfefa3f8c4805dffc459697dcc118 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 14:26:00 +0100 Subject: [PATCH 155/466] Backport of revno: 2617.68.37 Bug #46654 False deadlock on concurrent DML/DDL with partitions, inconsistent behavior The problem was that if one connection is running a multi-statement transaction which involves a single partitioned table, and another connection attempts to alter the table, the first connection gets ER_LOCK_DEADLOCK and cannot proceed anymore, even when the ALTER TABLE statement in another connection has timed out or failed. The reason for this was that the prepare phase for ALTER TABLE for partitioned tables removed all instances of the table from the table definition cache before it started waiting on the lock. The transaction running in the first connection would notice this and report ER_LOCK_DEADLOCK. This patch changes the prep_alter_part_table() ALTER TABLE code so that tdc_remove_table() is no longer called. Instead, only the TABLE instance changed by prep_alter_part_table() is marked as needing reopen. The patch also removes an unnecessary call to tdc_remove_table() from mysql_unpack_partition() as the changed TABLE object is destroyed by the caller at a later point. Test case added in partition_sync.test. --- mysql-test/r/partition_sync.result | 32 +++++++++++++++++++ mysql-test/t/partition_sync.test | 51 ++++++++++++++++++++++++++++++ sql/sql_base.cc | 8 ++--- sql/sql_handler.cc | 2 +- sql/sql_insert.cc | 2 +- sql/sql_partition.cc | 22 ++++--------- sql/sql_table.cc | 4 +++ sql/table.h | 15 +++++++-- 8 files changed, 112 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result index 41ca19426fe..fbea2e0273a 100644 --- a/mysql-test/r/partition_sync.result +++ b/mysql-test/r/partition_sync.result @@ -1,2 +1,34 @@ # Disabled until Bug#46654 False deadlock on concurrent DML/DDL # with partitions, inconsistent behavior is backported +# +# Bug #46654 False deadlock on concurrent DML/DDL +# with partitions, inconsistent behavior +# +DROP TABLE IF EXISTS tbl_with_partitions; +CREATE TABLE tbl_with_partitions ( i INT ) +PARTITION BY HASH(i); +INSERT INTO tbl_with_partitions VALUES (1); +# Connection 3 +LOCK TABLE tbl_with_partitions READ; +# Connection 1 +# Access table with disabled autocommit +SET AUTOCOMMIT = 0; +SELECT * FROM tbl_with_partitions; +i +1 +# Connection 2 +# Alter table, abort after prepare +set session debug="+d,abort_copy_table"; +ALTER TABLE tbl_with_partitions ADD COLUMN f INT; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection 1 +# Try accessing the table after Alter aborted. +# This used to give ER_LOCK_DEADLOCK. +SELECT * FROM tbl_with_partitions; +i +1 +# Connection 3 +UNLOCK TABLES; +# Connection 1 +# Cleanup +DROP TABLE tbl_with_partitions; diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test index 5d2b25e87f3..672e4cc0562 100644 --- a/mysql-test/t/partition_sync.test +++ b/mysql-test/t/partition_sync.test @@ -39,6 +39,57 @@ #DROP TABLE t1; +--echo # +--echo # Bug #46654 False deadlock on concurrent DML/DDL +--echo # with partitions, inconsistent behavior +--echo # + +--disable_warnings +DROP TABLE IF EXISTS tbl_with_partitions; +--enable_warnings + +CREATE TABLE tbl_with_partitions ( i INT ) + PARTITION BY HASH(i); +INSERT INTO tbl_with_partitions VALUES (1); + +connect(con2,localhost,root); +connect(con3,localhost,root); + +--echo # Connection 3 +connection con3; +LOCK TABLE tbl_with_partitions READ; + +--echo # Connection 1 +--echo # Access table with disabled autocommit +connection default; +SET AUTOCOMMIT = 0; +SELECT * FROM tbl_with_partitions; + +--echo # Connection 2 +--echo # Alter table, abort after prepare +connection con2; +set session debug="+d,abort_copy_table"; +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE tbl_with_partitions ADD COLUMN f INT; + +--echo # Connection 1 +--echo # Try accessing the table after Alter aborted. +--echo # This used to give ER_LOCK_DEADLOCK. +connection default; +SELECT * FROM tbl_with_partitions; + +--echo # Connection 3 +connection con3; +UNLOCK TABLES; + +--echo # Connection 1 +--echo # Cleanup +connection default; +disconnect con2; +disconnect con3; +DROP TABLE tbl_with_partitions; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7a3adc89ea9..9672647f30b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1530,8 +1530,8 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) *table_ptr=table->next; table->mdl_ticket= NULL; - if (table->needs_reopen() || - thd->version != refresh_version || !table->db_stat || + if (table->s->needs_reopen() || + thd->version != refresh_version || table->needs_reopen() || table_def_shutdown_in_progress) { free_cache_entry(table); @@ -8186,13 +8186,13 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use) thd_table= thd_table->next) { /* - Check for TABLE::db_stat is needed since in some places we call + Check for TABLE::needs_reopen() is needed since in some places we call handler::close() for table instance (and set TABLE::db_stat to 0) and do not remove such instances from the THD::open_tables for some time, during which other thread can see those instances (e.g. see partitioning code). */ - if (thd_table->db_stat) + if (!thd_table->needs_reopen()) signalled|= mysql_lock_abort_for_thread(thd, thd_table); } pthread_mutex_unlock(&LOCK_open); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 1966c7d8b39..94f5e84fb10 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -820,7 +820,7 @@ void mysql_ha_flush(THD *thd) if (hash_tables->table && (hash_tables->table->mdl_ticket && hash_tables->table->mdl_ticket->has_pending_conflicting_lock() || - hash_tables->table->needs_reopen())) + hash_tables->table->s->needs_reopen())) mysql_ha_close_table(thd, hash_tables); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8fd704c4f71..9a351085b3a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2657,7 +2657,7 @@ bool Delayed_insert::handle_inserts(void) thd_proc_info(&thd, "insert"); max_rows= delayed_insert_limit; - if (thd.killed || table->needs_reopen()) + if (thd.killed || table->s->needs_reopen()) { thd.killed= THD::KILL_CONNECTION; max_rows= ULONG_MAX; // Do as much as possible diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 52657deed83..bcf81a49f56 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4183,15 +4183,13 @@ bool mysql_unpack_partition(THD *thd, We need to free any memory objects allocated on item_free_list by the parser since we are keeping the old info from the first parser call in CREATE TABLE. - We'll ensure that this object isn't put into table cache also - just to ensure we don't get into strange situations with the - item objects. + + This table object can not be used any more. However, since + this is CREATE TABLE, we know that it will be destroyed by the + caller, and rely on that. */ thd->free_items(); part_info= thd->work_part_info; - tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, - table->s->db.str, - table->s->table_name.str); *work_part_info_used= true; } table->part_info= part_info; @@ -4484,17 +4482,11 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, /* We are going to manipulate the partition info on the table object - so we need to ensure that the table instances cached and all other - instances are properly closed. + so we need to ensure that the table instance is removed from the + table cache. */ if (table->part_info) - { - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, - table->s->db.str, - table->s->table_name.str); - pthread_mutex_unlock(&LOCK_open); - } + table->m_needs_reopen= TRUE; thd->work_part_info= thd->lex->part_info; if (thd->work_part_info && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e9474d9add6..556c0e65eee 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7049,6 +7049,10 @@ view_err: new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; new_table->next_number_field=new_table->found_next_number_field; thd_proc_info(thd, "copy to tmp table"); + DBUG_EXECUTE_IF("abort_copy_table", { + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); + goto err_new_table_cleanup; + }); error= copy_data_between_tables(table, new_table, alter_info->create_list, ignore, order_num, order, &copied, &deleted, diff --git a/sql/table.h b/sql/table.h index b44c2e9f3be..4b85d8a64f4 100644 --- a/sql/table.h +++ b/sql/table.h @@ -294,6 +294,8 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, struct TABLE_share; +extern ulong refresh_version; + /* This structure is shared between different table objects. There is one instance of table share per one table in the database. @@ -503,6 +505,14 @@ struct TABLE_SHARE return table_map_id; } + + /* + Must all TABLEs be reopened? + */ + inline bool needs_reopen() + { + return version != refresh_version; + } /** Convert unrelated members of TABLE_SHARE to one enum representing its type. @@ -605,8 +615,6 @@ struct TABLE_SHARE }; -extern ulong refresh_version; - /* Information for one open table */ enum index_hint_type { @@ -804,6 +812,7 @@ public: my_bool insert_or_update; /* Can be used by the handler */ my_bool alias_name_used; /* true if table_name is alias */ my_bool get_fields_in_item_tree; /* Signal to fix_field */ + my_bool m_needs_reopen; REGINFO reginfo; /* field connections */ MEM_ROOT mem_root; @@ -853,7 +862,7 @@ public: Is this instance of the table should be reopen? */ inline bool needs_reopen() - { return s->version != refresh_version; } + { return !db_stat || m_needs_reopen; } }; From e14d58f9783d0ab3f9564276f14901bf1016a5c1 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 10 Dec 2009 16:38:03 +0300 Subject: [PATCH 156/466] Backport a part of Monty's fix for Bug#39396, rev. 2736.2.11 "ha_maria.cc:2415: assertion in ha_maria::store_lock()". --- sql/lock.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sql/lock.cc b/sql/lock.cc index b64f7b0b44c..bd91494f174 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -168,9 +168,8 @@ int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) DBUG_RETURN(0); } - /** - Reset lock type in lock data and free. + Reset lock type in lock data @param mysql_lock Lock structures to reset. @@ -328,6 +327,13 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, } else if (rc == 1) /* aborted or killed */ { + /* + reset_lock_data is required here. If thr_multi_lock fails it + resets lock type for tables, which were locked before (and + including) one that caused error. Lock type for other tables + preserved. + */ + reset_lock_data(sql_lock); thd->some_tables_deleted=1; // Try again sql_lock->lock_count= 0; // Locks are already freed // Fall through: unlock, reset lock data, free and retry From ff0001ed579693bb102244560b35ce14769b5ef2 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 14:41:41 +0100 Subject: [PATCH 157/466] Backport of revno: 2617.80.1 Also re-enables the test for Bug #43867 Followup to Bug#46654 False deadlock on concurrent DML/DDL with partitions, inconsistent behavior Partition_sync.test uses features only available in debug builds. Disabling the test for non-debug builds. --- mysql-test/r/partition_sync.result | 27 +++++++++++- mysql-test/t/partition_sync.test | 66 +++++++++++++++--------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result index fbea2e0273a..d48362cb57c 100644 --- a/mysql-test/r/partition_sync.result +++ b/mysql-test/r/partition_sync.result @@ -1,5 +1,28 @@ -# Disabled until Bug#46654 False deadlock on concurrent DML/DDL -# with partitions, inconsistent behavior is backported +# +# Bug #43867 ALTER TABLE on a partitioned table +# causes unnecessary deadlocks +# +CREATE TABLE t1 (a int) PARTITION BY RANGE (a) +(PARTITION p0 VALUES LESS THAN (1), +PARTITION p1 VALUES LESS THAN (2)); +INSERT INTO t1 VALUES (0),(1); +# Connection 2 +BEGIN; +SELECT * FROM t1; +a +0 +1 +# Connection 1 +ALTER TABLE t1 DROP PARTITION p3; +ERROR HY000: Error in list of partitions to DROP +# Connection 2 +# This failed with deadlock and should not do so. +SELECT * FROM t1; +a +0 +1 +# Connection 1 +DROP TABLE t1; # # Bug #46654 False deadlock on concurrent DML/DDL # with partitions, inconsistent behavior diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test index 672e4cc0562..85eb33ebb6b 100644 --- a/mysql-test/t/partition_sync.test +++ b/mysql-test/t/partition_sync.test @@ -1,42 +1,40 @@ --source include/have_partition.inc +--source include/have_debug.inc # Save the initial number of concurrent sessions. --source include/count_sessions.inc ---echo # Disabled until Bug#46654 False deadlock on concurrent DML/DDL ---echo # with partitions, inconsistent behavior is backported +--echo # +--echo # Bug #43867 ALTER TABLE on a partitioned table +--echo # causes unnecessary deadlocks +--echo # -#--echo # -#--echo # Bug #43867 ALTER TABLE on a partitioned table -#--echo # causes unnecessary deadlocks -#--echo # -# -#CREATE TABLE t1 (a int) PARTITION BY RANGE (a) -#(PARTITION p0 VALUES LESS THAN (1), -# PARTITION p1 VALUES LESS THAN (2)); -# -#INSERT INTO t1 VALUES (0),(1); -# -#connect(con1,localhost,root); -# -#--echo # Connection 2 -#connection con1; -#BEGIN; -#SELECT * FROM t1; -# -#--echo # Connection 1 -#connection default; -#--error ER_DROP_PARTITION_NON_EXISTENT -#ALTER TABLE t1 DROP PARTITION p3; -# -#--echo # Connection 2 -#connection con1; -#--echo # This failed with deadlock and should not do so. -#SELECT * FROM t1; -# -#--echo # Connection 1 -#connection default; -#disconnect con1; -#DROP TABLE t1; +CREATE TABLE t1 (a int) PARTITION BY RANGE (a) +(PARTITION p0 VALUES LESS THAN (1), + PARTITION p1 VALUES LESS THAN (2)); + +INSERT INTO t1 VALUES (0),(1); + +connect(con1,localhost,root); + +--echo # Connection 2 +connection con1; +BEGIN; +SELECT * FROM t1; + +--echo # Connection 1 +connection default; +--error ER_DROP_PARTITION_NON_EXISTENT +ALTER TABLE t1 DROP PARTITION p3; + +--echo # Connection 2 +connection con1; +--echo # This failed with deadlock and should not do so. +SELECT * FROM t1; + +--echo # Connection 1 +connection default; +disconnect con1; +DROP TABLE t1; --echo # From 2264d619d878ee4b4dc918dcf558df7a6cc99efd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 10 Dec 2009 16:43:04 +0300 Subject: [PATCH 158/466] Backport of: 2630.16.14 Sergei Golubchik 2008-08-25 fixed a crash in partition tests introduced by HA_EXTRA_PREPARE_FOR_DROP patch --- sql/sql_base.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7a3adc89ea9..fad58900002 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1318,6 +1318,10 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, if (table->s->table_cache_key.length == key_length && !memcmp(table->s->table_cache_key.str, key, key_length)) { + /* Inform handler that table will be dropped after close */ + if (table->db_stat) + table->file->extra(HA_EXTRA_PREPARE_FOR_DROP); + /* Does nothing if the table is not locked. This allows one to use this function after a table From baf588a26f403d9a7a27b051d2be2ea473d9df85 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 10 Dec 2009 15:01:39 +0100 Subject: [PATCH 159/466] remove inadverent change --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b68bf2ae108..51a50f682ba 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,5 +239,3 @@ INSTALL(FILES COPYING EXCEPTIONS-CLIENT README DESTINATION .) IF(UNIX) INSTALL(FILES Docs/INSTALL-BINARY DESTINATION .) ENDIF() -GET_TARGET_PROPERTY(mysqld_location mysqld LOCATION) -INSTALL(FILES ${mysqld_location} DESTINATION win) From 8724320989a53dd7f5ec13f169a6a722b9ec995d Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 15:09:00 +0100 Subject: [PATCH 160/466] Backport of revno: 3685 Bug #48210 FLUSH TABLES WITH READ LOCK deadlocks against concurrent CREATE PROCEDURE This deadlock occured between a) CREATE PROCEDURE (or other commands listed below) b) FLUSH TABLES WITH READ LOCK If the execution of them happened in the following order: - a) opens a table (e.g. mysql.proc) - b) locks the global read lock (or GRL) - a) sleeps inside wait_if_global_read_lock() - b) increases refresh_version and sleeps waiting for old tables to go away Note that a) must start waiting on the GRL before FLUSH increases refresh_version. Otherwise a) won't wait on the GRL and instead close its tables for reopen, allowing FLUSH to complete and thus avoid the deadlock. With this patch the deadlock is avoided by making CREATE PROCEDURE acquire a protection against global read locks before it starts executing. This means that FLUSH TABLES WITH READ LOCK will have to wait until CREATE PROCEDURE completes before acquiring the global read lock, thereby avoiding the deadlock. This is implemented by introducing a new SQL command flag called CF_PROTECT_AGAINST_GRL. Commands marked with this flag will acquire a GRL protection in the beginning of mysql_execute_command(). This patch adds the flag to CREATE, ALTER and DROP for PROCEDURE and FUNCTION, as well as CREATE USER, DROP USER, RENAME USER and REVOKE ALL. All these commands either call open_grant_tables() or open_system_table_for_updated() which make them susceptible for this deadlock. The patch also adds the CF_PROTECT_AGAINST_GRL flag to a number of commands that previously acquired GRL protection in their respective SQLCOM case in mysql_execute_command(). Test case that checks for GRL protection for CREATE PROCEDURE and CREATE USER added to mdl_sync.test. --- mysql-test/r/mdl_sync.result | 37 ++++++++++++++ mysql-test/t/mdl_sync.test | 69 +++++++++++++++++++++++++ sql/lock.cc | 27 +++++++++- sql/sql_class.h | 10 ++++ sql/sql_parse.cc | 97 ++++++++++++------------------------ 5 files changed, 175 insertions(+), 65 deletions(-) diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index e5447c32b7d..130a8f22710 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -254,3 +254,40 @@ commit; # Switching to connection 'default'. # Clean-up drop table t1; +# +# Bug#48210 FLUSH TABLES WITH READ LOCK deadlocks +# against concurrent CREATE PROCEDURE +# +# Test 1: CREATE PROCEDURE +# Connection 1 +# Start CREATE PROCEDURE and open mysql.proc +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +CREATE PROCEDURE p1() SELECT 1; +# Connection 2 +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +# Check that FLUSH must wait to get the GRL +# and let CREATE PROCEDURE continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +FLUSH TABLES WITH READ LOCK; +# Connection 1 +# Connection 2 +UNLOCK TABLES; +# Connection 1 +DROP PROCEDURE p1; +SET DEBUG_SYNC= 'RESET'; +# Test 2: CREATE USER +# Start CREATE USER and open the grant tables +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +CREATE USER 'user_1@localhost'; +# Connection 2 +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +# Check that FLUSH must wait to get the GRL +# and let CREATE USER continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +FLUSH TABLES WITH READ LOCK; +# Connection 1 +# Connection 2 +UNLOCK TABLES; +# Connection 1 +DROP USER 'user_1@localhost'; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index fd66f6d539d..9422c67cb6f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -475,6 +475,75 @@ disconnect con46673; drop table t1; +--echo # +--echo # Bug#48210 FLUSH TABLES WITH READ LOCK deadlocks +--echo # against concurrent CREATE PROCEDURE +--echo # + +connect (con2, localhost, root); + +--echo # Test 1: CREATE PROCEDURE + +--echo # Connection 1 +connection default; +--echo # Start CREATE PROCEDURE and open mysql.proc +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +--send CREATE PROCEDURE p1() SELECT 1 + +--echo # Connection 2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +--echo # Check that FLUSH must wait to get the GRL +--echo # and let CREATE PROCEDURE continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +--send FLUSH TABLES WITH READ LOCK + +--echo # Connection 1 +connection default; +--reap + +--echo # Connection 2 +connection con2; +--reap +UNLOCK TABLES; + +--echo # Connection 1 +connection default; +DROP PROCEDURE p1; +SET DEBUG_SYNC= 'RESET'; + +--echo # Test 2: CREATE USER + +connection default; +--echo # Start CREATE USER and open the grant tables +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +--send CREATE USER 'user_1@localhost' + +--echo # Connection 2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +--echo # Check that FLUSH must wait to get the GRL +--echo # and let CREATE USER continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +--send FLUSH TABLES WITH READ LOCK + +--echo # Connection 1 +connection default; +--reap + +--echo # Connection 2 +connection con2; +--reap +UNLOCK TABLES; + +--echo # Connection 1 +connection default; +DROP USER 'user_1@localhost'; +SET DEBUG_SYNC= 'RESET'; + +disconnect con2; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/lock.cc b/sql/lock.cc index b64f7b0b44c..98217254240 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -74,6 +74,7 @@ */ #include "mysql_priv.h" +#include "debug_sync.h" #include #include @@ -1119,9 +1120,33 @@ bool lock_global_read_lock(THD *thd) if (!thd->global_read_lock) { const char *old_message; + const char *new_message= "Waiting to get readlock"; (void) pthread_mutex_lock(&LOCK_global_read_lock); + +#if defined(ENABLED_DEBUG_SYNC) + /* + The below sync point fires if we have to wait for + protect_against_global_read_lock. + + WARNING: Beware to use WAIT_FOR with this sync point. We hold + LOCK_global_read_lock here. + + Call the sync point before calling enter_cond() as it does use + enter_cond() and exit_cond() itself if a WAIT_FOR action is + executed in spite of the above warning. + + Pre-set proc_info so that it is available immediately after the + sync point sends a SIGNAL. This makes tests more reliable. + */ + if (protect_against_global_read_lock) + { + thd_proc_info(thd, new_message); + DEBUG_SYNC(thd, "wait_lock_global_read_lock"); + } +#endif /* defined(ENABLED_DEBUG_SYNC) */ + old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock, - "Waiting to get readlock"); + new_message); DBUG_PRINT("info", ("waiting_for: %d protect_against: %d", waiting_for_read_lock, protect_against_global_read_lock)); diff --git a/sql/sql_class.h b/sql/sql_class.h index 889d7c5472b..4bfd3e66438 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3341,6 +3341,16 @@ public: */ #define CF_DIAGNOSTIC_STMT (1U << 8) +/** + SQL statements that must be protected against impending global read lock + to prevent deadlock. This deadlock could otherwise happen if the statement + starts waiting for the GRL to go away inside mysql_lock_tables while at the + same time having "old" opened tables. The thread holding the GRL can be + waiting for these "old" opened tables to be closed, causing a deadlock + (FLUSH TABLES WITH READ LOCK). + */ +#define CF_PROTECT_AGAINST_GRL (1U << 10) + /* Bits in server_command_flags */ /** diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e462556c133..289b6ce0da8 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -182,14 +182,15 @@ void init_update_queries(void) memset(sql_command_flags, 0, sizeof(sql_command_flags)); sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_AUTO_COMMIT_TRANS; + CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | - CF_AUTO_COMMIT_TRANS; + CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | + CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; @@ -207,17 +208,17 @@ void init_update_queries(void) sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | - CF_REEXECUTION_FRAGILE; + CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | - CF_REEXECUTION_FRAGILE; + CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | - CF_REEXECUTION_FRAGILE; + CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | - CF_REEXECUTION_FRAGILE; + CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | - CF_REEXECUTION_FRAGILE; + CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | - CF_REEXECUTION_FRAGILE; + CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | @@ -276,20 +277,21 @@ void init_update_queries(void) CF_REEXECUTION_FRAGILE); - sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_REVOKE_ALL]= CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA; @@ -1969,6 +1971,17 @@ mysql_execute_command(THD *thd) thd->mdl_context.release_all_locks(); } + /* + Check if this command needs protection against the global read lock + to avoid deadlock. See CF_PROTECT_AGAINST_GRL. + start_waiting_global_read_lock() is called at the end of + mysql_execute_command(). + */ + if (((sql_command_flags[lex->sql_command] & CF_PROTECT_AGAINST_GRL) != 0) && + !thd->locked_tables_mode) + if (wait_if_global_read_lock(thd, FALSE, TRUE)) + goto error; + switch (lex->sql_command) { case SQLCOM_SHOW_EVENTS: @@ -2309,12 +2322,9 @@ case SQLCOM_PREPARE: start_waiting_global_read_lock(). We protect the normal CREATE TABLE in the same way. That way we avoid that a new table is created during a global read lock. + Protection against grl is covered by the CF_PROTECT_AGAINST_GRL flag. */ - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - { - res= 1; - goto end_with_restore_list; - } + #ifdef WITH_PARTITION_STORAGE_ENGINE { partition_info *part_info= thd->lex->part_info; @@ -2617,12 +2627,6 @@ end_with_restore_list: "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - { - res= 1; - break; - } - thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_alter_table(thd, select_lex->db, lex->name.str, &create_info, @@ -2852,8 +2856,6 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (update_precheck(thd, all_tables)) break; - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - goto error; DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); MYSQL_UPDATE_START(thd->query()); @@ -2884,15 +2886,6 @@ end_with_restore_list: else res= 0; - /* - Protection might have already been risen if its a fall through - from the SQLCOM_UPDATE case above. - */ - if (!thd->locked_tables_mode && - lex->sql_command == SQLCOM_UPDATE_MULTI && - wait_if_global_read_lock(thd, 0, 1)) - goto error; - res= mysql_multi_update_prepare(thd); #ifdef HAVE_REPLICATION @@ -2992,11 +2985,6 @@ end_with_restore_list: if ((res= insert_precheck(thd, all_tables))) break; - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - { - res= 1; - break; - } MYSQL_INSERT_START(thd->query()); res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, lex->update_list, lex->value_list, @@ -3031,11 +3019,6 @@ end_with_restore_list: unit->set_limit(select_lex); - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - { - res= 1; - break; - } if (!(res= open_and_lock_tables(thd, all_tables))) { MYSQL_INSERT_SELECT_START(thd->query()); @@ -3113,11 +3096,6 @@ end_with_restore_list: DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - { - res= 1; - break; - } MYSQL_DELETE_START(thd->query()); res = mysql_delete(thd, all_tables, select_lex->where, &select_lex->order_list, @@ -3133,12 +3111,6 @@ end_with_restore_list: (TABLE_LIST *)thd->lex->auxiliary_table_list.first; multi_delete *del_result; - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - { - res= 1; - break; - } - if ((res= multi_delete_precheck(thd, all_tables))) break; @@ -3277,9 +3249,6 @@ end_with_restore_list: if (check_one_table_access(thd, privilege, all_tables)) goto error; - if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1)) - goto error; - res= mysql_load(thd, lex->exchange, first_table, lex->field_list, lex->update_list, lex->value_list, lex->duplicates, lex->ignore, (bool) lex->local_file); From d7f9583a9b3846b25402fce4510c3bc55e6cddc5 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 15:09:56 +0100 Subject: [PATCH 161/466] Backport of revno: 3690 Postfix for Bug#48210 FLUSH TABLES WITH READ LOCK deadlocks against concurrent CREATE PROCEDURE Rewrote the second test to use DROP PROCEDURE instead of CREATE USER as CREATE USER does not work with embedded server. --- mysql-test/r/mdl_sync.result | 10 ++++------ mysql-test/t/mdl_sync.test | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 130a8f22710..ec02f29b008 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -273,21 +273,19 @@ FLUSH TABLES WITH READ LOCK; # Connection 2 UNLOCK TABLES; # Connection 1 -DROP PROCEDURE p1; SET DEBUG_SYNC= 'RESET'; -# Test 2: CREATE USER -# Start CREATE USER and open the grant tables +# Test 2: DROP PROCEDURE +# Start DROP PROCEDURE and open tables SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; -CREATE USER 'user_1@localhost'; +DROP PROCEDURE p1; # Connection 2 SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; # Check that FLUSH must wait to get the GRL -# and let CREATE USER continue +# and let DROP PROCEDURE continue SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; FLUSH TABLES WITH READ LOCK; # Connection 1 # Connection 2 UNLOCK TABLES; # Connection 1 -DROP USER 'user_1@localhost'; SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 9422c67cb6f..e3aceaa05fa 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -509,21 +509,20 @@ UNLOCK TABLES; --echo # Connection 1 connection default; -DROP PROCEDURE p1; SET DEBUG_SYNC= 'RESET'; ---echo # Test 2: CREATE USER +--echo # Test 2: DROP PROCEDURE connection default; ---echo # Start CREATE USER and open the grant tables +--echo # Start DROP PROCEDURE and open tables SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; ---send CREATE USER 'user_1@localhost' +--send DROP PROCEDURE p1 --echo # Connection 2 connection con2; SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; --echo # Check that FLUSH must wait to get the GRL ---echo # and let CREATE USER continue +--echo # and let DROP PROCEDURE continue SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; --send FLUSH TABLES WITH READ LOCK @@ -538,7 +537,6 @@ UNLOCK TABLES; --echo # Connection 1 connection default; -DROP USER 'user_1@localhost'; SET DEBUG_SYNC= 'RESET'; disconnect con2; From 323f20eaef320b4d0c27620eb178cdc68e7a8afc Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Thu, 10 Dec 2009 15:49:15 +0100 Subject: [PATCH 162/466] Bug#41425 Assertion in Protocol::end_statement() (pushbuild2) (diagnostics_area) Execution of CREATE TABLE ... SELECT statement was not atomic in the sense that concurrent statements trying to affect its target table might have sneaked in between the moment when the table was created and moment when it was filled according to SELECT clause. This resulted in inconsistent binary log, unexpected target table contents. In cases when concurrent statement was a DDL statement CREATE TABLE ... SELECT might have failed with ER_CANT_LOCK error. In more detail: Due to premature metadata lock downgrade which occured after CREATE TABLE SELECT statement created table but before it managed to obtain table-level lock on it other statements were allowed to open, lock and change target table in the middle of CREATE TABLE SELECT execution. This also meant that it was possible that CREATE TABLE SELECT would wait in mysql_lock_tables() when it was called for newly created table and that this wait could have been aborted by concurrent DDL. The latter led to execution of unexpected branch of code and CREATE TABLE SELECT ending with ER_CANT_LOCK error. The premature downgrade occured because open_table(), which was called for newly created table, decided that it is OK to downgrade metadata lock from exclusive to shared since table exists, even although it was not acquired within this call. This fix ensures that open_table() does not downgrade metadata lock if it is not acquired during its current invocation. Testing: The bug is exposed in a race condition, and is thus difficult to expose in a standard mysql-test-run test case. Instead, a stress test using the Random Query Generator (https://launchpad.net/randgen) will trip the problem occasionally. % perl runall.pl \ --basedir= \ --mysqld=--table-lock-wait-timeout=5 \ --mysqld=--skip-safemalloc \ --grammar=conf/maria_bulk_insert.yy \ --reporters=ErrorLog,Backtrace,WinPackage \ --mysqld=--log-output=file \ --queries=100000 \ --threads=10 \ --engine=myisam Note: You will need a debug build to expose the bug When the bug is tripped, the server will abort and dump core. Backport from 6.0-codebase (revid: 2617.53.4) --- sql/sql_base.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9672647f30b..eae69c415bd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2840,7 +2840,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table exists now we should downgrade our exclusive metadata lock on this table to shared metadata lock. */ - if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL) + if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL && + !(flags & MYSQL_OPEN_HAS_MDL_LOCK)) mdl_ticket->downgrade_exclusive_lock(); table->mdl_ticket= mdl_ticket; From 351f28d0cea345224f478307f014f5be62bb5e50 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Thu, 10 Dec 2009 16:22:41 +0100 Subject: [PATCH 163/466] Bug#46374 crash, INSERT INTO t1 uses function, function modifies t1 An error occuring in the execution of a stored procedure, called from do_select is masked, since the error condition is not propagated back to the caller (join->conds->val_int() returns a result value, and not an error code) An explicit check was added to see if the thd error code has been set, and if so, the loop status is set to the error state. Backport from 6.0-codebase (revid: 2617.68.31) --- mysql-test/r/sp-error.result | 11 +++++++++++ mysql-test/t/sp-error.test | 30 ++++++++++++++++++++++++++++++ sql/sql_select.cc | 7 +++++++ 3 files changed, 48 insertions(+) diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 00ae7dd4eca..034923bbd4f 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1687,6 +1687,17 @@ NULL SELECT non_existent (a) FROM t1 WHERE b = 999999; ERROR 42000: FUNCTION test.non_existent does not exist DROP TABLE t1; +CREATE TABLE t1 ( f2 INTEGER, f3 INTEGER ); +INSERT INTO t1 VALUES ( 1, 1 ); +CREATE FUNCTION func_1 () RETURNS INTEGER +BEGIN +INSERT INTO t1 SELECT * FROM t1 ; +RETURN 1 ; +END| +INSERT INTO t1 SELECT * FROM (SELECT 2 AS f1, 2 AS f2) AS A WHERE func_1() = 5; +ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. +DROP FUNCTION func_1; +DROP TABLE t1; # # Bug #47788: Crash in TABLE_LIST::hide_view_error on UPDATE + VIEW + # SP + MERGE + ALTER diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index e33adf56284..4df1118cd56 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -2490,6 +2490,35 @@ SELECT AVG (a) FROM t1 WHERE b = 999999; SELECT non_existent (a) FROM t1 WHERE b = 999999; DROP TABLE t1; + +# +# Bug #46374 crash, INSERT INTO t1 uses function, function modifies t1 +# +CREATE TABLE t1 ( f2 INTEGER, f3 INTEGER ); +INSERT INTO t1 VALUES ( 1, 1 ); + +delimiter |; + +CREATE FUNCTION func_1 () RETURNS INTEGER +BEGIN + INSERT INTO t1 SELECT * FROM t1 ; + RETURN 1 ; +END| + +delimiter ;| + +# The bug caused the following INSERT statement to trigger +# an assertion. Error 1442 is the correct response +# +--error 1442 +INSERT INTO t1 SELECT * FROM (SELECT 2 AS f1, 2 AS f2) AS A WHERE func_1() = 5; + +# Cleanup +DROP FUNCTION func_1; +DROP TABLE t1; + + + --echo # --echo # Bug #47788: Crash in TABLE_LIST::hide_view_error on UPDATE + VIEW + --echo # SP + MERGE + ALTER @@ -2513,3 +2542,4 @@ DROP VIEW v1; DROP TABLE t1; --echo End of 5.1 tests + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5320da45322..2195719a3e9 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -11096,6 +11096,13 @@ do_select(JOIN *join,List *fields,TABLE *table,Procedure *procedure) fields); rc= join->result->send_data(*columns_list); } + /* + An error can happen when evaluating the conds + (the join condition and piece of where clause + relevant to this join table). + */ + if (join->thd->is_error()) + error= NESTED_LOOP_ERROR; } else { From efee99957c0745068e203db75f16cf73e9cbad93 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 11 Dec 2009 14:07:38 +0300 Subject: [PATCH 164/466] Partial backport of: ---------------------------------------------------- 2736.2.10 Michael Widenius 2008-10-22 Fix for bug#39395 Maria: ma_extra.c:286: maria_extra: Assertion `share->reopen == 1' failed --- sql/sql_base.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9f88393706a..a19f0bb96a7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2118,13 +2118,15 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function) { enum thr_lock_type old_lock_type; - DBUG_ENTER("wait_while_table_is_used"); DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", table->s->table_name.str, (ulong) table->s, table->db_stat, table->s->version)); - (void) table->file->extra(function); + /* Ensure no one can reopen table before it's removed */ + pthread_mutex_lock(&LOCK_open); + table->s->version= 0; + pthread_mutex_unlock(&LOCK_open); old_lock_type= table->reginfo.lock_type; mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ @@ -2139,6 +2141,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, table->s->db.str, table->s->table_name.str); pthread_mutex_unlock(&LOCK_open); + /* extra() call must come only after all instances above are closed */ + (void) table->file->extra(function); DBUG_RETURN(FALSE); } @@ -2165,6 +2169,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, void drop_open_table(THD *thd, TABLE *table, const char *db_name, const char *table_name) { + DBUG_ENTER("drop_open_table"); if (table->s->tmp_table) close_temporary_table(thd, table, 1, 1); else @@ -2176,10 +2181,12 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name, table->s->version= 0; pthread_mutex_lock(&LOCK_open); + table->file->extra(HA_EXTRA_PREPARE_FOR_DROP); close_thread_table(thd, &thd->open_tables); quick_rm_table(table_type, db_name, table_name, 0); pthread_mutex_unlock(&LOCK_open); } + DBUG_VOID_RETURN; } @@ -3141,7 +3148,7 @@ Locked_tables_list::unlock_locked_tables(THD *thd) the locked tables list. Passing a TABLE_LIST instance that is not part of locked tables list will lead to a crash. - @parma remove_from_locked_tables + @param remove_from_locked_tables TRUE if the table is removed from the list permanently. @@ -3443,7 +3450,6 @@ check_and_update_table_version(THD *thd, } DBUG_EXECUTE_IF("reprepare_each_statement", return inject_reprepare(thd);); - return FALSE; } @@ -4119,6 +4125,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, MEM_ROOT new_frm_mem; bool has_prelocking_list= thd->lex->requires_prelocking(); DBUG_ENTER("open_tables"); + /* Close HANDLER tables which are marked for flush or against which there are pending exclusive metadata locks. Note that we do this not to avoid From 30962f55c27f1399b2fa18e14e8081eb6c6f0155 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 11 Dec 2009 14:12:47 +0300 Subject: [PATCH 165/466] Partial backport of: ----------------------------------------------------------- 2497.392.1 Michael Widenius 2008-08-19 Fixes for Bug #38016 Maria: trying to access freed memory when committing a transaction. Don't write out states if they haven't changed. --- sql/sql_table.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 556c0e65eee..759afe79be9 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7645,7 +7645,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (to->file->ha_end_bulk_insert() && error <= 0) { to->file->print_error(my_errno,MYF(0)); - error=1; + error= 1; } to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); @@ -7673,6 +7673,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, to->file->ha_release_auto_increment(); if (to->file->ha_external_lock(thd,F_UNLCK)) error=1; + if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME)) + error= 1; DBUG_RETURN(error > 0 ? -1 : 0); } From a3814e36356fb339f0d22d19a886a7bb68a9161f Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 11 Dec 2009 14:18:59 +0300 Subject: [PATCH 166/466] Backport of: ----------------------------------------------------------- 2630.28.28 Magne Mahre 2008-12-05 Bug #38661 'all threads hang in "opening tables" or "waiting for table" and cpu is at 100%' Concurrent execution of FLUSH TABLES statement and at least two statements using the same table might have led to live-lock which caused all three connections to stall and hog 100% of CPU. tdc_wait_for_old_versions() wrongly assumed that there cannot be a share with an old version and no used TABLE instances and thus was failing to perform wait in situation when such old share was cached in MDL subsystem thanks to a still active metadata lock on the table. So it might have happened that two or more connections simultaneously executing statements which involve table being flushed managed to prevent each other from waiting in this function by keeping shared metadata lock on the table constantly active (i.e. one of the statements managed to take/hold this lock while other statements were calling tdc_wait_for_old_versions()). Thus they were forcing each other to loop infinitely in open_tables() - close_thread_tables_for_reopen() - tdc_wait_for_old_versions() cycle causing CPU hogging. This patch fixes this problem by removing this false assumption from tdc_wait_for_old_versions(). Note that the problem is specific only for server versions >= 6.0. No test case is submitted for this test, as the test infrastructure hasn't got the necessary primitives to test the behaviour. The manifestation is that throughput will decrease to a low level (possibly 0) after some time, and stay at that level. Several transactions will not complete. Manual testing can be done by running the code submitted by Shane Bester attached to the bug report. If the bug persists, the transaction thruput will almost immediately drop to near zero (shown as the transaction count output from the test program staying on a close to constant value, instead of increasing rapidly). --- sql/sql_base.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a19f0bb96a7..cce376d86d1 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8324,8 +8324,7 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) { if ((share= get_cached_table_share(mdl_request->key.db_name(), mdl_request->key.name())) && - share->version != refresh_version && - !share->used_tables.is_empty()) + share->version != refresh_version) break; } if (!mdl_request) From 700a361a6a6044f8ef49b6aca7db5810c2641e54 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 11 Dec 2009 15:24:23 +0300 Subject: [PATCH 167/466] Backport of: ------------------------------------------------------------ 2599.161.3 Ingo Struewing 2009-07-21 Bug#20667 - Truncate table fails for a write locked table TRUNCATE TABLE was not allowed under LOCK TABLES. The patch removes this restriction. mysql_truncate() does now handle that case. --- mysql-test/r/merge.result | 29 +++-- mysql-test/r/truncate.result | 87 ++++++++++++++- mysql-test/r/truncate_coverage.result | 70 ++++++++++++ mysql-test/t/merge.test | 16 ++- mysql-test/t/truncate.test | 73 +++++++++++- mysql-test/t/truncate_coverage.test | 155 ++++++++++++++++++++++++++ sql/sql_delete.cc | 73 +++++++++--- sql/sql_parse.cc | 2 +- 8 files changed, 470 insertions(+), 35 deletions(-) create mode 100644 mysql-test/r/truncate_coverage.result create mode 100644 mysql-test/t/truncate_coverage.test diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 0417b91490e..a215c818b0f 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -1046,18 +1046,21 @@ c1 LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; INSERT INTO t1 VALUES (1); TRUNCATE TABLE t3; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 -2 +UNLOCK TABLES; +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 # # Truncate child table under locked tables. +LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 2 UNLOCK TABLES; DROP TABLE t1, t2, t3; @@ -1089,18 +1092,24 @@ INSERT INTO t1 VALUES (1); CREATE TABLE t4 (c1 INT, INDEX(c1)); LOCK TABLE t4 WRITE; TRUNCATE TABLE t3; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 -2 +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 # # Truncate temporary child table under locked tables. +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 +2 +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 2 UNLOCK TABLES; DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/r/truncate.result b/mysql-test/r/truncate.result index 8ce2ad9be21..8f237c81e75 100644 --- a/mysql-test/r/truncate.result +++ b/mysql-test/r/truncate.result @@ -1,4 +1,4 @@ -drop table if exists t1; +drop table if exists t1, t2; create table t1 (a integer, b integer,c1 CHAR(10)); insert into t1 (a) values (1),(2); truncate table t1; @@ -61,6 +61,91 @@ ERROR 42S02: Table 'test.v1' doesn't exist drop view v1; drop table t1; # +# Bug#20667 - Truncate table fails for a write locked table +# +CREATE TABLE t1 (c1 INT); +LOCK TABLE t1 WRITE; +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +c1 +1 +TRUNCATE TABLE t1; +SELECT * FROM t1; +c1 +UNLOCK TABLES; +LOCK TABLE t1 READ; +TRUNCATE TABLE t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +UNLOCK TABLES; +CREATE TABLE t2 (c1 INT); +LOCK TABLE t2 WRITE; +TRUNCATE TABLE t1; +ERROR HY000: Table 't1' was not locked with LOCK TABLES +UNLOCK TABLES; +CREATE VIEW v1 AS SELECT t1.c1 FROM t1,t2 WHERE t1.c1 = t2.c1; +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (3), (4); +SELECT * FROM v1; +c1 +1 +3 +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +c1 +1 +3 +LOCK TABLE t1 WRITE; +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +UNLOCK TABLES; +LOCK TABLE t1 WRITE, t2 WRITE; +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +UNLOCK TABLES; +LOCK TABLE v1 WRITE; +SELECT * FROM v1; +c1 +1 +3 +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +c1 +1 +3 +UNLOCK TABLES; +LOCK TABLE t1 WRITE, t2 WRITE, v1 WRITE; +SELECT * FROM v1; +c1 +1 +3 +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +c1 +1 +3 +UNLOCK TABLES; +DROP VIEW v1; +DROP TABLE t1, t2; +CREATE PROCEDURE p1() SET @a = 5; +TRUNCATE p1; +ERROR 42S02: Table 'test.p1' doesn't exist +SHOW CREATE PROCEDURE p1; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +SET @a = 5 latin1 latin1_swedish_ci latin1_swedish_ci +DROP PROCEDURE p1; +# # Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE # DROP TABLE IF EXISTS t1; diff --git a/mysql-test/r/truncate_coverage.result b/mysql-test/r/truncate_coverage.result new file mode 100644 index 00000000000..bb036329f6f --- /dev/null +++ b/mysql-test/r/truncate_coverage.result @@ -0,0 +1,70 @@ +SET DEBUG_SYNC='RESET'; +DROP TABLE IF EXISTS t1; +# +# Bug#20667 - Truncate table fails for a write locked table +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# connection con1 +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# connection default +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +TRUNCATE TABLE t1; +# +# connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +KILL QUERY @id; +COMMIT; +# +# connection default +ERROR 70100: Query execution was interrupted +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# connection con1 +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# connection default +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +TRUNCATE TABLE t1; +# +# connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +COMMIT; +# +# connection default +ERROR 42S02: Table 'test.t1' doesn't exist +UNLOCK TABLES; +DROP TABLE t1; +ERROR 42S02: Unknown table 't1' +SET DEBUG_SYNC='RESET'; +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# connection con1 +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# connection default +SET DEBUG_SYNC='mdl_acquire_exclusive_locks_wait SIGNAL waiting'; +TRUNCATE TABLE t1; +# +# connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +KILL QUERY @id; +COMMIT; +# +# connection default +ERROR 70100: Query execution was interrupted +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index b9e6813a4df..c3499037e97 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -675,12 +675,16 @@ SELECT * FROM t3; --echo # Truncate MERGE table under locked tables. LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; INSERT INTO t1 VALUES (1); ---error ER_LOCK_OR_ACTIVE_TRANSACTION TRUNCATE TABLE t3; SELECT * FROM t3; +UNLOCK TABLES; +SELECT * FROM t1; +SELECT * FROM t2; --echo # --echo # Truncate child table under locked tables. ---error ER_LOCK_OR_ACTIVE_TRANSACTION +LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; SELECT * FROM t3; UNLOCK TABLES; @@ -706,14 +710,18 @@ SELECT * FROM t3; INSERT INTO t1 VALUES (1); CREATE TABLE t4 (c1 INT, INDEX(c1)); LOCK TABLE t4 WRITE; ---error ER_LOCK_OR_ACTIVE_TRANSACTION TRUNCATE TABLE t3; SELECT * FROM t3; +SELECT * FROM t1; +SELECT * FROM t2; --echo # --echo # Truncate temporary child table under locked tables. ---error ER_LOCK_OR_ACTIVE_TRANSACTION +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; SELECT * FROM t3; +SELECT * FROM t1; +SELECT * FROM t2; UNLOCK TABLES; DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/t/truncate.test b/mysql-test/t/truncate.test index feec4051e35..cdfa448f78a 100644 --- a/mysql-test/t/truncate.test +++ b/mysql-test/t/truncate.test @@ -2,7 +2,7 @@ # Test of truncate # --disable_warnings -drop table if exists t1; +drop table if exists t1, t2; --enable_warnings create table t1 (a integer, b integer,c1 CHAR(10)); @@ -69,6 +69,77 @@ drop table t1; # End of 5.0 tests +--echo # +--echo # Bug#20667 - Truncate table fails for a write locked table +--echo # +CREATE TABLE t1 (c1 INT); +LOCK TABLE t1 WRITE; +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +TRUNCATE TABLE t1; +SELECT * FROM t1; +UNLOCK TABLES; +# +LOCK TABLE t1 READ; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +TRUNCATE TABLE t1; +UNLOCK TABLES; +# +CREATE TABLE t2 (c1 INT); +LOCK TABLE t2 WRITE; +--error ER_TABLE_NOT_LOCKED +TRUNCATE TABLE t1; +UNLOCK TABLES; +# +CREATE VIEW v1 AS SELECT t1.c1 FROM t1,t2 WHERE t1.c1 = t2.c1; +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (3), (4); +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +SELECT * FROM v1; +# +LOCK TABLE t1 WRITE; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +UNLOCK TABLES; +# +LOCK TABLE t1 WRITE, t2 WRITE; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +UNLOCK TABLES; +# +LOCK TABLE v1 WRITE; +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +SELECT * FROM v1; +UNLOCK TABLES; +# +LOCK TABLE t1 WRITE, t2 WRITE, v1 WRITE; +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +SELECT * FROM v1; +UNLOCK TABLES; +# +DROP VIEW v1; +DROP TABLE t1, t2; +# +CREATE PROCEDURE p1() SET @a = 5; +--error ER_NO_SUCH_TABLE +TRUNCATE p1; +SHOW CREATE PROCEDURE p1; +DROP PROCEDURE p1; + --echo # --echo # Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE --echo # diff --git a/mysql-test/t/truncate_coverage.test b/mysql-test/t/truncate_coverage.test new file mode 100644 index 00000000000..9870fbb5ebf --- /dev/null +++ b/mysql-test/t/truncate_coverage.test @@ -0,0 +1,155 @@ +# +# Code coverage testing of TRUNCATE TABLE. +# +# Ingo Struewing, 2009-07-20 +# + +--source include/have_debug_sync.inc +SET DEBUG_SYNC='RESET'; + +--let $MYSQLD_DATADIR= `SELECT @@datadir` + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # +--echo # Bug#20667 - Truncate table fails for a write locked table +--echo # +######## +# Attack wait_while_table_is_used(). Kill query while trying to +# upgrade MDL. +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# Start a transaction and execute a DML in it. Since 5.4.4 this leaves +# a shared meta data lock (MDL) behind. TRUNCATE shall block on it. +# +--echo # +--echo # connection con1 +--connect (con1, localhost, root,,) +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# Get connection id of default connection. +# Lock the table and start TRUNCATE, which will block on MDL upgrade. +# +--echo # +--echo # connection default +--connection default +let $ID= `SELECT @id := CONNECTION_ID()`; +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +send TRUNCATE TABLE t1; +# +# Get the default connection ID into a variable in an invisible statement. +# Kill the TRUNCATE query. This shall result in an error return +# from wait_while_table_is_used(). +# +--echo # +--echo # connection con1 +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +let $invisible_assignment_in_select = `SELECT @id := $ID`; +KILL QUERY @id; +COMMIT; +--disconnect con1 +--echo # +--echo # connection default +--connection default +--error ER_QUERY_INTERRUPTED +reap; +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; +######## +# Attack reopen_tables(). Remove form file. +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# Start a transaction and execute a DML in it. Since 5.4.4 this leaves +# a shared meta data lock (MDL) behind. TRUNCATE shall block on it. +# +--echo # +--echo # connection con1 +--connect (con1, localhost, root,,) +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# Lock the table and start TRUNCATE, which will block on MDL upgrade. +# +--echo # +--echo # connection default +--connection default +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +send TRUNCATE TABLE t1; +# +# Remove datafile. +# Commit to let TRUNCATE continue. +# +--echo # +--echo # connection con1 +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +--remove_file $MYSQLD_DATADIR/test/t1.frm +COMMIT; +--disconnect con1 +--echo # +--echo # connection default +--connection default +--error ER_NO_SUCH_TABLE +reap; +UNLOCK TABLES; +--error ER_BAD_TABLE_ERROR +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; +######## +# Attack acquire_exclusive_locks(). Hold a global read lock. +# Non-LOCK TABLE case. +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# Start a transaction and execute a DML in it. Since 5.4.4 this leaves +# a shared meta data lock (MDL) behind. TRUNCATE shall block on it. +# +--echo # +--echo # connection con1 +--connect (con1, localhost, root,,) +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# Get connection id of default connection. +# Start TRUNCATE, which will block on acquire_exclusive_locks(). +# +--echo # +--echo # connection default +--connection default +let $ID= `SELECT @id := CONNECTION_ID()`; +SET DEBUG_SYNC='mdl_acquire_exclusive_locks_wait SIGNAL waiting'; +send TRUNCATE TABLE t1; +# +# Get the default connection ID into a variable in an invisible statement. +# Kill the TRUNCATE query. This shall result in an error return +# from wait_while_table_is_used(). +# +--echo # +--echo # connection con1 +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +let $invisible_assignment_in_select = `SELECT @id := $ID`; +KILL QUERY @id; +COMMIT; +--disconnect con1 +--echo # +--echo # connection default +--connection default +--error ER_QUERY_INTERRUPTED +reap; +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; + diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1f3621de502..26478b31290 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1098,9 +1098,15 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) HA_CREATE_INFO create_info; char path[FN_REFLEN + 1]; TABLE *table; - bool error; + bool error= TRUE; uint path_length; MDL_request mdl_request; + /* + Is set if we're under LOCK TABLES, and used + to downgrade the exclusive lock after the + table was truncated. + */ + MDL_ticket *mdl_ticket= NULL; bool has_mdl_lock= FALSE; DBUG_ENTER("mysql_truncate"); @@ -1119,7 +1125,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) goto trunc_by_del; table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); - + close_temporary_table(thd, table, 0, 0); // Don't free share ha_create_table(thd, share->normalized_path.str, share->db.str, share->table_name.str, &create_info, 1); @@ -1177,21 +1183,47 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); - if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) - DBUG_RETURN(TRUE); - has_mdl_lock= TRUE; + if (thd->locked_tables_mode) + { + if (!(table= find_write_locked_table(thd->open_tables, table_list->db, + table_list->table_name))) + DBUG_RETURN(TRUE); + mdl_ticket= table->mdl_ticket; + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto end; + close_all_tables_for_name(thd, table->s, FALSE); + } + else + { + /* + Even though we could use the previous execution branch + here just as well, we must not try to open the table: + MySQL manual documents that TRUNCATE can be used to + repair a damaged table, i.e. a table that can not be + fully "opened". In particular MySQL manual says: - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, - table_list->table_name); - pthread_mutex_unlock(&LOCK_open); + As long as the table format file tbl_name.frm is valid, + the table can be re-created as an empty table with TRUNCATE + TABLE, even if the data or index files have become corrupted. + */ + mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, + MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) + DBUG_RETURN(TRUE); + has_mdl_lock= TRUE; + pthread_mutex_lock(&LOCK_open); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, + table_list->table_name); + pthread_mutex_unlock(&LOCK_open); + } } - // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this - // crashes, replacement works. *(path + path_length - reg_ext_length)= - // '\0'; + /* + Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this + crashes, replacement works. *(path + path_length - reg_ext_length)= + '\0'; + */ path[path_length - reg_ext_length] = 0; pthread_mutex_lock(&LOCK_open); error= ha_create_table(thd, path, table_list->db, table_list->table_name, @@ -1202,6 +1234,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) end: if (!dont_send_ok) { + if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + /* + Even if we failed to reopen some tables, + the operation itself succeeded, write the binlog. + */ if (!error) { /* @@ -1213,12 +1251,11 @@ end: } if (has_mdl_lock) thd->mdl_context.release_lock(mdl_request.ticket); + if (mdl_ticket) + mdl_ticket->downgrade_exclusive_lock(); } - else if (error) - { - if (has_mdl_lock) - thd->mdl_context.release_lock(mdl_request.ticket); - } + + DBUG_PRINT("exit", ("error: %d", error)); DBUG_RETURN(error); trunc_by_del: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 289b6ce0da8..0d21eb82b87 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3078,7 +3078,7 @@ end_with_restore_list: Don't allow this within a transaction because we want to use re-generate table */ - if (thd->locked_tables_mode || thd->active_transaction()) + if (thd->active_transaction()) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); From 6454d6e2c69611e17f54a8c4f39a65101074ab4d Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 11 Dec 2009 15:51:39 +0100 Subject: [PATCH 168/466] Introduce a workaround for Solaris' inability to deal with dtrace in static libraries. Rerun dtrace -G on all objects that can contan dtrace probes (also objects that are part of static libs) before linking mysqld to produce dtrace_probes_all.o and link dtrace_probes_all.o with mysqld This ugly workaround was inspired by handling dtrace using autotools. --- cmake/dtrace.cmake | 26 +++++++++++---- cmake/dtrace_prelink.cmake | 15 +++++---- sql/CMakeLists.txt | 66 +++++++++++++++++++++++++++----------- sql/Makefile.am | 2 +- sql/main.cc | 25 +++++++++++++++ sql/mysqld.cc | 4 +-- 6 files changed, 103 insertions(+), 35 deletions(-) create mode 100644 sql/main.cc diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index 58fbbba022c..960a7aa4c84 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -83,16 +83,30 @@ MACRO (DTRACE_INSTRUMENT target) -DOUTFILE=${outfile} -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d -DDTRACE_FLAGS=${DTRACE_FLAGS} + -DDIRS=. -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake WORKING_DIRECTORY ${objdir} ) - GET_TARGET_PROPERTY(target_link_flags ${target} LINK_FLAGS) - IF(NOT target_link_flags) - SET(target_link_flags) + # Add full object path to linker flags + GET_TARGET_PROPERTY(target_type ${target} TYPE) + IF(NOT target_type MATCHES "STATIC") + SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS "${outfile}") + ELSE() + # For static library flags, add the object to the library. + # Note: DTrace probes in static libraries are unusable currently + # (see http://opensolaris.org/jive/thread.jspa?messageID=432454) + # but maybe one day this will be fixed. + GET_TARGET_PROPERTY(target_location ${target} LOCATION) + ADD_CUSTOM_COMMAND( + TARGET ${target} POST_BUILD + COMMAND ${CMAKE_AR} r ${target_location} ${outfile} + COMMAND ${CMAKE_RANLIB} ${target_location} + ) + + # Remember the object directory (it is used to workaround lack of static + # library support when linking mysqld) + SET(TARGET_OBJECT_DIRECTORY_${target} ${objdir} CACHE INTERNAL "") ENDIF() - - SET_TARGET_PROPERTIES(${target} PROPERTIES LINK_FLAGS - "${target_link_flags} ${outfile}") ENDIF() ENDIF() ENDMACRO() diff --git a/cmake/dtrace_prelink.cmake b/cmake/dtrace_prelink.cmake index 53e12407cb9..ae475a90921 100644 --- a/cmake/dtrace_prelink.cmake +++ b/cmake/dtrace_prelink.cmake @@ -19,13 +19,14 @@ # Do not follow symlinks in GLOB_RECURSE CMAKE_POLICY(SET CMP0009 NEW) -FILE(GLOB_RECURSE OBJECTS *.o) - -# Use relative paths to generate shorter command line -GET_FILENAME_COMPONENT(CURRENT_ABS_DIR . ABSOLUTE) -FOREACH(OBJ ${OBJECTS}) - FILE(RELATIVE_PATH REL ${CURRENT_ABS_DIR} ${OBJ}) - LIST(APPEND REL_OBJECTS ${REL}) +FOREACH(dir ${DIRS}) + FILE(GLOB_RECURSE OBJECTS ${dir}/*.o) + # Use relative paths to generate shorter command line + GET_FILENAME_COMPONENT(CURRENT_ABS_DIR . ABSOLUTE) + FOREACH(OBJ ${OBJECTS}) + FILE(RELATIVE_PATH REL ${CURRENT_ABS_DIR} ${OBJ}) + LIST(APPEND REL_OBJECTS ${REL}) + ENDFOREACH() ENDFOREACH() EXECUTE_PROCESS( diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index d086149831e..92cf55aa4ff 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -43,7 +43,6 @@ IF(SSL_DEFINES) ENDIF() - SET (SQL_SOURCE ../sql-common/client.c derror.cc des_key_file.cc discover.cc ../libmysql/errmsg.c field.cc field_conv.cc @@ -85,22 +84,55 @@ SET (SQL_SOURCE MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY) +ADD_LIBRARY(sql STATIC ${SQL_SOURCE}) +DTRACE_INSTRUMENT(sql) +TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS} + mysys dbug strings vio regex + ${LIBWRAP} ${LIBCRYPT} ${LIBDL} + ${SSL_LIBRARIES}) + + + +# Ugly workaround for Solaris' DTrace inability to use probes +# from static libraries currently, discussed e.g in this thread +# (http://opensolaris.org/jive/thread.jspa?messageID=432454) +# We have to collect all object files that may be instrumented +# and go into the mysqld (also those that come from in static libs) +# run them again through dtrace -G to generate an ELF file that links +# to mysqld. +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND ENABLE_DTRACE) + SET(DTRACE_PROBES_ALL ${CMAKE_CURRENT_BINARY_DIR}/dtrace_probes_all.o) + SET(DTRACED_LIBS sql ${MYSQLD_STATIC_PLUGIN_LIBS} mysys) + FOREACH(lib ${DTRACED_LIBS}) + SET(DTRACE_DIRS ${DTRACE_DIRS} ${TARGET_OBJECT_DIRECTORY_${lib}}) + ENDFOREACH() + + MESSAGE("DTRACE_DIRS=${DTRACE_DIRS}") + ADD_CUSTOM_COMMAND( + OUTPUT ${DTRACE_PROBES_ALL} + DEPENDS ${DTRACED_LIBS} + COMMAND ${CMAKE_COMMAND} + -DDTRACE=${DTRACE} + -DOUTFILE=${DTRACE_PROBES_ALL} + -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d + -DDTRACE_FLAGS=${DTRACE_FLAGS} + "-DDIRS=${DTRACE_DIRS}" + -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake + VERBATIM + ) +ELSE() + SET(DTRACE_PROBES_ALL) +ENDIF() + IF(WIN32) - SET(SQL_SOURCE ${SQL_SOURCE} nt_servc.cc nt_servc.h) -ENDIF() -IF(MSVC) - ADD_LIBRARY(sql ${SQL_SOURCE}) - ADD_EXECUTABLE(mysqld mysqld_dummy.cc) - ADD_CUSTOM_COMMAND( - OUTPUT mysqld_dummy.cc - COMMAND cmake ARGS -E touch mysqld_dummy.cc - VERBATIM) - SET(SQL_LIB sql) + SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h) ELSE() - ADD_EXECUTABLE(mysqld ${SQL_SOURCE}) - DTRACE_INSTRUMENT(mysqld) -ENDIF() + SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL}) +ENDIF() + +ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) + IF(NOT WITHOUT_DYNAMIC_PLUGINS) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) @@ -128,11 +160,7 @@ ENDIF() SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) -TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_STATIC_PLUGIN_LIBS} - mysys dbug strings vio regex ${SQL_LIB} - ${LIBWRAP} ${LIBCRYPT} ${LIBDL} - ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) - +TARGET_LINK_LIBRARIES(mysqld sql) # Provide plugins with minimal set of libraries SET(INTERFACE_LIBS ${LIBRT}) IF(INTERFACE_LIBS) diff --git a/sql/Makefile.am b/sql/Makefile.am index 15ee0d588c4..5f5b8de97be 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -123,7 +123,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ net_serv.cc protocol.cc sql_state.c \ lock.cc my_lock.c \ sql_string.cc sql_manager.cc sql_map.cc \ - mysqld.cc password.c hash_filo.cc hostname.cc \ + main.cc mysqld.cc password.c hash_filo.cc hostname.cc \ sql_connect.cc scheduler.cc sql_parse.cc \ set_var.cc sql_yacc.yy \ sql_base.cc table.cc sql_select.cc sql_insert.cc \ diff --git a/sql/main.cc b/sql/main.cc new file mode 100644 index 00000000000..249a2a883fe --- /dev/null +++ b/sql/main.cc @@ -0,0 +1,25 @@ +/* Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + main() for mysqld. + Calls mysqld_main() entry point exported by sql library. +*/ +extern int mysqld_main(int argc, char **argv); + +int main(int argc, char **argv) +{ + return mysqld_main(argc, argv); +} diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 30ea646d2fd..e871d46d6a1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4309,7 +4309,7 @@ static void test_lc_time_sz() #ifdef __WIN__ int win_main(int argc, char **argv) #else -int main(int argc, char **argv) +int mysqld_main(int argc, char **argv) #endif { MY_INIT(argv[0]); // init my_sys library & pthreads @@ -4699,7 +4699,7 @@ default_service_handling(char **argv, } -int main(int argc, char **argv) +int mysqld_main(int argc, char **argv) { /* When several instances are running on the same machine, we From 670fb75598596fb02217ccdbf4100f3882adf7f5 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 11 Dec 2009 18:33:00 +0000 Subject: [PATCH 169/466] Introduce macro DTRACE_INSTRUMENT_STATIC_LIBS,to hide the ugly Solaris dtrace workarounds --- cmake/dtrace.cmake | 35 +++++++++++++++++++++++++++++++++-- sql/CMakeLists.txt | 38 +++++--------------------------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index 960a7aa4c84..653114d4963 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -103,10 +103,41 @@ MACRO (DTRACE_INSTRUMENT target) COMMAND ${CMAKE_RANLIB} ${target_location} ) - # Remember the object directory (it is used to workaround lack of static - # library support when linking mysqld) + # Used in DTRACE_INSTRUMENT_WITH_STATIC_LIBS SET(TARGET_OBJECT_DIRECTORY_${target} ${objdir} CACHE INTERNAL "") ENDIF() ENDIF() ENDIF() ENDMACRO() + + +# Ugly workaround for Solaris' DTrace inability to use probes +# from static libraries, discussed e.g in this thread +# (http://opensolaris.org/jive/thread.jspa?messageID=432454) +# We have to collect all object files that may be instrumented +# and go into the mysqld (also those that come from in static libs) +# run them again through dtrace -G to generate an ELF file that links +# to mysqld. +MACRO (DTRACE_INSTRUMENT_STATIC_LIBS target libs) +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND ENABLE_DTRACE) + FOREACH(lib ${libs}) + SET(dirs ${dirs} ${TARGET_OBJECT_DIRECTORY_${lib}}) + ENDFOREACH() + SET (obj ${CMAKE_BINARY_DIR}/${target}_dtrace_all.o) + ADD_CUSTOM_COMMAND( + OUTPUT ${obj} + DEPENDS ${libs} + COMMAND ${CMAKE_COMMAND} + -DDTRACE=${DTRACE} + -DOUTFILE=${obj} + -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d + -DDTRACE_FLAGS=${DTRACE_FLAGS} + "-DDIRS=${dirs}" + -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake + VERBATIM + ) + ADD_CUSTOM_TARGET(${target}_dtrace_all DEPENDS ${obj}) + ADD_DEPENDENCIES(${target} ${target}_dtrace_all) + TARGET_LINK_LIBRARIES(${target} ${obj}) +ENDIF() +ENDMACRO() diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 92cf55aa4ff..8062fd62c1a 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -91,39 +91,7 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS} ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${SSL_LIBRARIES}) - - -# Ugly workaround for Solaris' DTrace inability to use probes -# from static libraries currently, discussed e.g in this thread -# (http://opensolaris.org/jive/thread.jspa?messageID=432454) -# We have to collect all object files that may be instrumented -# and go into the mysqld (also those that come from in static libs) -# run them again through dtrace -G to generate an ELF file that links -# to mysqld. -IF(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND ENABLE_DTRACE) - SET(DTRACE_PROBES_ALL ${CMAKE_CURRENT_BINARY_DIR}/dtrace_probes_all.o) - SET(DTRACED_LIBS sql ${MYSQLD_STATIC_PLUGIN_LIBS} mysys) - FOREACH(lib ${DTRACED_LIBS}) - SET(DTRACE_DIRS ${DTRACE_DIRS} ${TARGET_OBJECT_DIRECTORY_${lib}}) - ENDFOREACH() - MESSAGE("DTRACE_DIRS=${DTRACE_DIRS}") - ADD_CUSTOM_COMMAND( - OUTPUT ${DTRACE_PROBES_ALL} - DEPENDS ${DTRACED_LIBS} - COMMAND ${CMAKE_COMMAND} - -DDTRACE=${DTRACE} - -DOUTFILE=${DTRACE_PROBES_ALL} - -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d - -DDTRACE_FLAGS=${DTRACE_FLAGS} - "-DDIRS=${DTRACE_DIRS}" - -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake - VERBATIM - ) -ELSE() - SET(DTRACE_PROBES_ALL) -ENDIF() - IF(WIN32) SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h) @@ -132,7 +100,6 @@ ELSE() ENDIF() ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) - IF(NOT WITHOUT_DYNAMIC_PLUGINS) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) @@ -168,6 +135,11 @@ IF(INTERFACE_LIBS) "${INTERFACE_LIBS}") ENDIF() +# On Solaris, some extra effort is required in order to get dtrace probes +# from static libraries +DTRACE_INSTRUMENT_STATIC_LIBS(mysqld + "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}") + INSTALL(TARGETS mysqld DESTINATION bin) INSTALL_DEBUG_SYMBOLS(mysqld) From c5d7bcf3761aab79ff0bd49b9a9f7565583ebd58 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 12 Dec 2009 02:03:05 +0100 Subject: [PATCH 170/466] Cleanup, use MYSQL_INSTALL_TARGETS macro instead of INSTALL(TARGETS) combined with INSTALL_DEBUG_SYMBOLS --- client/CMakeLists.txt | 3 +-- cmake/cmake_parse_arguments.cmake | 2 +- cmake/install_macros.cmake | 28 ++++++++++++++++++++++++++++ cmake/plugin.cmake | 3 +-- libmysql/CMakeLists.txt | 6 ++---- libmysqld/CMakeLists.txt | 6 ++---- sql/CMakeLists.txt | 5 ++--- storage/ibmdb2i/CMakeLists.txt | 1 - storage/innobase/CMakeLists.txt | 3 +-- storage/myisam/CMakeLists.txt | 5 +---- storage/myisammrg/CMakeLists.txt | 3 --- 11 files changed, 39 insertions(+), 26 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1cda6592a6e..7d501f19b10 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -81,6 +81,5 @@ SET(INSTALL_EXES mysqlbinlog mysqltest) -INSTALL(TARGETS ${INSTALL_EXES} DESTINATION bin) -INSTALL_DEBUG_SYMBOLS("${INSTALL_EXES}") +MYSQL_INSTALL_TARGETS(${INSTALL_EXES} DESTINATION bin) diff --git a/cmake/cmake_parse_arguments.cmake b/cmake/cmake_parse_arguments.cmake index e9bd7f872d1..ea601f916a8 100644 --- a/cmake/cmake_parse_arguments.cmake +++ b/cmake/cmake_parse_arguments.cmake @@ -26,7 +26,7 @@ MACRO(CMAKE_PARSE_ARGUMENTS prefix arg_names option_names) SET(current_arg_name DEFAULT_ARGS) SET(current_arg_list) - FOREACH(arg ${ARGN}) + FOREACH(arg ${ARGN}) SET(larg_names ${arg_names}) LIST(FIND larg_names "${arg}" is_arg_name) IF (is_arg_name GREATER -1) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 3925ce292de..b1f03b40fe6 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -13,6 +13,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake) MACRO (INSTALL_DEBUG_SYMBOLS targets) IF(MSVC) FOREACH(target ${targets}) @@ -69,3 +71,29 @@ IF(UNIX) ENDIF() ENDMACRO() +# Installs targets, also installs pdbs on Windows. +# +# More stuff can be added later, e.g signing +# or pre-link custom targets (one example is creating +# version resource for windows executables) + +FUNCTION(MYSQL_INSTALL_TARGETS) + CMAKE_PARSE_ARGUMENTS(ARG + "DESTINATION" + "" + ${ARGN} + ) + SET(TARGETS ${ARG_DEFAULT_ARGS}) + IF(NOT TARGETS) + MESSAGE(FATAL_ERROR "Need target list for MYSQL_INSTALL_TARGETS") + ENDIF() + IF(NOT ARG_DESTINATION) + MESSAGE(FATAL_ERROR "Need DESTINATION parameter for MYSQL_INSTALL_TARGETS") + ENDIF() + MESSAGE("INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION})") + INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION}) + SET(INSTALL_LOCATION ${ARG_DESTINATION} ) + INSTALL_DEBUG_SYMBOLS("${TARGETS}") + SET(INSTALL_LOCATION) +ENDFUNCTION() + diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index f718563acda..41fc3739979 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -153,8 +153,7 @@ MACRO(MYSQL_ADD_PLUGIN) OUTPUT_NAME "${ARG_MODULE_OUTPUT_NAME}") # Install dynamic library SET(INSTALL_LOCATION lib/plugin) - INSTALL(TARGETS ${target} DESTINATION ${INSTALL_LOCATION}) - INSTALL_DEBUG_SYMBOLS(${target}) + MYSQL_INSTALL_TARGETS(${target} DESTINATION ${INSTALL_LOCATION}) ENDIF() ENDMACRO() diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index db7d2ee2468..a2ccd8f340d 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -153,14 +153,14 @@ SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. MERGE_LIBRARIES(mysqlclient STATIC ${LIBS}) -INSTALL(TARGETS mysqlclient DESTINATION lib) +MYSQL_INSTALL_TARGETS(mysqlclient DESTINATION lib) IF(UNIX) INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient lib) ENDIF() -INSTALL_DEBUG_SYMBOLS(mysqlclient) IF(NOT DISABLE_SHARED) MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) + MYSQL_INSTALL_TARGETS(libmysql DESTINATION lib) IF(UNIX) # Name of shared library is mysqlclient on Unix SET_TARGET_PROPERTIES(libmysql PROPERTIES @@ -179,8 +179,6 @@ IF(NOT DISABLE_SHARED) #(mysqlclient in this case) SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) - INSTALL(TARGETS libmysql DESTINATION lib) INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) ENDIF() - INSTALL_DEBUG_SYMBOLS(libmysql) ENDIF() diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 56e16e6cd1c..b0131bddee0 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -120,11 +120,9 @@ ENDFOREACH() MERGE_LIBRARIES(mysqlserver STATIC ${EMBEDDED_LIBS} OUTPUT_NAME ${MYSQLSERVER_OUTPUT_NAME}) -INSTALL(TARGETS mysqlserver DESTINATION lib) -INSTALL_DEBUG_SYMBOLS(mysqlserver) +MYSQL_INSTALL_TARGETS(mysqlserver DESTINATION lib) IF(MSVC AND NOT DISABLE_SHARED) MERGE_LIBRARIES(libmysqld SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) - INSTALL(TARGETS libmysqld DESTINATION lib) - INSTALL_DEBUG_SYMBOLS(libmysqld) + MYSQL_INSTALL_TARGETS(libmysqld DESTINATION lib) ENDIF() diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 8062fd62c1a..6adad10a2e0 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -140,9 +140,8 @@ ENDIF() DTRACE_INSTRUMENT_STATIC_LIBS(mysqld "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}") -INSTALL(TARGETS mysqld DESTINATION bin) -INSTALL_DEBUG_SYMBOLS(mysqld) - +MYSQL_INSTALL_TARGETS(mysqld DESTINATION bin) + # Handle out-of-source build from source package with possibly broken # bison. Copy bison output to from source to build directory, if not already diff --git a/storage/ibmdb2i/CMakeLists.txt b/storage/ibmdb2i/CMakeLists.txt index b1517f53f7f..e8db6989583 100644 --- a/storage/ibmdb2i/CMakeLists.txt +++ b/storage/ibmdb2i/CMakeLists.txt @@ -13,7 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(IBMDB2I_PLUGIN_DYNAMIC "ha_ibmdb2i") CHECK_INCLUDE_FILES(qlgusr.h HAVE_PASE_ENVIRONMENT) IF(HAVE_PASE_ENVIRONMENT) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index e5ecc98dc3a..76acec8f6d7 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -197,8 +197,7 @@ ELSEIF (MYSQL_VERSION_ID LESS "50137") ADD_DEPENDENCIES(innobase GenError) ENDIF() ELSE() - # New plugin support, cross-platform , name for shared library - # is given in INNOBASE_PLUGIN_STATIC and INNOBASE_PLUGIN_DYNAMIC + # New plugin support, cross-platform , base name for shared module is "ha_innodb" MYSQL_ADD_PLUGIN(INNOBASE ${INNOBASE_SOURCES} STORAGE_ENGINE MODULE_OUTPUT_NAME ha_innodb LINK_LIBRARIES ${ZLIB_LIBRARY}) diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt index 396b5e29834..9359eca0ccc 100755 --- a/storage/myisam/CMakeLists.txt +++ b/storage/myisam/CMakeLists.txt @@ -13,9 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(MYISAM_PLUGIN_STATIC "myisam") -SET(MYISAM_PLUGIN_MANDATORY TRUE) - SET(MYISAM_SOURCES ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c ft_stem.c ha_myisam.cc ft_stopwords.c ft_update.c mi_cache.c mi_changed.c mi_check.c @@ -68,4 +65,4 @@ IF (MSVC) SET_TARGET_PROPERTIES(myisamchk myisampack PROPERTIES LINK_FLAGS "setargv.obj") ENDIF() -INSTALL(TARGETS myisamchk myisamlog myisampack myisam_ftdump DESTINATION bin) +MYSQL_INSTALL_TARGETS(myisamchk myisamlog myisampack myisam_ftdump DESTINATION bin) diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt index 59bc5ce4fc1..44f2e276c19 100755 --- a/storage/myisammrg/CMakeLists.txt +++ b/storage/myisammrg/CMakeLists.txt @@ -13,9 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(MYISAMMRG_PLUGIN_STATIC "myisammrg") -SET(MYISAMMRG_PLUGIN_MANDATORY 1) - SET(MYISAMMRG_SOURCES myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myrg_info.c ha_myisammrg.cc myrg_locking.c myrg_open.c myrg_panic.c myrg_queue.c myrg_range.c From 4a0af44a81f74d299b296e1b7cd100b9fecb0d82 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 12 Dec 2009 02:10:47 +0100 Subject: [PATCH 171/466] remove debug message --- cmake/install_macros.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index b1f03b40fe6..0f85e45c0d5 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -90,7 +90,6 @@ FUNCTION(MYSQL_INSTALL_TARGETS) IF(NOT ARG_DESTINATION) MESSAGE(FATAL_ERROR "Need DESTINATION parameter for MYSQL_INSTALL_TARGETS") ENDIF() - MESSAGE("INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION})") INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION}) SET(INSTALL_LOCATION ${ARG_DESTINATION} ) INSTALL_DEBUG_SYMBOLS("${TARGETS}") From a8e3b18bba320aed4458e5873c929119b45d8450 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 12 Dec 2009 20:30:18 +0100 Subject: [PATCH 172/466] new option SIGNCODE to sign executables and dlls with digital certificates --- cmake/install_macros.cmake | 49 ++++++++++++++++++++++++++++++++++++-- extra/CMakeLists.txt | 4 ++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 0f85e45c0d5..42b8198f48e 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -71,6 +71,43 @@ IF(UNIX) ENDIF() ENDMACRO() +IF(WIN32) + OPTION(SIGNCODE "Sign executables and dlls with digital certificate" OFF) + IF(SIGNCODE) + SET(SIGNTOOL_PARAMETERS + /a /t http://timestamp.verisign.com/scripts/timstamp.dll + CACHE STRING "parameters for signtool (list)") + MARK_AS_ADVANCED(SIGNCODE SIGNTOOL_PARAMETERS) + ENDIF() + IF(SIGNCODE) + FIND_PROGRAM(SIGNTOOL_EXECUTABLE signtool) + IF(NOT SIGNTOOL_EXECUTABLE) + MESSAGE(FATAL_ERROR + "signtool is not found. Signing executables not possible") + ENDIF() + ENDIF() +ENDIF() + +MACRO(SIGN_TARGET target) + GET_TARGET_PROPERTY(target_type ${target} TYPE) + IF(target_type AND NOT target_type MATCHES "STATIC") + GET_TARGET_PROPERTY(target_location ${target} LOCATION) + IF(CMAKE_GENERATOR MATCHES "Visual Studio") + STRING(REPLACE "${CMAKE_CFG_INTDIR}" "\${CMAKE_INSTALL_CONFIG_NAME}" + target_location ${target_location}) + ENDIF() + INSTALL(CODE + "EXECUTE_PROCESS(COMMAND + ${SIGNTOOL_EXECUTABLE} sign ${SIGNTOOL_PARAMETERS} ${target_location} + ERROR_VARIABLE ERR) + IF(NOT \${ERR} EQUAL 0) + MESSAGE(FATAL_ERROR \"Error signing ${target_location}\") + ENDIF() + ") + ENDIF() +ENDMACRO() + + # Installs targets, also installs pdbs on Windows. # # More stuff can be added later, e.g signing @@ -80,8 +117,8 @@ ENDMACRO() FUNCTION(MYSQL_INSTALL_TARGETS) CMAKE_PARSE_ARGUMENTS(ARG "DESTINATION" - "" - ${ARGN} + "" + ${ARGN} ) SET(TARGETS ${ARG_DEFAULT_ARGS}) IF(NOT TARGETS) @@ -90,6 +127,14 @@ FUNCTION(MYSQL_INSTALL_TARGETS) IF(NOT ARG_DESTINATION) MESSAGE(FATAL_ERROR "Need DESTINATION parameter for MYSQL_INSTALL_TARGETS") ENDIF() + + # If signing is required, sign executables before installing + IF(SIGNCODE) + FOREACH(target ${TARGETS}) + SIGN_TARGET(${target}) + ENDFOREACH() + ENDIF() + INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION}) SET(INSTALL_LOCATION ${ARG_DESTINATION} ) INSTALL_DEBUG_SYMBOLS("${TARGETS}") diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index 899e5ecf1fd..90f28698564 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -77,7 +77,7 @@ IF(UNIX) ADD_EXECUTABLE(mysql_waitpid mysql_waitpid.c) TARGET_LINK_LIBRARIES(mysql_waitpid mysys) - INSTALL(TARGETS innochecksum mysql_waitpid resolve_stack_dump DESTINATION bin) + MYSQL_INSTALL_TARGETS(innochecksum mysql_waitpid resolve_stack_dump DESTINATION bin) ENDIF() -INSTALL(TARGETS perror resolveip my_print_defaults replace DESTINATION bin) +MYSQL_INSTALL_TARGETS(perror resolveip my_print_defaults DESTINATION bin) From 535d1fa97ea7816fb2789f44a7b42edf0b7954ac Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 13 Dec 2009 00:17:42 +0100 Subject: [PATCH 173/466] Mark signing parameters as advanced --- cmake/install_macros.cmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 42b8198f48e..f840884c1e5 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -73,18 +73,17 @@ ENDMACRO() IF(WIN32) OPTION(SIGNCODE "Sign executables and dlls with digital certificate" OFF) + MARK_AS_ADVANCED(SIGNCODE) IF(SIGNCODE) SET(SIGNTOOL_PARAMETERS /a /t http://timestamp.verisign.com/scripts/timstamp.dll CACHE STRING "parameters for signtool (list)") - MARK_AS_ADVANCED(SIGNCODE SIGNTOOL_PARAMETERS) - ENDIF() - IF(SIGNCODE) FIND_PROGRAM(SIGNTOOL_EXECUTABLE signtool) IF(NOT SIGNTOOL_EXECUTABLE) MESSAGE(FATAL_ERROR "signtool is not found. Signing executables not possible") ENDIF() + MARK_AS_ADVANCED(SIGNTOOL_EXECUTABLE SIGNTOOL_PARAMETERS) ENDIF() ENDIF() From 826ecdb8520f7ab71407a77cc5712a3f104be6a3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 13 Dec 2009 05:42:26 +0000 Subject: [PATCH 174/466] Dtrace on Solaris: fix excessive relinking that results from rerunning dtrace -G second time on the objects that come into static library. The reason for relinking is that dtrace -G changes timestamp for the object file, which in turn results in re-archiving the mysys on next time make runs, which causes relinking of the whole mysql, because everything depends on mysys. The fix is to copy objects into temp before running dtrace -G in mysqld pre-build step. This way dtrace does not affect the object timestamp. --- cmake/dtrace.cmake | 9 +++--- cmake/dtrace_prelink.cmake | 60 ++++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index 653114d4963..92a39a224d5 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -75,7 +75,7 @@ MACRO (DTRACE_INSTRUMENT target) IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") SET(objdir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir) SET(outfile ${objdir}/${target}_dtrace.o) - + GET_TARGET_PROPERTY(target_type ${target} TYPE) ADD_CUSTOM_COMMAND( TARGET ${target} PRE_LINK COMMAND ${CMAKE_COMMAND} @@ -84,6 +84,7 @@ MACRO (DTRACE_INSTRUMENT target) -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d -DDTRACE_FLAGS=${DTRACE_FLAGS} -DDIRS=. + -DTYPE=${target_type} -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake WORKING_DIRECTORY ${objdir} ) @@ -94,7 +95,7 @@ MACRO (DTRACE_INSTRUMENT target) ELSE() # For static library flags, add the object to the library. # Note: DTrace probes in static libraries are unusable currently - # (see http://opensolaris.org/jive/thread.jspa?messageID=432454) + # (see explanation for DTRACE_INSTRUMENT_STATIC_LIBS below) # but maybe one day this will be fixed. GET_TARGET_PROPERTY(target_location ${target} LOCATION) ADD_CUSTOM_COMMAND( @@ -102,7 +103,6 @@ MACRO (DTRACE_INSTRUMENT target) COMMAND ${CMAKE_AR} r ${target_location} ${outfile} COMMAND ${CMAKE_RANLIB} ${target_location} ) - # Used in DTRACE_INSTRUMENT_WITH_STATIC_LIBS SET(TARGET_OBJECT_DIRECTORY_${target} ${objdir} CACHE INTERNAL "") ENDIF() @@ -123,7 +123,7 @@ IF(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND ENABLE_DTRACE) FOREACH(lib ${libs}) SET(dirs ${dirs} ${TARGET_OBJECT_DIRECTORY_${lib}}) ENDFOREACH() - SET (obj ${CMAKE_BINARY_DIR}/${target}_dtrace_all.o) + SET (obj ${CMAKE_CURRENT_BINARY_DIR}/${target}_dtrace_all.o) ADD_CUSTOM_COMMAND( OUTPUT ${obj} DEPENDS ${libs} @@ -133,6 +133,7 @@ IF(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND ENABLE_DTRACE) -DDFILE=${CMAKE_BINARY_DIR}/include/probes_mysql.d -DDTRACE_FLAGS=${DTRACE_FLAGS} "-DDIRS=${dirs}" + -DTYPE=MERGE -P ${CMAKE_SOURCE_DIR}/cmake/dtrace_prelink.cmake VERBATIM ) diff --git a/cmake/dtrace_prelink.cmake b/cmake/dtrace_prelink.cmake index ae475a90921..37e560cdaf8 100644 --- a/cmake/dtrace_prelink.cmake +++ b/cmake/dtrace_prelink.cmake @@ -19,17 +19,65 @@ # Do not follow symlinks in GLOB_RECURSE CMAKE_POLICY(SET CMP0009 NEW) +FILE(REMOVE ${OUTFILE}) + +MACRO(CONVERT_TO_RELATIVE_PATHS files rel_paths) + GET_FILENAME_COMPONENT(abs_dir . ABSOLUTE) + SET(${rel_paths}) + FOREACH(file ${files}) + FILE(RELATIVE_PATH rel ${abs_dir} ${file}) + LIST(APPEND ${rel_paths} ${rel}) + ENDFOREACH() +ENDMACRO() + +IF(TYPE STREQUAL "MERGE") + # Rerun dtrace on objects that are already in static libraries. + # Object paths are stored in text files named 'dtrace_objects' + # in the input directores. We have to copy the objects into temp. + # directory, as running dtrace -G on original files will change + # timestamps and cause rebuilds or the libraries / excessive + # relink + FILE(REMOVE_RECURSE dtrace_objects_merge) + MAKE_DIRECTORY(dtrace_objects_merge) + + FOREACH(dir ${DIRS}) + FILE(STRINGS ${dir}/dtrace_objects OBJS) + FOREACH(obj ${OBJS}) + IF(obj) + EXECUTE_PROCESS(COMMAND cp ${obj} dtrace_objects_merge) + ENDIF() + ENDFOREACH() + ENDFOREACH() + FILE(GLOB_RECURSE OBJECTS dtrace_objects_merge/*.o) + CONVERT_TO_RELATIVE_PATHS("${OBJECTS}" REL_OBJECTS) + EXECUTE_PROCESS( + COMMAND ${DTRACE} ${DTRACE_FLAGS} -o ${OUTFILE} -G -s ${DFILE} ${REL_OBJECTS} + ) + RETURN() +ENDIF() + FOREACH(dir ${DIRS}) FILE(GLOB_RECURSE OBJECTS ${dir}/*.o) - # Use relative paths to generate shorter command line - GET_FILENAME_COMPONENT(CURRENT_ABS_DIR . ABSOLUTE) - FOREACH(OBJ ${OBJECTS}) - FILE(RELATIVE_PATH REL ${CURRENT_ABS_DIR} ${OBJ}) - LIST(APPEND REL_OBJECTS ${REL}) - ENDFOREACH() + CONVERT_TO_RELATIVE_PATHS("${OBJECTS}" REL) + LIST(APPEND REL_OBJECTS ${REL}) ENDFOREACH() +FILE(WRITE dtrace_timestamp "") EXECUTE_PROCESS( COMMAND ${DTRACE} ${DTRACE_FLAGS} -o ${OUTFILE} -G -s ${DFILE} ${REL_OBJECTS} ) +# Save objects that contain dtrace probes in a file. +# This file is used when script is called with -DTYPE=MERGE +# to dtrace from static libs. +# To find objects with probes, look at the timestamp, it was updated +# by dtrace -G run +IF(TYPE MATCHES "STATIC") + FILE(WRITE dtrace_objects "") + FOREACH(obj ${REL_OBJECTS}) + IF(${obj} IS_NEWER_THAN dtrace_timestamp) + GET_FILENAME_COMPONENT(obj_absolute_path ${obj} ABSOLUTE) + FILE(APPEND dtrace_objects "${obj_absolute_path}\n" ) + ENDIF() + ENDFOREACH() +ENDIF() From 1f6bfeed4bbfbc38d4ec93935ba91893a900d6eb Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 13 Dec 2009 23:50:33 +0000 Subject: [PATCH 175/466] Fix add_suppresion in partition_recover_myisam. error message would contain full path to ha_myisam.cc, if MySQL is compiled with CMake but suppression assumed only the filename without path. --- mysql-test/suite/parts/r/partition_recover_myisam.result | 2 +- mysql-test/suite/parts/t/partition_recover_myisam.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/parts/r/partition_recover_myisam.result b/mysql-test/suite/parts/r/partition_recover_myisam.result index 49775ee498e..f4cdc2a418a 100644 --- a/mysql-test/suite/parts/r/partition_recover_myisam.result +++ b/mysql-test/suite/parts/r/partition_recover_myisam.result @@ -1,5 +1,5 @@ call mtr.add_suppression("./test/t1_will_crash"); -call mtr.add_suppression("Got an error from unknown thread, ha_myisam.cc"); +call mtr.add_suppression("Got an error from unknown thread"); CREATE TABLE t1_will_crash (a INT, KEY (a)) ENGINE=MyISAM; INSERT INTO t1_will_crash VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); FLUSH TABLES; diff --git a/mysql-test/suite/parts/t/partition_recover_myisam.test b/mysql-test/suite/parts/t/partition_recover_myisam.test index 64bc821ac37..d06e1914949 100644 --- a/mysql-test/suite/parts/t/partition_recover_myisam.test +++ b/mysql-test/suite/parts/t/partition_recover_myisam.test @@ -1,7 +1,7 @@ # test the auto-recover (--myisam-recover) of partitioned myisam tables call mtr.add_suppression("./test/t1_will_crash"); -call mtr.add_suppression("Got an error from unknown thread, ha_myisam.cc"); +call mtr.add_suppression("Got an error from unknown thread"); --source include/have_partition.inc --disable_warnings From b82642966b7914e4d9906b20c65f07ae032b775d Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 15 Dec 2009 10:12:24 +0100 Subject: [PATCH 176/466] Bug #49672 mtr should extract end of result log if mysqltest fails without output Extracts last 20 lines if no output after failure --- mysql-test/mysql-test-run.pl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 7d2426459d0..f0898e1cab7 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3500,7 +3500,7 @@ sub run_testcase ($) { { # mysqltest failed, probably crashed $tinfo->{comment}= - "mysqltest failed with unexpected return code $res"; + "mysqltest failed with unexpected return code $res\n"; report_failure_and_restart($tinfo); } @@ -4091,6 +4091,19 @@ sub report_failure_and_restart ($) { # about what failed has been saved to file. Save the report # in tinfo $tinfo->{logfile}= mtr_fromfile($logfile); + # If no newlines in the test log: + if ($tinfo->{logfile} !~ /\n/) + { + # Show how far it got before suddenly failing + $tinfo->{comment}.= "mysqltest failed but provided no output\n"; + my $log_file_name= $opt_vardir."/log/".$tinfo->{shortname}.".log"; + if (-e $log_file_name) { + $tinfo->{comment}.= + "The result from queries just before the failure was:". + "\n< snip >\n". + mtr_lastlinesfromfile($log_file_name, 20)."\n"; + } + } } else { From 3394cbf72cc09ca4735f88c2669ccd853134dfa8 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 15 Dec 2009 14:18:10 +0100 Subject: [PATCH 177/466] Bug #48940 MDL deadlocks against mysql_rm_db This deadlock would occur between two connections A and B if statements where executed in the following way: 1) Connection A executes a DML statement against table s1.t1 with autocommit off. This causes a shared metadata lock on s1.t1 to be acquired. (With autocommit on, the metadata lock will be dropped once the statment completes and the deadlock will not occour.) 2) Connection B tries to DROP DATABASE s1. This will block against the metadata lock connection A holds on s1.t1. While blocking, connection B will hold the LOCK_mysql_create_db mutex. 3) Connection A tries to ALTER DATABASE s1. This will block when trying to get LOCK_mysql_create_db mutex held by connection B. 4) Deadlock between DROP DATABASE and ALTER DATABASE (which has autocommit off). If Connection A used an explicitly started transaction rather than having autocommit off, this deadlock did not happen as ALTER DATABASE is disallowed inside transactions. This patch fixes the problem by changing ALTER DATABASE to cause an implicit commit before executing. This will cause the metadata lock on s1.t1 to be dropped, allowing DROP DATABASE to proceed. This will in turn cause the LOCK_mysql_create_db mutex to be unlocked, allowing ALTER DATABASE to proceed. Note that SQL commands other than ALTER DATABASE that also use LOCK_mysql_create_db, already cause an implicit commit. Incompatible change: ALTER DATABASE (and its synonym ALTER SCHEMA) now cause an implicit commit. This must be reflected in the documentation. Test case added to schema.test. --- mysql-test/r/schema.result | 17 +++++++++++++ mysql-test/t/schema.test | 50 ++++++++++++++++++++++++++++++++++++++ sql/sql_parse.cc | 10 ++++---- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index 564fb3626df..33a2d4d9448 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -11,3 +11,20 @@ mtr mysql test drop schema foo; +# +# Bug #48940 MDL deadlocks against mysql_rm_db +# +DROP SCHEMA IF EXISTS schema1; +# Connection default +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (a INT); +SET autocommit= FALSE; +INSERT INTO schema1.t1 VALUES (1); +# Connection 2 +DROP SCHEMA schema1; +# Connection default +ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; +ERROR HY000: Can't create/write to file './schema1/db.opt' (Errcode: 2) +SET autocommit= TRUE; +# Connection 2 +# Connection default diff --git a/mysql-test/t/schema.test b/mysql-test/t/schema.test index a08d9b38935..a380a6241dd 100644 --- a/mysql-test/t/schema.test +++ b/mysql-test/t/schema.test @@ -4,6 +4,9 @@ # Drop mysqltest1 database, as it can left from the previous tests. # +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + --disable_warnings drop database if exists mysqltest1; --enable_warnings @@ -12,3 +15,50 @@ create schema foo; show create schema foo; show schemas; drop schema foo; + + +--echo # +--echo # Bug #48940 MDL deadlocks against mysql_rm_db +--echo # + +--disable_warnings +DROP SCHEMA IF EXISTS schema1; +--enable_warnings + +connect(con2, localhost, root); + +--echo # Connection default +connection default; + +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (a INT); + +SET autocommit= FALSE; +INSERT INTO schema1.t1 VALUES (1); + +--echo # Connection 2 +connection con2; +--send DROP SCHEMA schema1 + +--echo # Connection default +connection default; +let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist + WHERE state= 'Waiting for table' + AND info='DROP SCHEMA schema1'; +--source include/wait_condition.inc +--error 1 +ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; +SET autocommit= TRUE; + +--echo # Connection 2 +connection con2; +--reap + +--echo # Connection default +connection default; +disconnect con2; + + +# Check that all connections opened by test cases in this file are really +# gone so execution of other tests won't be affected by their presence. +--source include/wait_until_count_sessions.inc diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0d21eb82b87..6ea6c3e850b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -193,6 +193,8 @@ void init_update_queries(void) CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | @@ -203,7 +205,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_AUTO_COMMIT_TRANS; @@ -283,7 +284,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE_ALL]= CF_PROTECT_AGAINST_GRL; - sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; @@ -3430,7 +3430,7 @@ end_with_restore_list: if (check_access(thd,DROP_ACL,lex->name.str,0,1,0, is_schema_db(lex->name.str))) break; - if (thd->locked_tables_mode || thd->active_transaction()) + if (thd->locked_tables_mode) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); @@ -3464,7 +3464,7 @@ end_with_restore_list: res= 1; break; } - if (thd->locked_tables_mode || thd->active_transaction()) + if (thd->locked_tables_mode) { res= 1; my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, @@ -3504,7 +3504,7 @@ end_with_restore_list: #endif if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str))) break; - if (thd->locked_tables_mode || thd->active_transaction()) + if (thd->locked_tables_mode) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); From 833be37d951a027bf4611373c4b8806f2d576d55 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 16 Dec 2009 02:51:13 +0100 Subject: [PATCH 178/466] Add version resource to windows executables --- cmake/Makefile.am | 3 +- cmake/install_macros.cmake | 6 ++ cmake/mysql_version.cmake | 124 +++++++++++++++++++++++++++++-------- cmake/versioninfo.rc.in | 25 ++++++++ 4 files changed, 131 insertions(+), 27 deletions(-) create mode 100644 cmake/versioninfo.rc.in diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 6beb4370ebf..95b5c313dca 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -19,4 +19,5 @@ EXTRA_DIST = \ stack_direction.c \ do_abi_check.cmake \ merge_archives_unix.cmake.in \ - dtrace_prelink.cmake + dtrace_prelink.cmake \ + versioninfo.rc.in diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index f840884c1e5..1cda14f8661 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -134,6 +134,12 @@ FUNCTION(MYSQL_INSTALL_TARGETS) ENDFOREACH() ENDIF() + IF(WIN32) + FOREACH(target ${TARGETS}) + EMBED_VERSION_INFO(${target}) + ENDFOREACH() + ENDIF() + INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION}) SET(INSTALL_LOCATION ${ARG_DESTINATION} ) INSTALL_DEBUG_SYMBOLS("${TARGETS}") diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index 8b0e8f4efab..54824221350 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -19,11 +19,11 @@ MACRO(MYSQL_GET_CONFIG_VALUE keyword var) IF(NOT ${var}) IF (EXISTS ${CMAKE_SOURCE_DIR}/configure.in) FILE (STRINGS ${CMAKE_SOURCE_DIR}/configure.in str REGEX "^[ ]*${keyword}=") - IF(str) - STRING(REPLACE "${keyword}=" "" str ${str}) - STRING(REGEX REPLACE "[ ].*" "" str ${str}) - SET(${var} ${str} CACHE INTERNAL "Config variable") - ENDIF() + IF(str) + STRING(REPLACE "${keyword}=" "" str ${str}) + STRING(REGEX REPLACE "[ ].*" "" str ${str}) + SET(${var} ${str} CACHE INTERNAL "Config variable") + ENDIF() ENDIF() ENDIF() ENDMACRO() @@ -35,19 +35,19 @@ MACRO(GET_MYSQL_VERSION) IF(NOT VERSION_STRING) IF(EXISTS ${CMAKE_SOURCE_DIR}/configure.in) - FILE(STRINGS ${CMAKE_SOURCE_DIR}/configure.in str REGEX "AM_INIT_AUTOMAKE") - STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-][^ \\)]+" VERSION_STRING "${str}") - IF(NOT VERSION_STRING) - FILE(STRINGS configure.in str REGEX "AC_INIT\\(") - STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-][^ \\]]+" VERSION_STRING "${str}") + FILE(STRINGS ${CMAKE_SOURCE_DIR}/configure.in str REGEX "AM_INIT_AUTOMAKE") + STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-][^ \\)]+" VERSION_STRING "${str}") + IF(NOT VERSION_STRING) + FILE(STRINGS configure.in str REGEX "AC_INIT\\(") + STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-][^ \\]]+" VERSION_STRING "${str}") + ENDIF() ENDIF() - ENDIF() ENDIF() IF(NOT VERSION_STRING) MESSAGE(FATAL_ERROR - "VERSION_STRING cannot be parsed, please specify -DVERSION_STRING=major.minor.patch-extra" - "when calling cmake") + "VERSION_STRING cannot be parsed, please specify -DVERSION_STRING=major.minor.patch-extra" + "when calling cmake") ENDIF() SET(VERSION ${VERSION_STRING}) @@ -100,18 +100,18 @@ IF(NOT CPACK_PACKAGE_FILE_NAME) SET(SYSTEM_NAME_AND_PROCESSOR "win32") ENDIF() ELSE() - IF(NOT PLATFORM) - SET(PLATFORM ${CMAKE_SYSTEM_NAME}) - ENDIF() - IF(NOT MACHINE) - SET(MACHINE ${CMAKE_SYSTEM_PROCESSOR}) - IF(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT ${MACHINE} MATCHES "ia64") - # On almost every 64 bit machine (except IA64) it is possible - # to build 32 bit packages. Add -64bit suffix to differentiate - # between 32 and 64 bit packages. - SET(MACHINE ${MACHINE}-64bit) - ENDIF() - ENDIF() + IF(NOT PLATFORM) + SET(PLATFORM ${CMAKE_SYSTEM_NAME}) + ENDIF() + IF(NOT MACHINE) + SET(MACHINE ${CMAKE_SYSTEM_PROCESSOR}) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT ${MACHINE} MATCHES "ia64") + # On almost every 64 bit machine (except IA64) it is possible + # to build 32 bit packages. Add -64bit suffix to differentiate + # between 32 and 64 bit packages. + SET(MACHINE ${MACHINE}-64bit) + ENDIF() + ENDIF() SET(SYSTEM_NAME_AND_PROCESSOR "${PLATFORM}-${MACHINE}") ENDIF() ENDIF() @@ -125,10 +125,11 @@ IF(NOT CPACK_PACKAGE_FILE_NAME) SET(CPACK_PACKAGE_FILE_NAME ${package_name}) ENDIF() + IF(NOT CPACK_SOURCE_PACKAGE_FILE_NAME) SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mysql-${VERSION}") ENDIF() -SET(CPACK_PACKAGE_VENDOR "Sun Microsystems") +SET(CPACK_PACKAGE_VENDOR "Sun Microsystems, Inc") SET(CPACK_SOURCE_GENERATOR "TGZ") SET(CPACK_SOURCE_IGNORE_FILES \\\\.bzr/ @@ -136,7 +137,78 @@ SET(CPACK_SOURCE_IGNORE_FILES .bzrignore CMakeCache.txt /CMakeFiles/ + /version_resources/ /_CPack_Packages/ $.gz $.zip ) + +# Defintions for windows version resources +SET(PRODUCTNAME "MySQL Server") +SET(COMPANYNAME ${CPACK_PACKAGE_VENDOR}) + +# Function to embed version info into executables/dlls on Windows +# Refer http://msdn.microsoft.com/en-us/library/aa381058(VS.85).aspx +# for more information +FUNCTION(EMBED_VERSION_INFO target) + IF(NOT WIN32) + RETURN() + ENDIF() + IF(NOT CMAKE_RC_COMPILER) + RETURN() + ENDIF() + + GET_TARGET_PROPERTY(target_type ${target} TYPE) + IF(target_type MATCHES "STATIC") + RETURN() + ENDIF() + IF(TARGET_TYPE MATCHES "EXE") + SET(FILETYPE VFT_APP) + ELSE() + SET(FILETYPE VFT_DLL) + ENDIF() + + IF(NOT MAJOR_VERSION) + MESSAGE(FATAL_ERROR "MAJOR_VERSION is not defined") + ENDIF() + IF(NOT MINOR_VERSION) + MESSAGE(FATAL_ERROR "MINOR_VERSION is not defined") + ENDIF() + IF(NOT PATCH) + SET(PATCH 0) + ENDIF() + + GET_TARGET_PROPERTY(target_location ${target} LOCATION) + GET_FILENAME_COMPONENT(INTERNALNAME ${target_location} NAME_WE) + IF(NOT FILEDESCRIPTION) + SET(FILEDESCRIPTION ${INTERNALNAME}) + ENDIF() + GET_FILENAME_COMPONENT(ORIGINALFILENAME ${target_location} NAME) + + # Directory where we have resouce script and compiled .res file + SET(RES_DIR ${CMAKE_CURRENT_BINARY_DIR}/version_resources) + + # Create resource script (.rc) + CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/versioninfo.rc.in + ${RES_DIR}/${target}_versioninfo.rc) + + # Compile resource script to object if required + IF(EXISTS ${RES_DIR}/${target}_versioninfo.res) + IF(${RES_DIR}/${target}_versioninfo.rc IS_NEWER_THAN + ${RES_DIR}/${target}_versioninfo.res) + SET(RUN_RC 1) + ENDIF() + ELSE() + SET(RUN_RC 1) + ENDIF() + + IF(RUN_RC) + # Run resource compiler + EXECUTE_PROCESS( + COMMAND ${CMAKE_RC_COMPILER} /nologo /fo ${target}_versioninfo.res + ${target}_versioninfo.rc + WORKING_DIRECTORY ${RES_DIR} + ) + ENDIF() + TARGET_LINK_LIBRARIES(${target} ${RES_DIR}/${target}_versioninfo.res) +ENDFUNCTION() diff --git a/cmake/versioninfo.rc.in b/cmake/versioninfo.rc.in new file mode 100644 index 00000000000..263398836a0 --- /dev/null +++ b/cmake/versioninfo.rc.in @@ -0,0 +1,25 @@ +#include +VS_VERSION_INFO VERSIONINFO +FILEVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH@,0 +PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH@,0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0 +FILEOS VOS__WINDOWS32 +FILETYPE @FILETYPE@ +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "FileDescription", "@FILEDESCRIPTION@\0" + VALUE "FileVersion", "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH@\0" + VALUE "InternalName", "@INTERNALNAME@\0" + VALUE "OriginalFilename", "@ORIGINALFILENAME@\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END From ffb0926373524b4e659b9a6035428880ad8b9e6b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 16 Dec 2009 03:51:07 +0100 Subject: [PATCH 179/466] attempt to fix error in rc call --- cmake/mysql_version.cmake | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index 54824221350..939b53f47c3 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -186,7 +186,7 @@ FUNCTION(EMBED_VERSION_INFO target) GET_FILENAME_COMPONENT(ORIGINALFILENAME ${target_location} NAME) # Directory where we have resouce script and compiled .res file - SET(RES_DIR ${CMAKE_CURRENT_BINARY_DIR}/version_resources) + SET(RES_DIR ${CMAKE_BINARY_DIR}/version_resources) # Create resource script (.rc) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/versioninfo.rc.in @@ -205,9 +205,8 @@ FUNCTION(EMBED_VERSION_INFO target) IF(RUN_RC) # Run resource compiler EXECUTE_PROCESS( - COMMAND ${CMAKE_RC_COMPILER} /nologo /fo ${target}_versioninfo.res - ${target}_versioninfo.rc - WORKING_DIRECTORY ${RES_DIR} + COMMAND ${CMAKE_RC_COMPILER} ${target}_versioninfo.rc + WORKING_DIRECTORY ${RES_DIR} OUTPUT_QUIET ) ENDIF() TARGET_LINK_LIBRARIES(${target} ${RES_DIR}/${target}_versioninfo.res) From fd81bc9f9cf756034d08c3a0359db27e023427d8 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 16 Dec 2009 10:06:37 +0100 Subject: [PATCH 180/466] backport mysqltest send_eval from 42520 --- client/mysqltest.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 3cd87bd3236..7f758e7c306 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -280,7 +280,7 @@ enum enum_commands { Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR, Q_LIST_FILES, Q_LIST_FILES_WRITE_FILE, Q_LIST_FILES_APPEND_FILE, Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER, - Q_MOVE_FILE, + Q_MOVE_FILE, Q_SEND_EVAL, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -378,6 +378,7 @@ const char *command_names[]= "send_shutdown", "shutdown_server", "move_file", + "send_eval", 0 }; @@ -7047,7 +7048,7 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) /* Evaluate query if this is an eval command */ - if (command->type == Q_EVAL) + if (command->type == Q_EVAL || command->type == Q_SEND_EVAL) { init_dynamic_string(&eval_query, "", command->query_len+256, 1024); do_eval(&eval_query, command->query, command->end, FALSE); @@ -7866,6 +7867,7 @@ int main(int argc, char **argv) break; } case Q_SEND: + case Q_SEND_EVAL: if (!*command->first_argument) { /* From ddea504a7d69d76de6da243d71b507df91d05593 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 16 Dec 2009 12:32:11 +0100 Subject: [PATCH 181/466] Bug #48541 Deadlock between LOCK_open and LOCK_mdl The reason for the deadlock was an improper exit from MDL_context::wait_for_locks() which caused mysys_var->current_mutex to remain LOCK_mdl even though LOCK_mdl was no longer held by that connection. This could for example lead to a deadlock in the following way: 1) INSERT DELAYED tries to open a table but fails, and trying to recover it calls wait_for_locks(). 2) Due to a pending exclusive request, wait_for_locks() fails and exits without resetting mysys_var->current_mutex for the delayed insert handler thread. So it continues to point to LOCK_mdl. 3) The handler thread manages to open a table. 4) A different connection takes LOCK_open and tries to take LOCK_mdl. 5) FLUSH TABLES from a third connection notices that the handler thread has a table open, and tries to kill it. This involves locking mysys_var->current_mutex while having LOCK_open locked. Since current_mutex mistakenly points to LOCK_mdl, we have a deadlock. This patch makes sure MDL_EXIT_COND() is called before exiting wait_for_locks(). This clears mysys->current_mutex which resolves the issue. An assert is added to recover_from_failed_open_table_attempt() after wait_for_locks() is called, to check that current_mutex is indeed reset. With this assert in place, existing tests in (e.g.) mdl_sync.test will fail without this patch. --- sql/mdl.cc | 3 ++- sql/sql_base.cc | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index a883b21423e..6187d4515a3 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1254,7 +1254,8 @@ MDL_context::wait_for_locks(MDL_request_list *mdl_requests) } if (!mdl_request) { - pthread_mutex_unlock(&LOCK_mdl); + /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ + MDL_EXIT_COND(m_thd, mysys_var, old_msg); break; } pthread_cond_wait(&COND_mdl, &LOCK_mdl); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 99a0b33f213..61028f692b3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3693,6 +3693,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table) case OT_WAIT: result= (thd->mdl_context.wait_for_locks(&m_mdl_requests) || tdc_wait_for_old_versions(thd, &m_mdl_requests)); + DBUG_ASSERT(thd->mysys_var->current_mutex == NULL); break; case OT_DISCOVER: { From f31b16543a7b0f1c07ecb54113df5b70ceef6550 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 16 Dec 2009 13:25:41 +0100 Subject: [PATCH 182/466] fix windows errors with version resource --- cmake/install_macros.cmake | 9 +++------ cmake/mysql_version.cmake | 36 ++++-------------------------------- sql/CMakeLists.txt | 1 + 3 files changed, 8 insertions(+), 38 deletions(-) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 1cda14f8661..7e11bdfb3ab 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -32,7 +32,9 @@ MACRO (INSTALL_DEBUG_SYMBOLS targets) STRING(REPLACE ".exe" ".pdb" pdb_location ${location}) STRING(REPLACE ".dll" ".pdb" pdb_location ${pdb_location}) STRING(REPLACE ".lib" ".pdb" pdb_location ${pdb_location}) - STRING(REPLACE "${CMAKE_CFG_INTDIR}" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) + IF(CMAKE_GENERATOR MATCHES "Visual Studio") + STRING(REPLACE "${CMAKE_CFG_INTDIR}" "\${CMAKE_INSTALL_CONFIG_NAME}" pdb_location ${pdb_location}) + ENDIF() INSTALL(FILES ${pdb_location} DESTINATION ${INSTALL_LOCATION}) ENDFOREACH() ENDIF() @@ -134,11 +136,6 @@ FUNCTION(MYSQL_INSTALL_TARGETS) ENDFOREACH() ENDIF() - IF(WIN32) - FOREACH(target ${TARGETS}) - EMBED_VERSION_INFO(${target}) - ENDFOREACH() - ENDIF() INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION}) SET(INSTALL_LOCATION ${ARG_DESTINATION} ) diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index 939b53f47c3..27564850107 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -150,7 +150,7 @@ SET(COMPANYNAME ${CPACK_PACKAGE_VENDOR}) # Function to embed version info into executables/dlls on Windows # Refer http://msdn.microsoft.com/en-us/library/aa381058(VS.85).aspx # for more information -FUNCTION(EMBED_VERSION_INFO target) +FUNCTION(WIN32_ADD_VERSION_INFO SOURCES) IF(NOT WIN32) RETURN() ENDIF() @@ -158,14 +158,8 @@ FUNCTION(EMBED_VERSION_INFO target) RETURN() ENDIF() - GET_TARGET_PROPERTY(target_type ${target} TYPE) - IF(target_type MATCHES "STATIC") - RETURN() - ENDIF() - IF(TARGET_TYPE MATCHES "EXE") + IF(NOT FILETYPE) SET(FILETYPE VFT_APP) - ELSE() - SET(FILETYPE VFT_DLL) ENDIF() IF(NOT MAJOR_VERSION) @@ -185,29 +179,7 @@ FUNCTION(EMBED_VERSION_INFO target) ENDIF() GET_FILENAME_COMPONENT(ORIGINALFILENAME ${target_location} NAME) - # Directory where we have resouce script and compiled .res file - SET(RES_DIR ${CMAKE_BINARY_DIR}/version_resources) - - # Create resource script (.rc) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/versioninfo.rc.in - ${RES_DIR}/${target}_versioninfo.rc) - - # Compile resource script to object if required - IF(EXISTS ${RES_DIR}/${target}_versioninfo.res) - IF(${RES_DIR}/${target}_versioninfo.rc IS_NEWER_THAN - ${RES_DIR}/${target}_versioninfo.res) - SET(RUN_RC 1) - ENDIF() - ELSE() - SET(RUN_RC 1) - ENDIF() - - IF(RUN_RC) - # Run resource compiler - EXECUTE_PROCESS( - COMMAND ${CMAKE_RC_COMPILER} ${target}_versioninfo.rc - WORKING_DIRECTORY ${RES_DIR} OUTPUT_QUIET - ) - ENDIF() - TARGET_LINK_LIBRARIES(${target} ${RES_DIR}/${target}_versioninfo.res) + ${CMAKE_CURRENT_BINARY_DIR}/${target}_versioninfo.rc) + LIST(APPEND ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/${target}_versioninfo.rc) ENDFUNCTION() diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 6adad10a2e0..9438fd8479d 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -95,6 +95,7 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS} IF(WIN32) SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h) + WIN32_ADD_VERSION_INFO(MYSQLD_SOURCE) ELSE() SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL}) ENDIF() From 5ec9e5daab35b108f8b5daa59ca1ab1c7e6459b4 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 17 Dec 2009 13:43:07 +0100 Subject: [PATCH 183/466] Bug #48724 Deadlock between INSERT DELAYED and FLUSH TABLES If the handler (or delayed insert) thread failed to lock a table due to being killed, the "dead" flag was used to notify the connection thread of this failure. However, with the changes introduced by Bug#45949, the handler thread will no longer try to lock the table if it was killed. This meant that the "dead" flag would not be set, and the connection thread would not notice that the handler thread had failed. This could happen with concurrent INSERT DELAYED and FLUSH TABLES. FLUSH TABLES would kill any active INSERT DELAYED that had opened any table(s) to be flushed. This could cause the INSERT DELAYED connection thread to be stuck waiting for the handler thread to lock its table, while the handler thread would be looping, trying to get the connection thread to notice the error. The root of the problem was that the handler thread had both the "dead" flag and "thd->killed" to indicate that it had been killed. Most places both were set, but some only set "thd->killed". And Delayed_insert::get_local_table() only checked "dead" while waiting for the table to be locked. This patch removes the "dead" variable and replaces its usage with "thd->killed", thereby resolving the issue. --- sql/sql_insert.cc | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b108ff75f44..9f8af87f1e2 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1761,7 +1761,7 @@ public: pthread_mutex_t mutex; pthread_cond_t cond,cond_client; volatile uint tables_in_use,stacked_inserts; - volatile bool status,dead; + volatile bool status; COPY_INFO info; I_List rows; ulong group_count; @@ -1769,8 +1769,7 @@ public: Delayed_insert() :locks_in_memory(0), - table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0), - group_count(0) + table(0),tables_in_use(0),stacked_inserts(0), status(0), group_count(0) { thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user; thd.security_ctx->host=(char*) my_localhost; @@ -1918,9 +1917,8 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) a given consumer (delayed insert thread), only at different stages of producer-consumer relationship. - 'dead' and 'status' variables in Delayed_insert are redundant - too, since there is already 'di->thd.killed' and - di->stacked_inserts. + The 'status' variable in Delayed_insert is redundant + too, since there is already di->stacked_inserts. */ static @@ -2073,17 +2071,29 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) { thd_proc_info(client_thd, "waiting for handler lock"); pthread_cond_signal(&cond); // Tell handler to lock table - while (!dead && !thd.lock && ! client_thd->killed) + while (!thd.killed && !thd.lock && ! client_thd->killed) { pthread_cond_wait(&cond_client,&mutex); } thd_proc_info(client_thd, "got handler lock"); if (client_thd->killed) goto error; - if (dead) + if (thd.killed) { - /* Don't copy over "Server shutdown in progress". */ - if (thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN) + /* + Copy the error message. Note that we don't treat fatal + errors in the delayed thread as fatal errors in the + main thread. If delayed thread was killed, we don't + want to send "Server shutdown in progress" in the + INSERT THREAD. + + The thread could be killed with an error message if + di->handle_inserts() or di->open_and_lock_table() fails. + The thread could be killed without an error message if + killed using mysql_notify_thread_having_shared_lock() or + kill_delayed_threads_for_table(). + */ + if (!thd.is_error() || thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN) my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0)); else my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0)); @@ -2454,7 +2464,8 @@ pthread_handler_t handle_delayed_insert(void *arg) break; // Time to die } - if (!di->status && !di->stacked_inserts) + /* Shouldn't wait if killed or an insert is waiting. */ + if (!thd->killed && !di->status && !di->stacked_inserts) { struct timespec abstime; set_timespec(abstime, delayed_insert_timeout); @@ -2465,7 +2476,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd_proc_info(&(di->thd), "Waiting for INSERT"); DBUG_PRINT("info",("Waiting for someone to insert rows")); - while (!thd->killed) + while (!thd->killed && !di->status) { int error; #if defined(HAVE_BROKEN_COND_TIMEDWAIT) @@ -2481,13 +2492,8 @@ pthread_handler_t handle_delayed_insert(void *arg) } #endif #endif - if (thd->killed || di->status) - break; if (error == ETIMEDOUT || error == ETIME) - { thd->killed= THD::KILL_CONNECTION; - break; - } } /* We can't lock di->mutex and mysys_var->mutex at the same time */ pthread_mutex_unlock(&di->mutex); @@ -2528,14 +2534,12 @@ pthread_handler_t handle_delayed_insert(void *arg) di->table= 0; if (di->open_and_lock_table()) { - di->dead= 1; thd->killed= THD::KILL_CONNECTION; } } else { /* Fatal error */ - di->dead= 1; thd->killed= THD::KILL_CONNECTION; } } @@ -2546,7 +2550,6 @@ pthread_handler_t handle_delayed_insert(void *arg) if (di->handle_inserts()) { /* Some fatal error */ - di->dead= 1; thd->killed= THD::KILL_CONNECTION; } } @@ -2598,7 +2601,6 @@ pthread_handler_t handle_delayed_insert(void *arg) close_thread_tables(thd); // Free the table di->table=0; - di->dead= 1; // If error thd->killed= THD::KILL_CONNECTION; // If error pthread_cond_broadcast(&di->cond_client); // Safety pthread_mutex_unlock(&di->mutex); From 555d46ea7f1c02edf32b2102a3ff3f8dff7aa147 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 17 Dec 2009 14:41:50 +0100 Subject: [PATCH 184/466] Replace ADD_EXECUTABLE with MYSQL_ADD_EXECUTABLE for binaries that are installed. MYSQL_ADD_EXECUTABLE will instructs CPack where to install the exe. On Windows, it also adds version resource and if -DSIGNCODE was given, will sign the exe in packaging step. --- CMakeLists.txt | 2 ++ client/CMakeLists.txt | 34 ++++++++------------------- cmake/libutils.cmake | 24 +++++++++++-------- cmake/mysql_version.cmake | 44 ++++------------------------------- cmake/plugin.cmake | 19 ++++++++------- cmake/versioninfo.rc.in | 2 +- extra/CMakeLists.txt | 19 +++++++-------- libmysql/CMakeLists.txt | 2 -- libmysqld/CMakeLists.txt | 2 -- sql/CMakeLists.txt | 6 +---- storage/myisam/CMakeLists.txt | 29 +++++++++++------------ 11 files changed, 66 insertions(+), 117 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51a50f682ba..81681dddb6e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 2.6) CMAKE_POLICY(VERSION 2.8) endif() +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) # First, decide about build type (debug or release) # If custom compiler flags are set or cmake is invoked with -DCMAKE_BUILD_TYPE, @@ -70,6 +71,7 @@ INCLUDE(cmake/libutils.cmake) INCLUDE(cmake/dtrace.cmake) INCLUDE(cmake/plugin.cmake) INCLUDE(cmake/install_macros.cmake) +INCLUDE(mysql_add_executable) # Handle options OPTION(DISABLE_SHARED diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 7d501f19b10..f4e223c9c72 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -27,40 +27,40 @@ INCLUDE_DIRECTORIES( ADD_DEFINITIONS(${READLINE_DEFINES}) ADD_DEFINITIONS(${SSL_DEFINES}) -ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc) +MYSQL_ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc) TARGET_LINK_LIBRARIES(mysql mysqlclient) IF(UNIX) TARGET_LINK_LIBRARIES(mysql ${READLINE_LIBRARY}) ENDIF(UNIX) -ADD_EXECUTABLE(mysqltest mysqltest.cc) +MYSQL_ADD_EXECUTABLE(mysqltest mysqltest.cc) SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") TARGET_LINK_LIBRARIES(mysqltest mysqlclient regex) -ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) +MYSQL_ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient) -ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c) +MYSQL_ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c) TARGET_LINK_LIBRARIES(mysqldump mysqlclient) -ADD_EXECUTABLE(mysqlimport mysqlimport.c) +MYSQL_ADD_EXECUTABLE(mysqlimport mysqlimport.c) TARGET_LINK_LIBRARIES(mysqlimport mysqlclient) -ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c) +MYSQL_ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c) TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient) ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs) -ADD_EXECUTABLE(mysqlshow mysqlshow.c) +MYSQL_ADD_EXECUTABLE(mysqlshow mysqlshow.c) TARGET_LINK_LIBRARIES(mysqlshow mysqlclient) -ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc) +MYSQL_ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc) TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient) -ADD_EXECUTABLE(mysqladmin mysqladmin.cc) +MYSQL_ADD_EXECUTABLE(mysqladmin mysqladmin.cc) TARGET_LINK_LIBRARIES(mysqladmin mysqlclient) -ADD_EXECUTABLE(mysqlslap mysqlslap.c) +MYSQL_ADD_EXECUTABLE(mysqlslap mysqlslap.c) SET_SOURCE_FILES_PROPERTIES(mysqlslap.c PROPERTIES COMPILE_FLAGS "-DTHREADS") TARGET_LINK_LIBRARIES(mysqlslap mysqlclient) @@ -69,17 +69,3 @@ ADD_EXECUTABLE(echo echo.c) SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap PROPERTIES HAS_CXX TRUE) -SET(INSTALL_EXES - mysqlcheck - mysqldump - mysqlimport - mysql_upgrade - mysqlshow - mysqlslap - mysql - mysqladmin - mysqlbinlog - mysqltest) - -MYSQL_INSTALL_TARGETS(${INSTALL_EXES} DESTINATION bin) - diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index 8f2b66f73c3..2d125c8a103 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -205,7 +205,7 @@ ENDMACRO() # Create libs from libs. # Merges static libraries, creates shared libraries out of convenience libraries. -# MYSQL_MERGE_LIBRARIES(target [STATIC|SHARED|MODULE] +# MERGE_LIBRARIES(target [STATIC|SHARED|MODULE] # [linklib1 .... linklibN] # [EXPORTS exported_func1 .... exportedFuncN] # [OUTPUT_NAME output_name] @@ -213,7 +213,7 @@ ENDMACRO() MACRO(MERGE_LIBRARIES) CMAKE_PARSE_ARGUMENTS(ARG "EXPORTS;OUTPUT_NAME" - "STATIC;SHARED;MODULE" + "STATIC;SHARED;MODULE;NOINSTALL" ${ARGN} ) LIST(GET ARG_DEFAULT_ARGS 0 TARGET) @@ -247,10 +247,11 @@ MACRO(MERGE_LIBRARIES) ENDIF() ENDFOREACH() ENDIF() - - CREATE_EXPORT_FILE(SRC ${TARGET} "${ARG_EXPORTS}") - ADD_LIBRARY(${TARGET} SHARED ${SRC}) + IF(WIN32 AND NOT ARG_NOINSTALL) + ADD_VERSION_INFO(${TARGET} VFT_DLL SRC ) + ENDIF() + ADD_LIBRARY(${TARGET} ${LIBTYPE} ${SRC}) TARGET_LINK_LIBRARIES(${TARGET} ${LIBS}) IF(ARG_OUTPUT_NAME) SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME "${ARG_OUTPUT_NAME}") @@ -258,6 +259,9 @@ MACRO(MERGE_LIBRARIES) ELSE() MESSAGE(FATAL_ERROR "Unknown library type") ENDIF() + IF(NOT ARG_NOINSTALL) + MYSQL_INSTALL_TARGETS(${TARGET} DESTINATION lib) + ENDIF() ENDMACRO() FUNCTION(GET_DEPENDEND_OS_LIBS target result) @@ -279,11 +283,11 @@ ENDFUNCTION() MACRO(RESTRICT_SYMBOL_EXPORTS target) IF(CMAKE_COMPILER_IS_GNUCXX AND UNIX) GET_TARGET_PROPERTY(COMPILE_FLAGS ${target} COMPILE_FLAGS) - IF(NOT COMPILE_FLAGS) - # Avoid COMPILE_FLAGS-NOTFOUND - SET(COMPILE_FLAGS) - ENDIF() + IF(NOT COMPILE_FLAGS) + # Avoid COMPILE_FLAGS-NOTFOUND + SET(COMPILE_FLAGS) + ENDIF() SET_TARGET_PROPERTIES(${target} PROPERTIES - COMPILE_FLAGS "${COMPILE_FLAGS} -fvisibility=hidden") + COMPILE_FLAGS "${COMPILE_FLAGS} -fvisibility=hidden") ENDIF() ENDMACRO() diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index b0a77b0bbc9..d8677e6c3ad 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -55,9 +55,10 @@ MACRO(GET_MYSQL_VERSION) SET(VERSION ${VERSION_STRING}) - STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+[^ ]+" "\\1" MAJOR_VERSION "${VERSION_STRING}") - STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+[^ ]+" "\\1" MINOR_VERSION "${VERSION_STRING}") - STRING(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)[^ ]+" "\\1" PATCH "${VERSION_STRING}") + STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" MAJOR_VERSION "${VERSION_STRING}") + STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" MINOR_VERSION "${VERSION_STRING}") + STRING(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" PATCH "${VERSION_STRING}") + SET(MYSQL_BASE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}" CACHE INTERNAL "MySQL Base version") SET(MYSQL_NO_DASH_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH}") MATH(EXPR MYSQL_VERSION_ID "10000*${MAJOR_VERSION} + 100*${MINOR_VERSION} + ${PATCH}") @@ -150,39 +151,4 @@ SET(CPACK_SOURCE_IGNORE_FILES SET(PRODUCTNAME "MySQL Server") SET(COMPANYNAME ${CPACK_PACKAGE_VENDOR}) -# Function to embed version info into executables/dlls on Windows -# Refer http://msdn.microsoft.com/en-us/library/aa381058(VS.85).aspx -# for more information -FUNCTION(WIN32_ADD_VERSION_INFO SOURCES) - IF(NOT WIN32) - RETURN() - ENDIF() - IF(NOT CMAKE_RC_COMPILER) - RETURN() - ENDIF() - - IF(NOT FILETYPE) - SET(FILETYPE VFT_APP) - ENDIF() - - IF(NOT MAJOR_VERSION) - MESSAGE(FATAL_ERROR "MAJOR_VERSION is not defined") - ENDIF() - IF(NOT MINOR_VERSION) - MESSAGE(FATAL_ERROR "MINOR_VERSION is not defined") - ENDIF() - IF(NOT PATCH) - SET(PATCH 0) - ENDIF() - - GET_TARGET_PROPERTY(target_location ${target} LOCATION) - GET_FILENAME_COMPONENT(INTERNALNAME ${target_location} NAME_WE) - IF(NOT FILEDESCRIPTION) - SET(FILEDESCRIPTION ${INTERNALNAME}) - ENDIF() - GET_FILENAME_COMPONENT(ORIGINALFILENAME ${target_location} NAME) - - CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/versioninfo.rc.in - ${CMAKE_CURRENT_BINARY_DIR}/${target}_versioninfo.rc) - LIST(APPEND ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/${target}_versioninfo.rc) -ENDFUNCTION() + diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 41fc3739979..b6a55b2ede0 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -120,7 +120,16 @@ MACRO(MYSQL_ADD_PLUGIN) FORCE) ENDIF() ELSEIF(NOT WITHOUT_${plugin} AND NOT ARG_STATIC_ONLY AND NOT WITHOUT_DYNAMIC_PLUGINS) - + IF(NOT ARG_MODULE_OUTPUT_NAME) + IF(ARG_STORAGE_ENGINE) + SET(ARG_MODULE_OUTPUT_NAME "ha_${target}") + ELSE() + SET(ARG_MODULE_OUTPUT_NAME "${target}") + ENDIF() + ENDIF() + IF(WIN32) + ADD_VERSION_INFO(${ARG_MODULE_OUTPUT_NAME} VFT_DLL SOURCES) + ENDIF() ADD_LIBRARY(${target} MODULE ${SOURCES}) DTRACE_INSTRUMENT(${target}) SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "" @@ -142,13 +151,7 @@ MACRO(MYSQL_ADD_PLUGIN) ENDIF() ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) - IF(NOT ARG_MODULE_OUTPUT_NAME) - IF(ARG_STORAGE_ENGINE) - SET(ARG_MODULE_OUTPUT_NAME "ha_${target}") - ELSE() - SET(ARG_MODULE_OUTPUT_NAME "${target}") - ENDIF() - ENDIF() + SET_TARGET_PROPERTIES(${target} PROPERTIES OUTPUT_NAME "${ARG_MODULE_OUTPUT_NAME}") # Install dynamic library diff --git a/cmake/versioninfo.rc.in b/cmake/versioninfo.rc.in index 263398836a0..9d581043a2c 100644 --- a/cmake/versioninfo.rc.in +++ b/cmake/versioninfo.rc.in @@ -13,7 +13,7 @@ BEGIN BLOCK "040904E4" BEGIN VALUE "FileDescription", "@FILEDESCRIPTION@\0" - VALUE "FileVersion", "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH@\0" + VALUE "FileVersion", "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH@.0\0" VALUE "InternalName", "@INTERNALNAME@\0" VALUE "OriginalFilename", "@ORIGINALFILENAME@\0" END diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index 90f28698564..479fb8cbd71 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -49,14 +49,14 @@ ADD_CUSTOM_TARGET(GenError ${PROJECT_BINARY_DIR}/sql/share/english/errmsg.sys ${PROJECT_SOURCE_DIR}/sql/share/errmsg-utf8.txt) -ADD_EXECUTABLE(my_print_defaults my_print_defaults.c) +MYSQL_ADD_EXECUTABLE(my_print_defaults my_print_defaults.c) TARGET_LINK_LIBRARIES(my_print_defaults mysys) -ADD_EXECUTABLE(perror perror.c) +MYSQL_ADD_EXECUTABLE(perror perror.c) ADD_DEPENDENCIES(perror GenError) TARGET_LINK_LIBRARIES(perror mysys) -ADD_EXECUTABLE(resolveip resolveip.c) +MYSQL_ADD_EXECUTABLE(resolveip resolveip.c) TARGET_LINK_LIBRARIES(resolveip mysys) IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") INCLUDE(CheckFunctionExists) @@ -69,15 +69,12 @@ ENDIF() ADD_EXECUTABLE(replace replace.c) TARGET_LINK_LIBRARIES(replace mysys) IF(UNIX) - ADD_EXECUTABLE(innochecksum innochecksum.c) - - ADD_EXECUTABLE(resolve_stack_dump resolve_stack_dump.c) + MYSQL_ADD_EXECUTABLE(innochecksum innochecksum.c) + + MYSQL_ADD_EXECUTABLE(resolve_stack_dump resolve_stack_dump.c) TARGET_LINK_LIBRARIES(resolve_stack_dump mysys) - - ADD_EXECUTABLE(mysql_waitpid mysql_waitpid.c) + + MYSQL_ADD_EXECUTABLE(mysql_waitpid mysql_waitpid.c) TARGET_LINK_LIBRARIES(mysql_waitpid mysys) - - MYSQL_INSTALL_TARGETS(innochecksum mysql_waitpid resolve_stack_dump DESTINATION bin) ENDIF() -MYSQL_INSTALL_TARGETS(perror resolveip my_print_defaults DESTINATION bin) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 6277fec2bda..f8b6a4ac9e2 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -152,14 +152,12 @@ SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. MERGE_LIBRARIES(mysqlclient STATIC ${LIBS}) -MYSQL_INSTALL_TARGETS(mysqlclient DESTINATION lib) IF(UNIX) INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient lib) ENDIF() IF(NOT DISABLE_SHARED) MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) - MYSQL_INSTALL_TARGETS(libmysql DESTINATION lib) IF(UNIX) # Name of shared library is mysqlclient on Unix SET_TARGET_PROPERTIES(libmysql PROPERTIES diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index b0131bddee0..211305bee76 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -120,9 +120,7 @@ ENDFOREACH() MERGE_LIBRARIES(mysqlserver STATIC ${EMBEDDED_LIBS} OUTPUT_NAME ${MYSQLSERVER_OUTPUT_NAME}) -MYSQL_INSTALL_TARGETS(mysqlserver DESTINATION lib) IF(MSVC AND NOT DISABLE_SHARED) MERGE_LIBRARIES(libmysqld SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS}) - MYSQL_INSTALL_TARGETS(libmysqld DESTINATION lib) ENDIF() diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 9438fd8479d..b6f33236675 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -95,12 +95,11 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS} IF(WIN32) SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h) - WIN32_ADD_VERSION_INFO(MYSQLD_SOURCE) ELSE() SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL}) ENDIF() -ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) +MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) IF(NOT WITHOUT_DYNAMIC_PLUGINS) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) @@ -127,7 +126,6 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS) ENDIF() SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) -SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) TARGET_LINK_LIBRARIES(mysqld sql) # Provide plugins with minimal set of libraries SET(INTERFACE_LIBS ${LIBRT}) @@ -140,8 +138,6 @@ ENDIF() # from static libraries DTRACE_INSTRUMENT_STATIC_LIBS(mysqld "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}") - -MYSQL_INSTALL_TARGETS(mysqld DESTINATION bin) # Handle out-of-source build from source package with possibly broken diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt index 9359eca0ccc..4b7007055d8 100755 --- a/storage/myisam/CMakeLists.txt +++ b/storage/myisam/CMakeLists.txt @@ -31,38 +31,37 @@ MYSQL_ADD_PLUGIN(myisam ${MYISAM_SOURCES} MANDATORY RECOMPILE_FOR_EMBEDDED) -ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c) -TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys) +TARGET_LINK_LIBRARIES(myisam mysys) -ADD_EXECUTABLE(myisamchk myisamchk.c) -TARGET_LINK_LIBRARIES(myisamchk myisam mysys) +MYSQL_ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c) +TARGET_LINK_LIBRARIES(myisam_ftdump myisam) -ADD_EXECUTABLE(myisamlog myisamlog.c) -TARGET_LINK_LIBRARIES(myisamlog myisam mysys) +MYSQL_ADD_EXECUTABLE(myisamchk myisamchk.c) +TARGET_LINK_LIBRARIES(myisamchk myisam) -ADD_EXECUTABLE(myisampack myisampack.c) +MYSQL_ADD_EXECUTABLE(myisamlog myisamlog.c) +TARGET_LINK_LIBRARIES(myisamlog myisam) -TARGET_LINK_LIBRARIES(myisampack myisam mysys) +MYSQL_ADD_EXECUTABLE(myisampack myisampack.c) +TARGET_LINK_LIBRARIES(myisampack myisam) IF(WITH_UNIT_TESTS AND FALSE) ADD_EXECUTABLE(mi_test1 mi_test1.c) - TARGET_LINK_LIBRARIES(mi_test1 myisam mysys) + TARGET_LINK_LIBRARIES(mi_test1 myisam) ADD_EXECUTABLE(mi_test2 mi_test2.c) - TARGET_LINK_LIBRARIES(mi_test2 myisam mysys) + TARGET_LINK_LIBRARIES(mi_test2 myisam) ADD_EXECUTABLE(mi_test3 mi_test3.c) - TARGET_LINK_LIBRARIES(mi_test3 myisam mysys) + TARGET_LINK_LIBRARIES(mi_test3 myisam) ADD_EXECUTABLE(sp_test sp_test.c) - TARGET_LINK_LIBRARIES(sp_test myisam mysys) + TARGET_LINK_LIBRARIES(sp_test myisam) ADD_EXECUTABLE(rt_test rt_test.c) - TARGET_LINK_LIBRARIES(rt_test myisam mysys) + TARGET_LINK_LIBRARIES(rt_test myisam) ENDIF() IF (MSVC) SET_TARGET_PROPERTIES(myisamchk myisampack PROPERTIES LINK_FLAGS "setargv.obj") ENDIF() - -MYSQL_INSTALL_TARGETS(myisamchk myisamlog myisampack myisam_ftdump DESTINATION bin) From f032795ccc86509e48b7782b0e40130869ce47b6 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 17 Dec 2009 16:40:02 +0100 Subject: [PATCH 185/466] Partial backport of: ------------------------------------------------------------ revno: 2617.14.26 committer: Vladislav Vaintroub branch nick: mysql-6.0-wtf timestamp: Wed 2008-11-05 11:19:19 +0100 message: CMakeLists.txt files cleanup. - remove SAFEMALLOC and SAFE_MUTEX definitions that were present in *each* CMakeLists.txt. Instead, put them into top level MakeLists.txt, but disable on Windows, because a) SAFEMALLOC does not add any functionality that is not already present in Debug C runtime ( and 2 safe malloc one on top of the other only unnecessarily slows down the server) b)SAFE_MUTEX does not work on Windows and have been explicitely disabled on Windows with #undef previously. Fortunately, ntdll does pretty good job identifying l problems with CRITICAL_SECTIONs. (DebugBreak()s on using uninited critical section, unlocking unowned critical section) -Remove occationally used -D_DEBUG (added by compiler anyway) -Remove MAP file generation, it became obsolete . There are many ways to get callstack of a crash now, with stacktrace in error log , minidump etc --- storage/innobase/CMakeLists.txt | 6 ------ zlib/CMakeLists.txt | 2 -- 2 files changed, 8 deletions(-) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index d67b518642c..d8213216383 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -16,12 +16,6 @@ # This is the CMakeLists for InnoDB Plugin -# TODO: remove the two FLAGS_DEBUG settings when merging into -# 6.0-based trees, like is already the case for other engines in -# those trees. -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - # Starting at 5.1.38, MySQL CMake files are simplified. But the plugin # CMakeLists.txt still needs to work with previous versions of MySQL. IF (MYSQL_VERSION_ID GREATER "50137") diff --git a/zlib/CMakeLists.txt b/zlib/CMakeLists.txt index 43235b631f6..20ba4bc51e0 100755 --- a/zlib/CMakeLists.txt +++ b/zlib/CMakeLists.txt @@ -20,8 +20,6 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib) -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") - SET(ZLIB_SOURCES adler32.c compress.c crc32.c crc32.h deflate.c deflate.h gzio.c infback.c inffast.c inffast.h inffixed.h inflate.c inflate.h inftrees.c inftrees.h trees.c trees.h uncompr.c zconf.h zlib.h zutil.c zutil.h) From 7363c50346bce0dbc5d34c9606df01012a1b20f5 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 18 Dec 2009 00:43:35 +0100 Subject: [PATCH 186/466] add new file --- cmake/mysql_add_executable.cmake | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 cmake/mysql_add_executable.cmake diff --git a/cmake/mysql_add_executable.cmake b/cmake/mysql_add_executable.cmake new file mode 100644 index 00000000000..6df7c8dda33 --- /dev/null +++ b/cmake/mysql_add_executable.cmake @@ -0,0 +1,89 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Add executable plus some additional MySQL specific stuff +# Usage (same as for standard CMake's ADD_EXECUTABLE) +# +# MYSQL_ADD_EXECUTABLE(target source1...sourceN) +# +# MySQL specifics: +# - instruct CPack to install executable under ${CMAKE_INSTALL_PREFIX}/bin directory +# On Windows : +# - add version resource +# - instruct CPack to do autenticode signing if SIGNCODE is set + +INCLUDE(cmake_parse_arguments) + +FUNCTION (MYSQL_ADD_EXECUTABLE) + # Pass-through arguments for ADD_EXECUTABLE + CMAKE_PARSE_ARGUMENTS(ARG + "WIN32;MACOSX_BUNDLE;EXCLUDE_FROM_ALL" + "" + ${ARGN} + ) + LIST(GET ARG_DEFAULT_ARGS 0 target) + LIST(REMOVE_AT ARG_DEFAULT_ARGS 0) + + SET(sources ${ARG_DEFAULT_ARGS}) + IF(WIN32) + ADD_VERSION_INFO(${target} VFT_APP sources) + ENDIF() + + ADD_EXECUTABLE(${target} ${ARG_WIN32} ${ARG_MACOSX_BUNDLE} ${ARG_EXCLUDE_FROM_ALL} ${sources}) + + # tell CPack where to install + IF(NOT ARG_EXCLUDE_FROM_ALL) + MYSQL_INSTALL_TARGETS(${target} DESTINATION bin) + ENDIF() +ENDFUNCTION() + +# Function to embed version info into executables/dlls on Windows +# Refer http://msdn.microsoft.com/en-us/library/aa381058(VS.85).aspx +# for more information +MACRO(ADD_VERSION_INFO target filetype sources) + SET(HAVE_RC_FILE) + FOREACH(src ${sources}) + IF("${src}" MATCHES "\\.rc$") + SET(HAVE_RC_FILE 1) + BREAK() + ENDIF() + ENDFOREACH() + IF(NOT HAVE_RC_FILE) + IF(NOT MAJOR_VERSION) + MESSAGE(FATAL_ERROR "MAJOR_VERSION is not defined") + ENDIF() + IF(NOT MINOR_VERSION) + MESSAGE(FATAL_ERROR "MINOR_VERSION is not defined") + ENDIF() + IF(NOT PATCH) + SET(PATCH 0) + ENDIF() + SET(FILETYPE ${filetype}) + SET(INTERNALNAME ${target}) + IF(FILETYPE STREQUAL VFT_APP) + SET(ORIGINALFILENAME ${target}.exe) + ELSE() + SET(ORIGINALFILENAME ${target}.dll) + ENDIF() + SET(FILEDESCRIPTION ${target}) + FIND_FILE(VERINFO_IN versioninfo.rc.in ${CMAKE_MODULE_PATH}) + IF(NOT VERINFO_IN) + MESSAGE(FATAL_ERROR "cannot find version info template") + ENDIF() + CONFIGURE_FILE(${VERINFO_IN} + ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc) + LIST(APPEND ${sources} ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc) + ENDIF() + ENDMACRO() \ No newline at end of file From b9d05f2b68c29fe906b67276076d3fced7b7b9d4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 18 Dec 2009 01:32:01 +0100 Subject: [PATCH 187/466] add missing file to EXTRA_DIST --- cmake/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 95b5c313dca..d072178d1b9 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -20,4 +20,5 @@ EXTRA_DIST = \ do_abi_check.cmake \ merge_archives_unix.cmake.in \ dtrace_prelink.cmake \ - versioninfo.rc.in + versioninfo.rc.in \ + mysql_add_executable.cmake From 54c5a4beefefb8047a3fb1f0259d38e5e8bb664e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 18 Dec 2009 23:53:30 +0100 Subject: [PATCH 188/466] Simplify embedding version info into executables with ADD_VERSION_INFO --- CMakeLists.txt | 18 ++++++------- cmake/install_macros.cmake | 10 +++---- cmake/libutils.cmake | 3 --- cmake/mysql_add_executable.cmake | 45 +------------------------------- cmake/mysql_version.cmake | 43 ++++++++++++++++++++++++++++++ cmake/plugin.cmake | 6 ++--- cmake/versioninfo.rc.in | 4 +-- sql/CMakeLists.txt | 5 ++-- 8 files changed, 64 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81681dddb6e..4c1cdb94da4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,15 +62,15 @@ ELSE() ENDIF() # Add macros -INCLUDE(cmake/character_sets.cmake) -INCLUDE(cmake/zlib.cmake) -INCLUDE(cmake/ssl.cmake) -INCLUDE(cmake/readline.cmake) -INCLUDE(cmake/mysql_version.cmake) -INCLUDE(cmake/libutils.cmake) -INCLUDE(cmake/dtrace.cmake) -INCLUDE(cmake/plugin.cmake) -INCLUDE(cmake/install_macros.cmake) +INCLUDE(character_sets) +INCLUDE(zlib) +INCLUDE(ssl) +INCLUDE(readline) +INCLUDE(mysql_version) +INCLUDE(libutils) +INCLUDE(dtrace) +INCLUDE(plugin) +INCLUDE(install_macros) INCLUDE(mysql_add_executable) # Handle options diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 7e11bdfb3ab..902b717c1d4 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -130,12 +130,12 @@ FUNCTION(MYSQL_INSTALL_TARGETS) ENDIF() # If signing is required, sign executables before installing - IF(SIGNCODE) - FOREACH(target ${TARGETS}) + FOREACH(target ${TARGETS}) + IF(SIGNCODE) SIGN_TARGET(${target}) - ENDFOREACH() - ENDIF() - + ENDIF() + ADD_VERSION_INFO(${target}) + ENDFOREACH() INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION}) SET(INSTALL_LOCATION ${ARG_DESTINATION} ) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index 2d125c8a103..5198cd3a898 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -248,9 +248,6 @@ MACRO(MERGE_LIBRARIES) ENDFOREACH() ENDIF() CREATE_EXPORT_FILE(SRC ${TARGET} "${ARG_EXPORTS}") - IF(WIN32 AND NOT ARG_NOINSTALL) - ADD_VERSION_INFO(${TARGET} VFT_DLL SRC ) - ENDIF() ADD_LIBRARY(${TARGET} ${LIBTYPE} ${SRC}) TARGET_LINK_LIBRARIES(${TARGET} ${LIBS}) IF(ARG_OUTPUT_NAME) diff --git a/cmake/mysql_add_executable.cmake b/cmake/mysql_add_executable.cmake index 6df7c8dda33..cb0237332c2 100644 --- a/cmake/mysql_add_executable.cmake +++ b/cmake/mysql_add_executable.cmake @@ -37,53 +37,10 @@ FUNCTION (MYSQL_ADD_EXECUTABLE) LIST(REMOVE_AT ARG_DEFAULT_ARGS 0) SET(sources ${ARG_DEFAULT_ARGS}) - IF(WIN32) - ADD_VERSION_INFO(${target} VFT_APP sources) - ENDIF() ADD_EXECUTABLE(${target} ${ARG_WIN32} ${ARG_MACOSX_BUNDLE} ${ARG_EXCLUDE_FROM_ALL} ${sources}) - # tell CPack where to install IF(NOT ARG_EXCLUDE_FROM_ALL) MYSQL_INSTALL_TARGETS(${target} DESTINATION bin) ENDIF() -ENDFUNCTION() - -# Function to embed version info into executables/dlls on Windows -# Refer http://msdn.microsoft.com/en-us/library/aa381058(VS.85).aspx -# for more information -MACRO(ADD_VERSION_INFO target filetype sources) - SET(HAVE_RC_FILE) - FOREACH(src ${sources}) - IF("${src}" MATCHES "\\.rc$") - SET(HAVE_RC_FILE 1) - BREAK() - ENDIF() - ENDFOREACH() - IF(NOT HAVE_RC_FILE) - IF(NOT MAJOR_VERSION) - MESSAGE(FATAL_ERROR "MAJOR_VERSION is not defined") - ENDIF() - IF(NOT MINOR_VERSION) - MESSAGE(FATAL_ERROR "MINOR_VERSION is not defined") - ENDIF() - IF(NOT PATCH) - SET(PATCH 0) - ENDIF() - SET(FILETYPE ${filetype}) - SET(INTERNALNAME ${target}) - IF(FILETYPE STREQUAL VFT_APP) - SET(ORIGINALFILENAME ${target}.exe) - ELSE() - SET(ORIGINALFILENAME ${target}.dll) - ENDIF() - SET(FILEDESCRIPTION ${target}) - FIND_FILE(VERINFO_IN versioninfo.rc.in ${CMAKE_MODULE_PATH}) - IF(NOT VERINFO_IN) - MESSAGE(FATAL_ERROR "cannot find version info template") - ENDIF() - CONFIGURE_FILE(${VERINFO_IN} - ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc) - LIST(APPEND ${sources} ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc) - ENDIF() - ENDMACRO() \ No newline at end of file +ENDFUNCTION() \ No newline at end of file diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index d8677e6c3ad..6b83cb50b2d 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -151,4 +151,47 @@ SET(CPACK_SOURCE_IGNORE_FILES SET(PRODUCTNAME "MySQL Server") SET(COMPANYNAME ${CPACK_PACKAGE_VENDOR}) +# Add version information to the exe and dll files +# Refer to http://msdn.microsoft.com/en-us/library/aa381058(VS.85).aspx +# for more info. +IF(MSVC) + GET_TARGET_PROPERTY(location gen_versioninfo LOCATION) + IF(NOT location) + GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) + SET(FILETYPE VFT_APP) + CONFIGURE_FILE(${MYSQL_CMAKE_SCRIPT_DIR}/versioninfo.rc.in + ${CMAKE_BINARY_DIR}/versioninfo_exe.rc) + SET(FILETYPE VFT_DLL) + CONFIGURE_FILE(${MYSQL_CMAKE_SCRIPT_DIR}/versioninfo.rc.in + ${CMAKE_BINARY_DIR}/versioninfo_dll.rc) + + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_BINARY_DIR}/versioninfo_exe.res + ${CMAKE_BINARY_DIR}/versioninfo_dll.res + COMMAND ${CMAKE_RC_COMPILER} versioninfo_exe.rc + COMMAND ${CMAKE_RC_COMPILER} versioninfo_dll.rc + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + ADD_CUSTOM_TARGET(gen_versioninfo + DEPENDS + ${CMAKE_BINARY_DIR}/versioninfo_exe.res + ${CMAKE_BINARY_DIR}/versioninfo_dll.res + ) + ENDIF() + + FUNCTION(ADD_VERSION_INFO target) + GET_TARGET_PROPERTY(target_type ${target} TYPE) + ADD_DEPENDENCIES(${target} gen_versioninfo) + IF(target_type MATCHES "SHARED" OR target_type MATCHES "MODULE") + SET_PROPERTY(TARGET ${target} APPEND PROPERTY LINK_FLAGS + "\"${CMAKE_BINARY_DIR}/versioninfo_dll.res\"") + ELSEIF(target_type MATCHES "EXE") + SET_PROPERTY(TARGET ${target} APPEND PROPERTY LINK_FLAGS + "${target_link_flags} \"${CMAKE_BINARY_DIR}/versioninfo_exe.res\"") + ENDIF() + ENDFUNCTION() +ELSE() + FUNCTION(ADD_VERSION_INFO) + ENDFUNCTION() +ENDIF() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index b6a55b2ede0..3e46f523973 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -127,10 +127,8 @@ MACRO(MYSQL_ADD_PLUGIN) SET(ARG_MODULE_OUTPUT_NAME "${target}") ENDIF() ENDIF() - IF(WIN32) - ADD_VERSION_INFO(${ARG_MODULE_OUTPUT_NAME} VFT_DLL SOURCES) - ENDIF() - ADD_LIBRARY(${target} MODULE ${SOURCES}) + + ADD_LIBRARY(${target} MODULE ${SOURCES}) DTRACE_INSTRUMENT(${target}) SET_TARGET_PROPERTIES (${target} PROPERTIES PREFIX "" COMPILE_DEFINITIONS "MYSQL_DYNAMIC_PLUGIN") diff --git a/cmake/versioninfo.rc.in b/cmake/versioninfo.rc.in index 9d581043a2c..97c45ec86c0 100644 --- a/cmake/versioninfo.rc.in +++ b/cmake/versioninfo.rc.in @@ -12,10 +12,8 @@ BEGIN BEGIN BLOCK "040904E4" BEGIN - VALUE "FileDescription", "@FILEDESCRIPTION@\0" VALUE "FileVersion", "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH@.0\0" - VALUE "InternalName", "@INTERNALNAME@\0" - VALUE "OriginalFilename", "@ORIGINALFILENAME@\0" + VALUE "ProductVersion", "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH@.0\0" END END BLOCK "VarFileInfo" diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index b6f33236675..43f22903aef 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -103,13 +103,14 @@ MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) IF(NOT WITHOUT_DYNAMIC_PLUGINS) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) + GET_TARGET_PROPERTY(mysqld_link_flags mysqld LINK_FLAGS) IF (MINGW OR CYGWIN) - SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "{mysqld_link_flags} -Wl,--export-all-symbols") ENDIF() IF(MSVC) # Set module definition file. Also use non-incremental linker, # incremental appears to crash from time to time,if used with /DEF option - SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "/DEF:mysqld.def /INCREMENTAL:NO") + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} /DEF:mysqld.def /INCREMENTAL:NO") FOREACH (CORELIB sql mysys dbug strings) GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION) From dd88cefe0b0528e641540bdbfc06113ac79bdcde Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 19 Dec 2009 03:39:02 +0000 Subject: [PATCH 189/466] Fix missing 64 bit Solaris atomic prototypes --- include/atomic/solaris.h | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/include/atomic/solaris.h b/include/atomic/solaris.h index 45efd9faaba..f8845a16bae 100644 --- a/include/atomic/solaris.h +++ b/include/atomic/solaris.h @@ -60,6 +60,18 @@ my_atomic_cas32(int32 volatile *a, int32 *cmp, int32 set) return ret; } +STATIC_INLINE int +my_atomic_cas64(int64 volatile *a, int64 *cmp, int64 set) +{ + int ret; + int64 sav; + sav = (int64) atomic_cas_64((volatile uint64_t *)a, (uint64_t)*cmp, + (uint64_t)set); + if (! (ret = (sav == *cmp))) + *cmp = sav; + return ret; +} + STATIC_INLINE int my_atomic_casptr(void * volatile *a, void **cmp, void *set) { @@ -97,6 +109,14 @@ my_atomic_add32(int32 volatile *a, int32 v) return (nv - v); } +STATIC_INLINE int64 +my_atomic_add64(int64 volatile *a, int64 v) +{ + int64 nv; + nv = atomic_add_64_nv((volatile uint64_t *)a, v); + return (nv - v); +} + /* ------------------------------------------------------------------------ */ #ifdef MY_ATOMIC_MODE_DUMMY @@ -110,6 +130,9 @@ my_atomic_load16(int16 volatile *a) { return (*a); } STATIC_INLINE int32 my_atomic_load32(int32 volatile *a) { return (*a); } +STATIC_INLINE int64 +my_atomic_load64(int64 volatile *a) { return (*a); } + STATIC_INLINE void * my_atomic_loadptr(void * volatile *a) { return (*a); } @@ -124,6 +147,9 @@ my_atomic_store16(int16 volatile *a, int16 v) { *a = v; } STATIC_INLINE void my_atomic_store32(int32 volatile *a, int32 v) { *a = v; } +STATIC_INLINE void +my_atomic_store64(int64 volatile *a, int64 v) { *a = v; } + STATIC_INLINE void my_atomic_storeptr(void * volatile *a, void *v) { *a = v; } @@ -149,6 +175,12 @@ my_atomic_load32(int32 volatile *a) return ((int32) atomic_or_32_nv((volatile uint32_t *)a, 0)); } +STATIC_INLINE int64 +my_atomic_load64(int64 volatile *a) +{ + return ((int64) atomic_or_64_nv((volatile uint64_t *)a, 0)); +} + STATIC_INLINE void * my_atomic_loadptr(void * volatile *a) { @@ -175,6 +207,12 @@ my_atomic_store32(int32 volatile *a, int32 v) (void) atomic_swap_32((volatile uint32_t *)a, (uint32_t)v); } +STATIC_INLINE void +my_atomic_store64(int64 volatile *a, int64 v) +{ + (void) atomic_swap_64((volatile uint64_t *)a, (uint64_t)v); +} + STATIC_INLINE void my_atomic_storeptr(void * volatile *a, void *v) { @@ -203,6 +241,12 @@ my_atomic_fas32(int32 volatile *a, int32 v) return ((int32) atomic_swap_32((volatile uint32_t *)a, (uint32_t)v)); } +STATIC_INLINE int64 +my_atomic_fas64(int64 volatile *a, int64 v) +{ + return ((int64) atomic_swap_64((volatile uint64_t *)a, (uint64_t)v)); +} + STATIC_INLINE void * my_atomic_fasptr(void * volatile *a, void *v) { From 5c7a62287eea3d738940535c5212352ddf6a12f9 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 19 Dec 2009 22:15:10 +0100 Subject: [PATCH 190/466] Fix build with LDFLAGS=--Wl,--as-needed on Linux --- cmake/readline.cmake | 14 +++++++++++++- cmd-line-utils/libedit/CMakeLists.txt | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmake/readline.cmake b/cmake/readline.cmake index 44be0c2196e..4f0cb4a30c7 100644 --- a/cmake/readline.cmake +++ b/cmake/readline.cmake @@ -79,7 +79,7 @@ MACRO (MYSQL_CHECK_MULTIBYTE) ENDMACRO() MACRO (FIND_CURSES) - INCLUDE (FindCurses) + FIND_PACKAGE(Curses) MARK_AS_ADVANCED(CURSES_CURSES_H_PATH CURSES_FORM_LIBRARY CURSES_HAVE_CURSES_H) IF(NOT CURSES_FOUND) SET(ERRORMSG "Curses library not found. Please install appropriate package, @@ -104,6 +104,18 @@ MACRO (FIND_CURSES) SET(CURSES_LIBRARY "curses" CACHE INTERNAL "" FORCE) SET(CURSES_CURSES_LIBRARY "curses" CACHE INTERNAL "" FORCE) ENDIF() + + IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + # -Wl,--as-needed breaks linking with -lcurses, e.g on Fedora + # Lower-level libcurses calls are exposed by libtinfo + CHECK_LIBRARY_EXISTS(${CURSES_LIBRARY} tputs "" HAVE_TPUTS_IN_CURSES) + IF(NOT HAVE_TPUTS_IN_CURSES) + CHECK_LIBRARY_EXISTS(tinfo tputs "" HAVE_TPUTS_IN_TINFO) + IF(HAVE_TPUTS_IN_TINFO) + SET(CURSES_LIBRARY tinfo) + ENDIF() + ENDIF() + ENDIF() ENDMACRO() MACRO (MYSQL_USE_BUNDLED_READLINE) diff --git a/cmd-line-utils/libedit/CMakeLists.txt b/cmd-line-utils/libedit/CMakeLists.txt index 763da8a4e04..517cbb4211f 100644 --- a/cmd-line-utils/libedit/CMakeLists.txt +++ b/cmd-line-utils/libedit/CMakeLists.txt @@ -18,7 +18,7 @@ INCLUDE(CheckIncludeFile) include(CheckFunctionExists) CHECK_INCLUDE_FILES(term.h HAVE_TERM_H) -SET(CMAKE_REQUIRED_LIBRARIES ${CURSES_CURSES_LIBRARY}) +SET(CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY}) CHECK_CXX_SOURCE_COMPILES(" #include int main() From 6b2aaa6a23d958be65acb91e5175b0d6e2b6ed56 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 20 Dec 2009 23:54:23 +0000 Subject: [PATCH 191/466] Fix inconsistently defined THR_LOCK_myisam_mmap It was pthread_mutex_t in mi_static.c and mysql_mutex_t in my_thr_init.c Solaris linker complains about different size of the symbol. --- include/myisam.h | 2 +- storage/myisam/mi_packrec.c | 16 ++++++++-------- storage/myisam/mi_static.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/myisam.h b/include/myisam.h index e9d1e4ab43e..e6404cb5405 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -260,7 +260,7 @@ extern ulong myisam_bulk_insert_tree_size, myisam_data_pointer_size; /* which is normally forbidden */ extern int (*myisam_test_invalid_symlink)(const char *filename); extern ulonglong myisam_mmap_size, myisam_mmap_used; -extern pthread_mutex_t THR_LOCK_myisam_mmap; +extern mysql_mutex_t THR_LOCK_myisam_mmap; /* Prototypes for myisam-functions */ diff --git a/storage/myisam/mi_packrec.c b/storage/myisam/mi_packrec.c index b3a6bf78566..580c58e6ea1 100644 --- a/storage/myisam/mi_packrec.c +++ b/storage/myisam/mi_packrec.c @@ -1502,11 +1502,11 @@ my_bool _mi_memmap_file(MI_INFO *info) if (myisam_mmap_size != SIZE_T_MAX) { - pthread_mutex_lock(&THR_LOCK_myisam_mmap); + mysql_mutex_lock(&THR_LOCK_myisam_mmap); eom= data_file_length > myisam_mmap_size - myisam_mmap_used - MEMMAP_EXTRA_MARGIN; if (!eom) myisam_mmap_used+= data_file_length + MEMMAP_EXTRA_MARGIN; - pthread_mutex_unlock(&THR_LOCK_myisam_mmap); + mysql_mutex_unlock(&THR_LOCK_myisam_mmap); } else eom= data_file_length > myisam_mmap_size - MEMMAP_EXTRA_MARGIN; @@ -1522,9 +1522,9 @@ my_bool _mi_memmap_file(MI_INFO *info) DBUG_PRINT("warning",("File isn't extended for memmap")); if (myisam_mmap_size != SIZE_T_MAX) { - pthread_mutex_lock(&THR_LOCK_myisam_mmap); + mysql_mutex_lock(&THR_LOCK_myisam_mmap); myisam_mmap_used-= data_file_length + MEMMAP_EXTRA_MARGIN; - pthread_mutex_unlock(&THR_LOCK_myisam_mmap); + mysql_mutex_unlock(&THR_LOCK_myisam_mmap); } DBUG_RETURN(0); } @@ -1534,9 +1534,9 @@ my_bool _mi_memmap_file(MI_INFO *info) { if (myisam_mmap_size != SIZE_T_MAX) { - pthread_mutex_lock(&THR_LOCK_myisam_mmap); + mysql_mutex_lock(&THR_LOCK_myisam_mmap); myisam_mmap_used-= data_file_length + MEMMAP_EXTRA_MARGIN; - pthread_mutex_unlock(&THR_LOCK_myisam_mmap); + mysql_mutex_unlock(&THR_LOCK_myisam_mmap); } DBUG_RETURN(0); } @@ -1555,9 +1555,9 @@ void _mi_unmap_file(MI_INFO *info) if (myisam_mmap_size != SIZE_T_MAX) { - pthread_mutex_lock(&THR_LOCK_myisam_mmap); + mysql_mutex_lock(&THR_LOCK_myisam_mmap); myisam_mmap_used-= info->s->mmaped_length + MEMMAP_EXTRA_MARGIN; - pthread_mutex_unlock(&THR_LOCK_myisam_mmap); + mysql_mutex_unlock(&THR_LOCK_myisam_mmap); } } diff --git a/storage/myisam/mi_static.c b/storage/myisam/mi_static.c index b763308a4a9..85a3124cba9 100644 --- a/storage/myisam/mi_static.c +++ b/storage/myisam/mi_static.c @@ -41,7 +41,7 @@ my_off_t myisam_max_temp_length= MAX_FILE_SIZE; ulong myisam_bulk_insert_tree_size=8192*1024; ulong myisam_data_pointer_size=4; ulonglong myisam_mmap_size= SIZE_T_MAX, myisam_mmap_used= 0; -pthread_mutex_t THR_LOCK_myisam_mmap; +mysql_mutex_t THR_LOCK_myisam_mmap; static int always_valid(const char *filename __attribute__((unused))) { From 39a1a50dfb52578224758ee1b240f1a89f95cc73 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 22 Dec 2009 19:09:15 +0300 Subject: [PATCH 192/466] A prerequisite patch for the fix for Bug#46224 "HANDLER statements within a transaction might lead to deadlocks". Introduce a notion of a sentinel to MDL_context. A sentinel is a ticket that separates all tickets in the context into two groups: before and after it. Currently we can have (and need) only one designated sentinel -- it separates all locks taken by LOCK TABLE or HANDLER statement, which must survive COMMIT and ROLLBACK and all other locks, which must be released at COMMIT or ROLLBACK. The tricky part is maintaining the sentinel up to date when someone release its corresponding ticket. This can happen, e.g. if someone issues DROP TABLE under LOCK TABLES (generally, see all calls to release_all_locks_for_name()). MDL_context::release_ticket() is modified to take care of it. ****** A fix and a test case for Bug#46224 "HANDLER statements within a transaction might lead to deadlocks". An attempt to mix HANDLER SQL statements, which are transaction- agnostic, an open multi-statement transaction, and DDL against the involved tables (in a concurrent connection) could lead to a deadlock. The deadlock would occur when HANDLER OPEN or HANDLER READ would have to wait on a conflicting metadata lock. If the connection that issued HANDLER statement also had other metadata locks (say, acquired in scope of a transaction), a classical deadlock situation of mutual wait could occur. Incompatible change: entering LOCK TABLES mode automatically closes all open HANDLERs in the current connection. Incompatible change: previously an attempt to wait on a lock in a connection that has an open HANDLER statement could wait indefinitely/deadlock. After this patch, an error ER_LOCK_DEADLOCK is produced. The idea of the fix is to merge thd->handler_mdl_context with the main mdl_context of the connection, used for transactional locks. This makes deadlock detection possible, since all waits with locks are "visible" and available to analysis in a single MDL context of the connection. Since HANDLER locks and transactional locks have a different life cycle -- HANDLERs are explicitly open and closed, and so are HANDLER locks, explicitly acquired and released, whereas transactional locks "accumulate" till the end of a transaction and are released only with COMMIT, ROLLBACK and ROLLBACK TO SAVEPOINT, a concept of "sentinel" was introduced to MDL_context. All locks, HANDLER and others, reside in the same linked list. However, a selected element of the list separates locks with different life cycle. HANDLER locks always reside at the end of the list, after the sentinel. Transactional locks are prepended to the beginning of the list, before the sentinel. Thus, ROLLBACK, COMMIT or ROLLBACK TO SAVEPOINT, only release those locks that reside before the sentinel. HANDLER locks must be released explicitly as part of HANDLER CLOSE statement, or an implicit close. The same approach with sentinel is also employed for LOCK TABLES locks. Since HANDLER and LOCK TABLES statement has never worked together, the implementation is made simple and only maintains one sentinel, which is used either for HANDLER locks, or for LOCK TABLES locks. --- mysql-test/include/handler.inc | 617 ++++++++++++++++++++++++++++- mysql-test/r/handler_innodb.result | 588 ++++++++++++++++++++++++++- mysql-test/r/handler_myisam.result | 587 ++++++++++++++++++++++++++- sql/lock.cc | 17 +- sql/log_event.cc | 3 +- sql/mdl.cc | 308 +++++++++----- sql/mdl.h | 46 ++- sql/mysql_priv.h | 1 + sql/rpl_injector.cc | 3 +- sql/rpl_rli.cc | 4 +- sql/set_var.cc | 3 +- sql/slave.cc | 3 +- sql/sql_acl.cc | 6 +- sql/sql_base.cc | 164 +++++--- sql/sql_class.cc | 17 +- sql/sql_class.h | 21 +- sql/sql_handler.cc | 207 ++++------ sql/sql_insert.cc | 3 +- sql/sql_parse.cc | 47 +-- sql/sql_plist.h | 40 +- sql/sql_prepare.cc | 13 +- sql/sql_servers.cc | 3 +- sql/sql_table.cc | 13 +- sql/transaction.cc | 12 +- 24 files changed, 2335 insertions(+), 391 deletions(-) diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 8ff38c7e7a1..a9965f97926 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -561,14 +561,29 @@ let $wait_condition= --source include/wait_condition.inc connection default; --echo connection: default +--echo # +--echo # RENAME placed two pending locks and waits. +--echo # When HANDLER t2 OPEN does open_tables(), it calls +--echo # mysql_ha_flush(), which in turn closes the open HANDLER for t1. +--echo # RENAME TABLE gets unblocked. If it gets scheduled quickly +--echo # and manages to complete before open_tables() +--echo # of HANDLER t2 OPEN, open_tables() and therefore the whole +--echo # HANDLER t2 OPEN succeeds. Otherwise open_tables() +--echo # notices a pending or active exclusive metadata lock on t2 +--echo # and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK +--echo # error. +--echo # +--error 0, ER_LOCK_DEADLOCK handler t2 open; -handler t2 read first; ---error ER_NO_SUCH_TABLE -handler t1 read next; -handler t1 close; +--error 0, ER_UNKNOWN_TABLE handler t2 close; +--echo connection: flush connection flush; reap; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--error ER_UNKNOWN_TABLE +handler t1 close; connection default; drop table t2; connection flush; @@ -748,3 +763,597 @@ USE information_schema; --error ER_WRONG_USAGE HANDLER COLUMNS OPEN; USE test; + +--echo # +--echo # Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL. +--echo # +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)) select * from t1; +create temporary table t3 (a int, key a (a)) select * from t2; +handler t1 open; +handler t2 open; +handler t3 open; +--echo # +--echo # LOCK TABLES implicitly closes all handlers. +--echo # +lock table t3 read; +--echo # +--echo # No HANDLER sql is available under lock tables anyway. +--echo # +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t1 open; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t1 read next; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t2 close; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t3 open; +--echo # After UNLOCK TABLES no handlers are around, they were +--echo # implicitly closed. +unlock tables; +drop temporary table t3; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--error ER_UNKNOWN_TABLE +handler t2 close; +--error ER_UNKNOWN_TABLE +handler t3 read next; +--echo # +--echo # Other operations also implicitly close handler: +--echo # +--echo # TRUNCATE +--echo # +handler t1 open; +truncate table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +handler t1 open; +--echo # +--echo # CREATE TRIGGER +--echo # +create trigger t1_ai after insert on t1 for each row set @a=1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # DROP TRIGGER +--echo # +handler t1 open; +drop trigger t1_ai; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # ALTER TABLE +--echo # +handler t1 open; +alter table t1 add column b int; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # ANALYZE TABLE +--echo # +handler t1 open; +analyze table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # OPTIMIZE TABLE +--echo # +handler t1 open; +optimize table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # REPAIR TABLE +--echo # +handler t1 open; +repair table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # DROP TABLE, naturally. +--echo # +handler t1 open; +drop table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +create table t1 (a int, b int, key a (a)) select a from t2; +--echo # +--echo # RENAME TABLE, naturally +--echo # +handler t1 open; +rename table t1 to t3; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # CREATE TABLE (even with IF NOT EXISTS clause, +--echo # and the table exists). +--echo # +handler t2 open; +create table if not exists t2 (a int); +--error ER_UNKNOWN_TABLE +handler t2 read next; +rename table t3 to t1; +drop table t2; +--echo # +--echo # FLUSH TABLE doesn't close the table but loses the position +--echo # +handler t1 open; +handler t1 read a prev; +flush table t1; +handler t1 read a prev; +handler t1 close; +--echo # +--echo # FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE. +--echo # +handler t1 open; +handler t1 read a prev; +flush tables with read lock; +handler t1 read a prev; +handler t1 close; +unlock tables; +--echo # +--echo # Explore the effect of HANDLER locks on concurrent DDL +--echo # +handler t1 open; +--echo # Establishing auxiliary connections con1, con2, con3 +connect(con1, localhost, root,,); +connect(con2, localhost, root,,); +connect(con3, localhost, root,,); +--echo # --> connection con1; +connection con1; +--echo # Sending: +--send drop table t1 +--echo # We can't use connection 'default' as wait_condition will +--echo # autoclose handlers. +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +handler t1 read a prev; +handler t1 read a prev; +handler t1 close; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # Explore the effect of HANDLER locks in parallel with SELECT +--echo # +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +handler t1 open; +handler t1 read a prev; +handler t1 read a prev; +handler t1 close; +--echo # --> connection con1; +connection con1; +--echo # Sending: +--send drop table t1 +--echo # --> connection con2 +connection con2; +--echo # Waiting for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +--echo # We can still use the table, it's part of the transaction +select * from t1; +--echo # Such are the circumstances that t1 is a part of transaction, +--echo # thus we can reopen it in the handler +handler t1 open; +--echo # We can commit the transaction, it doesn't close the handler +--echo # and doesn't let DROP to proceed. +commit; +handler t1 read a prev; +handler t1 read a prev; +handler t1 read a prev; +handler t1 close; +--echo # --> connection con1 +connection con1; +--echo # Now drop can proceed +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # Demonstrate that HANDLER locks and transaction locks +--echo # reside in the same context, and we don't back-off +--echo # when have transaction or handler locks. +--echo # +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)); +insert into t2 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +--echo # --> connection con1 +connection con1; +lock table t2 read; +--echo # --> connection con2 +connection con2; +--echo # Sending: +--send drop table t2 +--echo # --> connection con1 +connection con1; +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +--error ER_LOCK_DEADLOCK +handler t2 open; +--error ER_LOCK_DEADLOCK +select * from t2; +handler t1 open; +commit; +--error ER_LOCK_DEADLOCK +handler t2 open; +handler t1 close; +--echo # --> connection con1 +connection con1; +unlock tables; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # --> connection default +connection default; +handler t1 open; +handler t1 read a prev; +handler t1 close; +--echo # +--echo # Likewise, this doesn't require a multi-statement transaction. +--echo # ER_LOCK_DEADLOCK is also produced when we have an open +--echo # HANDLER and try to acquire locks for a single statement. +--echo # +create table t2 (a int, key a (a)); +handler t1 open; +--echo # --> connection con1 +connection con1; +lock tables t2 read; +--echo # --> connection con2 +connection con2; +--echo # Sending 'drop table t2'... +--send drop table t2 +--echo # --> connection con1 +connection con1; +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +--error ER_LOCK_DEADLOCK +select * from t2; +--echo # --> connection con1 +connection con1; +unlock tables; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # --> connection default +connection default; +handler t1 close; + +--echo # +--echo # ROLLBACK TO SAVEPOINT releases transactional locks, +--echo # but has no effect on open HANDLERs +--echo # +create table t2 like t1; +create table t3 like t1; +begin; +--echo # Have something before the savepoint +select * from t3; +savepoint sv; +handler t1 open; +handler t1 read a first; +handler t1 read a next; +select * from t2; +--echo # --> connection con1 +connection con1; +--echo # Sending: +--send drop table t1 +--echo # --> connection con2 +connection con2; +--echo # Sending: +--send drop table t2 +--echo # --> connection default +connection default; +--echo # Let DROP TABLE statements sync in. We must use +--echo # a separate connection for that, because otherwise SELECT +--echo # will auto-close the HANDLERs, becaues there are pending +--echo # exclusive locks against them. +--echo # --> connection con3 +connection con3; +--echo # Waiting for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # Demonstrate that t2 lock was released and t2 was dropped +--echo # after ROLLBACK TO SAVEPOINT +--echo # --> connection default +connection default; +rollback to savepoint sv; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +--echo # lock. +--echo # --> connection default +connection default; +handler t1 read a next; +handler t1 read a next; +--echo # Demonstrate that the drop will go through as soon as we close the +--echo # HANDLER +handler t1 close; +--echo # connection con1 +connection con1; +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +commit; +drop table t3; +--echo # +--echo # A few special cases when using SAVEPOINT/ROLLBACK TO +--echo # SAVEPOINT and HANDLER. +--echo # +--echo # Show that rollback to the savepoint taken in the beginning +--echo # of the transaction doesn't release mdl lock on +--echo # the HANDLER that was opened later. +--echo # +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +begin; +savepoint sv; +handler t1 open; +handler t1 read a first; +handler t1 read a next; +select * from t2; +--echo # --> connection con1 +connection con1; +--echo # Sending: +--send drop table t1 +--echo # --> connection con2 +connection con2; +--echo # Sending: +--send drop table t2 +--echo # --> connection default +connection default; +--echo # Let DROP TABLE statements sync in. We must use +--echo # a separate connection for that, because otherwise SELECT +--echo # will auto-close the HANDLERs, becaues there are pending +--echo # exclusive locks against them. +--echo # --> connection con3 +connection con3; +--echo # Waiting for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # Demonstrate that t2 lock was released and t2 was dropped +--echo # after ROLLBACK TO SAVEPOINT +--echo # --> connection default +connection default; +rollback to savepoint sv; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +--echo # lock. +--echo # --> connection default +connection default; +handler t1 read a next; +handler t1 read a next; +--echo # Demonstrate that the drop will go through as soon as we close the +--echo # HANDLER +handler t1 close; +--echo # connection con1 +connection con1; +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +commit; +--echo # +--echo # Show that rollback to the savepoint taken in the beginning +--echo # of the transaction works properly (no valgrind warnins, etc), +--echo # even though it's done after the HANDLER mdl lock that was there +--echo # at the beginning is released and added again. +--echo # +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +create table t3 like t1; +insert into t3 (a) select a from t1; +begin; +handler t1 open; +savepoint sv; +handler t1 read a first; +select * from t2; +handler t1 close; +handler t3 open; +handler t3 read a first; +rollback to savepoint sv; +--echo # --> connection con1 +connection con1; +drop table t1, t2; +--echo # Sending: +--send drop table t3 +--echo # Let DROP TABLE statement sync in. +--echo # --> connection con2 +connection con2; +--echo # Waiting for 'drop table t3' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t3'; +--source include/wait_condition.inc +--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +--echo # lock. +--echo # --> connection default +connection default; +handler t3 read a next; +--echo # Demonstrate that the drop will go through as soon as we close the +--echo # HANDLER +handler t3 close; +--echo # connection con1 +connection con1; +--echo # Reaping 'drop table t3'... +--reap +--echo # --> connection default +connection default; +commit; + +--echo # +--echo # If we have to wait on an exclusive locks while having +--echo # an open HANDLER, ER_LOCK_DEADLOCK is reported. +--echo # +create table t1 (a int, key a(a)); +create table t2 like t1; +handler t1 open; +--echo # --> connection con1 +connection con1; +lock table t2 read; +--echo # --> connection default +connection default; +--error ER_LOCK_DEADLOCK +drop table t2; +--error ER_LOCK_DEADLOCK +rename table t2 to t3; +--echo # Demonstrate that there is no deadlock with FLUSH TABLE, +--echo # even though it is waiting for the other table to go away +--echo # Sending: +--send flush table t2 +--echo # --> connection con2 +connection con2; +drop table t1; +--echo # --> connection con1 +connection con1; +unlock tables; +--echo # --> connection default +connection default; +--echo # Reaping 'flush table t2'... +--reap +drop table t2; + +--echo # +--echo # Bug #46224 HANDLER statements within a transaction might +--echo # lead to deadlocks +--echo # +create table t1 (a int, key a(a)); + +--echo # --> connection default +connection default; +begin; +select * from t1; +handler t1 open; + +--echo # --> connection con1 +connection con1; +lock tables t1 write; + +--echo # --> connection default +connection default; +--echo # Sending: +--send handler t1 read a next + +--echo # --> connection con1 +connection con1; +--echo # Waiting for 'handler t1 read a next' to get blocked... +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "handler t1 read a next"; +--source include/wait_condition.inc +--echo # Sending: +--send drop table t1 + +--echo # --> connection con2 +connection con2; +--echo # Waiting for 'drop table t1' to get blocked... +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "drop table t1"; +--source include/wait_condition.inc + +--echo # --> connection default +connection default; +--echo # Reaping 'handler t1 read a next'... +--error ER_LOCK_DEADLOCK +--reap +handler t1 close; +commit; + +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop table t1'... +--reap + +--echo # --> connection con1 +connection con1; +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # --> connection con2 +connection con2; +disconnect con2; +--source include/wait_until_disconnected.inc +--echo # --> connection con3 +connection con3; +disconnect con3; +--source include/wait_until_disconnected.inc +connection default; + +--echo # +--echo # A temporary table test. +--echo # Check that we don't loose positions of HANDLER opened +--echo # against a temporary table. +--echo # +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create temporary table t2 (a int, b int, key a (a)); +insert into t2 (a) select a from t1; +handler t1 open; +handler t1 read a next; +handler t2 open; +handler t2 read a next; +flush table t1; +handler t2 read a next; +--echo # Sic: the position is lost +handler t1 read a next; +select * from t1; +--echo # Sic: the position is not lost +handler t2 read a next; +--error ER_CANT_REOPEN_TABLE +select * from t2; +handler t2 read a next; +drop table t1; +drop temporary table t2; + +--echo # +--echo # A test for lock_table_names()/unlock_table_names() function. +--echo # It should work properly in presence of open HANDLER. +--echo # +create table t1 (a int, b int, key a (a)); +create table t2 like t1; +create table t3 like t1; +create table t4 like t1; +handler t1 open; +handler t2 open; +rename table t4 to t5, t3 to t4, t5 to t3; +handler t1 read first; +handler t2 read first; +drop table t1, t2, t3, t4; diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index 5990b19062b..df948f3d0b6 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -570,13 +570,25 @@ connection: flush rename table t1 to t2;; connection: waiter connection: default +# +# RENAME placed two pending locks and waits. +# When HANDLER t2 OPEN does open_tables(), it calls +# mysql_ha_flush(), which in turn closes the open HANDLER for t1. +# RENAME TABLE gets unblocked. If it gets scheduled quickly +# and manages to complete before open_tables() +# of HANDLER t2 OPEN, open_tables() and therefore the whole +# HANDLER t2 OPEN succeeds. Otherwise open_tables() +# notices a pending or active exclusive metadata lock on t2 +# and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK +# error. +# handler t2 open; -handler t2 read first; -c1 -handler t1 read next; -ERROR 42S02: Table 'test.t1' doesn't exist -handler t1 close; handler t2 close; +connection: flush +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 close; +ERROR 42S02: Unknown table 't1' in HANDLER drop table t2; drop table if exists t1; create temporary table t1 (a int, b char(1), key a(a), key b(a,b)); @@ -745,3 +757,569 @@ USE information_schema; HANDLER COLUMNS OPEN; ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema USE test; +# +# Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL. +# +drop table if exists t1, t2, t3; +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)) select * from t1; +create temporary table t3 (a int, key a (a)) select * from t2; +handler t1 open; +handler t2 open; +handler t3 open; +# +# LOCK TABLES implicitly closes all handlers. +# +lock table t3 read; +# +# No HANDLER sql is available under lock tables anyway. +# +handler t1 open; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +handler t1 read next; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +handler t2 close; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +handler t3 open; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# After UNLOCK TABLES no handlers are around, they were +# implicitly closed. +unlock tables; +drop temporary table t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t2 close; +ERROR 42S02: Unknown table 't2' in HANDLER +handler t3 read next; +ERROR 42S02: Unknown table 't3' in HANDLER +# +# Other operations also implicitly close handler: +# +# TRUNCATE +# +handler t1 open; +truncate table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 open; +# +# CREATE TRIGGER +# +create trigger t1_ai after insert on t1 for each row set @a=1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TRIGGER +# +handler t1 open; +drop trigger t1_ai; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ALTER TABLE +# +handler t1 open; +alter table t1 add column b int; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ANALYZE TABLE +# +handler t1 open; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# OPTIMIZE TABLE +# +handler t1 open; +optimize table t1; +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# REPAIR TABLE +# +handler t1 open; +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair note The storage engine for the table doesn't support repair +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TABLE, naturally. +# +handler t1 open; +drop table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +create table t1 (a int, b int, key a (a)) select a from t2; +# +# RENAME TABLE, naturally +# +handler t1 open; +rename table t1 to t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# CREATE TABLE (even with IF NOT EXISTS clause, +# and the table exists). +# +handler t2 open; +create table if not exists t2 (a int); +Warnings: +Note 1050 Table 't2' already exists +handler t2 read next; +ERROR 42S02: Unknown table 't2' in HANDLER +rename table t3 to t1; +drop table t2; +# +# FLUSH TABLE doesn't close the table but loses the position +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush table t1; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +# +# FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE. +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush tables with read lock; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +unlock tables; +# +# Explore the effect of HANDLER locks on concurrent DDL +# +handler t1 open; +# Establishing auxiliary connections con1, con2, con3 +# --> connection con1; +# Sending: +drop table t1 ; +# We can't use connection 'default' as wait_condition will +# autoclose handlers. +# --> connection con2 +# Waitng for 'drop table t1' to get blocked... +# --> connection default +handler t1 read a prev; +b a +NULL 5 +handler t1 read a prev; +b a +NULL 4 +handler t1 close; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection default +# +# Explore the effect of HANDLER locks in parallel with SELECT +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 close; +# --> connection con1; +# Sending: +drop table t1 ; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# We can still use the table, it's part of the transaction +select * from t1; +a +1 +2 +3 +4 +5 +# Such are the circumstances that t1 is a part of transaction, +# thus we can reopen it in the handler +handler t1 open; +# We can commit the transaction, it doesn't close the handler +# and doesn't let DROP to proceed. +commit; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 read a prev; +a +3 +handler t1 close; +# --> connection con1 +# Now drop can proceed +# Reaping 'drop table t1'... +# --> connection default +# +# Demonstrate that HANDLER locks and transaction locks +# reside in the same context, and we don't back-off +# when have transaction or handler locks. +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)); +insert into t2 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +# --> connection con1 +lock table t2 read; +# --> connection con2 +# Sending: +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 open; +commit; +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 close; +# +# Likewise, this doesn't require a multi-statement transaction. +# ER_LOCK_DEADLOCK is also produced when we have an open +# HANDLER and try to acquire locks for a single statement. +# +create table t2 (a int, key a (a)); +handler t1 open; +# --> connection con1 +lock tables t2 read; +# --> connection con2 +# Sending 'drop table t2'... +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 close; +# +# ROLLBACK TO SAVEPOINT releases transactional locks, +# but has no effect on open HANDLERs +# +create table t2 like t1; +create table t3 like t1; +begin; +# Have something before the savepoint +select * from t3; +a +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +handler t1 read a next; +a +3 +handler t1 read a next; +a +4 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +drop table t3; +# +# A few special cases when using SAVEPOINT/ROLLBACK TO +# SAVEPOINT and HANDLER. +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction doesn't release mdl lock on +# the HANDLER that was opened later. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +begin; +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +handler t1 read a next; +a +3 +handler t1 read a next; +a +4 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction works properly (no valgrind warnins, etc), +# even though it's done after the HANDLER mdl lock that was there +# at the beginning is released and added again. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +create table t3 like t1; +insert into t3 (a) select a from t1; +begin; +handler t1 open; +savepoint sv; +handler t1 read a first; +a +1 +select * from t2; +a +handler t1 close; +handler t3 open; +handler t3 read a first; +a +1 +rollback to savepoint sv; +# --> connection con1 +drop table t1, t2; +# Sending: +drop table t3; +# Let DROP TABLE statement sync in. +# --> connection con2 +# Waiting for 'drop table t3' to get blocked... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +handler t3 read a next; +a +2 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t3 close; +# connection con1 +# Reaping 'drop table t3'... +# --> connection default +commit; +# +# If we have to wait on an exclusive locks while having +# an open HANDLER, ER_LOCK_DEADLOCK is reported. +# +create table t1 (a int, key a(a)); +create table t2 like t1; +handler t1 open; +# --> connection con1 +lock table t2 read; +# --> connection default +drop table t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +rename table t2 to t3; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Demonstrate that there is no deadlock with FLUSH TABLE, +# even though it is waiting for the other table to go away +# Sending: +flush table t2; +# --> connection con2 +drop table t1; +# --> connection con1 +unlock tables; +# --> connection default +# Reaping 'flush table t2'... +drop table t2; +# +# Bug #46224 HANDLER statements within a transaction might +# lead to deadlocks +# +create table t1 (a int, key a(a)); +# --> connection default +begin; +select * from t1; +a +handler t1 open; +# --> connection con1 +lock tables t1 write; +# --> connection default +# Sending: +handler t1 read a next; +# --> connection con1 +# Waiting for 'handler t1 read a next' to get blocked... +# Sending: +drop table t1; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# Reaping 'handler t1 read a next'... +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +commit; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection con1 +# --> connection con2 +# --> connection con3 +# +# A temporary table test. +# Check that we don't loose positions of HANDLER opened +# against a temporary table. +# +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create temporary table t2 (a int, b int, key a (a)); +insert into t2 (a) select a from t1; +handler t1 open; +handler t1 read a next; +a b +1 NULL +handler t2 open; +handler t2 read a next; +a b +1 NULL +flush table t1; +handler t2 read a next; +a b +2 NULL +# Sic: the position is lost +handler t1 read a next; +a b +1 NULL +select * from t1; +a b +1 NULL +2 NULL +3 NULL +4 NULL +5 NULL +# Sic: the position is not lost +handler t2 read a next; +a b +3 NULL +select * from t2; +ERROR HY000: Can't reopen table: 't2' +handler t2 read a next; +a b +4 NULL +drop table t1; +drop temporary table t2; +# +# A test for lock_table_names()/unlock_table_names() function. +# It should work properly in presence of open HANDLER. +# +create table t1 (a int, b int, key a (a)); +create table t2 like t1; +create table t3 like t1; +create table t4 like t1; +handler t1 open; +handler t2 open; +rename table t4 to t5, t3 to t4, t5 to t3; +handler t1 read first; +a b +handler t2 read first; +a b +drop table t1, t2, t3, t4; diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index f7b0ff2e04e..4b287e6560b 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -569,13 +569,25 @@ connection: flush rename table t1 to t2;; connection: waiter connection: default +# +# RENAME placed two pending locks and waits. +# When HANDLER t2 OPEN does open_tables(), it calls +# mysql_ha_flush(), which in turn closes the open HANDLER for t1. +# RENAME TABLE gets unblocked. If it gets scheduled quickly +# and manages to complete before open_tables() +# of HANDLER t2 OPEN, open_tables() and therefore the whole +# HANDLER t2 OPEN succeeds. Otherwise open_tables() +# notices a pending or active exclusive metadata lock on t2 +# and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK +# error. +# handler t2 open; -handler t2 read first; -c1 -handler t1 read next; -ERROR 42S02: Table 'test.t1' doesn't exist -handler t1 close; handler t2 close; +connection: flush +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 close; +ERROR 42S02: Unknown table 't1' in HANDLER drop table t2; drop table if exists t1; create temporary table t1 (a int, b char(1), key a(a), key b(a,b)); @@ -744,6 +756,571 @@ HANDLER COLUMNS OPEN; ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema USE test; # +# Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL. +# +drop table if exists t1, t2, t3; +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)) select * from t1; +create temporary table t3 (a int, key a (a)) select * from t2; +handler t1 open; +handler t2 open; +handler t3 open; +# +# LOCK TABLES implicitly closes all handlers. +# +lock table t3 read; +# +# No HANDLER sql is available under lock tables anyway. +# +handler t1 open; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +handler t1 read next; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +handler t2 close; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +handler t3 open; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# After UNLOCK TABLES no handlers are around, they were +# implicitly closed. +unlock tables; +drop temporary table t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t2 close; +ERROR 42S02: Unknown table 't2' in HANDLER +handler t3 read next; +ERROR 42S02: Unknown table 't3' in HANDLER +# +# Other operations also implicitly close handler: +# +# TRUNCATE +# +handler t1 open; +truncate table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 open; +# +# CREATE TRIGGER +# +create trigger t1_ai after insert on t1 for each row set @a=1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TRIGGER +# +handler t1 open; +drop trigger t1_ai; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ALTER TABLE +# +handler t1 open; +alter table t1 add column b int; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ANALYZE TABLE +# +handler t1 open; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Table is already up to date +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# OPTIMIZE TABLE +# +handler t1 open; +optimize table t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# REPAIR TABLE +# +handler t1 open; +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TABLE, naturally. +# +handler t1 open; +drop table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +create table t1 (a int, b int, key a (a)) select a from t2; +# +# RENAME TABLE, naturally +# +handler t1 open; +rename table t1 to t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# CREATE TABLE (even with IF NOT EXISTS clause, +# and the table exists). +# +handler t2 open; +create table if not exists t2 (a int); +Warnings: +Note 1050 Table 't2' already exists +handler t2 read next; +ERROR 42S02: Unknown table 't2' in HANDLER +rename table t3 to t1; +drop table t2; +# +# FLUSH TABLE doesn't close the table but loses the position +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush table t1; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +# +# FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE. +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush tables with read lock; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +unlock tables; +# +# Explore the effect of HANDLER locks on concurrent DDL +# +handler t1 open; +# Establishing auxiliary connections con1, con2, con3 +# --> connection con1; +# Sending: +drop table t1 ; +# We can't use connection 'default' as wait_condition will +# autoclose handlers. +# --> connection con2 +# Waitng for 'drop table t1' to get blocked... +# --> connection default +handler t1 read a prev; +b a +NULL 5 +handler t1 read a prev; +b a +NULL 4 +handler t1 close; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection default +# +# Explore the effect of HANDLER locks in parallel with SELECT +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 close; +# --> connection con1; +# Sending: +drop table t1 ; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# We can still use the table, it's part of the transaction +select * from t1; +a +1 +2 +3 +4 +5 +# Such are the circumstances that t1 is a part of transaction, +# thus we can reopen it in the handler +handler t1 open; +# We can commit the transaction, it doesn't close the handler +# and doesn't let DROP to proceed. +commit; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 read a prev; +a +3 +handler t1 close; +# --> connection con1 +# Now drop can proceed +# Reaping 'drop table t1'... +# --> connection default +# +# Demonstrate that HANDLER locks and transaction locks +# reside in the same context, and we don't back-off +# when have transaction or handler locks. +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)); +insert into t2 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +# --> connection con1 +lock table t2 read; +# --> connection con2 +# Sending: +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 open; +commit; +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 close; +# +# Likewise, this doesn't require a multi-statement transaction. +# ER_LOCK_DEADLOCK is also produced when we have an open +# HANDLER and try to acquire locks for a single statement. +# +create table t2 (a int, key a (a)); +handler t1 open; +# --> connection con1 +lock tables t2 read; +# --> connection con2 +# Sending 'drop table t2'... +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 close; +# +# ROLLBACK TO SAVEPOINT releases transactional locks, +# but has no effect on open HANDLERs +# +create table t2 like t1; +create table t3 like t1; +begin; +# Have something before the savepoint +select * from t3; +a +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +handler t1 read a next; +a +3 +handler t1 read a next; +a +4 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +drop table t3; +# +# A few special cases when using SAVEPOINT/ROLLBACK TO +# SAVEPOINT and HANDLER. +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction doesn't release mdl lock on +# the HANDLER that was opened later. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +begin; +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +handler t1 read a next; +a +3 +handler t1 read a next; +a +4 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction works properly (no valgrind warnins, etc), +# even though it's done after the HANDLER mdl lock that was there +# at the beginning is released and added again. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +create table t3 like t1; +insert into t3 (a) select a from t1; +begin; +handler t1 open; +savepoint sv; +handler t1 read a first; +a +1 +select * from t2; +a +handler t1 close; +handler t3 open; +handler t3 read a first; +a +1 +rollback to savepoint sv; +# --> connection con1 +drop table t1, t2; +# Sending: +drop table t3; +# Let DROP TABLE statement sync in. +# --> connection con2 +# Waiting for 'drop table t3' to get blocked... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +handler t3 read a next; +a +2 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t3 close; +# connection con1 +# Reaping 'drop table t3'... +# --> connection default +commit; +# +# If we have to wait on an exclusive locks while having +# an open HANDLER, ER_LOCK_DEADLOCK is reported. +# +create table t1 (a int, key a(a)); +create table t2 like t1; +handler t1 open; +# --> connection con1 +lock table t2 read; +# --> connection default +drop table t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +rename table t2 to t3; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Demonstrate that there is no deadlock with FLUSH TABLE, +# even though it is waiting for the other table to go away +# Sending: +flush table t2; +# --> connection con2 +drop table t1; +# --> connection con1 +unlock tables; +# --> connection default +# Reaping 'flush table t2'... +drop table t2; +# +# Bug #46224 HANDLER statements within a transaction might +# lead to deadlocks +# +create table t1 (a int, key a(a)); +# --> connection default +begin; +select * from t1; +a +handler t1 open; +# --> connection con1 +lock tables t1 write; +# --> connection default +# Sending: +handler t1 read a next; +# --> connection con1 +# Waiting for 'handler t1 read a next' to get blocked... +# Sending: +drop table t1; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# Reaping 'handler t1 read a next'... +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +commit; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection con1 +# --> connection con2 +# --> connection con3 +# +# A temporary table test. +# Check that we don't loose positions of HANDLER opened +# against a temporary table. +# +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create temporary table t2 (a int, b int, key a (a)); +insert into t2 (a) select a from t1; +handler t1 open; +handler t1 read a next; +a b +1 NULL +handler t2 open; +handler t2 read a next; +a b +1 NULL +flush table t1; +handler t2 read a next; +a b +2 NULL +# Sic: the position is lost +handler t1 read a next; +a b +1 NULL +select * from t1; +a b +1 NULL +2 NULL +3 NULL +4 NULL +5 NULL +# Sic: the position is not lost +handler t2 read a next; +a b +3 NULL +select * from t2; +ERROR HY000: Can't reopen table: 't2' +handler t2 read a next; +a b +4 NULL +drop table t1; +drop temporary table t2; +# +# A test for lock_table_names()/unlock_table_names() function. +# It should work properly in presence of open HANDLER. +# +create table t1 (a int, b int, key a (a)); +create table t2 like t1; +create table t3 like t1; +create table t4 like t1; +handler t1 open; +handler t2 open; +rename table t4 to t5, t3 to t4, t5 to t3; +handler t1 read first; +a b +handler t2 read first; +a b +drop table t1, t2, t3, t4; +# # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash # CREATE TABLE t1 AS SELECT 1 AS f1; diff --git a/sql/lock.cc b/sql/lock.cc index d414d7d6ae2..31773585bff 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -335,23 +335,12 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, preserved. */ reset_lock_data(sql_lock); - thd->some_tables_deleted=1; // Try again sql_lock->lock_count= 0; // Locks are already freed // Fall through: unlock, reset lock data, free and retry } - else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH)) + else { - /* - Success and nobody set thd->some_tables_deleted to force reopen - or we were called with MYSQL_LOCK_IGNORE_FLUSH so such attempts - should be ignored. - */ - break; - } - else if (!thd->open_tables) - { - // Only using temporary tables, no need to unlock - thd->some_tables_deleted=0; + /* Success */ break; } thd_proc_info(thd, 0); @@ -986,7 +975,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) void unlock_table_names(THD *thd) { DBUG_ENTER("unlock_table_names"); - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); DBUG_VOID_RETURN; } diff --git a/sql/log_event.cc b/sql/log_event.cc index 46d016b2c15..8afd243da63 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5321,8 +5321,7 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli) if (!(res= trans_commit(thd))) { close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); } return res; } diff --git a/sql/mdl.cc b/sql/mdl.cc index 6187d4515a3..40074879e21 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -187,6 +187,7 @@ void MDL_context::init(THD *thd_arg) { m_has_global_shared_lock= FALSE; m_thd= thd_arg; + m_lt_or_ha_sentinel= NULL; /* FIXME: In reset_n_backup_open_tables_state, we abuse "init" as a reset, i.e. call it on an already @@ -217,76 +218,6 @@ void MDL_context::destroy() } -/** - Backup and reset state of meta-data locking context. - - mdl_context_backup_and_reset(), mdl_context_restore() and - mdl_context_merge() are used by HANDLER implementation which - needs to open table for new HANDLER independently of already - open HANDLERs and add this table/metadata lock to the set of - tables open/metadata locks for HANDLERs afterwards. -*/ - -void MDL_context::backup_and_reset(MDL_context *backup) -{ - DBUG_ASSERT(backup->m_tickets.is_empty()); - - m_tickets.swap(backup->m_tickets); - - backup->m_has_global_shared_lock= m_has_global_shared_lock; - /* - When the main context is swapped out, one can not take - the global shared lock, and one can not rely on it: - the functionality in this mode is reduced, since it exists as - a temporary hack to support ad-hoc opening of system tables. - */ - m_has_global_shared_lock= FALSE; -} - - -/** - Restore state of meta-data locking context from backup. -*/ - -void MDL_context::restore_from_backup(MDL_context *backup) -{ - DBUG_ASSERT(m_tickets.is_empty()); - DBUG_ASSERT(m_has_global_shared_lock == FALSE); - - m_tickets.swap(backup->m_tickets); - m_has_global_shared_lock= backup->m_has_global_shared_lock; -} - - -/** - Merge meta-data locks from one context into another. -*/ - -void MDL_context::merge(MDL_context *src) -{ - MDL_ticket *ticket; - - DBUG_ASSERT(m_thd == src->m_thd); - - if (!src->m_tickets.is_empty()) - { - Ticket_iterator it(src->m_tickets); - while ((ticket= it++)) - { - DBUG_ASSERT(ticket->m_ctx); - ticket->m_ctx= this; - m_tickets.push_front(ticket); - } - src->m_tickets.empty(); - } - /* - MDL_context::merge() is a hack used in one place only: to open - an SQL handler. We never acquire the global shared lock there. - */ - DBUG_ASSERT(! src->m_has_global_shared_lock); -} - - /** Initialize a lock request. @@ -606,7 +537,7 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar if (waiting.is_empty() || type_arg == MDL_SHARED_HIGH_PRIO) can_grant= TRUE; } - else if (granted.head()->get_ctx() == requestor_ctx) + else if (granted.front()->get_ctx() == requestor_ctx) { /* When exclusive lock comes from the same context we can satisfy our @@ -659,20 +590,31 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar /** Check whether the context already holds a compatible lock ticket on an object. + Start searching the transactional locks. If not + found in the list of transactional locks, look at LOCK TABLES + and HANDLER locks. @param mdl_request Lock request object for lock to be acquired + @param[out] is_lt_or_ha Did we pass beyond m_lt_or_ha_sentinel while + searching for ticket? @return A pointer to the lock ticket for the object or NULL otherwise. */ MDL_ticket * -MDL_context::find_ticket(MDL_request *mdl_request) +MDL_context::find_ticket(MDL_request *mdl_request, + bool *is_lt_or_ha) { MDL_ticket *ticket; Ticket_iterator it(m_tickets); + *is_lt_or_ha= FALSE; + while ((ticket= it++)) { + if (ticket == m_lt_or_ha_sentinel) + *is_lt_or_ha= TRUE; + if (mdl_request->type == ticket->m_type && mdl_request->key.is_equal(&ticket->m_lock->key)) break; @@ -709,6 +651,7 @@ MDL_context::try_acquire_shared_lock(MDL_request *mdl_request) MDL_lock *lock; MDL_key *key= &mdl_request->key; MDL_ticket *ticket; + bool is_lt_or_ha; DBUG_ASSERT(mdl_request->is_shared() && mdl_request->ticket == NULL); @@ -727,12 +670,35 @@ MDL_context::try_acquire_shared_lock(MDL_request *mdl_request) Check whether the context already holds a shared lock on the object, and if so, grant the request. */ - if ((ticket= find_ticket(mdl_request))) + if ((ticket= find_ticket(mdl_request, &is_lt_or_ha))) { DBUG_ASSERT(ticket->m_state == MDL_ACQUIRED); /* Only shared locks can be recursive. */ DBUG_ASSERT(ticket->is_shared()); + /* + If the request is for a transactional lock, and we found + a transactional lock, just reuse the found ticket. + + It's possible that we found a transactional lock, + but the request is for a HANDLER lock. In that case HANDLER + code will clone the ticket (see below why it's needed). + + If the request is for a transactional lock, and we found + a HANDLER lock, create a copy, to make sure that when user + does HANDLER CLOSE, the transactional lock is not released. + + If the request is for a handler lock, and we found a + HANDLER lock, also do the clone. HANDLER CLOSE for one alias + should not release the lock on the table HANDLER opened through + a different alias. + */ mdl_request->ticket= ticket; + if (is_lt_or_ha && clone_ticket(mdl_request)) + { + /* Clone failed. */ + mdl_request->ticket= NULL; + return TRUE; + } return FALSE; } @@ -786,6 +752,46 @@ MDL_context::try_acquire_shared_lock(MDL_request *mdl_request) } +/** + Create a copy of a granted ticket. + This is used to make sure that HANDLER ticket + is never shared with a ticket that belongs to + a transaction, so that when we HANDLER CLOSE, + we don't release a transactional ticket, and + vice versa -- when we COMMIT, we don't mistakenly + release a ticket for an open HANDLER. + + @retval TRUE Out of memory. + @retval FALSE Success. +*/ + +bool +MDL_context::clone_ticket(MDL_request *mdl_request) +{ + MDL_ticket *ticket; + + safe_mutex_assert_not_owner(&LOCK_open); + /* Only used for HANDLER. */ + DBUG_ASSERT(mdl_request->ticket && mdl_request->ticket->is_shared()); + + if (!(ticket= MDL_ticket::create(this, mdl_request->type))) + return TRUE; + + ticket->m_state= MDL_ACQUIRED; + ticket->m_lock= mdl_request->ticket->m_lock; + mdl_request->ticket= ticket; + + pthread_mutex_lock(&LOCK_mdl); + ticket->m_lock->granted.push_front(ticket); + if (mdl_request->type == MDL_SHARED_UPGRADABLE) + global_lock.active_intention_exclusive++; + pthread_mutex_unlock(&LOCK_mdl); + + m_tickets.push_front(ticket); + + return FALSE; +} + /** Notify a thread holding a shared metadata lock which conflicts with a pending exclusive lock. @@ -850,7 +856,9 @@ bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests) safe_mutex_assert_not_owner(&LOCK_open); /* Exclusive locks must always be acquired first, all at once. */ - DBUG_ASSERT(! has_locks()); + DBUG_ASSERT(! has_locks() || + (m_lt_or_ha_sentinel && + m_tickets.front() == m_lt_or_ha_sentinel)); if (m_has_global_shared_lock) { @@ -924,6 +932,17 @@ bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests) if (!mdl_request) break; + if (m_lt_or_ha_sentinel) + { + /* + We're about to start waiting. Don't do it if we have + HANDLER locks (we can't have any other locks here). + Waiting with locks may lead to a deadlock. + */ + my_error(ER_LOCK_DEADLOCK, MYF(0)); + goto err; + } + /* There is a shared or exclusive lock on the object. */ DEBUG_SYNC(m_thd, "mdl_acquire_exclusive_locks_wait"); @@ -1041,6 +1060,30 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() if (m_lock->can_grant_lock(m_ctx, MDL_EXCLUSIVE, TRUE)) break; + /* + If m_ctx->lt_or_ha_sentinel(), and this sentinel is for HANDLER, + we can deadlock. However, HANDLER is not allowed under + LOCK TABLES, and apart from LOCK TABLES there are only + two cases of lock upgrade: ALTER TABLE and CREATE/DROP + TRIGGER (*). This leaves us with the following scenario + for deadlock: + + connection 1 connection 2 + handler t1 open; handler t2 open; + alter table t2 ... alter table t1 ... + + This scenario is quite remote, since ALTER + (and CREATE/DROP TRIGGER) performs mysql_ha_flush() in + the beginning, and thus closes open HANDLERS against which + there is a pending lock upgrade. Still, two ALTER statements + can interleave and not notice each other's pending lock + (e.g. if both upgrade their locks at the same time). + This, however, is quite unlikely, so we do nothing to + address it. + + (*) There is no requirement to upgrade lock in + CREATE/DROP TRIGGER, it's used there just for convenience. + */ bool signalled= FALSE; MDL_ticket *conflicting_ticket; MDL_lock::Ticket_iterator it(m_lock->granted); @@ -1280,6 +1323,9 @@ void MDL_context::release_ticket(MDL_ticket *ticket) safe_mutex_assert_owner(&LOCK_mdl); + if (ticket == m_lt_or_ha_sentinel) + m_lt_or_ha_sentinel= ++Ticket_list::Iterator(m_tickets, ticket); + m_tickets.remove(ticket); switch (ticket->m_type) @@ -1317,18 +1363,27 @@ void MDL_context::release_ticket(MDL_ticket *ticket) /** - Release all locks associated with the context. + Release all locks associated with the context. If the sentinel + is not NULL, do not release locks stored in the list after and + including the sentinel. - This function is used to back off in case of a lock conflict. - It is also used to release shared locks in the end of an SQL - statement. + Transactional locks are added to the beginning of the list, i.e. + stored in reverse temporal order. This allows to employ this + function to: + - back off in case of a lock conflict. + - release all locks in the end of a transaction + - rollback to a savepoint. + + The sentinel semantics is used to support LOCK TABLES + mode and HANDLER statements: locks taken by these statements + survive COMMIT, ROLLBACK, ROLLBACK TO SAVEPOINT. */ -void MDL_context::release_all_locks() +void MDL_context::release_locks_stored_before(MDL_ticket *sentinel) { MDL_ticket *ticket; Ticket_iterator it(m_tickets); - DBUG_ENTER("MDL_context::release_all_locks"); + DBUG_ENTER("MDL_context::release_locks_stored_before"); safe_mutex_assert_not_owner(&LOCK_open); @@ -1336,7 +1391,7 @@ void MDL_context::release_all_locks() DBUG_VOID_RETURN; pthread_mutex_lock(&LOCK_mdl); - while ((ticket= it++)) + while ((ticket= it++) && ticket != sentinel) { DBUG_PRINT("info", ("found lock to release ticket=%p", ticket)); release_ticket(ticket); @@ -1345,8 +1400,6 @@ void MDL_context::release_all_locks() pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); - m_tickets.empty(); - DBUG_VOID_RETURN; } @@ -1452,8 +1505,9 @@ MDL_context::is_exclusive_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name) { MDL_request mdl_request; + bool is_lt_or_ha_unused; mdl_request.init(mdl_namespace, db, name, MDL_EXCLUSIVE); - MDL_ticket *ticket= find_ticket(&mdl_request); + MDL_ticket *ticket= find_ticket(&mdl_request, &is_lt_or_ha_unused); DBUG_ASSERT(ticket == NULL || ticket->m_state == MDL_ACQUIRED); @@ -1593,19 +1647,87 @@ void *MDL_ticket::get_cached_object() void MDL_context::rollback_to_savepoint(MDL_ticket *mdl_savepoint) { - MDL_ticket *ticket; - Ticket_iterator it(m_tickets); DBUG_ENTER("MDL_context::rollback_to_savepoint"); - while ((ticket= it++)) - { - /* Stop when lock was acquired before this savepoint. */ - if (ticket == mdl_savepoint) - break; - release_lock(ticket); - } + /* If savepoint is NULL, it is from the start of the transaction. */ + release_locks_stored_before(mdl_savepoint ? + mdl_savepoint : m_lt_or_ha_sentinel); DBUG_VOID_RETURN; } +/** + Release locks acquired by normal statements (SELECT, UPDATE, + DELETE, etc) in the course of a transaction. Do not release + HANDLER locks, if there are any. + + This method is used at the end of a transaction, in + implementation of COMMIT (implicit or explicit) and ROLLBACK. +*/ + +void MDL_context::release_transactional_locks() +{ + DBUG_ENTER("MDL_context::release_transactional_locks"); + release_locks_stored_before(m_lt_or_ha_sentinel); + DBUG_VOID_RETURN; +} + + +/** + Does this savepoint have this lock? + + @retval TRUE The ticket is older than the savepoint and + is not LT or HA ticket. Thus it belongs to + the savepoint. + @retval FALSE The ticket is newer than the savepoint + or is an LT or HA ticket. +*/ + +bool MDL_context::has_lock(MDL_ticket *mdl_savepoint, + MDL_ticket *mdl_ticket) +{ + MDL_ticket *ticket; + MDL_context::Ticket_iterator it(m_tickets); + bool found_savepoint= FALSE; + + while ((ticket= it++) && ticket != m_lt_or_ha_sentinel) + { + /* + First met the savepoint. The ticket must be + somewhere after it. + */ + if (ticket == mdl_savepoint) + found_savepoint= TRUE; + /* + Met the ticket. If we haven't yet met the savepoint, + the ticket is newer than the savepoint. + */ + if (ticket == mdl_ticket) + return found_savepoint; + } + /* Reached m_lt_or_ha_sentinel. The ticket must be an LT or HA ticket. */ + return FALSE; +} + + +/** + Rearrange the ticket to reside in the part of the list that's + beyond m_lt_or_ha_sentinel. This effectively changes the ticket + life cycle, from automatic to manual: i.e. the ticket is no + longer released by MDL_context::release_transactional_locks() or + MDL_context::rollback_to_savepoint(), it must be released manually. +*/ + +void MDL_context::move_ticket_after_lt_or_ha_sentinel(MDL_ticket *mdl_ticket) +{ + m_tickets.remove(mdl_ticket); + if (m_lt_or_ha_sentinel == NULL) + { + m_lt_or_ha_sentinel= mdl_ticket; + /* sic: linear from the number of transactional tickets acquired so-far! */ + m_tickets.push_back(mdl_ticket); + } + else + m_tickets.insert_after(m_lt_or_ha_sentinel, mdl_ticket); +} diff --git a/sql/mdl.h b/sql/mdl.h index 2758bd3a8e6..e85f1232ff9 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -327,19 +327,15 @@ public: void init(THD *thd); void destroy(); - void backup_and_reset(MDL_context *backup); - void restore_from_backup(MDL_context *backup); - void merge(MDL_context *source); - bool try_acquire_shared_lock(MDL_request *mdl_request); bool acquire_exclusive_lock(MDL_request *mdl_request); bool acquire_exclusive_locks(MDL_request_list *requests); bool try_acquire_exclusive_lock(MDL_request *mdl_request); bool acquire_global_shared_lock(); + bool clone_ticket(MDL_request *mdl_request); bool wait_for_locks(MDL_request_list *requests); - void release_all_locks(); void release_all_locks_for_name(MDL_ticket *ticket); void release_lock(MDL_ticket *ticket); void release_global_shared_lock(); @@ -350,26 +346,60 @@ public: bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name); + + bool has_lock(MDL_ticket *mdl_savepoint, MDL_ticket *mdl_ticket); + inline bool has_locks() const { return !m_tickets.is_empty(); } - inline MDL_ticket *mdl_savepoint() + MDL_ticket *mdl_savepoint() { - return m_tickets.head(); + /* + NULL savepoint represents the start of the transaction. + Checking for m_lt_or_ha_sentinel also makes sure we never + return a pointer to HANDLER ticket as a savepoint. + */ + return m_tickets.front() == m_lt_or_ha_sentinel ? NULL : m_tickets.front(); } + void set_lt_or_ha_sentinel() + { + DBUG_ASSERT(m_lt_or_ha_sentinel == NULL); + m_lt_or_ha_sentinel= mdl_savepoint(); + } + MDL_ticket *lt_or_ha_sentinel() const { return m_lt_or_ha_sentinel; } + + void clear_lt_or_ha_sentinel() + { + m_lt_or_ha_sentinel= NULL; + } + void move_ticket_after_lt_or_ha_sentinel(MDL_ticket *mdl_ticket); + + void release_transactional_locks(); void rollback_to_savepoint(MDL_ticket *mdl_savepoint); inline THD *get_thd() const { return m_thd; } private: Ticket_list m_tickets; bool m_has_global_shared_lock; + /** + This member has two uses: + 1) When entering LOCK TABLES mode, remember the last taken + metadata lock. COMMIT/ROLLBACK must preserve these metadata + locks. + 2) When we have an open HANDLER tables, store the position + in the list beyond which we keep locks for HANDLER tables. + COMMIT/ROLLBACK must, again, preserve HANDLER metadata locks. + */ + MDL_ticket *m_lt_or_ha_sentinel; THD *m_thd; private: void release_ticket(MDL_ticket *ticket); - MDL_ticket *find_ticket(MDL_request *mdl_req); + MDL_ticket *find_ticket(MDL_request *mdl_req, + bool *is_lt_or_ha); + void release_locks_stored_before(MDL_ticket *sentinel); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1a171705dae..caf3130c517 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1534,6 +1534,7 @@ TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list); int drop_temporary_table(THD *thd, TABLE_LIST *table_list); void close_temporary_table(THD *thd, TABLE *table, bool free_share, bool delete_table); +void mark_tmp_table_for_reuse(TABLE *table); void close_temporary(TABLE *table, bool free_share, bool delete_table); bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 9d82307d2e7..d47c49ed515 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -86,8 +86,7 @@ int injector::transaction::commit() if (!trans_commit(m_thd)) { close_thread_tables(m_thd); - if (!m_thd->locked_tables_mode) - m_thd->mdl_context.release_all_locks(); + m_thd->mdl_context.release_transactional_locks(); } DBUG_RETURN(0); } diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index b4554bb4b6c..bd03afb8dd8 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1189,8 +1189,8 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) } m_table_map.clear_tables(); slave_close_thread_tables(thd); - if (error && !thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + if (error) + thd->mdl_context.release_transactional_locks(); clear_flag(IN_STMT); /* Cleanup for the flags that have been set at do_apply_event. diff --git a/sql/set_var.cc b/sql/set_var.cc index dd009541274..ce7cfcc81a8 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3196,8 +3196,7 @@ static bool set_option_autocommit(THD *thd, set_var *var) return TRUE; close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); } if (var->save_result.ulong_value != 0) diff --git a/sql/slave.cc b/sql/slave.cc index 62ddc8aaf21..ca72aaea69a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2432,8 +2432,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) exec_res= 0; trans_rollback(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); /* chance for concurrent connection to get more locks */ safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE), (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4b9cee98211..451b2293109 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -730,8 +730,7 @@ my_bool acl_reload(THD *thd) end: trans_commit_implicit(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); DBUG_RETURN(return_val); } @@ -3895,8 +3894,7 @@ my_bool grant_reload(THD *thd) rw_unlock(&LOCK_grant); trans_commit_implicit(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); /* It is OK failing to load procs_priv table because we may be diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 61028f692b3..459ca646d8c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1169,40 +1169,54 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd) for (TABLE *table= thd->temporary_tables ; table ; table= table->next) { if ((table->query_id == thd->query_id) && ! table->open_by_handler) - { - table->query_id= 0; - table->file->ha_reset(); - - /* Detach temporary MERGE children from temporary parent. */ - DBUG_ASSERT(table->file); - table->file->extra(HA_EXTRA_DETACH_CHILDREN); - - /* - Reset temporary table lock type to it's default value (TL_WRITE). - - Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE - .. SELECT FROM tmp and UPDATE may under some circumstances modify - the lock type of the tables participating in the statement. This - isn't a problem for non-temporary tables since their lock type is - reset at every open, but the same does not occur for temporary - tables for historical reasons. - - Furthermore, the lock type of temporary tables is not really that - important because they can only be used by one query at a time and - not even twice in a query -- a temporary table is represented by - only one TABLE object. Nonetheless, it's safer from a maintenance - point of view to reset the lock type of this singleton TABLE object - as to not cause problems when the table is reused. - - Even under LOCK TABLES mode its okay to reset the lock type as - LOCK TABLES is allowed (but ignored) for a temporary table. - */ - table->reginfo.lock_type= TL_WRITE; - } + mark_tmp_table_for_reuse(table); } } +/** + Reset a single temporary table. + Effectively this "closes" one temporary table, + in a session. + + @param table Temporary table. +*/ + +void mark_tmp_table_for_reuse(TABLE *table) +{ + DBUG_ASSERT(table->s->tmp_table); + + table->query_id= 0; + table->file->ha_reset(); + + /* Detach temporary MERGE children from temporary parent. */ + DBUG_ASSERT(table->file); + table->file->extra(HA_EXTRA_DETACH_CHILDREN); + + /* + Reset temporary table lock type to it's default value (TL_WRITE). + + Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE + .. SELECT FROM tmp and UPDATE may under some circumstances modify + the lock type of the tables participating in the statement. This + isn't a problem for non-temporary tables since their lock type is + reset at every open, but the same does not occur for temporary + tables for historical reasons. + + Furthermore, the lock type of temporary tables is not really that + important because they can only be used by one query at a time and + not even twice in a query -- a temporary table is represented by + only one TABLE object. Nonetheless, it's safer from a maintenance + point of view to reset the lock type of this singleton TABLE object + as to not cause problems when the table is reused. + + Even under LOCK TABLES mode its okay to reset the lock type as + LOCK TABLES is allowed (but ignored) for a temporary table. + */ + table->reginfo.lock_type= TL_WRITE; +} + + /* Mark all tables in the list which were used by current substatement as free for reuse. @@ -1261,7 +1275,6 @@ static void close_open_tables(THD *thd) while (thd->open_tables) found_old_table|= close_thread_table(thd, &thd->open_tables); - thd->some_tables_deleted= 0; /* Free tables to hold down open files */ while (table_cache_count > table_cache_size && unused_tables) @@ -1475,7 +1488,7 @@ void close_thread_tables(THD *thd) if (thd->locked_tables_mode == LTM_LOCK_TABLES) DBUG_VOID_RETURN; - thd->locked_tables_mode= LTM_NONE; + thd->leave_locked_tables_mode(); /* Fallthrough */ } @@ -1505,16 +1518,27 @@ void close_thread_tables(THD *thd) if (thd->open_tables) close_open_tables(thd); - /* - Defer the release of metadata locks until the current transaction - is either committed or rolled back. This prevents other statements - from modifying the table for the entire duration of this transaction. - This provides commitment ordering for guaranteeing serializability - across multiple transactions. - */ - if (!thd->in_multi_stmt_transaction() || - (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) - thd->mdl_context.release_all_locks(); + if (thd->state_flags & Open_tables_state::BACKUPS_AVAIL) + { + /* We can't have an open HANDLER in the backup open tables state. */ + DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); + /* + Due to the above assert, this is guaranteed to release *all* locks + in the context. + */ + thd->mdl_context.release_transactional_locks(); + } + else if (! thd->in_multi_stmt_transaction()) + { + /* + Defer the release of metadata locks until the current transaction + is either committed or rolled back. This prevents other statements + from modifying the table for the entire duration of this transaction. + This provides commitment ordering for guaranteeing serializability + across multiple transactions. + */ + thd->mdl_context.release_transactional_locks(); + } DBUG_VOID_RETURN; } @@ -2337,7 +2361,8 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, enforced by asserts in metadata locking subsystem. */ mdl_request->set_type(MDL_EXCLUSIVE); - DBUG_ASSERT(! thd->mdl_context.has_locks()); + DBUG_ASSERT(! thd->mdl_context.has_locks() || + thd->handler_tables_hash.records); if (thd->mdl_context.acquire_exclusive_lock(mdl_request)) return 1; @@ -2791,7 +2816,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (!share->free_tables.is_empty()) { - table= share->free_tables.head(); + table= share->free_tables.front(); table_def_use_table(thd, table); /* We need to release share as we have EXTRA reference to it in our hands. */ release_table_share(share); @@ -3082,7 +3107,7 @@ Locked_tables_list::init_locked_tables(THD *thd) return TRUE; } } - thd->locked_tables_mode= LTM_LOCK_TABLES; + thd->enter_locked_tables_mode(LTM_LOCK_TABLES); return FALSE; } @@ -3121,7 +3146,7 @@ Locked_tables_list::unlock_locked_tables(THD *thd) */ table_list->table->pos_in_locked_tables= NULL; } - thd->locked_tables_mode= LTM_NONE; + thd->leave_locked_tables_mode(); close_thread_tables(thd); } @@ -3636,7 +3661,8 @@ end_with_lock_open: Open_table_context::Open_table_context(THD *thd) :m_action(OT_NO_ACTION), - m_can_deadlock(thd->in_multi_stmt_transaction() && + m_can_deadlock((thd->in_multi_stmt_transaction() || + thd->mdl_context.lt_or_ha_sentinel())&& thd->mdl_context.has_locks()) {} @@ -4136,7 +4162,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, even if they don't create problems for current thread (i.e. to avoid having DDL blocked by HANDLERs opened for long time). */ - if (thd->handler_tables) + if (thd->handler_tables_hash.records) mysql_ha_flush(thd); /* @@ -4642,13 +4668,14 @@ retry: while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) && ot_ctx.can_recover_from_failed_open_table()) { + /* We can't back off with an open HANDLER, we don't wait with locks. */ + DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); /* Even though we have failed to open table we still need to - call release_all_locks() to release metadata locks which + call release_transactional_locks() to release metadata locks which might have been acquired successfully. */ - if (! thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); table_list->mdl_request.ticket= 0; if (ot_ctx.recover_from_failed_open_table_attempt(thd, table_list)) break; @@ -4699,8 +4726,12 @@ retry: close_thread_tables(thd); table_list->table= NULL; table_list->mdl_request.ticket= NULL; - if (! thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + /* + We can't back off with an open HANDLER, + we don't wait with locks. + */ + DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); + thd->mdl_context.release_transactional_locks(); goto retry; } } @@ -4769,7 +4800,8 @@ bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, break; if (!need_reopen) DBUG_RETURN(TRUE); - if (thd->in_multi_stmt_transaction() && has_locks) + if ((thd->in_multi_stmt_transaction() || + thd->mdl_context.lt_or_ha_sentinel()) && has_locks) { my_error(ER_LOCK_DEADLOCK, MYF(0)); DBUG_RETURN(TRUE); @@ -5124,7 +5156,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, */ mark_real_tables_as_free_for_reuse(first_not_own); DBUG_PRINT("info",("locked_tables_mode= LTM_PRELOCKED")); - thd->locked_tables_mode= LTM_PRELOCKED; + thd->enter_locked_tables_mode(LTM_PRELOCKED); } } else @@ -5226,8 +5258,13 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) for (tmp= first_not_own_table; tmp; tmp= tmp->next_global) tmp->mdl_request.ticket= NULL; close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + /* We can't back off with an open HANDLERs, we must not wait with locks. */ + DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); + /* + Due to the above assert, this effectively releases *all* locks + of this session, so that we can safely wait on tables. + */ + thd->mdl_context.release_transactional_locks(); } @@ -8202,6 +8239,15 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use) if (!thd_table->needs_reopen()) signalled|= mysql_lock_abort_for_thread(thd, thd_table); } + /* + Wake up threads waiting in tdc_wait_for_old_versions(). + Normally such threads would already get blocked + in MDL subsystem, when trying to acquire a shared lock. + But in case a thread has an open HANDLER statement, + (and thus already grabbed a metadata lock), it gets + blocked only too late -- at the table cache level. + */ + broadcast_refresh(); pthread_mutex_unlock(&LOCK_open); return signalled; } @@ -8721,7 +8767,9 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) pthread_mutex_unlock(&LOCK_open); - thd->mdl_context.release_all_locks(); + /* We can't have an open HANDLER in the backup context. */ + DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); + thd->mdl_context.release_transactional_locks(); thd->restore_backup_open_tables_state(backup); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index aa647aada60..95c985b2c10 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -482,7 +482,7 @@ THD::THD() catalog= (char*)"std"; // the only catalog we have for now main_security_ctx.init(); security_ctx= &main_security_ctx; - some_tables_deleted=no_errors=password= 0; + no_errors=password= 0; query_start_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; killed= NOT_KILLED; @@ -997,6 +997,7 @@ void THD::cleanup(void) } locked_tables_list.unlock_locked_tables(this); + mysql_ha_cleanup(this); /* If the thread was in the middle of an ongoing transaction (rolled @@ -1005,14 +1006,19 @@ void THD::cleanup(void) metadata locks. Release them. */ DBUG_ASSERT(open_tables == NULL); - mdl_context.release_all_locks(); + /* All HANDLERs must have been closed by now. */ + DBUG_ASSERT(mdl_context.lt_or_ha_sentinel() == NULL); + /* + Due to the above assert, this is guaranteed to release *all* in + this session. + */ + mdl_context.release_transactional_locks(); #if defined(ENABLED_DEBUG_SYNC) /* End the Debug Sync Facility. See debug_sync.cc. */ debug_sync_end_thread(this); #endif /* defined(ENABLED_DEBUG_SYNC) */ - mysql_ha_cleanup(this); delete_dynamic(&user_var_events); my_hash_free(&user_vars); close_temporary_tables(this); @@ -1061,8 +1067,6 @@ THD::~THD() cleanup(); mdl_context.destroy(); - handler_mdl_context.destroy(); - ha_close_connection(this); plugin_thdvar_cleanup(this); @@ -3038,12 +3042,11 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) to be sure that it was properly cleaned up. */ DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 && - handler_tables == 0 && derived_tables == 0 && + derived_tables == 0 && lock == 0 && locked_tables_mode == LTM_NONE && m_reprepare_observer == NULL); mdl_context.destroy(); - handler_mdl_context.destroy(); set_open_tables_state(backup); DBUG_VOID_RETURN; diff --git a/sql/sql_class.h b/sql/sql_class.h index ebea2041715..11e0010d85b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -914,11 +914,6 @@ public: XXX Why are internal temporary tables added to this list? */ TABLE *temporary_tables; - /** - List of tables that were opened with HANDLER OPEN and are - still in use by this thread. - */ - TABLE *handler_tables; TABLE *derived_tables; /* During a MySQL session, one can lock tables in two modes: automatic @@ -985,7 +980,6 @@ public: uint state_flags; MDL_context mdl_context; - MDL_context handler_mdl_context; /** This constructor initializes Open_tables_state instance which can only @@ -1011,13 +1005,23 @@ public: void reset_open_tables_state(THD *thd) { - open_tables= temporary_tables= handler_tables= derived_tables= 0; + open_tables= temporary_tables= derived_tables= 0; extra_lock= lock= 0; locked_tables_mode= LTM_NONE; state_flags= 0U; m_reprepare_observer= NULL; mdl_context.init(thd); - handler_mdl_context.init(thd); + } + void enter_locked_tables_mode(enum_locked_tables_mode mode_arg) + { + DBUG_ASSERT(locked_tables_mode == LTM_NONE); + mdl_context.set_lt_or_ha_sentinel(); + locked_tables_mode= mode_arg; + } + void leave_locked_tables_mode() + { + locked_tables_mode= LTM_NONE; + mdl_context.clear_lt_or_ha_sentinel(); } }; @@ -1902,7 +1906,6 @@ public: bool slave_thread, one_shot_set; /* tells if current statement should binlog row-based(1) or stmt-based(0) */ bool current_stmt_binlog_row_based; - bool some_tables_deleted; bool last_cuted_field; bool no_errors, password; /** diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 94f5e84fb10..ccfe21d1af5 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -33,27 +33,21 @@ */ /* - There are two containers holding information about open handler tables. - The first is 'thd->handler_tables'. It is a linked list of TABLE objects. - It is used like 'thd->open_tables' in the table cache. The trick is to - exchange these two lists during open and lock of tables. Thus the normal - table cache code can be used. - The second container is a HASH. It holds objects of the type TABLE_LIST. - Despite its name, no lists of tables but only single structs are hashed - (the 'next' pointer is always NULL). The reason for theis second container - is, that we want handler tables to survive FLUSH TABLE commands. A table - affected by FLUSH TABLE must be closed so that other threads are not - blocked by handler tables still in use. Since we use the normal table cache - functions with 'thd->handler_tables', the closed tables are removed from - this list. Hence we need the original open information for the handler - table in the case that it is used again. This information is handed over - to mysql_ha_open() as a TABLE_LIST. So we store this information in the - second container, where it is not affected by FLUSH TABLE. The second - container is implemented as a hash for performance reasons. Consequently, - we use it not only for re-opening a handler table, but also for the - HANDLER ... READ commands. For this purpose, we store a pointer to the - TABLE structure (in the first container) in the TBALE_LIST object in the - second container. When the table is flushed, the pointer is cleared. + The information about open HANDLER objects is stored in a HASH. + It holds objects of type TABLE_LIST, which are indexed by table + name/alias, and allows us to quickly find a HANDLER table for any + operation at hand - be it HANDLER READ or HANDLER CLOSE. + + It also allows us to maintain an "open" HANDLER even in cases + when there is no physically open cursor. E.g. a FLUSH TABLE + statement in this or some other connection demands that all open + HANDLERs against the flushed table are closed. In order to + preserve the information about an open HANDLER, we don't perform + a complete HANDLER CLOSE, but only close the TABLE object. The + corresponding TABLE_LIST is kept in the cache with 'table' + pointer set to NULL. The table will be reopened on next access + (this, however, leads to loss of cursor position, unless the + cursor points at the first record). */ #include "mysql_priv.h" @@ -124,32 +118,19 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) { - TABLE **table_ptr; - MDL_ticket *mdl_ticket; - /* - Though we could take the table pointer from hash_tables->table, - we must follow the thd->handler_tables chain anyway, as we need the - address of the 'next' pointer referencing this table - for close_thread_table(). - */ - for (table_ptr= &(thd->handler_tables); - *table_ptr && (*table_ptr != tables->table); - table_ptr= &(*table_ptr)->next) - ; - - if (*table_ptr) + if (tables->table && !tables->table->s->tmp_table) { - (*table_ptr)->file->ha_index_or_rnd_end(); - mdl_ticket= (*table_ptr)->mdl_ticket; + /* Non temporary table. */ + tables->table->file->ha_index_or_rnd_end(); pthread_mutex_lock(&LOCK_open); - if (close_thread_table(thd, table_ptr)) + if (close_thread_table(thd, &tables->table)) { /* Tell threads waiting for refresh that something has happened */ broadcast_refresh(); } pthread_mutex_unlock(&LOCK_open); - thd->handler_mdl_context.release_lock(mdl_ticket); + thd->mdl_context.release_lock(tables->mdl_request.ticket); } else if (tables->table) { @@ -158,6 +139,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) table->file->ha_index_or_rnd_end(); table->query_id= thd->query_id; table->open_by_handler= 0; + mark_tmp_table_for_reuse(table); } /* Mark table as closed, ready for re-open if necessary. */ @@ -195,7 +177,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) uint dblen, namelen, aliaslen, counter; bool error; TABLE *backup_open_tables; - MDL_context backup_mdl_context; + MDL_ticket *mdl_savepoint; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -265,6 +247,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED); + /* for now HANDLER can be used only for real TABLES */ + hash_tables->required_type= FRMTYPE_TABLE; /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) @@ -283,16 +267,11 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) from open_tables(), thd->open_tables will contain only the opened table. - The thd->handler_tables list is kept as-is to avoid deadlocks if - open_table(), called by open_tables(), needs to back-off because - of a pending exclusive metadata lock or flush for the table being - opened. - See open_table() back-off comments for more details. */ backup_open_tables= thd->open_tables; thd->open_tables= NULL; - thd->mdl_context.backup_and_reset(&backup_mdl_context); + mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* open_tables() will set 'hash_tables->table' if successful. @@ -300,53 +279,47 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) */ DBUG_ASSERT(! hash_tables->table); - /* for now HANDLER can be used only for real TABLES */ - hash_tables->required_type= FRMTYPE_TABLE; /* We use open_tables() here, rather than, say, open_ltable() or open_table() because we would like to be able to open a temporary table. */ error= open_tables(thd, &hash_tables, &counter, 0); - if (thd->open_tables) - { - if (thd->open_tables->next) - { - /* - We opened something that is more than a single table. - This happens with MERGE engine. Don't try to link - this mess into thd->handler_tables list, close it - and report an error. We must do it right away - because mysql_ha_close_table(), called down the road, - can close a single table only. - */ - close_thread_tables(thd); - thd->mdl_context.release_all_locks(); - my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias); - error= TRUE; - } - else - { - /* Merge the opened table into handler_tables list. */ - thd->open_tables->next= thd->handler_tables; - thd->handler_tables= thd->open_tables; - } - } - thd->handler_mdl_context.merge(&thd->mdl_context); - thd->open_tables= backup_open_tables; - thd->mdl_context.restore_from_backup(&backup_mdl_context); - - if (error) - goto err; - - /* There can be only one table in '*tables'. */ - if (! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) + if (! error && + ! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) { my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); - goto err; + error= TRUE; } + if (!error && + hash_tables->mdl_request.ticket && + thd->mdl_context.has_lock(mdl_savepoint, + hash_tables->mdl_request.ticket)) + { + /* The ticket returned is within a savepoint. Make a copy. */ + error= thd->mdl_context.clone_ticket(&hash_tables->mdl_request); + hash_tables->table->mdl_ticket= hash_tables->mdl_request.ticket; + } + if (error) + { + close_thread_tables(thd); + thd->open_tables= backup_open_tables; + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + if (!reopen) + my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); + else + hash_tables->table= NULL; + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(TRUE); + } + thd->open_tables= backup_open_tables; + if (hash_tables->mdl_request.ticket) + thd->mdl_context. + move_ticket_after_lt_or_ha_sentinel(hash_tables->mdl_request.ticket); + /* Assert that the above check prevent opening of views and merge tables. */ + DBUG_ASSERT(hash_tables->table->next == NULL); /* If it's a temp table, don't reset table->query_id as the table is being used by this handler. Otherwise, no meaning at all. @@ -357,14 +330,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) my_ok(thd); DBUG_PRINT("exit",("OK")); DBUG_RETURN(FALSE); - -err: - if (hash_tables->table) - mysql_ha_close_table(thd, hash_tables); - if (!reopen) - my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); - DBUG_PRINT("exit",("ERROR")); - DBUG_RETURN(TRUE); } @@ -496,59 +461,41 @@ retry: hash_tables->db, hash_tables->table_name, hash_tables->alias, table)); } - table->pos_in_table_list= tables; -#if MYSQL_VERSION_ID < 40100 - if (*tables->db && strcmp(table->table_cache_key, tables->db)) - { - DBUG_PRINT("info",("wrong db")); - table= NULL; - } -#endif } else table= NULL; if (!table) { -#if MYSQL_VERSION_ID < 40100 - char buff[MAX_DBKEY_LENGTH]; - if (*tables->db) - strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name, - NullS); - else - strncpy(buff, tables->alias, sizeof(buff)); - my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER"); -#else my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER"); -#endif goto err0; } - tables->table=table; /* save open_tables state */ backup_open_tables= thd->open_tables; + /* Always a one-element list, see mysql_ha_open(). */ + DBUG_ASSERT(hash_tables->table->next == NULL); /* mysql_lock_tables() needs thd->open_tables to be set correctly to - be able to handle aborts properly. When the abort happens, it's - safe to not protect thd->handler_tables because it won't close any - tables. + be able to handle aborts properly. */ - thd->open_tables= thd->handler_tables; + thd->open_tables= hash_tables->table; - lock= mysql_lock_tables(thd, &tables->table, 1, 0, &need_reopen); - /* restore previous context */ + lock= mysql_lock_tables(thd, &thd->open_tables, 1, 0, &need_reopen); + + /* + In 5.1 and earlier, mysql_lock_tables() could replace the TABLE + object with another one (reopen it). This is no longer the case + with new MDL. + */ + DBUG_ASSERT(hash_tables->table == thd->open_tables); + /* Restore previous context. */ thd->open_tables= backup_open_tables; if (need_reopen) { mysql_ha_close_table(thd, hash_tables); - /* - The lock might have been aborted, we need to manually reset - thd->some_tables_deleted because handler's tables are closed - in a non-standard way. Otherwise we might loop indefinitely. - */ - thd->some_tables_deleted= 0; goto retry; } @@ -556,7 +503,8 @@ retry: goto err0; // mysql_lock_tables() printed error message already // Always read all columns - tables->table->read_set= &tables->table->s->all_set; + hash_tables->table->read_set= &hash_tables->table->s->all_set; + tables->table= hash_tables->table; if (cond) { @@ -811,6 +759,14 @@ void mysql_ha_flush(THD *thd) safe_mutex_assert_not_owner(&LOCK_open); + /* + Don't try to flush open HANDLERs when we're working with + system tables. The main MDL context is backed up and we can't + properly release HANDLER locks stored there. + */ + if (thd->state_flags & Open_tables_state::BACKUPS_AVAIL) + DBUG_VOID_RETURN; + for (uint i= 0; i < thd->handler_tables_hash.records; i++) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); @@ -818,9 +774,10 @@ void mysql_ha_flush(THD *thd) TABLE::mdl_ticket is 0 for temporary tables so we need extra check. */ if (hash_tables->table && - (hash_tables->table->mdl_ticket && - hash_tables->table->mdl_ticket->has_pending_conflicting_lock() || - hash_tables->table->s->needs_reopen())) + ((hash_tables->table->mdl_ticket && + hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) || + (!hash_tables->table->s->tmp_table && + hash_tables->table->s->needs_reopen()))) mysql_ha_close_table(thd, hash_tables); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 9f8af87f1e2..40ef55423a9 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3610,8 +3610,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, /* mysql_lock_tables() below should never fail with request to reopen table since it won't wait for the table lock (we have exclusive metadata lock on - the table) and thus can't get aborted and since it ignores other threads - setting THD::some_tables_deleted thanks to MYSQL_LOCK_IGNORE_FLUSH. + the table) and thus can't get aborted. */ if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH, ¬_used)) || diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7fbf04c751f..79f10120268 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1180,8 +1180,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (trans_commit_implicit(thd)) break; close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); if (check_global_access(thd,RELOAD_ACL)) break; general_log_print(thd, command, NullS); @@ -1211,8 +1210,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (trans_commit_implicit(thd)) break; close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); my_ok(thd); break; } @@ -1967,8 +1965,7 @@ mysql_execute_command(THD *thd) goto error; /* Close tables and release metadata locks. */ close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); } /* @@ -3302,7 +3299,7 @@ end_with_restore_list: if (thd->options & OPTION_TABLE_LOCK) { trans_commit_implicit(thd); - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); thd->options&= ~(OPTION_TABLE_LOCK); } if (thd->global_read_lock) @@ -3311,11 +3308,19 @@ end_with_restore_list: break; case SQLCOM_LOCK_TABLES: thd->locked_tables_list.unlock_locked_tables(thd); + /* + As of 5.5, entering LOCK TABLES mode implicitly closes all + open HANDLERs. Both HANDLER code and LOCK TABLES mode use + the sentinel mechanism in MDL subsystem and thus could not be + used at the same time. All HANDLER operations are prohibited + under LOCK TABLES anyway. + */ + mysql_ha_cleanup(thd); /* we must end the trasaction first, regardless of anything */ if (trans_commit_implicit(thd)) goto error; /* release transactional metadata locks. */ - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; @@ -3354,7 +3359,7 @@ end_with_restore_list: */ close_thread_tables(thd); DBUG_ASSERT(!thd->locked_tables_mode); - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); thd->options&= ~(OPTION_TABLE_LOCK); } else @@ -3845,8 +3850,7 @@ end_with_restore_list: thd->locked_tables_mode == LTM_LOCK_TABLES); if (trans_commit(thd)) goto error; - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); /* Begin transaction with the same isolation level. */ if (lex->tx_chain && trans_begin(thd)) goto error; @@ -3860,8 +3864,7 @@ end_with_restore_list: thd->locked_tables_mode == LTM_LOCK_TABLES); if (trans_rollback(thd)) goto error; - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); /* Begin transaction with the same isolation level. */ if (lex->tx_chain && trans_begin(thd)) goto error; @@ -4212,8 +4215,7 @@ create_sp_error: close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); #ifndef NO_EMBEDDED_ACCESS_CHECKS if (sp_automatic_privileges && !opt_noacl && @@ -4394,15 +4396,13 @@ create_sp_error: case SQLCOM_XA_COMMIT: if (trans_xa_commit(thd)) goto error; - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); my_ok(thd); break; case SQLCOM_XA_ROLLBACK: if (trans_xa_rollback(thd)) goto error; - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); my_ok(thd); break; case SQLCOM_XA_RECOVER: @@ -4555,11 +4555,10 @@ finish: thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); /* Commit the normal transaction if one is active. */ trans_commit_implicit(thd); + thd->stmt_da->can_overwrite_status= FALSE; /* Close tables and release metadata locks. */ close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); - thd->stmt_da->can_overwrite_status= FALSE; + thd->mdl_context.release_transactional_locks(); } DBUG_RETURN(res || thd->is_error()); @@ -6539,7 +6538,9 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } #endif /*HAVE_QUERY_CACHE*/ - DBUG_ASSERT(!thd || thd->locked_tables_mode || !thd->mdl_context.has_locks()); + DBUG_ASSERT(!thd || thd->locked_tables_mode || + !thd->mdl_context.has_locks() || + thd->handler_tables_hash.records); /* Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too diff --git a/sql/sql_plist.h b/sql/sql_plist.h index b0a0bb016d0..94e437362a9 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -71,6 +71,36 @@ public: first= a; *B::prev_ptr(a)= &first; } + inline void push_back(T *a) + { + insert_after(back(), a); + } + inline T *back() + { + T *t= front(); + if (t) + { + while (*B::next_ptr(t)) + t= *B::next_ptr(t); + } + return t; + } + inline void insert_after(T *pos, T *a) + { + if (pos == NULL) + push_front(a); + else + { + *B::next_ptr(a)= *B::next_ptr(pos); + *B::prev_ptr(a)= B::next_ptr(pos); + *B::next_ptr(pos)= a; + if (*B::next_ptr(a)) + { + T *old_next= *B::next_ptr(a); + *B::prev_ptr(old_next)= B::next_ptr(a); + } + } + } inline void remove(T *a) { T *next= *B::next_ptr(a); @@ -78,8 +108,8 @@ public: *B::prev_ptr(next)= *B::prev_ptr(a); **B::prev_ptr(a)= next; } - inline T* head() { return first; } - inline const T *head() const { return first; } + inline T* front() { return first; } + inline const T *front() const { return first; } void swap(I_P_List &rhs) { swap_variables(T *, first, rhs.first); @@ -106,6 +136,7 @@ class I_P_List_iterator T *current; public: I_P_List_iterator(I_P_List &a) : list(&a), current(a.first) {} + I_P_List_iterator(I_P_List &a, T* current_arg) : list(&a), current(current_arg) {} inline void init(I_P_List &a) { list= &a; @@ -118,6 +149,11 @@ public: current= *B::next_ptr(current); return result; } + inline T* operator++() + { + current= *B::next_ptr(current); + return current; + } inline void rewind() { current= list->first; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 27fdd1e2a8d..700017f2b3e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3193,8 +3193,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) Marker used to release metadata locks acquired while the prepared statement is being checked. */ - if (thd->in_multi_stmt_transaction()) - mdl_savepoint= thd->mdl_context.mdl_savepoint(); + mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* The only case where we should have items in the thd->free_list is @@ -3220,13 +3219,11 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex_end(lex); cleanup_stmt(); /* - If not inside a multi-statement transaction, the metadata locks have - already been released and the rollback_to_savepoint is a nop. - Otherwise, release acquired locks -- a NULL mdl_savepoint means that - all locks are going to be released or that the transaction didn't - own any locks. + If not inside a multi-statement transaction, the metadata + locks have already been released and our savepoint points + to ticket which has been released as well. */ - if (!thd->locked_tables_mode) + if (thd->in_multi_stmt_transaction()) thd->mdl_context.rollback_to_savepoint(mdl_savepoint); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 464a70e4175..4d5a4f41849 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -247,8 +247,7 @@ bool servers_reload(THD *thd) end: trans_commit_implicit(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); DBUG_PRINT("info", ("unlocking servers_cache")); rw_unlock(&THR_LOCK_servers); DBUG_RETURN(return_val); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 941352cb963..3b87a4dd6e8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4714,8 +4714,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); lex->reset_query_tables_list(FALSE); table->table=0; // For query cache if (protocol->write()) @@ -4766,8 +4765,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_rollback_stmt(thd); trans_rollback(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); tmp_disable_binlog(thd); // binlogging is done by caller if wanted result_code= mysql_recreate_table(thd, table); reenable_binlog(thd); @@ -4883,8 +4881,7 @@ send_result_message: trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); DEBUG_SYNC(thd, "ha_admin_try_alter"); protocol->store(STRING_WITH_LEN("note"), system_charset_info); protocol->store(STRING_WITH_LEN( @@ -4910,8 +4907,7 @@ send_result_message: trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); - if (!thd->locked_tables_mode) - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); if (!result_code) // recreation went ok { /* Clear the ticket released in close_thread_tables(). */ @@ -7429,7 +7425,6 @@ end_temporary: (ulong) (copied + deleted), (ulong) deleted, (ulong) thd->warning_info->statement_warn_count()); my_ok(thd, copied + deleted, 0L, tmp_name); - thd->some_tables_deleted=0; DBUG_RETURN(FALSE); err_new_table_cleanup: diff --git a/sql/transaction.cc b/sql/transaction.cc index d1c7244ba83..1ca455028f0 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -103,7 +103,7 @@ bool trans_begin(THD *thd, uint flags) Release transactional metadata locks only after the transaction has been committed. */ - thd->mdl_context.release_all_locks(); + thd->mdl_context.release_transactional_locks(); thd->options|= OPTION_BEGIN; thd->server_status|= SERVER_STATUS_IN_TRANS; @@ -341,6 +341,10 @@ bool trans_savepoint(THD *thd, LEX_STRING name) Remember the last acquired lock before the savepoint was set. This is used as a marker to only release locks acquired after the setting of this savepoint. + Note: this works just fine if we're under LOCK TABLES, + since mdl_savepoint() is guaranteed to be beyond + the last locked table. This allows to release some + locks acquired during LOCK TABLES. */ newsv->mdl_savepoint = thd->mdl_context.mdl_savepoint(); @@ -388,8 +392,10 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) thd->transaction.savepoints= sv; - /* Release metadata locks that were acquired during this savepoint unit. */ - if (!res && !thd->locked_tables_mode) + /* + Release metadata locks that were acquired during this savepoint unit. + */ + if (!res) thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint); DBUG_RETURN(test(res)); From 301f28c158359ba06858a02fd8cbc55f4ee28c80 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 23 Dec 2009 02:00:18 +0100 Subject: [PATCH 193/466] fix loadxml test, it tries to disconnect without reading results. when EXTRA_DEBUG is enabled, mtr complains --- mysql-test/t/loadxml.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/t/loadxml.test b/mysql-test/t/loadxml.test index 8b9c1bd1b0d..627b74ce919 100644 --- a/mysql-test/t/loadxml.test +++ b/mysql-test/t/loadxml.test @@ -84,7 +84,10 @@ sleep 1; sleep 2; --enable_query_log +connection addconroot; +--reap disconnect addconroot; +connection default; # # Clean up From 5dbbb867e1c38a37bc0a842581250113c7d61c00 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 23 Dec 2009 04:44:25 +0100 Subject: [PATCH 194/466] restore flaky loadxml test, fix solaris plugin load using -lpthread instead of -lthread --- configure.cmake | 7 +++++++ mysql-test/t/loadxml.test | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/configure.cmake b/configure.cmake index a0db5669471..353d70aa571 100644 --- a/configure.cmake +++ b/configure.cmake @@ -284,6 +284,13 @@ IF(UNIX) ENDIF() FIND_PACKAGE(Threads) + IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + # CMake defined -lthread as thread flag + # This crashes in dlopen when trying to load plugins + # Workaround with -lpthread + SET(CMAKE_THREADS_LIBS_INIT -lpthread) + ENDIF() + SET(CMAKE_REQUIRED_LIBRARIES ${LIBM} ${LIBNSL} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT} ${LIBRT}) diff --git a/mysql-test/t/loadxml.test b/mysql-test/t/loadxml.test index 627b74ce919..8b9c1bd1b0d 100644 --- a/mysql-test/t/loadxml.test +++ b/mysql-test/t/loadxml.test @@ -84,10 +84,7 @@ sleep 1; sleep 2; --enable_query_log -connection addconroot; ---reap disconnect addconroot; -connection default; # # Clean up From c716ed9ad84affb291513855f75595d3dc61cc63 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 23 Dec 2009 16:37:22 +0100 Subject: [PATCH 195/466] recompile partition plugin for embedded server, its behavior depends on WITH_EMBEDDED_SERVER preprocessor definition --- sql/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 43f22903aef..b32a66a6c92 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -82,7 +82,8 @@ SET (SQL_SOURCE ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE}) -MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY) +MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY +RECOMPILE_FOR_EMBEDDED) ADD_LIBRARY(sql STATIC ${SQL_SOURCE}) DTRACE_INSTRUMENT(sql) @@ -242,8 +243,7 @@ IF(WIN32 AND MYSQLD_EXECUTABLE) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data/mysql/user.frm COMMAND ${CMAKE_COMMAND} - ${CONFIG_PARAM} - -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake + ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data DEPENDS mysqld ) From f8d896fca5c56f25f602d9d4f59ff3ccd4314b29 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 24 Dec 2009 07:21:05 +0100 Subject: [PATCH 196/466] use systemtap on linux if installed, for dtrace functionality --- cmake/dtrace.cmake | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index 92a39a224d5..f110e2ab32c 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -41,7 +41,7 @@ MACRO (DTRACE_HEADER provider header header_no_dtrace) ADD_CUSTOM_COMMAND( OUTPUT ${header} ${header_no_dtrace} COMMAND ${DTRACE} -h -s ${provider} -o ${header} - COMMAND perl ${CMAKE_SOURCE_DIR}/scripts/dheadgen.pl -f ${provider} > ${header_no_dtrace} + COMMAND perl ${CMAKE_SOURCE_DIR}/scripts/dheadgen.pl -f ${provider} > ${header_no_dtrace} DEPENDS ${provider} ) ENDIF() @@ -57,6 +57,13 @@ IF(ENABLE_DTRACE) ${CMAKE_BINARY_DIR}/include/probes_mysql_dtrace.h ${CMAKE_BINARY_DIR}/include/probes_mysql_nodtrace.h ) + IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + # Systemtap object + EXECUTE_PROCESS( + COMMAND ${DTRACE} -G -s ${CMAKE_SOURCE_DIR}/include/probes_mysql.d.base + -o ${CMAKE_BINARY_DIR}/probes_mysql.o + ) + ENDIF() ADD_CUSTOM_TARGET(gen_dtrace_header DEPENDS ${CMAKE_BINARY_DIR}/include/probes_mysql.d @@ -66,12 +73,16 @@ IF(ENABLE_DTRACE) ENDIF() -MACRO (DTRACE_INSTRUMENT target) +MACRO(DTRACE_INSTRUMENT target) IF(ENABLE_DTRACE) ADD_DEPENDENCIES(${target} gen_dtrace_header) - # On Solaris, invoke dtrace -G to generate object file and - # link it together with target. + IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + TARGET_LINK_LIBRARIES(${target} ${CMAKE_BINARY_DIR}/probes_mysql.o) + ENDIF() + + # On Solaris, invoke dtrace -G to generate object file and + # link it together with target. IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") SET(objdir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir) SET(outfile ${objdir}/${target}_dtrace.o) From e64ba44004bff5d4c53ff5918a55a3757348b28f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 25 Dec 2009 03:22:23 +0100 Subject: [PATCH 197/466] enable Cygwin build again --- cmake/ssl.cmake | 2 ++ sql/CMakeLists.txt | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/ssl.cmake b/cmake/ssl.cmake index 44f57899784..2bf9b561bba 100644 --- a/cmake/ssl.cmake +++ b/cmake/ssl.cmake @@ -30,8 +30,10 @@ MACRO (MYSQL_USE_BUNDLED_SSL) #Remove -fno-implicit-templates #(yassl sources cannot be compiled with it) SET(SAVE_CXX_FLAGS ${CXX_FLAGS}) + IF(CMAKE_CXX_FLAGS) STRING(REPLACE "-fno-implicit-templates" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + ENDIF() ADD_SUBDIRECTORY(extra/yassl) ADD_SUBDIRECTORY(extra/yassl/taocrypt) SET(CXX_FLAGS ${SAVE_CXX_FLAGS}) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index b32a66a6c92..28424bd3e50 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -105,8 +105,11 @@ MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) IF(NOT WITHOUT_DYNAMIC_PLUGINS) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) GET_TARGET_PROPERTY(mysqld_link_flags mysqld LINK_FLAGS) + IF(NOT mysqld_link_flags) + SET(mysqld_link_flags) + ENDIF() IF (MINGW OR CYGWIN) - SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "{mysqld_link_flags} -Wl,--export-all-symbols") + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} -Wl,--export-all-symbols") ENDIF() IF(MSVC) # Set module definition file. Also use non-incremental linker, From 3b311f399d996cd28b263d246ae27e9b1dc56840 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 29 Dec 2009 15:19:05 +0300 Subject: [PATCH 198/466] Apply and review: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3655 Jon Olav Hauglid 2009-10-19 Bug #30977 Concurrent statement using stored function and DROP FUNCTION breaks SBR Bug #48246 assert in close_thread_table Implement a fix for: Bug #41804 purge stored procedure cache causes mysterious hang for many minutes Bug #49972 Crash in prepared statements The problem was that concurrent execution of DML statements that use stored functions and DDL statements that drop/modify the same function might result in incorrect binary log in statement (and mixed) mode and therefore break replication. This patch fixes the problem by introducing metadata locking for stored procedures and functions. This is similar to what is done in Bug#25144 for views. Procedures and functions now are locked using metadata locks until the transaction is either committed or rolled back. This prevents other statements from modifying the procedure/function while it is being executed. This provides commit ordering - guaranteeing serializability across multiple transactions and thus fixes the reported binlog problem. Note that we do not take locks for top-level CALLs. This means that procedures called directly are not protected from changes by simultaneous DDL operations so they are executed at the state they had at the time of the CALL. By not taking locks for top-level CALLs, we still allow transactions to be started inside procedures. This patch also changes stored procedure cache invalidation. Upon a change of cache version, we no longer invalidate the entire cache, but only those routines which we use, only when a statement is executed that uses them. This patch also changes the logic of prepared statement validation. A stored procedure used by a prepared statement is now validated only once a metadata lock has been acquired. A version mismatch causes a flush of the obsolete routine from the cache and statement reprepare. Incompatible changes: 1) ER_LOCK_DEADLOCK is reported for a transaction trying to access a procedure/function that is locked by a DDL operation in another connection. 2) Procedure/function DDL operations are now prohibited in LOCK TABLES mode as exclusive locks must be taken all at once and LOCK TABLES provides no way to specifiy procedures/functions to be locked. Test cases have been added to sp-lock.test and rpl_sp.test. Work on this bug has very much been a team effort and this patch includes and is based on contributions from Davi Arnaut, Dmitry Lenev, Magne Mæhre and Konstantin Osipov. --- mysql-test/r/ps_ddl.result | 13 +- mysql-test/r/ps_ddl1.result | 2 +- mysql-test/r/sp-error.result | 2 +- mysql-test/r/sp-lock.result | 697 +++++++++++++++++++++ mysql-test/suite/rpl/r/rpl_sp.result | 45 +- mysql-test/suite/rpl/t/rpl_sp.test | 59 +- mysql-test/t/ps_ddl.test | 11 +- mysql-test/t/ps_ddl1.test | 2 +- mysql-test/t/sp-error.test | 2 +- mysql-test/t/sp-lock.test | 876 +++++++++++++++++++++++++++ sql/lock.cc | 51 ++ sql/mysql_priv.h | 3 + sql/sp.cc | 168 +++-- sql/sp.h | 18 +- sql/sp_cache.cc | 67 +- sql/sp_cache.h | 4 +- sql/sp_head.cc | 15 +- sql/sp_head.h | 30 +- sql/sql_base.cc | 239 ++++++-- sql/sql_class.h | 7 +- sql/sql_parse.cc | 166 ++--- sql/sql_prepare.cc | 50 +- sql/sql_show.cc | 1 - sql/sql_table.cc | 18 + sql/sql_update.cc | 4 - 25 files changed, 2243 insertions(+), 307 deletions(-) create mode 100644 mysql-test/r/sp-lock.result create mode 100644 mysql-test/t/sp-lock.test diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index c72d129c8e4..2ce43d363c3 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -269,8 +269,6 @@ Part 7: TABLE -> TABLE (TRIGGER dependencies) transitions ===================================================================== # Test 7-a: dependent PROCEDURE has changed # -# Note, this scenario is not supported, subject of Bug#12093 -# create table t1 (a int); create trigger t1_ai after insert on t1 for each row call p1(new.a); @@ -282,10 +280,9 @@ drop procedure p1; create procedure p1 (a int) begin end; set @var= 2; execute stmt using @var; -ERROR 42000: PROCEDURE test.p1 does not exist # Cleanup drop procedure p1; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); SUCCESS # Test 7-b: dependent FUNCTION has changed @@ -361,11 +358,13 @@ set @var=8; # XXX: bug, the SQL statement in the trigger is still # pointing at table 't3', since the view was expanded # at first statement execution. +# Since the view definition is inlined in the statement +# at prepare, changing the view definition does not cause +# repreparation. # Repreparation of the main statement doesn't cause repreparation # of trigger statements. execute stmt using @var; -ERROR 42S02: Table 'test.t3' doesn't exist -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS # @@ -382,6 +381,7 @@ select * from t3; a 6 7 +8 flush table t1; set @var=9; execute stmt using @var; @@ -396,6 +396,7 @@ select * from t3; a 6 7 +8 drop view v1; drop table t1,t2,t3; # Test 7-d: dependent TABLE has changed diff --git a/mysql-test/r/ps_ddl1.result b/mysql-test/r/ps_ddl1.result index e41a72ceb96..87abcd90590 100644 --- a/mysql-test/r/ps_ddl1.result +++ b/mysql-test/r/ps_ddl1.result @@ -460,7 +460,7 @@ create schema mysqltest; end| execute stmt; ERROR 42000: PROCEDURE test.p1 does not exist -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS execute stmt; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 034923bbd4f..4cefee95903 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -512,7 +512,7 @@ select * from t1; end| lock table t1 read| alter procedure bug9566 comment 'Some comment'| -ERROR HY000: Table 'proc' was not locked with LOCK TABLES +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables| drop procedure bug9566| drop procedure if exists bug7299| diff --git a/mysql-test/r/sp-lock.result b/mysql-test/r/sp-lock.result new file mode 100644 index 00000000000..65524d02d08 --- /dev/null +++ b/mysql-test/r/sp-lock.result @@ -0,0 +1,697 @@ +# +# Test coverage for changes performed by the fix +# for Bug#30977 "Concurrent statement using stored function +# and DROP FUNCTION breaks SBR. +# +# +# 1) Verify that the preceding transaction is +# (implicitly) committed before CREATE/ALTER/DROP +# PROCEDURE. Note, that this is already tested +# in implicit_commit.test, but here we use an alternative +# approach. +# +# Start a transaction, create a savepoint, +# then call a DDL operation on a procedure, and then check +# that the savepoint is no longer present. +drop table if exists t1; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; +drop function if exists f1; +create table t1 (a int); +# +# Test 'CREATE PROCEDURE'. +# +begin; +savepoint sv; +create procedure p1() begin end; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'ALTER PROCEDURE'. +# +begin; +savepoint sv; +alter procedure p1 comment 'changed comment'; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'DROP PROCEDURE'. +# +begin; +savepoint sv; +drop procedure p1; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'CREATE FUNCTION'. +# +begin; +savepoint sv; +create function f1() returns int return 1; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'ALTER FUNCTION'. +# +begin; +savepoint sv; +alter function f1 comment 'new comment'; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'DROP FUNCTION'. +# +begin; +savepoint sv; +drop function f1; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# 2) Verify that procedure DDL operations fail +# under lock tables. +# +# Auxiliary routines to test ALTER. +create procedure p1() begin end; +create function f1() returns int return 1; +lock table t1 write; +create procedure p2() begin end; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter procedure p1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +drop procedure p1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +create function f2() returns int return 1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter function f1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +lock table t1 read; +create procedure p2() begin end; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter procedure p1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +drop procedure p1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +create function f2() returns int return 1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter function f1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +# +# Even if we locked a temporary table. +# Todo: this is a restriction we could possibly lift. +# +drop table t1; +create temporary table t1 (a int); +lock table t1 read; +create procedure p2() begin end; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter procedure p1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +drop procedure p1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +create function f2() returns int return 1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter function f1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +drop function f1; +drop procedure p1; +drop temporary table t1; +# +# 3) Verify that CREATE/ALTER/DROP routine grab an +# exclusive lock. +# +# For that, start a transaction, use a routine. In a concurrent +# connection, try to drop or alter the routine. It should place +# a pending or exlusive lock and block. In a concurrnet +# connection, try to use the routine under LOCK TABLES. +# That should yield ER_LOCK_DEADLOCK. +# +# Establish helper connections. +# +# Test DROP PROCEDURE. +# +# --> connection default +create table t1 (a int); +create procedure p1() begin end; +create function f1() returns int +begin +call p1(); +return 1; +end| +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'drop procedure p1'... +drop procedure p1; +# --> connection con2 +# Waitng for 'drop procedure t1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'drop procedure p1'... +# --> connection default +# +# Test CREATE PROCEDURE. +# +create procedure p1() begin end; +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'create procedure p1'... +create procedure p1() begin end; +# --> connection con2 +# Waitng for 'create procedure t1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'create procedure p1'... +ERROR 42000: PROCEDURE p1 already exists +# +# Test ALTER PROCEDURE. +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter procedure p1'... +alter procedure p1 contains sql; +# --> connection con2 +# Waitng for 'alter procedure t1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'alter procedure p1'... +# --> connection default +# +# Test DROP FUNCTION. +# +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'drop function f1'... +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# Test CREATE FUNCTION. +# +create function f1() returns int return 1; +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'create function f1'... +create function f1() returns int return 2; +# --> connection con2 +# Waitng for 'create function f1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'create function f1'... +ERROR 42000: FUNCTION f1 already exists +# --> connection default +# +# Test ALTER FUNCTION. +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter function f1'... +alter function f1 contains sql; +# --> connection con2 +# Waitng for 'alter function f1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'alter function f1'... +# --> connection default +drop function f1; +drop procedure p1; +# +# 4) MDL lock should not be taken for +# unrolled CALL statements. +# The primary goal of metadata locks is a consistent binary log. +# When a call statement is unrolled, it doesn't get to the +# binary log, instead the statements that are contained +# in the procedure body do. This can nest to any level. +# +create procedure p1() begin end; +create procedure p2() begin end; +create procedure p3() +begin +call p1(); +call p1(); +call p2(); +end| +create procedure p4() +begin +call p1(); +call p1(); +call p2(); +call p2(); +call p3(); +end| +begin; +select * from t1; +a +savepoint sv; +call p4(); +# Prepared statement should not add any locks either. +prepare stmt from "call p4()"; +execute stmt; +execute stmt; +# --> connection con1 +drop procedure p1; +drop procedure p2; +drop procedure p3; +drop procedure p4; +# --> connection default +# This is to verify there was no implicit commit. +rollback to savepoint sv; +call p4(); +ERROR 42000: PROCEDURE test.p4 does not exist +commit; +drop table t1; +# +# 5) Locks should be taken on routines +# used indirectly by views or triggers. +# +# +# A function is used from a trigger. +# +create function f1() returns int return 1; +create table t1 (a int); +create table t2 (a int, b int); +create trigger t1_ai after insert on t1 for each row +insert into t2 (a, b) values (new.a, f1()); +begin; +insert into t1 (a) values (1); +# --> connection con1 +# Sending 'drop function f1' +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# A function is used from a view. +# +create function f1() returns int return 1; +create view v1 as select f1() as a; +begin; +select * from v1; +a +1 +# --> connection con1 +# Sending 'drop function f1' +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# A procedure is used from a function. +# +create function f1() returns int +begin +declare v_out int; +call p1(v_out); +return v_out; +end| +create procedure p1(out v_out int) set v_out=3; +begin; +select * from v1; +a +3 +# --> connection con1 +# Sending 'drop procedure p1' +drop procedure p1; +# --> connection con2 +# Waitng for 'drop procedure p1' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop procedure p1'... +# --> connection default +# +# Deep nesting: a function is used from a procedure used +# from a function used from a view used in a trigger. +# +create function f2() returns int return 4; +create procedure p1(out v_out int) set v_out=f2(); +drop trigger t1_ai; +create trigger t1_ai after insert on t1 for each row +insert into t2 (a, b) values (new.a, (select max(a) from v1)); +begin; +insert into t1 (a) values (3); +# --> connection con1 +# Sending 'drop function f2' +drop function f2; +# --> connection con2 +# Waitng for 'drop function f2' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop function f2'... +# --> connection default +drop view v1; +drop function f1; +drop procedure p1; +drop table t1, t2; +# +# 6) Check that ER_LOCK_DEADLOCK is reported if +# acquisition of a shared lock fails during a transaction or +# we need to back off to flush the sp cache. +# +# a) A back off due to a lock conflict. +# +create table t1 (a int); +create function f1() returns int return 6; +begin; +select f1(); +f1() +6 +# --> connection con1 +# Sending 'drop function f1'... +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +begin; +select * from t1; +a +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +commit; +# --> connection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# b) A back off to flush the cache. +# Sic: now this situation does not require a back off since we +# flush the cache on the fly. +# +create function f1() returns int return 7; +begin; +select * from t1; +a +select f1(); +f1() +7 +commit; +drop table t1; +drop function f1; +# +# 7) Demonstrate that under LOCK TABLES we accumulate locks +# on stored routines, and release metadata locks in +# ROLLBACK TO SAVEPOINT. That is done only for those stored +# routines that are not part of LOCK TABLES prelocking list. +# Those stored routines that are part of LOCK TABLES +# prelocking list are implicitly locked when entering +# LOCK TABLES, and ROLLBACK TO SAVEPOINT has no effect on +# them. +# +create function f1() returns varchar(20) return "f1()"; +create function f2() returns varchar(20) return "f2()"; +create view v1 as select f1() as a; +set @@session.autocommit=0; +lock table v1 read; +select * from v1; +a +f1() +savepoint sv; +select f2(); +f2() +f2() +# --> connection con1 +# Sending 'drop function f1'... +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# Sending 'drop function f2'... +drop function f2; +# --> connection default +# Waitng for 'drop function f2' to get blocked on MDL lock... +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop function f2'... +# --> connection default +unlock tables; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +drop function f1; +ERROR 42000: FUNCTION test.f1 does not exist +drop function f2; +ERROR 42000: FUNCTION test.f2 does not exist +drop view v1; +set @@session.autocommit=default; +# +# 8) Check the situation when we're preparing or executing a +# prepared statement, and as part of that try to flush the +# session sp cache. However, one of the procedures that +# needs a flush is in use. Verify that there is no infinite +# reprepare loop and no crash. +# +create function f1() returns int return 1; +# +# We just mention p1() in the body of f2() to make +# sure that p1() metadata is validated when validating +# 'select f2()'. +# Recursion is not allowed in stored functions, so +# an attempt to just invoke p1() from f2() which is in turn +# called from p1() would have given a run-time error. +# +create function f2() returns int +begin +if @var is null then +call p1(); +end if; +return 1; +end| +create procedure p1() +begin +select f1() into @var; +execute stmt; +end| +# --> connection con2 +prepare stmt from "select f2()"; +# --> connection default +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter function f1 ...'... +alter function f1 comment "comment"; +# --> connection con2 +# Waitng for 'alter function f1 ...' to get blocked on MDL lock... +# Sending 'call p1()'... +call p1(); +# Waitng for 'call p1()' to get blocked on MDL lock on f1... +# Let 'alter function f1 ...' go through... +commit; +# --> connection con1 +# Reaping 'alter function f1 ...' +# --> connection con2 +# Reaping 'call p1()'... +f2() +1 +deallocate prepare stmt; +# --> connection default +drop function f1; +drop function f2; +drop procedure p1; +# +# 9) Check the situation when a stored function is invoked +# from a stored procedure, and recursively invokes the +# stored procedure that is in use. But for the second +# invocation, a cache flush is requested. We can't +# flush the procedure that's in use, and are forced +# to use an old version. It is not a violation of +# consistency, since we unroll top-level calls. +# Just verify the code works. +# +create function f1() returns int return 1; +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter function f1 ...'... +alter function f1 comment "comment"; +# --> connection con2 +# Waitng for 'alter function f1 ...' to get blocked on MDL lock... +# +# We just mention p1() in the body of f2() to make +# sure that p1() is prelocked for f2(). +# Recursion is not allowed in stored functions, so +# an attempt to just invoke p1() from f2() which is in turn +# called from p1() would have given a run-time error. +# +create function f2() returns int +begin +if @var is null then +call p1(); +end if; +return 1; +end| +create procedure p1() +begin +select f1() into @var; +select f2() into @var; +end| +# Sending 'call p1()'... +call p1(); +# Waitng for 'call p1()' to get blocked on MDL lock on f1... +# Let 'alter function f1 ...' go through... +commit; +# --> connection con1 +# Reaping 'alter function f1 ...' +# --> connection con2 +# Reaping 'call p1()'... +# --> connection default +drop function f1; +drop function f2; +drop procedure p1; +# +# 10) A select from information_schema.routines now +# flushes the stored routines caches. Test that this +# does not remove from the cache a stored routine +# that is already prelocked. +# +create function f1() returns int return get_lock("30977", 100000); +create function f2() returns int return 2; +create function f3() returns varchar(255) +begin +declare res varchar(255); +declare c cursor for select routine_name from +information_schema.routines where routine_name='f1'; +select f1() into @var; +open c; +fetch c into res; +close c; +select f2() into @var; +return res; +end| +# --> connection con1 +select get_lock("30977", 0); +get_lock("30977", 0) +1 +# --> connection default +# Sending 'select f3()'... +select f3(); +# --> connection con1 +# Waitng for 'select f3()' to get blocked on the user level lock... +# Do something to change the cache version. +create function f4() returns int return 4; +drop function f4; +select release_lock("30977"); +release_lock("30977") +1 +# --> connection default +# Reaping 'select f3()'... +# Routine 'f2()' should exist and get executed successfully. +f3() +f1 +select @var; +@var +2 +drop function f1; +drop function f2; +drop function f3; +# 11) Check the situation when the connection is flushing the +# SP cache which contains a procedure that is being executed. +# +# Function f1() calls p1(). Procedure p1() has a DROP +# VIEW statement, which, we know, invalidates the routines cache. +# During cache flush p1() must not be flushed since it's in +# use. +# +create function f1() returns int +begin +call p1(); +return 1; +end| +create procedure p1() +begin +create view v1 as select 1; +drop view v1; +select f1() into @var; +set @exec_count=@exec_count+1; +end| +set @exec_count=0; +call p1(); +ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1 +select @exec_count; +@exec_count +0 +set @@session.max_sp_recursion_depth=5; +set @exec_count=0; +call p1(); +ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +select @exec_count; +@exec_count +0 +drop procedure p1; +drop function f1; +set @@session.max_sp_recursion_depth=default; +# --> connection con1 +# --> connection con2 +# --> connection default +# +# End of 5.5 tests +# diff --git a/mysql-test/suite/rpl/r/rpl_sp.result b/mysql-test/suite/rpl/r/rpl_sp.result index 33d8267ad91..ba748049ac1 100644 --- a/mysql-test/suite/rpl/r/rpl_sp.result +++ b/mysql-test/suite/rpl/r/rpl_sp.result @@ -977,4 +977,47 @@ drop procedure mysqltestbug36570_p1; drop procedure ` mysqltestbug36570_p2`; drop function mysqltestbug36570_f1; End of 5.0 tests -End of 5.1 tests +# End of 5.1 tests +# +# Test Bug#30977 Concurrent statement using stored +# function and DROP FUNCTION breaks SBR. +# +# Demonstrate that stored function DDL can not go through, +# or, worse yet, make its way into the binary log, while +# the stored function is in use. +# For that, try to insert a result of a stored function +# into a table. Block the insert in the beginning, waiting +# on a table lock. While insert is blocked, attempt to +# drop the routine. Verify that this attempt +# blocks and waits for INSERT to complete. Commit and +# reap the chain of events. Master and slave must contain +# identical data. Statements in the binrary log must be +# consistent with data in the table. +# +# --> connection default +drop table if exists t1, t2; +drop function if exists t1; +create table t1 (a int); +create table t2 (a int) as select 1 as a; +create function f1() returns int deterministic return (select max(a) from t2); +lock table t2 write; +# --> connection master +# Sending 'insert into t1 (a) values (f1())'... +insert into t1 (a) values (f1()); +# Waitng for 'insert into t1 ...' to get blocked on table lock... +# Sending 'drop function f1'. It will abort the table lock wait. +drop function f1; +# --> connection default +# Now let's let 'insert' go through... +unlock tables; +# --> connection con1 +# Reaping 'insert into t1 (a) values (f1())'... +ERROR 42000: FUNCTION test.f1 does not exist +select * from t1; +a +select * from t1; +a +drop table t1, t2; +drop function f1; +ERROR 42000: FUNCTION test.f1 does not exist +# End of 5.5 tests. diff --git a/mysql-test/suite/rpl/t/rpl_sp.test b/mysql-test/suite/rpl/t/rpl_sp.test index 96a41d9a9ad..231f0c6bcc0 100644 --- a/mysql-test/suite/rpl/t/rpl_sp.test +++ b/mysql-test/suite/rpl/t/rpl_sp.test @@ -621,7 +621,64 @@ drop procedure mysqltestbug36570_p1; drop procedure ` mysqltestbug36570_p2`; drop function mysqltestbug36570_f1; --echo End of 5.0 tests ---echo End of 5.1 tests +--echo # End of 5.1 tests +--echo # +--echo # Test Bug#30977 Concurrent statement using stored +--echo # function and DROP FUNCTION breaks SBR. +--echo # +--echo # Demonstrate that stored function DDL can not go through, +--echo # or, worse yet, make its way into the binary log, while +--echo # the stored function is in use. +--echo # For that, try to insert a result of a stored function +--echo # into a table. Block the insert in the beginning, waiting +--echo # on a table lock. While insert is blocked, attempt to +--echo # drop the routine. Verify that this attempt +--echo # blocks and waits for INSERT to complete. Commit and +--echo # reap the chain of events. Master and slave must contain +--echo # identical data. Statements in the binrary log must be +--echo # consistent with data in the table. +--echo # +--echo # --> connection default +connection default; +--disable_warnings +drop table if exists t1, t2; +drop function if exists t1; +--enable_warnings +create table t1 (a int); +create table t2 (a int) as select 1 as a; +create function f1() returns int deterministic return (select max(a) from t2); +lock table t2 write; +--echo # --> connection master +connection master; +--echo # Sending 'insert into t1 (a) values (f1())'... +--send insert into t1 (a) values (f1()) +connection master1; +--echo # Waitng for 'insert into t1 ...' to get blocked on table lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Table lock' and info='insert into t1 (a) values (f1())'; +--source include/wait_condition.inc +--echo # Sending 'drop function f1'. It will abort the table lock wait. +drop function f1; +--echo # --> connection default +connection default; +--echo # Now let's let 'insert' go through... +unlock tables; +--echo # --> connection con1 +connection master; +--echo # Reaping 'insert into t1 (a) values (f1())'... +--error ER_SP_DOES_NOT_EXIST +--reap +connection master1; +select * from t1; +connection slave; +select * from t1; +connection master; +drop table t1, t2; +--error ER_SP_DOES_NOT_EXIST +drop function f1; + +--echo # End of 5.5 tests. + # Cleanup sync_slave_with_master; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index e00d63aaedc..6a2b49ac9b7 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -278,8 +278,6 @@ deallocate prepare stmt; --echo # Test 7-a: dependent PROCEDURE has changed --echo # ---echo # Note, this scenario is not supported, subject of Bug#12093 ---echo # create table t1 (a int); create trigger t1_ai after insert on t1 for each row @@ -291,11 +289,10 @@ execute stmt using @var; drop procedure p1; create procedure p1 (a int) begin end; set @var= 2; ---error ER_SP_DOES_NOT_EXIST execute stmt using @var; --echo # Cleanup drop procedure p1; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); --echo # Test 7-b: dependent FUNCTION has changed --echo # @@ -355,11 +352,13 @@ set @var=8; --echo # XXX: bug, the SQL statement in the trigger is still --echo # pointing at table 't3', since the view was expanded --echo # at first statement execution. +--echo # Since the view definition is inlined in the statement +--echo # at prepare, changing the view definition does not cause +--echo # repreparation. --echo # Repreparation of the main statement doesn't cause repreparation --echo # of trigger statements. ---error ER_NO_SUCH_TABLE execute stmt using @var; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); --echo # --echo # Sic: the insert went into t3, even though the view now --echo # points at t2. This is because neither the merged view diff --git a/mysql-test/t/ps_ddl1.test b/mysql-test/t/ps_ddl1.test index 379ed576b5f..0145d445a14 100644 --- a/mysql-test/t/ps_ddl1.test +++ b/mysql-test/t/ps_ddl1.test @@ -363,7 +363,7 @@ end| delimiter ;| --error ER_SP_DOES_NOT_EXIST execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); --error ER_SP_DOES_NOT_EXIST execute stmt; call p_verify_reprepare_count(0); diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 4df1118cd56..c8b2595e23d 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -723,7 +723,7 @@ lock table t1 read| # This should fail since we forgot to lock mysql.proc for writing # explicitly, and we can't open mysql.proc for _writing_ if there # are locked tables. ---error 1100 +--error ER_LOCK_OR_ACTIVE_TRANSACTION alter procedure bug9566 comment 'Some comment'| unlock tables| # This should succeed diff --git a/mysql-test/t/sp-lock.test b/mysql-test/t/sp-lock.test new file mode 100644 index 00000000000..a90b85a59be --- /dev/null +++ b/mysql-test/t/sp-lock.test @@ -0,0 +1,876 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# +# Metadata lock handling for stored procedures and +# functions. +# +--echo # +--echo # Test coverage for changes performed by the fix +--echo # for Bug#30977 "Concurrent statement using stored function +--echo # and DROP FUNCTION breaks SBR. +--echo # + +--echo # +--echo # 1) Verify that the preceding transaction is +--echo # (implicitly) committed before CREATE/ALTER/DROP +--echo # PROCEDURE. Note, that this is already tested +--echo # in implicit_commit.test, but here we use an alternative +--echo # approach. +--echo # + +--echo # Start a transaction, create a savepoint, +--echo # then call a DDL operation on a procedure, and then check +--echo # that the savepoint is no longer present. + +--disable_warnings +drop table if exists t1; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; +drop function if exists f1; +--enable_warnings +create table t1 (a int); +--echo # +--echo # Test 'CREATE PROCEDURE'. +--echo # +begin; +savepoint sv; +create procedure p1() begin end; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'ALTER PROCEDURE'. +--echo # +begin; +savepoint sv; +alter procedure p1 comment 'changed comment'; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'DROP PROCEDURE'. +--echo # +begin; +savepoint sv; +drop procedure p1; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'CREATE FUNCTION'. +--echo # +begin; +savepoint sv; +create function f1() returns int return 1; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'ALTER FUNCTION'. +--echo # +begin; +savepoint sv; +alter function f1 comment 'new comment'; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'DROP FUNCTION'. +--echo # +begin; +savepoint sv; +drop function f1; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; + +--echo # +--echo # 2) Verify that procedure DDL operations fail +--echo # under lock tables. +--echo # +--echo # Auxiliary routines to test ALTER. +create procedure p1() begin end; +create function f1() returns int return 1; + +lock table t1 write; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create procedure p2() begin end; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter procedure p1 comment 'changed comment'; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +drop procedure p1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create function f2() returns int return 1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter function f1 comment 'changed comment'; +lock table t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create procedure p2() begin end; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter procedure p1 comment 'changed comment'; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +drop procedure p1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create function f2() returns int return 1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter function f1 comment 'changed comment'; +unlock tables; +--echo # +--echo # Even if we locked a temporary table. +--echo # Todo: this is a restriction we could possibly lift. +--echo # +drop table t1; +create temporary table t1 (a int); +lock table t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create procedure p2() begin end; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter procedure p1 comment 'changed comment'; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +drop procedure p1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create function f2() returns int return 1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter function f1 comment 'changed comment'; +unlock tables; + +drop function f1; +drop procedure p1; +drop temporary table t1; + +--echo # +--echo # 3) Verify that CREATE/ALTER/DROP routine grab an +--echo # exclusive lock. +--echo # +--echo # For that, start a transaction, use a routine. In a concurrent +--echo # connection, try to drop or alter the routine. It should place +--echo # a pending or exlusive lock and block. In a concurrnet +--echo # connection, try to use the routine under LOCK TABLES. +--echo # That should yield ER_LOCK_DEADLOCK. +--echo # +--echo # Establish helper connections. +connect(con1, localhost, root,,); +connect(con2, localhost, root,,); + +--echo # +--echo # Test DROP PROCEDURE. +--echo # +--echo # --> connection default +connection default; +create table t1 (a int); +create procedure p1() begin end; +delimiter |; +create function f1() returns int +begin + call p1(); + return 1; +end| +delimiter ;| +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop procedure p1'... +--send drop procedure p1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop procedure t1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop procedure p1'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop procedure p1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Test CREATE PROCEDURE. +--echo # +create procedure p1() begin end; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'create procedure p1'... +--send create procedure p1() begin end +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'create procedure t1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='create procedure p1() begin end'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'create procedure p1'... +--error ER_SP_ALREADY_EXISTS +--reap +connection default; +--echo # +--echo # Test ALTER PROCEDURE. +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter procedure p1'... +--send alter procedure p1 contains sql +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter procedure t1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='alter procedure p1 contains sql'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter procedure p1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Test DROP FUNCTION. +--echo # +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1'... +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Test CREATE FUNCTION. +--echo # +create function f1() returns int return 1; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'create function f1'... +--send create function f1() returns int return 2 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'create function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='create function f1() returns int return 2'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'create function f1'... +--error ER_SP_ALREADY_EXISTS +--reap +--echo # --> connection default +connection default; +--echo # +--echo # Test ALTER FUNCTION. +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter function f1'... +--send alter function f1 contains sql +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='alter function f1 contains sql'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter function f1'... +--reap +--echo # --> connection default +connection default; +drop function f1; +drop procedure p1; + +--echo # +--echo # 4) MDL lock should not be taken for +--echo # unrolled CALL statements. +--echo # The primary goal of metadata locks is a consistent binary log. +--echo # When a call statement is unrolled, it doesn't get to the +--echo # binary log, instead the statements that are contained +--echo # in the procedure body do. This can nest to any level. +--echo # +create procedure p1() begin end; +create procedure p2() begin end; +delimiter |; +create procedure p3() +begin + call p1(); + call p1(); + call p2(); +end| +create procedure p4() +begin + call p1(); + call p1(); + call p2(); + call p2(); + call p3(); +end| +delimiter ;| +begin; +select * from t1; +savepoint sv; +call p4(); +--echo # Prepared statement should not add any locks either. +prepare stmt from "call p4()"; +execute stmt; +execute stmt; +--echo # --> connection con1 +connection con1; +drop procedure p1; +drop procedure p2; +drop procedure p3; +drop procedure p4; +--echo # --> connection default +connection default; +--echo # This is to verify there was no implicit commit. +rollback to savepoint sv; +--error ER_SP_DOES_NOT_EXIST +call p4(); +commit; +drop table t1; + +--echo # +--echo # 5) Locks should be taken on routines +--echo # used indirectly by views or triggers. +--echo # +--echo # +--echo # A function is used from a trigger. +--echo # +create function f1() returns int return 1; +create table t1 (a int); +create table t2 (a int, b int); +create trigger t1_ai after insert on t1 for each row + insert into t2 (a, b) values (new.a, f1()); +begin; +insert into t1 (a) values (1); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1' +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # A function is used from a view. +--echo # +create function f1() returns int return 1; +create view v1 as select f1() as a; +begin; +select * from v1; +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1' +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # A procedure is used from a function. +--echo # +delimiter |; +create function f1() returns int +begin + declare v_out int; + call p1(v_out); + return v_out; +end| +delimiter ;| +create procedure p1(out v_out int) set v_out=3; +begin; +select * from v1; +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop procedure p1' +--send drop procedure p1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop procedure p1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop procedure p1'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop procedure p1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Deep nesting: a function is used from a procedure used +--echo # from a function used from a view used in a trigger. +--echo # +create function f2() returns int return 4; +create procedure p1(out v_out int) set v_out=f2(); +drop trigger t1_ai; +create trigger t1_ai after insert on t1 for each row + insert into t2 (a, b) values (new.a, (select max(a) from v1)); +begin; +insert into t1 (a) values (3); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f2' +--send drop function f2 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f2' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f2'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f2'... +--reap +--echo # --> connection default +connection default; + +drop view v1; +drop function f1; +drop procedure p1; +drop table t1, t2; + +--echo # +--echo # 6) Check that ER_LOCK_DEADLOCK is reported if +--echo # acquisition of a shared lock fails during a transaction or +--echo # we need to back off to flush the sp cache. +--echo # +--echo # a) A back off due to a lock conflict. +--echo # +create table t1 (a int); +create function f1() returns int return 6; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1'... +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +begin; +select * from t1; +--error ER_LOCK_DEADLOCK +select f1(); +commit; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # b) A back off to flush the cache. +--echo # Sic: now this situation does not require a back off since we +--echo # flush the cache on the fly. +--echo # +create function f1() returns int return 7; +begin; +select * from t1; +# Used to have a back-off here, with optional ER_LOCK_DEADLOCK +#--error ER_LOCK_DEADLOCK +select f1(); +commit; +drop table t1; +drop function f1; + +--echo # +--echo # 7) Demonstrate that under LOCK TABLES we accumulate locks +--echo # on stored routines, and release metadata locks in +--echo # ROLLBACK TO SAVEPOINT. That is done only for those stored +--echo # routines that are not part of LOCK TABLES prelocking list. +--echo # Those stored routines that are part of LOCK TABLES +--echo # prelocking list are implicitly locked when entering +--echo # LOCK TABLES, and ROLLBACK TO SAVEPOINT has no effect on +--echo # them. +--echo # +create function f1() returns varchar(20) return "f1()"; +create function f2() returns varchar(20) return "f2()"; +create view v1 as select f1() as a; +set @@session.autocommit=0; +lock table v1 read; +select * from v1; +savepoint sv; +select f2(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1'... +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # Sending 'drop function f2'... +--send drop function f2 +--echo # --> connection default +connection default; +--echo # Waitng for 'drop function f2' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f2'; +--source include/wait_condition.inc +rollback to savepoint sv; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop function f2'... +--reap +--echo # --> connection default +connection default; +unlock tables; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--error ER_SP_DOES_NOT_EXIST +drop function f1; +--error ER_SP_DOES_NOT_EXIST +drop function f2; +drop view v1; +set @@session.autocommit=default; + +--echo # +--echo # 8) Check the situation when we're preparing or executing a +--echo # prepared statement, and as part of that try to flush the +--echo # session sp cache. However, one of the procedures that +--echo # needs a flush is in use. Verify that there is no infinite +--echo # reprepare loop and no crash. +--echo # +create function f1() returns int return 1; +delimiter |; +--echo # +--echo # We just mention p1() in the body of f2() to make +--echo # sure that p1() metadata is validated when validating +--echo # 'select f2()'. +--echo # Recursion is not allowed in stored functions, so +--echo # an attempt to just invoke p1() from f2() which is in turn +--echo # called from p1() would have given a run-time error. +--echo # +create function f2() returns int +begin + if @var is null then + call p1(); + end if; + return 1; +end| +create procedure p1() +begin + select f1() into @var; + execute stmt; +end| +delimiter ;| +--echo # --> connection con2 +connection con2; +prepare stmt from "select f2()"; +--echo # --> connection default +connection default; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter function f1 ...'... +--send alter function f1 comment "comment" +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info like 'alter function f1 comment%'; +--source include/wait_condition.inc +--echo # Sending 'call p1()'... +--send call p1() +connection default; +--echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1() into @var'; +--source include/wait_condition.inc +--echo # Let 'alter function f1 ...' go through... +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter function f1 ...' +--reap +--echo # --> connection con2 +connection con2; +--echo # Reaping 'call p1()'... +--reap +deallocate prepare stmt; +--echo # --> connection default +connection default; +drop function f1; +drop function f2; +drop procedure p1; + +--echo # +--echo # 9) Check the situation when a stored function is invoked +--echo # from a stored procedure, and recursively invokes the +--echo # stored procedure that is in use. But for the second +--echo # invocation, a cache flush is requested. We can't +--echo # flush the procedure that's in use, and are forced +--echo # to use an old version. It is not a violation of +--echo # consistency, since we unroll top-level calls. +--echo # Just verify the code works. +--echo # +create function f1() returns int return 1; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter function f1 ...'... +--send alter function f1 comment "comment" +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info like 'alter function f1 comment%'; +--source include/wait_condition.inc +delimiter |; +--echo # +--echo # We just mention p1() in the body of f2() to make +--echo # sure that p1() is prelocked for f2(). +--echo # Recursion is not allowed in stored functions, so +--echo # an attempt to just invoke p1() from f2() which is in turn +--echo # called from p1() would have given a run-time error. +--echo # +create function f2() returns int +begin + if @var is null then + call p1(); + end if; + return 1; +end| +create procedure p1() +begin + select f1() into @var; + select f2() into @var; +end| +delimiter ;| +--echo # Sending 'call p1()'... +--send call p1() +connection default; +--echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1() into @var'; +--source include/wait_condition.inc +--echo # Let 'alter function f1 ...' go through... +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter function f1 ...' +--reap +--echo # --> connection con2 +connection con2; +--echo # Reaping 'call p1()'... +--reap +--echo # --> connection default +connection default; +drop function f1; +drop function f2; +drop procedure p1; + +--echo # +--echo # 10) A select from information_schema.routines now +--echo # flushes the stored routines caches. Test that this +--echo # does not remove from the cache a stored routine +--echo # that is already prelocked. +--echo # +create function f1() returns int return get_lock("30977", 100000); +create function f2() returns int return 2; +delimiter |; +create function f3() returns varchar(255) +begin + declare res varchar(255); + declare c cursor for select routine_name from + information_schema.routines where routine_name='f1'; + select f1() into @var; + open c; + fetch c into res; + close c; + select f2() into @var; + return res; +end| +delimiter ;| +--echo # --> connection con1 +connection con1; +select get_lock("30977", 0); +--echo # --> connection default +connection default; +--echo # Sending 'select f3()'... +--send select f3() +--echo # --> connection con1 +connection con1; +--echo # Waitng for 'select f3()' to get blocked on the user level lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='User lock' and info='select f1() into @var'; +--source include/wait_condition.inc +--echo # Do something to change the cache version. +create function f4() returns int return 4; +drop function f4; +select release_lock("30977"); +--echo # --> connection default +connection default; +--echo # Reaping 'select f3()'... +--echo # Routine 'f2()' should exist and get executed successfully. +--reap +select @var; +drop function f1; +drop function f2; +drop function f3; + + +--echo # 11) Check the situation when the connection is flushing the +--echo # SP cache which contains a procedure that is being executed. +--echo # +--echo # Function f1() calls p1(). Procedure p1() has a DROP +--echo # VIEW statement, which, we know, invalidates the routines cache. +--echo # During cache flush p1() must not be flushed since it's in +--echo # use. +--echo # +delimiter |; +create function f1() returns int +begin + call p1(); + return 1; +end| +create procedure p1() +begin + create view v1 as select 1; + drop view v1; + select f1() into @var; + set @exec_count=@exec_count+1; +end| +delimiter ;| +set @exec_count=0; +--error ER_SP_RECURSION_LIMIT +call p1(); +select @exec_count; +set @@session.max_sp_recursion_depth=5; +set @exec_count=0; +--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG +call p1(); +select @exec_count; +drop procedure p1; +drop function f1; +set @@session.max_sp_recursion_depth=default; + +--echo # --> connection con1 +connection con1; +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # --> connection con2 +connection con2; +disconnect con2; +--source include/wait_until_disconnected.inc +--echo # --> connection default +connection default; +--echo # +--echo # End of 5.5 tests +--echo # diff --git a/sql/lock.cc b/sql/lock.cc index 31773585bff..6cdf2e4a202 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -980,6 +980,57 @@ void unlock_table_names(THD *thd) } +/** + Obtain an exclusive metadata lock on the stored routine name. + + @param thd Thread handle. + @param is_function Stored routine type (only functions or procedures + are name-locked. + @param db The schema the routine belongs to. + @param name Routine name. + + This function assumes that no metadata locks were acquired + before calling it. Additionally, it cannot be called while + holding LOCK_open mutex. Both these invariants are enforced by + asserts in MDL_context::acquire_exclusive_locks(). + To avoid deadlocks, we do not try to obtain exclusive metadata + locks in LOCK TABLES mode, since in this mode there may be + other metadata locks already taken by the current connection, + and we must not wait for MDL locks while holding locks. + + @retval FALSE Success. + @retval TRUE Failure: we're in LOCK TABLES mode, or out of memory, + or this connection was killed. +*/ + +bool lock_routine_name(THD *thd, bool is_function, + const char *db, const char *name) +{ + MDL_key::enum_mdl_namespace mdl_type= (is_function ? + MDL_key::FUNCTION : + MDL_key::PROCEDURE); + MDL_request mdl_request; + + if (thd->locked_tables_mode) + { + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); + return TRUE; + } + + DBUG_ASSERT(name); + DEBUG_SYNC(thd, "before_wait_locked_pname"); + + mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE); + + if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) + return TRUE; + + DEBUG_SYNC(thd, "after_wait_locked_pname"); + return FALSE; +} + + static void print_lock_error(int error, const char *table) { int textno; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index caf3130c517..124392f4c63 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2111,6 +2111,9 @@ void broadcast_refresh(void); bool lock_table_names(THD *thd, TABLE_LIST *table_list); void unlock_table_names(THD *thd); +/* Lock based on stored routine name */ +bool lock_routine_name(THD *thd, bool is_function, const char *db, + const char *name); /* old unireg functions */ diff --git a/sql/sp.cc b/sql/sp.cc index 279f0d44890..1375d44cb9b 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -753,6 +753,11 @@ sp_create_routine(THD *thd, int type, sp_head *sp) */ thd->clear_current_stmt_binlog_row_based(); + /* Grab an exclusive MDL lock. */ + if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, + sp->m_db.str, sp->m_name.str)) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + saved_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_WARN; @@ -919,7 +924,10 @@ sp_create_routine(THD *thd, int type, sp_head *sp) ret= SP_OK; if (table->file->ha_write_row(table->record[0])) ret= SP_WRITE_ROW_FAILED; - else if (mysql_bin_log.is_open()) + if (ret == SP_OK) + sp_cache_invalidate(); + + if (ret == SP_OK && mysql_bin_log.is_open()) { thd->clear_error(); @@ -948,7 +956,6 @@ sp_create_routine(THD *thd, int type, sp_head *sp) FALSE, FALSE, 0); thd->variables.sql_mode= 0; } - } done: @@ -994,6 +1001,11 @@ sp_drop_routine(THD *thd, int type, sp_name *name) */ thd->clear_current_stmt_binlog_row_based(); + /* Grab an exclusive MDL lock. */ + if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, + name->m_db.str, name->m_name.str)) + DBUG_RETURN(SP_DELETE_ROW_FAILED); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -1006,6 +1018,20 @@ sp_drop_routine(THD *thd, int type, sp_name *name) { write_bin_log(thd, TRUE, thd->query(), thd->query_length()); sp_cache_invalidate(); + + /* + A lame workaround for lack of cache flush: + make sure the routine is at least gone from the + local cache. + */ + { + sp_head *sp; + sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? + &thd->sp_func_cache : &thd->sp_proc_cache); + sp= sp_cache_lookup(spc, name); + if (sp) + sp_cache_flush_obsolete(spc, &sp); + } } close_thread_tables(thd); @@ -1041,6 +1067,12 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || type == TYPE_ENUM_FUNCTION); + + /* Grab an exclusive MDL lock. */ + if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, + name->m_db.str, name->m_name.str)) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the @@ -1052,6 +1084,30 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) { + if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators && + mysql_bin_log.is_open() && + (chistics->daccess == SP_CONTAINS_SQL || + chistics->daccess == SP_MODIFIES_SQL_DATA)) + { + char *ptr; + bool is_deterministic; + ptr= get_field(thd->mem_root, + table->field[MYSQL_PROC_FIELD_DETERMINISTIC]); + if (ptr == NULL) + { + ret= SP_INTERNAL_ERROR; + goto err; + } + is_deterministic= ptr[0] == 'N' ? FALSE : TRUE; + if (!is_deterministic) + { + my_message(ER_BINLOG_UNSAFE_ROUTINE, + ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0)); + ret= SP_INTERNAL_ERROR; + goto err; + } + } + store_record(table,record[1]); table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); @@ -1077,7 +1133,7 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) write_bin_log(thd, TRUE, thd->query(), thd->query_length()); sp_cache_invalidate(); } - +err: close_thread_tables(thd); DBUG_RETURN(ret); } @@ -1161,10 +1217,7 @@ err: bool sp_show_create_routine(THD *thd, int type, sp_name *name) { - bool err_status= TRUE; sp_head *sp; - sp_cache **cache = type == TYPE_ENUM_PROCEDURE ? - &thd->sp_proc_cache : &thd->sp_func_cache; DBUG_ENTER("sp_show_create_routine"); DBUG_PRINT("enter", ("name: %.*s", @@ -1174,28 +1227,29 @@ sp_show_create_routine(THD *thd, int type, sp_name *name) DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || type == TYPE_ENUM_FUNCTION); - if (type == TYPE_ENUM_PROCEDURE) + /* + @todo: Consider using prelocking for this code as well. Currently + SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data + dictionary, i.e. takes no metadata locks. + It is "safe" to do as long as it doesn't affect the results + of the binary log or the query cache, which currently it does not. + */ + if (sp_cache_routine(thd, type, name, FALSE, &sp)) + DBUG_RETURN(TRUE); + + if (sp == NULL || sp->show_create_routine(thd, type)) { /* - SHOW CREATE PROCEDURE may require two instances of one sp_head - object when SHOW CREATE PROCEDURE is called for the procedure that - is being executed. Basically, there is no actual recursion, so we - increase the recursion limit for this statement (kind of hack). - - SHOW CREATE FUNCTION does not require this because SHOW CREATE - statements are prohibitted within stored functions. - */ - - thd->variables.max_sp_recursion_depth++; + If we have insufficient privileges, pretend the routine + does not exist. + */ + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), + type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE", + name->m_name.str); + DBUG_RETURN(TRUE); } - if ((sp= sp_find_routine(thd, type, name, cache, FALSE))) - err_status= sp->show_create_routine(thd, type); - - if (type == TYPE_ENUM_PROCEDURE) - thd->variables.max_sp_recursion_depth--; - - DBUG_RETURN(err_status); + DBUG_RETURN(FALSE); } @@ -1451,6 +1505,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn); prelocking_ctx->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); rn->belong_to_view= belong_to_view; + rn->m_sp_cache_version= 0; return TRUE; } return FALSE; @@ -1596,41 +1651,81 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, } +/** + A helper wrapper around sp_cache_routine() to use from + prelocking until 'sp_name' is eradicated as a class. +*/ + +int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, + bool lookup_only, sp_head **sp) +{ + char qname_buff[NAME_LEN*2+1+1]; + sp_name name(&rt->mdl_request.key, qname_buff); + MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace(); + int type= ((mdl_type == MDL_key::FUNCTION) ? + TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE); + + /* + Check that we have an MDL lock on this routine, unless it's a top-level + CALL. The assert below should be unambiguous: the first element + in sroutines_list has an MDL lock unless it's a top-level call, or a + trigger, but triggers can't occur here (see the preceding assert). + */ + DBUG_ASSERT(rt->mdl_request.ticket || + rt == (Sroutine_hash_entry*) thd->lex->sroutines_list.first); + + return sp_cache_routine(thd, type, &name, lookup_only, sp); +} + + /** Ensure that routine is present in cache by loading it from the mysql.proc - table if needed. Emit an appropriate error if there was a problem during + table if needed. If the routine is present but old, reload it. + Emit an appropriate error if there was a problem during loading. @param[in] thd Thread context. @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE). @param[in] name Name of routine. + @param[in] lookup_only Only check that the routine is in the cache. + If it's not, don't try to load. If it is present, + but old, don't try to reload. @param[out] sp Pointer to sp_head object for routine, NULL if routine was - not found, + not found. @retval 0 Either routine is found and was succesfully loaded into cache or it does not exist. @retval non-0 Error while loading routine from mysql,proc table. */ -int sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp) +int sp_cache_routine(THD *thd, int type, sp_name *name, + bool lookup_only, sp_head **sp) { int ret= 0; + sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? + &thd->sp_func_cache : &thd->sp_proc_cache); DBUG_ENTER("sp_cache_routine"); DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); - if (!(*sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache), - name))) + + *sp= sp_cache_lookup(spc, name); + + if (lookup_only) + DBUG_RETURN(SP_OK); + + if (*sp) + { + sp_cache_flush_obsolete(spc, sp); + if (*sp) + DBUG_RETURN(SP_OK); + } + + switch ((ret= db_find_routine(thd, type, name, sp))) { - switch ((ret= db_find_routine(thd, type, name, sp))) - { case SP_OK: - if (type == TYPE_ENUM_FUNCTION) - sp_cache_insert(&thd->sp_func_cache, *sp); - else - sp_cache_insert(&thd->sp_proc_cache, *sp); + sp_cache_insert(spc, *sp); break; case SP_KEY_NOT_FOUND: ret= SP_OK; @@ -1669,7 +1764,6 @@ int sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp) my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); } break; - } } DBUG_RETURN(ret); } diff --git a/sql/sp.h b/sql/sp.h index a66d72d0e9e..cab4d7dbeb5 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -43,7 +43,13 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, bool cache_only); int -sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp); +sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, + bool lookup_only, sp_head **sp); + + +int +sp_cache_routine(THD *thd, int type, sp_name *name, + bool lookup_only, sp_head **sp); bool sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any); @@ -88,6 +94,16 @@ public: statement uses routine both via view and directly. */ TABLE_LIST *belong_to_view; + /** + This is for prepared statement validation purposes. + A statement looks up and pre-loads all its stored functions + at prepare. Later on, if a function is gone from the cache, + execute may fail. + Remember the version of sp_head at prepare to be able to + invalidate the prepared statement at execute if it + changes. + */ + ulong m_sp_cache_version; }; diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index d9a23d2be4e..bb962ad0300 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -31,8 +31,6 @@ static ulong volatile Cversion= 0; class sp_cache { public: - ulong version; - sp_cache(); ~sp_cache(); @@ -48,25 +46,10 @@ public: namelen); } -#ifdef NOT_USED - inline bool remove(char *name, uint namelen) + inline void remove(sp_head *sp) { - sp_head *sp= lookup(name, namelen); - if (sp) - { - hash_delete(&m_hashtable, (uchar *)sp); - return TRUE; - } - return FALSE; + my_hash_delete(&m_hashtable, (uchar *)sp); } -#endif - - inline void remove_all() - { - cleanup(); - init(); - } - private: void init(); void cleanup(); @@ -129,8 +112,9 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp) { if (!(c= new sp_cache())) return; // End of memory error - c->version= Cversion; // No need to lock when reading long variable } + /* Reading a ulong variable with no lock. */ + sp->set_sp_cache_version(Cversion); DBUG_PRINT("info",("sp_cache: inserting: %.*s", (int) sp->m_qname.length, sp->m_qname.str)); c->insert(sp); @@ -181,46 +165,34 @@ void sp_cache_invalidate() } -/* - Remove out-of-date SPs from the cache. - - SYNOPSIS - sp_cache_flush_obsolete() - cp Cache to flush +/** + Remove an out-of-date SP from the cache. - NOTE - This invalidates pointers to sp_head objects this thread uses. - In practice that means 'dont call this function when inside SP'. + @param[in] cp Cache to flush + @param[in] sp SP to remove. + + @note This invalidates pointers to sp_head objects this thread + uses. In practice that means 'dont call this function when + inside SP'. */ -void sp_cache_flush_obsolete(sp_cache **cp) +void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp) { - sp_cache *c= *cp; - if (c) + if ((*sp)->sp_cache_version() < Cversion && !(*sp)->is_invoked()) { - ulong v; - v= Cversion; // No need to lock when reading long variable - if (c->version < v) - { - DBUG_PRINT("info",("sp_cache: deleting all functions")); - /* We need to delete all elements. */ - c->remove_all(); - c->version= v; - } + (*cp)->remove(*sp); + *sp= NULL; } } /** - Return the current version of the cache. + Return the current global version of the cache. */ -ulong sp_cache_version(sp_cache **cp) +ulong sp_cache_version() { - sp_cache *c= *cp; - if (c) - return c->version; - return 0; + return Cversion; } @@ -265,7 +237,6 @@ sp_cache::init() { my_hash_init(&m_hashtable, system_charset_info, 0, 0, 0, hash_get_key_for_sp_head, hash_free_sp_head, 0); - version= 0; } diff --git a/sql/sp_cache.h b/sql/sp_cache.h index f4d44a1f29f..7dbb0d5429c 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -57,7 +57,7 @@ void sp_cache_clear(sp_cache **cp); void sp_cache_insert(sp_cache **cp, sp_head *sp); sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); void sp_cache_invalidate(); -void sp_cache_flush_obsolete(sp_cache **cp); -ulong sp_cache_version(sp_cache **cp); +void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp); +ulong sp_cache_version(); #endif /* _SP_CACHE_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index da99d9e0f6a..4f5ca1fff04 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -511,7 +511,10 @@ sp_head::operator delete(void *ptr, size_t size) throw() sp_head::sp_head() :Query_arena(&main_mem_root, INITIALIZED_FOR_SP), - m_flags(0), m_recursion_level(0), m_next_cached_sp(0), + m_flags(0), + m_sp_cache_version(0), + m_recursion_level(0), + m_next_cached_sp(0), m_cont_level(0) { const LEX_STRING str_reset= { NULL, 0 }; @@ -727,16 +730,6 @@ create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List *src) } -int -sp_head::create(THD *thd) -{ - DBUG_ENTER("sp_head::create"); - DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s", - m_type, m_name.str, m_params.str, m_body.str)); - - DBUG_RETURN(sp_create_routine(thd, m_type, this)); -} - sp_head::~sp_head() { DBUG_ENTER("sp_head::~sp_head"); diff --git a/sql/sp_head.h b/sql/sp_head.h index 74fcd03180e..bac97a1fd77 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -175,7 +175,34 @@ public: LEX_STRING m_definer_user; LEX_STRING m_definer_host; + /** + Is this routine being executed? + */ + bool is_invoked() const { return m_flags & IS_INVOKED; } + + /** + Get the value of the SP cache version, as remembered + when the routine was inserted into the cache. + */ + ulong sp_cache_version() const { return m_sp_cache_version; } + + /** Set the value of the SP cache version. */ + void set_sp_cache_version(ulong version_arg) + { + m_sp_cache_version= version_arg; + } private: + /** + Version of the stored routine cache at the moment when the + routine was added to it. Is used only for functions and + procedures, not used for triggers or events. When sp_head is + created, its version is 0. When it's added to the cache, the + version is assigned the global value 'Cversion'. + If later on Cversion is incremented, we know that the routine + is obsolete and should not be used -- + sp_cache_flush_obsolete() will purge it. + */ + ulong m_sp_cache_version; Stored_program_creation_ctx *m_creation_ctx; public: @@ -263,9 +290,6 @@ public: void set_stmt_end(THD *thd); - int - create(THD *thd); - virtual ~sp_head(); /// Free memory diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 459ca646d8c..78bb9f9bad7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -21,6 +21,7 @@ #include "sql_select.h" #include "sp_head.h" #include "sp.h" +#include "sp_cache.h" #include "sql_trigger.h" #include "transaction.h" #include "sql_prepare.h" @@ -3479,6 +3480,66 @@ check_and_update_table_version(THD *thd, } +/** + Compares versions of a stored routine obtained from the sp cache + and the version used at prepare. + + @details If the new and the old values mismatch, invoke + Metadata_version_observer. + At prepared statement prepare, all Sroutine_hash_entry version values + are NULL and we always have a mismatch. But there is no observer set + in THD, and therefore no error is reported. Instead, we update + the value in Sroutine_hash_entry, effectively recording the original + version. + At prepared statement execute, an observer may be installed. If + there is a version mismatch, we push an error and return TRUE. + + For conventional execution (no prepared statements), the + observer is never installed. + + @param[in] thd used to report errors + @param[in/out] rt pointer to stored routine entry in the + parse tree + @param[in] sp pointer to stored routine cache entry. + Can be NULL if there is no such routine. + @retval TRUE an error, which has been reported + @retval FALSE success, version in Sroutine_hash_entry has been updated +*/ + +static bool +check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt, + sp_head *sp) +{ + ulong spc_version= sp_cache_version(); + /* sp is NULL if there is no such routine. */ + ulong version= sp ? sp->sp_cache_version() : spc_version; + /* + If the version in the parse tree is stale, + or the version in the cache is stale and sp is not used, + we need to reprepare. + Sic: version != spc_version <--> sp is not NULL. + */ + if (rt->m_sp_cache_version != version || + (version != spc_version && !sp->is_invoked())) + { + if (thd->m_reprepare_observer && + thd->m_reprepare_observer->report_error(thd)) + { + /* + Version of the sp cache is different from the + previous execution of the prepared statement, and it is + unacceptable for this SQLCOM. Error has been reported. + */ + DBUG_ASSERT(thd->is_error()); + return TRUE; + } + /* Always maintain the latest cache version. */ + rt->m_sp_cache_version= version; + } + return FALSE; +} + + /** Open view by getting its definition from disk (and table cache in future). @@ -3696,13 +3757,16 @@ request_backoff_action(enum_open_table_action action_arg) /** - Recover from failed attempt ot open table by performing requested action. + Recover from failed attempt of open table by performing requested action. @param thd Thread context - @param table Table list element for table that caused problem - @param action Type of action requested by failed open_table() call + @param mdl_request MDL_request of the object that caused the problem. + @param table Optional (can be NULL). Used only if action is OT_REPAIR. + In that case a TABLE_LIST for the table to be repaired. + @todo: It's unnecessary and should be removed. - @pre This function should be called only with "action" != OT_NO_ACTION. + @pre This function should be called only with "action" != OT_NO_ACTION + and after having called @sa close_tables_for_reopen(). @retval FALSE - Success. One should try to open tables once again. @retval TRUE - Error @@ -3710,7 +3774,8 @@ request_backoff_action(enum_open_table_action action_arg) bool Open_table_context:: -recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table) +recover_from_failed_open(THD *thd, MDL_request *mdl_request, + TABLE_LIST *table) { bool result= FALSE; /* Execute the action. */ @@ -3723,14 +3788,20 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table) break; case OT_DISCOVER: { - MDL_request mdl_xlock_request(&table->mdl_request); + MDL_request mdl_xlock_request(mdl_request); mdl_xlock_request.set_type(MDL_EXCLUSIVE); if ((result= thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request))) break; + + DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); - ha_create_table_from_engine(thd, table->db, table->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, + mdl_request->key.db_name(), + mdl_request->key.name()); + ha_create_table_from_engine(thd, + mdl_request->key.db_name(), + mdl_request->key.name()); pthread_mutex_unlock(&LOCK_open); thd->warning_info->clear_warning_info(thd->query_id); @@ -3740,14 +3811,17 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table) } case OT_REPAIR: { - MDL_request mdl_xlock_request(&table->mdl_request); + MDL_request mdl_xlock_request(mdl_request); mdl_xlock_request.set_type(MDL_EXCLUSIVE); if ((result= thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request))) break; + DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, + mdl_request->key.db_name(), + mdl_request->key.name()); pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); @@ -3808,7 +3882,11 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) @param[in] prelocking_strategy Strategy which specifies how the prelocking set should be extended when one of its elements is processed. - @param[out] need_prelocking Set to TRUE if it was detected that this + @param[in] has_prelocking_list Indicates that prelocking set/list for + this statement has already been built. + @param[in] ot_ctx Context of open_table used to recover from + locking failures. + @param[out] need_prelocking Set to TRUE if it was detected that this statement will require prelocked mode for its execution, not touched otherwise. @@ -3820,32 +3898,99 @@ static bool open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, Sroutine_hash_entry *rt, Prelocking_strategy *prelocking_strategy, + bool has_prelocking_list, + Open_table_context *ot_ctx, bool *need_prelocking) { + MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace(); DBUG_ENTER("open_and_process_routine"); - switch (rt->mdl_request.key.mdl_namespace()) + switch (mdl_type) { case MDL_key::FUNCTION: case MDL_key::PROCEDURE: { - char qname_buff[NAME_LEN*2+1+1]; - sp_name name(&rt->mdl_request.key, qname_buff); sp_head *sp; - int type= (rt->mdl_request.key.mdl_namespace() == MDL_key::FUNCTION) ? - TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE; - - if (sp_cache_routine(thd, type, &name, &sp)) - DBUG_RETURN(TRUE); - - if (sp) + /* + Try to get MDL lock on the routine. + Note that we do not take locks on top-level CALLs as this can + lead to a deadlock. Not locking top-level CALLs does not break + the binlog as only the statements in the called procedure show + up there, not the CALL itself. + */ + if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first || + mdl_type != MDL_key::PROCEDURE) { - prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, - need_prelocking); + ot_ctx->add_request(&rt->mdl_request); + if (thd->mdl_context.try_acquire_shared_lock(&rt->mdl_request)) + DBUG_RETURN(TRUE); + + if (rt->mdl_request.ticket == NULL) + { + /* A lock conflict. Someone's trying to modify SP metadata. */ + ot_ctx->request_backoff_action(Open_table_context::OT_WAIT); + DBUG_RETURN(TRUE); + } + DEBUG_SYNC(thd, "after_shared_lock_pname"); + + /* Ensures the routine is up-to-date and cached, if exists. */ + if (sp_cache_routine(thd, rt, has_prelocking_list, &sp)) + DBUG_RETURN(TRUE); + + /* Remember the version of the routine in the parse tree. */ + if (check_and_update_routine_version(thd, rt, sp)) + DBUG_RETURN(TRUE); + + /* 'sp' is NULL when there is no such routine. */ + if (sp && !has_prelocking_list) + { + prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, + need_prelocking); + } + } + else + { + /* + If it's a top level call, just make sure we have a recent + version of the routine, if it exists. + Validating routine version is unnecessary, since CALL + does not affect the prepared statement prelocked list. + */ + sp_cache_routine(thd, rt, FALSE, &sp); } } break; case MDL_key::TRIGGER: + /** + We add trigger entries to lex->sroutines_list, but we don't + load them here. The trigger entry is only used when building + a transitive closure of objects used in a statement, to avoid + adding to this closure objects that are used in the trigger more + than once. + E.g. if a trigger trg refers to table t2, and the trigger table t1 + is used multiple times in the statement (say, because it's used in + function f1() twice), we will only add t2 once to the list of + tables to prelock. + + We don't take metadata locks on triggers either: they are protected + by a respective lock on the table, on which the trigger is defined. + + The only two cases which give "trouble" are SHOW CREATE TRIGGER + and DROP TRIGGER statements. For these, statement syntax doesn't + specify the table on which this trigger is defined, so we have + to make a "dirty" read in the data dictionary to find out the + table name. Once we discover the table name, we take a metadata + lock on it, and this protects all trigger operations. + Of course the table, in theory, may disappear between the dirty + read and metadata lock acquisition, but in that case we just return + a run-time error. + + Grammar of other trigger DDL statements (CREATE, DROP) requires + the table to be specified explicitly, so we use the table metadata + lock to protect trigger metadata in these statements. Similarly, in + DML we always use triggers together with their tables, and thus don't + need to take separate metadata locks on them. + */ break; default: /* Impossible type value. */ @@ -3965,7 +4110,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, if (error) { - if (! ot_ctx->can_recover_from_failed_open_table() && safe_to_ignore_table) + if (! ot_ctx->can_recover_from_failed_open() && safe_to_ignore_table) { DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", tables->db, tables->alias)); @@ -4150,7 +4295,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, Open_table_context ot_ctx(thd); bool error= FALSE; MEM_ROOT new_frm_mem; - bool has_prelocking_list= thd->lex->requires_prelocking(); + bool has_prelocking_list; DBUG_ENTER("open_tables"); /* @@ -4172,7 +4317,8 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, init_sql_alloc(&new_frm_mem, 8024, 8024); thd->current_tablenr= 0; - restart: +restart: + has_prelocking_list= thd->lex->requires_prelocking(); table_to_open= start; sroutine_to_open= (Sroutine_hash_entry**) &thd->lex->sroutines_list.first; *counter= 0; @@ -4184,7 +4330,6 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, */ while (*table_to_open || (thd->locked_tables_mode <= LTM_LOCK_TABLES && - ! has_prelocking_list && *sroutine_to_open)) { /* @@ -4201,7 +4346,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, if (error) { - if (ot_ctx.can_recover_from_failed_open_table()) + if (ot_ctx.can_recover_from_failed_open()) { /* We have met exclusive metadata lock or old version of table. @@ -4220,12 +4365,14 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, */ TABLE_LIST *failed_table= *table_to_open; close_tables_for_reopen(thd, start); + /* Here we rely on the fact that 'tables' still points to the valid TABLE_LIST element. Altough currently this assumption is valid it may change in future. */ - if (ot_ctx.recover_from_failed_open_table_attempt(thd, failed_table)) + if (ot_ctx.recover_from_failed_open(thd, &failed_table->mdl_request, + failed_table)) goto err; error= FALSE; @@ -4239,8 +4386,11 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, If we are not already in prelocked mode and extended table list is not yet built for our statement we need to cache routines it uses and build the prelocking list for it. + If we are not in prelocked mode but have built the extended table + list, we still need to call open_and_process_routine() to take + MDL locks on the routines. */ - if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES) { bool need_prelocking= FALSE; TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; @@ -4256,12 +4406,21 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, for (Sroutine_hash_entry *rt= *sroutine_to_open; rt; sroutine_to_open= &rt->next, rt= rt->next) { - error= open_and_process_routine(thd, thd->lex, rt, - prelocking_strategy, + error= open_and_process_routine(thd, thd->lex, rt, prelocking_strategy, + has_prelocking_list, &ot_ctx, &need_prelocking); if (error) { + if (ot_ctx.can_recover_from_failed_open()) + { + close_tables_for_reopen(thd, start); + if (ot_ctx.recover_from_failed_open(thd, &rt->mdl_request, NULL)) + goto err; + + error= FALSE; + goto restart; + } /* Serious error during reading stored routines from mysql.proc table. Something is wrong with the table or its contents, and an error has @@ -4666,7 +4825,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, retry: while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) && - ot_ctx.can_recover_from_failed_open_table()) + ot_ctx.can_recover_from_failed_open()) { /* We can't back off with an open HANDLER, we don't wait with locks. */ DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); @@ -4677,7 +4836,8 @@ retry: */ thd->mdl_context.release_transactional_locks(); table_list->mdl_request.ticket= 0; - if (ot_ctx.recover_from_failed_open_table_attempt(thd, table_list)) + if (ot_ctx.recover_from_failed_open(thd, &table_list->mdl_request, + table_list)) break; } @@ -5242,11 +5402,18 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) if (first_not_own_table == *tables) *tables= 0; thd->lex->chop_off_not_own_tables(); + /* Reset MDL tickets for procedures/functions */ + for (Sroutine_hash_entry *rt= + (Sroutine_hash_entry*)thd->lex->sroutines_list.first; + rt; rt= rt->next) + rt->mdl_request.ticket= NULL; sp_remove_not_own_routines(thd->lex); for (tmp= *tables; tmp; tmp= tmp->next_global) { tmp->table= 0; tmp->mdl_request.ticket= NULL; + /* We have to cleanup translation tables of views. */ + tmp->cleanup_items(); } /* Metadata lock requests for tables from extended part of prelocking set @@ -8363,6 +8530,10 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) MDL_request_list::Iterator it(*mdl_requests); while ((mdl_request= it++)) { + /* Skip requests on non-TDC objects. */ + if (mdl_request->key.mdl_namespace() != MDL_key::TABLE) + continue; + if ((share= get_cached_table_share(mdl_request->key.db_name(), mdl_request->key.name())) && share->version != refresh_version) diff --git a/sql/sql_class.h b/sql/sql_class.h index 11e0010d85b..ff1b51e7e87 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1271,7 +1271,7 @@ private: /** A context of open_tables() function, used to recover - from a failed open_table() attempt. + from a failed open_table() or open_routine() attempt. Implemented in sql_base.cc. */ @@ -1288,13 +1288,14 @@ public: }; Open_table_context(THD *thd); - bool recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *tables); + bool recover_from_failed_open(THD *thd, MDL_request *mdl_request, + TABLE_LIST *table); bool request_backoff_action(enum_open_table_action action_arg); void add_request(MDL_request *request) { m_mdl_requests.push_front(request); } - bool can_recover_from_failed_open_table() const + bool can_recover_from_failed_open() const { return m_action != OT_NO_ACTION; } bool can_deadlock() const { return m_can_deadlock; } private: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 79f10120268..689b2cec270 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -284,14 +284,14 @@ void init_update_queries(void) sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE_ALL]= CF_PROTECT_AGAINST_GRL; - sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; - sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; - sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; - sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; - sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA; @@ -319,10 +319,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_CREATE_PROCEDURE]|= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_CREATE_SPFUNCTION]|= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_PROCEDURE]|= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_FUNCTION]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS; @@ -1988,9 +1984,10 @@ mysql_execute_command(THD *thd) #endif case SQLCOM_SHOW_STATUS_PROC: case SQLCOM_SHOW_STATUS_FUNC: - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))) - res= execute_sqlcom_select(thd, all_tables); + goto error; + res= execute_sqlcom_select(thd, all_tables); break; case SQLCOM_SHOW_STATUS: { @@ -3939,7 +3936,7 @@ end_with_restore_list: if (sp_process_definer(thd)) goto create_sp_error; - res= (sp_result= lex->sphead->create(thd)); + res= (sp_result= sp_create_routine(thd, lex->sphead->m_type, lex->sphead)); switch (sp_result) { case SP_OK: { #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -3949,6 +3946,16 @@ end_with_restore_list: bool restore_backup_context= false; Security_context *backup= NULL; LEX_USER *definer= thd->lex->definer; + /* + We're going to issue an implicit GRANT statement. + It takes metadata locks and updates system tables. + Make sure that sp_create_routine() did not leave any + locks in the MDL context, so there is no risk to + deadlock. + */ + trans_commit_implicit(thd); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); /* Check if the definer exists on slave, then use definer privilege to insert routine privileges to mysql.procs_priv. @@ -4021,7 +4028,6 @@ create_sp_error: case SQLCOM_CALL: { sp_head *sp; - /* This will cache all SP and SF and open and lock all tables required for execution. @@ -4117,65 +4123,22 @@ create_sp_error: case SQLCOM_ALTER_FUNCTION: { int sp_result; - sp_head *sp; - st_sp_chistics chistics; + int type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ? + TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - memcpy(&chistics, &lex->sp_chistics, sizeof(chistics)); - if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) - sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, - &thd->sp_proc_cache, FALSE); - else - sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname, - &thd->sp_func_cache, FALSE); - thd->warning_info->opt_clear_warning_info(thd->query_id); - if (! sp) - { - if (lex->spname->m_db.str) - sp_result= SP_KEY_NOT_FOUND; - else - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - goto error; - } - } - else - { - if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str, - sp->m_name.str, - lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) - goto error; + if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, + lex->spname->m_name.str, + lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) + goto error; - memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics)); - if ((sp->m_type == TYPE_ENUM_FUNCTION) && - !trust_function_creators && mysql_bin_log.is_open() && - !sp->m_chistics->detistic && - (chistics.daccess == SP_CONTAINS_SQL || - chistics.daccess == SP_MODIFIES_SQL_DATA)) - { - my_message(ER_BINLOG_UNSAFE_ROUTINE, - ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0)); - sp_result= SP_INTERNAL_ERROR; - } - else - { - /* - Note that if you implement the capability of ALTER FUNCTION to - alter the body of the function, this command should be made to - follow the restrictions that log-bin-trust-function-creators=0 - already puts on CREATE FUNCTION. - */ - /* Conditionally writes to binlog */ - - int type= lex->sql_command == SQLCOM_ALTER_PROCEDURE ? - TYPE_ENUM_PROCEDURE : - TYPE_ENUM_FUNCTION; - - sp_result= sp_update_routine(thd, - type, - lex->spname, - &lex->sp_chistics); - } - } + /* + Note that if you implement the capability of ALTER FUNCTION to + alter the body of the function, this command should be made to + follow the restrictions that log-bin-trust-function-creators=0 + already puts on CREATE FUNCTION. + */ + /* Conditionally writes to binlog */ + sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics); switch (sp_result) { case SP_OK: @@ -4199,6 +4162,12 @@ create_sp_error: int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); + /* + @todo: here we break the metadata locking protocol by + looking up the information about the routine without + a metadata lock. Rewrite this piece to make sp_drop_routine + return whether the routine existed or not. + */ sp_result= sp_routine_exists_in_table(thd, type, lex->spname); thd->warning_info->opt_clear_warning_info(thd->query_id); if (sp_result == SP_OK) @@ -4210,30 +4179,30 @@ create_sp_error: lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) goto error; - if (trans_commit_implicit(thd)) - goto error; - - close_thread_tables(thd); - - thd->mdl_context.release_transactional_locks(); + /* Conditionally writes to binlog */ + sp_result= sp_drop_routine(thd, type, lex->spname); #ifndef NO_EMBEDDED_ACCESS_CHECKS + /* + We're going to issue an implicit REVOKE statement. + It takes metadata locks and updates system tables. + Make sure that sp_create_routine() did not leave any + locks in the MDL context, so there is no risk to + deadlock. + */ + trans_commit_implicit(thd); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + if (sp_automatic_privileges && !opt_noacl && - sp_revoke_privileges(thd, db, name, + sp_revoke_privileges(thd, db, name, lex->sql_command == SQLCOM_DROP_PROCEDURE)) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PROC_AUTO_REVOKE_FAIL, ER(ER_PROC_AUTO_REVOKE_FAIL)); } #endif - /* Conditionally writes to binlog */ - - int type= lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : - TYPE_ENUM_FUNCTION; - - sp_result= sp_drop_routine(thd, type, lex->spname); } else { @@ -4292,21 +4261,13 @@ create_sp_error: case SQLCOM_SHOW_CREATE_PROC: { if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname)) - { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - SP_COM_STRING(lex), lex->spname->m_name.str); - goto error; - } + goto error; break; } case SQLCOM_SHOW_CREATE_FUNC: { if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname)) - { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - SP_COM_STRING(lex), lex->spname->m_name.str); goto error; - } break; } case SQLCOM_SHOW_PROC_CODE: @@ -4314,13 +4275,11 @@ create_sp_error: { #ifndef DBUG_OFF sp_head *sp; + int type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ? + TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - if (lex->sql_command == SQLCOM_SHOW_PROC_CODE) - sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, - &thd->sp_proc_cache, FALSE); - else - sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname, - &thd->sp_func_cache, FALSE); + if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp)) + goto error; if (!sp || sp->show_routine_code(thd)) { /* We don't distinguish between errors for now */ @@ -5579,9 +5538,6 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, { LEX *lex= thd->lex; - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); - Parser_state parser_state(thd, inBuf, length); bool err= parse_sql(thd, & parser_state, NULL); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 700017f2b3e..838f25320ec 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -173,8 +173,6 @@ private: SELECT_LEX and other classes). */ MEM_ROOT main_mem_root; - /* Version of the stored functions cache at the time of prepare. */ - ulong m_sp_cache_version; private: bool set_db(const char *db, uint db_length); bool set_parameters(String *expanded_query, @@ -2138,9 +2136,6 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) DBUG_VOID_RETURN; } - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); - thd->protocol= &thd->protocol_binary; if (stmt->prepare(packet, packet_length)) @@ -2419,6 +2414,13 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) { tables->reinit_before_use(thd); } + + /* Reset MDL tickets for procedures/functions */ + for (Sroutine_hash_entry *rt= + (Sroutine_hash_entry*)thd->lex->sroutines_list.first; + rt; rt= rt->next) + rt->mdl_request.ticket= NULL; + /* Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ... (multi-delete). We do a full clean up, although at the moment all we @@ -2512,9 +2514,6 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_PRINT("exec_query", ("%s", stmt->query())); DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); - open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY); thd->protocol= &thd->protocol_binary; @@ -2964,8 +2963,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) param_array(0), param_count(0), last_errno(0), - flags((uint) IS_IN_USE), - m_sp_cache_version(0) + flags((uint) IS_IN_USE) { init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size, thd_arg->variables.query_prealloc_size); @@ -3234,20 +3232,6 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) init_stmt_after_parse(lex); state= Query_arena::PREPARED; flags&= ~ (uint) IS_IN_USE; - /* - This is for prepared statement validation purposes. - A statement looks up and pre-loads all its stored functions - at prepare. Later on, if a function is gone from the cache, - execute may fail. - Remember the cache version to be able to invalidate the prepared - statement at execute if it changes. - We only need to care about version of the stored functions cache: - if a prepared statement uses a stored procedure, it's indirect, - via a stored function. The only exception is SQLCOM_CALL, - but the latter one looks up the stored procedure each time - it's invoked, rather than once at prepare. - */ - m_sp_cache_version= sp_cache_version(&thd->sp_func_cache); /* Log COM_EXECUTE to the general log. Note, that in case of SQL @@ -3588,13 +3572,12 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) is allocated in the old arena. */ swap_variables(Item_param **, param_array, copy->param_array); - /* Swap flags: this is perhaps unnecessary */ - swap_variables(uint, flags, copy->flags); + /* Don't swap flags: the copy has IS_SQL_PREPARE always set. */ + /* swap_variables(uint, flags, copy->flags); */ /* Swap names, the old name is allocated in the wrong memory root */ swap_variables(LEX_STRING, name, copy->name); /* Ditto */ swap_variables(char *, db, copy->db); - swap_variables(ulong, m_sp_cache_version, copy->m_sp_cache_version); DBUG_ASSERT(db_length == copy->db_length); DBUG_ASSERT(param_count == copy->param_count); @@ -3653,19 +3636,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) return TRUE; } - /* - Reprepare the statement if we're using stored functions - and the version of the stored routines cache has changed. - */ - if (lex->uses_stored_routines() && - m_sp_cache_version != sp_cache_version(&thd->sp_func_cache) && - thd->m_reprepare_observer && - thd->m_reprepare_observer->report_error(thd)) - { - return TRUE; - } - - /* For SHOW VARIABLES lex->result is NULL, as it's a non-SELECT command. For such queries we don't return an error and don't diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 492bb46ebad..72fb49cf38c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3237,7 +3237,6 @@ end_share: end_unlock: pthread_mutex_unlock(&LOCK_open); -end: thd->mdl_context.release_lock(table_list.mdl_request.ticket); thd->clear_error(); return res; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3b87a4dd6e8..30d6efff7ec 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -21,6 +21,7 @@ #include #include #include "sp_head.h" +#include "sp.h" #include "sql_trigger.h" #include "sql_show.h" #include "transaction.h" @@ -5010,6 +5011,23 @@ send_result_message: trans_commit_implicit(thd); close_thread_tables(thd); table->table=0; // For query cache + + /* + If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run + separate open_tables() for each CHECK TABLE argument. + Right now we do not have a separate method to reset the prelocking + state in the lex to the state after parsing, so each open will pollute + this state: add elements to lex->srotuines_list, TABLE_LISTs to + lex->query_tables. Below is a lame attempt to recover from this + pollution. + @todo: have a method to reset a prelocking context, or use separate + contexts for each open. + */ + for (Sroutine_hash_entry *rt= + (Sroutine_hash_entry*)thd->lex->sroutines_list.first; + rt; rt= rt->next) + rt->mdl_request.ticket= NULL; + if (protocol->write()) goto err; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b81fb30ec27..603ab1b9682 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1117,10 +1117,6 @@ reopen_tables: while ((item= it++)) item->cleanup(); - /* We have to cleanup translation tables of views. */ - for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) - tbl->cleanup_items(); - /* To not to hog memory (as a result of the unit->reinit_exec_mechanism() call below): From 93613d4e07b6e6bd42942eeceec577794c5050b1 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Tue, 29 Dec 2009 18:50:01 +0300 Subject: [PATCH 199/466] A test case for Bug#49972 (Crash in prepared statements). --- tests/mysql_client_test.c | 110 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 866c63ae1d3..cce5910311f 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18935,6 +18935,115 @@ static void test_bug44495() DBUG_VOID_RETURN; } +/* + Bug#49972: Crash in prepared statements. + + The following case lead to a server crash: + - Use binary protocol; + - Prepare a statement with OUT-parameter; + - Execute the statement; + - Cause re-prepare of the statement (change dependencies); + - Execute the statement again -- crash here. +*/ + +static void test_bug49972() +{ + int rc; + MYSQL_STMT *stmt; + + MYSQL_BIND in_param_bind; + MYSQL_BIND out_param_bind; + int int_data; + my_bool is_null; + + DBUG_ENTER("test_bug49972"); + myheader("test_49972"); + + rc= mysql_query(mysql, "DROP FUNCTION IF EXISTS f1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE FUNCTION f1() RETURNS INT RETURN 1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE PROCEDURE p1(IN a INT, OUT b INT) SET b = a"); + myquery(rc); + + stmt= mysql_simple_prepare(mysql, "CALL p1((SELECT f1()), ?)"); + check_stmt(stmt); + + bzero((char *) &in_param_bind, sizeof (in_param_bind)); + + in_param_bind.buffer_type= MYSQL_TYPE_LONG; + in_param_bind.buffer= (char *) &int_data; + in_param_bind.length= 0; + in_param_bind.is_null= 0; + + rc= mysql_stmt_bind_param(stmt, &in_param_bind); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + { + bzero(&out_param_bind, sizeof (out_param_bind)); + + out_param_bind.buffer_type= MYSQL_TYPE_LONG; + out_param_bind.is_null= &is_null; + out_param_bind.buffer= &int_data; + out_param_bind.buffer_length= sizeof (int_data); + + rc= mysql_stmt_bind_result(stmt, &out_param_bind); + check_execute(stmt, rc); + + rc= mysql_stmt_fetch(stmt); + rc= mysql_stmt_fetch(stmt); + DBUG_ASSERT(rc == MYSQL_NO_DATA); + + mysql_stmt_next_result(stmt); + mysql_stmt_fetch(stmt); + } + + rc= mysql_query(mysql, "DROP FUNCTION f1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE FUNCTION f1() RETURNS INT RETURN 1"); + myquery(rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + { + bzero(&out_param_bind, sizeof (out_param_bind)); + + out_param_bind.buffer_type= MYSQL_TYPE_LONG; + out_param_bind.is_null= &is_null; + out_param_bind.buffer= &int_data; + out_param_bind.buffer_length= sizeof (int_data); + + rc= mysql_stmt_bind_result(stmt, &out_param_bind); + check_execute(stmt, rc); + + rc= mysql_stmt_fetch(stmt); + rc= mysql_stmt_fetch(stmt); + DBUG_ASSERT(rc == MYSQL_NO_DATA); + + mysql_stmt_next_result(stmt); + mysql_stmt_fetch(stmt); + } + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP PROCEDURE p1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP FUNCTION f1"); + myquery(rc); + + DBUG_VOID_RETURN; +} + /* Read and parse arguments and MySQL options from my.cnf */ @@ -19264,6 +19373,7 @@ static struct my_tests_st my_tests[]= { #endif { "test_bug41078", test_bug41078 }, { "test_bug44495", test_bug44495 }, + { "test_bug49972", test_bug49972 }, { 0, 0 } }; From cd6fbffc38bb1c90a81e23c00f0a0fd042d39030 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Tue, 29 Dec 2009 21:12:06 +0300 Subject: [PATCH 200/466] Disable test case for Bug#49972. --- tests/mysql_client_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index cce5910311f..1b580f2412b 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -19373,7 +19373,7 @@ static struct my_tests_st my_tests[]= { #endif { "test_bug41078", test_bug41078 }, { "test_bug44495", test_bug44495 }, - { "test_bug49972", test_bug49972 }, + /* XXX { "test_bug49972", test_bug49972 }, */ { 0, 0 } }; From 236539b47160a384901533fc41c6793bb5241e0b Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Wed, 30 Dec 2009 20:53:30 +0300 Subject: [PATCH 201/466] Implementation of simple deadlock detection for metadata locks. This change is supposed to reduce number of ER_LOCK_DEADLOCK errors which occur when multi-statement transaction encounters conflicting metadata lock in cases when waiting is possible. The idea is not to fail ER_LOCK_DEADLOCK error immediately when we encounter conflicting metadata lock. Instead we release all metadata locks acquired by current statement and start to wait until conflicting lock go away. To avoid deadlocks we use simple empiric which aborts waiting with ER_LOCK_DEADLOCK error if it turns out that somebody is waiting for metadata locks owned by this transaction. This patch also fixes bug #46273 "MySQL 5.4.4 new MDL: Bug#989 is not fully fixed in case of ALTER". The bug was that concurrent execution of UPDATE or MULTI-UPDATE statement as a part of multi-statement transaction that already has used table being updated and ALTER TABLE statement might have resulted of loss of isolation between this transaction and ALTER TABLE statement, which manifested itself as changes performed by ALTER TABLE becoming visible in transaction and wrong binary log order as a consequence. This problem occurred when UPDATE or MULTI-UPDATE's wait in mysql_lock_tables() call was aborted due to metadata lock upgrade performed by concurrent ALTER TABLE. After such abort all metadata locks held by transaction were released but transaction silently continued to be executed as if nothing has happened. We solve this problem by changing our code not to release all locks in such case. Instead we release only locks which were acquired by current statement and then try to reacquire them by restarting open/lock tables process. We piggyback on simple deadlock detector implementation since this change has to be done anyway for it. --- mysql-test/include/handler.inc | 32 ++- mysql-test/r/handler_innodb.result | 27 +- mysql-test/r/handler_myisam.result | 27 +- mysql-test/r/mdl_sync.result | 251 +++++++++++++++++ mysql-test/r/sp-lock.result | 96 +++---- mysql-test/t/mdl_sync.test | 420 +++++++++++++++++++++++++++++ mysql-test/t/sp-lock.test | 210 ++++++++------- sql/log_event_old.cc | 2 +- sql/mdl.cc | 172 +++++++++++- sql/mdl.h | 13 + sql/mysql_priv.h | 3 +- sql/sql_base.cc | 165 +++++++----- sql/sql_class.h | 15 +- sql/sql_insert.cc | 2 +- sql/sql_plist.h | 6 +- sql/sql_show.cc | 4 +- sql/sql_update.cc | 6 +- 17 files changed, 1189 insertions(+), 262 deletions(-) diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index a9965f97926..5562f7b2558 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -732,7 +732,7 @@ connection default; --disable_warnings drop table if exists t1; --enable_warnings -create table t1 (a int); +create table t1 (a int, key a (a)); insert into t1 values (1); handler t1 open; connection con1; @@ -743,7 +743,6 @@ let $wait_condition= where state = "Waiting for table" and info = "alter table t1 engine=memory"; --source include/wait_condition.inc connection default; ---error ER_ILLEGAL_HA handler t1 read a next; handler t1 close; connection con1; @@ -983,11 +982,12 @@ lock table t2 read; --echo # --> connection con2 connection con2; --echo # Sending: ---send drop table t2 +send rename table t2 to t3, t1 to t2, t3 to t1; --echo # --> connection con1 connection con1; ---echo # Waiting for 'drop table t2' to get blocked... -let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--echo # Waiting for 'rename table ...' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='rename table t2 to t3, t1 to t2, t3 to t1'; --source include/wait_condition.inc --echo # --> connection default connection default; @@ -997,25 +997,26 @@ handler t2 open; select * from t2; handler t1 open; commit; ---error ER_LOCK_DEADLOCK -handler t2 open; handler t1 close; --echo # --> connection con1 connection con1; unlock tables; --echo # --> connection con2 connection con2; ---echo # Reaping 'drop table t2'... +--echo # Reaping 'rename table ...'... --reap --echo # --> connection default connection default; handler t1 open; handler t1 read a prev; handler t1 close; +drop table t2; --echo # ---echo # Likewise, this doesn't require a multi-statement transaction. ---echo # ER_LOCK_DEADLOCK is also produced when we have an open ---echo # HANDLER and try to acquire locks for a single statement. +--echo # Originally there was a deadlock error in this test. +--echo # With implementation of deadlock detector +--echo # we no longer deadlock, but block and wait on a lock. +--echo # The HANDLER is auto-closed as soon as the connection +--echo # sees a pending conflicting lock against it. --echo # create table t2 (a int, key a (a)); handler t1 open; @@ -1033,10 +1034,12 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where --source include/wait_condition.inc --echo # --> connection default connection default; ---error ER_LOCK_DEADLOCK -select * from t2; +--echo # Sending 'select * from t2' +send select * from t2; --echo # --> connection con1 connection con1; +--echo # Waiting for 'select * from t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='select * from t2'; unlock tables; --echo # --> connection con2 connection con2; @@ -1044,6 +1047,9 @@ connection con2; --reap --echo # --> connection default connection default; +--echo # Reaping 'select * from t2' +--error ER_NO_SUCH_TABLE +reap; handler t1 close; --echo # diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index df948f3d0b6..807e8becea8 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -745,12 +745,13 @@ drop table t1; handler t1 read a next; ERROR 42S02: Unknown table 't1' in HANDLER drop table if exists t1; -create table t1 (a int); +create table t1 (a int, key a (a)); insert into t1 values (1); handler t1 open; alter table t1 engine=memory; handler t1 read a next; -ERROR HY000: Table storage engine for 't1' doesn't have this option +a +1 handler t1 close; drop table t1; USE information_schema; @@ -1002,9 +1003,9 @@ a lock table t2 read; # --> connection con2 # Sending: -drop table t2; +rename table t2 to t3, t1 to t2, t3 to t1; # --> connection con1 -# Waiting for 'drop table t2' to get blocked... +# Waiting for 'rename table ...' to get blocked... # --> connection default handler t2 open; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction @@ -1012,23 +1013,24 @@ select * from t2; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 open; commit; -handler t2 open; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 close; # --> connection con1 unlock tables; # --> connection con2 -# Reaping 'drop table t2'... +# Reaping 'rename table ...'... # --> connection default handler t1 open; handler t1 read a prev; a 5 handler t1 close; +drop table t2; # -# Likewise, this doesn't require a multi-statement transaction. -# ER_LOCK_DEADLOCK is also produced when we have an open -# HANDLER and try to acquire locks for a single statement. +# Originally there was a deadlock error in this test. +# With implementation of deadlock detector +# we no longer deadlock, but block and wait on a lock. +# The HANDLER is auto-closed as soon as the connection +# sees a pending conflicting lock against it. # create table t2 (a int, key a (a)); handler t1 open; @@ -1040,13 +1042,16 @@ drop table t2; # --> connection con1 # Waiting for 'drop table t2' to get blocked... # --> connection default +# Sending 'select * from t2' select * from t2; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # --> connection con1 +# Waiting for 'select * from t2' to get blocked... unlock tables; # --> connection con2 # Reaping 'drop table t2'... # --> connection default +# Reaping 'select * from t2' +ERROR 42S02: Table 'test.t2' doesn't exist handler t1 close; # # ROLLBACK TO SAVEPOINT releases transactional locks, diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index 4b287e6560b..adcbf068b97 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -743,12 +743,13 @@ drop table t1; handler t1 read a next; ERROR 42S02: Unknown table 't1' in HANDLER drop table if exists t1; -create table t1 (a int); +create table t1 (a int, key a (a)); insert into t1 values (1); handler t1 open; alter table t1 engine=memory; handler t1 read a next; -ERROR HY000: Table storage engine for 't1' doesn't have this option +a +1 handler t1 close; drop table t1; USE information_schema; @@ -999,9 +1000,9 @@ a lock table t2 read; # --> connection con2 # Sending: -drop table t2; +rename table t2 to t3, t1 to t2, t3 to t1; # --> connection con1 -# Waiting for 'drop table t2' to get blocked... +# Waiting for 'rename table ...' to get blocked... # --> connection default handler t2 open; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction @@ -1009,23 +1010,24 @@ select * from t2; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 open; commit; -handler t2 open; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 close; # --> connection con1 unlock tables; # --> connection con2 -# Reaping 'drop table t2'... +# Reaping 'rename table ...'... # --> connection default handler t1 open; handler t1 read a prev; a 5 handler t1 close; +drop table t2; # -# Likewise, this doesn't require a multi-statement transaction. -# ER_LOCK_DEADLOCK is also produced when we have an open -# HANDLER and try to acquire locks for a single statement. +# Originally there was a deadlock error in this test. +# With implementation of deadlock detector +# we no longer deadlock, but block and wait on a lock. +# The HANDLER is auto-closed as soon as the connection +# sees a pending conflicting lock against it. # create table t2 (a int, key a (a)); handler t1 open; @@ -1037,13 +1039,16 @@ drop table t2; # --> connection con1 # Waiting for 'drop table t2' to get blocked... # --> connection default +# Sending 'select * from t2' select * from t2; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # --> connection con1 +# Waiting for 'select * from t2' to get blocked... unlock tables; # --> connection con2 # Reaping 'drop table t2'... # --> connection default +# Reaping 'select * from t2' +ERROR 42S02: Table 'test.t2' doesn't exist handler t1 close; # # ROLLBACK TO SAVEPOINT releases transactional locks, diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index ec02f29b008..0c9b6432e95 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -20,6 +20,220 @@ ERROR 42S02: Unknown table 't1' drop table t3; SET DEBUG_SYNC= 'RESET'; # +# Test coverage for basic deadlock detection in metadata +# locking subsystem. +# +drop tables if exists t1, t2, t3, t4; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (k int); +# +# Test for the case in which no deadlock occurs. +# +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'deadlock_con2'. +begin; +insert into t2 values (1); +# +# Switching to connection 'default'. +# Send: +rename table t2 to t0, t3 to t2, t0 to t3;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con2' which holds shared metadata lock on 't2'. +# The below statement should wait for exclusive metadata lock +# on 't2' to go away and should not produce ER_LOCK_DEADLOCK +# as no deadlock is possible in this situation. +# Send: +select * from t2;; +# +# Switching to connection 'deadlock_con2'. +# Wait until the above SELECT * FROM t2 is starts waiting +# for an exclusive metadata lock to go away. +# +# Unblock RENAME TABLE by releasing shared metadata lock on t2. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE. +# +# Switching to connection 'deadlock_con1'. +# Reap SELECT. +k +# +# Switching to connection 'default'. +# +# Let us check that in the process of waiting for conflicting lock +# on table 't2' to go away transaction in connection 'deadlock_con1' +# has not released metadata lock on table 't1'. +# Send: +rename table t1 to t0, t3 to t1, t0 to t3;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con1' which should still hold shared metadata lock on +# table 't1'. +# Commit transaction to unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE. +# +# Test for case when deadlock occurs and should be detected immediately. +# +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (2); +# +# Switching to connection 'default'. +# Send: +rename table t2 to t0, t1 to t2, t0 to t1;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con1' which holds shared metadata lock on 't1'. +# +# The below statement should not wait as doing so will cause deadlock. +# Instead it should fail and emit ER_LOCK_DEADLOCK statement. +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Let us check that failure of the above statement has not released +# metadata lock on table 't1', i.e. that RENAME TABLE is still blocked. +# Commit transaction to unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE. +# +# Test for the case in which deadlock also occurs but not immediately. +# +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'deadlock_con2'. +begin; +insert into t3 values (1); +# +# Switching to connection 'default'. +# Send: +rename table t2 to t0, t3 to t2, t0 to t3;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con2' which holds shared metadata lock on 't3'. +# The below SELECT statement should wait for metadata lock +# on table 't2' and should not produce ER_LOCK_DEADLOCK +# immediately as no deadlock is possible at the moment. +select * from t2;; +# +# Switching to connection 'deadlock_con3'. +# Wait until the above SELECT * FROM t2 is starts waiting +# for an exclusive metadata lock to go away. +# Send RENAME TABLE statement that will deadlock with the +# SELECT statement and thus should abort the latter. +rename table t1 to t0, t2 to t1, t0 to t2;; +# +# Switching to connection 'deadlock_con1'. +# Since the latest RENAME TABLE entered in deadlock with SELECT +# statement the latter should be aborted and emit ER_LOCK_DEADLOCK +# error. +# Reap SELECT * FROM t2. +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Again let us check that failure of the SELECT statement has not +# released metadata lock on table 't1', i.e. that the latest RENAME +# is blocked. +# Commit transaction to unblock this RENAME TABLE. +commit; +# +# Switching to connection 'deadlock_con3'. +# Reap RENAME TABLE t1 TO t0 ... . +# +# Switching to connection 'deadlock_con2'. +# Commit transaction to unblock the first RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE t2 TO t0 ... . +drop tables t1, t2, t3, t4; +# +# Now, test case which shows that deadlock detection empiric +# also takes into account requests for metadata lock upgrade. +# +create table t1 (i int); +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'default'. +# Send: +alter table t1 add column j int, rename to t2;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above ALTER TABLE ... RENAME acquires exclusive +# metadata lock on 't2' and starts waiting for connection +# 'deadlock_con1' which holds shared lock on 't1'. +# The below statement should not wait as it will cause deadlock. +# An appropriate error should be reported instead. +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Again let us check that failure of the above statement has not +# released all metadata locks in connection 'deadlock_con1' and +# so ALTER TABLE ... RENAME is still blocked. +# Commit transaction to unblock ALTER TABLE ... RENAME. +commit; +# +# Switching to connection 'default'. +# Reap ALTER TABLE ... RENAME. +drop table t2; +# +# Finally, test case in which deadlock (or potentially livelock) occurs +# between metadata locking subsystem and table definition cache/table +# locks, but which should still be detected by our empiric. +# +create table t1 (i int); +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'default'. +lock tables t1 write; +# +# Switching to connection 'deadlock_con1'. +# Send: +insert into t1 values (2);; +# +# Switching to connection 'default'. +# Wait until INSERT in connection 'deadlock_con1' is blocked on +# table-level lock. +# Send: +alter table t1 add column j int;; +# +# Switching to connection 'deadlock_con1'. +# The above ALTER TABLE statement should cause INSERT statement in +# this connection to be aborted and emit ER_LOCK_DEADLOCK error. +# Reap INSERT +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Commit transaction to unblock ALTER TABLE. +commit; +# +# Switching to connection 'default'. +# Reap ALTER TABLE. +unlock tables; +drop table t1; +# # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() # on INSERT + CREATE TRIGGER". # @@ -234,6 +448,43 @@ drop table t2; # Clean-up. drop table t1; # +# Test for bug #46273 "MySQL 5.4.4 new MDL: Bug#989 is not fully fixed +# in case of ALTER". +# +drop table if exists t1; +set debug_sync= 'RESET'; +create table t1 (c1 int primary key, c2 int, c3 int); +insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); +begin; +update t1 set c3=c3+1 where c2=3; +# +# Switching to connection 'con46273'. +set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR alter_go'; +alter table t1 add column e int, rename to t2;; +# +# Switching to connection 'default'. +set debug_sync='now WAIT_FOR alter_table_locked'; +set debug_sync='wait_for_lock SIGNAL alter_go'; +# The below statement should get ER_LOCK_DEADLOCK error +# (i.e. it should not allow ALTER to proceed, and then +# fail due to 't1' changing its name to 't2'). +update t1 set c3=c3+1 where c2=4; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Let us check that failure of the above statement has not released +# metadata lock on table 't1', i.e. that ALTER TABLE is still blocked. +# Unblock ALTER TABLE by commiting transaction and thus releasing +# metadata lock on 't1'. +commit; +# +# Switching to connection 'con46273'. +# Reap ALTER TABLE. +# +# Switching to connection 'default'. +# Clean-up. +set debug_sync= 'RESET'; +drop table t2; +# # Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK # and DML". # diff --git a/mysql-test/r/sp-lock.result b/mysql-test/r/sp-lock.result index 65524d02d08..e9087f61807 100644 --- a/mysql-test/r/sp-lock.result +++ b/mysql-test/r/sp-lock.result @@ -125,16 +125,15 @@ drop temporary table t1; # # For that, start a transaction, use a routine. In a concurrent # connection, try to drop or alter the routine. It should place -# a pending or exlusive lock and block. In a concurrnet -# connection, try to use the routine under LOCK TABLES. -# That should yield ER_LOCK_DEADLOCK. +# a pending or exclusive lock and block. In another concurrnet +# connection, try to use the routine. +# That should block on the pending exclusive lock. # # Establish helper connections. # # Test DROP PROCEDURE. # # --> connection default -create table t1 (a int); create procedure p1() begin end; create function f1() returns int begin @@ -151,14 +150,17 @@ drop procedure p1; # --> connection con2 # Waitng for 'drop procedure t1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'drop procedure p1'... +# --> connection con2 +# Reaping 'select f1()' +ERROR 42000: PROCEDURE test.p1 does not exist # --> connection default # # Test CREATE PROCEDURE. @@ -174,17 +176,22 @@ create procedure p1() begin end; # --> connection con2 # Waitng for 'create procedure t1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'create procedure p1'... ERROR 42000: PROCEDURE p1 already exists +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # # Test ALTER PROCEDURE. +# begin; select f1(); f1() @@ -195,14 +202,18 @@ alter procedure p1 contains sql; # --> connection con2 # Waitng for 'alter procedure t1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'alter procedure p1'... +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # --> connection default # # Test DROP FUNCTION. @@ -217,14 +228,17 @@ drop function f1; # --> connection con2 # Waitng for 'drop function f1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'drop function f1'... +# --> connection con2 +# Reaping 'select f1()' +ERROR 42000: FUNCTION test.f1 does not exist # --> connection default # # Test CREATE FUNCTION. @@ -240,18 +254,23 @@ create function f1() returns int return 2; # --> connection con2 # Waitng for 'create function f1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'create function f1'... ERROR 42000: FUNCTION f1 already exists +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # --> connection default # # Test ALTER FUNCTION. +# begin; select f1(); f1() @@ -262,14 +281,18 @@ alter function f1 contains sql; # --> connection con2 # Waitng for 'alter function f1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'alter function f1'... +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # --> connection default drop function f1; drop procedure p1; @@ -283,6 +306,7 @@ drop procedure p1; # create procedure p1() begin end; create procedure p2() begin end; +create table t1 (a int); create procedure p3() begin call p1(); @@ -415,36 +439,11 @@ drop table t1, t2; # acquisition of a shared lock fails during a transaction or # we need to back off to flush the sp cache. # -# a) A back off due to a lock conflict. -# -create table t1 (a int); -create function f1() returns int return 6; -begin; -select f1(); -f1() -6 -# --> connection con1 -# Sending 'drop function f1'... -drop function f1; -# --> connection con2 -# Waitng for 'drop function f1' to get blocked on MDL lock... -begin; -select * from t1; -a -select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -commit; -# --> connection default -commit; -# --> connection con1 -# Reaping 'drop function f1'... -# --> connection default -# -# b) A back off to flush the cache. # Sic: now this situation does not require a back off since we # flush the cache on the fly. # create function f1() returns int return 7; +create table t1 (a int); begin; select * from t1; a @@ -691,6 +690,7 @@ drop function f1; set @@session.max_sp_recursion_depth=default; # --> connection con1 # --> connection con2 +# --> connection con3 # --> connection default # # End of 5.5 tests diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index e3aceaa05fa..c817012fb2f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -73,6 +73,366 @@ SET DEBUG_SYNC= 'RESET'; --enable_warnings +--echo # +--echo # Test coverage for basic deadlock detection in metadata +--echo # locking subsystem. +--echo # +--disable_warnings +drop tables if exists t1, t2, t3, t4; +--enable_warnings + +connect(deadlock_con1,localhost,root,,); +connect(deadlock_con2,localhost,root,,); +connect(deadlock_con3,localhost,root,,); +connection default; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (k int); + +--echo # +--echo # Test for the case in which no deadlock occurs. +--echo # + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +begin; +insert into t2 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send rename table t2 to t0, t3 to t2, t0 to t3; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con2' which holds shared metadata lock on 't2'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t3 to t2, t0 to t3"; +--source include/wait_condition.inc +--echo # The below statement should wait for exclusive metadata lock +--echo # on 't2' to go away and should not produce ER_LOCK_DEADLOCK +--echo # as no deadlock is possible in this situation. +--echo # Send: +--send select * from t2; + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +--echo # Wait until the above SELECT * FROM t2 is starts waiting +--echo # for an exclusive metadata lock to go away. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "select * from t2"; +--source include/wait_condition.inc +--echo # +--echo # Unblock RENAME TABLE by releasing shared metadata lock on t2. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE. +--reap + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Reap SELECT. +--reap + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # +--echo # Let us check that in the process of waiting for conflicting lock +--echo # on table 't2' to go away transaction in connection 'deadlock_con1' +--echo # has not released metadata lock on table 't1'. +--echo # Send: +--send rename table t1 to t0, t3 to t1, t0 to t3; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con1' which should still hold shared metadata lock on +--echo # table 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t1 to t0, t3 to t1, t0 to t3"; +--source include/wait_condition.inc +--echo # Commit transaction to unblock RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE. +--reap + +--echo # +--echo # Test for case when deadlock occurs and should be detected immediately. +--echo # + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (2); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send rename table t2 to t0, t1 to t2, t0 to t1; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con1' which holds shared metadata lock on 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t1 to t2, t0 to t1"; +--source include/wait_condition.inc +--echo # +--echo # The below statement should not wait as doing so will cause deadlock. +--echo # Instead it should fail and emit ER_LOCK_DEADLOCK statement. +--error ER_LOCK_DEADLOCK +select * from t2; + +--echo # +--echo # Let us check that failure of the above statement has not released +--echo # metadata lock on table 't1', i.e. that RENAME TABLE is still blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t1 to t2, t0 to t1"; +--source include/wait_condition.inc +--echo # Commit transaction to unblock RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE. +--reap + +--echo # +--echo # Test for the case in which deadlock also occurs but not immediately. +--echo # + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +begin; +insert into t3 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send rename table t2 to t0, t3 to t2, t0 to t3; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con2' which holds shared metadata lock on 't3'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t3 to t2, t0 to t3"; +--source include/wait_condition.inc +--echo # The below SELECT statement should wait for metadata lock +--echo # on table 't2' and should not produce ER_LOCK_DEADLOCK +--echo # immediately as no deadlock is possible at the moment. +--send select * from t2; + +--echo # +--echo # Switching to connection 'deadlock_con3'. +connection deadlock_con3; +--echo # Wait until the above SELECT * FROM t2 is starts waiting +--echo # for an exclusive metadata lock to go away. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "select * from t2"; +--source include/wait_condition.inc + +--echo # Send RENAME TABLE statement that will deadlock with the +--echo # SELECT statement and thus should abort the latter. +--send rename table t1 to t0, t2 to t1, t0 to t2; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Since the latest RENAME TABLE entered in deadlock with SELECT +--echo # statement the latter should be aborted and emit ER_LOCK_DEADLOCK +--echo # error. +--echo # Reap SELECT * FROM t2. +--error ER_LOCK_DEADLOCK +--reap + +--echo # +--echo # Again let us check that failure of the SELECT statement has not +--echo # released metadata lock on table 't1', i.e. that the latest RENAME +--echo # is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t1 to t0, t2 to t1, t0 to t2"; +--source include/wait_condition.inc +--echo # Commit transaction to unblock this RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'deadlock_con3'. +connection deadlock_con3; +--echo # Reap RENAME TABLE t1 TO t0 ... . +--reap; + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +--echo # Commit transaction to unblock the first RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE t2 TO t0 ... . +--reap + +drop tables t1, t2, t3, t4; + +--echo # +--echo # Now, test case which shows that deadlock detection empiric +--echo # also takes into account requests for metadata lock upgrade. +--echo # +create table t1 (i int); + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send alter table t1 add column j int, rename to t2; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above ALTER TABLE ... RENAME acquires exclusive +--echo # metadata lock on 't2' and starts waiting for connection +--echo # 'deadlock_con1' which holds shared lock on 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 add column j int, rename to t2"; +--source include/wait_condition.inc + +--echo # The below statement should not wait as it will cause deadlock. +--echo # An appropriate error should be reported instead. +--error ER_LOCK_DEADLOCK +select * from t2; + +--echo # Again let us check that failure of the above statement has not +--echo # released all metadata locks in connection 'deadlock_con1' and +--echo # so ALTER TABLE ... RENAME is still blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 add column j int, rename to t2"; +--source include/wait_condition.inc + +--echo # Commit transaction to unblock ALTER TABLE ... RENAME. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap ALTER TABLE ... RENAME. +--reap + +drop table t2; + +--echo # +--echo # Finally, test case in which deadlock (or potentially livelock) occurs +--echo # between metadata locking subsystem and table definition cache/table +--echo # locks, but which should still be detected by our empiric. +--echo # +create table t1 (i int); + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +lock tables t1 write; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Send: +--send insert into t1 values (2); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Wait until INSERT in connection 'deadlock_con1' is blocked on +--echo # table-level lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "insert into t1 values (2)"; +--source include/wait_condition.inc + +--echo # Send: +--send alter table t1 add column j int; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # The above ALTER TABLE statement should cause INSERT statement in +--echo # this connection to be aborted and emit ER_LOCK_DEADLOCK error. +--echo # Reap INSERT +--error ER_LOCK_DEADLOCK +--reap +--echo # Commit transaction to unblock ALTER TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap ALTER TABLE. +--reap +unlock tables; + +drop table t1; +disconnect deadlock_con1; +disconnect deadlock_con2; +disconnect deadlock_con3; + + --echo # --echo # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() --echo # on INSERT + CREATE TRIGGER". @@ -439,6 +799,66 @@ disconnect con46044_2; drop table t1; +--echo # +--echo # Test for bug #46273 "MySQL 5.4.4 new MDL: Bug#989 is not fully fixed +--echo # in case of ALTER". +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +set debug_sync= 'RESET'; +connect (con46273,localhost,root,,test,,); +connection default; +create table t1 (c1 int primary key, c2 int, c3 int); +insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); + +begin; +update t1 set c3=c3+1 where c2=3; + +--echo # +--echo # Switching to connection 'con46273'. +connection con46273; +set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR alter_go'; +--send alter table t1 add column e int, rename to t2; + +--echo # +--echo # Switching to connection 'default'. +connection default; +set debug_sync='now WAIT_FOR alter_table_locked'; +set debug_sync='wait_for_lock SIGNAL alter_go'; +--echo # The below statement should get ER_LOCK_DEADLOCK error +--echo # (i.e. it should not allow ALTER to proceed, and then +--echo # fail due to 't1' changing its name to 't2'). +--error ER_LOCK_DEADLOCK +update t1 set c3=c3+1 where c2=4; + +--echo # +--echo # Let us check that failure of the above statement has not released +--echo # metadata lock on table 't1', i.e. that ALTER TABLE is still blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 add column e int, rename to t2"; +--source include/wait_condition.inc + +--echo # Unblock ALTER TABLE by commiting transaction and thus releasing +--echo # metadata lock on 't1'. +commit; + +--echo # +--echo # Switching to connection 'con46273'. +connection con46273; +--echo # Reap ALTER TABLE. +--reap + +--echo # +--echo # Switching to connection 'default'. +connection default; +disconnect con46273; +--echo # Clean-up. +set debug_sync= 'RESET'; +drop table t2; + + --echo # --echo # Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK --echo # and DML". diff --git a/mysql-test/t/sp-lock.test b/mysql-test/t/sp-lock.test index a90b85a59be..0b31eabb0f1 100644 --- a/mysql-test/t/sp-lock.test +++ b/mysql-test/t/sp-lock.test @@ -153,20 +153,20 @@ drop temporary table t1; --echo # --echo # For that, start a transaction, use a routine. In a concurrent --echo # connection, try to drop or alter the routine. It should place ---echo # a pending or exlusive lock and block. In a concurrnet ---echo # connection, try to use the routine under LOCK TABLES. ---echo # That should yield ER_LOCK_DEADLOCK. +--echo # a pending or exclusive lock and block. In another concurrnet +--echo # connection, try to use the routine. +--echo # That should block on the pending exclusive lock. --echo # --echo # Establish helper connections. connect(con1, localhost, root,,); connect(con2, localhost, root,,); +connect(con3, localhost, root,,); --echo # --echo # Test DROP PROCEDURE. --echo # --echo # --> connection default connection default; -create table t1 (a int); create procedure p1() begin end; delimiter |; create function f1() returns int @@ -180,7 +180,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'drop procedure p1'... ---send drop procedure p1 +send drop procedure p1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop procedure t1' to get blocked on MDL lock... @@ -188,17 +188,25 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop procedure p1'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop procedure p1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +--error ER_SP_DOES_NOT_EXIST +reap; --echo # --> connection default connection default; @@ -211,7 +219,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'create procedure p1'... ---send create procedure p1() begin end +send create procedure p1() begin end; --echo # --> connection con2 connection con2; --echo # Waitng for 'create procedure t1' to get blocked on MDL lock... @@ -219,10 +227,13 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='create procedure p1() begin end'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; @@ -230,16 +241,22 @@ commit; connection con1; --echo # Reaping 'create procedure p1'... --error ER_SP_ALREADY_EXISTS ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; connection default; + --echo # --echo # Test ALTER PROCEDURE. +--echo # begin; select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter procedure p1'... ---send alter procedure p1 contains sql +send alter procedure p1 contains sql; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter procedure t1' to get blocked on MDL lock... @@ -247,17 +264,24 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='alter procedure p1 contains sql'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter procedure p1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; --echo # --> connection default connection default; @@ -269,7 +293,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1'... ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -277,17 +301,25 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop function f1'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +--error ER_SP_DOES_NOT_EXIST +reap; --echo # --> connection default connection default; @@ -300,7 +332,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'create function f1'... ---send create function f1() returns int return 2 +send create function f1() returns int return 2; --echo # --> connection con2 connection con2; --echo # Waitng for 'create function f1' to get blocked on MDL lock... @@ -308,10 +340,13 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='create function f1() returns int return 2'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; @@ -319,17 +354,23 @@ commit; connection con1; --echo # Reaping 'create function f1'... --error ER_SP_ALREADY_EXISTS ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; --echo # --> connection default connection default; + --echo # --echo # Test ALTER FUNCTION. +--echo # begin; select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter function f1'... ---send alter function f1 contains sql +send alter function f1 contains sql; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter function f1' to get blocked on MDL lock... @@ -337,17 +378,24 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='alter function f1 contains sql'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter function f1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; --echo # --> connection default connection default; drop function f1; @@ -363,6 +411,7 @@ drop procedure p1; --echo # create procedure p1() begin end; create procedure p2() begin end; +create table t1 (a int); delimiter |; create procedure p3() begin @@ -419,7 +468,7 @@ insert into t1 (a) values (1); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1' ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -432,7 +481,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; --echo # --> connection default connection default; --echo # @@ -445,7 +494,7 @@ select * from v1; --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1' ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -458,7 +507,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; --echo # --> connection default connection default; --echo # @@ -478,7 +527,7 @@ select * from v1; --echo # --> connection con1 connection con1; --echo # Sending 'drop procedure p1' ---send drop procedure p1 +send drop procedure p1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop procedure p1' to get blocked on MDL lock... @@ -491,7 +540,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop procedure p1'... ---reap +reap; --echo # --> connection default connection default; @@ -509,7 +558,7 @@ insert into t1 (a) values (3); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f2' ---send drop function f2 +send drop function f2; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f2' to get blocked on MDL lock... @@ -522,7 +571,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f2'... ---reap +reap; --echo # --> connection default connection default; @@ -536,42 +585,11 @@ drop table t1, t2; --echo # acquisition of a shared lock fails during a transaction or --echo # we need to back off to flush the sp cache. --echo # ---echo # a) A back off due to a lock conflict. ---echo # -create table t1 (a int); -create function f1() returns int return 6; -begin; -select f1(); ---echo # --> connection con1 -connection con1; ---echo # Sending 'drop function f1'... ---send drop function f1 ---echo # --> connection con2 -connection con2; ---echo # Waitng for 'drop function f1' to get blocked on MDL lock... -let $wait_condition=select count(*)=1 from information_schema.processlist -where state='Waiting for table' and info='drop function f1'; ---source include/wait_condition.inc -begin; -select * from t1; ---error ER_LOCK_DEADLOCK -select f1(); -commit; ---echo # --> connection default -connection default; -commit; ---echo # --> connection con1 -connection con1; ---echo # Reaping 'drop function f1'... ---reap ---echo # --> connection default -connection default; ---echo # ---echo # b) A back off to flush the cache. --echo # Sic: now this situation does not require a back off since we --echo # flush the cache on the fly. --echo # create function f1() returns int return 7; +create table t1 (a int); begin; select * from t1; # Used to have a back-off here, with optional ER_LOCK_DEADLOCK @@ -602,7 +620,7 @@ select f2(); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1'... ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -610,7 +628,7 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop function f1'; --source include/wait_condition.inc --echo # Sending 'drop function f2'... ---send drop function f2 +send drop function f2; --echo # --> connection default connection default; --echo # Waitng for 'drop function f2' to get blocked on MDL lock... @@ -621,14 +639,14 @@ rollback to savepoint sv; --echo # --> connection con2 connection con2; --echo # Reaping 'drop function f2'... ---reap +reap; --echo # --> connection default connection default; unlock tables; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; --echo # --> connection default connection default; --error ER_SP_DOES_NOT_EXIST @@ -678,7 +696,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter function f1 ...'... ---send alter function f1 comment "comment" +send alter function f1 comment "comment"; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... @@ -686,7 +704,7 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info like 'alter function f1 comment%'; --source include/wait_condition.inc --echo # Sending 'call p1()'... ---send call p1() +send call p1(); connection default; --echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... let $wait_condition=select count(*)=1 from information_schema.processlist @@ -697,11 +715,11 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter function f1 ...' ---reap +reap; --echo # --> connection con2 connection con2; --echo # Reaping 'call p1()'... ---reap +reap; deallocate prepare stmt; --echo # --> connection default connection default; @@ -725,7 +743,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter function f1 ...'... ---send alter function f1 comment "comment" +send alter function f1 comment "comment"; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... @@ -754,7 +772,7 @@ begin end| delimiter ;| --echo # Sending 'call p1()'... ---send call p1() +send call p1(); connection default; --echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... let $wait_condition=select count(*)=1 from information_schema.processlist @@ -765,11 +783,11 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter function f1 ...' ---reap +reap; --echo # --> connection con2 connection con2; --echo # Reaping 'call p1()'... ---reap +reap; --echo # --> connection default connection default; drop function f1; @@ -804,7 +822,7 @@ select get_lock("30977", 0); --echo # --> connection default connection default; --echo # Sending 'select f3()'... ---send select f3() +send select f3(); --echo # --> connection con1 connection con1; --echo # Waitng for 'select f3()' to get blocked on the user level lock... @@ -819,7 +837,7 @@ select release_lock("30977"); connection default; --echo # Reaping 'select f3()'... --echo # Routine 'f2()' should exist and get executed successfully. ---reap +reap; select @var; drop function f1; drop function f2; @@ -869,6 +887,10 @@ disconnect con1; connection con2; disconnect con2; --source include/wait_until_disconnected.inc +--echo # --> connection con3 +connection con3; +disconnect con3; +--source include/wait_until_disconnected.inc --echo # --> connection default connection default; --echo # diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 942396fc3da..0f6f4d1d0e5 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1506,7 +1506,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) */ thd->binlog_flush_pending_rows_event(false); TABLE_LIST *tables= rli->tables_to_lock; - close_tables_for_reopen(thd, &tables); + close_tables_for_reopen(thd, &tables, NULL); uint tables_count= rli->tables_to_lock_count; if ((error= open_tables(thd, &tables, &tables_count, 0))) diff --git a/sql/mdl.cc b/sql/mdl.cc index 40074879e21..af7f310e598 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -196,6 +196,7 @@ void MDL_context::init(THD *thd_arg) to empty the list. */ m_tickets.empty(); + m_is_waiting_in_mdl= FALSE; } @@ -803,14 +804,28 @@ MDL_context::clone_ticket(MDL_request *mdl_request) @retval FALSE Lock is not a shared one or no thread was woken up */ -static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) +bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) { bool woke= FALSE; if (conflicting_ticket->is_shared()) { THD *conflicting_thd= conflicting_ticket->get_ctx()->get_thd(); DBUG_ASSERT(thd != conflicting_thd); /* Self-deadlock */ - woke= mysql_notify_thread_having_shared_lock(thd, conflicting_thd); + + /* + If the thread that holds the conflicting lock is waiting + on an MDL lock, wake it up by broadcasting on COND_mdl. + Otherwise it must be waiting on a table-level lock + or some other non-MDL resource, so delegate its waking up + to an external call. + */ + if (conflicting_ticket->get_ctx()->is_waiting_in_mdl()) + { + pthread_cond_broadcast(&COND_mdl); + woke= TRUE; + } + else + woke= mysql_notify_thread_having_shared_lock(thd, conflicting_thd); } return woke; } @@ -957,7 +972,7 @@ bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests) to abort this thread once again. */ struct timespec abstime; - set_timespec(abstime, 10); + set_timespec(abstime, 1); pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); } if (mysys_var->abort) @@ -1032,6 +1047,7 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; THD *thd= m_ctx->get_thd(); + MDL_ticket *pending_ticket; DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive"); DEBUG_SYNC(thd, "mdl_upgrade_shared_lock_to_exclusive"); @@ -1045,8 +1061,22 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() /* Only allow upgrades from MDL_SHARED_UPGRADABLE */ DBUG_ASSERT(m_type == MDL_SHARED_UPGRADABLE); + /* + Create an auxiliary ticket to represent a pending exclusive + lock and add it to the 'waiting' queue for the duration + of upgrade. During upgrade we abort waits of connections + that own conflicting locks. A pending request is used + to signal such connections that upon waking up they + must back off, rather than fall into sleep again. + */ + if (! (pending_ticket= MDL_ticket::create(m_ctx, MDL_EXCLUSIVE))) + DBUG_RETURN(TRUE); + pthread_mutex_lock(&LOCK_mdl); + pending_ticket->m_lock= m_lock; + m_lock->waiting.push_front(pending_ticket); + old_msg= MDL_ENTER_COND(thd, mysys_var); /* @@ -1088,6 +1118,30 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() MDL_ticket *conflicting_ticket; MDL_lock::Ticket_iterator it(m_lock->granted); + /* + A temporary work-around to avoid deadlocks/livelocks in + a situation when in one connection ALTER TABLE tries to + upgrade its metadata lock and in another connection + the active transaction already got this lock in some + of its earlier statements. + In such case this transaction always succeeds with getting + a metadata lock on the table -- it already has one. + But later on it may block on the table level lock, since ALTER + got TL_WRITE_ALLOW_READ, and subsequently get aborted + by notify_shared_lock(). + An abort will lead to a back off, and a second attempt to + get an MDL lock (successful), and a table lock (-> livelock). + + The call below breaks this loop by forcing transactions to call + tdc_wait_for_old_versions() (even if the transaction doesn't need + any new metadata locks), which in turn will check if someone + is waiting on the owned MDL lock, and produce ER_LOCK_DEADLOCK. + + TODO: Long-term such deadlocks/livelock will be resolved within + MDL subsystem and thus this call will become unnecessary. + */ + mysql_abort_transactions_with_shared_lock(&m_lock->key); + while ((conflicting_ticket= it++)) { if (conflicting_ticket->m_ctx != m_ctx) @@ -1108,12 +1162,15 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() to abort this thread once again. */ struct timespec abstime; - set_timespec(abstime, 10); + set_timespec(abstime, 1); DBUG_PRINT("info", ("Failed to wake-up from table-level lock ... sleeping")); pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime); } if (mysys_var->abort) { + /* Remove and destroy the auxiliary pending ticket. */ + m_lock->waiting.remove(pending_ticket); + MDL_ticket::destroy(pending_ticket); /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); MDL_EXIT_COND(thd, mysys_var, old_msg); @@ -1124,6 +1181,11 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() m_lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE; /* Set the new type of lock in the ticket. */ m_type= MDL_EXCLUSIVE; + + /* Remove and destroy the auxiliary pending ticket. */ + m_lock->waiting.remove(pending_ticket); + MDL_ticket::destroy(pending_ticket); + if (m_lock->cached_object) (*m_lock->cached_object_release_hook)(m_lock->cached_object); m_lock->cached_object= 0; @@ -1239,6 +1301,59 @@ bool MDL_context::acquire_global_shared_lock() } +/** + Check if there are any pending exclusive locks which conflict + with shared locks held by this thread. + + @pre The caller already has acquired LOCK_mdl. + + @return TRUE If there are any pending conflicting locks. + FALSE Otherwise. +*/ + +bool MDL_context::can_wait_lead_to_deadlock_impl() const +{ + Ticket_iterator ticket_it(m_tickets); + MDL_ticket *ticket; + + while ((ticket= ticket_it++)) + { + /* + In MySQL we never call this method while holding exclusive or + upgradeable shared metadata locks. + Otherwise we would also have to check for the presence of pending + requests for conflicting types of global lock. + In addition MDL_ticket::has_pending_conflicting_lock_impl() + won't work properly for exclusive type of lock. + */ + DBUG_ASSERT(! ticket->is_upgradable_or_exclusive()); + + if (ticket->has_pending_conflicting_lock_impl()) + return TRUE; + } + return FALSE; +} + + +/** + Implement a simple deadlock detection heuristic: check if there + are any pending exclusive locks which conflict with shared locks + held by this thread. In that case waiting can be circular, + i.e. lead to a deadlock. + + @return TRUE if there are any conflicting locks, FALSE otherwise. +*/ + +bool MDL_context::can_wait_lead_to_deadlock() const +{ + bool result; + pthread_mutex_lock(&LOCK_mdl); + result= can_wait_lead_to_deadlock_impl(); + pthread_mutex_unlock(&LOCK_mdl); + return result; +} + + /** Wait until there will be no locks that conflict with lock requests in the given list. @@ -1249,7 +1364,7 @@ bool MDL_context::acquire_global_shared_lock() Does not acquire the locks! @retval FALSE Success. One can try to obtain metadata locks. - @retval TRUE Failure (thread was killed) + @retval TRUE Failure (thread was killed or deadlock is possible). */ bool @@ -1278,6 +1393,26 @@ MDL_context::wait_for_locks(MDL_request_list *mdl_requests) mysql_ha_flush(m_thd); pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(m_thd, mysys_var); + + /* + In cases when we wait while still holding some metadata + locks deadlocks are possible. + To avoid them we use the following simple empiric - don't + wait for new lock request to be satisfied if for one of the + locks which are already held by this connection there is + a conflicting request (i.e. this connection should not wait + if someone waits for it). + This empiric should work well (e.g. give low number of false + negatives) in situations when conflicts are rare (in our + case this is true since DDL statements should be rare). + */ + if (can_wait_lead_to_deadlock_impl()) + { + MDL_EXIT_COND(m_thd, mysys_var, old_msg); + my_error(ER_LOCK_DEADLOCK, MYF(0)); + return TRUE; + } + it.rewind(); while ((mdl_request= it++)) { @@ -1301,7 +1436,9 @@ MDL_context::wait_for_locks(MDL_request_list *mdl_requests) MDL_EXIT_COND(m_thd, mysys_var, old_msg); break; } + m_is_waiting_in_mdl= TRUE; pthread_cond_wait(&COND_mdl, &LOCK_mdl); + m_is_waiting_in_mdl= FALSE; /* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */ MDL_EXIT_COND(m_thd, mysys_var, old_msg); } @@ -1550,21 +1687,38 @@ MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, existing shared lock. @pre The ticket must match an acquired lock. + @pre The caller already has acquired LOCK_mdl. - @param ticket Shared lock against which check should be performed. + @return TRUE if there is a conflicting lock request, FALSE otherwise. +*/ - @return TRUE if there are any conflicting locks, FALSE otherwise. +bool MDL_ticket::has_pending_conflicting_lock_impl() const +{ + DBUG_ASSERT(is_shared()); + safe_mutex_assert_owner(&LOCK_mdl); + + return !m_lock->waiting.is_empty(); +} + + +/** + Check if we have any pending exclusive locks which conflict with + existing shared lock. + + @pre The ticket must match an acquired lock. + + @return TRUE if there is a pending conflicting lock request, + FALSE otherwise. */ bool MDL_ticket::has_pending_conflicting_lock() const { bool result; - DBUG_ASSERT(is_shared()); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - result= !m_lock->waiting.is_empty(); + result= has_pending_conflicting_lock_impl(); pthread_mutex_unlock(&LOCK_mdl); return result; } diff --git a/sql/mdl.h b/sql/mdl.h index e85f1232ff9..8edbfbc0777 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -300,6 +300,8 @@ private: private: MDL_ticket(const MDL_ticket &); /* not implemented */ MDL_ticket &operator=(const MDL_ticket &); /* not implemented */ + + bool has_pending_conflicting_lock_impl() const; }; @@ -380,10 +382,19 @@ public: void release_transactional_locks(); void rollback_to_savepoint(MDL_ticket *mdl_savepoint); + bool can_wait_lead_to_deadlock() const; + inline THD *get_thd() const { return m_thd; } + + bool is_waiting_in_mdl() const { return m_is_waiting_in_mdl; } private: Ticket_list m_tickets; bool m_has_global_shared_lock; + /** + Indicates that the owner of this context is waiting in + wait_for_locks() method. + */ + bool m_is_waiting_in_mdl; /** This member has two uses: 1) When entering LOCK TABLES mode, remember the last taken @@ -397,6 +408,7 @@ private: THD *m_thd; private: void release_ticket(MDL_ticket *ticket); + bool can_wait_lead_to_deadlock_impl() const; MDL_ticket *find_ticket(MDL_request *mdl_req, bool *is_lt_or_ha); void release_locks_stored_before(MDL_ticket *sentinel); @@ -413,6 +425,7 @@ void mdl_destroy(); extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use); extern void mysql_ha_flush(THD *thd); +extern void mysql_abort_transactions_with_shared_lock(const MDL_key *mdl_key); extern "C" const char *set_thd_proc_info(THD *thd, const char *info, const char *calling_function, const char *calling_file, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 124392f4c63..bb5bf428ef0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1522,7 +1522,8 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables); +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, + MDL_ticket *mdl_savepoint); TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, const char *db_name, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 78bb9f9bad7..d02e4f38807 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2142,25 +2142,13 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function) { - enum thr_lock_type old_lock_type; DBUG_ENTER("wait_while_table_is_used"); DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", table->s->table_name.str, (ulong) table->s, table->db_stat, table->s->version)); - /* Ensure no one can reopen table before it's removed */ - pthread_mutex_lock(&LOCK_open); - table->s->version= 0; - pthread_mutex_unlock(&LOCK_open); - - old_lock_type= table->reginfo.lock_type; - mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ - if (table->mdl_ticket->upgrade_shared_lock_to_exclusive()) - { - mysql_lock_downgrade_write(thd, table, old_lock_type); DBUG_RETURN(TRUE); - } pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, @@ -3722,9 +3710,10 @@ end_with_lock_open: Open_table_context::Open_table_context(THD *thd) :m_action(OT_NO_ACTION), - m_can_deadlock((thd->in_multi_stmt_transaction() || - thd->mdl_context.lt_or_ha_sentinel())&& - thd->mdl_context.has_locks()) + m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()), + m_has_locks((thd->in_multi_stmt_transaction() || + thd->mdl_context.lt_or_ha_sentinel()) && + thd->mdl_context.has_locks()) {} @@ -3741,12 +3730,22 @@ Open_table_context:: request_backoff_action(enum_open_table_action action_arg) { /* - We have met a exclusive metadata lock or a old version of - table and we are inside a transaction that already hold locks. - We can't follow the locking protocol in this scenario as it - might lead to deadlocks. + We are inside a transaction that already holds locks and have + met a broken table or a table which needs re-discovery. + Performing any recovery action requires acquiring an exclusive + metadata lock on this table. Doing that with locks breaks the + metadata locking protocol and might lead to deadlocks, + so we report an error. + + However, if we have only met a conflicting lock or an old + TABLE version, and just need to wait for the conflict to + disappear/old version to go away, allow waiting. + While waiting, we use a simple empiric to detect + deadlocks: we never wait on someone who's waiting too. + Waiting will be done after releasing metadata locks acquired + by this statement. */ - if (m_can_deadlock) + if (m_has_locks && action_arg != OT_WAIT) { my_error(ER_LOCK_DEADLOCK, MYF(0)); return TRUE; @@ -4364,7 +4363,7 @@ restart: elements from the table list (if MERGE tables are involved), */ TABLE_LIST *failed_table= *table_to_open; - close_tables_for_reopen(thd, start); + close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp()); /* Here we rely on the fact that 'tables' still points to the valid @@ -4414,7 +4413,8 @@ restart: { if (ot_ctx.can_recover_from_failed_open()) { - close_tables_for_reopen(thd, start); + close_tables_for_reopen(thd, start, + ot_ctx.start_of_statement_svp()); if (ot_ctx.recover_from_failed_open(thd, &rt->mdl_request, NULL)) goto err; @@ -4827,14 +4827,14 @@ retry: while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) && ot_ctx.can_recover_from_failed_open()) { - /* We can't back off with an open HANDLER, we don't wait with locks. */ + /* We never have an open HANDLER or LOCK TABLES here. */ DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); /* Even though we have failed to open table we still need to call release_transactional_locks() to release metadata locks which might have been acquired successfully. */ - thd->mdl_context.release_transactional_locks(); + thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp()); table_list->mdl_request.ticket= 0; if (ot_ctx.recover_from_failed_open(thd, &table_list->mdl_request, table_list)) @@ -4876,24 +4876,13 @@ retry: { if (refresh) { - if (ot_ctx.can_deadlock()) - { - my_error(ER_LOCK_DEADLOCK, MYF(0)); - table= 0; - } - else - { - close_thread_tables(thd); - table_list->table= NULL; - table_list->mdl_request.ticket= NULL; - /* - We can't back off with an open HANDLER, - we don't wait with locks. - */ - DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); - thd->mdl_context.release_transactional_locks(); - goto retry; - } + close_thread_tables(thd); + table_list->table= NULL; + table_list->mdl_request.ticket= NULL; + /* We never have an open HANDLER or LOCK TABLES here. */ + DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); + thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp()); + goto retry; } else table= 0; @@ -4941,7 +4930,16 @@ bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, { uint counter; bool need_reopen; - bool has_locks= thd->mdl_context.has_locks(); + /* + Remember the set of metadata locks which this connection + managed to acquire before the start of the current statement. + It can be either transaction-scope locks, or HANDLER locks, + or LOCK TABLES locks. If mysql_lock_tables() fails with + need_reopen request, we'll use it to instruct + close_tables_for_reopen() to release all locks of this + statement. + */ + MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_and_lock_tables_derived"); DBUG_PRINT("enter", ("derived handling: %d", derived)); @@ -4960,13 +4958,7 @@ bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, break; if (!need_reopen) DBUG_RETURN(TRUE); - if ((thd->in_multi_stmt_transaction() || - thd->mdl_context.lt_or_ha_sentinel()) && has_locks) - { - my_error(ER_LOCK_DEADLOCK, MYF(0)); - DBUG_RETURN(TRUE); - } - close_tables_for_reopen(thd, &tables); + close_tables_for_reopen(thd, &tables, start_of_statement_svp); } if (derived && (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -5280,6 +5272,8 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, flags, need_reopen))) DBUG_RETURN(TRUE); + DEBUG_SYNC(thd, "after_lock_tables_takes_lock"); + if (thd->lex->requires_prelocking() && thd->lex->sql_command != SQLCOM_LOCK_TABLES) { @@ -5379,18 +5373,24 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, } -/* +/** Prepare statement for reopening of tables and recalculation of set of prelocked tables. - SYNOPSIS - close_tables_for_reopen() - thd in Thread context - tables in/out List of tables which we were trying to open and lock - + @param[in] thd Thread context. + @param[in,out] tables List of tables which we were trying to open + and lock. + @param[in] start_of_statement_svp MDL savepoint which represents the set + of metadata locks which the current transaction + managed to acquire before execution of the current + statement and to which we should revert before + trying to reopen tables. NULL if no metadata locks + were held and thus all metadata locks should be + released. */ -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, + MDL_ticket *start_of_statement_svp) { TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); TABLE_LIST *tmp; @@ -5425,13 +5425,7 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) for (tmp= first_not_own_table; tmp; tmp= tmp->next_global) tmp->mdl_request.ticket= NULL; close_thread_tables(thd); - /* We can't back off with an open HANDLERs, we must not wait with locks. */ - DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); - /* - Due to the above assert, this effectively releases *all* locks - of this session, so that we can safely wait on tables. - */ - thd->mdl_context.release_transactional_locks(); + thd->mdl_context.rollback_to_savepoint(start_of_statement_svp); } @@ -8413,6 +8407,8 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use) But in case a thread has an open HANDLER statement, (and thus already grabbed a metadata lock), it gets blocked only too late -- at the table cache level. + Starting from 5.5, this could also easily happen in + a multi-statement transaction. */ broadcast_refresh(); pthread_mutex_unlock(&LOCK_open); @@ -8420,6 +8416,28 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use) } +/** + Force transactions holding shared metadata lock on the table to call + MDL_context::can_wait_lead_to_deadlock() even if they don't need any + new metadata locks so they can detect potential deadlocks between + metadata locking subsystem and table-level locks. + + @param mdl_key MDL key for the table on which we are upgrading + metadata lock. +*/ + +void mysql_abort_transactions_with_shared_lock(const MDL_key *mdl_key) +{ + if (mdl_key->mdl_namespace() == MDL_key::TABLE) + { + pthread_mutex_lock(&LOCK_open); + tdc_remove_table(NULL, TDC_RT_REMOVE_UNUSED, mdl_key->db_name(), + mdl_key->name()); + pthread_mutex_unlock(&LOCK_open); + } +} + + /** Remove all or some (depending on parameter) instances of TABLE and TABLE_SHARE from the table definition cache. @@ -8525,6 +8543,25 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) to broadcast on COND_refresh because of this. */ mysql_ha_flush(thd); + + /* + Check if there is someone waiting for one of metadata locks + held by this connection and return an error if that's the + case, since this situation may lead to a deadlock. + This can happen, when, for example, this connection is + waiting for an old version of some table to go away and + another connection is trying to upgrade its shared + metadata lock to exclusive, and thus is waiting + for this to release its lock. We must check for + the condition on each iteration of the loop to remove + any window for a race. + */ + if (thd->mdl_context.can_wait_lead_to_deadlock()) + { + my_error(ER_LOCK_DEADLOCK, MYF(0)); + return TRUE; + } + pthread_mutex_lock(&LOCK_open); MDL_request_list::Iterator it(*mdl_requests); diff --git a/sql/sql_class.h b/sql/sql_class.h index ff1b51e7e87..5654dcb07a6 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1297,18 +1297,29 @@ public: bool can_recover_from_failed_open() const { return m_action != OT_NO_ACTION; } - bool can_deadlock() const { return m_can_deadlock; } + + /** + When doing a back-off, we close all tables acquired by this + statement. Return an MDL savepoint taken at the beginning of + the statement, so that we can rollback to it before waiting on + locks. + */ + MDL_ticket *start_of_statement_svp() const + { + return m_start_of_statement_svp; + } private: /** List of requests for all locks taken so far. Used for waiting on locks. */ MDL_request_list m_mdl_requests; /** Back off action. */ enum enum_open_table_action m_action; + MDL_ticket *m_start_of_statement_svp; /** Whether we had any locks when this context was created. If we did, they are from the previous statement of a transaction, and we can't safely do back-off (and release them). */ - bool m_can_deadlock; + bool m_has_locks; }; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 40ef55423a9..f2478213bbe 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2530,7 +2530,7 @@ pthread_handler_t handle_delayed_insert(void *arg) aborted. Try to reopen table and if it fails die. */ TABLE_LIST *tl_ptr = &di->table_list; - close_tables_for_reopen(thd, &tl_ptr); + close_tables_for_reopen(thd, &tl_ptr, NULL); di->table= 0; if (di->open_and_lock_table()) { diff --git a/sql/sql_plist.h b/sql/sql_plist.h index 94e437362a9..8f2aee6bd5f 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -132,11 +132,11 @@ public: template class I_P_List_iterator { - I_P_List *list; + const I_P_List *list; T *current; public: - I_P_List_iterator(I_P_List &a) : list(&a), current(a.first) {} - I_P_List_iterator(I_P_List &a, T* current_arg) : list(&a), current(current_arg) {} + I_P_List_iterator(const I_P_List &a) : list(&a), current(a.first) {} + I_P_List_iterator(const I_P_List &a, T* current_arg) : list(&a), current(current_arg) {} inline void init(I_P_List &a) { list= &a; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 72fb49cf38c..e9d1426b3e3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2941,7 +2941,7 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, table, res, db_name, table_name)); thd->temporary_tables= 0; - close_tables_for_reopen(thd, &show_table_list); + close_tables_for_reopen(thd, &show_table_list, NULL); DBUG_RETURN(error); } @@ -3500,7 +3500,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) res= schema_table->process_table(thd, show_table_list, table, res, &orig_db_name, &tmp_lex_string); - close_tables_for_reopen(thd, &show_table_list); + close_tables_for_reopen(thd, &show_table_list, NULL); } DBUG_ASSERT(!lex->query_tables_own_last); if (res) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 603ab1b9682..980f87f21bb 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -206,6 +206,7 @@ int mysql_update(THD *thd, ulonglong id; List all_fields; THD::killed_state killed_status= THD::NOT_KILLED; + MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("mysql_update"); for ( ; ; ) @@ -226,7 +227,7 @@ int mysql_update(THD *thd, break; if (!need_reopen) DBUG_RETURN(1); - close_tables_for_reopen(thd, &table_list); + close_tables_for_reopen(thd, &table_list, start_of_statement_svp); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -981,6 +982,7 @@ int mysql_multi_update_prepare(THD *thd) const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI); bool need_reopen= FALSE; + MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("mysql_multi_update_prepare"); /* following need for prepared statements, to run next time multi-update */ @@ -1145,7 +1147,7 @@ reopen_tables: */ cleanup_items(thd->free_list); - close_tables_for_reopen(thd, &table_list); + close_tables_for_reopen(thd, &table_list, start_of_statement_svp); goto reopen_tables; } From 7edec1218595cf98edaac99a4abe6645cd2dbee2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 5 Jan 2010 02:26:20 +0100 Subject: [PATCH 202/466] Fix the last merge --- config.h.cmake | 1 + configure.cmake | 4 ++-- storage/example/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config.h.cmake b/config.h.cmake index 259a6eb66d0..452525da24e 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -602,6 +602,7 @@ #define HAVE_SPATIAL 1 #define HAVE_RTREE_KEYS 1 #define HAVE_QUERY_CACHE 1 +#define BIG_TABLES 1 /* Important storage engines (those that really need define diff --git a/configure.cmake b/configure.cmake index 353d70aa571..7b64aa77061 100644 --- a/configure.cmake +++ b/configure.cmake @@ -660,8 +660,8 @@ CHECK_FUNCTION_EXISTS_UNIX(rdtscll HAVE_RDTSCLL) CHECK_SYMBOL_EXISTS_UNIX(sys_errlist "stdio.h" HAVE_SYS_ERRLIST) CHECK_SYMBOL_EXISTS_UNIX(madvise "sys/mman.h" HAVE_DECL_MADVISE) -CHECK_SYMBOL_EXISTS_UNIX(tzname "time.h" HAVE_TZNAME) -CHECK_SYMBOL_EXISTS(lrand48 "stdlib.h" HAVE_LRAND48) +CHECK_SYMBOL_EXISTS(tzname "time.h" HAVE_TZNAME) +CHECK_SYMBOL_EXISTS_UNIX(lrand48 "stdlib.h" HAVE_LRAND48) CHECK_SYMBOL_EXISTS_UNIX(getpagesize "unistd.h" HAVE_GETPAGESIZE) CHECK_SYMBOL_EXISTS_UNIX(TIOCGWINSZ "sys/ioctl.h" GWINSZ_IN_SYS_IOCTL) CHECK_SYMBOL_EXISTS_UNIX(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL) diff --git a/storage/example/CMakeLists.txt b/storage/example/CMakeLists.txt index 109f4bfa844..5a6f5d78c6b 100644 --- a/storage/example/CMakeLists.txt +++ b/storage/example/CMakeLists.txt @@ -15,4 +15,4 @@ SET(EXAMPLE_PLUGIN_DYNAMIC "ha_example") SET(EXAMPLE_SOURCES ha_example.cc) -MYSQL_ADD_PLUGIN(example ${EXAMPLE_SOURCES} STORAGE_ENGINE) +MYSQL_ADD_PLUGIN(example ${EXAMPLE_SOURCES} STORAGE_ENGINE MODULE_ONLY) From c7a1f5212c17a95cf91f4f83877d4b10ac6ede83 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 5 Jan 2010 13:05:00 +0100 Subject: [PATCH 203/466] Bug #49166 mtr --combination is broken after restrictions of combination names Combinations beginning with -- not allowed Allow them... --- mysql-test/mysql-test-run.pl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 7d2426459d0..27c2ace56b2 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3251,9 +3251,11 @@ sub run_testcase ($) { mtr_verbose("Running test:", $tinfo->{name}); - # Allow only alpanumerics pluss _ - + . in combination names + # Allow only alpanumerics pluss _ - + . in combination names, + # or anything beginning with -- (the latter comes from --combination) my $combination= $tinfo->{combination}; - if ($combination && $combination !~ /^\w[-\w\.\+]+$/) + if ($combination && $combination !~ /^\w[-\w\.\+]+$/ + && $combination !~ /^--/) { mtr_error("Combination '$combination' contains illegal characters"); } From eec8deed8b14aa20e58a4698f657f60583b6ab34 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 5 Jan 2010 13:31:38 +0100 Subject: [PATCH 204/466] Bug #49166 mtr --combination is broken after restrictions of combination names Combinations beginning with -- not allowed Allow them... --- mysql-test/mysql-test-run.pl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 7d2426459d0..27c2ace56b2 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3251,9 +3251,11 @@ sub run_testcase ($) { mtr_verbose("Running test:", $tinfo->{name}); - # Allow only alpanumerics pluss _ - + . in combination names + # Allow only alpanumerics pluss _ - + . in combination names, + # or anything beginning with -- (the latter comes from --combination) my $combination= $tinfo->{combination}; - if ($combination && $combination !~ /^\w[-\w\.\+]+$/) + if ($combination && $combination !~ /^\w[-\w\.\+]+$/ + && $combination !~ /^--/) { mtr_error("Combination '$combination' contains illegal characters"); } From 2def71f0c163cd13c30fcc54b66a7580b61485a5 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 5 Jan 2010 13:55:58 +0100 Subject: [PATCH 205/466] Fix mysqld--help to ignore optional engines --- mysql-test/include/mysqld--help.inc | 4 ++-- mysql-test/mysql-test-run.pl | 2 +- mysql-test/r/mysqld--help-notwin.result | 13 ------------- mysql-test/r/mysqld--help-win.result | 13 ------------- 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc index d2ae312406f..630d16d4dee 100644 --- a/mysql-test/include/mysqld--help.inc +++ b/mysql-test/include/mysqld--help.inc @@ -10,9 +10,9 @@ exec $MYSQLD_BOOTSTRAP_CMD --symbolic-links=0 --lower-case-table-names=1 --help --verbose > $MYSQL_TMP_DIR/mysqld--help.txt 2>&1; perl; - @skipvars=qw/basedir open-files-limit general-log-file log + @skipvars=qw/basedir open-files-limit general-log-file log plugin-dir log-slow-queries pid-file slow-query-log-file/; - @plugins=qw/innodb ndb ndbcluster safemalloc debug temp-pool ssl des-key-file + @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster safemalloc debug temp-pool ssl des-key-file thread-concurrency super-large-pages mutex-deadlock-detector/; @env=qw/MYSQLTEST_VARDIR MYSQL_TEST_DIR MYSQL_LIBDIR MYSQL_CHARSETSDIR MYSQL_SHAREDIR /; $re1=join('|', @skipvars, @plugins); diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 3c423712ab9..81adcb49a33 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1975,7 +1975,7 @@ sub environment_setup { $ENV{'DEFAULT_MASTER_PORT'}= $mysqld_variables{'port'}; $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; - $ENV{'MYSQL_LIBDIR'}= "$bindir/lib"; + $ENV{'MYSQL_LIBDIR'}= "$basedir/lib"; $ENV{'MYSQL_SHAREDIR'}= $path_language; $ENV{'MYSQL_CHARSETSDIR'}= $path_charsetsdir; diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 0ebd3e2cb42..b3c162d82d3 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -14,8 +14,6 @@ The following options may be given as the first argument: library, for example exit() from libc.so -a, --ansi Use ANSI SQL syntax instead of MySQL syntax. This mode will also set transaction isolation level 'serializable'. - --archive[=name] Enable or disable ARCHIVE plugin. Possible values are ON, - OFF, FORCE (don't start if the plugin fails to load). --auto-increment-increment[=#] Auto-increment columns are incremented by this --auto-increment-offset[=#] @@ -59,8 +57,6 @@ The following options may be given as the first argument: The maximum size of a row-based binary log event in bytes. Rows will be grouped into events smaller than this size if possible. The value has to be a multiple of 256. - --blackhole[=name] Enable or disable BLACKHOLE plugin. Possible values are - ON, OFF, FORCE (don't start if the plugin fails to load). --bootstrap Used by mysql installation scripts. --bulk-insert-buffer-size=# Size of tree cache used in bulk insert optimisation. Note @@ -147,8 +143,6 @@ The following options may be given as the first argument: With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running. Disable with --skip-external-locking. - --federated[=name] Enable or disable FEDERATED plugin. Possible values are - ON, OFF, FORCE (don't start if the plugin fails to load). --flush Flush MyISAM tables to disk between SQL commands --flush-time=# A dedicated thread is created to flush all tables at the given interval @@ -415,8 +409,6 @@ The following options may be given as the first argument: is one of {index_merge, index_merge_union, index_merge_sort_union, index_merge_intersection} and val is one of {on, off, default} - --partition[=name] Enable or disable partition plugin. Possible values are - ON, OFF, FORCE (don't start if the plugin fails to load). --pid-file=name Pid file used by safe_mysqld --plugin-dir=name Directory for plugins --plugin-load=name Optional semicolon-separated list of plugins to load, @@ -720,7 +712,6 @@ and boolean options {FALSE|TRUE} Value (after reading options) ----------------------------------- -------------------------------------- abort-slave-event-count 0 allow-suspicious-udfs FALSE -archive ON auto-increment-increment 1 auto-increment-offset 1 automatic-sp-privileges TRUE @@ -730,7 +721,6 @@ bind-address (No default value) binlog-cache-size 32768 binlog-format STATEMENT binlog-row-event-max-size 1024 -blackhole ON bulk-insert-buffer-size 8388608 character-set-client-handshake TRUE character-set-filesystem binary @@ -761,7 +751,6 @@ engine-condition-pushdown TRUE event-scheduler OFF expire-logs-days 0 external-locking FALSE -federated ON flush FALSE flush-time 0 ft-boolean-syntax + -><()~*:""&| @@ -854,8 +843,6 @@ old-style-user-limits FALSE optimizer-prune-level 1 optimizer-search-depth 62 optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on -partition ON -plugin-dir MYSQL_LIBDIR/mysql/plugin plugin-load (No default value) port 3306 port-open-timeout 0 diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index b42cb522c41..dfaf33f301d 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -14,8 +14,6 @@ The following options may be given as the first argument: library, for example exit() from libc.so -a, --ansi Use ANSI SQL syntax instead of MySQL syntax. This mode will also set transaction isolation level 'serializable'. - --archive[=name] Enable or disable ARCHIVE plugin. Possible values are ON, - OFF, FORCE (don't start if the plugin fails to load). --auto-increment-increment[=#] Auto-increment columns are incremented by this --auto-increment-offset[=#] @@ -59,8 +57,6 @@ The following options may be given as the first argument: The maximum size of a row-based binary log event in bytes. Rows will be grouped into events smaller than this size if possible. The value has to be a multiple of 256. - --blackhole[=name] Enable or disable BLACKHOLE plugin. Possible values are - ON, OFF, FORCE (don't start if the plugin fails to load). --bootstrap Used by mysql installation scripts. --bulk-insert-buffer-size=# Size of tree cache used in bulk insert optimisation. Note @@ -147,8 +143,6 @@ The following options may be given as the first argument: With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running. Disable with --skip-external-locking. - --federated[=name] Enable or disable FEDERATED plugin. Possible values are - ON, OFF, FORCE (don't start if the plugin fails to load). --flush Flush MyISAM tables to disk between SQL commands --flush-time=# A dedicated thread is created to flush all tables at the given interval @@ -415,8 +409,6 @@ The following options may be given as the first argument: is one of {index_merge, index_merge_union, index_merge_sort_union, index_merge_intersection} and val is one of {on, off, default} - --partition[=name] Enable or disable partition plugin. Possible values are - ON, OFF, FORCE (don't start if the plugin fails to load). --pid-file=name Pid file used by safe_mysqld --plugin-dir=name Directory for plugins --plugin-load=name Optional semicolon-separated list of plugins to load, @@ -724,7 +716,6 @@ and boolean options {FALSE|TRUE} Value (after reading options) --------------------------------- ---------------------------------------- abort-slave-event-count 0 allow-suspicious-udfs FALSE -archive ON auto-increment-increment 1 auto-increment-offset 1 automatic-sp-privileges TRUE @@ -734,7 +725,6 @@ bind-address (No default value) binlog-cache-size 32768 binlog-format STATEMENT binlog-row-event-max-size 1024 -blackhole ON bulk-insert-buffer-size 8388608 character-set-client-handshake TRUE character-set-filesystem binary @@ -765,7 +755,6 @@ engine-condition-pushdown TRUE event-scheduler OFF expire-logs-days 0 external-locking FALSE -federated ON flush FALSE flush-time 1800 ft-boolean-syntax + -><()~*:""&| @@ -858,8 +847,6 @@ old-style-user-limits FALSE optimizer-prune-level 1 optimizer-search-depth 62 optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on -partition ON -plugin-dir MYSQL_LIBDIR/plugin plugin-load (No default value) port 3306 port-open-timeout 0 From 7accdb2af645f9abd21cb8f896d4bdf1bcae3760 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 5 Jan 2010 16:30:41 +0100 Subject: [PATCH 206/466] fix white space differences in mysqld--help-notwin.result --- mysql-test/r/mysqld--help-notwin.result | 424 ++++++++++++------------ 1 file changed, 212 insertions(+), 212 deletions(-) diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index b3c162d82d3..2fad24a93b5 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -708,218 +708,218 @@ The following options may be given as the first argument: -W, --warnings[=#] Deprecated; use --log-warnings instead. Variables (--variable-name=value) -and boolean options {FALSE|TRUE} Value (after reading options) ------------------------------------ -------------------------------------- -abort-slave-event-count 0 -allow-suspicious-udfs FALSE -auto-increment-increment 1 -auto-increment-offset 1 -automatic-sp-privileges TRUE -back-log 50 -big-tables FALSE -bind-address (No default value) -binlog-cache-size 32768 -binlog-format STATEMENT -binlog-row-event-max-size 1024 -bulk-insert-buffer-size 8388608 -character-set-client-handshake TRUE -character-set-filesystem binary -character-set-server latin1 -character-sets-dir MYSQL_CHARSETSDIR/ -chroot (No default value) -collation-server latin1_swedish_ci -completion-type NO_CHAIN -concurrent-insert AUTO -connect-timeout 10 -console FALSE -datadir MYSQLTEST_VARDIR/install.db/ -date-format %Y-%m-%d -datetime-format %Y-%m-%d %H:%i:%s -default-character-set latin1 -default-collation latin1_swedish_ci -default-storage-engine MyISAM -default-time-zone (No default value) -default-week-format 0 -delay-key-write ON -delayed-insert-limit 100 -delayed-insert-timeout 300 -delayed-queue-size 1000 -disconnect-slave-event-count 0 -div-precision-increment 4 -enable-locking FALSE -engine-condition-pushdown TRUE -event-scheduler OFF -expire-logs-days 0 -external-locking FALSE -flush FALSE -flush-time 0 -ft-boolean-syntax + -><()~*:""&| -ft-max-word-len 84 -ft-min-word-len 4 -ft-query-expansion-limit 20 -ft-stopword-file (No default value) -gdb FALSE -general-log FALSE -group-concat-max-len 1024 -help TRUE -ignore-builtin-innodb FALSE -init-connect -init-file (No default value) -init-rpl-role MASTER -init-slave -interactive-timeout 28800 -join-buffer-size 131072 -keep-files-on-create FALSE -key-buffer-size 8388608 -key-cache-age-threshold 300 -key-cache-block-size 1024 -key-cache-division-limit 100 -language MYSQL_SHAREDIR/ -large-pages FALSE -lc-messages en_US -lc-messages-dir MYSQL_SHAREDIR/ -lc-time-names en_US -local-infile TRUE -log-bin (No default value) -log-bin-index (No default value) -log-bin-trust-function-creators FALSE -log-error -log-isam myisam.log -log-output FILE -log-queries-not-using-indexes FALSE -log-short-format FALSE -log-slave-updates FALSE -log-slow-admin-statements FALSE -log-slow-slave-statements FALSE -log-tc tc.log -log-tc-size 24576 -log-update (No default value) -log-warnings 1 -long-query-time 10 -low-priority-updates FALSE -lower-case-table-names 1 -master-info-file master.info -master-retry-count 86400 -max-allowed-packet 1048576 -max-binlog-cache-size 18446744073709547520 -max-binlog-dump-events 0 -max-binlog-size 1073741824 -max-connect-errors 10 -max-connections 151 -max-delayed-threads 20 -max-error-count 64 -max-heap-table-size 16777216 -max-join-size 18446744073709551615 -max-length-for-sort-data 1024 -max-prepared-stmt-count 16382 -max-relay-log-size 0 -max-seeks-for-key 18446744073709551615 -max-sort-length 1024 -max-sp-recursion-depth 0 -max-tmp-tables 32 -max-user-connections 0 -max-write-lock-count 18446744073709551615 -memlock FALSE -min-examined-row-limit 0 -multi-range-count 256 -myisam-block-size 1024 -myisam-data-pointer-size 6 -myisam-max-sort-file-size 9223372036853727232 -myisam-mmap-size 18446744073709551615 -myisam-recover-options OFF -myisam-repair-threads 1 -myisam-sort-buffer-size 8388608 -myisam-stats-method nulls_unequal -myisam-use-mmap FALSE -net-buffer-length 16384 -net-read-timeout 30 -net-retry-count 10 -net-write-timeout 60 -new FALSE -old FALSE -old-alter-table FALSE -old-passwords FALSE -old-style-user-limits FALSE -optimizer-prune-level 1 -optimizer-search-depth 62 -optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on -plugin-load (No default value) -port 3306 -port-open-timeout 0 -preload-buffer-size 32768 -profiling-history-size 15 -query-alloc-block-size 8192 -query-cache-limit 1048576 -query-cache-min-res-unit 4096 -query-cache-size 0 -query-cache-type ON -query-cache-wlock-invalidate FALSE -query-prealloc-size 8192 -range-alloc-block-size 4096 -read-buffer-size 131072 -read-only FALSE -read-rnd-buffer-size 262144 -record-buffer 131072 -relay-log (No default value) -relay-log-index (No default value) -relay-log-info-file relay-log.info -relay-log-purge TRUE -relay-log-recovery FALSE -relay-log-space-limit 0 -replicate-same-server-id FALSE -report-host (No default value) -report-password (No default value) -report-port 3306 -report-user (No default value) -rpl-recovery-rank 0 -safe-user-create FALSE -secure-auth FALSE -secure-file-priv (No default value) -server-id 0 -show-slave-auth-info FALSE -skip-grant-tables TRUE -skip-networking FALSE -skip-show-database FALSE -skip-slave-start FALSE -slave-compressed-protocol FALSE -slave-exec-mode STRICT -slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ -slave-net-timeout 3600 -slave-skip-errors (No default value) -slave-transaction-retries 10 -slow-launch-time 2 -slow-query-log FALSE -socket /tmp/mysql.sock -sort-buffer-size 2097152 -sporadic-binlog-dump-fail FALSE -sql-mode -symbolic-links FALSE -sync-binlog 0 -sync-frm TRUE -sync-master-info 0 -sync-relay-log 0 -sync-relay-log-info 0 -sysdate-is-now FALSE -table-cache 400 -table-definition-cache 400 -table-lock-wait-timeout 50 -table-open-cache 400 -tc-heuristic-recover COMMIT -thread-cache-size 0 -thread-handling one-thread-per-connection -thread-stack 262144 -time-format %H:%i:%s -timed-mutexes FALSE -tmp-table-size 16777216 -tmpdir MYSQLTEST_VARDIR/tmp/ -transaction-alloc-block-size 8192 -transaction-isolation REPEATABLE-READ -transaction-prealloc-size 4096 -updatable-views-with-limit YES -use-symbolic-links FALSE -verbose TRUE -wait-timeout 28800 -warnings 1 +and boolean options {FALSE|TRUE} Value (after reading options) +--------------------------------- ---------------------------------------- +abort-slave-event-count 0 +allow-suspicious-udfs FALSE +auto-increment-increment 1 +auto-increment-offset 1 +automatic-sp-privileges TRUE +back-log 50 +big-tables FALSE +bind-address (No default value) +binlog-cache-size 32768 +binlog-format STATEMENT +binlog-row-event-max-size 1024 +bulk-insert-buffer-size 8388608 +character-set-client-handshake TRUE +character-set-filesystem binary +character-set-server latin1 +character-sets-dir MYSQL_CHARSETSDIR/ +chroot (No default value) +collation-server latin1_swedish_ci +completion-type NO_CHAIN +concurrent-insert AUTO +connect-timeout 10 +console FALSE +datadir MYSQLTEST_VARDIR/install.db/ +date-format %Y-%m-%d +datetime-format %Y-%m-%d %H:%i:%s +default-character-set latin1 +default-collation latin1_swedish_ci +default-storage-engine MyISAM +default-time-zone (No default value) +default-week-format 0 +delay-key-write ON +delayed-insert-limit 100 +delayed-insert-timeout 300 +delayed-queue-size 1000 +disconnect-slave-event-count 0 +div-precision-increment 4 +enable-locking FALSE +engine-condition-pushdown TRUE +event-scheduler OFF +expire-logs-days 0 +external-locking FALSE +flush FALSE +flush-time 0 +ft-boolean-syntax + -><()~*:""&| +ft-max-word-len 84 +ft-min-word-len 4 +ft-query-expansion-limit 20 +ft-stopword-file (No default value) +gdb FALSE +general-log FALSE +group-concat-max-len 1024 +help TRUE +ignore-builtin-innodb FALSE +init-connect +init-file (No default value) +init-rpl-role MASTER +init-slave +interactive-timeout 28800 +join-buffer-size 131072 +keep-files-on-create FALSE +key-buffer-size 8388608 +key-cache-age-threshold 300 +key-cache-block-size 1024 +key-cache-division-limit 100 +language MYSQL_SHAREDIR/ +large-pages FALSE +lc-messages en_US +lc-messages-dir MYSQL_SHAREDIR/ +lc-time-names en_US +local-infile TRUE +log-bin (No default value) +log-bin-index (No default value) +log-bin-trust-function-creators FALSE +log-error +log-isam myisam.log +log-output FILE +log-queries-not-using-indexes FALSE +log-short-format FALSE +log-slave-updates FALSE +log-slow-admin-statements FALSE +log-slow-slave-statements FALSE +log-tc tc.log +log-tc-size 24576 +log-update (No default value) +log-warnings 1 +long-query-time 10 +low-priority-updates FALSE +lower-case-table-names 1 +master-info-file master.info +master-retry-count 86400 +max-allowed-packet 1048576 +max-binlog-cache-size 18446744073709547520 +max-binlog-dump-events 0 +max-binlog-size 1073741824 +max-connect-errors 10 +max-connections 151 +max-delayed-threads 20 +max-error-count 64 +max-heap-table-size 16777216 +max-join-size 18446744073709551615 +max-length-for-sort-data 1024 +max-prepared-stmt-count 16382 +max-relay-log-size 0 +max-seeks-for-key 18446744073709551615 +max-sort-length 1024 +max-sp-recursion-depth 0 +max-tmp-tables 32 +max-user-connections 0 +max-write-lock-count 18446744073709551615 +memlock FALSE +min-examined-row-limit 0 +multi-range-count 256 +myisam-block-size 1024 +myisam-data-pointer-size 6 +myisam-max-sort-file-size 9223372036853727232 +myisam-mmap-size 18446744073709551615 +myisam-recover-options OFF +myisam-repair-threads 1 +myisam-sort-buffer-size 8388608 +myisam-stats-method nulls_unequal +myisam-use-mmap FALSE +net-buffer-length 16384 +net-read-timeout 30 +net-retry-count 10 +net-write-timeout 60 +new FALSE +old FALSE +old-alter-table FALSE +old-passwords FALSE +old-style-user-limits FALSE +optimizer-prune-level 1 +optimizer-search-depth 62 +optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on +plugin-load (No default value) +port 3306 +port-open-timeout 0 +preload-buffer-size 32768 +profiling-history-size 15 +query-alloc-block-size 8192 +query-cache-limit 1048576 +query-cache-min-res-unit 4096 +query-cache-size 0 +query-cache-type ON +query-cache-wlock-invalidate FALSE +query-prealloc-size 8192 +range-alloc-block-size 4096 +read-buffer-size 131072 +read-only FALSE +read-rnd-buffer-size 262144 +record-buffer 131072 +relay-log (No default value) +relay-log-index (No default value) +relay-log-info-file relay-log.info +relay-log-purge TRUE +relay-log-recovery FALSE +relay-log-space-limit 0 +replicate-same-server-id FALSE +report-host (No default value) +report-password (No default value) +report-port 3306 +report-user (No default value) +rpl-recovery-rank 0 +safe-user-create FALSE +secure-auth FALSE +secure-file-priv (No default value) +server-id 0 +show-slave-auth-info FALSE +skip-grant-tables TRUE +skip-networking FALSE +skip-show-database FALSE +skip-slave-start FALSE +slave-compressed-protocol FALSE +slave-exec-mode STRICT +slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ +slave-net-timeout 3600 +slave-skip-errors (No default value) +slave-transaction-retries 10 +slow-launch-time 2 +slow-query-log FALSE +socket /tmp/mysql.sock +sort-buffer-size 2097152 +sporadic-binlog-dump-fail FALSE +sql-mode +symbolic-links FALSE +sync-binlog 0 +sync-frm TRUE +sync-master-info 0 +sync-relay-log 0 +sync-relay-log-info 0 +sysdate-is-now FALSE +table-cache 400 +table-definition-cache 400 +table-lock-wait-timeout 50 +table-open-cache 400 +tc-heuristic-recover COMMIT +thread-cache-size 0 +thread-handling one-thread-per-connection +thread-stack 262144 +time-format %H:%i:%s +timed-mutexes FALSE +tmp-table-size 16777216 +tmpdir MYSQLTEST_VARDIR/tmp/ +transaction-alloc-block-size 8192 +transaction-isolation REPEATABLE-READ +transaction-prealloc-size 4096 +updatable-views-with-limit YES +use-symbolic-links FALSE +verbose TRUE +wait-timeout 28800 +warnings 1 To see what values a running MySQL server is using, type 'mysqladmin variables' instead of 'mysqld --verbose --help'. From 1ae30d113da3f5829584f4646b1b6dda5e4fec13 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 5 Jan 2010 21:35:50 +0100 Subject: [PATCH 207/466] Bug #49345 re-introduce gprof to mysql-test-run.pl Was available in v1 Porting to v2 required some rewriting Updated after review comments --- mysql-test/lib/mtr_gprof.pl | 45 +++++++++--------------------------- mysql-test/mysql-test-run.pl | 12 +++++++++- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/mysql-test/lib/mtr_gprof.pl b/mysql-test/lib/mtr_gprof.pl index f6615301dd7..5820a4007b8 100644 --- a/mysql-test/lib/mtr_gprof.pl +++ b/mysql-test/lib/mtr_gprof.pl @@ -20,43 +20,20 @@ use strict; -# These are not to be prefixed with "mtr_" +sub gprof_collect ($@) { + my ($exe_mysqld, @gprof_dirs)= @_; -sub gprof_prepare (); -sub gprof_collect (); + print ("Collecting gprof reports.....\n"); -############################################################################## -# -# -# -############################################################################## - -sub gprof_prepare () { - - rmtree($::opt_gprof_dir); - mkdir($::opt_gprof_dir); -} - -# FIXME what about master1 and slave1?! -sub gprof_collect () { - - if ( -f "$::master->[0]->{'path_myddir'}/gmon.out" ) + foreach my $datadir (@gprof_dirs) { - # FIXME check result code?! - mtr_run("gprof", - [$::exe_master_mysqld, - "$::master->[0]->{'path_myddir'}/gmon.out"], - $::opt_gprof_master, "", "", ""); - print "Master execution profile has been saved in $::opt_gprof_master\n"; - } - if ( -f "$::slave->[0]->{'path_myddir'}/gmon.out" ) - { - # FIXME check result code?! - mtr_run("gprof", - [$::exe_slave_mysqld, - "$::slave->[0]->{'path_myddir'}/gmon.out"], - $::opt_gprof_slave, "", "", ""); - print "Slave execution profile has been saved in $::opt_gprof_slave\n"; + my $gprof_msg= "$datadir/gprof.msg"; + my $gprof_err= "$datadir/gprof.err"; + if ( -f "$datadir/gmon.out" ) + { + system("gprof $exe_mysqld $datadir/gmon.out 2>$gprof_err >$gprof_msg"); + print ("GPROF output in $gprof_msg, errors in $gprof_err\n"); + } } } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 27c2ace56b2..ff986b9e793 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -87,6 +87,7 @@ use IO::Select; require "lib/mtr_process.pl"; require "lib/mtr_io.pl"; require "lib/mtr_gcov.pl"; +require "lib/mtr_gprof.pl"; require "lib/mtr_misc.pl"; $SIG{INT}= sub { mtr_error("Got ^C signal"); }; @@ -167,6 +168,9 @@ our $opt_gcov_exe= "gcov"; our $opt_gcov_err= "mysql-test-gcov.msg"; our $opt_gcov_msg= "mysql-test-gcov.err"; +our $opt_gprof; +our %gprof_dirs; + our $glob_debugger= 0; our $opt_gdb; our $opt_client_gdb; @@ -745,6 +749,9 @@ sub run_worker ($) { if ($opt_valgrind_mysqld) { valgrind_exit_reports(); } + if ( $opt_gprof ) { + gprof_collect (find_mysqld($basedir), keys %gprof_dirs); + } exit(0); } else { @@ -858,6 +865,7 @@ sub command_line_setup { # Coverage, profiling etc 'gcov' => \$opt_gcov, + 'gprof' => \$opt_gprof, 'valgrind|valgrind-all' => \$opt_valgrind, 'valgrind-mysqltest' => \$opt_valgrind_mysqltest, 'valgrind-mysqld' => \$opt_valgrind_mysqld, @@ -1250,7 +1258,7 @@ sub command_line_setup { # -------------------------------------------------------------------------- # Gcov flag # -------------------------------------------------------------------------- - if ( $opt_gcov and ! $source_dist ) + if ( ($opt_gcov or $opt_gprof) and ! $source_dist ) { mtr_error("Coverage test needs the source - please use source dist"); } @@ -4304,6 +4312,8 @@ sub mysqld_start ($$) { } # Remember this log file for valgrind error report search $mysqld_logs{$output}= 1 if $opt_valgrind; + # Remember data dir for gmon.out files if using gprof + $gprof_dirs{$mysqld->value('datadir')}= 1 if $opt_gprof; if ( defined $exe ) { From e72db2b4cf3a5a1cc87dc59ced5d28d500cd1c03 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 5 Jan 2010 22:36:08 +0100 Subject: [PATCH 208/466] Fix buggy test that fails due to "error: packets out of order" message in the trace file when EXTRA_DEBUG is enabled. The reason for the error message is that a query was issued with send, but response was not read with reap. --- mysql-test/t/loadxml.test | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mysql-test/t/loadxml.test b/mysql-test/t/loadxml.test index 8b9c1bd1b0d..84a89a332a0 100644 --- a/mysql-test/t/loadxml.test +++ b/mysql-test/t/loadxml.test @@ -77,15 +77,17 @@ sleep 3; connection default; -sleep 1; --disable_query_log --eval kill $PSEUDO_THREAD_ID -sleep 2; ---enable_query_log - +connection addconroot; +# Read response from connection to avoid packets out-of-order when disconnecting +# Note, that connection can already be dead due to previously issued kill +--error 0,2013 +--reap disconnect addconroot; - +connection default; +--enable_query_log # # Clean up # From 2ec01d90d8f20f2517d68989ae102183bbf70704 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 6 Jan 2010 09:42:21 +0100 Subject: [PATCH 209/466] Bug #48863 mysql test: enable and disable case insensitive compare mode Implemented --lowercase_result which lower cases next result --- client/mysqltest.cc | 26 ++++++++++++++++++++++-- mysql-test/r/mysqltest.result | 23 +++++++++++++++++++++ mysql-test/t/mysqltest.test | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index e2a9a2a5349..f516415f15d 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -97,7 +97,7 @@ static my_bool sp_protocol= 0, sp_protocol_enabled= 0; static my_bool view_protocol= 0, view_protocol_enabled= 0; static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; static my_bool parsing_disabled= 0; -static my_bool display_result_vertically= FALSE, +static my_bool display_result_vertically= FALSE, display_result_lower= FALSE, display_metadata= FALSE, display_result_sorted= FALSE; static my_bool disable_query_log= 0, disable_result_log= 0; static my_bool disable_warnings= 0; @@ -272,6 +272,7 @@ enum enum_commands { Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT, + Q_LOWERCASE, Q_START_TIMER, Q_END_TIMER, Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, @@ -346,6 +347,7 @@ const char *command_names[]= "query_vertical", "query_horizontal", "sorted_result", + "lowercase_result", "start_timer", "end_timer", "character_set", @@ -7876,6 +7878,13 @@ int main(int argc, char **argv) */ display_result_sorted= TRUE; break; + case Q_LOWERCASE: + /* + Turn on lowercasing of result, will be reset after next + command + */ + display_result_lower= TRUE; + break; case Q_LET: do_let(command); break; case Q_EVAL_RESULT: die("'eval_result' command is deprecated"); @@ -8091,8 +8100,9 @@ int main(int argc, char **argv) */ free_all_replace(); - /* Also reset "sorted_result" */ + /* Also reset "sorted_result" and "lowercase"*/ display_result_sorted= FALSE; + display_result_lower= FALSE; } last_command_executed= command_executed; @@ -9496,6 +9506,18 @@ void replace_dynstr_append_mem(DYNAMIC_STRING *ds, fix_win_paths(val, len); #endif + if (display_result_lower) + { + /* Convert to lower case, and do this first */ + char lower[512]; + char *c= lower; + for (const char *v= val; *v; v++) + *c++= my_tolower(charset_info, *v); + *c= '\0'; + /* Copy from this buffer instead */ + val= lower; + } + if (glob_replace_regex) { /* Regex replace */ diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 671f88cb00a..f327640782c 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -682,6 +682,29 @@ INSERT INTO t1 SELECT f1 - 256 FROM t1; INSERT INTO t1 SELECT f1 - 512 FROM t1; SELECT * FROM t1; DROP TABLE t1; +select "500g blbrsyltety" as "will be lower cased"; +will be lower cased +500g blbrsyltety +SELECT "UPPER" AS "WILL NOT BE lower cased"; +WILL NOT BE lower cased +UPPER +UP +SELECT 0 as "UP AGAIN"; +UP AGAIN +0 +select "abcdef" as "uvwxyz"; +uvwxyz +abcdef +select "xyz" as name union select "abc" as name order by name desc; +name +abc +xyz +select 1 as "some new text"; +some new text +1 +select 0 as "will not lower case "; +will not lower case +0 CREATE TABLE t1( a int, b varchar(255), c datetime ); diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index b4ea9202df7..ce9b31eb30f 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -2059,6 +2059,44 @@ INSERT INTO t1 SELECT f1 - 512 FROM t1; SELECT * FROM t1; --enable_result_log DROP TABLE t1; + +# ---------------------------------------------------------------------------- +# test for lowercase_result +# ---------------------------------------------------------------------------- + +# 1. Basic test +--lowercase_result +SELECT "500g BLBRSYLTETY" AS "WILL BE lower cased"; + +# 2. test that it does not apply to next statement +SELECT "UPPER" AS "WILL NOT BE lower cased"; + +# 3. test that it does not affect non-SQL or the following statement +--lowercase_result +--echo UP +SELECT 0 as "UP AGAIN"; + +# 4. test that it works with eval and variables +let $lower_stmt=SELECT "ABCdef" AS "uvwXYZ"; +--lowercase_result +eval $lower_stmt; + +# 5. test that it works in combination with sort +sorted_result; +lowercase_result; +SELECT "Xyz" AS Name UNION SELECT "Abc" as Name ORDER BY Name DESC; + +# 6. Test combination with replace, and that lower casing is done first +--lowercase_result +--replace_result old new +SELECT 1 as "SOME OLD TEXT"; + +# 7. Test missing lower casing of "unknown" characters +--character_set utf8 +--lowercase_result +SELECT 0 as "WILL NOT lower case "; +--character_set latin1 + # ---------------------------------------------------------------------------- # Some coverage tests # ---------------------------------------------------------------------------- From 583f77011b0113de92431a8dee789cde8b90d13b Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 6 Jan 2010 09:45:28 +0100 Subject: [PATCH 210/466] Bug #49269 mysqltest crashes on 'reap' if query executed after 'send' Set a flag after send to trap the case --- client/mysqltest.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index f516415f15d..804db8fbefe 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -228,6 +228,8 @@ struct st_connection char *name; size_t name_len; MYSQL_STMT* stmt; + /* Set after send to disallow other queries before reap */ + my_bool pending; #ifdef EMBEDDED_LIBRARY const char *cur_query; @@ -4691,7 +4693,8 @@ void do_close_connection(struct st_command *command) if (con->util_mysql) mysql_close(con->util_mysql); con->util_mysql= 0; - + con->pending= FALSE; + my_free(con->name, MYF(0)); /* @@ -6513,6 +6516,9 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, if (flags & QUERY_SEND_FLAG) { + if (cn->pending) + die ("Cannot run query on connection between send and reap"); + /* Send the query */ @@ -6532,8 +6538,11 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, wait_query_thread_end(cn); #endif /*EMBEDDED_LIBRARY*/ if (!(flags & QUERY_REAP_FLAG)) + { + cn->pending= TRUE; DBUG_VOID_RETURN; - + } + do { /* @@ -6622,6 +6631,7 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, end: + cn->pending= FALSE; /* We save the return code (mysql_errno(mysql)) from the last call sent to the server into the mysqltest builtin variable $mysql_errno. This From 385f3f87c9575780eb354f5aacf1c2f20be5089f Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 6 Jan 2010 09:47:25 +0100 Subject: [PATCH 211/466] Bug #49761 mysqltest.test does not have any tests for send/reap Added them NB the 6th case is adapted to Bug no. 49269, gives wrong output without it --- mysql-test/r/mysqltest.result | 20 ++++++++++++++ mysql-test/t/mysqltest.test | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index f327640782c..e77dcd7b0a6 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -523,6 +523,26 @@ a D 1 1 1 4 drop table t1; +create table t1 ( f1 char(10)); +insert into t1 values ("Abcd"); +select * from t1; +f1 +Abcd +select * from t2;; +ERROR 42S02: Table 'test.t2' doesn't exist +select * from t1; +f1 +Abcd +select * from t1;; +Result coming up +f1 +Abcd +select * from t1;; +f1 +Abcd +mysqltest: At line 2: Cannot run query on connection between send and reap +select * from t1;; +drop table t1; mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file' mysqltest: At line 1: Missing required argument 'filename' to command 'write_file' mysqltest: At line 1: End of file encountered before 'EOF' delimiter was found diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index ce9b31eb30f..45fc0715312 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1605,6 +1605,57 @@ insert into t1 values (2,4); select * from t1; drop table t1; +# ---------------------------------------------------------------------------- +# Tests of send +# ---------------------------------------------------------------------------- + +create table t1 ( f1 char(10)); +insert into t1 values ("Abcd"); + +# 1. Basic test + +send select * from t1; +reap; + +# 2. Test with error + +--send select * from t2; +--error ER_NO_SUCH_TABLE +--reap + +# 3. test send of next stmt + +--send +select * from t1; +--reap + +# 4. Non-query stmt betwen send and reap allowed + +--send select * from t1; +--sleep 0.05 +--echo Result coming up +--reap + +# 5. Test of send_eval + +--let $my_stmt= select * from t1; +--send_eval $my_stmt +--reap + +# 6. Test that mysqltest does not allow query stmt between send and reap +# Untestable directly as it causes mysqltest to fail + +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.in +--send select * from t1; +select 1; +--reap +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.in 2>&1 +remove_file $MYSQLTEST_VARDIR/tmp/mysqltest.in; + +drop table t1; + # ---------------------------------------------------------------------------- # test for remove_file # ---------------------------------------------------------------------------- From 570ee4c5e8e6510990abfa0427fc87a1c76183e4 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 6 Jan 2010 12:56:22 +0100 Subject: [PATCH 212/466] Bug #49269 mysqltest crashes on 'reap' if query executed after 'send' Small amendment: ignore pending reap when switching connection, add test --- client/mysqltest.cc | 1 + mysql-test/r/mysqltest.result | 7 +++++++ mysql-test/t/mysqltest.test | 12 ++++++++++++ 3 files changed, 20 insertions(+) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 804db8fbefe..d0c58d2f57b 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -4622,6 +4622,7 @@ void select_connection_name(const char *name) if (!con) die("connection '%s' not found in connection pool", name); + con->pending= FALSE; set_current_connection(con); DBUG_VOID_RETURN; diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index e77dcd7b0a6..def5d202c2f 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -542,6 +542,13 @@ f1 Abcd mysqltest: At line 2: Cannot run query on connection between send and reap select * from t1;; +select * from t1;; +select 1; +1 +1 +select 2; +2 +2 drop table t1; mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file' mysqltest: At line 1: Missing required argument 'filename' to command 'write_file' diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 45fc0715312..f3fea9348e4 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1654,6 +1654,18 @@ EOF --exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.in 2>&1 remove_file $MYSQLTEST_VARDIR/tmp/mysqltest.in; +# 7. Test that stmt after send without reap IS allowed +# if we have switched connections + +connect (test_con1,localhost,root,,); +--send select * from t1; +connection default; +select 1; +connection test_con1; +select 2; +disconnect test_con1; +connection default; + drop table t1; # ---------------------------------------------------------------------------- From fcb597348bccfe1ae42633fd2450d4365dbeb750 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 6 Jan 2010 17:57:10 +0000 Subject: [PATCH 213/466] remove table formatting (squeeze many spaces into one) in mysqld--help test. When mysql is compiled with different options,columns might have different size (this depends on parameter names and some parameters might only be available when some option is switched on) --- mysql-test/include/mysqld--help.inc | 6 +- mysql-test/r/mysqld--help-notwin.result | 1826 +++++++++++----------- mysql-test/r/mysqld--help-win.result | 1838 +++++++++++------------ 3 files changed, 1835 insertions(+), 1835 deletions(-) diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc index 630d16d4dee..ad5272c095e 100644 --- a/mysql-test/include/mysqld--help.inc +++ b/mysql-test/include/mysqld--help.inc @@ -21,12 +21,16 @@ perl; open(F, '<', "$ENV{MYSQL_TMP_DIR}/mysqld--help.txt") or die; while () { next if 1../The following groups are read/; + # formatting, skip line consisting entirely of dashes and blanks + next if /^[\- ]+$/; + next if /Value \(after reading options\)/; # skip table header next if /^($re1) /; next if /^($re2)-/; $skip=0 if /^ -/; $skip=1 if / --($re2)\b/; - # fixes for 32-bit y!\\!/!; + s/[ ]+/ /; # squeeze spaces to remove table formatting + # fixes for 32-bit s/\b4294967295\b/18446744073709551615/; s/\b2146435072\b/9223372036853727232/; s/\b196608\b/262144/; diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 2fad24a93b5..13236cb9684 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -4,922 +4,920 @@ The following options may be given as the first argument: --defaults-file=# Only read default options from the given file # --defaults-extra-file=# Read this file after the global files are read - --abort-slave-event-count=# - Option used by mysql-test for debugging and testing of - replication. - --allow-suspicious-udfs - Allows use of UDFs consisting of only one symbol xxx() - without corresponding xxx_init() or xxx_deinit(). That - also means that one can load any function from any - library, for example exit() from libc.so - -a, --ansi Use ANSI SQL syntax instead of MySQL syntax. This mode - will also set transaction isolation level 'serializable'. - --auto-increment-increment[=#] - Auto-increment columns are incremented by this - --auto-increment-offset[=#] - Offset added to Auto-increment columns. Used when - auto-increment-increment != 1 - --automatic-sp-privileges - Creating and dropping stored procedures alters ACLs - (Defaults to on; use --skip-automatic-sp-privileges to disable.) - --back-log=# The number of outstanding connection requests MySQL can - have. This comes into play when the main MySQL thread - gets very many connection requests in a very short time - -b, --basedir=name Path to installation directory. All paths are usually - resolved relative to this - --big-tables Allow big result sets by saving all temporary sets on - file (Solves most 'table full' errors) - --bind-address=name IP address to bind to. - --binlog-cache-size=# - The size of the cache to hold the SQL statements for the - binary log during a transaction. If you often use big, - multi-statement transactions you can increase this to get - more performance - --binlog-do-db=name Tells the master it should log updates for the specified - database, and exclude all others not explicitly - mentioned. - --binlog-format=name - What form of binary logging the master will use: either - ROW for row-based binary logging, STATEMENT for - statement-based binary logging, or MIXED. MIXED is - statement-based binary logging except for those - statements where only row-based is correct: those which - involve user-defined functions (i.e. UDFs) or the UUID() - function; for those, row-based binary logging is - automatically used. If NDBCLUSTER is enabled and - binlog-format is MIXED, the format switches to row-based - and back implicitly per each query accessing an - NDBCLUSTER table - --binlog-ignore-db=name - Tells the master that updates to the given database - should not be logged tothe binary log. - --binlog-row-event-max-size=# - The maximum size of a row-based binary log event in - bytes. Rows will be grouped into events smaller than this - size if possible. The value has to be a multiple of 256. - --bootstrap Used by mysql installation scripts. - --bulk-insert-buffer-size=# - Size of tree cache used in bulk insert optimisation. Note - that this is a limit per thread! - --character-set-client-handshake - Don't ignore client side character set value sent during - handshake. - (Defaults to on; use --skip-character-set-client-handshake to disable.) - --character-set-filesystem=name - Set the filesystem character set. - -C, --character-set-server=name - Set the default character set. - --character-sets-dir=name - Directory where character sets are - -r, --chroot=name Chroot mysqld daemon during startup. - --collation-server=name - Set the default collation. - --completion-type=name - The transaction completion type, one of NO_CHAIN, CHAIN, - RELEASE - --concurrent-insert[=name] - Use concurrent insert with MyISAM. Possible values are - NEVER, AUTO, ALWAYS - --connect-timeout=# The number of seconds the mysqld server is waiting for a - connect packet before responding with 'Bad handshake' - --console Write error output on screen; Don't remove the console - window on windows. - --core-file Write core on errors. - -h, --datadir=name Path to the database root directory - --date-format=name The DATE format (ignored) - --datetime-format=name - The DATETIME format (ignored) - -C, --default-character-set=name - Set the default character set (deprecated option, use - --character-set-server instead). - --default-collation=name - Set the default collation (deprecated option, use - --collation-server instead). - --default-storage-engine=name - The default storage engine for new tables - --default-time-zone=name - Set the default time zone. - --default-week-format=# - The default week format used by WEEK() functions - --delay-key-write[=name] - Type of DELAY_KEY_WRITE - --delay-key-write-for-all-tables - Don't flush key buffers between writes for any MyISAM - table (Deprecated option, use --delay-key-write=all - instead). - --delayed-insert-limit=# - After inserting delayed_insert_limit rows, the INSERT - DELAYED handler will check if there are any SELECT - statements pending. If so, it allows these to execute - before continuing - --delayed-insert-timeout=# - How long a INSERT DELAYED thread should wait for INSERT - statements before terminating - --delayed-queue-size=# - What size queue (in rows) should be allocated for - handling INSERT DELAYED. If the queue becomes full, any - client that does INSERT DELAYED will wait until there is - room in the queue again - --disconnect-slave-event-count=# - Option used by mysql-test for debugging and testing of - replication. - --div-precision-increment=# - Precision of the result of '/' operator will be increased - on that value - --enable-locking Deprecated option, use --external-locking instead. - --engine-condition-pushdown - Push supported query conditions to the storage engine - (Defaults to on; use --skip-engine-condition-pushdown to disable.) - --event-scheduler[=name] - Enable the event scheduler. Possible values are ON, OFF, - and DISABLED (keep the event scheduler completely - deactivated, it cannot be activated run-time) - -T, --exit-info[=#] Used for debugging; Use at your own risk! - --expire-logs-days=# - If non-zero, binary logs will be purged after - expire_logs_days days; possible purges happen at startup - and at binary log rotation - --external-locking Use system (external) locking (disabled by default). - With this option enabled you can run myisamchk to test - (not repair) tables while the MySQL server is running. - Disable with --skip-external-locking. - --flush Flush MyISAM tables to disk between SQL commands - --flush-time=# A dedicated thread is created to flush all tables at the - given interval - --ft-boolean-syntax=name - List of operators for MATCH ... AGAINST ( ... IN BOOLEAN - MODE) - --ft-max-word-len=# The maximum length of the word to be included in a - FULLTEXT index. Note: FULLTEXT indexes must be rebuilt - after changing this variable - --ft-min-word-len=# The minimum length of the word to be included in a - FULLTEXT index. Note: FULLTEXT indexes must be rebuilt - after changing this variable - --ft-query-expansion-limit=# - Number of best matches to use for query expansion - --ft-stopword-file=name - Use stopwords from this file instead of built-in list - --gdb Set up signals usable for debugging - --general-log Log connections and queries to a table or log file. - Defaults logging to a file hostname.log or a table - mysql.general_logif --log-output=TABLE is used - --general-log-file=name - Log connections and queries to given file - --group-concat-max-len=# - The maximum length of the result of function - GROUP_CONCAT() - -?, --help Display this help and exit. - --ignore-builtin-innodb - Disable initialization of builtin InnoDB plugin - --init-connect=name Command(s) that are executed for each new connection - --init-file=name Read SQL commands from this file at startup - --init-rpl-role=name - Set the replication role. - --init-slave=name Command(s) that are executed by a slave server each time - the SQL thread starts - --interactive-timeout=# - The number of seconds the server waits for activity on an - interactive connection before closing it - --join-buffer-size=# - The size of the buffer that is used for full joins - --keep-files-on-create - Don't overwrite stale .MYD and .MYI even if no directory - is specified - --key-buffer-size=# The size of the buffer used for index blocks for MyISAM - tables. Increase this to get better index handling (for - all reads and multiple writes) to as much as you can - afford - --key-cache-age-threshold=# - This characterizes the number of hits a hot block has to - be untouched until it is considered aged enough to be - downgraded to a warm block. This specifies the percentage - ratio of that number of hits to the total number of - blocks in key cache - --key-cache-block-size=# - The default size of key cache blocks - --key-cache-division-limit=# - The minimum percentage of warm blocks in key cache - -L, --language=name Client error messages in given language. May be given as - a full path. Deprecated. Use --lc-messages-dir instead. - --large-pages Enable support for large pages - --lc-messages=name Set the language used for the error messages. - -L, --lc-messages-dir=name - Directory where error messages are - --lc-time-names=name - Set the language used for the month names and the days of - the week. - --local-infile Enable LOAD DATA LOCAL INFILE - (Defaults to on; use --skip-local-infile to disable.) - -l, --log[=name] Log connections and queries to file (deprecated option, - use --general-log/--general-log-file instead). - --log-bin[=name] Log update queries in binary format. Optional (but - strongly recommended to avoid replication problems if - server's hostname changes) argument should be the chosen - location for the binary log files. - --log-bin-index=name - File that holds the names for last binary log files. - --log-bin-trust-function-creators - If set to FALSE (the default), then when --log-bin is - used, creation of a stored function (or trigger) is - allowed only to users having the SUPER privilege and only - if this stored function (trigger) may not break binary - logging. Note that if ALL connections to this server - ALWAYS use row-based binary logging, the security issues - do not exist and the binary logging cannot break, so you - can safely set this to TRUE - --log-error[=name] Error log file - --log-isam[=name] Log all MyISAM changes to file. - -0, --log-long-format - Log some extra information to update log. Please note - that this option is deprecated; see --log-short-format - option. - --log-output=name Syntax: log-output=value[,value...], where "value" could - be TABLE, FILE or NONE - --log-queries-not-using-indexes - Log queries that are executed without benefit of any - index to the slow log if it is open - --log-short-format Don't log extra information to update and slow-query - logs. - --log-slave-updates Tells the slave to log the updates from the slave thread - to the binary log. You will need to turn it on if you - plan to daisy-chain the slaves - --log-slow-admin-statements - Log slow OPTIMIZE, ANALYZE, ALTER and other - administrative statements to the slow log if it is open. - --log-slow-queries[=name] - Log slow queries to a table or log file. Defaults logging - to table mysql.slow_log or hostname-slow.log if - --log-output=file is used. Must be enabled to activate - other slow log options. Deprecated option, use - --slow-query-log/--slow-query-log-file instead. - --log-slow-slave-statements - Log slow statements executed by slave thread to the slow - log if it is open. - --log-tc=name Path to transaction coordinator log (used for - transactions that affect more than one storage engine, - when binary log is disabled) - --log-tc-size=# Size of transaction coordinator log. - --log-update[=name] The update log is deprecated since version 5.0, is - replaced by the binary log and this option justs turns on - --log-bin instead. - -W, --log-warnings[=#] - Log some not critical warnings to the log file - --long-query-time=# Log all queries that have taken more than long_query_time - seconds to execute to file. The argument will be treated - as a decimal value with microsecond precision - --low-priority-updates - INSERT/DELETE/UPDATE has lower priority than selects - --lower-case-table-names[=#] - If set to 1 table names are stored in lowercase on disk - and table names will be case-insensitive. Should be set - to 2 if you are using a case insensitive file system - --master-info-file=name - The location and name of the file that remembers the - master and where the I/O replication thread is in the - master's binlogs. - --master-retry-count=# - The number of tries the slave will make to connect to the - master before giving up. - --max-allowed-packet=# - Max packet length to send to or receive from the server - --max-binlog-cache-size=# - Can be used to restrict the total size used to cache a - multi-transaction query - --max-binlog-dump-events=# - Option used by mysql-test for debugging and testing of - replication. - --max-binlog-size=# Binary log will be rotated automatically when the size - exceeds this value. Will also apply to relay logs if - max_relay_log_size is 0 - --max-connect-errors=# - If there is more than this number of interrupted - connections from a host this host will be blocked from - further connections - --max-connections=# The number of simultaneous clients allowed - --max-delayed-threads=# - Don't start more than this number of threads to handle - INSERT DELAYED statements. If set to zero INSERT DELAYED - will be not used - --max-error-count=# Max number of errors/warnings to store for a statement - --max-heap-table-size=# - Don't allow creation of heap tables bigger than this - --max-join-size=# Joins that are probably going to read more than - max_join_size records return an error - --max-length-for-sort-data=# - Max number of bytes in sorted records - --max-prepared-stmt-count=# - Maximum number of prepared statements in the server - --max-relay-log-size=# - If non-zero: relay log will be rotated automatically when - the size exceeds this value; if zero: when the size - exceeds max_binlog_size - --max-seeks-for-key=# - Limit assumed max number of seeks when looking up rows - based on a key - --max-sort-length=# The number of bytes to use when sorting BLOB or TEXT - values (only the first max_sort_length bytes of each - value are used; the rest are ignored) - --max-sp-recursion-depth[=#] - Maximum stored procedure recursion depth - --max-tmp-tables=# Maximum number of temporary tables a client can keep open - at a time - --max-user-connections=# - The maximum number of active connections for a single - user (0 = no limit) - --max-write-lock-count=# - After this many write locks, allow some read locks to run - in between - --memlock Lock mysqld in memory. - --min-examined-row-limit=# - Don't write queries to slow log that examine fewer rows - than that - --multi-range-count=# - Number of key ranges to request at once - --myisam-block-size=# - Block size to be used for MyISAM index pages - --myisam-data-pointer-size=# - Default pointer size to be used for MyISAM tables - --myisam-max-sort-file-size=# - Don't use the fast sort index method to created index if - the temporary file would get bigger than this - --myisam-mmap-size=# - Restricts the total memory used for memory mapping of - MySQL tables - --myisam-recover-options[=name] - Syntax: myisam-recover-options[=option[,option...]], - where option can be DEFAULT, BACKUP, FORCE, QUICK, or OFF - --myisam-repair-threads=# - If larger than 1, when repairing a MyISAM table all - indexes will be created in parallel, with one thread per - index. The value of 1 disables parallel repair - --myisam-sort-buffer-size=# - The buffer that is allocated when sorting the index when - doing a REPAIR or when creating indexes with CREATE INDEX - or ALTER TABLE - --myisam-stats-method=name - Specifies how MyISAM index statistics collection code - should treat NULLs. Possible values of name are - NULLS_UNEQUAL (default behavior for 4.1 and later), - NULLS_EQUAL (emulate 4.0 behavior), and NULLS_IGNORED - --myisam-use-mmap Use memory mapping for reading and writing MyISAM tables - --net-buffer-length=# - Buffer length for TCP/IP and socket communication - --net-read-timeout=# - Number of seconds to wait for more data from a connection - before aborting the read - --net-retry-count=# If a read on a communication port is interrupted, retry - this many times before giving up - --net-write-timeout=# - Number of seconds to wait for a block to be written to a - connection before aborting the write - -n, --new Use very new possible "unsafe" functions - --old Use compatible behavior - --old-alter-table Use old, non-optimized alter table - --old-passwords Use old password encryption method (needed for 4.0 and - older clients) - --old-style-user-limits - Enable old-style user limits (before 5.0.3 user resources - were counted per each user+host vs. per account) - --one-thread (deprecated): Only use one thread (for debugging under - Linux). Use thread-handling=no-threads instead - --open-files-limit=# - If this is not 0, then mysqld will use this value to - reserve file descriptors to use with setrlimit(). If this - value is 0 then mysqld will reserve max_connections*5 or - max_connections + table_cache*2 (whichever is larger) - number of file descriptors - --optimizer-prune-level=# - Controls the heuristic(s) applied during query - optimization to prune less-promising partial plans from - the optimizer search space. Meaning: 0 - do not apply any - heuristic, thus perform exhaustive search; 1 - prune - plans based on number of retrieved rows - --optimizer-search-depth=# - Maximum depth of search performed by the query optimizer. - Values larger than the number of relations in a query - result in better query plans, but take longer to compile - a query. Values smaller than the number of tables in a - relation result in faster optimization, but may produce - very bad query plans. If set to 0, the system will - automatically pick a reasonable value; if set to 63, the - optimizer will switch to the original find_best - search(used for testing/comparison) - --optimizer-switch=name - optimizer_switch=option=val[,option=val...], where option - is one of {index_merge, index_merge_union, - index_merge_sort_union, index_merge_intersection} and val - is one of {on, off, default} - --pid-file=name Pid file used by safe_mysqld - --plugin-dir=name Directory for plugins - --plugin-load=name Optional semicolon-separated list of plugins to load, - where each plugin is identified as name=library, where - name is the plugin name and library is the plugin library - in plugin_dir. - -P, --port=# Port number to use for connection or 0 to default to, - my.cnf, $MYSQL_TCP_PORT, /etc/services, built-in default - (3306), whatever comes first - --port-open-timeout=# - Maximum time in seconds to wait for the port to become - free. (Default: no wait) - --preload-buffer-size=# - The size of the buffer that is allocated when preloading - indexes - --profiling-history-size=# - Limit of query profiling memory - --query-alloc-block-size=# - Allocation block size for query parsing and execution - --query-cache-limit=# - Don't cache results that are bigger than this - --query-cache-min-res-unit=# - The minimum size for blocks allocated by the query cache - --query-cache-size=# - The memory allocated to store results from old queries - --query-cache-type=name - OFF = Don't cache or retrieve results. ON = Cache all - results except SELECT SQL_NO_CACHE ... queries. DEMAND = - Cache only SELECT SQL_CACHE ... queries - --query-cache-wlock-invalidate - Invalidate queries in query cache on LOCK for write - --query-prealloc-size=# - Persistent buffer for query parsing and execution - --range-alloc-block-size=# - Allocation block size for storing ranges during - optimization - --read-buffer-size=# - Each thread that does a sequential scan allocates a - buffer of this size for each table it scans. If you do - many sequential scans, you may want to increase this - value - --read-only Make all non-temporary tables read-only, with the - exception for replication (slave) threads and users with - the SUPER privilege - --read-rnd-buffer-size=# - When reading rows in sorted order after a sort, the rows - are read through this buffer to avoid a disk seeks. If - not set, then it's set to the value of record_buffer - --record-buffer=# Deprecated; use --read-buffer-size instead. - --relay-log=name The location and name to use for relay logs - --relay-log-index=name - The location and name to use for the file that keeps a - list of the last relay logs - --relay-log-info-file=name - The location and name of the file that remembers where - the SQL replication thread is in the relay logs - --relay-log-purge if disabled - do not purge relay logs. if enabled - purge - them as soon as they are no more needed - (Defaults to on; use --skip-relay-log-purge to disable.) - --relay-log-recovery - Enables automatic relay log recovery right after the - database startup, which means that the IO Thread starts - re-fetching from the master right after the last - transaction processed - --relay-log-space-limit=# - Maximum space to use for all relay logs - --replicate-do-db=name - Tells the slave thread to restrict replication to the - specified database. To specify more than one database, - use the directive multiple times, once for each database. - Note that this will only work if you do not use - cross-database queries such as UPDATE some_db.some_table - SET foo='bar' while having selected a different or no - database. If you need cross database updates to work, - make sure you have 3.23.28 or later, and use - replicate-wild-do-table=db_name.%. - --replicate-do-table=name - Tells the slave thread to restrict replication to the - specified table. To specify more than one table, use the - directive multiple times, once for each table. This will - work for cross-database updates, in contrast to - replicate-do-db. - --replicate-ignore-db=name - Tells the slave thread to not replicate to the specified - database. To specify more than one database to ignore, - use the directive multiple times, once for each database. - This option will not work if you use cross database - updates. If you need cross database updates to work, make - sure you have 3.23.28 or later, and use - replicate-wild-ignore-table=db_name.%. - --replicate-ignore-table=name - Tells the slave thread to not replicate to the specified - table. To specify more than one table to ignore, use the - directive multiple times, once for each table. This will - work for cross-datbase updates, in contrast to - replicate-ignore-db. - --replicate-rewrite-db=name - Updates to a database with a different name than the - original. Example: - replicate-rewrite-db=master_db_name->slave_db_name. - --replicate-same-server-id - In replication, if set to 1, do not skip events having - our server id. Default value is 0 (to break infinite - loops in circular replication). Can't be set to 1 if - --log-slave-updates is used. - --replicate-wild-do-table=name - Tells the slave thread to restrict replication to the - tables that match the specified wildcard pattern. To - specify more than one table, use the directive multiple - times, once for each table. This will work for - cross-database updates. Example: - replicate-wild-do-table=foo%.bar% will replicate only - updates to tables in all databases that start with foo - and whose table names start with bar. - --replicate-wild-ignore-table=name - Tells the slave thread to not replicate to the tables - that match the given wildcard pattern. To specify more - than one table to ignore, use the directive multiple - times, once for each table. This will work for - cross-database updates. Example: - replicate-wild-ignore-table=foo%.bar% will not do updates - to tables in databases that start with foo and whose - table names start with bar. - --report-host=name Hostname or IP of the slave to be reported to the master - during slave registration. Will appear in the output of - SHOW SLAVE HOSTS. Leave unset if you do not want the - slave to register itself with the master. Note that it is - not sufficient for the master to simply read the IP of - the slave off the socket once the slave connects. Due to - NAT and other routing issues, that IP may not be valid - for connecting to the slave from the master or other - hosts - --report-password=name - The account password of the slave to be reported to the - master during slave registration - --report-port=# Port for connecting to slave reported to the master - during slave registration. Set it only if the slave is - listening on a non-default port or if you have a special - tunnel from the master or other clients to the slave. If - not sure, leave this option unset - --report-user=name The account user name of the slave to be reported to the - master during slave registration - --rpl-recovery-rank=# - Unused, will be removed - --safe-mode Skip some optimize stages (for testing). - --safe-show-database - Deprecated option; use GRANT SHOW DATABASES instead... - --safe-user-create Don't allow new user creation by the user who has no - write privileges to the mysql.user table. - --secure-auth Disallow authentication for accounts that have old - (pre-4.1) passwords - --secure-file-priv=name - Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to - files within specified directory - --server-id=# Uniquely identifies the server instance in the community - of replication partners - -O, --set-variable=name - Change the value of a variable. Please note that this - option is deprecated;you can set variables directly with - --variable-name=value. - --show-slave-auth-info - Show user and password in SHOW SLAVE HOSTS on this master - --skip-grant-tables Start without grant tables. This gives all users FULL - ACCESS to all tables! - --skip-host-cache Don't cache host names. - --skip-locking Deprecated option, use --skip-external-locking instead. - --skip-name-resolve Don't resolve hostnames. All hostnames are IP's or - 'localhost'. - --skip-networking Don't allow connection with TCP/IP - --skip-new Don't use new, possible wrong routines. - --skip-show-database - Don't allow 'SHOW DATABASE' commands - --skip-slave-start If set, slave is not autostarted. - --skip-stack-trace Don't print a stack trace on failure. - --skip-symlink Don't allow symlinking of tables. Deprecated option. Use - --skip-symbolic-links instead. - --skip-thread-priority - Don't give threads different priorities. This option is - deprecated because it has no effect; the implied behavior - is already the default. - --slave-compressed-protocol - Use compression on master/slave protocol - --slave-exec-mode=name - Modes for how replication events should be executed. - Legal values are STRICT (default) and IDEMPOTENT. In - IDEMPOTENT mode, replication will not stop for operations - that are idempotent. In STRICT mode, replication will - stop on any unexpected difference between the master and - the slave - --slave-load-tmpdir=name - The location where the slave should put its temporary - files when replicating a LOAD DATA INFILE command - --slave-net-timeout=# - Number of seconds to wait for more data from a - master/slave connection before aborting the read - --slave-skip-errors=name - Tells the slave thread to continue replication when a - query event returns an error from the provided list - --slave-transaction-retries=# - Number of times the slave SQL thread will retry a - transaction in case it failed with a deadlock or elapsed - lock wait timeout, before giving up and stopping - --slow-launch-time=# - If creating the thread takes longer than this value (in - seconds), the Slow_launch_threads counter will be - incremented - --slow-query-log Log slow queries to a table or log file. Defaults logging - to a file hostname-slow.log or a table mysql.slow_log if - --log-output=TABLE is used. Must be enabled to activate - other slow log options - --slow-query-log-file=name - Log slow queries to given log file. Defaults logging to - hostname-slow.log. Must be enabled to activate other slow - log options - --socket=name Socket file to use for connection - --sort-buffer-size=# - Each thread that needs to do a sort allocates a buffer of - this size - --sporadic-binlog-dump-fail - Option used by mysql-test for debugging and testing of - replication. - --sql-bin-update-same - The update log is deprecated since version 5.0, is - replaced by the binary log and this option does nothing - anymore. - --sql-mode=name Syntax: sql-mode=mode[,mode[,mode...]]. See the manual - for the complete list of valid sql modes - -s, --symbolic-links - Enable symbolic link support. - --sync-binlog=# Synchronously flush binary log to disk after every #th - event. Use 0 (default) to disable synchronous flushing - --sync-frm Sync .frm files to disk on creation - (Defaults to on; use --skip-sync-frm to disable.) - --sync-master-info=# - Synchronously flush master info to disk after every #th - event. Use 0 (default) to disable synchronous flushing - --sync-relay-log=# Synchronously flush relay log to disk after every #th - event. Use 0 (default) to disable synchronous flushing - --sync-relay-log-info=# - Synchronously flush relay log info to disk after every - #th transaction. Use 0 (default) to disable synchronous - flushing - --sysdate-is-now Non-default option to alias SYSDATE() to NOW() to make it - safe-replicable. Since 5.0, SYSDATE() returns a `dynamic' - value different for different invocations, even within - the same statement. - --table-cache=# Deprecated; use --table-open-cache instead. - --table-definition-cache=# - The number of cached table definitions - --table-lock-wait-timeout=# - Timeout in seconds to wait for a table level lock before - returning an error. Used only if the connection has - active cursors - --table-open-cache=# - The number of cached open tables - --tc-heuristic-recover=name - Decision to use in heuristic recover process. Possible - values are COMMIT or ROLLBACK. - --thread-cache-size=# - How many threads we should keep in a cache for reuse - --thread-handling=name - Define threads usage for handling queries, one of - one-thread-per-connection, no-threads - --thread-stack=# The stack size for each thread - --time-format=name The TIME format (ignored) - --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are - currently supported) - --tmp-table-size=# If an internal in-memory temporary table exceeds this - size, MySQL will automatically convert it to an on-disk - MyISAM table - -t, --tmpdir=name Path for temporary files. Several paths may be specified, - separated by a colon (:), in this case they are used in a - round-robin fashion - --transaction-alloc-block-size=# - Allocation block size for transactions to be stored in - binary log - --transaction-isolation=name - Default transaction isolation level. - --transaction-prealloc-size=# - Persistent buffer for transactions to be stored in binary - log - --updatable-views-with-limit=name - YES = Don't issue an error message (warning only) if a - VIEW without presence of a key of the underlying table is - used in queries with a LIMIT clause for updating. NO = - Prohibit update of a VIEW, which does not contain a key - of the underlying table and the query uses a LIMIT clause - (usually get from GUI tools) - -s, --use-symbolic-links - Enable symbolic link support. Deprecated option; use - --symbolic-links instead. - -u, --user=name Run mysqld daemon as user. - -v, --verbose Used with --help option for detailed help - -V, --version Output version information and exit. - --wait-timeout=# The number of seconds the server waits for activity on a - connection before closing it - -W, --warnings[=#] Deprecated; use --log-warnings instead. + --abort-slave-event-count=# + Option used by mysql-test for debugging and testing of + replication. + --allow-suspicious-udfs + Allows use of UDFs consisting of only one symbol xxx() + without corresponding xxx_init() or xxx_deinit(). That + also means that one can load any function from any + library, for example exit() from libc.so + -a, --ansi Use ANSI SQL syntax instead of MySQL syntax. This mode + will also set transaction isolation level 'serializable'. + --auto-increment-increment[=#] + Auto-increment columns are incremented by this + --auto-increment-offset[=#] + Offset added to Auto-increment columns. Used when + auto-increment-increment != 1 + --automatic-sp-privileges + Creating and dropping stored procedures alters ACLs + (Defaults to on; use --skip-automatic-sp-privileges to disable.) + --back-log=# The number of outstanding connection requests MySQL can + have. This comes into play when the main MySQL thread + gets very many connection requests in a very short time + -b, --basedir=name Path to installation directory. All paths are usually + resolved relative to this + --big-tables Allow big result sets by saving all temporary sets on + file (Solves most 'table full' errors) + --bind-address=name IP address to bind to. + --binlog-cache-size=# + The size of the cache to hold the SQL statements for the + binary log during a transaction. If you often use big, + multi-statement transactions you can increase this to get + more performance + --binlog-do-db=name Tells the master it should log updates for the specified + database, and exclude all others not explicitly + mentioned. + --binlog-format=name + What form of binary logging the master will use: either + ROW for row-based binary logging, STATEMENT for + statement-based binary logging, or MIXED. MIXED is + statement-based binary logging except for those + statements where only row-based is correct: those which + involve user-defined functions (i.e. UDFs) or the UUID() + function; for those, row-based binary logging is + automatically used. If NDBCLUSTER is enabled and + binlog-format is MIXED, the format switches to row-based + and back implicitly per each query accessing an + NDBCLUSTER table + --binlog-ignore-db=name + Tells the master that updates to the given database + should not be logged tothe binary log. + --binlog-row-event-max-size=# + The maximum size of a row-based binary log event in + bytes. Rows will be grouped into events smaller than this + size if possible. The value has to be a multiple of 256. + --bootstrap Used by mysql installation scripts. + --bulk-insert-buffer-size=# + Size of tree cache used in bulk insert optimisation. Note + that this is a limit per thread! + --character-set-client-handshake + Don't ignore client side character set value sent during + handshake. + (Defaults to on; use --skip-character-set-client-handshake to disable.) + --character-set-filesystem=name + Set the filesystem character set. + -C, --character-set-server=name + Set the default character set. + --character-sets-dir=name + Directory where character sets are + -r, --chroot=name Chroot mysqld daemon during startup. + --collation-server=name + Set the default collation. + --completion-type=name + The transaction completion type, one of NO_CHAIN, CHAIN, + RELEASE + --concurrent-insert[=name] + Use concurrent insert with MyISAM. Possible values are + NEVER, AUTO, ALWAYS + --connect-timeout=# The number of seconds the mysqld server is waiting for a + connect packet before responding with 'Bad handshake' + --console Write error output on screen; Don't remove the console + window on windows. + --core-file Write core on errors. + -h, --datadir=name Path to the database root directory + --date-format=name The DATE format (ignored) + --datetime-format=name + The DATETIME format (ignored) + -C, --default-character-set=name + Set the default character set (deprecated option, use + --character-set-server instead). + --default-collation=name + Set the default collation (deprecated option, use + --collation-server instead). + --default-storage-engine=name + The default storage engine for new tables + --default-time-zone=name + Set the default time zone. + --default-week-format=# + The default week format used by WEEK() functions + --delay-key-write[=name] + Type of DELAY_KEY_WRITE + --delay-key-write-for-all-tables + Don't flush key buffers between writes for any MyISAM + table (Deprecated option, use --delay-key-write=all + instead). + --delayed-insert-limit=# + After inserting delayed_insert_limit rows, the INSERT + DELAYED handler will check if there are any SELECT + statements pending. If so, it allows these to execute + before continuing + --delayed-insert-timeout=# + How long a INSERT DELAYED thread should wait for INSERT + statements before terminating + --delayed-queue-size=# + What size queue (in rows) should be allocated for + handling INSERT DELAYED. If the queue becomes full, any + client that does INSERT DELAYED will wait until there is + room in the queue again + --disconnect-slave-event-count=# + Option used by mysql-test for debugging and testing of + replication. + --div-precision-increment=# + Precision of the result of '/' operator will be increased + on that value + --enable-locking Deprecated option, use --external-locking instead. + --engine-condition-pushdown + Push supported query conditions to the storage engine + (Defaults to on; use --skip-engine-condition-pushdown to disable.) + --event-scheduler[=name] + Enable the event scheduler. Possible values are ON, OFF, + and DISABLED (keep the event scheduler completely + deactivated, it cannot be activated run-time) + -T, --exit-info[=#] Used for debugging; Use at your own risk! + --expire-logs-days=# + If non-zero, binary logs will be purged after + expire_logs_days days; possible purges happen at startup + and at binary log rotation + --external-locking Use system (external) locking (disabled by default). + With this option enabled you can run myisamchk to test + (not repair) tables while the MySQL server is running. + Disable with --skip-external-locking. + --flush Flush MyISAM tables to disk between SQL commands + --flush-time=# A dedicated thread is created to flush all tables at the + given interval + --ft-boolean-syntax=name + List of operators for MATCH ... AGAINST ( ... IN BOOLEAN + MODE) + --ft-max-word-len=# The maximum length of the word to be included in a + FULLTEXT index. Note: FULLTEXT indexes must be rebuilt + after changing this variable + --ft-min-word-len=# The minimum length of the word to be included in a + FULLTEXT index. Note: FULLTEXT indexes must be rebuilt + after changing this variable + --ft-query-expansion-limit=# + Number of best matches to use for query expansion + --ft-stopword-file=name + Use stopwords from this file instead of built-in list + --gdb Set up signals usable for debugging + --general-log Log connections and queries to a table or log file. + Defaults logging to a file hostname.log or a table + mysql.general_logif --log-output=TABLE is used + --general-log-file=name + Log connections and queries to given file + --group-concat-max-len=# + The maximum length of the result of function + GROUP_CONCAT() + -?, --help Display this help and exit. + --ignore-builtin-innodb + Disable initialization of builtin InnoDB plugin + --init-connect=name Command(s) that are executed for each new connection + --init-file=name Read SQL commands from this file at startup + --init-rpl-role=name + Set the replication role. + --init-slave=name Command(s) that are executed by a slave server each time + the SQL thread starts + --interactive-timeout=# + The number of seconds the server waits for activity on an + interactive connection before closing it + --join-buffer-size=# + The size of the buffer that is used for full joins + --keep-files-on-create + Don't overwrite stale .MYD and .MYI even if no directory + is specified + --key-buffer-size=# The size of the buffer used for index blocks for MyISAM + tables. Increase this to get better index handling (for + all reads and multiple writes) to as much as you can + afford + --key-cache-age-threshold=# + This characterizes the number of hits a hot block has to + be untouched until it is considered aged enough to be + downgraded to a warm block. This specifies the percentage + ratio of that number of hits to the total number of + blocks in key cache + --key-cache-block-size=# + The default size of key cache blocks + --key-cache-division-limit=# + The minimum percentage of warm blocks in key cache + -L, --language=name Client error messages in given language. May be given as + a full path. Deprecated. Use --lc-messages-dir instead. + --large-pages Enable support for large pages + --lc-messages=name Set the language used for the error messages. + -L, --lc-messages-dir=name + Directory where error messages are + --lc-time-names=name + Set the language used for the month names and the days of + the week. + --local-infile Enable LOAD DATA LOCAL INFILE + (Defaults to on; use --skip-local-infile to disable.) + -l, --log[=name] Log connections and queries to file (deprecated option, + use --general-log/--general-log-file instead). + --log-bin[=name] Log update queries in binary format. Optional (but + strongly recommended to avoid replication problems if + server's hostname changes) argument should be the chosen + location for the binary log files. + --log-bin-index=name + File that holds the names for last binary log files. + --log-bin-trust-function-creators + If set to FALSE (the default), then when --log-bin is + used, creation of a stored function (or trigger) is + allowed only to users having the SUPER privilege and only + if this stored function (trigger) may not break binary + logging. Note that if ALL connections to this server + ALWAYS use row-based binary logging, the security issues + do not exist and the binary logging cannot break, so you + can safely set this to TRUE + --log-error[=name] Error log file + --log-isam[=name] Log all MyISAM changes to file. + -0, --log-long-format + Log some extra information to update log. Please note + that this option is deprecated; see --log-short-format + option. + --log-output=name Syntax: log-output=value[,value...], where "value" could + be TABLE, FILE or NONE + --log-queries-not-using-indexes + Log queries that are executed without benefit of any + index to the slow log if it is open + --log-short-format Don't log extra information to update and slow-query + logs. + --log-slave-updates Tells the slave to log the updates from the slave thread + to the binary log. You will need to turn it on if you + plan to daisy-chain the slaves + --log-slow-admin-statements + Log slow OPTIMIZE, ANALYZE, ALTER and other + administrative statements to the slow log if it is open. + --log-slow-queries[=name] + Log slow queries to a table or log file. Defaults logging + to table mysql.slow_log or hostname-slow.log if + --log-output=file is used. Must be enabled to activate + other slow log options. Deprecated option, use + --slow-query-log/--slow-query-log-file instead. + --log-slow-slave-statements + Log slow statements executed by slave thread to the slow + log if it is open. + --log-tc=name Path to transaction coordinator log (used for + transactions that affect more than one storage engine, + when binary log is disabled) + --log-tc-size=# Size of transaction coordinator log. + --log-update[=name] The update log is deprecated since version 5.0, is + replaced by the binary log and this option justs turns on + --log-bin instead. + -W, --log-warnings[=#] + Log some not critical warnings to the log file + --long-query-time=# Log all queries that have taken more than long_query_time + seconds to execute to file. The argument will be treated + as a decimal value with microsecond precision + --low-priority-updates + INSERT/DELETE/UPDATE has lower priority than selects + --lower-case-table-names[=#] + If set to 1 table names are stored in lowercase on disk + and table names will be case-insensitive. Should be set + to 2 if you are using a case insensitive file system + --master-info-file=name + The location and name of the file that remembers the + master and where the I/O replication thread is in the + master's binlogs. + --master-retry-count=# + The number of tries the slave will make to connect to the + master before giving up. + --max-allowed-packet=# + Max packet length to send to or receive from the server + --max-binlog-cache-size=# + Can be used to restrict the total size used to cache a + multi-transaction query + --max-binlog-dump-events=# + Option used by mysql-test for debugging and testing of + replication. + --max-binlog-size=# Binary log will be rotated automatically when the size + exceeds this value. Will also apply to relay logs if + max_relay_log_size is 0 + --max-connect-errors=# + If there is more than this number of interrupted + connections from a host this host will be blocked from + further connections + --max-connections=# The number of simultaneous clients allowed + --max-delayed-threads=# + Don't start more than this number of threads to handle + INSERT DELAYED statements. If set to zero INSERT DELAYED + will be not used + --max-error-count=# Max number of errors/warnings to store for a statement + --max-heap-table-size=# + Don't allow creation of heap tables bigger than this + --max-join-size=# Joins that are probably going to read more than + max_join_size records return an error + --max-length-for-sort-data=# + Max number of bytes in sorted records + --max-prepared-stmt-count=# + Maximum number of prepared statements in the server + --max-relay-log-size=# + If non-zero: relay log will be rotated automatically when + the size exceeds this value; if zero: when the size + exceeds max_binlog_size + --max-seeks-for-key=# + Limit assumed max number of seeks when looking up rows + based on a key + --max-sort-length=# The number of bytes to use when sorting BLOB or TEXT + values (only the first max_sort_length bytes of each + value are used; the rest are ignored) + --max-sp-recursion-depth[=#] + Maximum stored procedure recursion depth + --max-tmp-tables=# Maximum number of temporary tables a client can keep open + at a time + --max-user-connections=# + The maximum number of active connections for a single + user (0 = no limit) + --max-write-lock-count=# + After this many write locks, allow some read locks to run + in between + --memlock Lock mysqld in memory. + --min-examined-row-limit=# + Don't write queries to slow log that examine fewer rows + than that + --multi-range-count=# + Number of key ranges to request at once + --myisam-block-size=# + Block size to be used for MyISAM index pages + --myisam-data-pointer-size=# + Default pointer size to be used for MyISAM tables + --myisam-max-sort-file-size=# + Don't use the fast sort index method to created index if + the temporary file would get bigger than this + --myisam-mmap-size=# + Restricts the total memory used for memory mapping of + MySQL tables + --myisam-recover-options[=name] + Syntax: myisam-recover-options[=option[,option...]], + where option can be DEFAULT, BACKUP, FORCE, QUICK, or OFF + --myisam-repair-threads=# + If larger than 1, when repairing a MyISAM table all + indexes will be created in parallel, with one thread per + index. The value of 1 disables parallel repair + --myisam-sort-buffer-size=# + The buffer that is allocated when sorting the index when + doing a REPAIR or when creating indexes with CREATE INDEX + or ALTER TABLE + --myisam-stats-method=name + Specifies how MyISAM index statistics collection code + should treat NULLs. Possible values of name are + NULLS_UNEQUAL (default behavior for 4.1 and later), + NULLS_EQUAL (emulate 4.0 behavior), and NULLS_IGNORED + --myisam-use-mmap Use memory mapping for reading and writing MyISAM tables + --net-buffer-length=# + Buffer length for TCP/IP and socket communication + --net-read-timeout=# + Number of seconds to wait for more data from a connection + before aborting the read + --net-retry-count=# If a read on a communication port is interrupted, retry + this many times before giving up + --net-write-timeout=# + Number of seconds to wait for a block to be written to a + connection before aborting the write + -n, --new Use very new possible "unsafe" functions + --old Use compatible behavior + --old-alter-table Use old, non-optimized alter table + --old-passwords Use old password encryption method (needed for 4.0 and + older clients) + --old-style-user-limits + Enable old-style user limits (before 5.0.3 user resources + were counted per each user+host vs. per account) + --one-thread (deprecated): Only use one thread (for debugging under + Linux). Use thread-handling=no-threads instead + --open-files-limit=# + If this is not 0, then mysqld will use this value to + reserve file descriptors to use with setrlimit(). If this + value is 0 then mysqld will reserve max_connections*5 or + max_connections + table_cache*2 (whichever is larger) + number of file descriptors + --optimizer-prune-level=# + Controls the heuristic(s) applied during query + optimization to prune less-promising partial plans from + the optimizer search space. Meaning: 0 - do not apply any + heuristic, thus perform exhaustive search; 1 - prune + plans based on number of retrieved rows + --optimizer-search-depth=# + Maximum depth of search performed by the query optimizer. + Values larger than the number of relations in a query + result in better query plans, but take longer to compile + a query. Values smaller than the number of tables in a + relation result in faster optimization, but may produce + very bad query plans. If set to 0, the system will + automatically pick a reasonable value; if set to 63, the + optimizer will switch to the original find_best + search(used for testing/comparison) + --optimizer-switch=name + optimizer_switch=option=val[,option=val...], where option + is one of {index_merge, index_merge_union, + index_merge_sort_union, index_merge_intersection} and val + is one of {on, off, default} + --pid-file=name Pid file used by safe_mysqld + --plugin-dir=name Directory for plugins + --plugin-load=name Optional semicolon-separated list of plugins to load, + where each plugin is identified as name=library, where + name is the plugin name and library is the plugin library + in plugin_dir. + -P, --port=# Port number to use for connection or 0 to default to, + my.cnf, $MYSQL_TCP_PORT, /etc/services, built-in default + (3306), whatever comes first + --port-open-timeout=# + Maximum time in seconds to wait for the port to become + free. (Default: no wait) + --preload-buffer-size=# + The size of the buffer that is allocated when preloading + indexes + --profiling-history-size=# + Limit of query profiling memory + --query-alloc-block-size=# + Allocation block size for query parsing and execution + --query-cache-limit=# + Don't cache results that are bigger than this + --query-cache-min-res-unit=# + The minimum size for blocks allocated by the query cache + --query-cache-size=# + The memory allocated to store results from old queries + --query-cache-type=name + OFF = Don't cache or retrieve results. ON = Cache all + results except SELECT SQL_NO_CACHE ... queries. DEMAND = + Cache only SELECT SQL_CACHE ... queries + --query-cache-wlock-invalidate + Invalidate queries in query cache on LOCK for write + --query-prealloc-size=# + Persistent buffer for query parsing and execution + --range-alloc-block-size=# + Allocation block size for storing ranges during + optimization + --read-buffer-size=# + Each thread that does a sequential scan allocates a + buffer of this size for each table it scans. If you do + many sequential scans, you may want to increase this + value + --read-only Make all non-temporary tables read-only, with the + exception for replication (slave) threads and users with + the SUPER privilege + --read-rnd-buffer-size=# + When reading rows in sorted order after a sort, the rows + are read through this buffer to avoid a disk seeks. If + not set, then it's set to the value of record_buffer + --record-buffer=# Deprecated; use --read-buffer-size instead. + --relay-log=name The location and name to use for relay logs + --relay-log-index=name + The location and name to use for the file that keeps a + list of the last relay logs + --relay-log-info-file=name + The location and name of the file that remembers where + the SQL replication thread is in the relay logs + --relay-log-purge if disabled - do not purge relay logs. if enabled - purge + them as soon as they are no more needed + (Defaults to on; use --skip-relay-log-purge to disable.) + --relay-log-recovery + Enables automatic relay log recovery right after the + database startup, which means that the IO Thread starts + re-fetching from the master right after the last + transaction processed + --relay-log-space-limit=# + Maximum space to use for all relay logs + --replicate-do-db=name + Tells the slave thread to restrict replication to the + specified database. To specify more than one database, + use the directive multiple times, once for each database. + Note that this will only work if you do not use + cross-database queries such as UPDATE some_db.some_table + SET foo='bar' while having selected a different or no + database. If you need cross database updates to work, + make sure you have 3.23.28 or later, and use + replicate-wild-do-table=db_name.%. + --replicate-do-table=name + Tells the slave thread to restrict replication to the + specified table. To specify more than one table, use the + directive multiple times, once for each table. This will + work for cross-database updates, in contrast to + replicate-do-db. + --replicate-ignore-db=name + Tells the slave thread to not replicate to the specified + database. To specify more than one database to ignore, + use the directive multiple times, once for each database. + This option will not work if you use cross database + updates. If you need cross database updates to work, make + sure you have 3.23.28 or later, and use + replicate-wild-ignore-table=db_name.%. + --replicate-ignore-table=name + Tells the slave thread to not replicate to the specified + table. To specify more than one table to ignore, use the + directive multiple times, once for each table. This will + work for cross-datbase updates, in contrast to + replicate-ignore-db. + --replicate-rewrite-db=name + Updates to a database with a different name than the + original. Example: + replicate-rewrite-db=master_db_name->slave_db_name. + --replicate-same-server-id + In replication, if set to 1, do not skip events having + our server id. Default value is 0 (to break infinite + loops in circular replication). Can't be set to 1 if + --log-slave-updates is used. + --replicate-wild-do-table=name + Tells the slave thread to restrict replication to the + tables that match the specified wildcard pattern. To + specify more than one table, use the directive multiple + times, once for each table. This will work for + cross-database updates. Example: + replicate-wild-do-table=foo%.bar% will replicate only + updates to tables in all databases that start with foo + and whose table names start with bar. + --replicate-wild-ignore-table=name + Tells the slave thread to not replicate to the tables + that match the given wildcard pattern. To specify more + than one table to ignore, use the directive multiple + times, once for each table. This will work for + cross-database updates. Example: + replicate-wild-ignore-table=foo%.bar% will not do updates + to tables in databases that start with foo and whose + table names start with bar. + --report-host=name Hostname or IP of the slave to be reported to the master + during slave registration. Will appear in the output of + SHOW SLAVE HOSTS. Leave unset if you do not want the + slave to register itself with the master. Note that it is + not sufficient for the master to simply read the IP of + the slave off the socket once the slave connects. Due to + NAT and other routing issues, that IP may not be valid + for connecting to the slave from the master or other + hosts + --report-password=name + The account password of the slave to be reported to the + master during slave registration + --report-port=# Port for connecting to slave reported to the master + during slave registration. Set it only if the slave is + listening on a non-default port or if you have a special + tunnel from the master or other clients to the slave. If + not sure, leave this option unset + --report-user=name The account user name of the slave to be reported to the + master during slave registration + --rpl-recovery-rank=# + Unused, will be removed + --safe-mode Skip some optimize stages (for testing). + --safe-show-database + Deprecated option; use GRANT SHOW DATABASES instead... + --safe-user-create Don't allow new user creation by the user who has no + write privileges to the mysql.user table. + --secure-auth Disallow authentication for accounts that have old + (pre-4.1) passwords + --secure-file-priv=name + Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to + files within specified directory + --server-id=# Uniquely identifies the server instance in the community + of replication partners + -O, --set-variable=name + Change the value of a variable. Please note that this + option is deprecated;you can set variables directly with + --variable-name=value. + --show-slave-auth-info + Show user and password in SHOW SLAVE HOSTS on this master + --skip-grant-tables Start without grant tables. This gives all users FULL + ACCESS to all tables! + --skip-host-cache Don't cache host names. + --skip-locking Deprecated option, use --skip-external-locking instead. + --skip-name-resolve Don't resolve hostnames. All hostnames are IP's or + 'localhost'. + --skip-networking Don't allow connection with TCP/IP + --skip-new Don't use new, possible wrong routines. + --skip-show-database + Don't allow 'SHOW DATABASE' commands + --skip-slave-start If set, slave is not autostarted. + --skip-stack-trace Don't print a stack trace on failure. + --skip-symlink Don't allow symlinking of tables. Deprecated option. Use + --skip-symbolic-links instead. + --skip-thread-priority + Don't give threads different priorities. This option is + deprecated because it has no effect; the implied behavior + is already the default. + --slave-compressed-protocol + Use compression on master/slave protocol + --slave-exec-mode=name + Modes for how replication events should be executed. + Legal values are STRICT (default) and IDEMPOTENT. In + IDEMPOTENT mode, replication will not stop for operations + that are idempotent. In STRICT mode, replication will + stop on any unexpected difference between the master and + the slave + --slave-load-tmpdir=name + The location where the slave should put its temporary + files when replicating a LOAD DATA INFILE command + --slave-net-timeout=# + Number of seconds to wait for more data from a + master/slave connection before aborting the read + --slave-skip-errors=name + Tells the slave thread to continue replication when a + query event returns an error from the provided list + --slave-transaction-retries=# + Number of times the slave SQL thread will retry a + transaction in case it failed with a deadlock or elapsed + lock wait timeout, before giving up and stopping + --slow-launch-time=# + If creating the thread takes longer than this value (in + seconds), the Slow_launch_threads counter will be + incremented + --slow-query-log Log slow queries to a table or log file. Defaults logging + to a file hostname-slow.log or a table mysql.slow_log if + --log-output=TABLE is used. Must be enabled to activate + other slow log options + --slow-query-log-file=name + Log slow queries to given log file. Defaults logging to + hostname-slow.log. Must be enabled to activate other slow + log options + --socket=name Socket file to use for connection + --sort-buffer-size=# + Each thread that needs to do a sort allocates a buffer of + this size + --sporadic-binlog-dump-fail + Option used by mysql-test for debugging and testing of + replication. + --sql-bin-update-same + The update log is deprecated since version 5.0, is + replaced by the binary log and this option does nothing + anymore. + --sql-mode=name Syntax: sql-mode=mode[,mode[,mode...]]. See the manual + for the complete list of valid sql modes + -s, --symbolic-links + Enable symbolic link support. + --sync-binlog=# Synchronously flush binary log to disk after every #th + event. Use 0 (default) to disable synchronous flushing + --sync-frm Sync .frm files to disk on creation + (Defaults to on; use --skip-sync-frm to disable.) + --sync-master-info=# + Synchronously flush master info to disk after every #th + event. Use 0 (default) to disable synchronous flushing + --sync-relay-log=# Synchronously flush relay log to disk after every #th + event. Use 0 (default) to disable synchronous flushing + --sync-relay-log-info=# + Synchronously flush relay log info to disk after every + #th transaction. Use 0 (default) to disable synchronous + flushing + --sysdate-is-now Non-default option to alias SYSDATE() to NOW() to make it + safe-replicable. Since 5.0, SYSDATE() returns a `dynamic' + value different for different invocations, even within + the same statement. + --table-cache=# Deprecated; use --table-open-cache instead. + --table-definition-cache=# + The number of cached table definitions + --table-lock-wait-timeout=# + Timeout in seconds to wait for a table level lock before + returning an error. Used only if the connection has + active cursors + --table-open-cache=# + The number of cached open tables + --tc-heuristic-recover=name + Decision to use in heuristic recover process. Possible + values are COMMIT or ROLLBACK. + --thread-cache-size=# + How many threads we should keep in a cache for reuse + --thread-handling=name + Define threads usage for handling queries, one of + one-thread-per-connection, no-threads + --thread-stack=# The stack size for each thread + --time-format=name The TIME format (ignored) + --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are + currently supported) + --tmp-table-size=# If an internal in-memory temporary table exceeds this + size, MySQL will automatically convert it to an on-disk + MyISAM table + -t, --tmpdir=name Path for temporary files. Several paths may be specified, + separated by a colon (:), in this case they are used in a + round-robin fashion + --transaction-alloc-block-size=# + Allocation block size for transactions to be stored in + binary log + --transaction-isolation=name + Default transaction isolation level. + --transaction-prealloc-size=# + Persistent buffer for transactions to be stored in binary + log + --updatable-views-with-limit=name + YES = Don't issue an error message (warning only) if a + VIEW without presence of a key of the underlying table is + used in queries with a LIMIT clause for updating. NO = + Prohibit update of a VIEW, which does not contain a key + of the underlying table and the query uses a LIMIT clause + (usually get from GUI tools) + -s, --use-symbolic-links + Enable symbolic link support. Deprecated option; use + --symbolic-links instead. + -u, --user=name Run mysqld daemon as user. + -v, --verbose Used with --help option for detailed help + -V, --version Output version information and exit. + --wait-timeout=# The number of seconds the server waits for activity on a + connection before closing it + -W, --warnings[=#] Deprecated; use --log-warnings instead. Variables (--variable-name=value) -and boolean options {FALSE|TRUE} Value (after reading options) ---------------------------------- ---------------------------------------- -abort-slave-event-count 0 -allow-suspicious-udfs FALSE -auto-increment-increment 1 -auto-increment-offset 1 -automatic-sp-privileges TRUE -back-log 50 -big-tables FALSE -bind-address (No default value) -binlog-cache-size 32768 -binlog-format STATEMENT -binlog-row-event-max-size 1024 -bulk-insert-buffer-size 8388608 -character-set-client-handshake TRUE -character-set-filesystem binary -character-set-server latin1 -character-sets-dir MYSQL_CHARSETSDIR/ -chroot (No default value) -collation-server latin1_swedish_ci -completion-type NO_CHAIN -concurrent-insert AUTO -connect-timeout 10 -console FALSE -datadir MYSQLTEST_VARDIR/install.db/ -date-format %Y-%m-%d -datetime-format %Y-%m-%d %H:%i:%s -default-character-set latin1 -default-collation latin1_swedish_ci -default-storage-engine MyISAM -default-time-zone (No default value) -default-week-format 0 -delay-key-write ON -delayed-insert-limit 100 -delayed-insert-timeout 300 -delayed-queue-size 1000 -disconnect-slave-event-count 0 -div-precision-increment 4 -enable-locking FALSE -engine-condition-pushdown TRUE -event-scheduler OFF -expire-logs-days 0 -external-locking FALSE -flush FALSE -flush-time 0 -ft-boolean-syntax + -><()~*:""&| -ft-max-word-len 84 -ft-min-word-len 4 -ft-query-expansion-limit 20 -ft-stopword-file (No default value) -gdb FALSE -general-log FALSE -group-concat-max-len 1024 -help TRUE -ignore-builtin-innodb FALSE -init-connect -init-file (No default value) -init-rpl-role MASTER -init-slave -interactive-timeout 28800 -join-buffer-size 131072 -keep-files-on-create FALSE -key-buffer-size 8388608 -key-cache-age-threshold 300 -key-cache-block-size 1024 -key-cache-division-limit 100 -language MYSQL_SHAREDIR/ -large-pages FALSE -lc-messages en_US -lc-messages-dir MYSQL_SHAREDIR/ -lc-time-names en_US -local-infile TRUE -log-bin (No default value) -log-bin-index (No default value) -log-bin-trust-function-creators FALSE -log-error -log-isam myisam.log -log-output FILE -log-queries-not-using-indexes FALSE -log-short-format FALSE -log-slave-updates FALSE -log-slow-admin-statements FALSE -log-slow-slave-statements FALSE -log-tc tc.log -log-tc-size 24576 -log-update (No default value) -log-warnings 1 -long-query-time 10 -low-priority-updates FALSE -lower-case-table-names 1 -master-info-file master.info -master-retry-count 86400 -max-allowed-packet 1048576 -max-binlog-cache-size 18446744073709547520 -max-binlog-dump-events 0 -max-binlog-size 1073741824 -max-connect-errors 10 -max-connections 151 -max-delayed-threads 20 -max-error-count 64 -max-heap-table-size 16777216 -max-join-size 18446744073709551615 -max-length-for-sort-data 1024 -max-prepared-stmt-count 16382 -max-relay-log-size 0 -max-seeks-for-key 18446744073709551615 -max-sort-length 1024 -max-sp-recursion-depth 0 -max-tmp-tables 32 -max-user-connections 0 -max-write-lock-count 18446744073709551615 -memlock FALSE -min-examined-row-limit 0 -multi-range-count 256 -myisam-block-size 1024 -myisam-data-pointer-size 6 -myisam-max-sort-file-size 9223372036853727232 -myisam-mmap-size 18446744073709551615 -myisam-recover-options OFF -myisam-repair-threads 1 -myisam-sort-buffer-size 8388608 -myisam-stats-method nulls_unequal -myisam-use-mmap FALSE -net-buffer-length 16384 -net-read-timeout 30 -net-retry-count 10 -net-write-timeout 60 -new FALSE -old FALSE -old-alter-table FALSE -old-passwords FALSE -old-style-user-limits FALSE -optimizer-prune-level 1 -optimizer-search-depth 62 -optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on -plugin-load (No default value) -port 3306 -port-open-timeout 0 -preload-buffer-size 32768 -profiling-history-size 15 -query-alloc-block-size 8192 -query-cache-limit 1048576 -query-cache-min-res-unit 4096 -query-cache-size 0 -query-cache-type ON -query-cache-wlock-invalidate FALSE -query-prealloc-size 8192 -range-alloc-block-size 4096 -read-buffer-size 131072 -read-only FALSE -read-rnd-buffer-size 262144 -record-buffer 131072 -relay-log (No default value) -relay-log-index (No default value) -relay-log-info-file relay-log.info -relay-log-purge TRUE -relay-log-recovery FALSE -relay-log-space-limit 0 -replicate-same-server-id FALSE -report-host (No default value) -report-password (No default value) -report-port 3306 -report-user (No default value) -rpl-recovery-rank 0 -safe-user-create FALSE -secure-auth FALSE -secure-file-priv (No default value) -server-id 0 -show-slave-auth-info FALSE -skip-grant-tables TRUE -skip-networking FALSE -skip-show-database FALSE -skip-slave-start FALSE -slave-compressed-protocol FALSE -slave-exec-mode STRICT -slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ -slave-net-timeout 3600 -slave-skip-errors (No default value) -slave-transaction-retries 10 -slow-launch-time 2 -slow-query-log FALSE -socket /tmp/mysql.sock -sort-buffer-size 2097152 -sporadic-binlog-dump-fail FALSE -sql-mode -symbolic-links FALSE -sync-binlog 0 -sync-frm TRUE -sync-master-info 0 -sync-relay-log 0 -sync-relay-log-info 0 -sysdate-is-now FALSE -table-cache 400 -table-definition-cache 400 -table-lock-wait-timeout 50 -table-open-cache 400 -tc-heuristic-recover COMMIT -thread-cache-size 0 -thread-handling one-thread-per-connection -thread-stack 262144 -time-format %H:%i:%s -timed-mutexes FALSE -tmp-table-size 16777216 -tmpdir MYSQLTEST_VARDIR/tmp/ -transaction-alloc-block-size 8192 -transaction-isolation REPEATABLE-READ -transaction-prealloc-size 4096 -updatable-views-with-limit YES -use-symbolic-links FALSE -verbose TRUE -wait-timeout 28800 -warnings 1 +abort-slave-event-count 0 +allow-suspicious-udfs FALSE +auto-increment-increment 1 +auto-increment-offset 1 +automatic-sp-privileges TRUE +back-log 50 +big-tables FALSE +bind-address (No default value) +binlog-cache-size 32768 +binlog-format STATEMENT +binlog-row-event-max-size 1024 +bulk-insert-buffer-size 8388608 +character-set-client-handshake TRUE +character-set-filesystem binary +character-set-server latin1 +character-sets-dir MYSQL_CHARSETSDIR/ +chroot (No default value) +collation-server latin1_swedish_ci +completion-type NO_CHAIN +concurrent-insert AUTO +connect-timeout 10 +console FALSE +datadir MYSQLTEST_VARDIR/install.db/ +date-format %Y-%m-%d +datetime-format %Y-%m-%d %H:%i:%s +default-character-set latin1 +default-collation latin1_swedish_ci +default-storage-engine MyISAM +default-time-zone (No default value) +default-week-format 0 +delay-key-write ON +delayed-insert-limit 100 +delayed-insert-timeout 300 +delayed-queue-size 1000 +disconnect-slave-event-count 0 +div-precision-increment 4 +enable-locking FALSE +engine-condition-pushdown TRUE +event-scheduler OFF +expire-logs-days 0 +external-locking FALSE +flush FALSE +flush-time 0 +ft-boolean-syntax + -><()~*:""&| +ft-max-word-len 84 +ft-min-word-len 4 +ft-query-expansion-limit 20 +ft-stopword-file (No default value) +gdb FALSE +general-log FALSE +group-concat-max-len 1024 +help TRUE +ignore-builtin-innodb FALSE +init-connect +init-file (No default value) +init-rpl-role MASTER +init-slave +interactive-timeout 28800 +join-buffer-size 131072 +keep-files-on-create FALSE +key-buffer-size 8388608 +key-cache-age-threshold 300 +key-cache-block-size 1024 +key-cache-division-limit 100 +language MYSQL_SHAREDIR/ +large-pages FALSE +lc-messages en_US +lc-messages-dir MYSQL_SHAREDIR/ +lc-time-names en_US +local-infile TRUE +log-bin (No default value) +log-bin-index (No default value) +log-bin-trust-function-creators FALSE +log-error +log-isam myisam.log +log-output FILE +log-queries-not-using-indexes FALSE +log-short-format FALSE +log-slave-updates FALSE +log-slow-admin-statements FALSE +log-slow-slave-statements FALSE +log-tc tc.log +log-tc-size 24576 +log-update (No default value) +log-warnings 1 +long-query-time 10 +low-priority-updates FALSE +lower-case-table-names 1 +master-info-file master.info +master-retry-count 86400 +max-allowed-packet 1048576 +max-binlog-cache-size 18446744073709547520 +max-binlog-dump-events 0 +max-binlog-size 1073741824 +max-connect-errors 10 +max-connections 151 +max-delayed-threads 20 +max-error-count 64 +max-heap-table-size 16777216 +max-join-size 18446744073709551615 +max-length-for-sort-data 1024 +max-prepared-stmt-count 16382 +max-relay-log-size 0 +max-seeks-for-key 18446744073709551615 +max-sort-length 1024 +max-sp-recursion-depth 0 +max-tmp-tables 32 +max-user-connections 0 +max-write-lock-count 18446744073709551615 +memlock FALSE +min-examined-row-limit 0 +multi-range-count 256 +myisam-block-size 1024 +myisam-data-pointer-size 6 +myisam-max-sort-file-size 9223372036853727232 +myisam-mmap-size 18446744073709551615 +myisam-recover-options OFF +myisam-repair-threads 1 +myisam-sort-buffer-size 8388608 +myisam-stats-method nulls_unequal +myisam-use-mmap FALSE +net-buffer-length 16384 +net-read-timeout 30 +net-retry-count 10 +net-write-timeout 60 +new FALSE +old FALSE +old-alter-table FALSE +old-passwords FALSE +old-style-user-limits FALSE +optimizer-prune-level 1 +optimizer-search-depth 62 +optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on +plugin-load (No default value) +port 3306 +port-open-timeout 0 +preload-buffer-size 32768 +profiling-history-size 15 +query-alloc-block-size 8192 +query-cache-limit 1048576 +query-cache-min-res-unit 4096 +query-cache-size 0 +query-cache-type ON +query-cache-wlock-invalidate FALSE +query-prealloc-size 8192 +range-alloc-block-size 4096 +read-buffer-size 131072 +read-only FALSE +read-rnd-buffer-size 262144 +record-buffer 131072 +relay-log (No default value) +relay-log-index (No default value) +relay-log-info-file relay-log.info +relay-log-purge TRUE +relay-log-recovery FALSE +relay-log-space-limit 0 +replicate-same-server-id FALSE +report-host (No default value) +report-password (No default value) +report-port 3306 +report-user (No default value) +rpl-recovery-rank 0 +safe-user-create FALSE +secure-auth FALSE +secure-file-priv (No default value) +server-id 0 +show-slave-auth-info FALSE +skip-grant-tables TRUE +skip-networking FALSE +skip-show-database FALSE +skip-slave-start FALSE +slave-compressed-protocol FALSE +slave-exec-mode STRICT +slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ +slave-net-timeout 3600 +slave-skip-errors (No default value) +slave-transaction-retries 10 +slow-launch-time 2 +slow-query-log FALSE +socket /tmp/mysql.sock +sort-buffer-size 2097152 +sporadic-binlog-dump-fail FALSE +sql-mode +symbolic-links FALSE +sync-binlog 0 +sync-frm TRUE +sync-master-info 0 +sync-relay-log 0 +sync-relay-log-info 0 +sysdate-is-now FALSE +table-cache 400 +table-definition-cache 400 +table-lock-wait-timeout 50 +table-open-cache 400 +tc-heuristic-recover COMMIT +thread-cache-size 0 +thread-handling one-thread-per-connection +thread-stack 262144 +time-format %H:%i:%s +timed-mutexes FALSE +tmp-table-size 16777216 +tmpdir MYSQLTEST_VARDIR/tmp/ +transaction-alloc-block-size 8192 +transaction-isolation REPEATABLE-READ +transaction-prealloc-size 4096 +updatable-views-with-limit YES +use-symbolic-links FALSE +verbose TRUE +wait-timeout 28800 +warnings 1 To see what values a running MySQL server is using, type 'mysqladmin variables' instead of 'mysqld --verbose --help'. diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index dfaf33f301d..d37548b40b6 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -4,928 +4,926 @@ The following options may be given as the first argument: --defaults-file=# Only read default options from the given file # --defaults-extra-file=# Read this file after the global files are read - --abort-slave-event-count=# - Option used by mysql-test for debugging and testing of - replication. - --allow-suspicious-udfs - Allows use of UDFs consisting of only one symbol xxx() - without corresponding xxx_init() or xxx_deinit(). That - also means that one can load any function from any - library, for example exit() from libc.so - -a, --ansi Use ANSI SQL syntax instead of MySQL syntax. This mode - will also set transaction isolation level 'serializable'. - --auto-increment-increment[=#] - Auto-increment columns are incremented by this - --auto-increment-offset[=#] - Offset added to Auto-increment columns. Used when - auto-increment-increment != 1 - --automatic-sp-privileges - Creating and dropping stored procedures alters ACLs - (Defaults to on; use --skip-automatic-sp-privileges to disable.) - --back-log=# The number of outstanding connection requests MySQL can - have. This comes into play when the main MySQL thread - gets very many connection requests in a very short time - -b, --basedir=name Path to installation directory. All paths are usually - resolved relative to this - --big-tables Allow big result sets by saving all temporary sets on - file (Solves most 'table full' errors) - --bind-address=name IP address to bind to. - --binlog-cache-size=# - The size of the cache to hold the SQL statements for the - binary log during a transaction. If you often use big, - multi-statement transactions you can increase this to get - more performance - --binlog-do-db=name Tells the master it should log updates for the specified - database, and exclude all others not explicitly - mentioned. - --binlog-format=name - What form of binary logging the master will use: either - ROW for row-based binary logging, STATEMENT for - statement-based binary logging, or MIXED. MIXED is - statement-based binary logging except for those - statements where only row-based is correct: those which - involve user-defined functions (i.e. UDFs) or the UUID() - function; for those, row-based binary logging is - automatically used. If NDBCLUSTER is enabled and - binlog-format is MIXED, the format switches to row-based - and back implicitly per each query accessing an - NDBCLUSTER table - --binlog-ignore-db=name - Tells the master that updates to the given database - should not be logged tothe binary log. - --binlog-row-event-max-size=# - The maximum size of a row-based binary log event in - bytes. Rows will be grouped into events smaller than this - size if possible. The value has to be a multiple of 256. - --bootstrap Used by mysql installation scripts. - --bulk-insert-buffer-size=# - Size of tree cache used in bulk insert optimisation. Note - that this is a limit per thread! - --character-set-client-handshake - Don't ignore client side character set value sent during - handshake. - (Defaults to on; use --skip-character-set-client-handshake to disable.) - --character-set-filesystem=name - Set the filesystem character set. - -C, --character-set-server=name - Set the default character set. - --character-sets-dir=name - Directory where character sets are - -r, --chroot=name Chroot mysqld daemon during startup. - --collation-server=name - Set the default collation. - --completion-type=name - The transaction completion type, one of NO_CHAIN, CHAIN, - RELEASE - --concurrent-insert[=name] - Use concurrent insert with MyISAM. Possible values are - NEVER, AUTO, ALWAYS - --connect-timeout=# The number of seconds the mysqld server is waiting for a - connect packet before responding with 'Bad handshake' - --console Write error output on screen; Don't remove the console - window on windows. - --core-file Write core on errors. - -h, --datadir=name Path to the database root directory - --date-format=name The DATE format (ignored) - --datetime-format=name - The DATETIME format (ignored) - -C, --default-character-set=name - Set the default character set (deprecated option, use - --character-set-server instead). - --default-collation=name - Set the default collation (deprecated option, use - --collation-server instead). - --default-storage-engine=name - The default storage engine for new tables - --default-time-zone=name - Set the default time zone. - --default-week-format=# - The default week format used by WEEK() functions - --delay-key-write[=name] - Type of DELAY_KEY_WRITE - --delay-key-write-for-all-tables - Don't flush key buffers between writes for any MyISAM - table (Deprecated option, use --delay-key-write=all - instead). - --delayed-insert-limit=# - After inserting delayed_insert_limit rows, the INSERT - DELAYED handler will check if there are any SELECT - statements pending. If so, it allows these to execute - before continuing - --delayed-insert-timeout=# - How long a INSERT DELAYED thread should wait for INSERT - statements before terminating - --delayed-queue-size=# - What size queue (in rows) should be allocated for - handling INSERT DELAYED. If the queue becomes full, any - client that does INSERT DELAYED will wait until there is - room in the queue again - --disconnect-slave-event-count=# - Option used by mysql-test for debugging and testing of - replication. - --div-precision-increment=# - Precision of the result of '/' operator will be increased - on that value - --enable-locking Deprecated option, use --external-locking instead. - --engine-condition-pushdown - Push supported query conditions to the storage engine - (Defaults to on; use --skip-engine-condition-pushdown to disable.) - --event-scheduler[=name] - Enable the event scheduler. Possible values are ON, OFF, - and DISABLED (keep the event scheduler completely - deactivated, it cannot be activated run-time) - -T, --exit-info[=#] Used for debugging; Use at your own risk! - --expire-logs-days=# - If non-zero, binary logs will be purged after - expire_logs_days days; possible purges happen at startup - and at binary log rotation - --external-locking Use system (external) locking (disabled by default). - With this option enabled you can run myisamchk to test - (not repair) tables while the MySQL server is running. - Disable with --skip-external-locking. - --flush Flush MyISAM tables to disk between SQL commands - --flush-time=# A dedicated thread is created to flush all tables at the - given interval - --ft-boolean-syntax=name - List of operators for MATCH ... AGAINST ( ... IN BOOLEAN - MODE) - --ft-max-word-len=# The maximum length of the word to be included in a - FULLTEXT index. Note: FULLTEXT indexes must be rebuilt - after changing this variable - --ft-min-word-len=# The minimum length of the word to be included in a - FULLTEXT index. Note: FULLTEXT indexes must be rebuilt - after changing this variable - --ft-query-expansion-limit=# - Number of best matches to use for query expansion - --ft-stopword-file=name - Use stopwords from this file instead of built-in list - --gdb Set up signals usable for debugging - --general-log Log connections and queries to a table or log file. - Defaults logging to a file hostname.log or a table - mysql.general_logif --log-output=TABLE is used - --general-log-file=name - Log connections and queries to given file - --group-concat-max-len=# - The maximum length of the result of function - GROUP_CONCAT() - -?, --help Display this help and exit. - --ignore-builtin-innodb - Disable initialization of builtin InnoDB plugin - --init-connect=name Command(s) that are executed for each new connection - --init-file=name Read SQL commands from this file at startup - --init-rpl-role=name - Set the replication role. - --init-slave=name Command(s) that are executed by a slave server each time - the SQL thread starts - --interactive-timeout=# - The number of seconds the server waits for activity on an - interactive connection before closing it - --join-buffer-size=# - The size of the buffer that is used for full joins - --keep-files-on-create - Don't overwrite stale .MYD and .MYI even if no directory - is specified - --key-buffer-size=# The size of the buffer used for index blocks for MyISAM - tables. Increase this to get better index handling (for - all reads and multiple writes) to as much as you can - afford - --key-cache-age-threshold=# - This characterizes the number of hits a hot block has to - be untouched until it is considered aged enough to be - downgraded to a warm block. This specifies the percentage - ratio of that number of hits to the total number of - blocks in key cache - --key-cache-block-size=# - The default size of key cache blocks - --key-cache-division-limit=# - The minimum percentage of warm blocks in key cache - -L, --language=name Client error messages in given language. May be given as - a full path. Deprecated. Use --lc-messages-dir instead. - --lc-messages=name Set the language used for the error messages. - -L, --lc-messages-dir=name - Directory where error messages are - --lc-time-names=name - Set the language used for the month names and the days of - the week. - --local-infile Enable LOAD DATA LOCAL INFILE - (Defaults to on; use --skip-local-infile to disable.) - -l, --log[=name] Log connections and queries to file (deprecated option, - use --general-log/--general-log-file instead). - --log-bin[=name] Log update queries in binary format. Optional (but - strongly recommended to avoid replication problems if - server's hostname changes) argument should be the chosen - location for the binary log files. - --log-bin-index=name - File that holds the names for last binary log files. - --log-bin-trust-function-creators - If set to FALSE (the default), then when --log-bin is - used, creation of a stored function (or trigger) is - allowed only to users having the SUPER privilege and only - if this stored function (trigger) may not break binary - logging. Note that if ALL connections to this server - ALWAYS use row-based binary logging, the security issues - do not exist and the binary logging cannot break, so you - can safely set this to TRUE - --log-error[=name] Error log file - --log-isam[=name] Log all MyISAM changes to file. - -0, --log-long-format - Log some extra information to update log. Please note - that this option is deprecated; see --log-short-format - option. - --log-output=name Syntax: log-output=value[,value...], where "value" could - be TABLE, FILE or NONE - --log-queries-not-using-indexes - Log queries that are executed without benefit of any - index to the slow log if it is open - --log-short-format Don't log extra information to update and slow-query - logs. - --log-slave-updates Tells the slave to log the updates from the slave thread - to the binary log. You will need to turn it on if you - plan to daisy-chain the slaves - --log-slow-admin-statements - Log slow OPTIMIZE, ANALYZE, ALTER and other - administrative statements to the slow log if it is open. - --log-slow-queries[=name] - Log slow queries to a table or log file. Defaults logging - to table mysql.slow_log or hostname-slow.log if - --log-output=file is used. Must be enabled to activate - other slow log options. Deprecated option, use - --slow-query-log/--slow-query-log-file instead. - --log-slow-slave-statements - Log slow statements executed by slave thread to the slow - log if it is open. - --log-tc=name Path to transaction coordinator log (used for - transactions that affect more than one storage engine, - when binary log is disabled) - --log-tc-size=# Size of transaction coordinator log. - --log-update[=name] The update log is deprecated since version 5.0, is - replaced by the binary log and this option justs turns on - --log-bin instead. - -W, --log-warnings[=#] - Log some not critical warnings to the log file - --long-query-time=# Log all queries that have taken more than long_query_time - seconds to execute to file. The argument will be treated - as a decimal value with microsecond precision - --low-priority-updates - INSERT/DELETE/UPDATE has lower priority than selects - --lower-case-table-names[=#] - If set to 1 table names are stored in lowercase on disk - and table names will be case-insensitive. Should be set - to 2 if you are using a case insensitive file system - --master-info-file=name - The location and name of the file that remembers the - master and where the I/O replication thread is in the - master's binlogs. - --master-retry-count=# - The number of tries the slave will make to connect to the - master before giving up. - --max-allowed-packet=# - Max packet length to send to or receive from the server - --max-binlog-cache-size=# - Can be used to restrict the total size used to cache a - multi-transaction query - --max-binlog-dump-events=# - Option used by mysql-test for debugging and testing of - replication. - --max-binlog-size=# Binary log will be rotated automatically when the size - exceeds this value. Will also apply to relay logs if - max_relay_log_size is 0 - --max-connect-errors=# - If there is more than this number of interrupted - connections from a host this host will be blocked from - further connections - --max-connections=# The number of simultaneous clients allowed - --max-delayed-threads=# - Don't start more than this number of threads to handle - INSERT DELAYED statements. If set to zero INSERT DELAYED - will be not used - --max-error-count=# Max number of errors/warnings to store for a statement - --max-heap-table-size=# - Don't allow creation of heap tables bigger than this - --max-join-size=# Joins that are probably going to read more than - max_join_size records return an error - --max-length-for-sort-data=# - Max number of bytes in sorted records - --max-prepared-stmt-count=# - Maximum number of prepared statements in the server - --max-relay-log-size=# - If non-zero: relay log will be rotated automatically when - the size exceeds this value; if zero: when the size - exceeds max_binlog_size - --max-seeks-for-key=# - Limit assumed max number of seeks when looking up rows - based on a key - --max-sort-length=# The number of bytes to use when sorting BLOB or TEXT - values (only the first max_sort_length bytes of each - value are used; the rest are ignored) - --max-sp-recursion-depth[=#] - Maximum stored procedure recursion depth - --max-tmp-tables=# Maximum number of temporary tables a client can keep open - at a time - --max-user-connections=# - The maximum number of active connections for a single - user (0 = no limit) - --max-write-lock-count=# - After this many write locks, allow some read locks to run - in between - --memlock Lock mysqld in memory. - --min-examined-row-limit=# - Don't write queries to slow log that examine fewer rows - than that - --multi-range-count=# - Number of key ranges to request at once - --myisam-block-size=# - Block size to be used for MyISAM index pages - --myisam-data-pointer-size=# - Default pointer size to be used for MyISAM tables - --myisam-max-sort-file-size=# - Don't use the fast sort index method to created index if - the temporary file would get bigger than this - --myisam-mmap-size=# - Restricts the total memory used for memory mapping of - MySQL tables - --myisam-recover-options[=name] - Syntax: myisam-recover-options[=option[,option...]], - where option can be DEFAULT, BACKUP, FORCE, QUICK, or OFF - --myisam-repair-threads=# - If larger than 1, when repairing a MyISAM table all - indexes will be created in parallel, with one thread per - index. The value of 1 disables parallel repair - --myisam-sort-buffer-size=# - The buffer that is allocated when sorting the index when - doing a REPAIR or when creating indexes with CREATE INDEX - or ALTER TABLE - --myisam-stats-method=name - Specifies how MyISAM index statistics collection code - should treat NULLs. Possible values of name are - NULLS_UNEQUAL (default behavior for 4.1 and later), - NULLS_EQUAL (emulate 4.0 behavior), and NULLS_IGNORED - --myisam-use-mmap Use memory mapping for reading and writing MyISAM tables - --named-pipe Enable the named pipe (NT) - --net-buffer-length=# - Buffer length for TCP/IP and socket communication - --net-read-timeout=# - Number of seconds to wait for more data from a connection - before aborting the read - --net-retry-count=# If a read on a communication port is interrupted, retry - this many times before giving up - --net-write-timeout=# - Number of seconds to wait for a block to be written to a - connection before aborting the write - -n, --new Use very new possible "unsafe" functions - --old Use compatible behavior - --old-alter-table Use old, non-optimized alter table - --old-passwords Use old password encryption method (needed for 4.0 and - older clients) - --old-style-user-limits - Enable old-style user limits (before 5.0.3 user resources - were counted per each user+host vs. per account) - --one-thread (deprecated): Only use one thread (for debugging under - Linux). Use thread-handling=no-threads instead - --open-files-limit=# - If this is not 0, then mysqld will use this value to - reserve file descriptors to use with setrlimit(). If this - value is 0 then mysqld will reserve max_connections*5 or - max_connections + table_cache*2 (whichever is larger) - number of file descriptors - --optimizer-prune-level=# - Controls the heuristic(s) applied during query - optimization to prune less-promising partial plans from - the optimizer search space. Meaning: 0 - do not apply any - heuristic, thus perform exhaustive search; 1 - prune - plans based on number of retrieved rows - --optimizer-search-depth=# - Maximum depth of search performed by the query optimizer. - Values larger than the number of relations in a query - result in better query plans, but take longer to compile - a query. Values smaller than the number of tables in a - relation result in faster optimization, but may produce - very bad query plans. If set to 0, the system will - automatically pick a reasonable value; if set to 63, the - optimizer will switch to the original find_best - search(used for testing/comparison) - --optimizer-switch=name - optimizer_switch=option=val[,option=val...], where option - is one of {index_merge, index_merge_union, - index_merge_sort_union, index_merge_intersection} and val - is one of {on, off, default} - --pid-file=name Pid file used by safe_mysqld - --plugin-dir=name Directory for plugins - --plugin-load=name Optional semicolon-separated list of plugins to load, - where each plugin is identified as name=library, where - name is the plugin name and library is the plugin library - in plugin_dir. - -P, --port=# Port number to use for connection or 0 to default to, - my.cnf, $MYSQL_TCP_PORT, /etc/services, built-in default - (3306), whatever comes first - --port-open-timeout=# - Maximum time in seconds to wait for the port to become - free. (Default: no wait) - --preload-buffer-size=# - The size of the buffer that is allocated when preloading - indexes - --profiling-history-size=# - Limit of query profiling memory - --query-alloc-block-size=# - Allocation block size for query parsing and execution - --query-cache-limit=# - Don't cache results that are bigger than this - --query-cache-min-res-unit=# - The minimum size for blocks allocated by the query cache - --query-cache-size=# - The memory allocated to store results from old queries - --query-cache-type=name - OFF = Don't cache or retrieve results. ON = Cache all - results except SELECT SQL_NO_CACHE ... queries. DEMAND = - Cache only SELECT SQL_CACHE ... queries - --query-cache-wlock-invalidate - Invalidate queries in query cache on LOCK for write - --query-prealloc-size=# - Persistent buffer for query parsing and execution - --range-alloc-block-size=# - Allocation block size for storing ranges during - optimization - --read-buffer-size=# - Each thread that does a sequential scan allocates a - buffer of this size for each table it scans. If you do - many sequential scans, you may want to increase this - value - --read-only Make all non-temporary tables read-only, with the - exception for replication (slave) threads and users with - the SUPER privilege - --read-rnd-buffer-size=# - When reading rows in sorted order after a sort, the rows - are read through this buffer to avoid a disk seeks. If - not set, then it's set to the value of record_buffer - --record-buffer=# Deprecated; use --read-buffer-size instead. - --relay-log=name The location and name to use for relay logs - --relay-log-index=name - The location and name to use for the file that keeps a - list of the last relay logs - --relay-log-info-file=name - The location and name of the file that remembers where - the SQL replication thread is in the relay logs - --relay-log-purge if disabled - do not purge relay logs. if enabled - purge - them as soon as they are no more needed - (Defaults to on; use --skip-relay-log-purge to disable.) - --relay-log-recovery - Enables automatic relay log recovery right after the - database startup, which means that the IO Thread starts - re-fetching from the master right after the last - transaction processed - --relay-log-space-limit=# - Maximum space to use for all relay logs - --replicate-do-db=name - Tells the slave thread to restrict replication to the - specified database. To specify more than one database, - use the directive multiple times, once for each database. - Note that this will only work if you do not use - cross-database queries such as UPDATE some_db.some_table - SET foo='bar' while having selected a different or no - database. If you need cross database updates to work, - make sure you have 3.23.28 or later, and use - replicate-wild-do-table=db_name.%. - --replicate-do-table=name - Tells the slave thread to restrict replication to the - specified table. To specify more than one table, use the - directive multiple times, once for each table. This will - work for cross-database updates, in contrast to - replicate-do-db. - --replicate-ignore-db=name - Tells the slave thread to not replicate to the specified - database. To specify more than one database to ignore, - use the directive multiple times, once for each database. - This option will not work if you use cross database - updates. If you need cross database updates to work, make - sure you have 3.23.28 or later, and use - replicate-wild-ignore-table=db_name.%. - --replicate-ignore-table=name - Tells the slave thread to not replicate to the specified - table. To specify more than one table to ignore, use the - directive multiple times, once for each table. This will - work for cross-datbase updates, in contrast to - replicate-ignore-db. - --replicate-rewrite-db=name - Updates to a database with a different name than the - original. Example: - replicate-rewrite-db=master_db_name->slave_db_name. - --replicate-same-server-id - In replication, if set to 1, do not skip events having - our server id. Default value is 0 (to break infinite - loops in circular replication). Can't be set to 1 if - --log-slave-updates is used. - --replicate-wild-do-table=name - Tells the slave thread to restrict replication to the - tables that match the specified wildcard pattern. To - specify more than one table, use the directive multiple - times, once for each table. This will work for - cross-database updates. Example: - replicate-wild-do-table=foo%.bar% will replicate only - updates to tables in all databases that start with foo - and whose table names start with bar. - --replicate-wild-ignore-table=name - Tells the slave thread to not replicate to the tables - that match the given wildcard pattern. To specify more - than one table to ignore, use the directive multiple - times, once for each table. This will work for - cross-database updates. Example: - replicate-wild-ignore-table=foo%.bar% will not do updates - to tables in databases that start with foo and whose - table names start with bar. - --report-host=name Hostname or IP of the slave to be reported to the master - during slave registration. Will appear in the output of - SHOW SLAVE HOSTS. Leave unset if you do not want the - slave to register itself with the master. Note that it is - not sufficient for the master to simply read the IP of - the slave off the socket once the slave connects. Due to - NAT and other routing issues, that IP may not be valid - for connecting to the slave from the master or other - hosts - --report-password=name - The account password of the slave to be reported to the - master during slave registration - --report-port=# Port for connecting to slave reported to the master - during slave registration. Set it only if the slave is - listening on a non-default port or if you have a special - tunnel from the master or other clients to the slave. If - not sure, leave this option unset - --report-user=name The account user name of the slave to be reported to the - master during slave registration - --rpl-recovery-rank=# - Unused, will be removed - --safe-mode Skip some optimize stages (for testing). - --safe-show-database - Deprecated option; use GRANT SHOW DATABASES instead... - --safe-user-create Don't allow new user creation by the user who has no - write privileges to the mysql.user table. - --secure-auth Disallow authentication for accounts that have old - (pre-4.1) passwords - --secure-file-priv=name - Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to - files within specified directory - --server-id=# Uniquely identifies the server instance in the community - of replication partners - -O, --set-variable=name - Change the value of a variable. Please note that this - option is deprecated;you can set variables directly with - --variable-name=value. - --shared-memory Enable the shared memory - --shared-memory-base-name=name - Base name of shared memory - --show-slave-auth-info - Show user and password in SHOW SLAVE HOSTS on this master - --skip-grant-tables Start without grant tables. This gives all users FULL - ACCESS to all tables! - --skip-host-cache Don't cache host names. - --skip-locking Deprecated option, use --skip-external-locking instead. - --skip-name-resolve Don't resolve hostnames. All hostnames are IP's or - 'localhost'. - --skip-networking Don't allow connection with TCP/IP - --skip-new Don't use new, possible wrong routines. - --skip-show-database - Don't allow 'SHOW DATABASE' commands - --skip-slave-start If set, slave is not autostarted. - --skip-stack-trace Don't print a stack trace on failure. - --skip-symlink Don't allow symlinking of tables. Deprecated option. Use - --skip-symbolic-links instead. - --skip-thread-priority - Don't give threads different priorities. This option is - deprecated because it has no effect; the implied behavior - is already the default. - --slave-compressed-protocol - Use compression on master/slave protocol - --slave-exec-mode=name - Modes for how replication events should be executed. - Legal values are STRICT (default) and IDEMPOTENT. In - IDEMPOTENT mode, replication will not stop for operations - that are idempotent. In STRICT mode, replication will - stop on any unexpected difference between the master and - the slave - --slave-load-tmpdir=name - The location where the slave should put its temporary - files when replicating a LOAD DATA INFILE command - --slave-net-timeout=# - Number of seconds to wait for more data from a - master/slave connection before aborting the read - --slave-skip-errors=name - Tells the slave thread to continue replication when a - query event returns an error from the provided list - --slave-transaction-retries=# - Number of times the slave SQL thread will retry a - transaction in case it failed with a deadlock or elapsed - lock wait timeout, before giving up and stopping - --slow-launch-time=# - If creating the thread takes longer than this value (in - seconds), the Slow_launch_threads counter will be - incremented - --slow-query-log Log slow queries to a table or log file. Defaults logging - to a file hostname-slow.log or a table mysql.slow_log if - --log-output=TABLE is used. Must be enabled to activate - other slow log options - --slow-query-log-file=name - Log slow queries to given log file. Defaults logging to - hostname-slow.log. Must be enabled to activate other slow - log options - --socket=name Socket file to use for connection - --sort-buffer-size=# - Each thread that needs to do a sort allocates a buffer of - this size - --sporadic-binlog-dump-fail - Option used by mysql-test for debugging and testing of - replication. - --sql-bin-update-same - The update log is deprecated since version 5.0, is - replaced by the binary log and this option does nothing - anymore. - --sql-mode=name Syntax: sql-mode=mode[,mode[,mode...]]. See the manual - for the complete list of valid sql modes - --standalone Dummy option to start as a standalone program (NT). - -s, --symbolic-links - Enable symbolic link support. - --sync-binlog=# Synchronously flush binary log to disk after every #th - event. Use 0 (default) to disable synchronous flushing - --sync-frm Sync .frm files to disk on creation - (Defaults to on; use --skip-sync-frm to disable.) - --sync-master-info=# - Synchronously flush master info to disk after every #th - event. Use 0 (default) to disable synchronous flushing - --sync-relay-log=# Synchronously flush relay log to disk after every #th - event. Use 0 (default) to disable synchronous flushing - --sync-relay-log-info=# - Synchronously flush relay log info to disk after every - #th transaction. Use 0 (default) to disable synchronous - flushing - --sysdate-is-now Non-default option to alias SYSDATE() to NOW() to make it - safe-replicable. Since 5.0, SYSDATE() returns a `dynamic' - value different for different invocations, even within - the same statement. - --table-cache=# Deprecated; use --table-open-cache instead. - --table-definition-cache=# - The number of cached table definitions - --table-lock-wait-timeout=# - Timeout in seconds to wait for a table level lock before - returning an error. Used only if the connection has - active cursors - --table-open-cache=# - The number of cached open tables - --tc-heuristic-recover=name - Decision to use in heuristic recover process. Possible - values are COMMIT or ROLLBACK. - --thread-cache-size=# - How many threads we should keep in a cache for reuse - --thread-handling=name - Define threads usage for handling queries, one of - one-thread-per-connection, no-threads - --thread-stack=# The stack size for each thread - --time-format=name The TIME format (ignored) - --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are - currently supported) - --tmp-table-size=# If an internal in-memory temporary table exceeds this - size, MySQL will automatically convert it to an on-disk - MyISAM table - -t, --tmpdir=name Path for temporary files. Several paths may be specified, - separated by a semicolon (;), in this case they are used - in a round-robin fashion - --transaction-alloc-block-size=# - Allocation block size for transactions to be stored in - binary log - --transaction-isolation=name - Default transaction isolation level. - --transaction-prealloc-size=# - Persistent buffer for transactions to be stored in binary - log - --updatable-views-with-limit=name - YES = Don't issue an error message (warning only) if a - VIEW without presence of a key of the underlying table is - used in queries with a LIMIT clause for updating. NO = - Prohibit update of a VIEW, which does not contain a key - of the underlying table and the query uses a LIMIT clause - (usually get from GUI tools) - -s, --use-symbolic-links - Enable symbolic link support. Deprecated option; use - --symbolic-links instead. - -u, --user=name Run mysqld daemon as user. - -v, --verbose Used with --help option for detailed help - -V, --version Output version information and exit. - --wait-timeout=# The number of seconds the server waits for activity on a - connection before closing it - -W, --warnings[=#] Deprecated; use --log-warnings instead. + --abort-slave-event-count=# + Option used by mysql-test for debugging and testing of + replication. + --allow-suspicious-udfs + Allows use of UDFs consisting of only one symbol xxx() + without corresponding xxx_init() or xxx_deinit(). That + also means that one can load any function from any + library, for example exit() from libc.so + -a, --ansi Use ANSI SQL syntax instead of MySQL syntax. This mode + will also set transaction isolation level 'serializable'. + --auto-increment-increment[=#] + Auto-increment columns are incremented by this + --auto-increment-offset[=#] + Offset added to Auto-increment columns. Used when + auto-increment-increment != 1 + --automatic-sp-privileges + Creating and dropping stored procedures alters ACLs + (Defaults to on; use --skip-automatic-sp-privileges to disable.) + --back-log=# The number of outstanding connection requests MySQL can + have. This comes into play when the main MySQL thread + gets very many connection requests in a very short time + -b, --basedir=name Path to installation directory. All paths are usually + resolved relative to this + --big-tables Allow big result sets by saving all temporary sets on + file (Solves most 'table full' errors) + --bind-address=name IP address to bind to. + --binlog-cache-size=# + The size of the cache to hold the SQL statements for the + binary log during a transaction. If you often use big, + multi-statement transactions you can increase this to get + more performance + --binlog-do-db=name Tells the master it should log updates for the specified + database, and exclude all others not explicitly + mentioned. + --binlog-format=name + What form of binary logging the master will use: either + ROW for row-based binary logging, STATEMENT for + statement-based binary logging, or MIXED. MIXED is + statement-based binary logging except for those + statements where only row-based is correct: those which + involve user-defined functions (i.e. UDFs) or the UUID() + function; for those, row-based binary logging is + automatically used. If NDBCLUSTER is enabled and + binlog-format is MIXED, the format switches to row-based + and back implicitly per each query accessing an + NDBCLUSTER table + --binlog-ignore-db=name + Tells the master that updates to the given database + should not be logged tothe binary log. + --binlog-row-event-max-size=# + The maximum size of a row-based binary log event in + bytes. Rows will be grouped into events smaller than this + size if possible. The value has to be a multiple of 256. + --bootstrap Used by mysql installation scripts. + --bulk-insert-buffer-size=# + Size of tree cache used in bulk insert optimisation. Note + that this is a limit per thread! + --character-set-client-handshake + Don't ignore client side character set value sent during + handshake. + (Defaults to on; use --skip-character-set-client-handshake to disable.) + --character-set-filesystem=name + Set the filesystem character set. + -C, --character-set-server=name + Set the default character set. + --character-sets-dir=name + Directory where character sets are + -r, --chroot=name Chroot mysqld daemon during startup. + --collation-server=name + Set the default collation. + --completion-type=name + The transaction completion type, one of NO_CHAIN, CHAIN, + RELEASE + --concurrent-insert[=name] + Use concurrent insert with MyISAM. Possible values are + NEVER, AUTO, ALWAYS + --connect-timeout=# The number of seconds the mysqld server is waiting for a + connect packet before responding with 'Bad handshake' + --console Write error output on screen; Don't remove the console + window on windows. + --core-file Write core on errors. + -h, --datadir=name Path to the database root directory + --date-format=name The DATE format (ignored) + --datetime-format=name + The DATETIME format (ignored) + -C, --default-character-set=name + Set the default character set (deprecated option, use + --character-set-server instead). + --default-collation=name + Set the default collation (deprecated option, use + --collation-server instead). + --default-storage-engine=name + The default storage engine for new tables + --default-time-zone=name + Set the default time zone. + --default-week-format=# + The default week format used by WEEK() functions + --delay-key-write[=name] + Type of DELAY_KEY_WRITE + --delay-key-write-for-all-tables + Don't flush key buffers between writes for any MyISAM + table (Deprecated option, use --delay-key-write=all + instead). + --delayed-insert-limit=# + After inserting delayed_insert_limit rows, the INSERT + DELAYED handler will check if there are any SELECT + statements pending. If so, it allows these to execute + before continuing + --delayed-insert-timeout=# + How long a INSERT DELAYED thread should wait for INSERT + statements before terminating + --delayed-queue-size=# + What size queue (in rows) should be allocated for + handling INSERT DELAYED. If the queue becomes full, any + client that does INSERT DELAYED will wait until there is + room in the queue again + --disconnect-slave-event-count=# + Option used by mysql-test for debugging and testing of + replication. + --div-precision-increment=# + Precision of the result of '/' operator will be increased + on that value + --enable-locking Deprecated option, use --external-locking instead. + --engine-condition-pushdown + Push supported query conditions to the storage engine + (Defaults to on; use --skip-engine-condition-pushdown to disable.) + --event-scheduler[=name] + Enable the event scheduler. Possible values are ON, OFF, + and DISABLED (keep the event scheduler completely + deactivated, it cannot be activated run-time) + -T, --exit-info[=#] Used for debugging; Use at your own risk! + --expire-logs-days=# + If non-zero, binary logs will be purged after + expire_logs_days days; possible purges happen at startup + and at binary log rotation + --external-locking Use system (external) locking (disabled by default). + With this option enabled you can run myisamchk to test + (not repair) tables while the MySQL server is running. + Disable with --skip-external-locking. + --flush Flush MyISAM tables to disk between SQL commands + --flush-time=# A dedicated thread is created to flush all tables at the + given interval + --ft-boolean-syntax=name + List of operators for MATCH ... AGAINST ( ... IN BOOLEAN + MODE) + --ft-max-word-len=# The maximum length of the word to be included in a + FULLTEXT index. Note: FULLTEXT indexes must be rebuilt + after changing this variable + --ft-min-word-len=# The minimum length of the word to be included in a + FULLTEXT index. Note: FULLTEXT indexes must be rebuilt + after changing this variable + --ft-query-expansion-limit=# + Number of best matches to use for query expansion + --ft-stopword-file=name + Use stopwords from this file instead of built-in list + --gdb Set up signals usable for debugging + --general-log Log connections and queries to a table or log file. + Defaults logging to a file hostname.log or a table + mysql.general_logif --log-output=TABLE is used + --general-log-file=name + Log connections and queries to given file + --group-concat-max-len=# + The maximum length of the result of function + GROUP_CONCAT() + -?, --help Display this help and exit. + --ignore-builtin-innodb + Disable initialization of builtin InnoDB plugin + --init-connect=name Command(s) that are executed for each new connection + --init-file=name Read SQL commands from this file at startup + --init-rpl-role=name + Set the replication role. + --init-slave=name Command(s) that are executed by a slave server each time + the SQL thread starts + --interactive-timeout=# + The number of seconds the server waits for activity on an + interactive connection before closing it + --join-buffer-size=# + The size of the buffer that is used for full joins + --keep-files-on-create + Don't overwrite stale .MYD and .MYI even if no directory + is specified + --key-buffer-size=# The size of the buffer used for index blocks for MyISAM + tables. Increase this to get better index handling (for + all reads and multiple writes) to as much as you can + afford + --key-cache-age-threshold=# + This characterizes the number of hits a hot block has to + be untouched until it is considered aged enough to be + downgraded to a warm block. This specifies the percentage + ratio of that number of hits to the total number of + blocks in key cache + --key-cache-block-size=# + The default size of key cache blocks + --key-cache-division-limit=# + The minimum percentage of warm blocks in key cache + -L, --language=name Client error messages in given language. May be given as + a full path. Deprecated. Use --lc-messages-dir instead. + --lc-messages=name Set the language used for the error messages. + -L, --lc-messages-dir=name + Directory where error messages are + --lc-time-names=name + Set the language used for the month names and the days of + the week. + --local-infile Enable LOAD DATA LOCAL INFILE + (Defaults to on; use --skip-local-infile to disable.) + -l, --log[=name] Log connections and queries to file (deprecated option, + use --general-log/--general-log-file instead). + --log-bin[=name] Log update queries in binary format. Optional (but + strongly recommended to avoid replication problems if + server's hostname changes) argument should be the chosen + location for the binary log files. + --log-bin-index=name + File that holds the names for last binary log files. + --log-bin-trust-function-creators + If set to FALSE (the default), then when --log-bin is + used, creation of a stored function (or trigger) is + allowed only to users having the SUPER privilege and only + if this stored function (trigger) may not break binary + logging. Note that if ALL connections to this server + ALWAYS use row-based binary logging, the security issues + do not exist and the binary logging cannot break, so you + can safely set this to TRUE + --log-error[=name] Error log file + --log-isam[=name] Log all MyISAM changes to file. + -0, --log-long-format + Log some extra information to update log. Please note + that this option is deprecated; see --log-short-format + option. + --log-output=name Syntax: log-output=value[,value...], where "value" could + be TABLE, FILE or NONE + --log-queries-not-using-indexes + Log queries that are executed without benefit of any + index to the slow log if it is open + --log-short-format Don't log extra information to update and slow-query + logs. + --log-slave-updates Tells the slave to log the updates from the slave thread + to the binary log. You will need to turn it on if you + plan to daisy-chain the slaves + --log-slow-admin-statements + Log slow OPTIMIZE, ANALYZE, ALTER and other + administrative statements to the slow log if it is open. + --log-slow-queries[=name] + Log slow queries to a table or log file. Defaults logging + to table mysql.slow_log or hostname-slow.log if + --log-output=file is used. Must be enabled to activate + other slow log options. Deprecated option, use + --slow-query-log/--slow-query-log-file instead. + --log-slow-slave-statements + Log slow statements executed by slave thread to the slow + log if it is open. + --log-tc=name Path to transaction coordinator log (used for + transactions that affect more than one storage engine, + when binary log is disabled) + --log-tc-size=# Size of transaction coordinator log. + --log-update[=name] The update log is deprecated since version 5.0, is + replaced by the binary log and this option justs turns on + --log-bin instead. + -W, --log-warnings[=#] + Log some not critical warnings to the log file + --long-query-time=# Log all queries that have taken more than long_query_time + seconds to execute to file. The argument will be treated + as a decimal value with microsecond precision + --low-priority-updates + INSERT/DELETE/UPDATE has lower priority than selects + --lower-case-table-names[=#] + If set to 1 table names are stored in lowercase on disk + and table names will be case-insensitive. Should be set + to 2 if you are using a case insensitive file system + --master-info-file=name + The location and name of the file that remembers the + master and where the I/O replication thread is in the + master's binlogs. + --master-retry-count=# + The number of tries the slave will make to connect to the + master before giving up. + --max-allowed-packet=# + Max packet length to send to or receive from the server + --max-binlog-cache-size=# + Can be used to restrict the total size used to cache a + multi-transaction query + --max-binlog-dump-events=# + Option used by mysql-test for debugging and testing of + replication. + --max-binlog-size=# Binary log will be rotated automatically when the size + exceeds this value. Will also apply to relay logs if + max_relay_log_size is 0 + --max-connect-errors=# + If there is more than this number of interrupted + connections from a host this host will be blocked from + further connections + --max-connections=# The number of simultaneous clients allowed + --max-delayed-threads=# + Don't start more than this number of threads to handle + INSERT DELAYED statements. If set to zero INSERT DELAYED + will be not used + --max-error-count=# Max number of errors/warnings to store for a statement + --max-heap-table-size=# + Don't allow creation of heap tables bigger than this + --max-join-size=# Joins that are probably going to read more than + max_join_size records return an error + --max-length-for-sort-data=# + Max number of bytes in sorted records + --max-prepared-stmt-count=# + Maximum number of prepared statements in the server + --max-relay-log-size=# + If non-zero: relay log will be rotated automatically when + the size exceeds this value; if zero: when the size + exceeds max_binlog_size + --max-seeks-for-key=# + Limit assumed max number of seeks when looking up rows + based on a key + --max-sort-length=# The number of bytes to use when sorting BLOB or TEXT + values (only the first max_sort_length bytes of each + value are used; the rest are ignored) + --max-sp-recursion-depth[=#] + Maximum stored procedure recursion depth + --max-tmp-tables=# Maximum number of temporary tables a client can keep open + at a time + --max-user-connections=# + The maximum number of active connections for a single + user (0 = no limit) + --max-write-lock-count=# + After this many write locks, allow some read locks to run + in between + --memlock Lock mysqld in memory. + --min-examined-row-limit=# + Don't write queries to slow log that examine fewer rows + than that + --multi-range-count=# + Number of key ranges to request at once + --myisam-block-size=# + Block size to be used for MyISAM index pages + --myisam-data-pointer-size=# + Default pointer size to be used for MyISAM tables + --myisam-max-sort-file-size=# + Don't use the fast sort index method to created index if + the temporary file would get bigger than this + --myisam-mmap-size=# + Restricts the total memory used for memory mapping of + MySQL tables + --myisam-recover-options[=name] + Syntax: myisam-recover-options[=option[,option...]], + where option can be DEFAULT, BACKUP, FORCE, QUICK, or OFF + --myisam-repair-threads=# + If larger than 1, when repairing a MyISAM table all + indexes will be created in parallel, with one thread per + index. The value of 1 disables parallel repair + --myisam-sort-buffer-size=# + The buffer that is allocated when sorting the index when + doing a REPAIR or when creating indexes with CREATE INDEX + or ALTER TABLE + --myisam-stats-method=name + Specifies how MyISAM index statistics collection code + should treat NULLs. Possible values of name are + NULLS_UNEQUAL (default behavior for 4.1 and later), + NULLS_EQUAL (emulate 4.0 behavior), and NULLS_IGNORED + --myisam-use-mmap Use memory mapping for reading and writing MyISAM tables + --named-pipe Enable the named pipe (NT) + --net-buffer-length=# + Buffer length for TCP/IP and socket communication + --net-read-timeout=# + Number of seconds to wait for more data from a connection + before aborting the read + --net-retry-count=# If a read on a communication port is interrupted, retry + this many times before giving up + --net-write-timeout=# + Number of seconds to wait for a block to be written to a + connection before aborting the write + -n, --new Use very new possible "unsafe" functions + --old Use compatible behavior + --old-alter-table Use old, non-optimized alter table + --old-passwords Use old password encryption method (needed for 4.0 and + older clients) + --old-style-user-limits + Enable old-style user limits (before 5.0.3 user resources + were counted per each user+host vs. per account) + --one-thread (deprecated): Only use one thread (for debugging under + Linux). Use thread-handling=no-threads instead + --open-files-limit=# + If this is not 0, then mysqld will use this value to + reserve file descriptors to use with setrlimit(). If this + value is 0 then mysqld will reserve max_connections*5 or + max_connections + table_cache*2 (whichever is larger) + number of file descriptors + --optimizer-prune-level=# + Controls the heuristic(s) applied during query + optimization to prune less-promising partial plans from + the optimizer search space. Meaning: 0 - do not apply any + heuristic, thus perform exhaustive search; 1 - prune + plans based on number of retrieved rows + --optimizer-search-depth=# + Maximum depth of search performed by the query optimizer. + Values larger than the number of relations in a query + result in better query plans, but take longer to compile + a query. Values smaller than the number of tables in a + relation result in faster optimization, but may produce + very bad query plans. If set to 0, the system will + automatically pick a reasonable value; if set to 63, the + optimizer will switch to the original find_best + search(used for testing/comparison) + --optimizer-switch=name + optimizer_switch=option=val[,option=val...], where option + is one of {index_merge, index_merge_union, + index_merge_sort_union, index_merge_intersection} and val + is one of {on, off, default} + --pid-file=name Pid file used by safe_mysqld + --plugin-dir=name Directory for plugins + --plugin-load=name Optional semicolon-separated list of plugins to load, + where each plugin is identified as name=library, where + name is the plugin name and library is the plugin library + in plugin_dir. + -P, --port=# Port number to use for connection or 0 to default to, + my.cnf, $MYSQL_TCP_PORT, /etc/services, built-in default + (3306), whatever comes first + --port-open-timeout=# + Maximum time in seconds to wait for the port to become + free. (Default: no wait) + --preload-buffer-size=# + The size of the buffer that is allocated when preloading + indexes + --profiling-history-size=# + Limit of query profiling memory + --query-alloc-block-size=# + Allocation block size for query parsing and execution + --query-cache-limit=# + Don't cache results that are bigger than this + --query-cache-min-res-unit=# + The minimum size for blocks allocated by the query cache + --query-cache-size=# + The memory allocated to store results from old queries + --query-cache-type=name + OFF = Don't cache or retrieve results. ON = Cache all + results except SELECT SQL_NO_CACHE ... queries. DEMAND = + Cache only SELECT SQL_CACHE ... queries + --query-cache-wlock-invalidate + Invalidate queries in query cache on LOCK for write + --query-prealloc-size=# + Persistent buffer for query parsing and execution + --range-alloc-block-size=# + Allocation block size for storing ranges during + optimization + --read-buffer-size=# + Each thread that does a sequential scan allocates a + buffer of this size for each table it scans. If you do + many sequential scans, you may want to increase this + value + --read-only Make all non-temporary tables read-only, with the + exception for replication (slave) threads and users with + the SUPER privilege + --read-rnd-buffer-size=# + When reading rows in sorted order after a sort, the rows + are read through this buffer to avoid a disk seeks. If + not set, then it's set to the value of record_buffer + --record-buffer=# Deprecated; use --read-buffer-size instead. + --relay-log=name The location and name to use for relay logs + --relay-log-index=name + The location and name to use for the file that keeps a + list of the last relay logs + --relay-log-info-file=name + The location and name of the file that remembers where + the SQL replication thread is in the relay logs + --relay-log-purge if disabled - do not purge relay logs. if enabled - purge + them as soon as they are no more needed + (Defaults to on; use --skip-relay-log-purge to disable.) + --relay-log-recovery + Enables automatic relay log recovery right after the + database startup, which means that the IO Thread starts + re-fetching from the master right after the last + transaction processed + --relay-log-space-limit=# + Maximum space to use for all relay logs + --replicate-do-db=name + Tells the slave thread to restrict replication to the + specified database. To specify more than one database, + use the directive multiple times, once for each database. + Note that this will only work if you do not use + cross-database queries such as UPDATE some_db.some_table + SET foo='bar' while having selected a different or no + database. If you need cross database updates to work, + make sure you have 3.23.28 or later, and use + replicate-wild-do-table=db_name.%. + --replicate-do-table=name + Tells the slave thread to restrict replication to the + specified table. To specify more than one table, use the + directive multiple times, once for each table. This will + work for cross-database updates, in contrast to + replicate-do-db. + --replicate-ignore-db=name + Tells the slave thread to not replicate to the specified + database. To specify more than one database to ignore, + use the directive multiple times, once for each database. + This option will not work if you use cross database + updates. If you need cross database updates to work, make + sure you have 3.23.28 or later, and use + replicate-wild-ignore-table=db_name.%. + --replicate-ignore-table=name + Tells the slave thread to not replicate to the specified + table. To specify more than one table to ignore, use the + directive multiple times, once for each table. This will + work for cross-datbase updates, in contrast to + replicate-ignore-db. + --replicate-rewrite-db=name + Updates to a database with a different name than the + original. Example: + replicate-rewrite-db=master_db_name->slave_db_name. + --replicate-same-server-id + In replication, if set to 1, do not skip events having + our server id. Default value is 0 (to break infinite + loops in circular replication). Can't be set to 1 if + --log-slave-updates is used. + --replicate-wild-do-table=name + Tells the slave thread to restrict replication to the + tables that match the specified wildcard pattern. To + specify more than one table, use the directive multiple + times, once for each table. This will work for + cross-database updates. Example: + replicate-wild-do-table=foo%.bar% will replicate only + updates to tables in all databases that start with foo + and whose table names start with bar. + --replicate-wild-ignore-table=name + Tells the slave thread to not replicate to the tables + that match the given wildcard pattern. To specify more + than one table to ignore, use the directive multiple + times, once for each table. This will work for + cross-database updates. Example: + replicate-wild-ignore-table=foo%.bar% will not do updates + to tables in databases that start with foo and whose + table names start with bar. + --report-host=name Hostname or IP of the slave to be reported to the master + during slave registration. Will appear in the output of + SHOW SLAVE HOSTS. Leave unset if you do not want the + slave to register itself with the master. Note that it is + not sufficient for the master to simply read the IP of + the slave off the socket once the slave connects. Due to + NAT and other routing issues, that IP may not be valid + for connecting to the slave from the master or other + hosts + --report-password=name + The account password of the slave to be reported to the + master during slave registration + --report-port=# Port for connecting to slave reported to the master + during slave registration. Set it only if the slave is + listening on a non-default port or if you have a special + tunnel from the master or other clients to the slave. If + not sure, leave this option unset + --report-user=name The account user name of the slave to be reported to the + master during slave registration + --rpl-recovery-rank=# + Unused, will be removed + --safe-mode Skip some optimize stages (for testing). + --safe-show-database + Deprecated option; use GRANT SHOW DATABASES instead... + --safe-user-create Don't allow new user creation by the user who has no + write privileges to the mysql.user table. + --secure-auth Disallow authentication for accounts that have old + (pre-4.1) passwords + --secure-file-priv=name + Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to + files within specified directory + --server-id=# Uniquely identifies the server instance in the community + of replication partners + -O, --set-variable=name + Change the value of a variable. Please note that this + option is deprecated;you can set variables directly with + --variable-name=value. + --shared-memory Enable the shared memory + --shared-memory-base-name=name + Base name of shared memory + --show-slave-auth-info + Show user and password in SHOW SLAVE HOSTS on this master + --skip-grant-tables Start without grant tables. This gives all users FULL + ACCESS to all tables! + --skip-host-cache Don't cache host names. + --skip-locking Deprecated option, use --skip-external-locking instead. + --skip-name-resolve Don't resolve hostnames. All hostnames are IP's or + 'localhost'. + --skip-networking Don't allow connection with TCP/IP + --skip-new Don't use new, possible wrong routines. + --skip-show-database + Don't allow 'SHOW DATABASE' commands + --skip-slave-start If set, slave is not autostarted. + --skip-stack-trace Don't print a stack trace on failure. + --skip-symlink Don't allow symlinking of tables. Deprecated option. Use + --skip-symbolic-links instead. + --skip-thread-priority + Don't give threads different priorities. This option is + deprecated because it has no effect; the implied behavior + is already the default. + --slave-compressed-protocol + Use compression on master/slave protocol + --slave-exec-mode=name + Modes for how replication events should be executed. + Legal values are STRICT (default) and IDEMPOTENT. In + IDEMPOTENT mode, replication will not stop for operations + that are idempotent. In STRICT mode, replication will + stop on any unexpected difference between the master and + the slave + --slave-load-tmpdir=name + The location where the slave should put its temporary + files when replicating a LOAD DATA INFILE command + --slave-net-timeout=# + Number of seconds to wait for more data from a + master/slave connection before aborting the read + --slave-skip-errors=name + Tells the slave thread to continue replication when a + query event returns an error from the provided list + --slave-transaction-retries=# + Number of times the slave SQL thread will retry a + transaction in case it failed with a deadlock or elapsed + lock wait timeout, before giving up and stopping + --slow-launch-time=# + If creating the thread takes longer than this value (in + seconds), the Slow_launch_threads counter will be + incremented + --slow-query-log Log slow queries to a table or log file. Defaults logging + to a file hostname-slow.log or a table mysql.slow_log if + --log-output=TABLE is used. Must be enabled to activate + other slow log options + --slow-query-log-file=name + Log slow queries to given log file. Defaults logging to + hostname-slow.log. Must be enabled to activate other slow + log options + --socket=name Socket file to use for connection + --sort-buffer-size=# + Each thread that needs to do a sort allocates a buffer of + this size + --sporadic-binlog-dump-fail + Option used by mysql-test for debugging and testing of + replication. + --sql-bin-update-same + The update log is deprecated since version 5.0, is + replaced by the binary log and this option does nothing + anymore. + --sql-mode=name Syntax: sql-mode=mode[,mode[,mode...]]. See the manual + for the complete list of valid sql modes + --standalone Dummy option to start as a standalone program (NT). + -s, --symbolic-links + Enable symbolic link support. + --sync-binlog=# Synchronously flush binary log to disk after every #th + event. Use 0 (default) to disable synchronous flushing + --sync-frm Sync .frm files to disk on creation + (Defaults to on; use --skip-sync-frm to disable.) + --sync-master-info=# + Synchronously flush master info to disk after every #th + event. Use 0 (default) to disable synchronous flushing + --sync-relay-log=# Synchronously flush relay log to disk after every #th + event. Use 0 (default) to disable synchronous flushing + --sync-relay-log-info=# + Synchronously flush relay log info to disk after every + #th transaction. Use 0 (default) to disable synchronous + flushing + --sysdate-is-now Non-default option to alias SYSDATE() to NOW() to make it + safe-replicable. Since 5.0, SYSDATE() returns a `dynamic' + value different for different invocations, even within + the same statement. + --table-cache=# Deprecated; use --table-open-cache instead. + --table-definition-cache=# + The number of cached table definitions + --table-lock-wait-timeout=# + Timeout in seconds to wait for a table level lock before + returning an error. Used only if the connection has + active cursors + --table-open-cache=# + The number of cached open tables + --tc-heuristic-recover=name + Decision to use in heuristic recover process. Possible + values are COMMIT or ROLLBACK. + --thread-cache-size=# + How many threads we should keep in a cache for reuse + --thread-handling=name + Define threads usage for handling queries, one of + one-thread-per-connection, no-threads + --thread-stack=# The stack size for each thread + --time-format=name The TIME format (ignored) + --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are + currently supported) + --tmp-table-size=# If an internal in-memory temporary table exceeds this + size, MySQL will automatically convert it to an on-disk + MyISAM table + -t, --tmpdir=name Path for temporary files. Several paths may be specified, + separated by a semicolon (;), in this case they are used + in a round-robin fashion + --transaction-alloc-block-size=# + Allocation block size for transactions to be stored in + binary log + --transaction-isolation=name + Default transaction isolation level. + --transaction-prealloc-size=# + Persistent buffer for transactions to be stored in binary + log + --updatable-views-with-limit=name + YES = Don't issue an error message (warning only) if a + VIEW without presence of a key of the underlying table is + used in queries with a LIMIT clause for updating. NO = + Prohibit update of a VIEW, which does not contain a key + of the underlying table and the query uses a LIMIT clause + (usually get from GUI tools) + -s, --use-symbolic-links + Enable symbolic link support. Deprecated option; use + --symbolic-links instead. + -u, --user=name Run mysqld daemon as user. + -v, --verbose Used with --help option for detailed help + -V, --version Output version information and exit. + --wait-timeout=# The number of seconds the server waits for activity on a + connection before closing it + -W, --warnings[=#] Deprecated; use --log-warnings instead. Variables (--variable-name=value) -and boolean options {FALSE|TRUE} Value (after reading options) ---------------------------------- ---------------------------------------- -abort-slave-event-count 0 -allow-suspicious-udfs FALSE -auto-increment-increment 1 -auto-increment-offset 1 -automatic-sp-privileges TRUE -back-log 50 -big-tables FALSE -bind-address (No default value) -binlog-cache-size 32768 -binlog-format STATEMENT -binlog-row-event-max-size 1024 -bulk-insert-buffer-size 8388608 -character-set-client-handshake TRUE -character-set-filesystem binary -character-set-server latin1 -character-sets-dir MYSQL_CHARSETSDIR/ -chroot (No default value) -collation-server latin1_swedish_ci -completion-type NO_CHAIN -concurrent-insert AUTO -connect-timeout 10 -console FALSE -datadir MYSQLTEST_VARDIR/install.db/ -date-format %Y-%m-%d -datetime-format %Y-%m-%d %H:%i:%s -default-character-set latin1 -default-collation latin1_swedish_ci -default-storage-engine MyISAM -default-time-zone (No default value) -default-week-format 0 -delay-key-write ON -delayed-insert-limit 100 -delayed-insert-timeout 300 -delayed-queue-size 1000 -disconnect-slave-event-count 0 -div-precision-increment 4 -enable-locking FALSE -engine-condition-pushdown TRUE -event-scheduler OFF -expire-logs-days 0 -external-locking FALSE -flush FALSE -flush-time 1800 -ft-boolean-syntax + -><()~*:""&| -ft-max-word-len 84 -ft-min-word-len 4 -ft-query-expansion-limit 20 -ft-stopword-file (No default value) -gdb FALSE -general-log FALSE -group-concat-max-len 1024 -help TRUE -ignore-builtin-innodb FALSE -init-connect -init-file (No default value) -init-rpl-role MASTER -init-slave -interactive-timeout 28800 -join-buffer-size 131072 -keep-files-on-create FALSE -key-buffer-size 8388608 -key-cache-age-threshold 300 -key-cache-block-size 1024 -key-cache-division-limit 100 -language MYSQL_SHAREDIR/ -lc-messages en_US -lc-messages-dir MYSQL_SHAREDIR/ -lc-time-names en_US -local-infile TRUE -log-bin (No default value) -log-bin-index (No default value) -log-bin-trust-function-creators FALSE -log-error -log-isam myisam.log -log-output FILE -log-queries-not-using-indexes FALSE -log-short-format FALSE -log-slave-updates FALSE -log-slow-admin-statements FALSE -log-slow-slave-statements FALSE -log-tc tc.log -log-tc-size 24576 -log-update (No default value) -log-warnings 1 -long-query-time 10 -low-priority-updates FALSE -lower-case-table-names 1 -master-info-file master.info -master-retry-count 86400 -max-allowed-packet 1048576 -max-binlog-cache-size 18446744073709547520 -max-binlog-dump-events 0 -max-binlog-size 1073741824 -max-connect-errors 10 -max-connections 151 -max-delayed-threads 20 -max-error-count 64 -max-heap-table-size 16777216 -max-join-size 18446744073709551615 -max-length-for-sort-data 1024 -max-prepared-stmt-count 16382 -max-relay-log-size 0 -max-seeks-for-key 18446744073709551615 -max-sort-length 1024 -max-sp-recursion-depth 0 -max-tmp-tables 32 -max-user-connections 0 -max-write-lock-count 18446744073709551615 -memlock FALSE -min-examined-row-limit 0 -multi-range-count 256 -myisam-block-size 1024 -myisam-data-pointer-size 6 -myisam-max-sort-file-size 9223372036853727232 -myisam-mmap-size 18446744073709551615 -myisam-recover-options OFF -myisam-repair-threads 1 -myisam-sort-buffer-size 8388608 -myisam-stats-method nulls_unequal -myisam-use-mmap FALSE -named-pipe FALSE -net-buffer-length 16384 -net-read-timeout 30 -net-retry-count 10 -net-write-timeout 60 -new FALSE -old FALSE -old-alter-table FALSE -old-passwords FALSE -old-style-user-limits FALSE -optimizer-prune-level 1 -optimizer-search-depth 62 -optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on -plugin-load (No default value) -port 3306 -port-open-timeout 0 -preload-buffer-size 32768 -profiling-history-size 15 -query-alloc-block-size 8192 -query-cache-limit 1048576 -query-cache-min-res-unit 4096 -query-cache-size 0 -query-cache-type ON -query-cache-wlock-invalidate FALSE -query-prealloc-size 8192 -range-alloc-block-size 4096 -read-buffer-size 131072 -read-only FALSE -read-rnd-buffer-size 262144 -record-buffer 131072 -relay-log (No default value) -relay-log-index (No default value) -relay-log-info-file relay-log.info -relay-log-purge TRUE -relay-log-recovery FALSE -relay-log-space-limit 0 -replicate-same-server-id FALSE -report-host (No default value) -report-password (No default value) -report-port 3306 -report-user (No default value) -rpl-recovery-rank 0 -safe-user-create FALSE -secure-auth FALSE -secure-file-priv (No default value) -server-id 0 -shared-memory FALSE -shared-memory-base-name MYSQL -show-slave-auth-info FALSE -skip-grant-tables TRUE -skip-networking FALSE -skip-show-database FALSE -skip-slave-start FALSE -slave-compressed-protocol FALSE -slave-exec-mode STRICT -slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ -slave-net-timeout 3600 -slave-skip-errors (No default value) -slave-transaction-retries 10 -slow-launch-time 2 -slow-query-log FALSE -socket MySQL -sort-buffer-size 2097152 -sporadic-binlog-dump-fail FALSE -sql-mode -symbolic-links FALSE -sync-binlog 0 -sync-frm TRUE -sync-master-info 0 -sync-relay-log 0 -sync-relay-log-info 0 -sysdate-is-now FALSE -table-cache 400 -table-definition-cache 400 -table-lock-wait-timeout 50 -table-open-cache 400 -tc-heuristic-recover COMMIT -thread-cache-size 0 -thread-handling one-thread-per-connection -thread-stack 262144 -time-format %H:%i:%s -timed-mutexes FALSE -tmp-table-size 16777216 -tmpdir MYSQLTEST_VARDIR/tmp/ -transaction-alloc-block-size 8192 -transaction-isolation REPEATABLE-READ -transaction-prealloc-size 4096 -updatable-views-with-limit YES -use-symbolic-links FALSE -verbose TRUE -wait-timeout 28800 -warnings 1 +abort-slave-event-count 0 +allow-suspicious-udfs FALSE +auto-increment-increment 1 +auto-increment-offset 1 +automatic-sp-privileges TRUE +back-log 50 +big-tables FALSE +bind-address (No default value) +binlog-cache-size 32768 +binlog-format STATEMENT +binlog-row-event-max-size 1024 +bulk-insert-buffer-size 8388608 +character-set-client-handshake TRUE +character-set-filesystem binary +character-set-server latin1 +character-sets-dir MYSQL_CHARSETSDIR/ +chroot (No default value) +collation-server latin1_swedish_ci +completion-type NO_CHAIN +concurrent-insert AUTO +connect-timeout 10 +console FALSE +datadir MYSQLTEST_VARDIR/install.db/ +date-format %Y-%m-%d +datetime-format %Y-%m-%d %H:%i:%s +default-character-set latin1 +default-collation latin1_swedish_ci +default-storage-engine MyISAM +default-time-zone (No default value) +default-week-format 0 +delay-key-write ON +delayed-insert-limit 100 +delayed-insert-timeout 300 +delayed-queue-size 1000 +disconnect-slave-event-count 0 +div-precision-increment 4 +enable-locking FALSE +engine-condition-pushdown TRUE +event-scheduler OFF +expire-logs-days 0 +external-locking FALSE +flush FALSE +flush-time 1800 +ft-boolean-syntax + -><()~*:""&| +ft-max-word-len 84 +ft-min-word-len 4 +ft-query-expansion-limit 20 +ft-stopword-file (No default value) +gdb FALSE +general-log FALSE +group-concat-max-len 1024 +help TRUE +ignore-builtin-innodb FALSE +init-connect +init-file (No default value) +init-rpl-role MASTER +init-slave +interactive-timeout 28800 +join-buffer-size 131072 +keep-files-on-create FALSE +key-buffer-size 8388608 +key-cache-age-threshold 300 +key-cache-block-size 1024 +key-cache-division-limit 100 +language MYSQL_SHAREDIR/ +lc-messages en_US +lc-messages-dir MYSQL_SHAREDIR/ +lc-time-names en_US +local-infile TRUE +log-bin (No default value) +log-bin-index (No default value) +log-bin-trust-function-creators FALSE +log-error +log-isam myisam.log +log-output FILE +log-queries-not-using-indexes FALSE +log-short-format FALSE +log-slave-updates FALSE +log-slow-admin-statements FALSE +log-slow-slave-statements FALSE +log-tc tc.log +log-tc-size 24576 +log-update (No default value) +log-warnings 1 +long-query-time 10 +low-priority-updates FALSE +lower-case-table-names 1 +master-info-file master.info +master-retry-count 86400 +max-allowed-packet 1048576 +max-binlog-cache-size 18446744073709547520 +max-binlog-dump-events 0 +max-binlog-size 1073741824 +max-connect-errors 10 +max-connections 151 +max-delayed-threads 20 +max-error-count 64 +max-heap-table-size 16777216 +max-join-size 18446744073709551615 +max-length-for-sort-data 1024 +max-prepared-stmt-count 16382 +max-relay-log-size 0 +max-seeks-for-key 18446744073709551615 +max-sort-length 1024 +max-sp-recursion-depth 0 +max-tmp-tables 32 +max-user-connections 0 +max-write-lock-count 18446744073709551615 +memlock FALSE +min-examined-row-limit 0 +multi-range-count 256 +myisam-block-size 1024 +myisam-data-pointer-size 6 +myisam-max-sort-file-size 9223372036853727232 +myisam-mmap-size 18446744073709551615 +myisam-recover-options OFF +myisam-repair-threads 1 +myisam-sort-buffer-size 8388608 +myisam-stats-method nulls_unequal +myisam-use-mmap FALSE +named-pipe FALSE +net-buffer-length 16384 +net-read-timeout 30 +net-retry-count 10 +net-write-timeout 60 +new FALSE +old FALSE +old-alter-table FALSE +old-passwords FALSE +old-style-user-limits FALSE +optimizer-prune-level 1 +optimizer-search-depth 62 +optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on +plugin-load (No default value) +port 3306 +port-open-timeout 0 +preload-buffer-size 32768 +profiling-history-size 15 +query-alloc-block-size 8192 +query-cache-limit 1048576 +query-cache-min-res-unit 4096 +query-cache-size 0 +query-cache-type ON +query-cache-wlock-invalidate FALSE +query-prealloc-size 8192 +range-alloc-block-size 4096 +read-buffer-size 131072 +read-only FALSE +read-rnd-buffer-size 262144 +record-buffer 131072 +relay-log (No default value) +relay-log-index (No default value) +relay-log-info-file relay-log.info +relay-log-purge TRUE +relay-log-recovery FALSE +relay-log-space-limit 0 +replicate-same-server-id FALSE +report-host (No default value) +report-password (No default value) +report-port 3306 +report-user (No default value) +rpl-recovery-rank 0 +safe-user-create FALSE +secure-auth FALSE +secure-file-priv (No default value) +server-id 0 +shared-memory FALSE +shared-memory-base-name MYSQL +show-slave-auth-info FALSE +skip-grant-tables TRUE +skip-networking FALSE +skip-show-database FALSE +skip-slave-start FALSE +slave-compressed-protocol FALSE +slave-exec-mode STRICT +slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ +slave-net-timeout 3600 +slave-skip-errors (No default value) +slave-transaction-retries 10 +slow-launch-time 2 +slow-query-log FALSE +socket MySQL +sort-buffer-size 2097152 +sporadic-binlog-dump-fail FALSE +sql-mode +symbolic-links FALSE +sync-binlog 0 +sync-frm TRUE +sync-master-info 0 +sync-relay-log 0 +sync-relay-log-info 0 +sysdate-is-now FALSE +table-cache 400 +table-definition-cache 400 +table-lock-wait-timeout 50 +table-open-cache 400 +tc-heuristic-recover COMMIT +thread-cache-size 0 +thread-handling one-thread-per-connection +thread-stack 262144 +time-format %H:%i:%s +timed-mutexes FALSE +tmp-table-size 16777216 +tmpdir MYSQLTEST_VARDIR/tmp/ +transaction-alloc-block-size 8192 +transaction-isolation REPEATABLE-READ +transaction-prealloc-size 4096 +updatable-views-with-limit YES +use-symbolic-links FALSE +verbose TRUE +wait-timeout 28800 +warnings 1 To see what values a running MySQL server is using, type 'mysqladmin variables' instead of 'mysqld --verbose --help'. From 6c66620684796c4e8a0bda976bf374c377c25e0b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 7 Jan 2010 02:58:55 +0100 Subject: [PATCH 214/466] Fix version string parsing --- cmake/mysql_version.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index 6b83cb50b2d..33ac5e479b2 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -55,10 +55,12 @@ MACRO(GET_MYSQL_VERSION) SET(VERSION ${VERSION_STRING}) + # Remove trailing (non-numeric) part of the version string + STRING(REGEX REPLACE "[^\\.0-9].*" "" VERSION_STRING ${VERSION_STRING}) + STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" MAJOR_VERSION "${VERSION_STRING}") STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" MINOR_VERSION "${VERSION_STRING}") STRING(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" PATCH "${VERSION_STRING}") - SET(MYSQL_BASE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}" CACHE INTERNAL "MySQL Base version") SET(MYSQL_NO_DASH_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH}") MATH(EXPR MYSQL_VERSION_ID "10000*${MAJOR_VERSION} + 100*${MINOR_VERSION} + ${PATCH}") From 9ab9c73a87ac4530a40da0b2cf089e8e2af70ec1 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 7 Jan 2010 11:22:45 +0100 Subject: [PATCH 215/466] Bug #49269 mysqltest crashes on 'reap' if query executed after 'send' Undid amendment allowing pending reap after switching connections Moved check for pending reap earlier; failed if running with ps-protocol --- client/mysqltest.cc | 7 +++---- mysql-test/r/mysqltest.result | 7 ------- .../suite/sys_vars/t/innodb_table_locks_func.test | 1 + .../sys_vars/t/sql_low_priority_updates_func.test | 1 + mysql-test/t/mysqltest.test | 12 ------------ mysql-test/t/partition_innodb_semi_consistent.test | 1 + 6 files changed, 6 insertions(+), 23 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index d0c58d2f57b..45f94982472 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -4622,7 +4622,6 @@ void select_connection_name(const char *name) if (!con) die("connection '%s' not found in connection pool", name); - con->pending= FALSE; set_current_connection(con); DBUG_VOID_RETURN; @@ -6517,9 +6516,6 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, if (flags & QUERY_SEND_FLAG) { - if (cn->pending) - die ("Cannot run query on connection between send and reap"); - /* Send the query */ @@ -7111,6 +7107,9 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) init_dynamic_string(&ds_warnings, NULL, 0, 256); + if (cn->pending && (flags & QUERY_SEND_FLAG)) + die ("Cannot run query on connection between send and reap"); + /* Evaluate query if this is an eval command */ diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index def5d202c2f..e77dcd7b0a6 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -542,13 +542,6 @@ f1 Abcd mysqltest: At line 2: Cannot run query on connection between send and reap select * from t1;; -select * from t1;; -select 1; -1 -1 -select 2; -2 -2 drop table t1; mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file' mysqltest: At line 1: Missing required argument 'filename' to command 'write_file' diff --git a/mysql-test/suite/sys_vars/t/innodb_table_locks_func.test b/mysql-test/suite/sys_vars/t/innodb_table_locks_func.test index 6638a20c926..330addd6b3b 100644 --- a/mysql-test/suite/sys_vars/t/innodb_table_locks_func.test +++ b/mysql-test/suite/sys_vars/t/innodb_table_locks_func.test @@ -78,6 +78,7 @@ COMMIT; --echo 'CONNECTION con2' CONNECTION con2; +reap; UNLOCK tables; DROP TABLE t1; diff --git a/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test b/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test index 2ef6e34b0b3..5e0314c25ae 100644 --- a/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test +++ b/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test @@ -127,6 +127,7 @@ connection con0; SET SESSION low_priority_updates = OFF; --echo ** Connection con1 ** connection con1; +reap; SET SESSION low_priority_updates = OFF; --echo ** Connection default** connection default; diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index f3fea9348e4..45fc0715312 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1654,18 +1654,6 @@ EOF --exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.in 2>&1 remove_file $MYSQLTEST_VARDIR/tmp/mysqltest.in; -# 7. Test that stmt after send without reap IS allowed -# if we have switched connections - -connect (test_con1,localhost,root,,); ---send select * from t1; -connection default; -select 1; -connection test_con1; -select 2; -disconnect test_con1; -connection default; - drop table t1; # ---------------------------------------------------------------------------- diff --git a/mysql-test/t/partition_innodb_semi_consistent.test b/mysql-test/t/partition_innodb_semi_consistent.test index 6a6a7cf958e..2711d79f194 100644 --- a/mysql-test/t/partition_innodb_semi_consistent.test +++ b/mysql-test/t/partition_innodb_semi_consistent.test @@ -187,6 +187,7 @@ SELECT * FROM t1; --echo # Switch to connection con2 connection con2; +reap; SELECT * FROM t1; connection default; From be128c547fc614349695208f37fccf2a86e2139a Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 7 Jan 2010 13:17:54 +0100 Subject: [PATCH 216/466] backport some changes from WL 4378 --- mysql-test/mysql-test-run.pl | 39 +++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 83740e08ca6..d0f7d2b2a75 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -68,8 +68,8 @@ use My::File::Path; # Patched version of File::Path use File::Basename; use File::Copy; use File::Find; -use File::Temp qw / tempdir /; -use File::Spec::Functions qw / splitdir /; +use File::Temp qw/tempdir/; +use File::Spec::Functions qw/splitdir/; use My::Platform; use My::SafeProcess; use My::ConfigFactory; @@ -119,6 +119,8 @@ END { } } +sub env_or_val($$) { defined $ENV{$_[0]} ? $ENV{$_[0]} : $_[1] } + my $path_config_file; # The generated config file, var/my.cnf # Visual Studio produces executables in different sub-directories based on the @@ -217,7 +219,7 @@ my $start_only; my $opt_wait_all; my $opt_repeat= 1; my $opt_retry= 3; -my $opt_retry_failure= 2; +my $opt_retry_failure= env_or_val(MTR_RETRY_FAILURE => 2); my $opt_strace_client; @@ -247,9 +249,9 @@ our %mysqld_variables; my $source_dist= 0; -my $opt_max_save_core= $ENV{MTR_MAX_SAVE_CORE} || 5; -my $opt_max_save_datadir= $ENV{MTR_MAX_SAVE_DATADIR} || 20; -my $opt_max_test_fail= $ENV{MTR_MAX_TEST_FAIL} || 10; +my $opt_max_save_core= env_or_val(MTR_MAX_SAVE_CORE => 5); +my $opt_max_save_datadir= env_or_val(MTR_MAX_SAVE_DATADIR => 20); +my $opt_max_test_fail= env_or_val(MTR_MAX_TEST_FAIL => 10); my $opt_parallel= $ENV{MTR_PARALLEL} || 1; @@ -794,11 +796,12 @@ sub set_vardir { sub command_line_setup { my $opt_comment; my $opt_usage; + my $opt_list_options; # Read the command line options # Note: Keep list, and the order, in sync with usage at end of this file Getopt::Long::Configure("pass_through"); - GetOptions( + my %options=( # Control what engine/variation to run 'embedded-server' => \$opt_embedded_server, 'ps-protocol' => \$opt_ps_protocol, @@ -918,9 +921,13 @@ sub command_line_setup { 'timediff' => \&report_option, 'help|h' => \$opt_usage, - ) or usage("Can't read options"); + 'list-options' => \$opt_list_options, + ); + + GetOptions(%options) or usage("Can't read options"); usage("") if $opt_usage; + list_options(\%options) if $opt_list_options; # -------------------------------------------------------------------------- # Setup verbosity @@ -4977,7 +4984,7 @@ sub gdb_arguments { my $type= shift; # Write $args to gdb init file - my $str= join(" ", @$$args); + my $str= join " ", map { s/"/\\"/g; "\"$_\""; } @$$args; my $gdb_init_file= "$opt_vardir/tmp/gdbinit.$type"; # Remove the old gdbinit file @@ -5041,7 +5048,7 @@ sub ddd_arguments { my $type= shift; # Write $args to ddd init file - my $str= join(" ", @$$args); + my $str= join " ", map { s/"/\\"/g; "\"$_\""; } @$$args; my $gdb_init_file= "$opt_vardir/tmp/gdbinit.$type"; # Remove the old gdbinit file @@ -5436,3 +5443,15 @@ HERE } +sub list_options ($) { + my $hash= shift; + + for (keys %$hash) { + s/([:=].*|[+!])$//; + s/\|/\n--/g; + print "--$_\n" unless /list-options/; + } + + exit(1); +} + From 10c0917897a861cfcd1f7867869758886bd2b373 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 8 Jan 2010 07:20:43 +0100 Subject: [PATCH 217/466] some refactoring around readline/libedit (handle dependent options, allow user with change WITH_READLINE to WITH_LIBEDIT and other way around after the first compile) --- cmake/readline.cmake | 73 +++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/cmake/readline.cmake b/cmake/readline.cmake index 4f0cb4a30c7..7b5ad0f386f 100644 --- a/cmake/readline.cmake +++ b/cmake/readline.cmake @@ -13,16 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -MACRO(SET_WITH_BUNDLED_READLINE option) - IF(option) - SET(not_option OFF) - ELSE() - SET(not_option ON) - ENDIF() - SET(WITH_READLINE ${option} CACHE BOOL "Use bundled readline") - SET(WITH_LIBEDIT ${not_option} CACHE BOOL "Use bundled libedit") -ENDMACRO() - MACRO (MYSQL_CHECK_MULTIBYTE) CHECK_INCLUDE_FILE(wctype.h HAVE_WCTYPE_H) CHECK_INCLUDE_FILE(wchar.h HAVE_WCHAR_H) @@ -119,8 +109,9 @@ MACRO (FIND_CURSES) ENDMACRO() MACRO (MYSQL_USE_BUNDLED_READLINE) - SET_WITH_BUNDLED_READLINE(ON) SET(USE_NEW_READLINE_INTERFACE 1) + SET(HAVE_HIST_ENTRY) + SET(USE_LIBEDIT_INTERFACE) SET(READLINE_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/cmd-line-utils) SET(READLINE_LIBRARY readline) FIND_CURSES() @@ -128,26 +119,27 @@ MACRO (MYSQL_USE_BUNDLED_READLINE) ENDMACRO() MACRO (MYSQL_USE_BUNDLED_LIBEDIT) - SET_WITH_BUNDLED_READLINE(OFF) - SET(USE_LIBEDIT_INTERFACE 1 CACHE INTERNAL "") - SET(HAVE_HIST_ENTRY 1 CACHE INTERNAL "") - SET(READLINE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/cmd-line-utils/libedit") + SET(USE_LIBEDIT_INTERFACE 1) + SET(HAVE_HIST_ENTRY 1) + SET(READLINE_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/cmd-line-utils/libedit) SET(READLINE_LIBRARY edit) FIND_CURSES() ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/cmd-line-utils/libedit) ENDMACRO() + MACRO (MYSQL_FIND_SYSTEM_READLINE name) - FIND_PATH(SYSTEM_READLINE_INCLUDE_DIR readline/readline.h ) - FIND_LIBRARY(SYSTEM_READLINE_LIBRARY NAMES ${name}) - MARK_AS_ADVANCED(SYSTEM_READLINE_INCLUDE_DIR SYSTEM_READLINE_LIBRARY) + + FIND_PATH(${name}_INCLUDE_DIR readline/readline.h ) + FIND_LIBRARY(${name}_LIBRARY NAMES ${name}) + MARK_AS_ADVANCED(${name}_INCLUDE_DIR ${name}_LIBRARY) INCLUDE(CheckCXXSourceCompiles) - SET(CMAKE_REQUIRES_LIBRARIES ${SYSTEM_READLINE_LIBRARY}) + SET(CMAKE_REQUIRES_LIBRARIES ${${name}_LIBRARY}) - IF(SYSTEM_READLINE_LIBRARY AND SYSTEM_READLINE_INCLUDE_DIR) + IF(${name}_LIBRARY AND ${name}_INCLUDE_DIR) SET(SYSTEM_READLINE_FOUND 1) - SET(CMAKE_REQUIRED_LIBRARIES ${SYSTEM_READLINE_LIBRARY}) + SET(CMAKE_REQUIRED_LIBRARIES ${${name}_LIBRARY}) CHECK_CXX_SOURCE_COMPILES(" #include #include @@ -156,8 +148,8 @@ MACRO (MYSQL_FIND_SYSTEM_READLINE name) HIST_ENTRY entry; return 0; }" - HAVE_HIST_ENTRY) - + ${name}_HAVE_HIST_ENTRY) + CHECK_CXX_SOURCE_COMPILES(" #include #include @@ -166,7 +158,7 @@ MACRO (MYSQL_FIND_SYSTEM_READLINE name) char res= *(*rl_completion_entry_function)(0,0); completion_matches(0,0); }" - USE_LIBEDIT_INTERFACE) + ${name}_USE_LIBEDIT_INTERFACE) CHECK_CXX_SOURCE_COMPILES(" @@ -177,31 +169,48 @@ MACRO (MYSQL_FIND_SYSTEM_READLINE name) rl_completion_func_t *func1= (rl_completion_func_t*)0; rl_compentry_func_t *func2= (rl_compentry_func_t*)0; }" - USE_NEW_READLINE_INTERFACE) + ${name}_USE_NEW_READLINE_INTERFACE) - IF(USE_LIBEDIT_INTERFACE OR USE_NEW_READLINE_INTERFACE) - SET(READLINE_LIBRARY ${SYSTEM_READLINE_LIBRARY}) - SET(READLINE_INCLUDE_DIR ${SYSTEM_READLINE_INCLUDE_DIR}) + IF(${name}_USE_LIBEDIT_INTERFACE OR ${name}_USE_NEW_READLINE_INTERFACE) + SET(READLINE_LIBRARY ${${name}_LIBRARY}) + SET(READLINE_INCLUDE_DIR ${${name}_INCLUDE_DIR}) + SET(HAVE_HIST_ENTRY ${${name}_HAVE_HIST_ENTRY}) + SET(USE_LIBEDIT_INTERFACE ${${name}_USE_LIBEDIT_INTERFACE}) + SET(USE_NEW_READLINE_INTERFACE ${${name}_USE_NEW_READLINE_INTERFACE}) SET(READLINE_FOUND 1) ENDIF() ENDIF() ENDMACRO() + MACRO (MYSQL_CHECK_READLINE) IF (NOT WIN32) MYSQL_CHECK_MULTIBYTE() - OPTION(WITH_READLINE "Use bundled readline" OFF) IF(NOT CYGWIN) - # Bundled libedit does not compile on cygwin - OPTION(WITH_LIBEDIT "Use bundled libedit" ON) + SET(WITH_LIBEDIT ON CACHE BOOL "Use bundled libedit") + SET(WITH_READLINE OFF CACHE BOOL "Use bundled readline") ELSE() - OPTION(WITH_LIBEDIT "Use bundled libedit" OFF) + # Bundled libedit does not compile on cygwin, only readline + SET(WITH_READLINE OFF CACHE BOOL "Use bundled readline") + ENDIF() + + # Handle mutual exclusion of WITH_READLINE/WITH_LIBEDIT variables + # We save current setting to recognize when user switched between + # WITH_READLINE and WITH_LIBEDIT + IF(WITH_READLINE AND SAVE_READLINE_SETTING MATCHES "WITH_LIBEDIT" + OR NOT SAVE_READLINE_SETTING ) + SET(WITH_LIBEDIT OFF CACHE BOOL "Use bundled libedit" FORCE) + ELSEIF(WITH_LIBEDIT AND SAVE_READLINE_SETTING MATCHES "WITH_READLINE" + OR NOT SAVE_READLINE_SETTING ) + SET(WITH_READLINE OFF CACHE BOOL "Use bundled readline" FORCE) ENDIF() IF(WITH_READLINE) MYSQL_USE_BUNDLED_READLINE() + SET(SAVE_READLINE_SETTING WITH_READLINE CACHE INTERNAL "" FORCE) ELSEIF(WITH_LIBEDIT) MYSQL_USE_BUNDLED_LIBEDIT() + SET(SAVE_READLINE_SETTING WITH_LIBEDIT CACHE INTERNAL "" FORCE) ELSE() MYSQL_FIND_SYSTEM_READLINE(readline) IF(NOT READLINE_FOUND) From 4da913b50da1b0768d8e49345098cf096cbdc537 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 8 Jan 2010 10:33:28 +0100 Subject: [PATCH 218/466] Fix error with cmake 2.6 --- cmake/readline.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/readline.cmake b/cmake/readline.cmake index 7b5ad0f386f..548144f8f61 100644 --- a/cmake/readline.cmake +++ b/cmake/readline.cmake @@ -197,11 +197,11 @@ MACRO (MYSQL_CHECK_READLINE) # Handle mutual exclusion of WITH_READLINE/WITH_LIBEDIT variables # We save current setting to recognize when user switched between # WITH_READLINE and WITH_LIBEDIT - IF(WITH_READLINE AND SAVE_READLINE_SETTING MATCHES "WITH_LIBEDIT" - OR NOT SAVE_READLINE_SETTING ) + IF(WITH_READLINE AND (SAVE_READLINE_SETTING MATCHES "WITH_LIBEDIT" + OR NOT SAVE_READLINE_SETTING)) SET(WITH_LIBEDIT OFF CACHE BOOL "Use bundled libedit" FORCE) - ELSEIF(WITH_LIBEDIT AND SAVE_READLINE_SETTING MATCHES "WITH_READLINE" - OR NOT SAVE_READLINE_SETTING ) + ELSEIF(WITH_LIBEDIT AND (SAVE_READLINE_SETTING MATCHES "WITH_READLINE" + OR NOT SAVE_READLINE_SETTING)) SET(WITH_READLINE OFF CACHE BOOL "Use bundled readline" FORCE) ENDIF() From b51195d6514994fa7b05d6351ad4da213d34e878 Mon Sep 17 00:00:00 2001 From: vladislav vaintroub Date: Fri, 8 Jan 2010 11:06:42 +0100 Subject: [PATCH 219/466] CMake 2.6.2 fix --- cmake/readline.cmake | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmake/readline.cmake b/cmake/readline.cmake index 548144f8f61..5b6d23fc14e 100644 --- a/cmake/readline.cmake +++ b/cmake/readline.cmake @@ -197,12 +197,16 @@ MACRO (MYSQL_CHECK_READLINE) # Handle mutual exclusion of WITH_READLINE/WITH_LIBEDIT variables # We save current setting to recognize when user switched between # WITH_READLINE and WITH_LIBEDIT - IF(WITH_READLINE AND (SAVE_READLINE_SETTING MATCHES "WITH_LIBEDIT" - OR NOT SAVE_READLINE_SETTING)) - SET(WITH_LIBEDIT OFF CACHE BOOL "Use bundled libedit" FORCE) - ELSEIF(WITH_LIBEDIT AND (SAVE_READLINE_SETTING MATCHES "WITH_READLINE" - OR NOT SAVE_READLINE_SETTING)) - SET(WITH_READLINE OFF CACHE BOOL "Use bundled readline" FORCE) + IF(WITH_READLINE) + IF(NOT SAVE_READLINE_SETTING OR SAVE_READLINE_SETTING MATCHES + "WITH_LIBEDIT") + SET(WITH_LIBEDIT OFF CACHE BOOL "Use bundled libedit" FORCE) + ENDIF() + ELSEIF(WITH_LIBEDIT) + IF(NOT SAVE_READLINE_SETTING OR SAVE_READLINE_SETTING MATCHES + "WITH_READLINE") + SET(WITH_READLINE OFF CACHE BOOL "Use bundled readline" FORCE) + ENDIF() ENDIF() IF(WITH_READLINE) From 0bce0c9041c5faf5a64855f21680f6898113d550 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 8 Jan 2010 11:26:32 +0100 Subject: [PATCH 220/466] Fix for bug #48538 "Assertion in thr_lock() on LOAD DATA CONCURRENT INFILE". Attempts to execute an INSERT statement for a MEMORY table which invoked a trigger or called a stored function which tried to perform LOW_PRIORITY update on the table being inserted into, resulted in debug servers aborting due to an assertion failure. On non-debug servers such INSERTs failed with "Can't update table t1 in stored function/trigger because it is already used by statement which invoked this stored function/trigger" as expected. The problem was that in the above scenario TL_WRITE_CONCURRENT_INSERT is converted to TL_WRITE inside the thr_lock() function since the MEMORY engine does not support concurrent inserts. This triggered an assertion which assumed that for the same table, one thread always requests locks with higher thr_lock_type value first. When TL_WRITE_CONCURRENT_INSERT is upgraded to TL_WRITE after the locks have been sorted, this is no longer true. In this case, TL_WRITE was requested after acquiring a TL_WRITE_LOW_PRIORITY lock on the table, triggering the assert. This fix solves the problem by adjusting this assert to take this scenario into account. An alternative approach to change handler::store_locks() methods for all engines which do not support concurrent inserts in such way that TL_WRITE_CONCURRENT_INSERT is upgraded to TL_WRITE there instead, was considered too intrusive. Commit on behalf of Dmitry Lenev. --- mysql-test/r/lock.result | 13 +++++++++++++ mysql-test/t/lock.test | 22 ++++++++++++++++++++++ mysys/thr_lock.c | 29 ++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 8f680858fdc..348412ea441 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -356,5 +356,18 @@ ERROR HY000: Can't execute the given command because you have active locked tabl UNLOCK TABLES; DROP TABLE t1; # +# Simplified test for bug #48538 "Assertion in thr_lock() on LOAD DATA +# CONCURRENT INFILE". +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (f1 INT, f2 INT) ENGINE = MEMORY; +CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW +UPDATE LOW_PRIORITY t1 SET f2 = 7; +# Statement below should fail with ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG +# error instead of failing on assertion in table-level locking subsystem. +INSERT INTO t1(f1) VALUES(0); +ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. +DROP TABLE t1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 64003c9d861..49e98abdc76 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -441,6 +441,28 @@ FLUSH TABLES WITH READ LOCK; UNLOCK TABLES; DROP TABLE t1; + +--echo # +--echo # Simplified test for bug #48538 "Assertion in thr_lock() on LOAD DATA +--echo # CONCURRENT INFILE". +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (f1 INT, f2 INT) ENGINE = MEMORY; +CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW + UPDATE LOW_PRIORITY t1 SET f2 = 7; + +--echo # Statement below should fail with ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG +--echo # error instead of failing on assertion in table-level locking subsystem. +--error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG +INSERT INTO t1(f1) VALUES(0); + +DROP TABLE t1; + + --echo # --echo # End of 6.0 tests. --echo # diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index a3f3e9ee080..a6bc128f32a 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -674,14 +674,23 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, write locks are of TL_WRITE_ALLOW_WRITE type. Note that, since lock requests for the same table are sorted in - such way that requests with higher thr_lock_type value come first, - lock being requested usually has equal or "weaker" type than one - which thread might have already acquired. - The exceptions are situations when: - - old lock type is TL_WRITE_ALLOW_READ and new lock type is - TL_WRITE_ALLOW_WRITE - - when old lock type is TL_WRITE_DELAYED - But these should never happen within MySQL. + such way that requests with higher thr_lock_type value come first + (with one exception (*)), lock being requested usually (**) has + equal or "weaker" type than one which thread might have already + acquired. + *) The only exception to this rule is case when type of old lock + is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside + of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since + engine turns out to be not supporting concurrent inserts. + Note that since TL_WRITE has the same compatibility rules as + TL_WRITE_LOW_PRIORITY (their only difference is priority), + it is OK to grant new lock without additional checks in such + situation. + **) The exceptions are situations when: + - old lock type is TL_WRITE_ALLOW_READ and new lock type is + TL_WRITE_ALLOW_WRITE + - when old lock type is TL_WRITE_DELAYED + But these should never happen within MySQL. Therefore it is OK to allow acquiring write lock on the table if this thread already holds some write lock on it. @@ -690,7 +699,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, different types of write lock on the same table). */ DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) || - (lock_type <= lock->write.data->type && + ((lock_type <= lock->write.data->type || + (lock_type == TL_WRITE && + lock->write.data->type == TL_WRITE_LOW_PRIORITY)) && ! ((lock_type < TL_WRITE_ALLOW_READ && lock->write.data->type == TL_WRITE_ALLOW_READ) || lock->write.data->type == TL_WRITE_DELAYED))); From d882efd1c056ba92a92900f32dbe890fc0baa2f2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 9 Jan 2010 00:38:19 +0100 Subject: [PATCH 221/466] Address Kents comments: copy docs, remove autom4te.cache subdirectory if exists. --- cmake/make_dist.cmake.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmake/make_dist.cmake.in b/cmake/make_dist.cmake.in index aae5bdbc666..13950e08553 100644 --- a/cmake/make_dist.cmake.in +++ b/cmake/make_dist.cmake.in @@ -36,6 +36,8 @@ SET(CMAKE_GENERATOR "@CMAKE_GENERATOR@") SET(CMAKE_MAKE_PROGRAM "@CMAKE_MAKE_PROGRAM@") SET(CMAKE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@") +SET(MYSQL_DOCS_LOCATION "@MYSQL_DOCS_LOCATION@") + SET(PACKAGE_DIR ${CMAKE_BINARY_DIR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}) @@ -127,10 +129,16 @@ CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql/sql_yacc.h CONFIGURE_FILE(${CMAKE_BINARY_DIR}/sql/sql_yacc.cc ${PACKAGE_DIR}/sql/sql_yacc.cc COPYONLY) +# Add documentation, if user has specified where to find them +IF(MYSQL_DOCS_LOCATION) + MESSAGE("Copying documentation files from " ${MYSQL_DOCS_LOCATION}) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_directory "${MYSQL_DOCS_LOCATION}" "${PACKAGE_DIR}") +ENDIF() # In case we used CPack, it could have copied some # extra files that are not usable on different machines. FILE(REMOVE ${PACKAGE_DIR}/CMakeCache.txt) +FILE(REMOVE_RECURSE ${PACKAGE_DIR}/autom4te.cache) # When packing source, prefer gnu tar to "cmake -P tar" # cmake does not preserve timestamps.gnuwin32 tar is broken, cygwin is ok From 36549c4fa293ceb026b21c09de31b060e8961e6b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 9 Jan 2010 01:16:07 +0100 Subject: [PATCH 222/466] Install docs --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c1cdb94da4..bd6310a20eb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,3 +241,7 @@ INSTALL(FILES COPYING EXCEPTIONS-CLIENT README DESTINATION .) IF(UNIX) INSTALL(FILES Docs/INSTALL-BINARY DESTINATION .) 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") +MARK_AS_ADVANCED(MYSQL_DOCS_LOCATION) +INSTALL(DIRECTORY Docs DESTINATION .) From 125cd58f2b3239b7ff0ec727a62e8ca160f3a877 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 11 Jan 2010 14:42:07 +0100 Subject: [PATCH 223/466] Add option for specifying MYSQL build configurations. Add mysql_release.cmake intended for use by the build team ( compile options, build settings to do MySQL releases) --- CMakeLists.txt | 4 ++++ .../build_configurations/mysql_release.cmake | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 cmake/build_configurations/mysql_release.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index bd6310a20eb..6dafc6b3086 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,10 @@ if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 2.6) CMAKE_POLICY(VERSION 2.8) endif() +IF(BUILD_CONFIG) + INCLUDE(${CMAKE_SOURCE_DIR}/cmake/build_configurations/${BUILD_CONFIG}.cmake) +ENDIF() + SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) # First, decide about build type (debug or release) diff --git a/cmake/build_configurations/mysql_release.cmake b/cmake/build_configurations/mysql_release.cmake new file mode 100644 index 00000000000..d72624c8ac7 --- /dev/null +++ b/cmake/build_configurations/mysql_release.cmake @@ -0,0 +1,23 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This file includes build settings used for MySQL release + +SET(WITH_INNOBASE_STORAGE_ENGINE 1) +SET(WITH_ARCHIVE_STORAGE_ENGINE 1) +SET(WITH_BLACKHOLE_STORAGE_ENGINE 1) +SET(WITH_FEDERATED_STORAGE_ENGINE 1) +SET(WITHOUT_EXAMPLE_STORAGE_ENGINE 1) +SET(WITH_EMBEDDED_SERVER 1) From 3fe740e13d2913e9f5f77325d19eb189209bce88 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 12 Jan 2010 00:31:50 +0100 Subject: [PATCH 224/466] Workaround a linker problem on OSX 10.4 --- configure.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.cmake b/configure.cmake index 7b64aa77061..9df2a4f19a1 100644 --- a/configure.cmake +++ b/configure.cmake @@ -92,6 +92,12 @@ IF(CMAKE_COMPILER_IS_GNUCXX) # mininal architecture flags, i486 enables GCC atomics ADD_DEFINITIONS(-march=i486) ENDIF() + IF(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) + # Workaround linker problems on OSX 10.4 + IF(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.5") + ADD_DEFINITIONS(-fno-common) + ENDIF() + ENDIF() ENDIF() From 6766c0d676160deca76c6de69fa6d74cc19ebcdf Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Tue, 12 Jan 2010 12:32:55 +0100 Subject: [PATCH 225/466] Backport of Bug#45523 "Objects of class base_ilist should not be copyable". Suppress the compiler-generated public copy constructor and assignment operator of class base_ilist; instead, implement move_elements_to() function which transfers ownership of elements from one list to another. --- sql/sp_head.cc | 7 ++----- sql/sql_cursor.cc | 9 ++++----- sql/sql_list.h | 36 ++++++++++++++++++++++++++++++------ sql/sql_prepare.cc | 4 ++-- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 4f5ca1fff04..cd7486dec72 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1171,8 +1171,7 @@ sp_head::execute(THD *thd) We should also save Item tree change list to avoid rollback something too early in the calling query. */ - old_change_list= thd->change_list; - thd->change_list.empty(); + thd->change_list.move_elements_to(&old_change_list); /* Cursors will use thd->packet, so they may corrupt data which was prepared for sending by upper level. OTOH cursors in the same routine can share this @@ -1318,9 +1317,7 @@ sp_head::execute(THD *thd) /* Restore all saved */ old_packet.swap(thd->packet); DBUG_ASSERT(thd->change_list.is_empty()); - thd->change_list= old_change_list; - /* To avoid wiping out thd->change_list on old_change_list destruction */ - old_change_list.empty(); + old_change_list.move_elements_to(&thd->change_list); thd->lex= old_lex; thd->query_id= old_query_id; DBUG_ASSERT(!thd->derived_tables); diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 098f049704c..31aa3e7ea52 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -321,7 +321,7 @@ Sensitive_cursor::post_open(THD *thd) lock= thd->lock; query_id= thd->query_id; free_list= thd->free_list; - change_list= thd->change_list; + thd->change_list.move_elements_to(&change_list); reset_thd(thd); /* Now we have an active cursor and can cause a deadlock */ thd->lock_info.n_cursors++; @@ -437,7 +437,7 @@ Sensitive_cursor::fetch(ulong num_rows) thd->open_tables= open_tables; thd->lock= lock; thd->query_id= query_id; - thd->change_list= change_list; + change_list.move_elements_to(&thd->change_list); /* save references to memory allocated during fetch */ thd->set_n_backup_active_arena(this, &backup_arena); @@ -459,7 +459,7 @@ Sensitive_cursor::fetch(ulong num_rows) /* Grab free_list here to correctly free it in close */ thd->restore_active_arena(this, &backup_arena); - change_list= thd->change_list; + thd->change_list.move_elements_to(&change_list); reset_thd(thd); for (info= ht_info; info->read_view; info++) @@ -506,7 +506,7 @@ Sensitive_cursor::close() info->ht= 0; } - thd->change_list= change_list; + change_list.move_elements_to(&thd->change_list); { /* XXX: Another hack: we need to set THD state as if in a fetch to be @@ -532,7 +532,6 @@ Sensitive_cursor::close() join= 0; stmt_arena= 0; free_items(); - change_list.empty(); DBUG_VOID_RETURN; } diff --git a/sql/sql_list.h b/sql/sql_list.h index e1bf05fff23..fdc80b116a7 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -504,15 +504,12 @@ public: template class I_List_iterator; -/* - WARNING: copy constructor of this class does not create a usable - copy, as its members may point at each other. -*/ class base_ilist { + struct ilink *first; + struct ilink last; public: - struct ilink *first,last; inline void empty() { first= &last; last.prev= &first; } base_ilist() { empty(); } inline bool is_empty() { return first == &last; } @@ -540,7 +537,31 @@ public: { return (first != &last) ? first : 0; } - friend class base_list_iterator; + + /** + Moves list elements to new owner, and empties current owner (i.e. this). + + @param[in,out] new_owner The new owner of the list elements. + Should be empty in input. + */ + + void move_elements_to(base_ilist *new_owner) + { + DBUG_ASSERT(new_owner->is_empty()); + new_owner->first= first; + new_owner->last= last; + empty(); + } + + friend class base_ilist_iterator; + private: + /* + We don't want to allow copying of this class, as that would give us + two list heads containing the same elements. + So we declare, but don't define copy CTOR and assignment operator. + */ + base_ilist(const base_ilist&); + void operator=(const base_ilist&); }; @@ -573,6 +594,9 @@ public: inline void push_back(T* a) { base_ilist::push_back(a); } inline T* get() { return (T*) base_ilist::get(); } inline T* head() { return (T*) base_ilist::head(); } + inline void move_elements_to(I_List* new_owner) { + base_ilist::move_elements_to(new_owner); + } #ifndef _lint friend class I_List_iterator; #endif diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 838f25320ec..70f69c75de3 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3407,7 +3407,7 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) bool error; Query_arena *save_stmt_arena= thd->stmt_arena; Item_change_list save_change_list; - thd->change_list= save_change_list; + thd->change_list.move_elements_to(&save_change_list); state= CONVENTIONAL_EXECUTION; @@ -3431,7 +3431,7 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= save_stmt_arena; - save_change_list= thd->change_list; + save_change_list.move_elements_to(&thd->change_list); /* Items and memory will freed in destructor */ From db1888b53cbad8b6def0cb07024c573b40597941 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 12 Jan 2010 16:15:21 +0100 Subject: [PATCH 226/466] Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache This was a deadlock between LOCK TABLES/CREATE DATABASE in one connection and DROP DATABASE in another. It only happened if the table locked by LOCK TABLES was in the database to be dropped. The deadlock is similar to the one in Bug#48940, but with LOCK TABLES instead of an active transaction. The order of events needed to trigger the deadlock was: 1) Connection 1 locks table db1.t1 using LOCK TABLES. It will now have a metadata lock on the table name. 2) Connection 2 issues DROP DATABASE db1. This will wait inside the MDL subsystem for the lock on db1.t1 to go away. While waiting, it will hold the LOCK_mysql_create_db mutex. 3) Connection 1 issues CREATE DATABASE (database name irrelevant). This will hang trying to lock the same mutex. Since this is the connection holding the metadata lock blocking Connection 2, we have a deadlock. This deadlock would also happen for earlier trees without MDL, but there DROP DATABASE would wait for a table to be removed from the table definition cache. This patch fixes the problem by prohibiting CREATE DATABASE in LOCK TABLES mode. In the example above, this prevents Connection 1 from hanging trying to get the LOCK_mysql_create_db mutex. Note that other commands that use LOCK_mysql_create_db (ALTER/DROP DATABASE) are already prohibited in LOCK TABLES mode. Incompatible change: CREATE DATABASE is now disallowed in LOCK TABLES mode. Test case added to schema.test. --- mysql-test/r/drop.result | 2 +- mysql-test/r/schema.result | 21 +++++++++++++++++- mysql-test/t/drop.test | 2 +- mysql-test/t/schema.test | 45 +++++++++++++++++++++++++++++++++++++- sql/sql_parse.cc | 6 +++++ 5 files changed, 72 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index 54bd05e526f..8c6bd582232 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -77,8 +77,8 @@ drop table t1; drop database if exists mysqltest; drop table if exists t1; create table t1 (i int); -lock tables t1 read; create database mysqltest; +lock tables t1 read; drop table t1; show open tables; drop database mysqltest; diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index 33a2d4d9448..b43f601caef 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -24,7 +24,26 @@ INSERT INTO schema1.t1 VALUES (1); DROP SCHEMA schema1; # Connection default ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; -ERROR HY000: Can't create/write to file './schema1/db.opt' (Errcode: 2) +Got one of the listed errors SET autocommit= TRUE; # Connection 2 # Connection default +# +# Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache +# +DROP SCHEMA IF EXISTS schema1; +# Connection default +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (id INT); +LOCK TABLE schema1.t1 WRITE; +# Connection con2 +DROP SCHEMA schema1; +# Connection default +# CREATE SCHEMA used to give a deadlock. +# Now we prohibit CREATE SCHEMA in LOCK TABLES mode. +CREATE SCHEMA IF NOT EXISTS schema1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# UNLOCK TABLES so DROP SCHEMA can continue. +UNLOCK TABLES; +# Connection con2 +# Connection default diff --git a/mysql-test/t/drop.test b/mysql-test/t/drop.test index 4aeb7165bcb..5ef4a28b202 100644 --- a/mysql-test/t/drop.test +++ b/mysql-test/t/drop.test @@ -100,8 +100,8 @@ drop database if exists mysqltest; drop table if exists t1; --enable_warnings create table t1 (i int); -lock tables t1 read; create database mysqltest; +lock tables t1 read; connect (addconroot1, localhost, root,,); --send drop table t1 connect (addconroot2, localhost, root,,); diff --git a/mysql-test/t/schema.test b/mysql-test/t/schema.test index a380a6241dd..f106b9e4865 100644 --- a/mysql-test/t/schema.test +++ b/mysql-test/t/schema.test @@ -46,7 +46,8 @@ let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist WHERE state= 'Waiting for table' AND info='DROP SCHEMA schema1'; --source include/wait_condition.inc ---error 1 +# Listing the error twice to prevent result diffences based on filename +--error 1,1 ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; SET autocommit= TRUE; @@ -59,6 +60,48 @@ connection default; disconnect con2; +--echo # +--echo # Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache +--echo # + +--disable_warnings +DROP SCHEMA IF EXISTS schema1; +--enable_warnings + +connect (con2, localhost, root); + +--echo # Connection default +connection default; +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (id INT); +LOCK TABLE schema1.t1 WRITE; + +--echo # Connection con2 +connection con2; +--send DROP SCHEMA schema1 + +--echo # Connection default +connection default; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' and info='DROP SCHEMA schema1'; +--source include/wait_condition.inc + +--echo # CREATE SCHEMA used to give a deadlock. +--echo # Now we prohibit CREATE SCHEMA in LOCK TABLES mode. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +CREATE SCHEMA IF NOT EXISTS schema1; + +--echo # UNLOCK TABLES so DROP SCHEMA can continue. +UNLOCK TABLES; + +--echo # Connection con2 +connection con2; +--reap + +--echo # Connection default +connection default; +disconnect con2; + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 689b2cec270..486cb9af288 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3402,6 +3402,12 @@ end_with_restore_list: if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0, is_schema_db(lex->name.str))) break; + if (thd->locked_tables_mode) + { + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); + goto error; + } res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name.str), &create_info, 0); break; From 7e7965aeb4bea403a39f314ee7efb0662affad6b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 13 Jan 2010 13:33:32 +0100 Subject: [PATCH 227/466] Add WITH_FAST_MUTEXES option (non-debug configurations only) --- CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dafc6b3086..dcc4972fbc0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,17 +103,22 @@ IF(WITH_ERROR_INJECT) SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DERROR_INJECT_SUPPORT") ENDIF() - - OPTION(ENABLE_LOCAL_INFILE "If we should should enable LOAD DATA LOCAL by default" ${IF_WIN}) MARK_AS_ADVANCED(ENABLE_LOCAL_INFILE) -# Set DBUG_OFF for non-debug project types. +OPTION(WITH_FAST_MUTEXES "Compile with fast mutexes" OFF) +MARK_AS_ADVANCED(WITH_FAST_MUTEXES) + +# Set DBUG_OFF and other optional release-only flags for non-debug project types FOREACH(BUILD_TYPE RELEASE RELWITHDEBINFO MINSIZEREL) FOREACH(LANG C CXX) SET(CMAKE_${LANG}_FLAGS_${BUILD_TYPE} "${CMAKE_${LANG}_FLAGS_${BUILD_TYPE}} -DDBUG_OFF") + IF(WITH_FAST_MUTEXES) + SET(CMAKE_${LANG}_FLAGS_${BUILD_TYPE} + "${CMAKE_${LANG}_FLAGS_${BUILD_TYPE}} -DMY_PTHREAD_FASTMUTEX=1") + ENDIF() ENDFOREACH() ENDFOREACH() From 5cddb6c15f58925b8bc1c348ead1f3e96b1be1b7 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 13 Jan 2010 16:52:14 +0100 Subject: [PATCH 228/466] use _GNU_SOURCE on Linux to have all useful definitions (pthread_rwlock_t) for example --- cmake/abi_check.cmake | 2 +- config.h.cmake | 1 + configure.cmake | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/abi_check.cmake b/cmake/abi_check.cmake index 8e8426d5245..80c57fa0a6e 100644 --- a/cmake/abi_check.cmake +++ b/cmake/abi_check.cmake @@ -20,7 +20,7 @@ # # We use gcc specific preprocessing command and sed/diff, so it will # only be run on Unix and only if gcc is used. -IF(CMAKE_COMPILER_IS_GNUCC AND UNIX) +IF(CMAKE_COMPILER_IS_GNUCC AND Linux) IF(CMAKE_C_COMPILER MATCHES "ccache$") SET(COMPILER ${CMAKE_C_COMPILER_ARG1}) STRING(REGEX REPLACE "^ " "" COMPILER ${COMPILER}) diff --git a/config.h.cmake b/config.h.cmake index 452525da24e..7abd23cb4b8 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -18,6 +18,7 @@ #cmakedefine DOT_FRM_VERSION @DOT_FRM_VERSION@ /* Headers we may want to use. */ #cmakedefine STDC_HEADERS 1 +#cmakedefine _GNU_SOURCE 1 #cmakedefine HAVE_ALLOCA_H 1 #cmakedefine HAVE_AIO_H 1 #cmakedefine HAVE_ARPA_INET_H 1 diff --git a/configure.cmake b/configure.cmake index 9df2a4f19a1..271e18c04fd 100644 --- a/configure.cmake +++ b/configure.cmake @@ -49,6 +49,7 @@ ENDIF() IF (CMAKE_SYSTEM_NAME MATCHES "Linux") SET(TARGET_OS_LINUX 1) SET(HAVE_NPTL 1) + SET(_GNU_SOURCE 1) ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS") SET(TARGET_OS_SOLARIS 1) ENDIF() From 548e2e3e6367f676987eaafd305f94cf044d0516 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 13 Jan 2010 21:31:33 +0100 Subject: [PATCH 229/466] adjust test result file --- mysql-test/r/mysqld--help-notwin.result | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 13236cb9684..0066a942ea3 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -128,7 +128,8 @@ The following options may be given as the first argument: on that value --enable-locking Deprecated option, use --external-locking instead. --engine-condition-pushdown - Push supported query conditions to the storage engine + Push supported query conditions to the storage engine. + Deprecated, use --optimizer-switch instead. (Defaults to on; use --skip-engine-condition-pushdown to disable.) --event-scheduler[=name] Enable the event scheduler. Possible values are ON, OFF, @@ -407,8 +408,9 @@ The following options may be given as the first argument: --optimizer-switch=name optimizer_switch=option=val[,option=val...], where option is one of {index_merge, index_merge_union, - index_merge_sort_union, index_merge_intersection} and val - is one of {on, off, default} + index_merge_sort_union, index_merge_intersection, + engine_condition_pushdown} and val is one of {on, off, + default} --pid-file=name Pid file used by safe_mysqld --plugin-dir=name Directory for plugins --plugin-load=name Optional semicolon-separated list of plugins to load, @@ -730,7 +732,6 @@ completion-type NO_CHAIN concurrent-insert AUTO connect-timeout 10 console FALSE -datadir MYSQLTEST_VARDIR/install.db/ date-format %Y-%m-%d datetime-format %Y-%m-%d %H:%i:%s default-character-set latin1 @@ -840,7 +841,7 @@ old-passwords FALSE old-style-user-limits FALSE optimizer-prune-level 1 optimizer-search-depth 62 -optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on +optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on plugin-load (No default value) port 3306 port-open-timeout 0 @@ -881,7 +882,6 @@ skip-show-database FALSE skip-slave-start FALSE slave-compressed-protocol FALSE slave-exec-mode STRICT -slave-load-tmpdir MYSQLTEST_VARDIR/tmp/ slave-net-timeout 3600 slave-skip-errors (No default value) slave-transaction-retries 10 @@ -909,7 +909,6 @@ thread-stack 262144 time-format %H:%i:%s timed-mutexes FALSE tmp-table-size 16777216 -tmpdir MYSQLTEST_VARDIR/tmp/ transaction-alloc-block-size 8192 transaction-isolation REPEATABLE-READ transaction-prealloc-size 4096 From ccf2600f662d424276a3e9d061b87b3a4c0e2223 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 13 Jan 2010 21:56:51 +0100 Subject: [PATCH 230/466] Do not link with mtmalloc on Solaris. add WITH_MYSQLD_LIBS for extra linker flags for mysqld --- configure.cmake | 9 --------- sql/CMakeLists.txt | 12 ++++++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/configure.cmake b/configure.cmake index 271e18c04fd..244fa1e0e9e 100644 --- a/configure.cmake +++ b/configure.cmake @@ -326,15 +326,6 @@ IF(UNIX) ENDIF() ENDIF() -IF (CMAKE_SYSTEM_NAME MATCHES "SunOS") - INCLUDE(CheckLibraryExists) - CHECK_LIBRARY_EXISTS(mtmalloc malloc "" HAVE_LIBMTMALLOC) - IF(HAVE_LIBMTMALLOC) - LINK_LIBRARIES(mtmalloc) - ENDIF() -ENDIF() - - # Workaround for CMake bug#9051 IF(CMAKE_OSX_SYSROOT) SET(ENV{CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 389b1a8766b..eaf8a45b25d 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -132,6 +132,7 @@ ENDIF() SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) TARGET_LINK_LIBRARIES(mysqld sql) + # Provide plugins with minimal set of libraries SET(INTERFACE_LIBS ${LIBRT}) IF(INTERFACE_LIBS) @@ -145,6 +146,17 @@ DTRACE_INSTRUMENT_STATIC_LIBS(mysqld "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}") +SET(WITH_MYSQLD_LIBS "" CACHE STRING "Additional linker flags for mysqld") +MARK_AS_ADVANCED(WITH_MYSQLD_LIBS) +IF(WITH_MYSQLD_LIBS) + GET_TARGET_PROPERTY(mysqld LINK_FLAGS MYSQLD_LINK_FLAGS) + IF(NOT MYSQLD_LINK_FLAGS) + SET(MYSQLD_LINK_FLAGS) + ENDIF() + SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS + "${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LIBS}") +ENDIF() + # Handle out-of-source build from source package with possibly broken # bison. Copy bison output to from source to build directory, if not already # there From cd8a37094c67a54db9cf9e4e519a6e0acfeebb08 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 14 Jan 2010 00:09:40 +0100 Subject: [PATCH 231/466] WITH_MYSQLD_LIBS=>WITH_MYSQLD_LDFLAGS --- sql/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index eaf8a45b25d..6fab01596b9 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -146,15 +146,15 @@ DTRACE_INSTRUMENT_STATIC_LIBS(mysqld "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}") -SET(WITH_MYSQLD_LIBS "" CACHE STRING "Additional linker flags for mysqld") -MARK_AS_ADVANCED(WITH_MYSQLD_LIBS) -IF(WITH_MYSQLD_LIBS) +SET(WITH_MYSQLD_LDFLAGS "" CACHE STRING "Additional linker flags for mysqld") +MARK_AS_ADVANCED(WITH_MYSQLD_LDFLAGS) +IF(WITH_MYSQLD_LDFLAGS) GET_TARGET_PROPERTY(mysqld LINK_FLAGS MYSQLD_LINK_FLAGS) IF(NOT MYSQLD_LINK_FLAGS) SET(MYSQLD_LINK_FLAGS) ENDIF() SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS - "${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LIBS}") + "${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LDFLAGS}") ENDIF() # Handle out-of-source build from source package with possibly broken From 912ce798ff440ca327cdd93c7832443f851226d7 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 14 Jan 2010 00:35:07 +0000 Subject: [PATCH 232/466] Fix CFLAGS passed to mysql_config, in case makefile generator is Makefile based --- scripts/install_scripts.cmake.in | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/install_scripts.cmake.in b/scripts/install_scripts.cmake.in index f2f64351469..5a590d15b74 100644 --- a/scripts/install_scripts.cmake.in +++ b/scripts/install_scripts.cmake.in @@ -2,8 +2,16 @@ SET(CMAKE_SYSTEM_NAME @CMAKE_SYSTEM_NAME@) SET(UNIX @UNIX@) SET(WIN32 @WIN32@) SET(MSVC @MSVC@) -SET(CFLAGS "@CMAKE_C_FLAGS_RELWITHDEBINFO@") -SET(CXXFLAGS "@CMAKE_CXX_FLAGS_RELWITHDEBINFO@") +SET(CMAKE_GENERATOR "@CMAKE_GENERATOR@") +IF(CMAKE_GENERATOR MATCHES "Makefiles") + # No multiconfig build - use CMAKE_C_FLAGS + SET(CFLAGS "@CMAKE_C_FLAGS@") + SET(CXXFLAGS "@CMAKE_CXX_FLAGS@") +ELSE() + # Multiconfig build - use CMAKE_C_FLAGS_RELWITHDEBINFO + SET(CFLAGS "@CMAKE_C_FLAGS_RELWITHDEBINFO@") + SET(CXXFLAGS "@CMAKE_CXX_FLAGS_RELWITHDEBINFO@") +ENDIF() SET(LDFLAGS "@CMAKE_SHARED_LIBRARY_LINK_FLAGS@") SET(mysqlclient_LIB_DEPENDS "@mysqlclient_LIB_DEPENDS@") SET(mysqlserver_LIB_DEPENDS "@mysqlserver_LIB_DEPENDS@") From 5045ad38de456124d899bf22e45af92334cda64c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 14 Jan 2010 14:03:24 +0100 Subject: [PATCH 233/466] Partial backport of: revno: 2762 [merge] committer: Matthias Leich branch nick: mysql-6.0-bugteam-push timestamp: Wed 2008-08-13 22:05:34 +0200 message: Upmerge 5.1 -> 6.0 ------------------------------------------------------------ revno: 2497.374.2 committer: Matthias Leich branch nick: mysql-5.1-bugteam-push timestamp: Wed 2008-08-13 21:44:54 +0200 message: Fix for Bug#37853 Test "funcs_1.processlist_val_ps" fails in various ways + corrections of logic in poll routines + minor improvements --- mysql-test/suite/funcs_1/datadict/processlist_val.inc | 10 +++++----- .../suite/funcs_1/r/processlist_val_no_prot.result | 6 +++--- mysql-test/suite/funcs_1/r/processlist_val_ps.result | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mysql-test/suite/funcs_1/datadict/processlist_val.inc b/mysql-test/suite/funcs_1/datadict/processlist_val.inc index c34fb626bcd..8b10cfc5e97 100644 --- a/mysql-test/suite/funcs_1/datadict/processlist_val.inc +++ b/mysql-test/suite/funcs_1/datadict/processlist_val.inc @@ -367,13 +367,13 @@ echo ; connection default; echo -# Poll till INFO is no more NULL and State = 'Locked'. +# Poll till INFO is no more NULL and State = 'Table Lock'. ; let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE INFO IS NOT NULL AND STATE = 'Locked'; + WHERE INFO IS NOT NULL AND STATE = 'Table Lock'; --source include/wait_condition.inc # -# Expect to see the state 'Locked' for the third connection because the SELECT +# Expect to see the state 'Table Lock' for the third connection because the SELECT # collides with the WRITE TABLE LOCK. --replace_column 1 3 6
@@ -102,11 +106,12 @@ public: @param name Name of of the object @param key Where to store the the MDL key. */ - void mdl_key_init(enum_mdl_namespace mdl_namespace, const char *db, const char *name) + void mdl_key_init(enum_mdl_namespace mdl_namespace, + const char *db, const char *name) { m_ptr[0]= (char) mdl_namespace; - m_db_name_length= (uint) (strmov(m_ptr + 1, db) - m_ptr - 1); - m_length= (uint) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); + m_db_name_length= (uint16) (strmov(m_ptr + 1, db) - m_ptr - 1); + m_length= (uint16) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); } void mdl_key_init(const MDL_key *rhs) { @@ -119,20 +124,34 @@ public: return (m_length == rhs->m_length && memcmp(m_ptr, rhs->m_ptr, m_length) == 0); } + /** + Compare two MDL keys lexicographically. + */ + int cmp(const MDL_key *rhs) const + { + /* + The key buffer is always '\0'-terminated. Since key + character set is utf-8, we can safely assume that no + character starts with a zero byte. + */ + return memcmp(m_ptr, rhs->m_ptr, min(m_length, rhs->m_length)+1); + } + MDL_key(const MDL_key *rhs) { mdl_key_init(rhs); } - MDL_key(enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg) + MDL_key(enum_mdl_namespace namespace_arg, + const char *db_arg, const char *name_arg) { mdl_key_init(namespace_arg, db_arg, name_arg); } MDL_key() {} /* To use when part of MDL_request. */ private: + uint16 m_length; + uint16 m_db_name_length; char m_ptr[MAX_MDLKEY_LENGTH]; - uint m_length; - uint m_db_name_length; private: MDL_key(const MDL_key &); /* not implemented */ MDL_key &operator=(const MDL_key &); /* not implemented */ @@ -198,7 +217,7 @@ public: DBUG_ASSERT(ticket == NULL); type= type_arg; } - bool is_shared() const { return type < MDL_EXCLUSIVE; } + bool is_shared() const { return type < MDL_INTENTION_EXCLUSIVE; } static MDL_request *create(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, const char *name, @@ -243,6 +262,17 @@ typedef void (*mdl_cached_object_release_hook)(void *); @note Multiple shared locks on a same object are represented by a single ticket. The same does not apply for other lock types. + + @note There are two groups of MDL_ticket members: + - "Externally accessible". These members can be accessed from + threads/contexts different than ticket owner in cases when + ticket participates in some list of granted or waiting tickets + for a lock. Therefore one should change these members before + including then to waiting/granted lists or while holding lock + protecting those lists. + - "Context private". Such members are private to thread/context + owning this ticket. I.e. they should not be accessed from other + threads/contexts. */ class MDL_ticket @@ -250,12 +280,13 @@ class MDL_ticket public: /** Pointers for participating in the list of lock requests for this context. + Context private. */ MDL_ticket *next_in_context; MDL_ticket **prev_in_context; /** Pointers for participating in the list of satisfied/pending requests - for the lock. + for the lock. Externally accessible. */ MDL_ticket *next_in_lock; MDL_ticket **prev_in_lock; @@ -265,8 +296,8 @@ public: void *get_cached_object(); void set_cached_object(void *cached_object, mdl_cached_object_release_hook release_hook); - const MDL_context *get_ctx() const { return m_ctx; } - bool is_shared() const { return m_type < MDL_EXCLUSIVE; } + MDL_context *get_ctx() const { return m_ctx; } + bool is_shared() const { return m_type < MDL_INTENTION_EXCLUSIVE; } bool is_upgradable_or_exclusive() const { return m_type == MDL_SHARED_UPGRADABLE || m_type == MDL_EXCLUSIVE; @@ -275,6 +306,8 @@ public: void downgrade_exclusive_lock(); private: friend class MDL_context; + friend class MDL_global_lock; + friend class MDL_object_lock; MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg) : m_type(type_arg), @@ -283,31 +316,31 @@ private: m_lock(NULL) {} - static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg); static void destroy(MDL_ticket *ticket); private: - /** Type of metadata lock. */ + /** Type of metadata lock. Externally accessible. */ enum enum_mdl_type m_type; - /** State of the metadata lock ticket. */ + /** State of the metadata lock ticket. Context private. */ enum enum_mdl_state m_state; - /** Context of the owner of the metadata lock ticket. */ + /** + Context of the owner of the metadata lock ticket. Externally accessible. + */ MDL_context *m_ctx; - /** Pointer to the lock object for this lock ticket. */ + /** Pointer to the lock object for this lock ticket. Context private. */ MDL_lock *m_lock; private: MDL_ticket(const MDL_ticket &); /* not implemented */ MDL_ticket &operator=(const MDL_ticket &); /* not implemented */ - - bool has_pending_conflicting_lock_impl() const; }; typedef I_P_List > + &MDL_request::prev_in_list>, + I_P_List_counter> MDL_request_list; /** @@ -326,21 +359,19 @@ public: typedef Ticket_list::Iterator Ticket_iterator; - void init(THD *thd); + MDL_context(); void destroy(); bool try_acquire_shared_lock(MDL_request *mdl_request); bool acquire_exclusive_lock(MDL_request *mdl_request); bool acquire_exclusive_locks(MDL_request_list *requests); bool try_acquire_exclusive_lock(MDL_request *mdl_request); - bool acquire_global_shared_lock(); bool clone_ticket(MDL_request *mdl_request); bool wait_for_locks(MDL_request_list *requests); void release_all_locks_for_name(MDL_ticket *ticket); void release_lock(MDL_ticket *ticket); - void release_global_shared_lock(); bool is_exclusive_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, @@ -368,7 +399,6 @@ public: void set_lt_or_ha_sentinel() { - DBUG_ASSERT(m_lt_or_ha_sentinel == NULL); m_lt_or_ha_sentinel= mdl_savepoint(); } MDL_ticket *lt_or_ha_sentinel() const { return m_lt_or_ha_sentinel; } @@ -385,16 +415,35 @@ public: bool can_wait_lead_to_deadlock() const; inline THD *get_thd() const { return m_thd; } - - bool is_waiting_in_mdl() const { return m_is_waiting_in_mdl; } + + /** + Wake up context which is waiting for a change of MDL_lock state. + */ + void awake() + { + pthread_cond_signal(&m_ctx_wakeup_cond); + } + + bool try_acquire_global_intention_exclusive_lock(MDL_request *mdl_request); + bool acquire_global_intention_exclusive_lock(MDL_request *mdl_request); + + bool acquire_global_shared_lock(); + void release_global_shared_lock(); + + /** + Check if this context owns global lock of particular type. + */ + bool is_global_lock_owner(enum_mdl_type type_arg) + { + MDL_request mdl_request; + bool not_used; + mdl_request.init(MDL_key::GLOBAL, "", "", type_arg); + return find_ticket(&mdl_request, ¬_used); + } + + void init(THD *thd_arg) { m_thd= thd_arg; } private: Ticket_list m_tickets; - bool m_has_global_shared_lock; - /** - Indicates that the owner of this context is waiting in - wait_for_locks() method. - */ - bool m_is_waiting_in_mdl; /** This member has two uses: 1) When entering LOCK TABLES mode, remember the last taken @@ -406,12 +455,27 @@ private: */ MDL_ticket *m_lt_or_ha_sentinel; THD *m_thd; + /** + Condvar which is used for waiting until this context's pending + request can be satisfied or this thread has to perform actions + to resolve potential deadlock (we subscribe for such notification + by adding ticket corresponding to the request to an appropriate + queue of waiters). + */ + pthread_cond_t m_ctx_wakeup_cond; private: - void release_ticket(MDL_ticket *ticket); - bool can_wait_lead_to_deadlock_impl() const; MDL_ticket *find_ticket(MDL_request *mdl_req, bool *is_lt_or_ha); void release_locks_stored_before(MDL_ticket *sentinel); + + bool try_acquire_lock_impl(MDL_request *mdl_request); + bool acquire_lock_impl(MDL_request *mdl_request); + bool acquire_exclusive_lock_impl(MDL_request *mdl_request); + + friend bool MDL_ticket::upgrade_shared_lock_to_exclusive(); +private: + MDL_context(const MDL_context &rhs); /* not implemented */ + MDL_context &operator=(MDL_context &rhs); /* not implemented */ }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index bb5bf428ef0..9f153b5aa0e 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1689,13 +1689,13 @@ void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt); /* Functions to work with system tables. */ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, - Open_tables_state *backup); -void close_system_tables(THD *thd, Open_tables_state *backup); + Open_tables_backup *backup); +void close_system_tables(THD *thd, Open_tables_backup *backup); TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table); TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table, - Open_tables_state *backup); -void close_performance_schema_table(THD *thd, Open_tables_state *backup); + Open_tables_backup *backup); +void close_performance_schema_table(THD *thd, Open_tables_backup *backup); bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, bool wait_for_refresh); diff --git a/sql/sp.cc b/sql/sp.cc index 1375d44cb9b..4cbb0f28b59 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -260,7 +260,7 @@ Stored_routine_creation_ctx::load_from_db(THD *thd, \# Pointer to TABLE object of mysql.proc */ -TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) +TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup) { TABLE_LIST table; @@ -382,7 +382,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) String str(buff, sizeof(buff), &my_charset_bin); bool saved_time_zone_used= thd->time_zone_used; ulong sql_mode, saved_mode= thd->variables.sql_mode; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; Stored_program_creation_ctx *creation_ctx; DBUG_ENTER("db_find_routine"); @@ -1432,7 +1432,7 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name) { TABLE *table; int ret; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) ret= SP_OPEN_TABLE_FAILED; diff --git a/sql/sp.h b/sql/sp.h index cab4d7dbeb5..c051b584031 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -128,6 +128,6 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, Routines which allow open/lock and close mysql.proc table even when we already have some tables open and locked. */ -TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup); +TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup); #endif /* _SP_H_ */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8d3ef372842..2f891375163 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1519,25 +1519,23 @@ void close_thread_tables(THD *thd) if (thd->open_tables) close_open_tables(thd); - if (thd->state_flags & Open_tables_state::BACKUPS_AVAIL) + /* + - If inside a multi-statement transaction, + defer the release of metadata locks until the current + transaction is either committed or rolled back. This prevents + other statements from modifying the table for the entire + duration of this transaction. This provides commit ordering + and guarantees serializability across multiple transactions. + - If closing a system table, defer the release of metadata locks + to the caller. We have no sentinel in MDL subsystem to guard + transactional locks from system tables locks, so don't know + which locks are which here. + - If in autocommit mode, or outside a transactional context, + automatically release metadata locks of the current statement. + */ + if (! thd->in_multi_stmt_transaction() && + ! (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) { - /* We can't have an open HANDLER in the backup open tables state. */ - DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); - /* - Due to the above assert, this is guaranteed to release *all* locks - in the context. - */ - thd->mdl_context.release_transactional_locks(); - } - else if (! thd->in_multi_stmt_transaction()) - { - /* - Defer the release of metadata locks until the current transaction - is either committed or rolled back. This prevents other statements - from modifying the table for the entire duration of this transaction. - This provides commitment ordering for guaranteeing serializability - across multiple transactions. - */ thd->mdl_context.release_transactional_locks(); } @@ -2336,10 +2334,9 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx, uint flags) { - ot_ctx->add_request(mdl_request); - if (table_list->lock_strategy) { + MDL_request *global_request; /* In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table may not yet exist. Let's acquire an exclusive lock for that @@ -2349,10 +2346,24 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, shared locks. This invariant is preserved here and is also enforced by asserts in metadata locking subsystem. */ + mdl_request->set_type(MDL_EXCLUSIVE); DBUG_ASSERT(! thd->mdl_context.has_locks() || - thd->handler_tables_hash.records); + thd->handler_tables_hash.records || + thd->global_read_lock); + if (!(global_request= ot_ctx->get_global_mdl_request(thd))) + return 1; + + if (! global_request->ticket) + { + ot_ctx->add_request(global_request); + if (thd->mdl_context.acquire_global_intention_exclusive_lock( + global_request)) + return 1; + } + + ot_ctx->add_request(mdl_request); if (thd->mdl_context.acquire_exclusive_lock(mdl_request)) return 1; } @@ -2371,8 +2382,29 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, if (flags & MYSQL_LOCK_IGNORE_FLUSH) mdl_request->set_type(MDL_SHARED_HIGH_PRIO); + if (mdl_request->type == MDL_SHARED_UPGRADABLE) + { + MDL_request *global_request; + + if (!(global_request= ot_ctx->get_global_mdl_request(thd))) + return 1; + if (! global_request->ticket) + { + ot_ctx->add_request(global_request); + if (thd->mdl_context.try_acquire_global_intention_exclusive_lock( + global_request)) + return 1; + if (! global_request->ticket) + goto failure; + } + } + + ot_ctx->add_request(mdl_request); + if (thd->mdl_context.try_acquire_shared_lock(mdl_request)) return 1; + +failure: if (mdl_request->ticket == NULL) { if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT) @@ -2919,8 +2951,6 @@ err_unlock: release_table_share(share); err_unlock2: pthread_mutex_unlock(&LOCK_open); - if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) - thd->mdl_context.release_lock(mdl_ticket); DBUG_RETURN(TRUE); } @@ -3713,10 +3743,33 @@ Open_table_context::Open_table_context(THD *thd) m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()), m_has_locks((thd->in_multi_stmt_transaction() || thd->mdl_context.lt_or_ha_sentinel()) && - thd->mdl_context.has_locks()) + thd->mdl_context.has_locks()), + m_global_mdl_request(NULL) {} +/** + Get MDL_request object for global intention exclusive lock which + is acquired during opening tables for statements which take + upgradable shared metadata locks. +*/ + +MDL_request *Open_table_context::get_global_mdl_request(THD *thd) +{ + if (! m_global_mdl_request) + { + char *buff; + if ((buff= (char*)thd->alloc(sizeof(MDL_request)))) + { + m_global_mdl_request= new (buff) MDL_request(); + m_global_mdl_request->init(MDL_key::GLOBAL, "", "", + MDL_INTENTION_EXCLUSIVE); + } + } + return m_global_mdl_request; +} + + /** Check if we can back-off and set back off action if we can. Otherwise report and return error. @@ -3777,6 +3830,11 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, TABLE_LIST *table) { bool result= FALSE; + /* + Remove reference to released ticket from MDL_request. + */ + if (m_global_mdl_request) + m_global_mdl_request->ticket= NULL; /* Execute the action. */ switch (m_action) { @@ -3787,11 +3845,26 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, break; case OT_DISCOVER: { + MDL_request mdl_global_request; MDL_request mdl_xlock_request(mdl_request); + + mdl_global_request.init(MDL_key::GLOBAL, "", "", + MDL_INTENTION_EXCLUSIVE); mdl_xlock_request.set_type(MDL_EXCLUSIVE); + + + if ((result= thd->mdl_context.acquire_global_intention_exclusive_lock( + &mdl_global_request))) + break; + if ((result= thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request))) + { + /* + We rely on close_thread_tables() to release global lock eventually. + */ break; + } DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); pthread_mutex_lock(&LOCK_open); @@ -3805,16 +3878,30 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, thd->warning_info->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message - thd->mdl_context.release_lock(mdl_xlock_request.ticket); + thd->mdl_context.release_transactional_locks(); break; } case OT_REPAIR: { + MDL_request mdl_global_request; MDL_request mdl_xlock_request(mdl_request); + + mdl_global_request.init(MDL_key::GLOBAL, "", "", + MDL_INTENTION_EXCLUSIVE); mdl_xlock_request.set_type(MDL_EXCLUSIVE); + + if ((result= thd->mdl_context.acquire_global_intention_exclusive_lock( + &mdl_global_request))) + break; + if ((result= thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request))) + { + /* + We rely on close_thread_tables() to release global lock eventually. + */ break; + } DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); pthread_mutex_lock(&LOCK_open); @@ -3824,7 +3911,7 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); - thd->mdl_context.release_lock(mdl_xlock_request.ticket); + thd->mdl_context.release_transactional_locks(); break; } default: @@ -3921,6 +4008,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, mdl_type != MDL_key::PROCEDURE) { ot_ctx->add_request(&rt->mdl_request); + + /* + Since we acquire only shared lock on routines we don't + need to care about global intention exclusive locks. + */ + DBUG_ASSERT(rt->mdl_request.type == MDL_SHARED); + if (thd->mdl_context.try_acquire_shared_lock(&rt->mdl_request)) DBUG_RETURN(TRUE); @@ -8784,7 +8878,7 @@ has_write_table_with_auto_increment(TABLE_LIST *tables) bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, - Open_tables_state *backup) + Open_tables_backup *backup) { Query_tables_list query_tables_list_backup; LEX *lex= thd->lex; @@ -8830,13 +8924,13 @@ error: SYNOPSIS close_system_tables() thd Thread context - backup Pointer to Open_tables_state instance which holds + backup Pointer to Open_tables_backup instance which holds information about tables which were open before we decided to access system tables. */ void -close_system_tables(THD *thd, Open_tables_state *backup) +close_system_tables(THD *thd, Open_tables_backup *backup) { close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); @@ -8887,7 +8981,7 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table) */ TABLE * open_performance_schema_table(THD *thd, TABLE_LIST *one_table, - Open_tables_state *backup) + Open_tables_backup *backup) { uint flags= ( MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY | @@ -8936,51 +9030,9 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table, @param thd The current thread @param backup [in] the context to restore. */ -void close_performance_schema_table(THD *thd, Open_tables_state *backup) +void close_performance_schema_table(THD *thd, Open_tables_backup *backup) { - bool found_old_table; - - /* - If open_performance_schema_table() fails, - this function should not be called. - */ - DBUG_ASSERT(thd->lock != NULL); - - /* - Note: - We do not create explicitly a separate transaction for the - performance table I/O, but borrow the current transaction. - lock + unlock will autocommit the change done in the - performance schema table: this is the expected result. - The current transaction should not be affected by this code. - TODO: Note that if a transactional engine is used for log tables, - this code will need to be revised, as a separate transaction - might be needed. - */ - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; - - pthread_mutex_lock(&LOCK_open); - - found_old_table= false; - /* - Note that we need to hold LOCK_open while changing the - open_tables list. Another thread may work on it. - (See: notify_thread_having_shared_lock()) - */ - while (thd->open_tables) - found_old_table|= close_thread_table(thd, &thd->open_tables); - - if (found_old_table) - broadcast_refresh(); - - pthread_mutex_unlock(&LOCK_open); - - /* We can't have an open HANDLER in the backup context. */ - DBUG_ASSERT(thd->mdl_context.lt_or_ha_sentinel() == NULL); - thd->mdl_context.release_transactional_locks(); - - thd->restore_backup_open_tables_state(backup); + close_system_tables(thd, backup); } /** diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 95c985b2c10..62de06d382c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -471,6 +471,7 @@ THD::THD() { ulong tmp; + mdl_context.init(this); /* Pass nominal parameters to init_alloc_root only to ensure that the destructor works OK in case of an error. The main_mem_root @@ -1007,7 +1008,8 @@ void THD::cleanup(void) */ DBUG_ASSERT(open_tables == NULL); /* All HANDLERs must have been closed by now. */ - DBUG_ASSERT(mdl_context.lt_or_ha_sentinel() == NULL); + DBUG_ASSERT(mdl_context.lt_or_ha_sentinel() == NULL || + global_read_lock); /* Due to the above assert, this is guaranteed to release *all* in this session. @@ -3024,19 +3026,21 @@ bool Security_context::user_matches(Security_context *them) access to mysql.proc table to find definitions of stored routines. ****************************************************************************/ -void THD::reset_n_backup_open_tables_state(Open_tables_state *backup) +void THD::reset_n_backup_open_tables_state(Open_tables_backup *backup) { DBUG_ENTER("reset_n_backup_open_tables_state"); backup->set_open_tables_state(this); + backup->mdl_system_tables_svp= mdl_context.mdl_savepoint(); reset_open_tables_state(this); state_flags|= Open_tables_state::BACKUPS_AVAIL; DBUG_VOID_RETURN; } -void THD::restore_backup_open_tables_state(Open_tables_state *backup) +void THD::restore_backup_open_tables_state(Open_tables_backup *backup) { DBUG_ENTER("restore_backup_open_tables_state"); + mdl_context.rollback_to_savepoint(backup->mdl_system_tables_svp); /* Before we will throw away current open tables state we want to be sure that it was properly cleaned up. @@ -3046,7 +3050,6 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) lock == 0 && locked_tables_mode == LTM_NONE && m_reprepare_observer == NULL); - mdl_context.destroy(); set_open_tables_state(backup); DBUG_VOID_RETURN; diff --git a/sql/sql_class.h b/sql/sql_class.h index 5654dcb07a6..dce06f7e0c5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -978,9 +978,6 @@ public: Flags with information about the open tables state. */ uint state_flags; - - MDL_context mdl_context; - /** This constructor initializes Open_tables_state instance which can only be used as backup storage. To prepare Open_tables_state instance for @@ -1010,21 +1007,29 @@ public: locked_tables_mode= LTM_NONE; state_flags= 0U; m_reprepare_observer= NULL; - mdl_context.init(thd); - } - void enter_locked_tables_mode(enum_locked_tables_mode mode_arg) - { - DBUG_ASSERT(locked_tables_mode == LTM_NONE); - mdl_context.set_lt_or_ha_sentinel(); - locked_tables_mode= mode_arg; - } - void leave_locked_tables_mode() - { - locked_tables_mode= LTM_NONE; - mdl_context.clear_lt_or_ha_sentinel(); } }; + +/** + Storage for backup of Open_tables_state. Must + be used only to open system tables (TABLE_CATEGORY_SYSTEM + and TABLE_CATEGORY_LOG). +*/ + +class Open_tables_backup: public Open_tables_state +{ +public: + /** + When we backup the open tables state to open a system + table or tables, points at the last metadata lock + acquired before the backup. Is used to release + metadata locks on system tables after they are + no longer used. + */ + MDL_ticket *mdl_system_tables_svp; +}; + /** @class Sub_statement_state @brief Used to save context when executing a function or trigger @@ -1308,6 +1313,9 @@ public: { return m_start_of_statement_svp; } + + MDL_request *get_global_mdl_request(THD *thd); + private: /** List of requests for all locks taken so far. Used for waiting on locks. */ MDL_request_list m_mdl_requests; @@ -1320,6 +1328,11 @@ private: and we can't safely do back-off (and release them). */ bool m_has_locks; + /** + Request object for global intention exclusive lock which is acquired during + opening tables for statements which take upgradable shared metadata locks. + */ + MDL_request *m_global_mdl_request; }; @@ -1426,6 +1439,8 @@ class THD :public Statement, public Open_tables_state { public: + MDL_context mdl_context; + /* Used to execute base64 coded binlog events in MySQL server */ Relay_log_info* rli_fake; @@ -2314,8 +2329,8 @@ public: void set_status_var_init(); bool is_context_analysis_only() { return stmt_arena->is_stmt_prepare() || lex->view_prepare_mode; } - void reset_n_backup_open_tables_state(Open_tables_state *backup); - void restore_backup_open_tables_state(Open_tables_state *backup); + void reset_n_backup_open_tables_state(Open_tables_backup *backup); + void restore_backup_open_tables_state(Open_tables_backup *backup); void reset_sub_statement_state(Sub_statement_state *backup, uint new_state); void restore_sub_statement_state(Sub_statement_state *backup); void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); @@ -2567,6 +2582,19 @@ public: Protected with LOCK_thd_data mutex. */ void set_query(char *query_arg, uint32 query_length_arg); + void enter_locked_tables_mode(enum_locked_tables_mode mode_arg) + { + DBUG_ASSERT(locked_tables_mode == LTM_NONE); + DBUG_ASSERT(! mdl_context.lt_or_ha_sentinel() || + mdl_context.is_global_lock_owner(MDL_SHARED)); + mdl_context.set_lt_or_ha_sentinel(); + locked_tables_mode= mode_arg; + } + void leave_locked_tables_mode() + { + locked_tables_mode= LTM_NONE; + mdl_context.clear_lt_or_ha_sentinel(); + } private: /** The current internal error handler for this thread, or NULL. */ Internal_error_handler *m_internal_handler; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 26478b31290..228c001f71b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error= TRUE; uint path_length; - MDL_request mdl_request; + MDL_request mdl_global_request, mdl_request; /* Is set if we're under LOCK TABLES, and used to downgrade the exclusive lock after the @@ -1207,10 +1207,21 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) the table can be re-created as an empty table with TRUNCATE TABLE, even if the data or index files have become corrupted. */ + + mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); - if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) + if (thd->mdl_context.acquire_global_intention_exclusive_lock( + &mdl_global_request)) DBUG_RETURN(TRUE); + if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) + { + /* + We rely on that close_thread_tables() to release global lock + in this case. + */ + DBUG_RETURN(TRUE); + } has_mdl_lock= TRUE; pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, @@ -1250,7 +1261,7 @@ end: my_ok(thd); // This should return record count } if (has_mdl_lock) - thd->mdl_context.release_lock(mdl_request.ticket); + thd->mdl_context.release_transactional_locks(); if (mdl_ticket) mdl_ticket->downgrade_exclusive_lock(); } diff --git a/sql/sql_help.cc b/sql/sql_help.cc index af67db45b36..e9b15e07e9d 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -655,7 +655,12 @@ bool mysqld_help(THD *thd, const char *mask) tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql"; init_mdl_requests(tables); - Open_tables_state open_tables_state_backup; + /* + HELP must be available under LOCK TABLES. + Reset and backup the current open tables state to + make it possible. + */ + Open_tables_backup open_tables_state_backup; if (open_system_tables_for_read(thd, tables, &open_tables_state_backup)) goto error2; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 486cb9af288..397674471c5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6502,7 +6502,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, DBUG_ASSERT(!thd || thd->locked_tables_mode || !thd->mdl_context.has_locks() || - thd->handler_tables_hash.records); + thd->handler_tables_hash.records || + thd->global_read_lock); /* Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too diff --git a/sql/sql_plist.h b/sql/sql_plist.h index 8f2aee6bd5f..eb239a63467 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -18,7 +18,8 @@ #include -template class I_P_List_iterator; +template class I_P_List_iterator; +class I_P_List_null_counter; /** @@ -47,10 +48,14 @@ template class I_P_List_iterator; return &el->prev; } }; + @param C Policy class specifying how counting of elements in the list + should be done. Instance of this class is also used as a place + where information about number of list elements is stored. + @sa I_P_List_null_counter, I_P_List_counter */ -template -class I_P_List +template +class I_P_List : public C { T *first; @@ -61,7 +66,7 @@ class I_P_List */ public: I_P_List() : first(NULL) { }; - inline void empty() { first= NULL; } + inline void empty() { first= NULL; C::reset(); } inline bool is_empty() const { return (first == NULL); } inline void push_front(T* a) { @@ -70,6 +75,7 @@ public: *B::prev_ptr(first)= B::next_ptr(a); first= a; *B::prev_ptr(a)= &first; + C::inc(); } inline void push_back(T *a) { @@ -107,21 +113,23 @@ public: if (next) *B::prev_ptr(next)= *B::prev_ptr(a); **B::prev_ptr(a)= next; + C::dec(); } inline T* front() { return first; } inline const T *front() const { return first; } - void swap(I_P_List &rhs) + void swap(I_P_List &rhs) { swap_variables(T *, first, rhs.first); if (first) *B::prev_ptr(first)= &first; if (rhs.first) *B::prev_ptr(rhs.first)= &rhs.first; + C::swap(rhs); } #ifndef _lint - friend class I_P_List_iterator; + friend class I_P_List_iterator; #endif - typedef I_P_List_iterator Iterator; + typedef I_P_List_iterator Iterator; }; @@ -129,15 +137,15 @@ public: Iterator for I_P_List. */ -template +template class I_P_List_iterator { - const I_P_List *list; + const I_P_List *list; T *current; public: - I_P_List_iterator(const I_P_List &a) : list(&a), current(a.first) {} - I_P_List_iterator(const I_P_List &a, T* current_arg) : list(&a), current(current_arg) {} - inline void init(I_P_List &a) + I_P_List_iterator(const I_P_List &a) : list(&a), current(a.first) {} + I_P_List_iterator(const I_P_List &a, T* current_arg) : list(&a), current(current_arg) {} + inline void init(const I_P_List &a) { list= &a; current= a.first; @@ -160,4 +168,39 @@ public: } }; + +/** + Element counting policy class for I_P_List to be used in + cases when no element counting should be done. +*/ + +class I_P_List_null_counter +{ +protected: + void reset() {} + void inc() {} + void dec() {} + void swap(I_P_List_null_counter &rhs) {} +}; + + +/** + Element counting policy class for I_P_List which provides + basic element counting. +*/ + +class I_P_List_counter +{ + uint m_counter; +protected: + I_P_List_counter() : m_counter (0) {} + void reset() {m_counter= 0;} + void inc() {m_counter++;} + void dec() {m_counter--;} + void swap(I_P_List_counter &rhs) + { swap_variables(uint, m_counter, rhs.m_counter); } +public: + uint elements() const { return m_counter; } +}; + #endif diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e9d1426b3e3..278e0c1445f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2871,7 +2871,7 @@ make_table_name_list(THD *thd, List *table_names, LEX *lex, due to metadata locks, so to avoid them we should not wait in case if conflicting lock is present. - @param[in] open_tables_state_backup pointer to Open_tables_state object + @param[in] open_tables_state_backup pointer to Open_tables_backup object which is used to save|restore original status of variables related to open tables state @@ -2885,7 +2885,7 @@ static int fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, ST_SCHEMA_TABLE *schema_table, bool can_deadlock, - Open_tables_state *open_tables_state_backup) + Open_tables_backup *open_tables_state_backup) { LEX *lex= thd->lex; bool res; @@ -2941,7 +2941,8 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, table, res, db_name, table_name)); thd->temporary_tables= 0; - close_tables_for_reopen(thd, &show_table_list, NULL); + close_tables_for_reopen(thd, &show_table_list, + open_tables_state_backup->mdl_system_tables_svp); DBUG_RETURN(error); } @@ -3236,8 +3237,12 @@ end_share: end_unlock: pthread_mutex_unlock(&LOCK_open); + /* + Don't release the MDL lock, it can be part of a transaction. + If it is not, it will be released by the call to + MDL_context::rollback_to_savepoint() in the caller. + */ - thd->mdl_context.release_lock(table_list.mdl_request.ticket); thd->clear_error(); return res; } @@ -3281,7 +3286,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) COND *partial_cond= 0; uint derived_tables= lex->derived_tables; int error= 1; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; bool save_view_prepare_mode= lex->view_prepare_mode; Query_tables_list query_tables_list_backup; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -3500,7 +3505,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) res= schema_table->process_table(thd, show_table_list, table, res, &orig_db_name, &tmp_lex_string); - close_tables_for_reopen(thd, &show_table_list, NULL); + close_tables_for_reopen(thd, &show_table_list, + open_tables_state_backup.mdl_system_tables_svp); } DBUG_ASSERT(!lex->query_tables_own_last); if (res) @@ -4302,7 +4308,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) TABLE *table= tables->table; bool full_access; char definer[USER_HOST_BUFF_SIZE]; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; DBUG_ENTER("fill_schema_proc"); strxmov(definer, thd->security_ctx->priv_user, "@", diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 30d6efff7ec..e8c2af4c87b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2207,22 +2207,26 @@ err: locked. Additional check for 'non_temp_tables_count' is to avoid leaving LOCK TABLES mode if we have dropped only temporary tables. */ - if (thd->locked_tables_mode && - thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) + if (! thd->locked_tables_mode) + unlock_table_names(thd); + else { - thd->locked_tables_list.unlock_locked_tables(thd); - goto end; - } - for (table= tables; table; table= table->next_local) - { - if (table->mdl_request.ticket) + if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) { - /* - Under LOCK TABLES we may have several instances of table open - and locked and therefore have to remove several metadata lock - requests associated with them. - */ - thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket); + thd->locked_tables_list.unlock_locked_tables(thd); + goto end; + } + for (table= tables; table; table= table->next_local) + { + if (table->mdl_request.ticket) + { + /* + Under LOCK TABLES we may have several instances of table open + and locked and therefore have to remove several metadata lock + requests associated with them. + */ + thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket); + } } } } @@ -4349,6 +4353,14 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (!(table= table_list->table)) { + /* + If the table didn't exist, we have a shared metadata lock + on it that is left from mysql_admin_table()'s attempt to + open it. Release the shared metadata lock before trying to + acquire the exclusive lock to satisfy MDL asserts and avoid + deadlocks. + */ + thd->mdl_context.release_transactional_locks(); /* Attempt to do full-blown table open in mysql_admin_table() has failed. Let us try to open at least a .FRM for this table. @@ -4360,6 +4372,14 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, table_list->mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + + MDL_request mdl_global_request; + mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); + + if (thd->mdl_context.acquire_global_intention_exclusive_lock( + &mdl_global_request)) + DBUG_RETURN(0); + if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request)) DBUG_RETURN(0); has_mdl_lock= TRUE; @@ -4491,7 +4511,7 @@ end: } /* In case of a temporary table there will be no metadata lock. */ if (error && has_mdl_lock) - thd->mdl_context.release_lock(table_list->mdl_request.ticket); + thd->mdl_context.release_transactional_locks(); DBUG_RETURN(error); } @@ -6544,6 +6564,13 @@ view_err: { target_mdl_request.init(MDL_key::TABLE, new_db, new_name, MDL_EXCLUSIVE); + /* + Global intention exclusive lock must have been already acquired when + table to be altered was open, so there is no need to do it here. + */ + DBUG_ASSERT(thd-> + mdl_context.is_global_lock_owner(MDL_INTENTION_EXCLUSIVE)); + if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) DBUG_RETURN(TRUE); if (target_mdl_request.ticket == NULL) diff --git a/sql/tztime.cc b/sql/tztime.cc index 2ec641071ee..aa9780754d7 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1563,7 +1563,6 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) { THD *thd; TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT]; - Open_tables_state open_tables_state_backup; TABLE *table; Tz_names_entry *tmp_tzname; my_bool return_val= 1; @@ -1642,7 +1641,8 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) We need to open only mysql.time_zone_leap_second, but we try to open all time zone tables to see if they exist. */ - if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup)) + if (open_and_lock_tables_derived(thd, tz_tables, FALSE, + MYSQL_LOCK_IGNORE_FLUSH)) { sql_print_warning("Can't open and lock time zone table: %s " "trying to live without them", thd->stmt_da->message()); @@ -1651,6 +1651,9 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) goto end_with_setting_default_tz; } + for (TABLE_LIST *tl= tz_tables; tl; tl= tl->next_global) + tl->table->use_all_columns(); + /* Now we are going to load leap seconds descriptions that are shared between all time zones that use them. We are using index for getting @@ -1739,7 +1742,8 @@ end_with_close: if (time_zone_tables_exist) { thd->version--; /* Force close to free memory */ - close_system_tables(thd, &open_tables_state_backup); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); } end_with_cleanup: @@ -2293,7 +2297,7 @@ my_tz_find(THD *thd, const String *name) else if (time_zone_tables_exist) { TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT]; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; tz_init_table_list(tz_tables); init_mdl_requests(tz_tables); From a47998cccc1e35c18f422c1ee6c6a8f48e78b533 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Thu, 21 Jan 2010 17:57:57 -0700 Subject: [PATCH 280/466] Fixed merge problem (naming of CRYPTO_dynlock_value::lock in the tests) --- mysql-test/suite/perfschema/r/dml_setup_instruments.result | 3 +-- mysql-test/suite/perfschema/t/dml_setup_instruments.test | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/perfschema/r/dml_setup_instruments.result b/mysql-test/suite/perfschema/r/dml_setup_instruments.result index d680f8960d5..53d15e0a510 100644 --- a/mysql-test/suite/perfschema/r/dml_setup_instruments.result +++ b/mysql-test/suite/perfschema/r/dml_setup_instruments.result @@ -16,10 +16,9 @@ wait/synch/mutex/sql/LOCK_delayed_insert YES YES wait/synch/mutex/sql/LOCK_delayed_status YES YES select * from performance_schema.SETUP_INSTRUMENTS where name like 'Wait/Synch/Rwlock/sql/%' - and name not in ('wait/synch/mutex/sql/CRYPTO_dynlock_value::lock') + and name not in ('wait/synch/rwlock/sql/CRYPTO_dynlock_value::lock') order by name limit 10; NAME ENABLED TIMED -wait/synch/rwlock/sql/CRYPTO_dynlock_value::lock YES YES wait/synch/rwlock/sql/LOCK_dboptions YES YES wait/synch/rwlock/sql/LOCK_grant YES YES wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES diff --git a/mysql-test/suite/perfschema/t/dml_setup_instruments.test b/mysql-test/suite/perfschema/t/dml_setup_instruments.test index c6631e42612..b82cde15fb5 100644 --- a/mysql-test/suite/perfschema/t/dml_setup_instruments.test +++ b/mysql-test/suite/perfschema/t/dml_setup_instruments.test @@ -40,7 +40,7 @@ select * from performance_schema.SETUP_INSTRUMENTS select * from performance_schema.SETUP_INSTRUMENTS where name like 'Wait/Synch/Rwlock/sql/%' - and name not in ('wait/synch/mutex/sql/CRYPTO_dynlock_value::lock') + and name not in ('wait/synch/rwlock/sql/CRYPTO_dynlock_value::lock') order by name limit 10; # COND_handler_count is dependent on the build (Windows only) From f895fb60604ce7b1a3240ae0d6ee957481e3637f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 22 Jan 2010 02:21:54 +0100 Subject: [PATCH 281/466] cherrypick a followup fix from marc --- mysql-test/suite/perfschema/r/dml_setup_instruments.result | 3 +-- mysql-test/suite/perfschema/t/dml_setup_instruments.test | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/perfschema/r/dml_setup_instruments.result b/mysql-test/suite/perfschema/r/dml_setup_instruments.result index d680f8960d5..53d15e0a510 100644 --- a/mysql-test/suite/perfschema/r/dml_setup_instruments.result +++ b/mysql-test/suite/perfschema/r/dml_setup_instruments.result @@ -16,10 +16,9 @@ wait/synch/mutex/sql/LOCK_delayed_insert YES YES wait/synch/mutex/sql/LOCK_delayed_status YES YES select * from performance_schema.SETUP_INSTRUMENTS where name like 'Wait/Synch/Rwlock/sql/%' - and name not in ('wait/synch/mutex/sql/CRYPTO_dynlock_value::lock') + and name not in ('wait/synch/rwlock/sql/CRYPTO_dynlock_value::lock') order by name limit 10; NAME ENABLED TIMED -wait/synch/rwlock/sql/CRYPTO_dynlock_value::lock YES YES wait/synch/rwlock/sql/LOCK_dboptions YES YES wait/synch/rwlock/sql/LOCK_grant YES YES wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES diff --git a/mysql-test/suite/perfschema/t/dml_setup_instruments.test b/mysql-test/suite/perfschema/t/dml_setup_instruments.test index c6631e42612..b82cde15fb5 100644 --- a/mysql-test/suite/perfschema/t/dml_setup_instruments.test +++ b/mysql-test/suite/perfschema/t/dml_setup_instruments.test @@ -40,7 +40,7 @@ select * from performance_schema.SETUP_INSTRUMENTS select * from performance_schema.SETUP_INSTRUMENTS where name like 'Wait/Synch/Rwlock/sql/%' - and name not in ('wait/synch/mutex/sql/CRYPTO_dynlock_value::lock') + and name not in ('wait/synch/rwlock/sql/CRYPTO_dynlock_value::lock') order by name limit 10; # COND_handler_count is dependent on the build (Windows only) From bcc97357f107115c9b80338b11e7a45a646cbfc7 Mon Sep 17 00:00:00 2001 From: Bernd Ocklin Date: Fri, 22 Jan 2010 14:12:17 +0100 Subject: [PATCH 282/466] fix LD_LIBRARY_PATH (bug#50540) --- mysql-test/mysql-test-run.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d0f7d2b2a75..2805b156487 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1787,11 +1787,11 @@ sub environment_setup { { push(@ld_library_paths, "$basedir/libmysql/.libs/", "$basedir/libmysql_r/.libs/", - "$basedir/zlib.libs/"); + "$basedir/zlib/.libs/"); } else { - push(@ld_library_paths, "$basedir/lib"); + push(@ld_library_paths, "$basedir/lib", "$basedir/lib/mysql"); } } From 8976595d827e7c29000d02b2f4431d4177d8e427 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 22 Jan 2010 17:08:49 +0100 Subject: [PATCH 283/466] Backport from mysql-6.0-codebase of: ------------------------------------------------------------ revno: 2630.22.42 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Fri 2008-10-17 14:36:55 +0400 message: Update old wording (table engine -> storage engine). --- sql/share/errmsg.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 51bb83f07b9..31e630d5fea 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5015,7 +5015,7 @@ ER_WARN_HOSTNAME_WONT_WORK por "MySQL foi inicializado em modo --skip-name-resolve. Voc necesita reincializ-lo sem esta opo para este grant funcionar" spa "MySQL esta inicializado en modo --skip-name-resolve. Usted necesita reinicializarlo sin esta opcin para este derecho funcionar" ER_UNKNOWN_STORAGE_ENGINE 42000 - eng "Unknown table engine '%s'" + eng "Unknown storage engine '%s'" ger "Unbekannte Speicher-Engine '%s'" por "Motor de tabela desconhecido '%s'" spa "Desconocido motor de tabla '%s'" From 80f57fb2524bfd451c603bce92ad061d85b42aae Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 22 Jan 2010 20:23:45 +0100 Subject: [PATCH 284/466] Replace another abort() under DBUG_EXECUTE_IF with DBUG_ABORT to avoid popups on Windows. --- sql/log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/log.cc b/sql/log.cc index 385b067d663..fdc763df0de 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3532,7 +3532,7 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log, break; } - DBUG_EXECUTE_IF("crash_purge_before_update_index", abort();); + DBUG_EXECUTE_IF("crash_purge_before_update_index", DBUG_ABORT();); if ((error= sync_purge_index_file())) { From ad3ded49906838d9e49bf914d95cf947d49a6b34 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Fri, 22 Jan 2010 17:15:16 -0700 Subject: [PATCH 285/466] Bug#50478 perfschema.tampered_perfschema_table1 fails sporadically on Windows and Solaris Reviewed every call to my_error() using the va_args parameters, to make sure the arguments type are ok. Fixed the broken calls to my_error() to pass a strings as 'char *', not LEX_STRING. --- storage/perfschema/pfs_engine_table.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/perfschema/pfs_engine_table.cc b/storage/perfschema/pfs_engine_table.cc index 4190094b52b..8fe51e8c410 100644 --- a/storage/perfschema/pfs_engine_table.cc +++ b/storage/perfschema/pfs_engine_table.cc @@ -219,7 +219,7 @@ int PFS_engine_table::read_row(TABLE *table, if (! m_share_ptr->m_checked) { my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0), - PERFORMANCE_SCHEMA_str.str, m_share_ptr->m_name); + PERFORMANCE_SCHEMA_str.str, m_share_ptr->m_name.str); return HA_ERR_TABLE_NEEDS_UPGRADE; } @@ -256,7 +256,7 @@ int PFS_engine_table::update_row(TABLE *table, if (! m_share_ptr->m_checked) { my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0), - PERFORMANCE_SCHEMA_str.str, m_share_ptr->m_name); + PERFORMANCE_SCHEMA_str.str, m_share_ptr->m_name.str); return HA_ERR_TABLE_NEEDS_UPGRADE; } From 7146864986cb5228e08c7c6782bba2471299fead Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Fri, 22 Jan 2010 18:06:13 -0700 Subject: [PATCH 286/466] Removing perfschema.tampered_perfschema_table1 from the experimental tests --- mysql-test/collections/default.experimental | 3 --- 1 file changed, 3 deletions(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 34cf1f964b8..d3a0daf78f8 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -16,9 +16,6 @@ main.plugin # Bug#47146 Linking problem with exampl main.signal_demo3 @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun main.sp @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun -perfschema.tampered_perfschema_table1 @windows # Bug#50478 2010-01-20 alik perfschema.tampered_perfschema_table1 fails sporadically on Windows and Solaris -perfschema.tampered_perfschema_table1 @solaris # Bug#50478 2010-01-20 alik perfschema.tampered_perfschema_table1 fails sporadically on Windows and Solaris - rpl.rpl_get_master_version_and_clock* # Bug#49191 2009-12-01 Daogang rpl_get_master_version_and_clock failed on PB2: COM_REGISTER_SLAVE failed rpl.rpl_heartbeat_basic # BUG#43828 2009-10-22 luis fails sporadically rpl.rpl_heartbeat_2slaves # BUG#43828 2009-10-22 luis fails sporadically From dba07c73960eba1dc1e961a22a07eb6e1998e532 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Fri, 22 Jan 2010 19:00:19 -0700 Subject: [PATCH 287/466] Bug#11714 Non-sensical ALTER TABLE ADD CONSTRAINT allowed Bug#35578 Parser allows useless/illegal CREATE TABLE syntax Bug#38696 CREATE TABLE ... CHECK ... allows illegal syntax Backport from 6.0 to mysql-next-mr. --- mysql-test/r/constraints.result | 18 +++++++++++++- mysql-test/t/constraints.test | 44 ++++++++++++++++++++++++++++++++- sql/sql_yacc.yy | 10 +++----- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/constraints.result b/mysql-test/r/constraints.result index 116efe429d5..46a718e4c42 100644 --- a/mysql-test/r/constraints.result +++ b/mysql-test/r/constraints.result @@ -3,7 +3,7 @@ create table t1 (a int check (a>0)); insert into t1 values (1); insert into t1 values (0); drop table t1; -create table t1 (a int ,b int, check a>b); +create table t1 (a int, b int, check (a>b)); insert into t1 values (1,0); insert into t1 values (0,1); drop table t1; @@ -27,3 +27,19 @@ t1 CREATE TABLE `t1` ( UNIQUE KEY `key_2` (`a`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; +drop table if exists t_illegal; +create table t_illegal (a int, b int, check a>b); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a>b)' at line 1 +create table t_illegal (a int, b int, constraint abc check a>b); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a>b)' at line 1 +create table t_illegal (a int, b int, constraint abc); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1 +drop table if exists t_11714; +create table t_11714(a int, b int); +alter table t_11714 add constraint cons1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 +drop table t_11714; +CREATE TABLE t_illegal (col_1 INT CHECK something (whatever)); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'something (whatever))' at line 1 +CREATE TABLE t_illegal (col_1 INT CHECK something); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'something)' at line 1 diff --git a/mysql-test/t/constraints.test b/mysql-test/t/constraints.test index ed268ab5846..70a95e5f16e 100644 --- a/mysql-test/t/constraints.test +++ b/mysql-test/t/constraints.test @@ -10,7 +10,7 @@ create table t1 (a int check (a>0)); insert into t1 values (1); insert into t1 values (0); drop table t1; -create table t1 (a int ,b int, check a>b); +create table t1 (a int, b int, check (a>b)); insert into t1 values (1,0); insert into t1 values (0,1); drop table t1; @@ -29,3 +29,45 @@ show create table t1; drop table t1; # End of 4.1 tests + +# +# Bug#35578 (Parser allows useless/illegal CREATE TABLE syntax) +# + +--disable_warnings +drop table if exists t_illegal; +--enable_warnings + +--error ER_PARSE_ERROR +create table t_illegal (a int, b int, check a>b); + +--error ER_PARSE_ERROR +create table t_illegal (a int, b int, constraint abc check a>b); + +--error ER_PARSE_ERROR +create table t_illegal (a int, b int, constraint abc); + +# +# Bug#11714 (Non-sensical ALTER TABLE ADD CONSTRAINT allowed) +# + +--disable_warnings +drop table if exists t_11714; +--enable_warnings + +create table t_11714(a int, b int); + +--error ER_PARSE_ERROR +alter table t_11714 add constraint cons1; + +drop table t_11714; + +# +# Bug#38696 (CREATE TABLE ... CHECK ... allows illegal syntax) + +--error ER_PARSE_ERROR +CREATE TABLE t_illegal (col_1 INT CHECK something (whatever)); + +--error ER_PARSE_ERROR +CREATE TABLE t_illegal (col_1 INT CHECK something); + diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ab128a9b701..aebf80e340f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -765,10 +765,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 172 shift/reduce conflicts. + Currently there are 169 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 172 +%expect 169 /* Comments for TOKENS. @@ -5282,10 +5282,6 @@ key_def: /* Only used for ALTER TABLE. Ignored otherwise. */ lex->alter_info.flags|= ALTER_FOREIGN_KEY; } - | constraint opt_check_constraint - { - Lex->col_list.empty(); /* Alloced by sql_alloc */ - } | opt_constraint check_constraint { Lex->col_list.empty(); /* Alloced by sql_alloc */ @@ -5298,7 +5294,7 @@ opt_check_constraint: ; check_constraint: - CHECK_SYM expr + CHECK_SYM '(' expr ')' ; opt_constraint: From c827968a2df5ad35e64cea0116abf862776cef26 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 24 Jan 2010 16:23:16 +0100 Subject: [PATCH 288/466] Handle different installation layouts. using cmake option INSTALL_LAYOUT=STANDALONE would produce the layout as in tar.gz or zip packages. INSTALL_LAYOUT=UNIX will produce unixish install layout (with mysqld being in sbin subdirectory , libs in lib/mysql etc). This layout is used for RPM packages. Subtle differences in both packages unfortunately lead to the need to recompile MySQL to use with other package type - as otherwise for example default plugins or data directories would be wrong set. There are numerous other variables that allow fine-tuning packaging layout. (INSTALL_BINDIR, INSTALL_LIBDIR , INSTALL_PLUGINDIR etc). This options are different from autotools as they do not expect full paths to directories, but only subdirectory of CMAKE_INSTALL_PREFIX. There are 2 special options that expect full directory paths - MYSQL_DATADIR that defines default MYSQL data directory (autotools equivalent is --localstatedir) - SYSCONFDIR can be added to search my.cnf search path (autotools equivalent is --sysconfdir) --- CMakeLists.txt | 16 ++- cmake/Makefile.am | 3 +- cmake/configure.pl | 7 +- cmake/install_layout.cmake | 128 +++++++++++++++++++ cmake/libutils.cmake | 2 +- cmake/mysql_add_executable.cmake | 7 +- cmake/plugin.cmake | 3 +- config.h.cmake | 2 + include/CMakeLists.txt | 5 +- libmysql/CMakeLists.txt | 4 +- man/CMakeLists.txt | 4 +- mysql-test/CMakeLists.txt | 7 +- mysql-test/lib/My/SafeProcess/CMakeLists.txt | 2 +- scripts/CMakeLists.txt | 76 +++++------ scripts/mysqld_safe.sh | 7 + sql-bench/CMakeLists.txt | 14 +- sql/CMakeLists.txt | 6 +- sql/share/CMakeLists.txt | 6 +- support-files/CMakeLists.txt | 49 ++++--- 19 files changed, 256 insertions(+), 92 deletions(-) create mode 100755 cmake/install_layout.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 752e66ae746..668675d4444 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ INCLUDE(libutils) INCLUDE(dtrace) INCLUDE(plugin) INCLUDE(install_macros) +INCLUDE(install_layout) INCLUDE(mysql_add_executable) # Handle options @@ -165,12 +166,17 @@ IF(WIN32) SET(SHAREDIR share) ELSE() SET(DEFAULT_MYSQL_HOME ${CMAKE_INSTALL_PREFIX}) - SET(SHAREDIR ${DEFAULT_MYSQL_HOME}/share) + SET(SHAREDIR ${DEFAULT_MYSQL_HOME}/${INSTALL_MYSQLSHAREDIR}) ENDIF() SET(DEFAULT_BASEDIR "${DEFAULT_MYSQL_HOME}") -SET(MYSQL_DATADIR "${DEFAULT_MYSQL_HOME}/data") +SET(MYSQL_DATADIR "${DEFAULT_MYSQL_HOME}/${INSTALL_MYSQLDATADIR}" CACHE PATH + "default MySQL data directory") SET(DEFAULT_CHARSET_HOME "${DEFAULT_MYSQL_HOME}") +SET(PLUGINDIR "${DEFAULT_MYSQL_HOME}/${INSTALL_PLUGINDIR}") +IF(SYSCONFDIR) + SET(DEFAULT_SYSCONFDIR "${SYSCONFDIR}") +ENDIF() # Optionally read user configuration, generated by configure.js. @@ -255,11 +261,11 @@ ELSE() SET(CPACK_GENERATOR "TGZ") ENDIF() INCLUDE(CPack) -INSTALL(FILES COPYING EXCEPTIONS-CLIENT README DESTINATION .) +INSTALL(FILES COPYING EXCEPTIONS-CLIENT README DESTINATION ${INSTALL_DOCREADMEDIR}) IF(UNIX) - INSTALL(FILES Docs/INSTALL-BINARY DESTINATION .) + INSTALL(FILES Docs/INSTALL-BINARY DESTINATION ${INSTALL_DOCREADMEDIR}) 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") MARK_AS_ADVANCED(MYSQL_DOCS_LOCATION) -INSTALL(DIRECTORY Docs DESTINATION .) +INSTALL(DIRECTORY Docs/ DESTINATION ${INSTALL_DOCDIR}) diff --git a/cmake/Makefile.am b/cmake/Makefile.am index d072178d1b9..600cd442f6f 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -21,4 +21,5 @@ EXTRA_DIST = \ merge_archives_unix.cmake.in \ dtrace_prelink.cmake \ versioninfo.rc.in \ - mysql_add_executable.cmake + mysql_add_executable.cmake \ + install_layout.cmake diff --git a/cmake/configure.pl b/cmake/configure.pl index 3768b046530..f52435e2cde 100644 --- a/cmake/configure.pl +++ b/cmake/configure.pl @@ -11,7 +11,6 @@ my $srcdir = dirname(dirname(abs_path($0))); foreach my $option (@ARGV) { - if (substr ($option, 0, 2) == "--") { $option = substr($option, 2); @@ -83,6 +82,12 @@ foreach my $option (@ARGV) $cmakeargs = $cmakeargs." -DWITH_CHARSETS=complex"; next; } + if ($option =~ /localstatedir=/) + { + $cmakeargs = $cmakeargs." -DMYSQL_DATADIR=".substr($option,14); + next; + } + $option = uc($option); $option =~ s/-/_/g; $cmakeargs = $cmakeargs." -D".$option."=1"; diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake new file mode 100755 index 00000000000..b1a285a0695 --- /dev/null +++ b/cmake/install_layout.cmake @@ -0,0 +1,128 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# The purpose of this file is to set the default installation layout. +# Currently, there are 2 different installation layouts , +# one is used in tar.gz packages (Windows zip is about the same), another one +# in RPMs. + +# There are currently 2 layouts defines, named STANDALONE (tar.gz layout) +# and UNIX (rpm layout). To force a directory layout when invoking cmake use +# -DINSTALL_LAYOUT=[STANDALONE|UNIX]. +# This wil use a predefined layout. There is a possibility to further fine-tune +# installation directories. Several variables are can be overwritten +# +# - INSTALL_BINDIR (directory with client executables and Unix shell scripts) +# - INSTALL_SBINDIR (directory with mysqld) +# - INSTALL_LIBDIR (directory with client end embedded libraries) +# - INSTALL_PLUGINDIR (directory for plugins) +# - INSTALL_INCLUDEDIR (directory for MySQL headers) +# - INSTALL_DOCDIR (documentation) +# - INSTALL_MANDIR (man pages) +# - INSTALL_SCRIPTDIR (several scripts, rarely used) +# - INSTALL_MYSQLSHAREDIR (MySQL character sets and localized error messages) +# - INSTALL_SHAREDIR (location of aclocal/mysql.m4) +# - INSTALL_SQLBENCHDIR (sql-bench) +# - INSTALL_MYSQLTESTDIR (mysql-test) +# - INSTALL_DOCREADMEDIR (readme and similar) +# - INSTALL_SUPPORTFILESDIR (used only in standalone installer) + +# Default installation layout on Unix is UNIX (kent wants it so) +IF(NOT INSTALL_LAYOUT) + IF(WIN32) + SET(DEFAULT_INSTALL_LAYOUT "STANDALONE") + ELSE() + SET(DEFAULT_INSTALL_LAYOUT "UNIX") + ENDIF() +ENDIF() + +SET(INSTALL_LAYOUT "${DEFAULT_INSTALL_LAYOUT}" +CACHE STRING "Installation directory layout. Options are: STANDALONE (as in zip or tar.gz installer) or UNIX") + +IF(NOT INSTALL_LAYOUT MATCHES "STANDALONE") + IF(NOT INSTALL_LAYOUT MATCHES "UNIX") + SET(INSTALL_LAYOUT "${DEFAULT_INSTALL_LAYOUT}") + ENDIF() +ENDIF() + +IF(UNIX) + IF(INSTALL_LAYOUT MATCHES "UNIX") + SET(default_prefix "/usr") + ELSE() + SET(default_prefix "/usr/local/mysql") + ENDIF() + IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + SET(CMAKE_INSTALL_PREFIX ${default_prefix} + CACHE PATH "install prefix" FORCE) + ENDIF() + SET(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc" + CACHE PATH "config directory (for my.cnf)") + MARK_AS_ADVANCED(SYSCONFDIR) +ENDIF() + + + + # STANDALONE layout + SET(INSTALL_BINDIR_STANDALONE "bin") + SET(INSTALL_SBINDIR_STANDALONE "bin") + SET(INSTALL_LIBDIR_STANDALONE "lib") + SET(INSTALL_INCLUDEDIR_STANDALONE "include") + SET(INSTALL_PLUGINDIR_STANDALONE "lib/plugin") + SET(INSTALL_DOCDIR_STANDALONE "doc") + SET(INSTALL_MANDIR_STANDALONE "man") + SET(INSTALL_MYSQLSHAREDIR_STANDALONE "share") + SET(INSTALL_SHAREDIR_STANDALONE "share") + SET(INSTALL_SCRIPTDIR_STANDALONE "scripts") + SET(INSTALL_MYSQLTESTDIR_STANDALONE "mysql-test") + SET(INSTALL_SQLBENCHROOTDIR_STANDALONE ".") + SET(INSTALL_DOCREADMEDIR_STANDALONE ".") + SET(INSTALL_SUPPORTFILESDIR_STANDALONE "support-files") + SET(INSTALL_MYSQLDATADIR_STANDALONE "data") + + # UNIX layout + SET(INSTALL_BINDIR_UNIX "bin") + SET(INSTALL_SBINDIR_UNIX "sbin") + SET(INSTALL_LIBDIR_UNIX "lib/mysql") + SET(INSTALL_PLUGINDIR_UNIX "lib/mysql/plugin") + SET(INSTALL_DOCDIR_UNIX "share/mysql/doc/MySQL-server-${MYSQL_NO_DASH_VERSION}") + SET(INSTALL_MANDIR_UNIX "share/mysql/man") + SET(INSTALL_INCLUDEDIR_UNIX "include/mysql") + SET(INSTALL_MYSQLSHAREDIR_UNIX "share/mysql") + SET(INSTALL_SHAREDIR_UNIX "share") + SET(INSTALL_SCRIPTDIR_UNIX "bin") + SET(INSTALL_MYSQLTESTDIR_UNIX "mysql-test") + SET(INSTALL_SQLBENCHROOTDIR_UNIX "") + SET(INSTALL_DOCREADMEDIR_UNIX "share/mysql/doc/MySQL-server-${MYSQL_NO_DASH_VERSION}") + SET(INSTALL_SUPPORTFILESDIR_UNIX "") + SET(INSTALL_MYSQLDATADIR_STANDALONE "var") + + +# Clear cached variables if install layout was changed +IF(OLD_INSTALL_LAYOUT) + IF(NOT OLD_INSTALL_LAYOUT STREQUAL INSTALL_LAYOUR) + SET(FORCE FORCE) + ENDIF() +ENDIF() +SET(OLD_INSTALL_LAYOUT ${INSTALL_LAYOUT} CACHE INTERNAL "") + +# Set INSTALL_FOODIR variables for chosen layout +# (for example, INSTALL_BINDIR will be defined as +# ${INSTALL_BINDIR_STANDALONE} by default if STANDALONE layout is chosen) +FOREACH(var BIN SBIN LIB MYSQLSHARE SHARE PLUGIN INCLUDE SCRIPT DOC MAN + MYSQLTEST SQLBENCHROOT DOCREADME SUPPORTFILES MYSQLDATA) + SET(INSTALL_${var}DIR ${INSTALL_${var}DIR_${INSTALL_LAYOUT}} + CACHE STRING "${var} installation directory" ${FORCE}) + MARK_AS_ADVANCED(INSTALL_${var}DIR) +ENDFOREACH() diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index ff74809d224..677504f89af 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -257,7 +257,7 @@ MACRO(MERGE_LIBRARIES) MESSAGE(FATAL_ERROR "Unknown library type") ENDIF() IF(NOT ARG_NOINSTALL) - MYSQL_INSTALL_TARGETS(${TARGET} DESTINATION lib) + MYSQL_INSTALL_TARGETS(${TARGET} DESTINATION "${INSTALL_LIBDIR}") ENDIF() ENDMACRO() diff --git a/cmake/mysql_add_executable.cmake b/cmake/mysql_add_executable.cmake index cb0237332c2..2157d03e6d1 100644 --- a/cmake/mysql_add_executable.cmake +++ b/cmake/mysql_add_executable.cmake @@ -29,7 +29,7 @@ INCLUDE(cmake_parse_arguments) FUNCTION (MYSQL_ADD_EXECUTABLE) # Pass-through arguments for ADD_EXECUTABLE CMAKE_PARSE_ARGUMENTS(ARG - "WIN32;MACOSX_BUNDLE;EXCLUDE_FROM_ALL" + "WIN32;MACOSX_BUNDLE;EXCLUDE_FROM_ALL;DESTINATION" "" ${ARGN} ) @@ -41,6 +41,9 @@ FUNCTION (MYSQL_ADD_EXECUTABLE) ADD_EXECUTABLE(${target} ${ARG_WIN32} ${ARG_MACOSX_BUNDLE} ${ARG_EXCLUDE_FROM_ALL} ${sources}) # tell CPack where to install IF(NOT ARG_EXCLUDE_FROM_ALL) - MYSQL_INSTALL_TARGETS(${target} DESTINATION bin) + IF(NOT ARG_DESTINATION) + SET(ARG_DESTINATION ${INSTALL_BINDIR}) + ENDIF() + MYSQL_INSTALL_TARGETS(${target} DESTINATION ${ARG_DESTINATION}) ENDIF() ENDFUNCTION() \ No newline at end of file diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index b9169ebb73e..f312cd2d4e8 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -162,8 +162,7 @@ MACRO(MYSQL_ADD_PLUGIN) SET_TARGET_PROPERTIES(${target} PROPERTIES OUTPUT_NAME "${ARG_MODULE_OUTPUT_NAME}") # Install dynamic library - SET(INSTALL_LOCATION lib/plugin) - MYSQL_INSTALL_TARGETS(${target} DESTINATION ${INSTALL_LOCATION}) + MYSQL_INSTALL_TARGETS(${target} DESTINATION ${INSTALL_PLUGINDIR}) ENDIF() ENDMACRO() diff --git a/config.h.cmake b/config.h.cmake index e101a02e295..3b4a463714f 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -636,6 +636,8 @@ #cmakedefine DEFAULT_BASEDIR "@DEFAULT_BASEDIR@" #cmakedefine MYSQL_DATADIR "@MYSQL_DATADIR@" #cmakedefine DEFAULT_CHARSET_HOME "@DEFAULT_CHARSET_HOME@" +#cmakedefine PLUGINDIR "@PLUGINDIR@" +#cmakedefine DEFAULT_SYSCONFDIR "@DEFAULT_SYSCONFDIR@" #define PACKAGE "mysql" #define PACKAGE_BUGREPORT "" diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index c16f8ea1ff8..0d4220555f6 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -54,8 +54,7 @@ SET(HEADERS ${HEADERS_GEN_CONFIGURE} ) -INSTALL(FILES ${HEADERS} DESTINATION include) -INSTALL(DIRECTORY mysql/ DESTINATION include - FILES_MATCHING PATTERN "*.h") +INSTALL(FILES ${HEADERS} DESTINATION ${INSTALL_INCLUDEDIR}) +INSTALL(DIRECTORY mysql/ DESTINATION ${INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h") diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index c7a0a80cbeb..acdff87f4a0 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -154,7 +154,7 @@ SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) # and link them together into shared library. MERGE_LIBRARIES(mysqlclient STATIC ${LIBS}) IF(UNIX) - INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient lib) + INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r mysqlclient ${INSTALL_LIBDIR}) ENDIF() IF(NOT DISABLE_SHARED) @@ -177,6 +177,6 @@ IF(NOT DISABLE_SHARED) #(mysqlclient in this case) SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) - INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql lib) + INSTALL_SYMLINK(${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r libmysql ${INSTALL_LIBDIR}) ENDIF() ENDIF() diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt index 35da3411805..29de4432c0c 100644 --- a/man/CMakeLists.txt +++ b/man/CMakeLists.txt @@ -17,8 +17,8 @@ FILE(GLOB MAN1_FILES *.1) FILE(GLOB MAN8_FILES *.8) IF(MAN1_FILES) - INSTALL(FILES ${MAN1_FILES} DESTINATION man/man1) + INSTALL(FILES ${MAN1_FILES} DESTINATION ${INSTALL_MANDIR}/man1) ENDIF() IF(MAN8_FILES) - INSTALL(FILES ${MAN8_FILES} DESTINATION man/man8) + INSTALL(FILES ${MAN8_FILES} DESTINATION ${INSTALL_MANDIR}/man8) ENDIF() diff --git a/mysql-test/CMakeLists.txt b/mysql-test/CMakeLists.txt index 54203336dee..75e7502751c 100644 --- a/mysql-test/CMakeLists.txt +++ b/mysql-test/CMakeLists.txt @@ -15,13 +15,14 @@ INSTALL( DIRECTORY . - DESTINATION mysql-test + DESTINATION ${INSTALL_MYSQLTESTDIR} PATTERN "var/" EXCLUDE PATTERN "lib/My/SafeProcess" EXCLUDE PATTERN "CPack" EXCLUDE - PATTERN "CMake" EXCLUDE - PATTERN "mtr.out" EXCLUDE + PATTERN "CMake*" EXCLUDE + PATTERN "mtr.out*" EXCLUDE PATTERN ".cvsignore" EXCLUDE + PATTERN "*.am" EXCLUDE ) diff --git a/mysql-test/lib/My/SafeProcess/CMakeLists.txt b/mysql-test/lib/My/SafeProcess/CMakeLists.txt index d22d4fddddf..ec2a13b910c 100644 --- a/mysql-test/lib/My/SafeProcess/CMakeLists.txt +++ b/mysql-test/lib/My/SafeProcess/CMakeLists.txt @@ -24,4 +24,4 @@ INSTALL(TARGETS my_safe_process DESTINATION "mysql-test/lib/My/SafeProcess") IF(WIN32) INSTALL(TARGETS my_safe_kill DESTINATION "mysql-test/lib/My/SafeProcess") ENDIF() -INSTALL(FILES safe_process.pl Base.pm DESTINATION "mysql-test/lib/My/SafeProcess") +INSTALL(FILES safe_process.pl Base.pm DESTINATION "${INSTALL_MYSQLTESTDIR}/lib/My/SafeProcess") diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 0b574fc0621..44de52f254f 100755 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -66,7 +66,7 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables_data.sql ${CMAKE_CURRENT_SOURCE_DIR}/fill_help_tables.sql ${CMAKE_CURRENT_SOURCE_DIR}/mysql_test_data_timezone.sql - DESTINATION share + DESTINATION ${INSTALL_MYSQLSHAREDIR} ) # TCMalloc hacks @@ -137,13 +137,23 @@ ENDIF(UNIX) # i.e. makes access relative the current directory. This matches # the documentation, so better not change this. -SET(prefix .) -SET(bindir ./bin) -SET(sbindir ./bin) -SET(scriptdir ./bin) -SET(libexecdir ./bin) -SET(pkgdatadir ./share) -SET(localstatedir ./data) +IF(INSTALL_LAYOUT MATCHES "STANDALONE") + SET(prefix ".") +ELSE() + SET(prefix "${CMAKE_INSTALL_PREFIX}") +ENDIF() + +SET(bindir ${prefix}/${INSTALL_BINDIR}) +SET(sbindir ${prefix}/${INSTALL_SBINDIR}) +SET(scriptdir ${prefix}/${INSTALL_BINDIR}) +SET(libexecdir ${prefix}/${INSTALL_SBINDIR}) +SET(pkgdatadir ${prefix}/${INSTALL_MYSQLSHAREDIR}) +IF(INSTALL_LAYOUT MATCHES "STANDALONE") + SET(localstatedir ${prefix}/data) +ELSE() + SET(localstatedir ${MYSQL_DATADIR}) +ENDIF() + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysql_install_db.sh ${CMAKE_CURRENT_BINARY_DIR}/mysql_install_db ESCAPE_QUOTES @ONLY) @@ -158,15 +168,16 @@ INSTALL(FILES SET(prefix "${CMAKE_INSTALL_PREFIX}") SET(sysconfdir ${prefix}) -SET(bindir ${prefix}/bin) -SET(libexecdir ${prefix}/bin) -SET(scriptdir ${prefix}/bin) -SET(datadir ${prefix}/share) -SET(pkgdatadir ${prefix}/share) -SET(pkgincludedir ${prefix}/include) -SET(pkglibdir ${prefix}/lib) -SET(pkgplugindir ${prefix}/lib/plugin) -SET(localstatedir ${prefix}/data) +SET(bindir ${prefix}/${INSTALL_BINDIR}) +SET(libexecdir ${prefix}/${INSTALL_SBINDIR}) +SET(scriptdir ${prefix}/${INSTALL_BINDIR}) +SET(datadir ${prefix}/${INSTALL_MYSQLSHAREDIR}) +SET(pkgdatadir ${prefix}/${INSTALL_MYSQLSHAREDIR}) +SET(pkgincludedir ${prefix}/${INSTALL_INCLUDEDIR}) +SET(pkglibdir ${prefix}/${INSTALL_LIBDIR}) +SET(pkgplugindir ${prefix}/${INSTALL_PLUGINDIR}) +SET(localstatedir ${MYSQL_DATADIR}) + # some scripts use @TARGET_LINUX@ IF(CMAKE_SYSTEM_NAME MATCHES "Linux") SET(TARGET_LINUX 1) @@ -242,7 +253,7 @@ IF(WIN32) ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) INSTALL(FILES - "CMAKE_CURRENT_BINARY_DIR}/${file}.pl" + ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE @@ -254,7 +265,7 @@ IF(WIN32) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl ESCAPE_QUOTES @ONLY) INSTALL(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${file}.pl" + ${CMAKE_CURRENT_BINARY_DIR}/${file}.pl DESTINATION scripts PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE @@ -279,6 +290,7 @@ ELSE() mysqld_multi mysqlaccess mysqlaccess.conf + mysqld_safe ) FOREACH(file ${BIN_SCRIPTS}) IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.sh) @@ -291,38 +303,18 @@ ELSE() MESSAGE(FATAL_ERROR "Can not find ${file}.sh or ${file} in " "${CMAKE_CURRENT_SOURCE_DIR}" ) ENDIF() - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file} - DESTINATION bin + DESTINATION ${INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) ENDFOREACH() - - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysqld_safe.sh - ${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe @ONLY) - - INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe" - DESTINATION bin - PERMISSIONS OWNER_READ OWNER_WRITE - OWNER_EXECUTE GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE - ) - - # For some reason, mysqld_safe needs to be also in scripts directory - INSTALL(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mysqld_safe" - DESTINATION scripts - PERMISSIONS OWNER_READ OWNER_WRITE - OWNER_EXECUTE GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE - ) ENDIF() # Install libgcc as mylibgcc.a -IF(CMAKE_COMPILER_IS_GNUCXX) +IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_FLAGS MATCHES "-static") EXECUTE_PROCESS ( COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${CMAKE_CXX_FLAGS} --print-libgcc @@ -332,7 +324,7 @@ IF(CMAKE_COMPILER_IS_GNUCXX) ERROR_QUIET ) IF(${RESULT} EQUAL 0 AND EXISTS ${LIBGCC_LOCATION}) - INSTALL(FILES "${LIBGCC_LOCATION}" DESTINATION lib) + INSTALL(FILES "${LIBGCC_LOCATION}" DESTINATION ${INSTALL_LIBDIR}) ENDIF() ENDIF() diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 70b9b9f630e..f08e870d5e0 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -361,6 +361,9 @@ then if test -x "$MY_BASEDIR_VERSION/libexec/mysqld" then ledir="$MY_BASEDIR_VERSION/libexec" + elif test -x "$MY_BASEDIR_VERSION/sbin/mysqld" + then + ledir="$MY_BASEDIR_VERSION/sbin" else ledir="$MY_BASEDIR_VERSION/bin" fi @@ -373,6 +376,10 @@ elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where libexec, share and var are ledir="$MY_PWD/libexec" # Where mysqld is +elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/sbin/mysqld" +then + MY_BASEDIR_VERSION="$MY_PWD" # Where sbin, share and var are + ledir="$MY_PWD/sbin" # Where mysqld is # Since we didn't find anything, used the compiled-in defaults else MY_BASEDIR_VERSION='@prefix@' diff --git a/sql-bench/CMakeLists.txt b/sql-bench/CMakeLists.txt index e69fa9fa18f..88d15dc706f 100644 --- a/sql-bench/CMakeLists.txt +++ b/sql-bench/CMakeLists.txt @@ -22,6 +22,16 @@ ${CMAKE_SOURCE_DIR}/sql-bench/Comments/* ${CMAKE_SOURCE_DIR}/sql-bench/limits/* ) +IF(NOT INSTALL_SQLBENCHDIR) + RETURN() +ENDIF() + +IF(INSTALL_SQLBENCHROOTDIR STREQUAL ".") + SET(prefix) +ELSE() + SET(prefix ${INSTALL_SQLBENCHROOTDIR}/) +ENDIF() + GET_FILENAME_COMPONENT(basedir ${CMAKE_SOURCE_DIR} ABSOLUTE) FOREACH(file ${all_files}) IF(NOT IS_DIRECTORY ${file} AND NOT ${file} MATCHES "Make" ) @@ -42,10 +52,10 @@ FOREACH(file ${all_files}) CONFIGURE_FILE(${file} ${target} COPYONLY) IF (ext MATCHES ".bat") IF(WIN32) - INSTALL(FILES ${target} DESTINATION ${dir}) + INSTALL(FILES ${target} DESTINATION ${prefix}${dir}) ENDIF() ELSE() - INSTALL(FILES ${target} DESTINATION ${dir}) + INSTALL(FILES ${target} DESTINATION ${prefix}${dir}) ENDIF() ENDIF() ENDFOREACH() diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 6fab01596b9..a5a925b8dc2 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -100,7 +100,7 @@ ELSE() SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL}) ENDIF() -MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE}) +MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE} DESTINATION ${INSTALL_SBINDIR}) IF(NOT WITHOUT_DYNAMIC_PLUGINS) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) @@ -231,11 +231,11 @@ ADD_CUSTOM_TARGET(dist +IF(INSTALL_LAYOUT STREQUAL "STANDALONE") # We need to create empty directories (data/test) the installation. # This does not work with current CPack due to http://www.cmake.org/Bug/view.php?id=8767 # Avoid completely empty directories and install dummy file instead. - SET(DUMMY_FILE ${CMAKE_CURRENT_BINARY_DIR}/.empty ) FILE(WRITE ${DUMMY_FILE} "") INSTALL(FILES ${DUMMY_FILE} DESTINATION data/test) @@ -271,3 +271,5 @@ ELSE() # Not windows or cross compiling, just install an empty directory INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql) ENDIF() +ENDIF() + diff --git a/sql/share/CMakeLists.txt b/sql/share/CMakeLists.txt index 7a67833f9e3..944120cfc24 100644 --- a/sql/share/CMakeLists.txt +++ b/sql/share/CMakeLists.txt @@ -45,8 +45,8 @@ SET(files FOREACH (dir ${dirs}) INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${dir} - DESTINATION share) + DESTINATION ${INSTALL_MYSQLSHAREDIR}) ENDFOREACH() -INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION share) +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION ${INSTALL_MYSQLSHAREDIR}) -INSTALL(FILES ${files} DESTINATION share) +INSTALL(FILES ${files} DESTINATION ${INSTALL_MYSQLSHAREDIR}) diff --git a/support-files/CMakeLists.txt b/support-files/CMakeLists.txt index e9438cf6ae3..ff98a7dd885 100644 --- a/support-files/CMakeLists.txt +++ b/support-files/CMakeLists.txt @@ -15,59 +15,68 @@ IF(WIN32) SET(localstatedir "C:\\mysql\\data") - SET(install_destination .) SET(ini_file_extension "ini") ELSE() - SET(localstatedir "/usr/local/mysql/data") - SET(prefix "/usr/local") - SET(libexedir "/usr/local/mysql/bin") - SET(bindir "/usr/local/mysql/bin" ) - SET(sbindir "/usr/local/mysql/bin") - SET(datadir "/usr/local/mysql/data") + SET(localstatedir "${MYSQL_DATADIR}") + SET(prefix "${CMAKE_INSTALL_PREFIX}") + SET(libexecdir "${CMAKE_INSTALL_PREFIX}/${INSTALL_SBINDIR}") + SET(bindir "${CMAKE_INSTALL_PREFIX}/${INSTALL_BINDIR}" ) + SET(sbindir "${libexecdir}") + SET(datadir "${MYSQL_DATADIR}") SET(CC ${CMAKE_C_COMPILER}) SET(CXX ${CMAKE_CXX_COMPILER}) SET(CFLAGS ${CMAKE_C_FLAGS}) SET(CXXFLAGS ${CMAKE_CXX_FLAGS}) SET(MYSQLD_USER "mysql") - SET(install_destination "support-files") SET(ini_file_extension "cnf") ENDIF() FOREACH(inifile my-huge my-innodb-heavy-4G my-large my-medium my-small) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${inifile}.cnf.sh ${CMAKE_CURRENT_BINARY_DIR}/${inifile}.${ini_file_extension} @ONLY) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${inifile}.${ini_file_extension} DESTINATION ${install_destination}) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${inifile}.${ini_file_extension} DESTINATION ${INSTALL_DOCREADMEDIR}) ENDFOREACH() IF(UNIX) + IF(INSTALL_LAYOUT MATCHES "STANDALONE") + SET(prefix ".") + SET(inst_location ${INSTALL_SUPPORTFILESDIR}) + ELSE() + SET(prefix ${CMAKE_INSTALL_PREFIX}) + SET(inst_location ${INSTALL_MYSQLSHAREDIR}) + ENDIF() + FILE(GLOB ndb_ini_files ${CMAKE_CURRENT_SOURCE_DIR}/*.ini) - INSTALL(FILES ${ndb_ini_files} DESTINATION ${install_destination}) + INSTALL(FILES ${ndb_ini_files} DESTINATION ${inst_location}) FOREACH(script mysqld_multi.server mysql-log-rotate) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${script}.sh ${CMAKE_CURRENT_BINARY_DIR}/${script} @ONLY ) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${script} DESTINATION support-files + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${script} + DESTINATION ${inst_location} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) ENDFOREACH() - INSTALL(FILES magic DESTINATION support-files) - INSTALL(FILES mysql.m4 DESTINATION share/aclocal) + IF(INSTALL_SUPPORTFILESDIR) + INSTALL(FILES magic DESTINATION ${inst_location}) + ENDIF() + + INSTALL(FILES mysql.m4 DESTINATION ${INSTALL_SHAREDIR}/aclocal) CONFIGURE_FILE(MySQL-shared-compat.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/MySQL-shared-compat.spec @ONLY) CONFIGURE_FILE(mysql.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/mysql.spec @ONLY) CONFIGURE_FILE(mysql.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/mysql.${VERSION}.spec @ONLY) CONFIGURE_FILE(MySQL-shared-compat.spec.sh ${CMAKE_CURRENT_BINARY_DIR}/MySQL-shared-compat.spec @ONLY) - # mysql.server needs another set variables - SET(bindir ./bin) - SET(sbindir ./bin) - SET(scriptdir ./bin) - SET(libexecdir ./bin) - SET(pkgdatadir "${CMAKE_INSTALL_PREFIX}/data") + SET(bindir ${prefix}/${INSTALL_BINDIR}) + SET(sbindir ${prefix}/${INSTALL_SBINDIR}) + SET(scriptdir ${prefix}/${INSTALL_SCRIPTDIR}) + SET(libexecdir ${prefix}/${INSTALL_SBINDIR}) + SET(pkgdatadir ${prefix}/${INSTALL_MYSQLSHAREDIR}) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysql.server.sh ${CMAKE_CURRENT_BINARY_DIR}/mysql.server @ONLY) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mysql.server - DESTINATION support-files + DESTINATION ${inst_location} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) ENDIF() From 8ec9fdd4abfa63ed566f73b7ce487c68a157747e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 24 Jan 2010 20:03:10 +0100 Subject: [PATCH 289/466] Fix failing sys_vars.plugin_dir_basic - treat both '/' and '\' as path separators in get_relative_path() on Windows --- include/my_global.h | 2 ++ sql/mysqld.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/my_global.h b/include/my_global.h index 586a77e8ee9..d71ab89e008 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -818,12 +818,14 @@ typedef SOCKET_SIZE_TYPE size_socket; #ifdef _WIN32 #define FN_LIBCHAR '\\' +#define FN_LIBCHAR2 '/' #define FN_ROOTDIR "\\" #define FN_DEVCHAR ':' #define FN_NETWORK_DRIVES /* Uses \\ to indicate network drives */ #define FN_NO_CASE_SENCE /* Files are not case-sensitive */ #else #define FN_LIBCHAR '/' +#define FN_LIBCHAR2 '/' #define FN_ROOTDIR "/" #endif diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 276ec02a042..12bd1ec7c47 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7681,7 +7681,7 @@ static char *get_relative_path(const char *path) strcmp(DEFAULT_MYSQL_HOME,FN_ROOTDIR)) { path+=(uint) strlen(DEFAULT_MYSQL_HOME); - while (*path == FN_LIBCHAR) + while (*path == FN_LIBCHAR || *path == FN_LIBCHAR2) path++; } return (char*) path; From 03fcdd0194a0d3026c394b52e127d85577ff5eca Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 24 Jan 2010 21:03:21 +0100 Subject: [PATCH 290/466] Following autotools tradition, add preprocessor definitions specified in environment variable CPPFLAGS --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 668675d4444..f63e31d9cc0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,11 @@ ENDIF() PROJECT(MySQL) +# Following autotools tradition, add preprocessor definitions +# specified in environment variable CPPFLAGS +IF(DEFINED ENV{CPPFLAGS}) + ADD_DEFINITIONS($ENV{CPPFLAGS}) +ENDIF() IF(CYGWIN) SET(WIN32 0) From 1519d8ccd17db7d342743132a70fed03e542dab7 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 25 Jan 2010 02:52:32 +0100 Subject: [PATCH 291/466] Fix smaller stuff: disable transitive linking for MERGE_LIBRARIES results, compile embedded with PIC to allow linking with shared libraries --- cmake/libutils.cmake | 1 + libmysqld/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index 677504f89af..f20e21b8f86 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -259,6 +259,7 @@ MACRO(MERGE_LIBRARIES) IF(NOT ARG_NOINSTALL) MYSQL_INSTALL_TARGETS(${TARGET} DESTINATION "${INSTALL_LIBDIR}") ENDIF() + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES LINK_INTERFACE_LIBRARIES "") ENDMACRO() FUNCTION(GET_DEPENDEND_OS_LIBS target result) diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 0f62bf6eb64..acc7d3fecd2 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -88,7 +88,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ) -ADD_LIBRARY(sql_embedded STATIC ${SQL_EMBEDDED_SOURCES}) +ADD_CONVENIENCE_LIBRARY(sql_embedded ${SQL_EMBEDDED_SOURCES}) DTRACE_INSTRUMENT(sql_embedded) ADD_DEPENDENCIES(sql_embedded GenError GenServerSource) From efd5ecb0e07472682f89a8570200713689faf48c Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 25 Jan 2010 11:20:52 +0100 Subject: [PATCH 292/466] Bug #50182 mtr: allow $MTR_PORT_BASE or --port-base= as alternative to MTR_BUILD_THREAD As suggested, convert internally to value of build_thread --- mysql-test/mysql-test-run.pl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2805b156487..99d4774866f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -191,7 +191,9 @@ our $opt_experimental; our $experimental_test_cases; my $baseport; +# $opt_build_thread may later be set from $opt_port_base my $opt_build_thread= $ENV{'MTR_BUILD_THREAD'} || "auto"; +my $opt_port_base= $ENV{'MTR_PORT_BASE'} || "auto"; my $build_thread= 0; my $opt_record; @@ -838,6 +840,7 @@ sub command_line_setup { # Specify ports 'build-thread|mtr-build-thread=i' => \$opt_build_thread, + 'port-base|mtr-port-base=i' => \$opt_port_base, # Test case authoring 'record' => \$opt_record, @@ -1100,6 +1103,16 @@ sub command_line_setup { $opt_mem= undef; } + if ($opt_port_base ne "auto") + { + if (my $rem= $opt_port_base % 10) + { + mtr_warning ("Port base $opt_port_base rounded down to multiple of 10"); + $opt_port_base-= $rem; + } + $opt_build_thread= $opt_port_base / 10 - 1000; + } + # -------------------------------------------------------------------------- # Check if we should speed up tests by trying to run on tmpfs # -------------------------------------------------------------------------- @@ -5326,6 +5339,11 @@ Options to control what test suites or cases to run Options that specify ports + mtr-port-base=# Base for port numbers, ports from this number to + port-base=# number+9 are reserved. Should be divisible by 10; + if not it will be rounded down. May be set with + environment variable MTR_PORT_BASE. If this value is + set and is not "auto", it overrides build-thread. mtr-build-thread=# Specify unique number to calculate port number(s) from. build-thread=# Can be set in environment variable MTR_BUILD_THREAD. Set MTR_BUILD_THREAD="auto" to automatically aquire From 4428409ddb337eb4c8a290656aeb56e64d8d8de6 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Mon, 25 Jan 2010 04:55:31 -0700 Subject: [PATCH 293/466] Bug#34455 Ambiguous foreign keys syntax is accepted Backport from 6.0 to 5.5 --- mysql-test/r/foreign_key.result | 42 +++++++++++++++ mysql-test/t/foreign_key.test | 72 +++++++++++++++++++++++++ sql/sql_lex.h | 4 +- sql/sql_yacc.yy | 93 ++++++++++++++++++++++----------- 4 files changed, 180 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/foreign_key.result b/mysql-test/r/foreign_key.result index ece53db2e9a..a6577dd376b 100644 --- a/mysql-test/r/foreign_key.result +++ b/mysql-test/r/foreign_key.result @@ -13,3 +13,45 @@ foreign key (a,b) references t3 (c,d) on update set null); create index a on t1 (a); create unique index b on t1 (a,b); drop table t1; +drop table if exists t_34455; +create table t_34455 ( +a int not null, +foreign key (a) references t3 (a) match full match partial); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'match partial)' at line 3 +create table t_34455 ( +a int not null, +foreign key (a) references t3 (a) on delete set default match full); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'match full)' at line 3 +create table t_34455 ( +a int not null, +foreign key (a) references t3 (a) on update set default match full); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'match full)' at line 3 +create table t_34455 ( +a int not null, +foreign key (a) references t3 (a) +on delete set default on delete set default); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'delete set default)' at line 4 +create table t_34455 ( +a int not null, +foreign key (a) references t3 (a) +on update set default on update set default); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'update set default)' at line 4 +create table t_34455 (a int not null); +alter table t_34455 +add foreign key (a) references t3 (a) match full match partial); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'match partial)' at line 2 +alter table t_34455 +add foreign key (a) references t3 (a) on delete set default match full); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'match full)' at line 2 +alter table t_34455 +add foreign key (a) references t3 (a) on update set default match full); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'match full)' at line 2 +alter table t_34455 +add foreign key (a) references t3 (a) +on delete set default on delete set default); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'delete set default)' at line 3 +alter table t_34455 +add foreign key (a) references t3 (a) +on update set default on update set default); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'update set default)' at line 3 +drop table t_34455; diff --git a/mysql-test/t/foreign_key.test b/mysql-test/t/foreign_key.test index 0a3708e6dc8..2a6ab01f511 100644 --- a/mysql-test/t/foreign_key.test +++ b/mysql-test/t/foreign_key.test @@ -23,3 +23,75 @@ create unique index b on t1 (a,b); drop table t1; # End of 4.1 tests + +# +# Bug#34455 (Ambiguous foreign keys syntax is accepted) +# + +--disable_warnings +drop table if exists t_34455; +--enable_warnings + +# 2 match clauses, illegal +--error ER_PARSE_ERROR +create table t_34455 ( + a int not null, + foreign key (a) references t3 (a) match full match partial); + +# match after on delete, illegal +--error ER_PARSE_ERROR +create table t_34455 ( + a int not null, + foreign key (a) references t3 (a) on delete set default match full); + +# match after on update, illegal +--error ER_PARSE_ERROR +create table t_34455 ( + a int not null, + foreign key (a) references t3 (a) on update set default match full); + +# 2 on delete clauses, illegal +--error ER_PARSE_ERROR +create table t_34455 ( + a int not null, + foreign key (a) references t3 (a) + on delete set default on delete set default); + +# 2 on update clauses, illegal +--error ER_PARSE_ERROR +create table t_34455 ( + a int not null, + foreign key (a) references t3 (a) + on update set default on update set default); + +create table t_34455 (a int not null); + +# 2 match clauses, illegal +--error ER_PARSE_ERROR +alter table t_34455 + add foreign key (a) references t3 (a) match full match partial); + +# match after on delete, illegal +--error ER_PARSE_ERROR +alter table t_34455 + add foreign key (a) references t3 (a) on delete set default match full); + +# match after on update, illegal +--error ER_PARSE_ERROR +alter table t_34455 + add foreign key (a) references t3 (a) on update set default match full); + +# 2 on delete clauses, illegal +--error ER_PARSE_ERROR +alter table t_34455 + add foreign key (a) references t3 (a) + on delete set default on delete set default); + +# 2 on update clauses, illegal +--error ER_PARSE_ERROR +alter table t_34455 + add foreign key (a) references t3 (a) + on update set default on update set default); + +drop table t_34455; + diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 800a16cf2b6..7eb72bc5358 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1903,7 +1903,9 @@ struct LEX: public Query_tables_list uint profile_options; uint uint_geom_type; uint grant, grant_tot_col, which_columns; - uint fk_delete_opt, fk_update_opt, fk_match_option; + enum Foreign_key::fk_match_opt fk_match_option; + enum Foreign_key::fk_option fk_update_opt; + enum Foreign_key::fk_option fk_delete_opt; uint slave_thd_opt, start_transaction_opt; int nest_level; /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aebf80e340f..5e900b69aa3 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -756,6 +756,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type, struct p_elem_val *p_elem_value; enum index_hint_type index_hint; enum enum_filetype filetype; + enum Foreign_key::fk_option m_fk_option; Diag_condition_item_name diag_condition_item_name; } @@ -1422,7 +1423,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); type type_with_opt_collate int_type real_type order_dir lock_option udf_type if_exists opt_local opt_table_options table_options table_option opt_if_not_exists opt_no_write_to_binlog - delete_option opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_ignore_leaves fulltext_options spatial_type union_option start_transaction_opts opt_chain opt_release union_opt select_derived_init option_type2 @@ -1430,6 +1431,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt +%type + delete_option + %type ulong_num real_ulong_num merge_insert_types @@ -1544,7 +1548,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_precision opt_ignore opt_column opt_restrict grant revoke set lock unlock string_list field_options field_option field_opt_list opt_binary ascii unicode table_lock_list table_lock - ref_list opt_on_delete opt_on_delete_list opt_on_delete_item use + ref_list opt_match_clause opt_on_update_delete use opt_delete_options opt_delete_option varchar nchar nvarchar opt_outer table_list table_name table_alias_ref_list table_alias_ref opt_option opt_place @@ -5833,21 +5837,20 @@ opt_primary: ; references: - REFERENCES table_ident - { - LEX *lex=Lex; - lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0; - lex->ref_list.empty(); - } + REFERENCES + table_ident opt_ref_list + opt_match_clause + opt_on_update_delete { $$=$2; } ; opt_ref_list: - /* empty */ opt_on_delete {} - | '(' ref_list ')' opt_on_delete {} + /* empty */ + { Lex->ref_list.empty(); } + | '(' ref_list ')' ; ref_list: @@ -5863,34 +5866,64 @@ ref_list: Key_part_spec *key= new Key_part_spec($1, 0); if (key == NULL) MYSQL_YYABORT; - Lex->ref_list.push_back(key); + LEX *lex= Lex; + lex->ref_list.empty(); + lex->ref_list.push_back(key); } ; -opt_on_delete: - /* empty */ {} - | opt_on_delete_list {} +opt_match_clause: + /* empty */ + { Lex->fk_match_option= Foreign_key::FK_MATCH_UNDEF; } + | MATCH FULL + { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } + | MATCH PARTIAL + { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } + | MATCH SIMPLE_SYM + { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; } ; -opt_on_delete_list: - opt_on_delete_list opt_on_delete_item {} - | opt_on_delete_item {} - ; - -opt_on_delete_item: - ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; } - | ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; } - | MATCH FULL { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } - | MATCH PARTIAL { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } - | MATCH SIMPLE_SYM { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; } +opt_on_update_delete: + /* empty */ + { + LEX *lex= Lex; + lex->fk_update_opt= Foreign_key::FK_OPTION_UNDEF; + lex->fk_delete_opt= Foreign_key::FK_OPTION_UNDEF; + } + | ON UPDATE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $3; + lex->fk_delete_opt= Foreign_key::FK_OPTION_UNDEF; + } + | ON DELETE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= Foreign_key::FK_OPTION_UNDEF; + lex->fk_delete_opt= $3; + } + | ON UPDATE_SYM delete_option + ON DELETE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $3; + lex->fk_delete_opt= $6; + } + | ON DELETE_SYM delete_option + ON UPDATE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $6; + lex->fk_delete_opt= $3; + } ; delete_option: - RESTRICT { $$= (int) Foreign_key::FK_OPTION_RESTRICT; } - | CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; } - | SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; } - | NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; } - | SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; } + RESTRICT { $$= Foreign_key::FK_OPTION_RESTRICT; } + | CASCADE { $$= Foreign_key::FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= Foreign_key::FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= Foreign_key::FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= Foreign_key::FK_OPTION_DEFAULT; } ; normal_key_type: From 261a29e20ee852ef16cc1ef17caedb91d80efe09 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 25 Jan 2010 13:12:29 +0100 Subject: [PATCH 294/466] In standalone package, default data subdirectory is "data", not "var" --- cmake/install_layout.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index b1a285a0695..7a0d34d6a17 100755 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -106,7 +106,7 @@ ENDIF() SET(INSTALL_SQLBENCHROOTDIR_UNIX "") SET(INSTALL_DOCREADMEDIR_UNIX "share/mysql/doc/MySQL-server-${MYSQL_NO_DASH_VERSION}") SET(INSTALL_SUPPORTFILESDIR_UNIX "") - SET(INSTALL_MYSQLDATADIR_STANDALONE "var") + SET(INSTALL_MYSQLDATADIR_UNIX "var") # Clear cached variables if install layout was changed From 1c0f30ae9a7968ed5d97cf444943498d8218389f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 25 Jan 2010 15:14:42 +0100 Subject: [PATCH 295/466] Fix typos : - opensolaris cmake package is SUNWcmake not SUNWCMake - it is configure.cmake not config.cmake --- BUILD-CMAKE | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BUILD-CMAKE b/BUILD-CMAKE index b5a01d52a5a..68c40167da5 100644 --- a/BUILD-CMAKE +++ b/BUILD-CMAKE @@ -14,7 +14,7 @@ line needs to be modified to e.g "yum install" on Fedora or "zypper install" on OpenSUSE. OpenSolaris: -shell> pfexec pkgadd install SUNWCMake +shell> pfexec pkgadd install SUNWcmake Windows and Mac OSX: Download and install the latest distribution from @@ -199,8 +199,8 @@ FOR PROGRAMMERS: WRITING PLATFORM CHECKS -------------------------------------------------------------- If you modify MySQL source and want to add a new platform check,please read http://www.vtk.org/Wiki/CMake_HowToDoPlatformChecks first. In MySQL, most of -the platform tests are implemented in config.cmake and the template header file -is config.h.cmake +the platform tests are implemented in configure.cmake and the template header +file is config.h.cmake Bigger chunks of functionality, for example non-trivial macros are implemented in files /cmake subdirectory. From 51af6729f242aab68937ac73c3401d5c5a3983c2 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Mon, 25 Jan 2010 22:34:34 +0100 Subject: [PATCH 296/466] Patch to eliminate warnings in mysql-next-mr-bugteam. --- sql/repl_failsafe.cc | 2 +- sql/repl_failsafe.h | 2 +- sql/rpl_injector.cc | 12 ++++++------ sql/rpl_mi.cc | 4 ++-- sql/slave.cc | 12 ++++++------ sql/sql_class.cc | 6 +++--- sql/sql_db.cc | 2 +- sql/sql_parse.cc | 2 +- sql/sql_repl.cc | 4 ++-- sql/sql_table.cc | 2 +- sql/sys_vars.cc | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 5bf87dea90e..29443eb6e65 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -38,7 +38,7 @@ #define SLAVE_ERRMSG_SIZE (FN_REFLEN+64) -uint rpl_status=RPL_NULL; +RPL_STATUS rpl_status=RPL_NULL; mysql_mutex_t LOCK_rpl_status; mysql_cond_t COND_rpl_status; HASH slave_list; diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index dd6770be0b4..94b151aaee7 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -26,7 +26,7 @@ typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE, RPL_LOST_SOLDIER,RPL_TROOP_SOLDIER, RPL_RECOVERY_CAPTAIN,RPL_NULL /* inactive */, RPL_ANY /* wild card used by change_rpl_status */ } RPL_STATUS; -extern uint rpl_status; +extern RPL_STATUS rpl_status; extern mysql_mutex_t LOCK_rpl_status; extern mysql_cond_t COND_rpl_status; diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index ac879b5033f..64f79092057 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -112,8 +112,8 @@ int injector::transaction::write_row (server_id_type sid, table tbl, { DBUG_ENTER("injector::transaction::write_row(...)"); - int error= 0; - if (error= check_state(ROW_STATE)) + int error= check_state(ROW_STATE); + if (error) DBUG_RETURN(error); server_id_type save_id= m_thd->server_id; @@ -131,8 +131,8 @@ int injector::transaction::delete_row(server_id_type sid, table tbl, { DBUG_ENTER("injector::transaction::delete_row(...)"); - int error= 0; - if (error= check_state(ROW_STATE)) + int error= check_state(ROW_STATE); + if (error) DBUG_RETURN(error); server_id_type save_id= m_thd->server_id; @@ -150,8 +150,8 @@ int injector::transaction::update_row(server_id_type sid, table tbl, { DBUG_ENTER("injector::transaction::update_row(...)"); - int error= 0; - if (error= check_state(ROW_STATE)) + int error= check_state(ROW_STATE); + if (error) DBUG_RETURN(error); server_id_type save_id= m_thd->server_id; diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 38382fd2a0e..7f81834c1d3 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -33,8 +33,8 @@ int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f); Master_info::Master_info(bool is_slave_recovery) :Slave_reporting_capability("I/O"), ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), - port(MYSQL_PORT), connect_retry(DEFAULT_CONNECT_RETRY), inited(0), - rli(is_slave_recovery), abort_slave(0), + rli(is_slave_recovery), port(MYSQL_PORT), + connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0), slave_running(0), slave_run_id(0), sync_counter(0), heartbeat_period(0), received_heartbeats(0), master_id(0) { diff --git a/sql/slave.cc b/sql/slave.cc index a4f16f6ed28..af43b39c63b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1111,7 +1111,7 @@ int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f) memcpy(buf_act, buf, read_size); snd_size= my_b_gets(f, buf_act + read_size, max_size - read_size); if (snd_size == 0 || - (snd_size + 1 == max_size - read_size) && buf[max_size - 2] != '\n') + ((snd_size + 1 == max_size - read_size) && buf[max_size - 2] != '\n')) { /* failure to make the 2nd read or short read again @@ -3943,8 +3943,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) /* everything is filtered out from non-master */ (s_id != mi->master_id || /* for the master meta information is necessary */ - buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT && - buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT))) + (buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT && + buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT)))) { /* Do not write it to the relay log. @@ -3964,9 +3964,9 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) as well as rli->group_relay_log_pos. */ if (!(s_id == ::server_id && !mi->rli.replicate_same_server_id) || - buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT && - buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT && - buf[EVENT_TYPE_OFFSET] != STOP_EVENT) + (buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT && + buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT && + buf[EVENT_TYPE_OFFSET] != STOP_EVENT)) { mi->master_log_pos+= inc_pos; memcpy(rli->ign_master_log_name_end, mi->master_log_name, FN_REFLEN); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 28e5e56d8b9..d02d69aca0c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3531,7 +3531,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) { DBUG_ENTER("THD::decide_logging_format"); DBUG_PRINT("info", ("query: %s", query())); - DBUG_PRINT("info", ("variables.binlog_format: %ld", + DBUG_PRINT("info", ("variables.binlog_format: %u", variables.binlog_format)); DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x", lex->get_stmt_unsafe_flags())); @@ -3672,7 +3672,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) lock history on the slave will be different from the master. */ if (mixed_engine || - trans_has_updated_trans_table(this) && !all_trans_engines) + (trans_has_updated_trans_table(this) && !all_trans_engines)) lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS); DBUG_PRINT("info", ("flags_all_set: 0x%llx", flags_all_set)); @@ -3802,7 +3802,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) DBUG_PRINT("info", ("decision: no logging since " "mysql_bin_log.is_open() = %d " "and (options & OPTION_BIN_LOG) = 0x%llx " - "and binlog_format = %ld " + "and binlog_format = %u " "and binlog_filter->db_ok(db) = %d", mysql_bin_log.is_open(), (variables.option_bits & OPTION_BIN_LOG), diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 9fef7114898..4cb47c7b2df 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -854,7 +854,7 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) qinfo.db_len = strlen(db); /* These DDL methods and logging protected with LOCK_mysql_create_db */ - if (error= mysql_bin_log.write(&qinfo)) + if ((error= mysql_bin_log.write(&qinfo))) goto exit; } my_ok(thd, result); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5c272353664..bef00b13978 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3887,7 +3887,7 @@ end_with_restore_list: */ if (!lex->no_write_to_binlog && write_to_binlog) { - if (res= write_bin_log(thd, FALSE, thd->query(), thd->query_length())) + if ((res= write_bin_log(thd, FALSE, thd->query(), thd->query_length()))) break; } my_ok(thd); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 00cc28e6213..1fb0fcaf6dd 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -828,11 +828,11 @@ impossible position"; { if (coord) { - DBUG_ASSERT(heartbeat_ts && heartbeat_period != LL(0)); + DBUG_ASSERT(heartbeat_ts && heartbeat_period != 0); set_timespec_nsec(*heartbeat_ts, heartbeat_period); } ret= mysql_bin_log.wait_for_update_bin_log(thd, heartbeat_ts); - DBUG_ASSERT(ret == 0 || heartbeat_period != LL(0) && coord != NULL); + DBUG_ASSERT(ret == 0 || (heartbeat_period != 0 && coord != NULL)); if (ret == ETIMEDOUT || ret == ETIME) { #ifndef DBUG_OFF diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e87e52d5819..1e06b070494 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6533,7 +6533,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->clear_error(); Query_log_event qinfo(thd, thd->query(), thd->query_length(), FALSE, TRUE, FALSE, 0); - if (error= mysql_bin_log.write(&qinfo)) + if ((error= mysql_bin_log.write(&qinfo))) goto view_err_unlock; } my_ok(thd); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index fe3114c9d50..eb7781be5b9 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -324,7 +324,7 @@ static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var) return true; if (var->type == OPT_GLOBAL || (thd->variables.binlog_direct_non_trans_update == - var->save_result.ulonglong_value)) + static_cast(var->save_result.ulonglong_value))) return false; return false; From bc5479101045014d2a2c9c6cc7c7d358e87a6530 Mon Sep 17 00:00:00 2001 From: Guilhem Bichot Date: Mon, 25 Jan 2010 23:19:34 +0100 Subject: [PATCH 297/466] fixes for gcc 4.4.1 warnings --- sql/mysql_priv.h | 1 + sql/mysqld.cc | 25 +++++++++++++------------ sql/sql_select.cc | 2 +- sql/sql_yacc.yy | 4 ++-- sql/sys_vars.cc | 4 ++-- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4d5490e8219..6e35291b1c7 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -235,6 +235,7 @@ typedef struct my_locale_errmsgs extern char err_shared_dir[]; +/** @note Keep this a POD-type because we use offsetof() on it */ typedef struct my_locale_st { uint number; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 756ce7be5dc..05442643e23 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -928,8 +928,6 @@ static bool add_terminator(DYNAMIC_ARRAY *options); extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *); static void set_server_version(void); static int init_thread_environment(); -static void init_error_log_mutex(); -static void clean_up_error_log_mutex(); static char *get_relative_path(const char *path); static int fix_paths(void); void handle_connections_sockets(); @@ -1346,6 +1344,18 @@ extern "C" sig_handler print_signal_warning(int sig) #ifndef EMBEDDED_LIBRARY +static void init_error_log_mutex() +{ + mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST); +} + + +static void clean_up_error_log_mutex() +{ + mysql_mutex_destroy(&LOCK_error_log); +} + + /** cleanup all memory and end program nicely. @@ -3360,7 +3370,7 @@ static int init_common_variables() set the def_value member to 0 in my_long_options and initialize it to the correct value here. */ - default_storage_engine="MyISAM"; + default_storage_engine= const_cast("MyISAM"); /* Add server status variables to the dynamic list of @@ -3701,15 +3711,6 @@ You should consider changing lower_case_table_names to 1 or 2", return 0; } -static void init_error_log_mutex() -{ - mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST); -} - -static void clean_up_error_log_mutex() -{ - mysql_mutex_destroy(&LOCK_error_log); -} static int init_thread_environment() { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4775a297b0c..030c12de897 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4378,7 +4378,7 @@ best_access_path(JOIN *join, */ if (table->quick_keys.is_set(key) && (const_part & ((1 << table->quick_key_parts[key])-1)) == - ((1 << table->quick_key_parts[key])-1) && + (((key_part_map)1 << table->quick_key_parts[key])-1) && table->quick_n_ranges[key] == 1 && records > (double) table->quick_rows[key]) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ab128a9b701..5ee6f292204 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9218,8 +9218,8 @@ table_factor: lex->pop_context(); lex->nest_level--; } - else if ($3->select_lex && - $3->select_lex->master_unit()->is_union() || $5) + else if (($3->select_lex && + $3->select_lex->master_unit()->is_union()) || $5) { /* simple nested joins cannot have aliases or unions */ my_parse_error(ER(ER_SYNTAX_ERROR)); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index fe3114c9d50..e4dc0026128 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2956,14 +2956,14 @@ static bool check_locale(sys_var *self, THD *thd, set_var *var) static Sys_var_struct Sys_lc_messages( "lc_messages", "Set the language used for the error messages", SESSION_VAR(lc_messages), NO_CMD_LINE, - offsetof(MY_LOCALE, name), DEFAULT(&my_default_lc_messages), + my_offsetof(MY_LOCALE, name), DEFAULT(&my_default_lc_messages), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_locale)); static Sys_var_struct Sys_lc_time_names( "lc_time_names", "Set the language used for the month " "names and the days of the week", SESSION_VAR(lc_time_names), NO_CMD_LINE, - offsetof(MY_LOCALE, name), DEFAULT(&my_default_lc_time_names), + my_offsetof(MY_LOCALE, name), DEFAULT(&my_default_lc_time_names), NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_locale)); static Sys_var_tz Sys_time_zone( From d86d1221c0d313bb883b4963c60f09f133d736f0 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Mon, 25 Jan 2010 20:12:20 -0700 Subject: [PATCH 298/466] Bug#50596 Spurious test failures in perfschema.dml_mutex_instances Fixed the dml_mutex_instances and dml_rwlock_instances to be more reliable. In particular, the tests may not assume a mutex or rwlock is never locked. --- mysql-test/suite/perfschema/r/dml_mutex_instances.result | 2 +- mysql-test/suite/perfschema/r/dml_rwlock_instances.result | 2 +- mysql-test/suite/perfschema/t/dml_mutex_instances.test | 2 +- mysql-test/suite/perfschema/t/dml_rwlock_instances.test | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/perfschema/r/dml_mutex_instances.result b/mysql-test/suite/perfschema/r/dml_mutex_instances.result index 655ca811c06..862123b3450 100644 --- a/mysql-test/suite/perfschema/r/dml_mutex_instances.result +++ b/mysql-test/suite/perfschema/r/dml_mutex_instances.result @@ -1,6 +1,6 @@ select * from performance_schema.MUTEX_INSTANCES limit 1; NAME OBJECT_INSTANCE_BEGIN LOCKED_BY_THREAD_ID -# # NULL +# # # select * from performance_schema.MUTEX_INSTANCES where name='FOO'; NAME OBJECT_INSTANCE_BEGIN LOCKED_BY_THREAD_ID diff --git a/mysql-test/suite/perfschema/r/dml_rwlock_instances.result b/mysql-test/suite/perfschema/r/dml_rwlock_instances.result index 62b5fbeaa8a..686007e58e9 100644 --- a/mysql-test/suite/perfschema/r/dml_rwlock_instances.result +++ b/mysql-test/suite/perfschema/r/dml_rwlock_instances.result @@ -1,6 +1,6 @@ select * from performance_schema.RWLOCK_INSTANCES limit 1; NAME OBJECT_INSTANCE_BEGIN WRITE_LOCKED_BY_THREAD_ID READ_LOCKED_BY_COUNT -# # NULL 0 +# # # # select * from performance_schema.RWLOCK_INSTANCES where name='FOO'; NAME OBJECT_INSTANCE_BEGIN WRITE_LOCKED_BY_THREAD_ID READ_LOCKED_BY_COUNT diff --git a/mysql-test/suite/perfschema/t/dml_mutex_instances.test b/mysql-test/suite/perfschema/t/dml_mutex_instances.test index e3062c7b34c..0971c664eb8 100644 --- a/mysql-test/suite/perfschema/t/dml_mutex_instances.test +++ b/mysql-test/suite/perfschema/t/dml_mutex_instances.test @@ -18,7 +18,7 @@ --source include/not_embedded.inc --source include/have_perfschema.inc ---replace_column 1 # 2 # +--replace_column 1 # 2 # 3 # select * from performance_schema.MUTEX_INSTANCES limit 1; select * from performance_schema.MUTEX_INSTANCES diff --git a/mysql-test/suite/perfschema/t/dml_rwlock_instances.test b/mysql-test/suite/perfschema/t/dml_rwlock_instances.test index 251168237eb..33a42450681 100644 --- a/mysql-test/suite/perfschema/t/dml_rwlock_instances.test +++ b/mysql-test/suite/perfschema/t/dml_rwlock_instances.test @@ -18,7 +18,7 @@ --source include/not_embedded.inc --source include/have_perfschema.inc ---replace_column 1 # 2 # +--replace_column 1 # 2 # 3 # 4 # select * from performance_schema.RWLOCK_INSTANCES limit 1; select * from performance_schema.RWLOCK_INSTANCES From edb122134b6bf3bb3b65d35c58f6bf5baaf2ebdf Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Mon, 25 Jan 2010 20:50:31 -0700 Subject: [PATCH 299/466] Bug#50337 --defaults-file=~/something doesn't work anymore Before this fix, opening a configuration file located under "~" failed. To evaluate the "~" path, home_dir needs to be initialized. The 'home_dir' variable was initialized too late in my_init(). This fix: - moved the home_dir initialization from my_init() to my_basic_init(), using getenv("HOME")) - moved the initialization of my_umask / my_umask_dir also to my_basic_init(), to have all the my_umask / my_umask_dir init code in the same place. The second part is not strictly required, but makes the code more maintainable. Tested the fix manually. No MTR tests added, because MTR should not access or modify the $HOME directory of the user running tests. --- mysys/my_init.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/mysys/my_init.c b/mysys/my_init.c index 31adc5ed99e..a3e53fdaae1 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -74,6 +74,8 @@ static MYSQL_FILE instrumented_stdin; */ my_bool my_basic_init(void) { + char * str; + if (my_basic_init_done) return 0; my_basic_init_done= 1; @@ -82,6 +84,19 @@ my_bool my_basic_init(void) my_umask= 0660; /* Default umask for new files */ my_umask_dir= 0700; /* Default umask for new directories */ +#ifndef VMS + /* Default creation of new files */ + if ((str= getenv("UMASK")) != 0) + my_umask= (int) (atoi_octal(str) | 0600); + /* Default creation of new dir's */ + if ((str= getenv("UMASK_DIR")) != 0) + my_umask_dir= (int) (atoi_octal(str) | 0700); +#endif + + /* $HOME is needed early to parse configuration files located in ~/ */ + if ((home_dir= getenv("HOME")) != 0) + home_dir= intern_filename(home_dir_buff, home_dir); + init_glob_errs(); instrumented_stdin.m_file= stdin; @@ -124,7 +139,6 @@ my_bool my_basic_init(void) my_bool my_init(void) { - char * str; if (my_init_done) return 0; my_init_done= 1; @@ -142,24 +156,11 @@ my_bool my_init(void) { DBUG_ENTER("my_init"); DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown")); - if (!home_dir) - { /* Don't initialize twice */ - my_win_init(); - if ((home_dir=getenv("HOME")) != 0) - home_dir=intern_filename(home_dir_buff,home_dir); -#ifndef VMS - /* Default creation of new files */ - if ((str=getenv("UMASK")) != 0) - my_umask=(int) (atoi_octal(str) | 0600); - /* Default creation of new dir's */ - if ((str=getenv("UMASK_DIR")) != 0) - my_umask_dir=(int) (atoi_octal(str) | 0700); -#endif + my_win_init(); #ifdef VMS - init_ctype(); /* Stupid linker don't link _ctype.c */ + init_ctype(); /* Stupid linker don't link _ctype.c */ #endif - DBUG_PRINT("exit",("home: '%s'",home_dir)); - } + DBUG_PRINT("exit", ("home: '%s'", home_dir)); #ifdef __WIN__ win32_init_tcp_ip(); #endif From e0c1fb5227c5f07ba5d8ac41f09cc376afc0dc71 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Mon, 25 Jan 2010 21:53:04 -0700 Subject: [PATCH 300/466] Bug#50436 perfschema.aggregate fails on HPUX in 6.0 Relaxed the test conditions to account for objects destroyed, as was intended in the comments in mysql-test/suite/perfschema/t/aggregate.test --- mysql-test/suite/perfschema/r/aggregate.result | 12 ++++++------ mysql-test/suite/perfschema/t/aggregate.test | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mysql-test/suite/perfschema/r/aggregate.result b/mysql-test/suite/perfschema/r/aggregate.result index 598f9297cc5..3e38aebacfc 100644 --- a/mysql-test/suite/perfschema/r/aggregate.result +++ b/mysql-test/suite/perfschema/r/aggregate.result @@ -51,14 +51,14 @@ SELECT EVENT_NAME, e.SUM_TIMER_WAIT, SUM(i.SUM_TIMER_WAIT) FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_INSTANCE AS i USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.SUM_TIMER_WAIT <> SUM(i.SUM_TIMER_WAIT)) +HAVING (e.SUM_TIMER_WAIT < SUM(i.SUM_TIMER_WAIT)) OR @dump_all; EVENT_NAME SUM_TIMER_WAIT SUM(i.SUM_TIMER_WAIT) SELECT EVENT_NAME, e.MIN_TIMER_WAIT, MIN(i.MIN_TIMER_WAIT) FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_INSTANCE AS i USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MIN_TIMER_WAIT <> MIN(i.MIN_TIMER_WAIT)) +HAVING (e.MIN_TIMER_WAIT > MIN(i.MIN_TIMER_WAIT)) AND (MIN(i.MIN_TIMER_WAIT) != 0) OR @dump_all; EVENT_NAME MIN_TIMER_WAIT MIN(i.MIN_TIMER_WAIT) @@ -66,7 +66,7 @@ SELECT EVENT_NAME, e.MAX_TIMER_WAIT, MAX(i.MAX_TIMER_WAIT) FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_INSTANCE AS i USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MAX_TIMER_WAIT <> MAX(i.MAX_TIMER_WAIT)) +HAVING (e.MAX_TIMER_WAIT < MAX(i.MAX_TIMER_WAIT)) OR @dump_all; EVENT_NAME MAX_TIMER_WAIT MAX(i.MAX_TIMER_WAIT) "Verifying waits aggregate consistency (thread)" @@ -75,7 +75,7 @@ FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME AS t USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.SUM_TIMER_WAIT <> SUM(t.SUM_TIMER_WAIT)) +HAVING (e.SUM_TIMER_WAIT < SUM(t.SUM_TIMER_WAIT)) OR @dump_all; EVENT_NAME SUM_TIMER_WAIT SUM(t.SUM_TIMER_WAIT) SELECT EVENT_NAME, e.MIN_TIMER_WAIT, MIN(t.MIN_TIMER_WAIT) @@ -83,7 +83,7 @@ FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME AS t USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MIN_TIMER_WAIT <> MIN(t.MIN_TIMER_WAIT)) +HAVING (e.MIN_TIMER_WAIT > MIN(t.MIN_TIMER_WAIT)) AND (MIN(t.MIN_TIMER_WAIT) != 0) OR @dump_all; EVENT_NAME MIN_TIMER_WAIT MIN(t.MIN_TIMER_WAIT) @@ -92,7 +92,7 @@ FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME AS t USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MAX_TIMER_WAIT <> MAX(t.MAX_TIMER_WAIT)) +HAVING (e.MAX_TIMER_WAIT < MAX(t.MAX_TIMER_WAIT)) OR @dump_all; EVENT_NAME MAX_TIMER_WAIT MAX(t.MAX_TIMER_WAIT) update performance_schema.SETUP_CONSUMERS set enabled = 'YES'; diff --git a/mysql-test/suite/perfschema/t/aggregate.test b/mysql-test/suite/perfschema/t/aggregate.test index 7c01bdd0a4b..75bd5ad822e 100644 --- a/mysql-test/suite/perfschema/t/aggregate.test +++ b/mysql-test/suite/perfschema/t/aggregate.test @@ -1,4 +1,4 @@ -# Copyright (C) 2009 Sun Microsystems, Inc +# Copyright (C) 2009-2010 Sun Microsystems, 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 @@ -132,14 +132,14 @@ SELECT EVENT_NAME, e.SUM_TIMER_WAIT, SUM(i.SUM_TIMER_WAIT) FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_INSTANCE AS i USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.SUM_TIMER_WAIT <> SUM(i.SUM_TIMER_WAIT)) +HAVING (e.SUM_TIMER_WAIT < SUM(i.SUM_TIMER_WAIT)) OR @dump_all; SELECT EVENT_NAME, e.MIN_TIMER_WAIT, MIN(i.MIN_TIMER_WAIT) FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_INSTANCE AS i USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MIN_TIMER_WAIT <> MIN(i.MIN_TIMER_WAIT)) +HAVING (e.MIN_TIMER_WAIT > MIN(i.MIN_TIMER_WAIT)) AND (MIN(i.MIN_TIMER_WAIT) != 0) OR @dump_all; @@ -147,7 +147,7 @@ SELECT EVENT_NAME, e.MAX_TIMER_WAIT, MAX(i.MAX_TIMER_WAIT) FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_INSTANCE AS i USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MAX_TIMER_WAIT <> MAX(i.MAX_TIMER_WAIT)) +HAVING (e.MAX_TIMER_WAIT < MAX(i.MAX_TIMER_WAIT)) OR @dump_all; --echo "Verifying waits aggregate consistency (thread)" @@ -157,7 +157,7 @@ FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME AS t USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.SUM_TIMER_WAIT <> SUM(t.SUM_TIMER_WAIT)) +HAVING (e.SUM_TIMER_WAIT < SUM(t.SUM_TIMER_WAIT)) OR @dump_all; SELECT EVENT_NAME, e.MIN_TIMER_WAIT, MIN(t.MIN_TIMER_WAIT) @@ -165,7 +165,7 @@ FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME AS t USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MIN_TIMER_WAIT <> MIN(t.MIN_TIMER_WAIT)) +HAVING (e.MIN_TIMER_WAIT > MIN(t.MIN_TIMER_WAIT)) AND (MIN(t.MIN_TIMER_WAIT) != 0) OR @dump_all; @@ -174,7 +174,7 @@ FROM performance_schema.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME AS e JOIN performance_schema.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME AS t USING (EVENT_NAME) GROUP BY EVENT_NAME -HAVING (e.MAX_TIMER_WAIT <> MAX(t.MAX_TIMER_WAIT)) +HAVING (e.MAX_TIMER_WAIT < MAX(t.MAX_TIMER_WAIT)) OR @dump_all; From add502a268b95a2c81def5acd20d5efd99b4a97a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 07:54:03 +0100 Subject: [PATCH 301/466] add CMakeLists.txt to EXTRA_DIST for audit_null --- plugin/audit_null/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/audit_null/Makefile.am b/plugin/audit_null/Makefile.am index d57a72f8c18..4408c3f7c47 100644 --- a/plugin/audit_null/Makefile.am +++ b/plugin/audit_null/Makefile.am @@ -29,4 +29,4 @@ EXTRA_LIBRARIES= libadtnull.a noinst_LIBRARIES= @plugin_audit_null_static_target@ libadtnull_a_SOURCES= audit_null.c -EXTRA_DIST= plug.in +EXTRA_DIST= plug.in CMakeLists.txt From 0c4b0e9a3aad7550da2980af0db06a277687e1c7 Mon Sep 17 00:00:00 2001 From: Date: Tue, 26 Jan 2010 17:41:15 +0800 Subject: [PATCH 302/466] Bug #45855 row events in binlog after switch from binlog_fmt=mix to stmt with open tmp tbl Bug #45856 can't switch from binlog_format=row to mix with open tmp tbl If binlog_format=MIXED, there are open temporary tables, an unsafe statement is executed, and the user issues 'SET @@session.binlog_format = STATEMENT', then subsequent DML statements will be written in row format despite binlog_format=STATEMENT. Because the binlog format can't be reset to statement based by 'reset_current_stmt_binlog_row_based' function. If binlog_format=ROW, there are open temporary tables, and an unsafe statement is executed, then the statement 'SET @@session.binlog_format = MIXED' generates the error: "Cannot switch out of the row-based binary log format when the session has open temporary tables" However, it is safe to switch to MIXED mode because events in row format are allowed. To fix the above two problems, generate ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR and forbid switching from MIXED or ROW to STATEMENT when there are open temp tables and we are logging in row format. There is no error in any other case. --- .../binlog_format_switch_in_tmp_table.result | 78 +++++++++++++++++++ .../t/binlog_format_switch_in_tmp_table.test | 76 ++++++++++++++++++ sql/set_var.cc | 36 +++++---- 3 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 mysql-test/suite/binlog/r/binlog_format_switch_in_tmp_table.result create mode 100644 mysql-test/suite/binlog/t/binlog_format_switch_in_tmp_table.test diff --git a/mysql-test/suite/binlog/r/binlog_format_switch_in_tmp_table.result b/mysql-test/suite/binlog/r/binlog_format_switch_in_tmp_table.result new file mode 100644 index 00000000000..f886ccb134d --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_format_switch_in_tmp_table.result @@ -0,0 +1,78 @@ +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +MIXED +CREATE TABLE t1 (a VARCHAR(100)); +CREATE TEMPORARY TABLE t2 (a VARCHAR(100)); +# Test allow switching @@SESSION.binlog_format from MIXED to STATEMENT +# when there are open temp tables and we are logging in statement based format. +SET SESSION binlog_format = STATEMENT; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +STATEMENT +# Test allow switching @@SESSION.binlog_format from STATEMENT to +# STATEMENT when there are open temp tables. +SET SESSION binlog_format = STATEMENT; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +STATEMENT +INSERT INTO t1 VALUES ('statement based'); +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +STATEMENT +# Test allow switching @@SESSION.binlog_format from STATEMENT to +# MIXED when there are open temp tables. +SET SESSION binlog_format = MIXED; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +MIXED +# Test allow switching @@SESSION.binlog_format from MIXED to MIXED +# when there are open temp tables. +SET SESSION binlog_format = MIXED; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +MIXED +INSERT INTO t2 VALUES (UUID()); +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +MIXED +# Test forbit switching @@SESSION.binlog_format from MIXED to STATEMENT +# when there are open temp tables and we are logging in row based format. +SET SESSION binlog_format = STATEMENT; +ERROR HY000: Cannot switch out of the row-based binary log format when the session has open temporary tables +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +MIXED +SET SESSION binlog_format = ROW; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +ROW +INSERT INTO t1 VALUES ('row based'); +# Test allow switching @@SESSION.binlog_format from ROW to MIXED +# when there are open temp tables. +SET SESSION binlog_format = MIXED; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +MIXED +INSERT INTO t1 VALUES ('row based'); +# Test allow switching @@SESSION.binlog_format from MIXED to ROW +# when there are open temp tables. +SET SESSION binlog_format = ROW; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +ROW +# Test allow switching @@SESSION.binlog_format from ROW to ROW +# when there are open temp tables. +SET SESSION binlog_format = ROW; +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +ROW +INSERT INTO t1 VALUES ('row based'); +# Test forbit switching @@SESSION.binlog_format from ROW to STATEMENT +# when there are open temp tables. +SET SESSION binlog_format = STATEMENT; +ERROR HY000: Cannot switch out of the row-based binary log format when the session has open temporary tables +SELECT @@SESSION.binlog_format; +@@SESSION.binlog_format +ROW +DROP TEMPORARY TABLE t2; +DROP TABLE t1; diff --git a/mysql-test/suite/binlog/t/binlog_format_switch_in_tmp_table.test b/mysql-test/suite/binlog/t/binlog_format_switch_in_tmp_table.test new file mode 100644 index 00000000000..6868506008c --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_format_switch_in_tmp_table.test @@ -0,0 +1,76 @@ +# +# Bug #45855 row events in binlog after switch from binlog_fmt=mix to stmt with open tmp tbl +# Bug #45856 can't switch from binlog_format=row to mix with open tmp tbl +# This test verfies if the program will generate ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR +# error and forbid switching @@SESSION.binlog_format from MIXED or ROW to +# STATEMENT when there are open temp tables and we are logging in row format. +# There is no error in any other case. +# + +source include/have_binlog_format_mixed.inc; + +SELECT @@SESSION.binlog_format; +CREATE TABLE t1 (a VARCHAR(100)); +CREATE TEMPORARY TABLE t2 (a VARCHAR(100)); + +--echo # Test allow switching @@SESSION.binlog_format from MIXED to STATEMENT +--echo # when there are open temp tables and we are logging in statement based format. +SET SESSION binlog_format = STATEMENT; +SELECT @@SESSION.binlog_format; + +--echo # Test allow switching @@SESSION.binlog_format from STATEMENT to +--echo # STATEMENT when there are open temp tables. +SET SESSION binlog_format = STATEMENT; +SELECT @@SESSION.binlog_format; + +INSERT INTO t1 VALUES ('statement based'); +SELECT @@SESSION.binlog_format; +--echo # Test allow switching @@SESSION.binlog_format from STATEMENT to +--echo # MIXED when there are open temp tables. +SET SESSION binlog_format = MIXED; +SELECT @@SESSION.binlog_format; + +--echo # Test allow switching @@SESSION.binlog_format from MIXED to MIXED +--echo # when there are open temp tables. +SET SESSION binlog_format = MIXED; +SELECT @@SESSION.binlog_format; + +INSERT INTO t2 VALUES (UUID()); +SELECT @@SESSION.binlog_format; + +--echo # Test forbit switching @@SESSION.binlog_format from MIXED to STATEMENT +--echo # when there are open temp tables and we are logging in row based format. +--ERROR ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR +SET SESSION binlog_format = STATEMENT; +SELECT @@SESSION.binlog_format; + +SET SESSION binlog_format = ROW; +SELECT @@SESSION.binlog_format; + +INSERT INTO t1 VALUES ('row based'); +--echo # Test allow switching @@SESSION.binlog_format from ROW to MIXED +--echo # when there are open temp tables. +SET SESSION binlog_format = MIXED; +SELECT @@SESSION.binlog_format; + +INSERT INTO t1 VALUES ('row based'); +--echo # Test allow switching @@SESSION.binlog_format from MIXED to ROW +--echo # when there are open temp tables. +SET SESSION binlog_format = ROW; +SELECT @@SESSION.binlog_format; + +--echo # Test allow switching @@SESSION.binlog_format from ROW to ROW +--echo # when there are open temp tables. +SET SESSION binlog_format = ROW; +SELECT @@SESSION.binlog_format; + +INSERT INTO t1 VALUES ('row based'); +--echo # Test forbit switching @@SESSION.binlog_format from ROW to STATEMENT +--echo # when there are open temp tables. +--ERROR ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR +SET SESSION binlog_format = STATEMENT; +SELECT @@SESSION.binlog_format; + +DROP TEMPORARY TABLE t2; +DROP TABLE t1; + diff --git a/sql/set_var.cc b/sql/set_var.cc index d9bd14564bf..9dfbf2c7a6c 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1294,6 +1294,25 @@ bool sys_var_thd_binlog_format::check(THD *thd, set_var *var) { bool result= sys_var_thd_enum::check(thd, var); if (!result) result= check_log_update(thd, var); + /* + If RBR and open temporary tables, their CREATE TABLE may not be in the + binlog, so we can't toggle to SBR in this connection. + + If binlog_format=MIXED, there are open temporary tables, and an unsafe + statement is executed, then subsequent statements are logged in row + format and hence changes to temporary tables may be lost. So we forbid + switching @@SESSION.binlog_format from MIXED to STATEMENT when there are + open temp tables and we are logging in row format. + */ + if (thd->temporary_tables && var->type == OPT_SESSION && + var->save_result.ulong_value == BINLOG_FORMAT_STMT && + ((thd->variables.binlog_format == BINLOG_FORMAT_MIXED && + thd->is_current_stmt_binlog_format_row()) || + thd->variables.binlog_format == BINLOG_FORMAT_ROW)) + { + my_error(ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR, MYF(0)); + return 1; + } return result; } @@ -1303,23 +1322,6 @@ bool sys_var_thd_binlog_format::is_readonly() const Under certain circumstances, the variable is read-only (unchangeable): */ THD *thd= current_thd; - /* - If RBR and open temporary tables, their CREATE TABLE may not be in the - binlog, so we can't toggle to SBR in this connection. - The test below will also prevent SET GLOBAL, well it was not easy to test - if global or not here. - And this test will also prevent switching from RBR to RBR (a no-op which - should not happen too often). - - If we don't have row-based replication compiled in, the variable - is always read-only. - */ - if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW) && - thd->temporary_tables) - { - my_error(ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR, MYF(0)); - return 1; - } /* if in a stored function/trigger, it's too late to change mode */ From fda0ce99f80fd23f27c89cce1eaaa5078c742633 Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Tue, 26 Jan 2010 10:11:16 +0000 Subject: [PATCH 303/466] Removed test cases from experimental list: - rpl.rpl_slave_load_remove_tmpfile - rpl.rpl_sync --- mysql-test/collections/default.experimental | 2 -- 1 file changed, 2 deletions(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 34cf1f964b8..e34d5c24996 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -27,8 +27,6 @@ rpl.rpl_innodb_bug30888* @solaris # Bug#47646 2009-09-25 alik rpl.rpl_inn rpl.rpl_killed_ddl @windows # Bug#47638 2010-01-20 alik The rpl_killed_ddl test fails on Windows rpl.rpl_plugin_load* @solaris # Bug#47146 rpl.rpl_row_sp011* @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun -rpl.rpl_slave_load_remove_tmpfile* @windows # Bug#50474 2010-01-20 alik rpl_slave_load_remove_tmpfile failed on windows debug enabled binary -rpl.rpl_sync* @windows # Bug#50473 2010-01-20 alik rpl_sync fails on windows debug enabled binaries rpl.rpl_timezone* # Bug#47017 2009-10-27 alik rpl_timezone fails on PB-2 with mismatch error sys_vars.max_sp_recursion_depth_func @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun From 19a2c052a5ef270ab757e405e5bdb8e2d6129054 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 13:47:34 +0100 Subject: [PATCH 304/466] WL#5161: Implement Mats' suggestion of moving OS specific CMake code to OS specific files, instead of polluting code with IF(CMAKE_SYSTEM_NAME MATCHES...), first on Windows. --- CMakeLists.txt | 33 ++++--- cmake/Makefile.am | 4 +- cmake/libutils.cmake | 6 +- cmake/os/Windows.cmake | 183 ++++++++++++++++++++++++++++++++++++++ configure.cmake | 196 ++--------------------------------------- 5 files changed, 217 insertions(+), 205 deletions(-) create mode 100644 cmake/os/Windows.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f63e31d9cc0..0c40877d8ee 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,25 @@ ENDIF() PROJECT(MySQL) +# Include the platform-specific file. To allow exceptions, this code +# looks for files in order of how specific they are. If there is, for +# example, a generic Linux.cmake and a version-specific +# Linux-2.6.28-11-generic, it will pick Linux-2.6.28-11-generic and +# include it. It is then up to the file writer to include the generic +# version if necessary. +FOREACH(_base + ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_VERSION}-${CMAKE_SYSTEM_PROCESSOR} + ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_VERSION} + ${CMAKE_SYSTEM_NAME}) + SET(_file ${CMAKE_SOURCE_DIR}/cmake/os/${_base}.cmake) + IF(EXISTS ${_file}) + INCLUDE(${_file}) + BREAK() + ENDIF() +ENDFOREACH() + + + # Following autotools tradition, add preprocessor definitions # specified in environment variable CPPFLAGS IF(DEFINED ENV{CPPFLAGS}) @@ -71,12 +90,6 @@ IF(CYGWIN) SET(WIN32 0) ENDIF() -IF(WIN32) - SET(IF_WIN 0) -ELSE() - SET(IF_WIN 1) -ENDIF() - # Add macros INCLUDE(character_sets) INCLUDE(zlib) @@ -163,8 +176,6 @@ IF(WITH_DEBUG OR WITH_DEBUG_FULL AND NOT WIN32) ENDIF() - - # Set commonly used variables IF(WIN32) SET(DEFAULT_MYSQL_HOME "C:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}" ) @@ -184,12 +195,6 @@ IF(SYSCONFDIR) ENDIF() -# Optionally read user configuration, generated by configure.js. -# This is left for backward compatibility reasons only. -IF(WIN32) - INCLUDE(win/configure.data OPTIONAL) -ENDIF() - # Run platform tests INCLUDE(configure.cmake) diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 600cd442f6f..855af2e982f 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -22,4 +22,6 @@ EXTRA_DIST = \ dtrace_prelink.cmake \ versioninfo.rc.in \ mysql_add_executable.cmake \ - install_layout.cmake + install_layout.cmake \ + build_configurations/mysql_release.cmake \ + os/Windows.cmake diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index f20e21b8f86..2263b146ed6 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -155,8 +155,10 @@ MACRO(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE) ENDIF() ENDIF() ENDFOREACH() - LIST(REMOVE_DUPLICATES OSLIBS) - TARGET_LINK_LIBRARIES(${TARGET} ${OSLIBS}) + IF(OSLIBS) + LIST(REMOVE_DUPLICATES OSLIBS) + TARGET_LINK_LIBRARIES(${TARGET} ${OSLIBS}) + ENDIF() # Make the generated dummy source file depended on all static input # libs. If input lib changes,the source file is touched diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake new file mode 100644 index 00000000000..8a5ad4359d5 --- /dev/null +++ b/cmake/os/Windows.cmake @@ -0,0 +1,183 @@ +# Copyright (C) 2009 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This file includes Windows specific hacks, mostly around compiler flags + +INCLUDE (CheckCSourceCompiles) +INCLUDE (CheckCXXSourceCompiles) +INCLUDE (CheckStructHasMember) +INCLUDE (CheckLibraryExists) +INCLUDE (CheckFunctionExists) +INCLUDE (CheckCCompilerFlag) +INCLUDE (CheckCSourceRuns) +INCLUDE (CheckSymbolExists) +INCLUDE (CheckTypeSize) + +# Optionally read user configuration, generated by configure.js. +# This is left for backward compatibility reasons only. +INCLUDE(win/configure.data OPTIONAL) + +# OS display name (version_compile_os etc). +# Used by the test suite to ignore bugs on some platforms, +IF(CMAKE_SIZEOF_VOID_P MATCHES 8) + SET(SYSTEM_TYPE "Win64") +ELSE() + SET(SYSTEM_TYPE "Win32") +ENDIF() + +# Intel compiler is almost Visual C++ +# (same compile flags etc). Set MSVC flag +IF(CMAKE_C_COMPILER MATCHES "icl") + SET(MSVC TRUE) +ENDIF() + +ADD_DEFINITIONS("-D_WINDOWS -D__WIN__ -D_CRT_SECURE_NO_DEPRECATE") +ADD_DEFINITIONS("-D_WIN32_WINNT=0x0501") +# Speed up build process excluding unused header files +ADD_DEFINITIONS("-DWIN32_LEAN_AND_MEAN") + +# Adjust compiler and linker flags +IF(MINGW AND CMAKE_SIZEOF_VOIDP EQUAL 4) + # mininal architecture flags, i486 enables GCC atomics + ADD_DEFINITIONS(-march=i486) +ENDIF() + +IF(MSVC) + # Enable debug info also in Release build, and create PDB to be able to analyze + # crashes + FOREACH(lang C CXX) + SET(CMAKE_${lang}_FLAGS_RELEASE "${CMAKE_${lang}_FLAGS_RELEASE} /Zi") + ENDFOREACH() + FOREACH(type EXE SHARED MODULE) + SET(CMAKE_{type}_LINKER_FLAGS_RELEASE "${CMAKE_${type}_LINKER_FLAGS_RELEASE} /debug") + ENDFOREACH() + + # Force static runtime libraries, and remove support for exception handling + FOREACH(flag + CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG_INIT + CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG_INIT) + STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}") + STRING(REPLACE "/EHsc" "" "${flag}" "${${flag}}") + ENDFOREACH() + + # Fix CMake's predefined huge stack size + FOREACH(type EXE SHARED MODULE) + STRING(REGEX REPLACE "/STACK:([^ ]+)" "" CMAKE_${type}_LINKER_FLAGS "${CMAKE_${type}_LINKER_FLAGS}") + ENDFOREACH() + + ADD_DEFINITIONS(-DPTHREAD_STACK_MIN=1048576) + # Mark 32 bit executables large address aware so they can + # use > 2GB address space + IF(CMAKE_SIZEOF_VOID_P MATCHES 4) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") + ENDIF() + + # Speed up multiprocessor build + IF (MSVC_VERSION GREATER 1400) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + ENDIF() + + #TODO: update the code and remove the disabled warnings + IF (MSVC_VERSION GREATER 1310) + ADD_DEFINITIONS(/wd4800 /wd4805) + ADD_DEFINITIONS(/wd4996) + ENDIF() + + SET (PLATFORM X86) + IF(CMAKE_SIZEOF_VOID_P MATCHES 8) + # _WIN64 is defined by the compiler itself. + # Yet, we define it here again to work around a bug with Intellisense + # described here: http://tinyurl.com/2cb428. + # Syntax highlighting is important for proper debugger functionality. + ADD_DEFINITIONS("-D_WIN64") + SET (PLATFORM X64) + ENDIF() +ENDIF() + +# Always link with socket library +LINK_LIBRARIES(ws2_32) +# ..also for tests +SET(CMAKE_REQUIRED_LIBRARIES ws2_32) + +# System checks +SET(SIGNAL_WITH_VIO_CLOSE 1) # Something that runtime team needs + +# IPv6 constants appeared in Vista SDK first. We need to define them in any case if they are +# not in headers, to handle dual mode sockets correctly. +CHECK_SYMBOL_EXISTS(IPPROTO_IPV6 "winsock2.h" HAVE_IPPROTO_IPV6) +IF(NOT HAVE_IPPROTO_IPV6) + SET(HAVE_IPPROTO_IPV6 41) +ENDIF() +CHECK_SYMBOL_EXISTS(IPV6_V6ONLY "winsock2.h;ws2ipdef.h" HAVE_IPV6_V6ONLY) +IF(NOT HAVE_IPV6_V6ONLY) + SET(IPV6_V6ONLY 27) +ENDIF() + +# Some standard functions exist there under different +# names (e.g popen is _popen or strok_r is _strtok_s) +# If a replacement function exists, HAVE_FUNCTION is +# defined to 1. CMake variable will also +# be defined to the replacement name. +# So for example, CHECK_FUNCTION_REPLACEMENT(popen _popen) +# will define HAVE_POPEN to 1 and set variable named popen +# to _popen. If the header template, one needs to have +# cmakedefine popen @popen@ which will expand to +# define popen _popen after CONFIGURE_FILE + +MACRO(CHECK_FUNCTION_REPLACEMENT function replacement) + STRING(TOUPPER ${function} function_upper) + CHECK_FUNCTION_EXISTS(${function} HAVE_${function_upper}) + IF(NOT HAVE_${function_upper}) + CHECK_FUNCTION_EXISTS(${replacement} HAVE_${replacement}) + IF(HAVE_${replacement}) + SET(HAVE_${function_upper} 1 ) + SET(${function} ${replacement}) + ENDIF() + ENDIF() +ENDMACRO() +MACRO(CHECK_SYMBOL_REPLACEMENT symbol replacement header) + STRING(TOUPPER ${symbol} symbol_upper) + CHECK_SYMBOL_EXISTS(${symbol} ${header} HAVE_${symbol_upper}) + IF(NOT HAVE_${symbol_upper}) + CHECK_SYMBOL_EXISTS(${replacement} ${header} HAVE_${replacement}) + IF(HAVE_${replacement}) + SET(HAVE_${symbol_upper} 1) + SET(${symbol} ${replacement}) + ENDIF() + ENDIF() +ENDMACRO() + +CHECK_SYMBOL_REPLACEMENT(S_IROTH _S_IREAD sys/stat.h) +CHECK_SYMBOL_REPLACEMENT(S_IFIFO _S_IFIFO sys/stat.h) +CHECK_SYMBOL_REPLACEMENT(SIGQUIT SIGTERM signal.h) +CHECK_SYMBOL_REPLACEMENT(SIGPIPE SIGINT signal.h) +CHECK_SYMBOL_REPLACEMENT(isnan _isnan float.h) +CHECK_SYMBOL_REPLACEMENT(finite _finite float.h) +CHECK_FUNCTION_REPLACEMENT(popen _popen) +CHECK_FUNCTION_REPLACEMENT(pclose _pclose) +CHECK_FUNCTION_REPLACEMENT(access _access) +CHECK_FUNCTION_REPLACEMENT(strcasecmp _stricmp) +CHECK_FUNCTION_REPLACEMENT(strncasecmp _strnicmp) +CHECK_FUNCTION_REPLACEMENT(snprintf _snprintf) +CHECK_FUNCTION_REPLACEMENT(strtok_r strtok_s) +CHECK_FUNCTION_REPLACEMENT(strtoll _strtoi64) +CHECK_FUNCTION_REPLACEMENT(strtoull _strtoui64) +CHECK_TYPE_SIZE(ssize_t SIZE_OF_SSIZE_T) +IF(NOT HAVE_SIZE_OF_SSIZE_T) + SET(ssize_t SSIZE_T) +ENDIF() diff --git a/configure.cmake b/configure.cmake index 47d0f137d43..2c4f50568f5 100644 --- a/configure.cmake +++ b/configure.cmake @@ -46,6 +46,7 @@ ENDIF() # # Tests for OS +# IF (CMAKE_SYSTEM_NAME MATCHES "Linux") SET(TARGET_OS_LINUX 1) SET(HAVE_NPTL 1) @@ -54,17 +55,8 @@ ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS") SET(TARGET_OS_SOLARIS 1) ENDIF() - -# OS display name (version_compile_os etc). -# Used by the test suite to ignore bugs on some platforms, -# typically on Windows. -IF(WIN32) - IF(CMAKE_SIZEOF_VOID_P MATCHES 8) - SET(SYSTEM_TYPE "Win64") - ELSE() - SET(SYSTEM_TYPE "Win32") - ENDIF() -ELSE() +# System type affects version_compile_os variable +IF(NOT SYSTEM_TYPE) IF(PLATFORM) SET(SYSTEM_TYPE ${PLATFORM}) ELSE() @@ -73,12 +65,6 @@ ELSE() ENDIF() -# Intel compiler is almost Visual C++ -# (same compile flags etc). Set MSVC flag -IF(WIN32 AND CMAKE_C_COMPILER MATCHES "icl") - SET(MSVC TRUE) -ENDIF() - IF(CMAKE_COMPILER_IS_GNUCXX) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-implicit-templates -fno-exceptions -fno-rtti") @@ -89,10 +75,6 @@ IF(CMAKE_COMPILER_IS_GNUCXX) SET(HAVE_EXPLICIT_TEMPLATE_INSTANTIATION TRUE) ENDIF() ENDIF() - IF(MINGW AND CMAKE_SIZEOF_VOIDP EQUAL 4) - # mininal architecture flags, i486 enables GCC atomics - ADD_DEFINITIONS(-march=i486) - ENDIF() IF(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) # Workaround linker problems on OSX 10.4 IF(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.5") @@ -116,16 +98,6 @@ IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") ENDIF() - - -IF(MSVC) -# Enable debug info also in Release build, and create PDB to be able to analyze -# crashes - SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") - SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi") - SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /debug") -ENDIF() - IF(CMAKE_GENERATOR MATCHES "Visual Studio 7") # VS2003 has a bug that prevents linking mysqld with module definition file # (/DEF option for linker). Linker would incorrectly complain about multiply @@ -181,69 +153,6 @@ IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") ADD_DEFINITIONS(-DHAVE_RWLOCK_T) ENDIF() -# Disable warnings in Visual Studio 8 and above -IF(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7") - #TODO: update the code and remove the disabled warnings - ADD_DEFINITIONS(/wd4800 /wd4805) - ADD_DEFINITIONS(/wd4996) -ENDIF() - - -# Settings for Visual Studio 7 and above. -IF(MSVC) - # replace /MDd with /MTd - STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) - STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG_INIT ${CMAKE_C_FLAGS_DEBUG_INIT}) - - STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) - STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_CXX_FLAGS_DEBUG_INIT}) - - # generate map files, set stack size (see bug#20815) - SET(thread_stack_size 1048576) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:${thread_stack_size}") - ADD_DEFINITIONS(-DPTHREAD_STACK_MIN=${thread_stack_size}) - - # remove support for Exception handling - STRING(REPLACE "/GX" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS_INIT ${CMAKE_CXX_FLAGS_INIT}) - STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_CXX_FLAGS_DEBUG_INIT}) - - # Mark 32 bit executables large address aware so they can - # use > 2GB address space - IF(CMAKE_SIZEOF_VOID_P MATCHES 4) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") - ENDIF(CMAKE_SIZEOF_VOID_P MATCHES 4) -ENDIF(MSVC) - -IF(WIN32) - ADD_DEFINITIONS("-D_WINDOWS -D__WIN__ -D_CRT_SECURE_NO_DEPRECATE") - ADD_DEFINITIONS("-D_WIN32_WINNT=0x0501") - # Speed up build process excluding unused header files - ADD_DEFINITIONS("-DWIN32_LEAN_AND_MEAN") - IF (MSVC_VERSION GREATER 1400) - # Speed up multiprocessor build - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") - ENDIF() - - # default to x86 platform. We'll check for X64 in a bit - SET (PLATFORM X86) - IF(MSVC AND CMAKE_SIZEOF_VOID_P MATCHES 8) - # _WIN64 is defined by the compiler itself. - # Yet, we define it here again to work around a bug with Intellisense - # described here: http://tinyurl.com/2cb428. - # Syntax highlighting is important for proper debugger functionality. - ADD_DEFINITIONS("-D_WIN64") - SET (PLATFORM X64) - ENDIF() -ENDIF() - - # Figure out what engines to build and how (statically or dynamically), # add preprocessor defines for storage engines. @@ -252,15 +161,6 @@ IF(WITHOUT_DYNAMIC_PLUGINS) ENDIF(WITHOUT_DYNAMIC_PLUGINS) -# Perform machine tests on posix platforms only -IF(WIN32) - SET(SYSTEM_LIBS ws2_32) - SET(CMAKE_REQUIRED_INCLUDES "winsock2.h;ws2tcpip.h") - SET(CMAKE_REQUIRED_DEFINITONS "-D_WIN32_WINNT=0x0501") - SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ws2_32) - LINK_LIBRARIES(ws2_32) -ENDIF() - # Searches function in libraries # if function is found, sets output parameter result to the name of the library # if function is found in libc, result will be empty @@ -345,43 +245,12 @@ IF(CMAKE_OSX_SYSROOT) SET(ENV{MACOSX_DEPLOYMENT_TARGET} ${OSX_DEPLOYMENT_TARGET}) ENDIF() -# This macro is used only on Windows at the moment -# Some standard functions exist there under different -# names (e.g popen is _popen or strok_r is _strtok_s) -# If a replacement function exists, HAVE_FUNCTION is -# defined to 1. CMake variable will also -# be defined to the replacement name. -# So for example, CHECK_FUNCTION_REPLACEMENT(popen _popen) -# will define HAVE_POPEN to 1 and set variable named popen -# to _popen. If the header template, one needs to have -# cmakedefine popen @popen@ which will expand to -# define popen _popen after CONFIGURE_FILE - -MACRO(CHECK_FUNCTION_REPLACEMENT function replacement) - STRING(TOUPPER ${function} function_upper) - CHECK_FUNCTION_EXISTS(${function} HAVE_${function_upper}) - IF(NOT HAVE_${function_upper}) - CHECK_FUNCTION_EXISTS(${replacement} HAVE_${replacement}) - IF(HAVE_${replacement}) - SET(HAVE_${function_upper} 1 ) - SET(${function} ${replacement}) - ENDIF() - ENDIF() -ENDMACRO() - -MACRO(CHECK_SYMBOL_REPLACEMENT symbol replacement header) - STRING(TOUPPER ${symbol} symbol_upper) - CHECK_SYMBOL_EXISTS(${symbol} ${header} HAVE_${symbol_upper}) - IF(NOT HAVE_${symbol_upper}) - CHECK_SYMBOL_EXISTS(${replacement} ${header} HAVE_${replacement}) - IF(HAVE_${replacement}) - SET(HAVE_${symbol_upper} 1) - SET(${symbol} ${replacement}) - ENDIF() - ENDIF() -ENDMACRO() - +# System check macros that do nothing on Windows. +# Very often, it is known that some function is not available +# on Windows. In such cases it makes sense to use these macros +# as build with Visual Studio is considerably faster if irrelevant +# checks are omitted. MACRO(CHECK_INCLUDE_FILES_UNIX INCLUDES VAR) IF(UNIX) CHECK_INCLUDE_FILES ("${INCLUDES}" ${VAR}) @@ -1333,52 +1202,3 @@ CHECK_STRUCT_HAS_MEMBER("struct dirent" d_ino "dirent.h" STRUCT_DIRENT_HAS_D_IN CHECK_STRUCT_HAS_MEMBER("struct dirent" d_namlen "dirent.h" STRUCT_DIRENT_HAS_D_NAMLEN) SET(SPRINTF_RETURNS_INT 1) -IF(WIN32) - SET(SIGNAL_WITH_VIO_CLOSE 1) - CHECK_SYMBOL_REPLACEMENT(S_IROTH _S_IREAD sys/stat.h) - CHECK_SYMBOL_REPLACEMENT(S_IFIFO _S_IFIFO sys/stat.h) - CHECK_SYMBOL_REPLACEMENT(SIGQUIT SIGTERM signal.h) - CHECK_SYMBOL_REPLACEMENT(SIGPIPE SIGINT signal.h) - CHECK_SYMBOL_REPLACEMENT(isnan _isnan float.h) - CHECK_SYMBOL_REPLACEMENT(finite _finite float.h) - CHECK_FUNCTION_REPLACEMENT(popen _popen) - CHECK_FUNCTION_REPLACEMENT(pclose _pclose) - CHECK_FUNCTION_REPLACEMENT(access _access) - CHECK_FUNCTION_REPLACEMENT(strcasecmp _stricmp) - CHECK_FUNCTION_REPLACEMENT(strncasecmp _strnicmp) - CHECK_FUNCTION_REPLACEMENT(snprintf _snprintf) - CHECK_FUNCTION_REPLACEMENT(strtok_r strtok_s) - CHECK_FUNCTION_REPLACEMENT(strtoll _strtoi64) - CHECK_FUNCTION_REPLACEMENT(strtoull _strtoui64) - CHECK_TYPE_SIZE(ssize_t SIZE_OF_SSIZE_T) - IF(NOT SIZE_OF_SSIZE_T) - SET(ssize_t SSIZE_T) - ENDIF() - - - # IPv6 definition (appeared in Vista SDK first) - CHECK_C_SOURCE_COMPILES(" - #include - int main() - { - return IPPROTO_IPV6; - }" - HAVE_IPPROTO_IPV6) - - CHECK_C_SOURCE_COMPILES(" - #include - #include - int main() - { - return IPV6_V6ONLY; - }" - HAVE_IPV6_V6ONLY) - - IF(NOT HAVE_IPPROTO_IPV6) - SET(HAVE_IPPROTO_IPV6 41) - ENDIF() - IF(NOT HAVE_IPV6_V6ONLY) - SET(IPV6_V6ONLY 27) - ENDIF() - -ENDIF(WIN32) From 3a7be724dc056a41642b484f56da0859c96dbd94 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 15:56:50 +0100 Subject: [PATCH 305/466] WL#5161, implement Mats' suggeston to move platform specifics into own files under cmake/os. This patch does it for Linux --- cmake/Makefile.am | 3 +- cmake/os/Linux.cmake | 47 +++++++++++++++++++++++++++++++ cmake/os/Windows.cmake | 2 +- configure.cmake | 64 ++++++++---------------------------------- 4 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 cmake/os/Linux.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 855af2e982f..95c88ebd8e2 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -24,4 +24,5 @@ EXTRA_DIST = \ mysql_add_executable.cmake \ install_layout.cmake \ build_configurations/mysql_release.cmake \ - os/Windows.cmake + os/Windows.cmake \ + os/Linux.cmake diff --git a/cmake/os/Linux.cmake b/cmake/os/Linux.cmake new file mode 100644 index 00000000000..946e020d6f4 --- /dev/null +++ b/cmake/os/Linux.cmake @@ -0,0 +1,47 @@ + +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This file includes Linux specific options and quirks, related to system checks + +INCLUDE(CheckSymbolExists) + +# Something that needs to be set on legacy reasons +SET(TARGET_OS_LINUX 1) +SET(HAVE_NPTL 1) +SET(_GNU_SOURCE 1) + +# Fix CMake (< 2.8) flags. -rdynamic exports too many symbols. +FOREACH(LANG C CXX) + STRING(REPLACE "-rdynamic" "" + CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS + ${CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS} + ) +ENDFOREACH() + +# Ensure we have clean build for shared libraries +# without unresolved symbols +SET(LINK_FLAG_NO_UNDEFINED "--Wl,--no-undefined") + +# 64 bit file offset support flag +SET(_FILE_OFFSET_BITS 64) + +# Linux specific HUGETLB /large page support +CHECK_SYMBOL_EXISTS(SHM_HUGETLB sys/shm.h HAVE_DECL_SHM_HUGETLB) +IF(HAVE_DECL_SHM_HUGETLB) + SET(HAVE_LARGE_PAGES 1) + SET(HUGETLB_USE_PROC_MEMINFO 1) + SET(HAVE_LARGE_PAGE_OPTION 1) +ENDIF() diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake index 8a5ad4359d5..42917d0893d 100644 --- a/cmake/os/Windows.cmake +++ b/cmake/os/Windows.cmake @@ -1,4 +1,4 @@ -# Copyright (C) 2009 Sun Microsystems, Inc +# Copyright (C) 2010 Sun Microsystems, 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 diff --git a/configure.cmake b/configure.cmake index 2c4f50568f5..7f13a11572d 100644 --- a/configure.cmake +++ b/configure.cmake @@ -44,14 +44,8 @@ IF(UNIX) ENDIF() -# -# Tests for OS -# -IF (CMAKE_SYSTEM_NAME MATCHES "Linux") - SET(TARGET_OS_LINUX 1) - SET(HAVE_NPTL 1) - SET(_GNU_SOURCE 1) -ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") SET(TARGET_OS_SOLARIS 1) ENDIF() @@ -66,6 +60,8 @@ ENDIF() IF(CMAKE_COMPILER_IS_GNUCXX) + # MySQL "canonical" GCC flags. At least -fno-rtti flag affects + # ABI and cannot be simply removed. SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-implicit-templates -fno-exceptions -fno-rtti") IF(CMAKE_CXX_FLAGS) @@ -75,6 +71,12 @@ IF(CMAKE_COMPILER_IS_GNUCXX) SET(HAVE_EXPLICIT_TEMPLATE_INSTANTIATION TRUE) ENDIF() ENDIF() + + IF (CMAKE_EXE_LINKER_FLAGS MATCHES " -static " + OR CMAKE_EXE_LINKER_FLAGS MATCHES " -static$") + SET(HAVE_DLOPEN FALSE CACHE "Disable dlopen due to -static flag" FORCE) + SET(WITHOUT_DYNAMIC_PLUGINS TRUE) + ENDIF() IF(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) # Workaround linker problems on OSX 10.4 IF(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.5") @@ -90,7 +92,7 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") SET(_LARGEFILE64_SOURCE 1) SET(_FILE_OFFSET_BITS 64) ENDIF() -IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS" ) +IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS" ) SET(_FILE_OFFSET_BITS 64) ENDIF() IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") @@ -98,22 +100,6 @@ IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") ENDIF() -IF(CMAKE_GENERATOR MATCHES "Visual Studio 7") - # VS2003 has a bug that prevents linking mysqld with module definition file - # (/DEF option for linker). Linker would incorrectly complain about multiply - # defined symbols. Workaround is to disable dynamic plugins, so /DEF is not - # used. - MESSAGE( - "Warning: Building MySQL with Visual Studio 2003.NET is no more supported.") - MESSAGE("Please use a newer version of Visual Studio.") - SET(WITHOUT_DYNAMIC_PLUGINS TRUE) - - # VS2003 needs the /Op compiler option to disable floating point - # optimizations - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Op") - SET(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /Op") -ENDIF() - IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) IF(CMAKE_SIZEOF_VOID_P EQUAL 4) @@ -129,18 +115,6 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) ENDIF() ENDIF() -# Ensure we have clean build for shared libraries -# without extra dependencies and without unresolved symbols -# (on system that support it) -IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") - FOREACH(LANG C CXX) - STRING(REPLACE "-rdynamic" "" - CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS - ${CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS} - ) - ENDFOREACH() - SET(LINK_FLAG_NO_UNDEFINED "--Wl,--no-undefined") -ENDIF() #Some OS specific hacks IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") @@ -389,13 +363,6 @@ CHECK_FUNCTION_EXISTS_UNIX (compress HAVE_COMPRESS) CHECK_FUNCTION_EXISTS_UNIX (crypt HAVE_CRYPT) CHECK_FUNCTION_EXISTS_UNIX (dlerror HAVE_DLERROR) CHECK_FUNCTION_EXISTS_UNIX (dlopen HAVE_DLOPEN) -IF (CMAKE_COMPILER_IS_GNUCC) - IF (CMAKE_EXE_LINKER_FLAGS MATCHES " -static " - OR CMAKE_EXE_LINKER_FLAGS MATCHES " -static$") - SET(HAVE_DLOPEN FALSE CACHE "Disable dlopen due to -static flag" FORCE) - SET(WITHOUT_DYNAMIC_PLUGINS TRUE) - ENDIF() -ENDIF() CHECK_FUNCTION_EXISTS_UNIX (fchmod HAVE_FCHMOD) CHECK_FUNCTION_EXISTS_UNIX (fcntl HAVE_FCNTL) CHECK_FUNCTION_EXISTS_UNIX (fconvert HAVE_FCONVERT) @@ -1005,14 +972,7 @@ CHECK_CXX_SOURCE_COMPILES_UNIX(" }" HAVE_GETHOSTBYNAME_R_RETURN_INT) -IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") - CHECK_SYMBOL_EXISTS(SHM_HUGETLB sys/shm.h HAVE_DECL_SHM_HUGETLB) - IF(HAVE_DECL_SHM_HUGETLB) - SET(HAVE_LARGE_PAGES 1) - SET(HUGETLB_USE_PROC_MEMINFO 1) - SET(HAVE_LARGE_PAGE_OPTION 1) - ENDIF() -ENDIF() + IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") CHECK_SYMBOL_EXISTS(MHA_MAPSIZE_VA sys/mman.h HAVE_DECL_MHA_MAPSIZE_VA) From 25d8787a32d0142bf2db869ab97570d730a7ee91 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 15:43:53 +0000 Subject: [PATCH 306/466] WL#5161: Following Mats' suggestion, moved Solaris specific workaround to cmake/os/SunOS.cmake --- cmake/Makefile.am | 3 +- cmake/os/SunOS.cmake | 71 ++++++++++++++++++++++++++++++++++++++++++++ configure.cmake | 68 +++--------------------------------------- 3 files changed, 77 insertions(+), 65 deletions(-) create mode 100644 cmake/os/SunOS.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 95c88ebd8e2..2e6563cd92c 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -25,4 +25,5 @@ EXTRA_DIST = \ install_layout.cmake \ build_configurations/mysql_release.cmake \ os/Windows.cmake \ - os/Linux.cmake + os/Linux.cmake \ + os/SunOS.cmake diff --git a/cmake/os/SunOS.cmake b/cmake/os/SunOS.cmake new file mode 100644 index 00000000000..3d883eeb9c8 --- /dev/null +++ b/cmake/os/SunOS.cmake @@ -0,0 +1,71 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE(CheckSymbolExists) +INCLUDE(CheckCSourceRuns) + + +SET(TARGET_OS_SOLARIS 1) +# Enable 64 bit file offsets +SET(_FILE_OFFSET_BITS 64) + +# Legacy option, without it my_pthread is having problems +ADD_DEFINITIONS(-DHAVE_RWLOCK_T) + +# On Solaris, use of intrinsics will screw the lib search logic +# Force using -lm, so rint etc are found. +SET(LIBM m) + +# CMake defined -lthread as thread flag. This crashes in dlopen +# when trying to load plugins workaround with -lpthread +SET(CMAKE_THREADS_LIBS_INIT -lpthread CACHE INTERNAL "") + +# Solaris specific large page support +CHECK_SYMBOL_EXISTS(MHA_MAPSIZE_VA sys/mman.h HAVE_DECL_MHA_MAPSIZE_VA) +IF(HAVE_DECL_MHA_MAPSIZE_VA) + SET(HAVE_SOLARIS_LARGE_PAGES 1) + SET(HAVE_LARGE_PAGE_OPTION 1) +ENDIF() + + +# Solaris atomics +CHECK_C_SOURCE_RUNS( + " + #include + int main() + { + int foo = -10; int bar = 10; + int64_t foo64 = -10; int64_t bar64 = 10; + if (atomic_add_int_nv((uint_t *)&foo, bar) || foo) + return -1; + bar = atomic_swap_uint((uint_t *)&foo, (uint_t)bar); + if (bar || foo != 10) + return -1; + bar = atomic_cas_uint((uint_t *)&bar, (uint_t)foo, 15); + if (bar) + return -1; + if (atomic_add_64_nv((volatile uint64_t *)&foo64, bar64) || foo64) + return -1; + bar64 = atomic_swap_64((volatile uint64_t *)&foo64, (uint64_t)bar64); + if (bar64 || foo64 != 10) + return -1; + bar64 = atomic_cas_64((volatile uint64_t *)&bar64, (uint_t)foo64, 15); + if (bar64) + return -1; + atomic_or_64((volatile uint64_t *)&bar64, 0); + return 0; + } +" HAVE_SOLARIS_ATOMIC) + diff --git a/configure.cmake b/configure.cmake index 7f13a11572d..31d211256e8 100644 --- a/configure.cmake +++ b/configure.cmake @@ -45,10 +45,6 @@ ENDIF() -IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") - SET(TARGET_OS_SOLARIS 1) -ENDIF() - # System type affects version_compile_os variable IF(NOT SYSTEM_TYPE) IF(PLATFORM) @@ -92,9 +88,6 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") SET(_LARGEFILE64_SOURCE 1) SET(_FILE_OFFSET_BITS 64) ENDIF() -IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS" ) - SET(_FILE_OFFSET_BITS 64) -ENDIF() IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") SET(_LARGE_FILES 1) ENDIF() @@ -123,10 +116,6 @@ ELSEIF(CMAKE_SYSTEM MATCHES "HP-UX" AND CMAKE_SYSTEM MATCHES "11") ADD_DEFINITIONS(-DHPUX11) ENDIF() -IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") - ADD_DEFINITIONS(-DHAVE_RWLOCK_T) -ENDIF() - # Figure out what engines to build and how (statically or dynamically), # add preprocessor defines for storage engines. @@ -139,6 +128,10 @@ ENDIF(WITHOUT_DYNAMIC_PLUGINS) # if function is found, sets output parameter result to the name of the library # if function is found in libc, result will be empty FUNCTION(MY_SEARCH_LIBS func libs result) + IF(${${result}}) + # Library is already found or was predefined + RETURN() + ENDIF() CHECK_FUNCTION_EXISTS(${func} HAVE_${func}_IN_LIBC) IF(HAVE_${func}_IN_LIBC) SET(${result} "" PARENT_SCOPE) @@ -158,13 +151,6 @@ IF(UNIX) IF(NOT LIBM) MY_SEARCH_LIBS(__infinity m LIBM) ENDIF() - - IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") - # On Solaris, use of intrinsics will screw the lib search logic - # Force using -lm, so rint etc are found. - SET(LIBM m) - ENDIF() - MY_SEARCH_LIBS(gethostbyname_r "nsl_r;nsl" LIBNSL) MY_SEARCH_LIBS(bind "bind;socket" LIBBIND) MY_SEARCH_LIBS(crypt crypt LIBCRYPT) @@ -176,13 +162,6 @@ IF(UNIX) ENDIF() FIND_PACKAGE(Threads) - IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") - # CMake defined -lthread as thread flag - # This crashes in dlopen when trying to load plugins - # Workaround with -lpthread - SET(CMAKE_THREADS_LIBS_INIT -lpthread) - ENDIF() - SET(CMAKE_REQUIRED_LIBRARIES ${LIBM} ${LIBNSL} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT} ${LIBRT}) @@ -973,15 +952,6 @@ CHECK_CXX_SOURCE_COMPILES_UNIX(" HAVE_GETHOSTBYNAME_R_RETURN_INT) - -IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") - CHECK_SYMBOL_EXISTS(MHA_MAPSIZE_VA sys/mman.h HAVE_DECL_MHA_MAPSIZE_VA) - IF(HAVE_DECL_MHA_MAPSIZE_VA) - SET(HAVE_SOLARIS_LARGE_PAGES 1) - SET(HAVE_LARGE_PAGE_OPTION 1) - ENDIF() -ENDIF() - # Use of ALARMs to wakeup on timeout on sockets # # This feature makes use of a mutex and is a scalability hog we @@ -1086,36 +1056,6 @@ configuration. By default gcc built-in sync functions are used, if available and 'smp' configuration otherwise.") MARK_AS_ADVANCED(WITH_ATOMIC_LOCKS MY_ATOMIC_MODE_RWLOCK MY_ATOMIC_MODE_DUMMY) -IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") - CHECK_C_SOURCE_RUNS( - " - #include - int main() - { - int foo = -10; int bar = 10; - int64_t foo64 = -10; int64_t bar64 = 10; - if (atomic_add_int_nv((uint_t *)&foo, bar) || foo) - return -1; - bar = atomic_swap_uint((uint_t *)&foo, (uint_t)bar); - if (bar || foo != 10) - return -1; - bar = atomic_cas_uint((uint_t *)&bar, (uint_t)foo, 15); - if (bar) - return -1; - if (atomic_add_64_nv((volatile uint64_t *)&foo64, bar64) || foo64) - return -1; - bar64 = atomic_swap_64((volatile uint64_t *)&foo64, (uint64_t)bar64); - if (bar64 || foo64 != 10) - return -1; - bar64 = atomic_cas_64((volatile uint64_t *)&bar64, (uint_t)foo64, 15); - if (bar64) - return -1; - atomic_or_64((volatile uint64_t *)&bar64, 0); - return 0; - } -" HAVE_SOLARIS_ATOMIC) -ENDIF() - #-------------------------------------------------------------------- # Check for IPv6 support #-------------------------------------------------------------------- From e5154ba9c07a41b3c6ab1b6b48e7271245cc4b77 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 18:09:14 +0100 Subject: [PATCH 307/466] Move OSX specific checks to cmake/os/Darwin.cmake --- cmake/Makefile.am | 5 ++++- cmake/os/Darwin.cmake | 34 ++++++++++++++++++++++++++++++++++ configure.cmake | 21 +++++---------------- 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 cmake/os/Darwin.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 2e6563cd92c..623176288b7 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -26,4 +26,7 @@ EXTRA_DIST = \ build_configurations/mysql_release.cmake \ os/Windows.cmake \ os/Linux.cmake \ - os/SunOS.cmake + os/SunOS.cmake \ + os/Darwin.cmake + + diff --git a/cmake/os/Darwin.cmake b/cmake/os/Darwin.cmake new file mode 100644 index 00000000000..09d82bed7bd --- /dev/null +++ b/cmake/os/Darwin.cmake @@ -0,0 +1,34 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This file includes OSX specific options and quirks, related to system checks + +# Workaround for CMake bug#9051 +# (CMake does not pass CMAKE_OSX_SYSROOT and CMAKE_OSX_DEPLOYMENT_TARGET when +# running TRY_COMPILE) + +IF(CMAKE_OSX_SYSROOT) + SET(ENV{CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}) +ENDIF() +IF(CMAKE_OSX_SYSROOT) + SET(ENV{MACOSX_DEPLOYMENT_TARGET} ${OSX_DEPLOYMENT_TARGET}) +ENDIF() + +IF(CMAKE_OSX_DEPLOYMENT_TARGET) + # Workaround linker problems on OSX 10.4 + IF(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.5") + ADD_DEFINITIONS(-fno-common) + ENDIF() +ENDIF() diff --git a/configure.cmake b/configure.cmake index 31d211256e8..4493fd900bc 100644 --- a/configure.cmake +++ b/configure.cmake @@ -1,3 +1,4 @@ + # Copyright (C) 2009 Sun Microsystems,Inc # # This program is free software; you can redistribute it and/or modify @@ -73,12 +74,6 @@ IF(CMAKE_COMPILER_IS_GNUCXX) SET(HAVE_DLOPEN FALSE CACHE "Disable dlopen due to -static flag" FORCE) SET(WITHOUT_DYNAMIC_PLUGINS TRUE) ENDIF() - IF(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) - # Workaround linker problems on OSX 10.4 - IF(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.5") - ADD_DEFINITIONS(-fno-common) - ENDIF() - ENDIF() ENDIF() @@ -189,15 +184,7 @@ IF(UNIX) ENDIF() ENDIF() ENDIF() - -# Workaround for CMake bug#9051 -IF(CMAKE_OSX_SYSROOT) - SET(ENV{CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}) -ENDIF() -IF(CMAKE_OSX_SYSROOT) - SET(ENV{MACOSX_DEPLOYMENT_TARGET} ${OSX_DEPLOYMENT_TARGET}) -ENDIF() - +s # System check macros that do nothing on Windows. # Very often, it is known that some function is not available @@ -517,7 +504,7 @@ int main() { # INCLUDE(TestBigEndian) IF(APPLE) - # Cannot run endian test on universal PPC/Intel binaries + # Cannot run endian test on universal PPC/Intel binaries # would return inconsistent result. # config.h.cmake includes a special #ifdef for Darwin ELSE() @@ -550,6 +537,8 @@ IF(HAVE_STDINT_H) ENDIF(HAVE_STDINT_H) IF(NOT APPLE) + # Prevent some checks on OSX, they return ambigious results + # on universal 32/64 bit binariess CHECK_TYPE_SIZE("char *" SIZEOF_CHARP) CHECK_TYPE_SIZE(long SIZEOF_LONG) CHECK_TYPE_SIZE(size_t SIZEOF_SIZE_T) From 0bdfa9d5eb7298e3a812ccc4b79a1b29faaf750b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 18:13:37 +0100 Subject: [PATCH 308/466] Move FreeBSD specific hacks to cmake/os/FreeBSD.cmake --- cmake/os/FreeBSD.cmake | 20 ++++++++++++++++++++ configure.cmake | 4 +--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 cmake/os/FreeBSD.cmake diff --git a/cmake/os/FreeBSD.cmake b/cmake/os/FreeBSD.cmake new file mode 100644 index 00000000000..0b958c61315 --- /dev/null +++ b/cmake/os/FreeBSD.cmake @@ -0,0 +1,20 @@ + +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This file includes FreeBSD specific options and quirks, related to system checks +#Legacy option, maybe not needed anymore , taken as is from autotools build +ADD_DEFINITIONS(-DNET_RETRY_COUNT=1000000) + diff --git a/configure.cmake b/configure.cmake index 4493fd900bc..b42a92cf7c5 100644 --- a/configure.cmake +++ b/configure.cmake @@ -105,9 +105,7 @@ ENDIF() #Some OS specific hacks -IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - ADD_DEFINITIONS(-DNET_RETRY_COUNT=1000000) -ELSEIF(CMAKE_SYSTEM MATCHES "HP-UX" AND CMAKE_SYSTEM MATCHES "11") +IF(CMAKE_SYSTEM MATCHES "HP-UX" AND CMAKE_SYSTEM MATCHES "11") ADD_DEFINITIONS(-DHPUX11) ENDIF() From 1f07f5c18ea502445ad04159b7ba6806bc356677 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 22:05:41 +0100 Subject: [PATCH 309/466] Fixed typos and failed mysqld--help test. --- cmake/os/Windows.cmake | 6 +++++- cmake/plugin.cmake | 5 ++--- configure.cmake | 1 - mysql-test/include/mysqld--help.inc | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake index 42917d0893d..5573ecf701e 100644 --- a/cmake/os/Windows.cmake +++ b/cmake/os/Windows.cmake @@ -64,13 +64,17 @@ IF(MSVC) SET(CMAKE_{type}_LINKER_FLAGS_RELEASE "${CMAKE_${type}_LINKER_FLAGS_RELEASE} /debug") ENDFOREACH() - # Force static runtime libraries, and remove support for exception handling + # Force static runtime libraries FOREACH(flag CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG_INIT CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG_INIT) STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}") + ENDFOREACH() + + # Remove support for exceptions + FOREACH(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_INIT) STRING(REPLACE "/EHsc" "" "${flag}" "${${flag}}") ENDFOREACH() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index f312cd2d4e8..f4aaeb651d7 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -111,10 +111,9 @@ MACRO(MYSQL_ADD_PLUGIN) # Update mysqld dependencies SET (MYSQLD_STATIC_PLUGIN_LIBS ${MYSQLD_STATIC_PLUGIN_LIBS} - ${target} CACHE INTERNAL "") + ${target} CACHE INTERNAL "" FORCE) - - IF(ARG_STORAGE_ENGINE) + IF(NOT ARG_MANDATORY) SET(${with_var} ON CACHE BOOL "Link ${plugin} statically to the server" FORCE) ENDIF() diff --git a/configure.cmake b/configure.cmake index b42a92cf7c5..663e3db9a66 100644 --- a/configure.cmake +++ b/configure.cmake @@ -182,7 +182,6 @@ IF(UNIX) ENDIF() ENDIF() ENDIF() -s # System check macros that do nothing on Windows. # Very often, it is known that some function is not available diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc index e40e2a62a74..e318823d8af 100644 --- a/mysql-test/include/mysqld--help.inc +++ b/mysql-test/include/mysqld--help.inc @@ -14,7 +14,7 @@ perl; log-slow-queries pid-file slow-query-log-file datadir slave-load-tmpdir tmpdir/; @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster safemalloc debug temp-pool ssl des-key-file - thread-concurrency super-large-pages mutex-deadlock-detector/; + thread-concurrency super-large-pages mutex-deadlock-detector null-audit/; @env=qw/MYSQLTEST_VARDIR MYSQL_TEST_DIR MYSQL_LIBDIR MYSQL_CHARSETSDIR MYSQL_SHAREDIR /; $re1=join('|', @skipvars, @plugins); $re2=join('|', @plugins); From 8f68e3ee5668e97950730ad001c98e8697ae863e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2010 23:47:57 +0100 Subject: [PATCH 310/466] Fix failing test cases on pushbuild --- cmake/plugin.cmake | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index f4aaeb651d7..2c00c71c3ee 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -113,9 +113,12 @@ MACRO(MYSQL_ADD_PLUGIN) SET (MYSQLD_STATIC_PLUGIN_LIBS ${MYSQLD_STATIC_PLUGIN_LIBS} ${target} CACHE INTERNAL "" FORCE) - IF(NOT ARG_MANDATORY) + IF(ARG_MANDATORY) + SET(${with_var} ON CACHE INTERNAL "Link ${plugin} statically to the server" + FORCE) + ELSE() SET(${with_var} ON CACHE BOOL "Link ${plugin} statically to the server" - FORCE) + FORCE) ENDIF() IF(ARG_MANDATORY) From 8e5e420270687a16870246ad6192032232fffac4 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Tue, 26 Jan 2010 16:42:54 -0700 Subject: [PATCH 311/466] Bug#44210 Performance schema: pool-of-threads threads instrumentation is missing WL#5136 Pool of threads Added an explicit delete_thread() API in the instrumentation, to be used by the pool of threads implementations. --- include/mysql/psi/psi.h | 7 ++++++- include/mysql/psi/psi_abi_v1.h.pp | 2 ++ storage/perfschema/pfs.cc | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/mysql/psi/psi.h b/include/mysql/psi/psi.h index a9277cd18bd..51446fa83a5 100644 --- a/include/mysql/psi/psi.h +++ b/include/mysql/psi/psi.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2008-2009 Sun Microsystems, Inc +/* Copyright (C) 2008-2010 Sun Microsystems, 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 @@ -614,6 +614,9 @@ typedef void (*set_thread_v1_t)(struct PSI_thread *thread); /** Delete the current thread instrumentation. */ typedef void (*delete_current_thread_v1_t)(void); +/** Delete a thread instrumentation. */ +typedef void (*delete_thread_v1_t)(struct PSI_thread *thread); + /** Get a mutex instrumentation locker. @param mutex the instrumented mutex to lock @@ -890,6 +893,8 @@ struct PSI_v1 set_thread_v1_t set_thread; /** @sa delete_current_thread_v1_t. */ delete_current_thread_v1_t delete_current_thread; + /** @sa delete_thread_v1_t. */ + delete_thread_v1_t delete_thread; /** @sa get_thread_mutex_locker_v1_t. */ get_thread_mutex_locker_v1_t get_thread_mutex_locker; /** @sa get_thread_rwlock_locker_v1_t. */ diff --git a/include/mysql/psi/psi_abi_v1.h.pp b/include/mysql/psi/psi_abi_v1.h.pp index aedf28ba694..6ecd0f3098d 100644 --- a/include/mysql/psi/psi_abi_v1.h.pp +++ b/include/mysql/psi/psi_abi_v1.h.pp @@ -127,6 +127,7 @@ typedef void (*set_thread_id_v1_t)(struct PSI_thread *thread, typedef struct PSI_thread* (*get_thread_v1_t)(void); typedef void (*set_thread_v1_t)(struct PSI_thread *thread); typedef void (*delete_current_thread_v1_t)(void); +typedef void (*delete_thread_v1_t)(struct PSI_thread *thread); typedef struct PSI_mutex_locker* (*get_thread_mutex_locker_v1_t) (struct PSI_mutex *mutex, enum PSI_mutex_operation op); typedef struct PSI_rwlock_locker* (*get_thread_rwlock_locker_v1_t) @@ -204,6 +205,7 @@ struct PSI_v1 get_thread_v1_t get_thread; set_thread_v1_t set_thread; delete_current_thread_v1_t delete_current_thread; + delete_thread_v1_t delete_thread; get_thread_mutex_locker_v1_t get_thread_mutex_locker; get_thread_rwlock_locker_v1_t get_thread_rwlock_locker; get_thread_cond_locker_v1_t get_thread_cond_locker; diff --git a/storage/perfschema/pfs.cc b/storage/perfschema/pfs.cc index 01b4b3711c1..380801c8677 100644 --- a/storage/perfschema/pfs.cc +++ b/storage/perfschema/pfs.cc @@ -1081,6 +1081,13 @@ static void delete_current_thread_v1(void) } } +static void delete_thread_v1(PSI_thread *thread) +{ + PFS_thread *pfs= reinterpret_cast (thread); + if (pfs != NULL) + destroy_thread(pfs); +} + static PSI_mutex_locker* get_thread_mutex_locker_v1(PSI_mutex *mutex, PSI_mutex_operation op) { @@ -2007,6 +2014,7 @@ PSI_v1 PFS_v1= get_thread_v1, set_thread_v1, delete_current_thread_v1, + delete_thread_v1, get_thread_mutex_locker_v1, get_thread_rwlock_locker_v1, get_thread_cond_locker_v1, From a144dc31acbf82f8758969faff5330377d3bfbcc Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 27 Jan 2010 01:50:19 +0100 Subject: [PATCH 312/466] fix default architecture flag (v8) on sparc32 gcc that prevent mysql from compilng, add HPUX.cmake --- cmake/Makefile.am | 4 ++-- cmake/os/HP-UX.cmake | 47 ++++++++++++++++++++++++++++++++++++++++++++ cmake/os/SunOS.cmake | 27 ++++++++++++++++++++++++- configure.cmake | 29 +-------------------------- 4 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 cmake/os/HP-UX.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index 623176288b7..c8341cb14cb 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -27,6 +27,6 @@ EXTRA_DIST = \ os/Windows.cmake \ os/Linux.cmake \ os/SunOS.cmake \ - os/Darwin.cmake - + os/Darwin.cmake \ + os/HP-UX.cmake diff --git a/cmake/os/HP-UX.cmake b/cmake/os/HP-UX.cmake new file mode 100644 index 00000000000..5eaa0477040 --- /dev/null +++ b/cmake/os/HP-UX.cmake @@ -0,0 +1,47 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE(CheckCXXSourceCompiles) +# Enable 64 bit file offsets +SET(_LARGEFILE64_SOURCE 1) +SET(_FILE_OFFSET_BITS 64) +# If Itanium make shared library suffix .so +# OS understands both .sl and .so. CMake would +# use .sl, however MySQL prefers .so +IF(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "9000") + SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so" CACHE INTERNAL "" FORCE) + SET(CMAKE_SHARED_MODULE_SUFFIX ".so" CACHE INTERNAL "" FORCE) +ENDIF() +IF(CMAKE_SYSTEM MATCHES "11") + ADD_DEFINITIONS(-DHPUX11) +ENDIF() + +IF(CMAKE_CXX_COMPILER_ID MATCHES "HP") + # Enable standard C++ flags if required + # HP seems a bit traditional and "new" features like ANSI for-scope + # still require special flag to be set + CHECK_CXX_SOURCE_COMPILES( + "int main() + { + for(int i=0; i<1; i++); + for(int i=0; i<1; i++); + return 0; + } + " HAVE_ANSI_FOR_SCOPE) + IF(NOT HAVE_ANSI_FOR_SCOPE) + # Enable conformant behavior + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Aa") + ENDIF() +ENDIF() diff --git a/cmake/os/SunOS.cmake b/cmake/os/SunOS.cmake index 3d883eeb9c8..84ac64d8af3 100644 --- a/cmake/os/SunOS.cmake +++ b/cmake/os/SunOS.cmake @@ -15,7 +15,7 @@ INCLUDE(CheckSymbolExists) INCLUDE(CheckCSourceRuns) - +INCLUDE(CheckCSourceCompiles) SET(TARGET_OS_SOLARIS 1) # Enable 64 bit file offsets @@ -69,3 +69,28 @@ CHECK_C_SOURCE_RUNS( } " HAVE_SOLARIS_ATOMIC) + +# Check is special processor flag needs to be set on older GCC +#that defaults to v8 sparc . Code here is taken from my_rdtsc.c +IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_SIZEOF_VOID_P EQUAL 4 + AND CMAKE_SYSTEM_PROCESSOR MATCHES "sparc") + SET(SOURCE + " + int main() + { + long high\; + long low\; + __asm __volatile__ (\"rd %%tick,%1\; srlx %1,32,%0\" : \"=r\" ( high), \"=r\" (low))\; + return 0\; + } ") + CHECK_C_SOURCE_COMPILES(${SOURCE} HAVE_SPARC32_TICK) + IF(NOT HAVE_SPARC32_TICK) + SET(CMAKE_REQUIRED_FLAGS "-mcpu=v9") + CHECK_C_SOURCE_COMPILES(${SOURCE} HAVE_SPARC32_TICK_WITH_V9) + SET(CMAKE_REQUIRED_FLAGS) + IF(HAVE_SPARC32_TICK_WITH_V9) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=v9") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=v9") + ENDIF() + ENDIF() +ENDIF() diff --git a/configure.cmake b/configure.cmake index 663e3db9a66..016ae359486 100644 --- a/configure.cmake +++ b/configure.cmake @@ -79,37 +79,10 @@ ENDIF() # Large files SET(_LARGEFILE_SOURCE 1) -IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") - SET(_LARGEFILE64_SOURCE 1) - SET(_FILE_OFFSET_BITS 64) -ENDIF() IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") SET(_LARGE_FILES 1) ENDIF() - - -IF(CMAKE_SYSTEM_NAME STREQUAL "HP-UX" ) - IF(CMAKE_SIZEOF_VOID_P EQUAL 4) - # HPUX linker crashes building plugins - SET(WITHOUT_DYNAMIC_PLUGINS TRUE) - ENDIF() - # If Itanium make shared library suffix .so - # OS understands both .sl and .so. CMake would - # use .sl, however MySQL prefers .so - IF(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "9000") - SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so" CACHE INTERNAL "" FORCE) - SET(CMAKE_SHARED_MODULE_SUFFIX ".so" CACHE INTERNAL "" FORCE) - ENDIF() -ENDIF() - - -#Some OS specific hacks -IF(CMAKE_SYSTEM MATCHES "HP-UX" AND CMAKE_SYSTEM MATCHES "11") - ADD_DEFINITIONS(-DHPUX11) -ENDIF() - - # Figure out what engines to build and how (statically or dynamically), # add preprocessor defines for storage engines. IF(WITHOUT_DYNAMIC_PLUGINS) @@ -952,7 +925,7 @@ CHECK_CXX_SOURCE_COMPILES_UNIX(" IF(WIN32) SET(HAVE_SOCKET_TIMEOUT 1) -ELSEIF(CMAKE_SYSTEM MATCHES "HP") +ELSEIF(CMAKE_SYSTEM MATCHES "HP-UX") SET(HAVE_SOCKET_TIMEOUT 0) ELSEIF(CMAKE_CROSSCOMPILING) SET(HAVE_SOCKET_TIMEOUT 0) From 8d74e5c50e65d746c4afd408ea5a3d74aa01c885 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 27 Jan 2010 02:52:04 +0100 Subject: [PATCH 313/466] Added AIX, OS400 and Cygwin. This finishes of moving code from configure.cmake IF()'s to platform specific files --- cmake/Makefile.am | 6 +++++- cmake/os/AIX.cmake | 33 +++++++++++++++++++++++++++++++++ cmake/os/Cygwin.cmake | 17 +++++++++++++++++ cmake/os/OS400.cmake | 17 +++++++++++++++++ configure.cmake | 35 ++++++----------------------------- 5 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 cmake/os/AIX.cmake create mode 100644 cmake/os/Cygwin.cmake create mode 100644 cmake/os/OS400.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index c8341cb14cb..e136ac89d69 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -28,5 +28,9 @@ EXTRA_DIST = \ os/Linux.cmake \ os/SunOS.cmake \ os/Darwin.cmake \ - os/HP-UX.cmake + os/HP-UX.cmake \ + os/AIX.cmake \ + os/OS400.cmake \ + os/Cygwin.cmake + diff --git a/cmake/os/AIX.cmake b/cmake/os/AIX.cmake new file mode 100644 index 00000000000..b1b2cebdd14 --- /dev/null +++ b/cmake/os/AIX.cmake @@ -0,0 +1,33 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +#Enable 64 bit file offsets +SET(_LARGE_FILES 1) + +# Fix xlC oddity - it complains about same inline function defined multiple times +# in different compilation units +INCLUDE(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-qstaticinline" HAVE_QSTATICINLINE) + IF(HAVE_QSTATICINLINE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qstaticinline") + ENDIF() + +# The following is required to export all symbols +# (also with leading underscore) +STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS + ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}") +STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS + "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}") \ No newline at end of file diff --git a/cmake/os/Cygwin.cmake b/cmake/os/Cygwin.cmake new file mode 100644 index 00000000000..5b2d82c19b8 --- /dev/null +++ b/cmake/os/Cygwin.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Cygwin is not Windows +SET(WIN32 0) \ No newline at end of file diff --git a/cmake/os/OS400.cmake b/cmake/os/OS400.cmake new file mode 100644 index 00000000000..b8cad2917f9 --- /dev/null +++ b/cmake/os/OS400.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +GET_FILENAME_COMPONENT(_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +INCLUDE(${_SCRIPT_DIR}/AIX.cmake) \ No newline at end of file diff --git a/configure.cmake b/configure.cmake index 016ae359486..8a2a1f0ad78 100644 --- a/configure.cmake +++ b/configure.cmake @@ -25,10 +25,8 @@ INCLUDE (CheckCSourceRuns) INCLUDE (CheckSymbolExists) -# Sometimes it is handy to know if PIC option -# is set, to avoid recompilation of the same source -# for shared libs. We also allow it as an option for -# fast compile. +# WITH_PIC options.Not of much use, PIC is taken care of on platforms +# where it makes sense anyway. IF(UNIX) IF(APPLE) # OSX executable are always PIC @@ -76,19 +74,13 @@ IF(CMAKE_COMPILER_IS_GNUCXX) ENDIF() ENDIF() - -# Large files -SET(_LARGEFILE_SOURCE 1) -IF(CMAKE_SYSTEM_NAME MATCHES "AIX" OR CMAKE_SYSTEM_NAME MATCHES "OS400") - SET(_LARGE_FILES 1) -ENDIF() - -# Figure out what engines to build and how (statically or dynamically), -# add preprocessor defines for storage engines. IF(WITHOUT_DYNAMIC_PLUGINS) MESSAGE("Dynamic plugins are disabled.") ENDIF(WITHOUT_DYNAMIC_PLUGINS) +# Large files, common flag +SET(_LARGEFILE_SOURCE 1) + # Searches function in libraries # if function is found, sets output parameter result to the name of the library @@ -112,6 +104,7 @@ FUNCTION(MY_SEARCH_LIBS func libs result) ENDFOREACH() ENDFUNCTION() +# Find out which libraries to use. IF(UNIX) MY_SEARCH_LIBS(floor m LIBM) IF(NOT LIBM) @@ -955,22 +948,6 @@ SET(NO_ALARM "${HAVE_SOCKET_TIMEOUT}" CACHE BOOL "No need to use alarm to implement socket timeout") MARK_AS_ADVANCED(NO_ALARM) -IF(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") - # xlC oddity - it complains about same inline function defined multiple times - # in different compilation units - INCLUDE(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-qstaticinline" HAVE_QSTATICINLINE) - IF(HAVE_QSTATICINLINE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qstaticinline") - ENDIF() - - # The following is required to export all symbols - # (also with leading underscore) - STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS - ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}) - STRING(REPLACE "-bexpall" "-bexpfull" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS - ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) -ENDIF() IF(CMAKE_COMPILER_IS_GNUCXX) IF(WITH_ATOMIC_OPS STREQUAL "up") From ada6e38dc5169a561d244d21a67900640c6db3f9 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 27 Jan 2010 14:23:33 +0100 Subject: [PATCH 314/466] Fix issues around MYSQL_TCP_PORT_DEFAULT (should be really 0 all the time) --- cmake/mysql_version.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index a05df7f5f19..c1eafd887c9 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -84,7 +84,11 @@ ENDIF() IF(NOT MYSQL_TCP_PORT) SET(MYSQL_TCP_PORT ${MYSQL_TCP_PORT_DEFAULT}) SET(MYSQL_TCP_PORT_DEFAULT "0") +ELSEIF(MYSQL_TCP_PORT EQUAL MYSQL_TCP_PORT_DEFAULT) + SET(MYSQL_TCP_PORT_DEFAULT "0") ENDIF() + + IF(NOT MYSQL_UNIX_ADDR) SET(MYSQL_UNIX_ADDR "/tmp/mysql.sock") ENDIF() From a3748b18761446662aecb89f8c37225d32c9fc3e Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Wed, 27 Jan 2010 08:26:05 -0700 Subject: [PATCH 315/466] Misc cleanup --- mysql-test/suite/perfschema/r/aggregate.result | 1 + mysql-test/suite/perfschema/t/aggregate.test | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mysql-test/suite/perfschema/r/aggregate.result b/mysql-test/suite/perfschema/r/aggregate.result index 3e38aebacfc..197378ca38f 100644 --- a/mysql-test/suite/perfschema/r/aggregate.result +++ b/mysql-test/suite/perfschema/r/aggregate.result @@ -16,6 +16,7 @@ b CHAR(100) DEFAULT 'initial value') ENGINE=MyISAM; insert into t1 (id) values (1), (2), (3), (4), (5), (6), (7), (8); update performance_schema.SETUP_INSTRUMENTS SET enabled = 'NO'; +update performance_schema.SETUP_CONSUMERS set enabled = 'NO'; set @dump_all=FALSE; "Verifying file aggregate consistency" SELECT EVENT_NAME, e.COUNT_READ, SUM(i.COUNT_READ) diff --git a/mysql-test/suite/perfschema/t/aggregate.test b/mysql-test/suite/perfschema/t/aggregate.test index 75bd5ad822e..0fa4651ecda 100644 --- a/mysql-test/suite/perfschema/t/aggregate.test +++ b/mysql-test/suite/perfschema/t/aggregate.test @@ -50,6 +50,8 @@ insert into t1 (id) values (1), (2), (3), (4), (5), (6), (7), (8); # Stop recording data, so the select below don't add noise. update performance_schema.SETUP_INSTRUMENTS SET enabled = 'NO'; +# Disable all consumers, for long standing waits +update performance_schema.SETUP_CONSUMERS set enabled = 'NO'; # Helper to debug set @dump_all=FALSE; From bb617b2aa06743015c99964a228d57339a4d4716 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Wed, 27 Jan 2010 21:59:10 +0300 Subject: [PATCH 316/466] Typo fix for result file. --- mysql-test/r/mysqld--help-win.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 16239a6326a..e7048c71a48 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -37,7 +37,7 @@ The following options may be given as the first argument: binary log during a transaction. If you often use big, multi-statement transactions you can increase this to get more performance - --binlog-direct-non-transactional-updates + --binlog-direct-non-transactional-updates Causes updates to non-transactional engines using statement format to be written directly to binary log. Before using this option make sure that there are no From 236a0a7945ad939b33221d2bfc28f4682ac79f1d Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 28 Jan 2010 11:09:05 +0100 Subject: [PATCH 317/466] prettification of package names and fix compile bug on OSX/ppc --- cmake/Makefile.am | 1 + cmake/mysql_version.cmake | 49 +-------------- cmake/os/Windows.cmake | 2 - cmake/package_name.cmake | 124 ++++++++++++++++++++++++++++++++++++++ include/my_atomic.h | 2 +- sql/CMakeLists.txt | 9 ++- 6 files changed, 134 insertions(+), 53 deletions(-) create mode 100644 cmake/package_name.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index e136ac89d69..f6172a62e1d 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -1,5 +1,6 @@ EXTRA_DIST = \ cmake_parse_arguments.cmake \ + package_name.cmake \ configurable_file_content.in \ check_minimal_version.cmake \ create_initial_db.cmake.in \ diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index c1eafd887c9..67288767d20 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -97,53 +97,8 @@ IF(NOT COMPILATION_COMMENT) ENDIF() - -# Use meaningful package name for the binary package -IF(NOT CPACK_PACKAGE_FILE_NAME) - IF( NOT SYSTEM_NAME_AND_PROCESSOR) - IF(WIN32) - # CMake does not set CMAKE_SYSTEM_PROCESSOR correctly on Win64 - # (uses x86). Besides, we try to be compatible with existing naming - IF(CMAKE_SIZEOF_VOID_P EQUAL 8) - SET(SYSTEM_NAME_AND_PROCESSOR "winx64") - ELSE() - SET(SYSTEM_NAME_AND_PROCESSOR "win32") - ENDIF() - ELSE() - IF(NOT PLATFORM) - SET(PLATFORM ${CMAKE_SYSTEM_NAME}) - ENDIF() - IF(NOT MACHINE) - SET(MACHINE ${CMAKE_SYSTEM_PROCESSOR}) - IF(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT ${MACHINE} MATCHES "ia64") - # On almost every 64 bit machine (except IA64) it is possible - # to build 32 bit packages. Add -64bit suffix to differentiate - # between 32 and 64 bit packages. - SET(MACHINE ${MACHINE}-64bit) - ENDIF() - ENDIF() - SET(SYSTEM_NAME_AND_PROCESSOR "${PLATFORM}-${MACHINE}") - ENDIF() - ENDIF() - - IF(SHORT_PRODUCT_TAG) - SET(PRODUCT_TAG "-${SHORT_PRODUCT_TAG}") - ELSEIF(MYSQL_SERVER_SUFFIX) - SET(PRODUCT_TAG "${MYSQL_SERVER_SUFFIX}") # Already has a leading dash - ELSE() - SET(PRODUCT_TAG) - ENDIF() - - SET(package_name "mysql${PRODUCT_TAG}-${VERSION}-${SYSTEM_NAME_AND_PROCESSOR}") - - # Sometimes package suffix is added (something like "-icc-glibc23") - IF(PACKAGE_SUFFIX) - SET(package_name "${package_name}${PACKAGE_SUFFIX}") - ENDIF() - STRING(TOLOWER ${package_name} package_name) - SET(CPACK_PACKAGE_FILE_NAME ${package_name}) -ENDIF() - +INCLUDE(package_name) +GET_PACKAGE_FILE_NAME(CPACK_PACKAGE_FILE_NAME) IF(NOT CPACK_SOURCE_PACKAGE_FILE_NAME) SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mysql-${VERSION}") diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake index 5573ecf701e..ce82575ef76 100644 --- a/cmake/os/Windows.cmake +++ b/cmake/os/Windows.cmake @@ -102,14 +102,12 @@ IF(MSVC) ADD_DEFINITIONS(/wd4996) ENDIF() - SET (PLATFORM X86) IF(CMAKE_SIZEOF_VOID_P MATCHES 8) # _WIN64 is defined by the compiler itself. # Yet, we define it here again to work around a bug with Intellisense # described here: http://tinyurl.com/2cb428. # Syntax highlighting is important for proper debugger functionality. ADD_DEFINITIONS("-D_WIN64") - SET (PLATFORM X64) ENDIF() ENDIF() diff --git a/cmake/package_name.cmake b/cmake/package_name.cmake new file mode 100644 index 00000000000..5ae6fe5f7f0 --- /dev/null +++ b/cmake/package_name.cmake @@ -0,0 +1,124 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Produce meaningful package name for the binary package +# The logic is rather involved with special cases for different OSes +MACRO(GET_PACKAGE_FILE_NAME Var) +IF(NOT VERSION) + MESSAGE(FATAL_ERROR + "Variable VERSION needs to be set prior to calling GET_PACKAGE_FILE_NAME") + ENDIF() + IF(NOT SYSTEM_NAME_AND_PROCESSOR) + SET(NEED_DASH_BETWEEN_PLATFORM_AND_MACHINE 1) + SET(DEFAULT_PLATFORM ${CMAKE_SYSTEM_NAME}) + SET(DEFAULT_MACHINE ${CMAKE_SYSTEM_PROCESSOR}) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(64BIT 1) + ENDIF() + + IF(CMAKE_SYSTEM_NAME MATCHES "Windows") + SET(NEED_DASH_BETWEEN_PLATFORM_AND_MACHINE 0) + SET(DEFAULT_PLATFORM "win") + IF(64BIT) + SET(DEFAULT_MACHINE "x64") + ELSE() + SET(DEFAULT_MACHINE "32") + ENDIF() + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux") + IF(NOT 64BIT AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + SET(DEFAULT_MACHINE "i686") + ENDIF() + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + # SunOS 5.10=> solaris10 + STRING(REPLACE "5." "" VER "${CMAKE_SYSTEM_VERSION}") + SET(DEFAULT_PLATFORM "solaris${VER}") + IF(64BIT) + IF(CMAKE_SYSTEM_PROCESSOR MATCHES "i386") + SET(DEFAULT_MACHINE "x86_64") + ELSE() + SET(DEFAULT_MACHINE "${CMAKE_SYSTEM_PROCESSOR}-64bit") + ENDIF() + ENDIF() + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "HP-UX") + STRING(REPLACE "B." "" VER "${CMAKE_SYSTEM_VERSION}") + SET(DEFAULT_PLATFORM "hpux${VER}") + IF(64BIT) + SET(DEFAULT_MACHINE "${CMAKE_SYSTEM_PROCESSOR}-64bit") + ENDIF() + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "AIX") + SET(DEFAULT_PLATFORM "${CMAKE_SYSTEM_NAME}5.${CMAKE_SYSTEM_VERSION}") + IF(64BIT) + SET(DEFAULT_MACHINE "${CMAKE_SYSTEM_PROCESSOR}-64bit") + ENDIF() + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + STRING(REGEX MATCH "[0-9]+\\.[0-9]+" VER "${CMAKE_SYSTEM_VERSION}") + SET(DEFAULT_PLATFORM "${CMAKE_SYSTEM_NAME}${VER}") + IF(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64") + SET(DEFAULT_MACHINE "x86_64") + IF(NOT 64BIT) + SET(DEFAULT_MACHINE "i386") + ENDIF() + ENDIF() + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin") + IF(${CMAKE_OSX_DEPLOYMENT_TARGET}) + SET(VER "${CMAKE_OSX_DEPLOYMENT_TARGET}") + ELSE() + SET(VER "${CMAKE_SYSTEM_VERSION}") + ENDIF() + STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" VER "${VER}") + MATH(EXPR VER "${VER} -4") + SET(DEFAULT_PLATFORM "osx10.${VER}") + LIST(LENGTH CMAKE_OSX_ARCHITECTURES LEN) + IF(LEN GREATER 1) + SET(DEFAULT_MACHINE "universal") + ELSE() + SET(DEFAULT_MACHINE "${CMAKE_OSX_ARCHITECTURES}") + ENDIF() + IF(DEFAULT_MACHINE MATCHES "i386") + SET(DEFAULT_MACHINE "x86") + ENDIF() + ENDIF() + + IF(NOT PLATFORM) + SET(PLATFORM ${DEFAULT_PLATFORM}) + ENDIF() + IF(NOT MACHINE) + SET(MACHINE ${DEFAULT_MACHINE}) + ENDIF() + + IF(NEED_DASH_BETWEEN_PLATFORM_AND_MACHINE) + SET(SYSTEM_NAME_AND_PROCESSOR "${PLATFORM}-${MACHINE}") + ELSE() + SET(SYSTEM_NAME_AND_PROCESSOR "${PLATFORM}${MACHINE}") + ENDIF() + ENDIF() + + IF(SHORT_PRODUCT_TAG) + SET(PRODUCT_TAG "-${SHORT_PRODUCT_TAG}") + ELSEIF(MYSQL_SERVER_SUFFIX) + SET(PRODUCT_TAG "${MYSQL_SERVER_SUFFIX}") # Already has a leading dash + ELSE() + SET(PRODUCT_TAG) + ENDIF() + + SET(package_name "mysql${PRODUCT_TAG}-${VERSION}-${SYSTEM_NAME_AND_PROCESSOR}") + + # Sometimes package suffix is added (something like "-icc-glibc23") + IF(PACKAGE_SUFFIX) + SET(package_name "${package_name}${PACKAGE_SUFFIX}") + ENDIF() + STRING(TOLOWER ${package_name} package_name) + SET(${Var} ${package_name}) +ENDMACRO() diff --git a/include/my_atomic.h b/include/my_atomic.h index b506aa7d3d1..9914fd2428f 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -105,7 +105,7 @@ warning: 'transparent_union' attribute ignored */ #if defined(__GNUC__) && !defined(__cplusplus) && \ - ! (defined(__APPLE__) && defined(_ARCH_PPC64)) + ! (defined(__APPLE__) && (defined(_ARCH_PPC64) ||defined (_ARCH_PPC))) /* we want to be able to use my_atomic_xxx functions with both signed and unsigned integers. But gcc will issue a warning diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 4c7e16b802d..ee58c33c6f7 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -121,10 +121,13 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS) FILE(TO_NATIVE_PATH ${LOC} LOC) SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC}) ENDFOREACH (CORELIB ${MYSQLD_CORE_LIBS}) - + SET(_PLATFORM x86) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(_PLATFORM x64) + ENDIF() ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK - COMMAND echo ${PLATFORM} && cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js - ${PLATFORM} ${LIB_LOCATIONS} > mysqld.def + COMMAND echo ${_PLATFORM} && cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js + ${_PLATFORM} ${LIB_LOCATIONS} > mysqld.def WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) ADD_DEPENDENCIES(sql GenError) ENDIF() From 3a8c2b5e957086995244ae90b869c6a009a6bf98 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 28 Jan 2010 11:30:21 +0100 Subject: [PATCH 318/466] do not redefine CPACK_PACKAGE_FILE_NAME, if it is already given --- cmake/mysql_version.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/mysql_version.cmake b/cmake/mysql_version.cmake index 67288767d20..e367da651e9 100644 --- a/cmake/mysql_version.cmake +++ b/cmake/mysql_version.cmake @@ -98,7 +98,9 @@ ENDIF() INCLUDE(package_name) -GET_PACKAGE_FILE_NAME(CPACK_PACKAGE_FILE_NAME) +IF(NOT CPACK_PACKAGE_FILE_NAME) + GET_PACKAGE_FILE_NAME(CPACK_PACKAGE_FILE_NAME) +ENDIF() IF(NOT CPACK_SOURCE_PACKAGE_FILE_NAME) SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mysql-${VERSION}") From a09d8b78026dba796fb89ff6e86d90a8276b1b71 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 28 Jan 2010 13:01:01 +0100 Subject: [PATCH 319/466] upmerge 49210 --- mysql-test/lib/v1/mysql-test-run.pl | 6 ++++-- mysql-test/mysql-test-run.pl | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mysql-test/lib/v1/mysql-test-run.pl b/mysql-test/lib/v1/mysql-test-run.pl index 9630c65ade4..5d06d9c4dd8 100755 --- a/mysql-test/lib/v1/mysql-test-run.pl +++ b/mysql-test/lib/v1/mysql-test-run.pl @@ -1107,14 +1107,16 @@ sub command_line_setup () { if ( ! $opt_testcase_timeout ) { - $opt_testcase_timeout= $default_testcase_timeout; + $opt_testcase_timeout= + $ENV{MTR_TESTCASE_TIMEOUT} || $default_testcase_timeout; $opt_testcase_timeout*= 10 if $opt_valgrind; $opt_testcase_timeout*= 10 if ($opt_debug and $glob_win32); } if ( ! $opt_suite_timeout ) { - $opt_suite_timeout= $default_suite_timeout; + $opt_suite_timeout= + $ENV{MTR_SUITE_TIMEOUT} || $default_suite_timeout; $opt_suite_timeout*= 6 if $opt_valgrind; $opt_suite_timeout*= 6 if ($opt_debug and $glob_win32); } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index e42ec86a7f6..b006145a677 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -206,10 +206,10 @@ my $opt_mark_progress; my $opt_sleep; -my $opt_testcase_timeout= 15; # minutes -my $opt_suite_timeout = 300; # minutes -my $opt_shutdown_timeout= 10; # seconds -my $opt_start_timeout = 180; # seconds +my $opt_testcase_timeout= $ENV{MTR_TESTCASE_TIMEOUT} || 15; # minutes +my $opt_suite_timeout = $ENV{MTR_SUITE_TIMEOUT} || 300; # minutes +my $opt_shutdown_timeout= $ENV{MTR_SHUTDOWN_TIMEOUT} || 10; # seconds +my $opt_start_timeout = $ENV{MTR_START_TIMEOUT} || 180; # seconds sub testcase_timeout { return $opt_testcase_timeout * 60; }; sub suite_timeout { return $opt_suite_timeout * 60; }; From 051f769dc5352136f084670009b5f8f0b0d68592 Mon Sep 17 00:00:00 2001 From: vvaintroub Date: Thu, 28 Jan 2010 14:33:44 +0100 Subject: [PATCH 320/466] Move WITH_UNITTESTS option up - otherwise it skips plugin unittests --- CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c40877d8ee..369e03bf988 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,10 +86,6 @@ IF(DEFINED ENV{CPPFLAGS}) ADD_DEFINITIONS($ENV{CPPFLAGS}) ENDIF() -IF(CYGWIN) - SET(WIN32 0) -ENDIF() - # Add macros INCLUDE(character_sets) INCLUDE(zlib) @@ -113,6 +109,7 @@ OPTION(ENABLED_PROFILING "Enable profiling" ON) OPTION(CYBOZU "" OFF) OPTION(BACKUP_TEST "" OFF) OPTION(WITHOUT_SERVER OFF) +OPTION (WITH_UNIT_TESTS "Compile MySQL with unit tests" ON) MARK_AS_ADVANCED(CYBOZU BACKUP_TEST WITHOUT_SERVER DISABLE_SHARED) @@ -223,7 +220,7 @@ ADD_SUBDIRECTORY(regex) ADD_SUBDIRECTORY(mysys) ADD_SUBDIRECTORY(libmysql) -OPTION (WITH_UNIT_TESTS "Compile MySQL with unit tests" ON) + IF(WITH_UNIT_TESTS) ENABLE_TESTING() ENDIF() From cf72d8679a08317d3d5cb8d1338c9cafab67a961 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 28 Jan 2010 15:19:18 +0100 Subject: [PATCH 321/466] merge 49210 --- mysql-test/lib/v1/mysql-test-run.pl | 6 ++++-- mysql-test/mysql-test-run.pl | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mysql-test/lib/v1/mysql-test-run.pl b/mysql-test/lib/v1/mysql-test-run.pl index 9630c65ade4..5d06d9c4dd8 100755 --- a/mysql-test/lib/v1/mysql-test-run.pl +++ b/mysql-test/lib/v1/mysql-test-run.pl @@ -1107,14 +1107,16 @@ sub command_line_setup () { if ( ! $opt_testcase_timeout ) { - $opt_testcase_timeout= $default_testcase_timeout; + $opt_testcase_timeout= + $ENV{MTR_TESTCASE_TIMEOUT} || $default_testcase_timeout; $opt_testcase_timeout*= 10 if $opt_valgrind; $opt_testcase_timeout*= 10 if ($opt_debug and $glob_win32); } if ( ! $opt_suite_timeout ) { - $opt_suite_timeout= $default_suite_timeout; + $opt_suite_timeout= + $ENV{MTR_SUITE_TIMEOUT} || $default_suite_timeout; $opt_suite_timeout*= 6 if $opt_valgrind; $opt_suite_timeout*= 6 if ($opt_debug and $glob_win32); } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 99d4774866f..a2ea36aa34e 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -206,10 +206,10 @@ my $opt_mark_progress; my $opt_sleep; -my $opt_testcase_timeout= 15; # minutes -my $opt_suite_timeout = 300; # minutes -my $opt_shutdown_timeout= 10; # seconds -my $opt_start_timeout = 180; # seconds +my $opt_testcase_timeout= $ENV{MTR_TESTCASE_TIMEOUT} || 15; # minutes +my $opt_suite_timeout = $ENV{MTR_SUITE_TIMEOUT} || 300; # minutes +my $opt_shutdown_timeout= $ENV{MTR_SHUTDOWN_TIMEOUT} || 10; # seconds +my $opt_start_timeout = $ENV{MTR_START_TIMEOUT} || 180; # seconds sub testcase_timeout { return $opt_testcase_timeout * 60; }; sub suite_timeout { return $opt_suite_timeout * 60; }; From f719e51f8b317137fc7f380d290b4379d24aad06 Mon Sep 17 00:00:00 2001 From: vvaintroub Date: Fri, 29 Jan 2010 03:07:00 +0100 Subject: [PATCH 322/466] Add windows system check cache that prevents tests from running, most are irrelevant anyway Add cached variable WITH_XXX_STORAGE_ENGINE for dynamic plugins that can be static or dynamic. --- cmake/Makefile.am | 1 + cmake/os/Windows.cmake | 12 +- cmake/os/WindowsCache.cmake | 342 ++++++++++++++++++++++++++++++++++ cmake/plugin.cmake | 6 +- config.h.cmake | 1 + configure.cmake | 354 ++++++++++++++++++------------------ 6 files changed, 536 insertions(+), 180 deletions(-) create mode 100644 cmake/os/WindowsCache.cmake diff --git a/cmake/Makefile.am b/cmake/Makefile.am index f6172a62e1d..86244a526c1 100644 --- a/cmake/Makefile.am +++ b/cmake/Makefile.am @@ -26,6 +26,7 @@ EXTRA_DIST = \ install_layout.cmake \ build_configurations/mysql_release.cmake \ os/Windows.cmake \ + os/WindowsCache.cmake \ os/Linux.cmake \ os/SunOS.cmake \ os/Darwin.cmake \ diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake index ce82575ef76..f1f483dfb33 100644 --- a/cmake/os/Windows.cmake +++ b/cmake/os/Windows.cmake @@ -27,7 +27,14 @@ INCLUDE (CheckTypeSize) # Optionally read user configuration, generated by configure.js. # This is left for backward compatibility reasons only. -INCLUDE(win/configure.data OPTIONAL) +INCLUDE(${CMAKE_BINARY_DIR}/win/configure.data OPTIONAL) + +# avoid running system checks by using pre-cached check results +# system checks are expensive on VS since every tiny program is to be compiled in +# a VC solution. +GET_FILENAME_COMPONENT(_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +INCLUDE(${_SCRIPT_DIR}/WindowsCache.cmake) + # OS display name (version_compile_os etc). # Used by the test suite to ignore bugs on some platforms, @@ -97,8 +104,8 @@ IF(MSVC) ENDIF() #TODO: update the code and remove the disabled warnings + ADD_DEFINITIONS(/wd4800 /wd4805) IF (MSVC_VERSION GREATER 1310) - ADD_DEFINITIONS(/wd4800 /wd4805) ADD_DEFINITIONS(/wd4996) ENDIF() @@ -179,6 +186,7 @@ CHECK_FUNCTION_REPLACEMENT(snprintf _snprintf) CHECK_FUNCTION_REPLACEMENT(strtok_r strtok_s) CHECK_FUNCTION_REPLACEMENT(strtoll _strtoi64) CHECK_FUNCTION_REPLACEMENT(strtoull _strtoui64) +CHECK_FUNCTION_REPLACEMENT(vsnprintf _vsnprintf) CHECK_TYPE_SIZE(ssize_t SIZE_OF_SSIZE_T) IF(NOT HAVE_SIZE_OF_SSIZE_T) SET(ssize_t SSIZE_T) diff --git a/cmake/os/WindowsCache.cmake b/cmake/os/WindowsCache.cmake new file mode 100644 index 00000000000..8cf84e9d2e7 --- /dev/null +++ b/cmake/os/WindowsCache.cmake @@ -0,0 +1,342 @@ +# Copyright (C) 2010 Sun Microsystems, 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; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Avoid system checks on Windows by pre-caching results. Most of the system checks +# are not relevant for Windows anyway and it takes lot more time to run them, +# since CMake to creates a Visual Studio project for each tiny test. +# Note that only we cache values on VC++ only, MinGW would give slightly +# different results. + +IF(MSVC) +SET(HAVE_ACCESS 1 CACHE INTERNAL "") +SET(HAVE_AIO_H CACHE INTERNAL "") +SET(HAVE_AIO_READ CACHE INTERNAL "") +SET(HAVE_ALARM CACHE INTERNAL "") +SET(HAVE_ALLOCA_H CACHE INTERNAL "") +SET(HAVE_ARPA_INET_H CACHE INTERNAL "") +SET(HAVE_ASM_MSR_H CACHE INTERNAL "") +SET(HAVE_BACKTRACE CACHE INTERNAL "") +SET(HAVE_BACKTRACE_SYMBOLS CACHE INTERNAL "") +SET(HAVE_BACKTRACE_SYMBOLS_FD CACHE INTERNAL "") +SET(HAVE_BCMP CACHE INTERNAL "") +SET(HAVE_BFILL CACHE INTERNAL "") +SET(HAVE_BMOVE CACHE INTERNAL "") +SET(HAVE_BSD_SIGNALS CACHE INTERNAL "") +SET(HAVE_BSEARCH 1 CACHE INTERNAL "") +SET(HAVE_BSS_START CACHE INTERNAL "") +SET(HAVE_BZERO CACHE INTERNAL "") +SET(HAVE_CHOWN CACHE INTERNAL "") +SET(HAVE_CLOCK_GETTIME CACHE INTERNAL "") +SET(HAVE_COMPRESS CACHE INTERNAL "") +SET(HAVE_CRYPT CACHE INTERNAL "") +SET(HAVE_CRYPT_H CACHE INTERNAL "") +SET(HAVE_CUSERID CACHE INTERNAL "") +SET(HAVE_CXX_NEW 1 CACHE INTERNAL "") +SET(HAVE_DECL_MADVISE CACHE INTERNAL "") +SET(HAVE_DIRECTIO CACHE INTERNAL "") +SET(HAVE_DIRENT_H CACHE INTERNAL "") +SET(HAVE_DLERROR CACHE INTERNAL "") +SET(HAVE_DLFCN_H CACHE INTERNAL "") +SET(HAVE_DLOPEN CACHE INTERNAL "") +SET(HAVE_DOPRNT CACHE INTERNAL "") +SET(HAVE_EXECINFO_H CACHE INTERNAL "") +SET(HAVE_FCHMOD CACHE INTERNAL "") +SET(HAVE_FCNTL CACHE INTERNAL "") +SET(HAVE_FCNTL_H 1 CACHE INTERNAL "") +SET(HAVE_FCNTL_NONBLOCK CACHE INTERNAL "") +SET(HAVE_FCONVERT CACHE INTERNAL "") +SET(HAVE_FDATASYNC CACHE INTERNAL "") +SET(HAVE_FENV_H CACHE INTERNAL "") +SET(HAVE_FESETROUND CACHE INTERNAL "") +SET(HAVE_FGETLN CACHE INTERNAL "") +SET(HAVE_FINITE CACHE INTERNAL "") +SET(HAVE_FINITE_IN_MATH_H CACHE INTERNAL "") +SET(HAVE_FLOATINGPOINT_H CACHE INTERNAL "") +SET(HAVE_FLOAT_H 1 CACHE INTERNAL "") +SET(HAVE_FLOCKFILE CACHE INTERNAL "") +SET(HAVE_FNMATCH_H CACHE INTERNAL "") +SET(HAVE_FPSETMASK CACHE INTERNAL "") +SET(HAVE_FPU_CONTROL_H CACHE INTERNAL "") +SET(HAVE_FSEEKO CACHE INTERNAL "") +SET(HAVE_FSYNC CACHE INTERNAL "") +SET(HAVE_FTIME 1 CACHE INTERNAL "") +SET(HAVE_FTRUNCATE CACHE INTERNAL "") +SET(HAVE_GETADDRINFO 1 CACHE INTERNAL "") +SET(HAVE_GETCWD 1 CACHE INTERNAL "") +SET(HAVE_GETHOSTBYADDR_R CACHE INTERNAL "") +SET(HAVE_GETHOSTBYNAME_R CACHE INTERNAL "") +SET(HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE CACHE INTERNAL "") +SET(HAVE_GETHOSTBYNAME_R_RETURN_INT CACHE INTERNAL "") +SET(HAVE_GETHRTIME CACHE INTERNAL "") +SET(HAVE_GETLINE CACHE INTERNAL "") +SET(HAVE_GETNAMEINFO CACHE INTERNAL "") +SET(HAVE_GETPAGESIZE CACHE INTERNAL "") +SET(HAVE_GETPASS CACHE INTERNAL "") +SET(HAVE_GETPASSPHRASE CACHE INTERNAL "") +SET(HAVE_GETPWNAM CACHE INTERNAL "") +SET(HAVE_GETPWUID CACHE INTERNAL "") +SET(HAVE_GETRLIMIT CACHE INTERNAL "") +SET(HAVE_GETRUSAGE CACHE INTERNAL "") +SET(HAVE_GETTIMEOFDAY CACHE INTERNAL "") +SET(HAVE_GETWD CACHE INTERNAL "") +SET(HAVE_GMTIME_R CACHE INTERNAL "") +SET(HAVE_GRP_H CACHE INTERNAL "") +SET(HAVE_IA64INTRIN_H CACHE INTERNAL "") +SET(HAVE_IEEEFP_H CACHE INTERNAL "") +SET(HAVE_INDEX CACHE INTERNAL "") +SET(HAVE_INITGROUPS CACHE INTERNAL "") +SET(HAVE_INTTYPES_H CACHE INTERNAL "") +SET(HAVE_IPPROTO_IPV6 CACHE INTERNAL "") +SET(HAVE_IPV6 TRUE CACHE INTERNAL "") +SET(HAVE_IPV6_V6ONLY 1 CACHE INTERNAL "") +SET(HAVE_ISINF CACHE INTERNAL "") +SET(HAVE_ISNAN CACHE INTERNAL "") +SET(HAVE_ISSETUGID CACHE INTERNAL "") +SET(HAVE_LANGINFO_H CACHE INTERNAL "") +SET(HAVE_LDIV 1 CACHE INTERNAL "") +SET(HAVE_LIMITS_H 1 CACHE INTERNAL "") +SET(HAVE_LOCALE_H 1 CACHE INTERNAL "") +SET(HAVE_LOCALTIME_R CACHE INTERNAL "") +SET(HAVE_LOG2 CACHE INTERNAL "") +SET(HAVE_LONGJMP 1 CACHE INTERNAL "") +SET(HAVE_LRAND48 CACHE INTERNAL "") +SET(HAVE_LSTAT CACHE INTERNAL "") +SET(HAVE_MADVISE CACHE INTERNAL "") +SET(HAVE_MALLINFO CACHE INTERNAL "") +SET(HAVE_MALLOC_H 1 CACHE INTERNAL "") +SET(HAVE_MEMALIGN CACHE INTERNAL "") +SET(HAVE_MEMCPY 1 CACHE INTERNAL "") +SET(HAVE_MEMMOVE 1 CACHE INTERNAL "") +SET(HAVE_MEMORY_H 1 CACHE INTERNAL "") +SET(HAVE_MKSTEMP CACHE INTERNAL "") +SET(HAVE_MLOCK CACHE INTERNAL "") +SET(HAVE_MLOCKALL CACHE INTERNAL "") +SET(HAVE_MMAP CACHE INTERNAL "") +SET(HAVE_MMAP64 CACHE INTERNAL "") +SET(HAVE_NETINET_IN6_H CACHE INTERNAL "") +SET(HAVE_NETINET_IN_H CACHE INTERNAL "") +SET(HAVE_NL_LANGINFO CACHE INTERNAL "") +SET(HAVE_PASE_ENVIRONMENT CACHE INTERNAL "") +SET(HAVE_PATHS_H CACHE INTERNAL "") +SET(HAVE_PCLOSE CACHE INTERNAL "") +SET(HAVE_PERROR 1 CACHE INTERNAL "") +SET(HAVE_POLL CACHE INTERNAL "") +SET(HAVE_POPEN CACHE INTERNAL "") +SET(HAVE_PORT_CREATE CACHE INTERNAL "") +SET(HAVE_PORT_H CACHE INTERNAL "") +SET(HAVE_POSIX_FALLOCATE CACHE INTERNAL "") +SET(HAVE_POSIX_SIGNALS CACHE INTERNAL "") +SET(HAVE_PREAD CACHE INTERNAL "") +SET(HAVE_PRINTSTACK CACHE INTERNAL "") +SET(HAVE_PTHREAD_ATTR_CREATE CACHE INTERNAL "") +SET(HAVE_PTHREAD_ATTR_GETSTACKSIZE CACHE INTERNAL "") +SET(HAVE_PTHREAD_ATTR_SETSCOPE CACHE INTERNAL "") +SET(HAVE_PTHREAD_ATTR_SETSTACKSIZE CACHE INTERNAL "") +SET(HAVE_PTHREAD_CONDATTR_CREATE CACHE INTERNAL "") +SET(HAVE_PTHREAD_CONDATTR_SETCLOCK CACHE INTERNAL "") +SET(HAVE_PTHREAD_INIT CACHE INTERNAL "") +SET(HAVE_PTHREAD_KEY_DELETE CACHE INTERNAL "") +SET(HAVE_PTHREAD_RWLOCK_RDLOCK CACHE INTERNAL "") +SET(HAVE_PTHREAD_SIGMASK CACHE INTERNAL "") +SET(HAVE_PTHREAD_THREADMASK CACHE INTERNAL "") +SET(HAVE_PTHREAD_YIELD_NP CACHE INTERNAL "") +SET(HAVE_PTHREAD_YIELD_ZERO_ARG CACHE INTERNAL "") +SET(HAVE_PUTENV 1 CACHE INTERNAL "") +SET(HAVE_PWD_H CACHE INTERNAL "") +SET(HAVE_RDTSCLL CACHE INTERNAL "") +SET(HAVE_READDIR_R CACHE INTERNAL "") +SET(HAVE_READLINK CACHE INTERNAL "") +SET(HAVE_READ_REAL_TIME CACHE INTERNAL "") +SET(HAVE_REALPATH CACHE INTERNAL "") +SET(HAVE_REGCOMP CACHE INTERNAL "") +SET(HAVE_RENAME 1 CACHE INTERNAL "") +SET(HAVE_RE_COMP CACHE INTERNAL "") +SET(HAVE_RINT CACHE INTERNAL "") +SET(HAVE_RWLOCK_INIT CACHE INTERNAL "") +SET(HAVE_SCHED_H CACHE INTERNAL "") +SET(HAVE_SCHED_YIELD CACHE INTERNAL "") +SET(HAVE_SELECT 1 CACHE INTERNAL "") +SET(HAVE_SELECT_H CACHE INTERNAL "") +SET(HAVE_SEMAPHORE_H CACHE INTERNAL "") +SET(HAVE_SETENV CACHE INTERNAL "") +SET(HAVE_SETFD CACHE INTERNAL "") +SET(HAVE_SETLOCALE 1 CACHE INTERNAL "") +SET(HAVE_SHMAT CACHE INTERNAL "") +SET(HAVE_SHMCTL CACHE INTERNAL "") +SET(HAVE_SHMDT CACHE INTERNAL "") +SET(HAVE_SHMGET CACHE INTERNAL "") +SET(HAVE_SIGACTION CACHE INTERNAL "") +SET(HAVE_SIGADDSET CACHE INTERNAL "") +SET(HAVE_SIGEMPTYSET CACHE INTERNAL "") +SET(HAVE_SIGHOLD CACHE INTERNAL "") +SET(HAVE_SIGINT 1 CACHE INTERNAL "") +SET(HAVE_SIGPIPE CACHE INTERNAL "") +SET(HAVE_SIGQUIT CACHE INTERNAL "") +SET(HAVE_SIGSET CACHE INTERNAL "") +SET(HAVE_SIGTERM 1 CACHE INTERNAL "") +SET(HAVE_SIGTHREADMASK CACHE INTERNAL "") +SET(HAVE_SIGWAIT CACHE INTERNAL "") +SET(HAVE_SIZEOF_BOOL FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_CHAR TRUE CACHE INTERNAL "") +SET(SIZEOF_CHAR 1 CACHE INTERNAL "") +SET(HAVE_SIZEOF_CHARP TRUE CACHE INTERNAL "") +SET(SIZEOF_CHARP ${CMAKE_SIZEOF_VOID_P} CACHE INTERNAL "") +SET(HAVE_SIZEOF_IN6_ADDR TRUE CACHE INTERNAL "") +SET(HAVE_SIZEOF_INT TRUE CACHE INTERNAL "") +SET(SIZEOF_INT 4 CACHE INTERNAL "") +SET(HAVE_SIZEOF_INT16 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_INT32 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_INT64 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_INT8 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_LONG TRUE CACHE INTERNAL "") +SET(SIZEOF_LONG 4 CACHE INTERNAL "") +SET(HAVE_SIZEOF_LONG_LONG TRUE CACHE INTERNAL "") +SET(SIZEOF_LONG_LONG 8 CACHE INTERNAL "") +SET(HAVE_SIZEOF_MODE_T FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_OFF_T TRUE CACHE INTERNAL "") +SET(SIZEOF_OFF_T 4 CACHE INTERNAL "") +SET(HAVE_SIZEOF_SHORT TRUE CACHE INTERNAL "") +SET(SIZEOF_SHORT 2 CACHE INTERNAL "") +SET(HAVE_SIZEOF_SIGSET_T FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_SIZE_T TRUE CACHE INTERNAL "") +SET(SIZEOF_SIZE_T ${CMAKE_SIZEOF_VOID_P} CACHE INTERNAL "") +SET(HAVE_SIZEOF_SOCKADDR_IN6 TRUE CACHE INTERNAL "") +SET(HAVE_SIZEOF_SOCKLEN_T FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_UCHAR FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_UINT FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_UINT16 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_UINT32 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_UINT64 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_UINT8 FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_ULONG FALSE CACHE INTERNAL "") +SET(HAVE_SIZEOF_U_INT32_T FALSE CACHE INTERNAL "") +SET(HAVE_SIZE_OF_SSIZE_T FALSE CACHE INTERNAL "") +SET(HAVE_SLEEP CACHE INTERNAL "") +SET(HAVE_SNPRINTF CACHE INTERNAL "") +SET(HAVE_SOCKADDR_STORAGE_SS_FAMILY 1 CACHE INTERNAL "") +SET(HAVE_SOLARIS_STYLE_GETHOST CACHE INTERNAL "") +SET(STACK_DIRECTION -1 CACHE INTERNAL "") +SET(HAVE_STDARG_H 1 CACHE INTERNAL "") +SET(HAVE_STDDEF_H 1 CACHE INTERNAL "") +SET(HAVE_STDINT_H CACHE INTERNAL "") +SET(HAVE_STDLIB_H 1 CACHE INTERNAL "") +SET(HAVE_STPCPY CACHE INTERNAL "") +SET(HAVE_STRCASECMP CACHE INTERNAL "") +SET(HAVE_STRCOLL 1 CACHE INTERNAL "") +SET(HAVE_STRDUP 1 CACHE INTERNAL "") +SET(HAVE_STRERROR 1 CACHE INTERNAL "") +SET(HAVE_STRINGS_H CACHE INTERNAL "") +SET(HAVE_STRING_H 1 CACHE INTERNAL "") +SET(HAVE_STRLCAT CACHE INTERNAL "") +SET(HAVE_STRLCPY CACHE INTERNAL "") +SET(HAVE_STRNCASECMP CACHE INTERNAL "") +IF(MSVC_VERSION GREATER 1310) +SET(HAVE_STRNLEN 1 CACHE INTERNAL "") +ENDIF() +SET(HAVE_STRPBRK 1 CACHE INTERNAL "") +SET(HAVE_STRSEP CACHE INTERNAL "") +SET(HAVE_STRSIGNAL CACHE INTERNAL "") +SET(HAVE_STRSTR 1 CACHE INTERNAL "") +SET(HAVE_STRTOL 1 CACHE INTERNAL "") +SET(HAVE_STRTOLL CACHE INTERNAL "") +SET(HAVE_STRTOUL 1 CACHE INTERNAL "") +SET(HAVE_STRTOULL CACHE INTERNAL "") +SET(HAVE_SVR3_SIGNALS CACHE INTERNAL "") +SET(HAVE_SYNCH_H CACHE INTERNAL "") +SET(HAVE_SYSENT_H CACHE INTERNAL "") +SET(HAVE_SYS_CDEFS_H CACHE INTERNAL "") +SET(HAVE_SYS_DIR_H CACHE INTERNAL "") +SET(HAVE_SYS_ERRLIST CACHE INTERNAL "") +SET(HAVE_SYS_FILE_H CACHE INTERNAL "") +SET(HAVE_SYS_FPU_H CACHE INTERNAL "") +SET(HAVE_SYS_IOCTL CACHE INTERNAL "") +SET(HAVE_SYS_IOCTL_H CACHE INTERNAL "") +SET(HAVE_SYS_IPC_H CACHE INTERNAL "") +SET(HAVE_SYS_MALLOC_H CACHE INTERNAL "") +SET(HAVE_SYS_MMAN_H CACHE INTERNAL "") +SET(HAVE_SYS_PARAM_H CACHE INTERNAL "") +SET(HAVE_SYS_PRCTL_H CACHE INTERNAL "") +SET(HAVE_SYS_PTEM_H CACHE INTERNAL "") +SET(HAVE_SYS_PTE_H CACHE INTERNAL "") +SET(HAVE_SYS_RESOURCE_H CACHE INTERNAL "") +SET(HAVE_SYS_SELECT_H CACHE INTERNAL "") +SET(HAVE_SYS_SHM_H CACHE INTERNAL "") +SET(HAVE_SYS_SOCKET_H CACHE INTERNAL "") +SET(HAVE_SYS_STAT_H 1 CACHE INTERNAL "") +SET(HAVE_SYS_STREAM_H CACHE INTERNAL "") +SET(HAVE_SYS_TERMCAP_H CACHE INTERNAL "") +SET(HAVE_SYS_TIMEB_H 1 CACHE INTERNAL "") +SET(HAVE_SYS_TIMES_H CACHE INTERNAL "") +SET(HAVE_SYS_TIME_H CACHE INTERNAL "") +SET(HAVE_SYS_TYPES_H 1 CACHE INTERNAL "") +SET(HAVE_SYS_UN_H CACHE INTERNAL "") +SET(HAVE_SYS_UTIME_H 1 CACHE INTERNAL "") +SET(HAVE_SYS_VADVISE_H CACHE INTERNAL "") +SET(HAVE_SYS_WAIT_H CACHE INTERNAL "") +SET(HAVE_TCGETATTR CACHE INTERNAL "") +SET(HAVE_TELL 1 CACHE INTERNAL "") +SET(HAVE_TEMPNAM 1 CACHE INTERNAL "") +SET(HAVE_TERMCAP_H CACHE INTERNAL "") +SET(HAVE_TERMIOS_H CACHE INTERNAL "") +SET(HAVE_TERMIO_H CACHE INTERNAL "") +SET(HAVE_TERM_H CACHE INTERNAL "") +SET(HAVE_THR_SETCONCURRENCY CACHE INTERNAL "") +SET(HAVE_THR_YIELD CACHE INTERNAL "") +SET(HAVE_TIME 1 CACHE INTERNAL "") +SET(HAVE_TIMES CACHE INTERNAL "") +SET(HAVE_TIMESPEC_TS_SEC CACHE INTERNAL "") +SET(HAVE_TIME_H 1 CACHE INTERNAL "") +SET(HAVE_TZNAME 1 CACHE INTERNAL "") +SET(HAVE_UNISTD_H CACHE INTERNAL "") +SET(HAVE_UTIME_H CACHE INTERNAL "") +SET(HAVE_VALLOC CACHE INTERNAL "") +SET(HAVE_VARARGS_H 1 CACHE INTERNAL "") +SET(HAVE_VASPRINTF CACHE INTERNAL "") +SET(HAVE_VPRINTF 1 CACHE INTERNAL "") +IF(MSVC_VERSION GREATER 1310) +SET(HAVE_VSNPRINTF 1 CACHE INTERNAL "") +ENDIF() +SET(HAVE_WEAK_SYMBOL CACHE INTERNAL "") +SET(HAVE_WORDS_BIGENDIAN TRUE CACHE INTERNAL "") +SET(WORDS_BIGENDIAN CACHE INTERNAL "") +SET(HAVE__S_IFIFO 1 CACHE INTERNAL "") +SET(HAVE__S_IREAD 1 CACHE INTERNAL "") +SET(HAVE__finite 1 CACHE INTERNAL "") +SET(HAVE__isnan 1 CACHE INTERNAL "") +SET(HAVE__pclose 1 CACHE INTERNAL "") +SET(HAVE__popen 1 CACHE INTERNAL "") +SET(HAVE__snprintf 1 CACHE INTERNAL "") +SET(HAVE__stricmp 1 CACHE INTERNAL "") +SET(HAVE__strnicmp 1 CACHE INTERNAL "") +SET(HAVE__strtoi64 1 CACHE INTERNAL "") +SET(HAVE__strtoui64 1 CACHE INTERNAL "") +IF(MSVC_VERSION GREATER 1310) + SET(HAVE_strtok_s 1 CACHE INTERNAL "") +ENDIF() +SET(STDC_HEADERS CACHE 1 INTERNAL "") +SET(STRUCT_DIRENT_HAS_D_INO CACHE INTERNAL "") +SET(STRUCT_DIRENT_HAS_D_INO CACHE INTERNAL "") +SET(STRUCT_DIRENT_HAS_D_NAMLEN CACHE INTERNAL "") +SET(TIME_WITH_SYS_TIME CACHE INTERNAL "") +SET(TIOCSTAT_IN_SYS_IOCTL CACHE INTERNAL "") +SET(HAVE_S_IROTH CACHE INTERNAL "") +SET(HAVE_S_IFIFO CACHE INTERNAL "") +SET(QSORT_TYPE_IS_VOID 1 CACHE INTERNAL "") +SET(SIGNAL_RETURN_TYPE_IS_VOID 1 CACHE INTERNAL "") +SET(C_HAS_inline CACHE INTERNAL "") +SET(C_HAS___inline 1 CACHE INTERNAL "") +SET(FIONREAD_IN_SYS_IOCTL CACHE INTERNAL "") +SET(GWINSZ_IN_SYS_IOCTL CACHE INTERNAL "") +ENDIF() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 2c00c71c3ee..d6411641192 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -160,7 +160,11 @@ MACRO(MYSQL_ADD_PLUGIN) ENDIF() ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) - + IF(NOT ARG_MODULE_ONLY) + # set cached variable, e.g with checkbox in GUI + SET(${with_var} OFF CACHE BOOL "Link ${plugin} statically to the server" + FORCE) + ENDIF() SET_TARGET_PROPERTIES(${target} PROPERTIES OUTPUT_NAME "${ARG_MODULE_OUTPUT_NAME}") # Install dynamic library diff --git a/config.h.cmake b/config.h.cmake index 3b4a463714f..8e7cc99f1c2 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -540,6 +540,7 @@ #cmakedefine strtok_r @strtok_r@ #cmakedefine strtoll @strtoll@ #cmakedefine strtoull @strtoull@ +#cmakedefine vsnprintf @vsnprintf@ #if (_MSC_VER > 1310) #define HAVE_SETENV #define setenv(a,b,c) _putenv_s(a,b) diff --git a/configure.cmake b/configure.cmake index 8a2a1f0ad78..9a1fdfa7942 100644 --- a/configure.cmake +++ b/configure.cmake @@ -154,7 +154,7 @@ ENDIF() # on Windows. In such cases it makes sense to use these macros # as build with Visual Studio is considerably faster if irrelevant # checks are omitted. -MACRO(CHECK_INCLUDE_FILES_UNIX INCLUDES VAR) +MACRO(CHECK_INCLUDE_FILES INCLUDES VAR) IF(UNIX) CHECK_INCLUDE_FILES ("${INCLUDES}" ${VAR}) ENDIF() @@ -166,19 +166,19 @@ IF(UNIX) ENDIF() ENDMACRO() -MACRO(CHECK_CXX_SOURCE_COMPILES_UNIX SRC VAR) +MACRO(CHECK_CXX_SOURCE_COMPILES SRC VAR) IF(UNIX) CHECK_CXX_SOURCE_COMPILES("${SRC}" ${VAR}) ENDIF() ENDMACRO() -MACRO(CHECK_FUNCTION_EXISTS_UNIX FUNC VAR) +MACRO(CHECK_FUNCTION_EXISTS FUNC VAR) IF(UNIX) CHECK_FUNCTION_EXISTS("${FUNC}" ${VAR}) ENDIF() ENDMACRO() -MACRO (CHECK_SYMBOL_EXISTS_UNIX SYM HEADER VAR) +MACRO (CHECK_SYMBOL_EXISTS SYM HEADER VAR) IF(UNIX) CHECK_SYMBOL_EXISTS("${SYM}" "${HEADER}" ${VAR}) ENDIF() @@ -191,74 +191,74 @@ INCLUDE (CheckIncludeFiles) CHECK_INCLUDE_FILES ("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) CHECK_INCLUDE_FILES (sys/types.h HAVE_SYS_TYPES_H) -CHECK_INCLUDE_FILES_UNIX (alloca.h HAVE_ALLOCA_H) -CHECK_INCLUDE_FILES_UNIX (aio.h HAVE_AIO_H) -CHECK_INCLUDE_FILES_UNIX (arpa/inet.h HAVE_ARPA_INET_H) -CHECK_INCLUDE_FILES_UNIX (crypt.h HAVE_CRYPT_H) +CHECK_INCLUDE_FILES (alloca.h HAVE_ALLOCA_H) +CHECK_INCLUDE_FILES (aio.h HAVE_AIO_H) +CHECK_INCLUDE_FILES (arpa/inet.h HAVE_ARPA_INET_H) +CHECK_INCLUDE_FILES (crypt.h HAVE_CRYPT_H) CHECK_INCLUDE_FILES (dirent.h HAVE_DIRENT_H) -CHECK_INCLUDE_FILES_UNIX (dlfcn.h HAVE_DLFCN_H) -CHECK_INCLUDE_FILES_UNIX (execinfo.h HAVE_EXECINFO_H) +CHECK_INCLUDE_FILES (dlfcn.h HAVE_DLFCN_H) +CHECK_INCLUDE_FILES (execinfo.h HAVE_EXECINFO_H) CHECK_INCLUDE_FILES (fcntl.h HAVE_FCNTL_H) CHECK_INCLUDE_FILES (fenv.h HAVE_FENV_H) CHECK_INCLUDE_FILES (float.h HAVE_FLOAT_H) -CHECK_INCLUDE_FILES_UNIX (floatingpoint.h HAVE_FLOATINGPOINT_H) -CHECK_INCLUDE_FILES_UNIX (fpu_control.h HAVE_FPU_CONTROL_H) -CHECK_INCLUDE_FILES_UNIX (grp.h HAVE_GRP_H) -CHECK_INCLUDE_FILES_UNIX (ieeefp.h HAVE_IEEEFP_H) +CHECK_INCLUDE_FILES (floatingpoint.h HAVE_FLOATINGPOINT_H) +CHECK_INCLUDE_FILES (fpu_control.h HAVE_FPU_CONTROL_H) +CHECK_INCLUDE_FILES (grp.h HAVE_GRP_H) +CHECK_INCLUDE_FILES (ieeefp.h HAVE_IEEEFP_H) CHECK_INCLUDE_FILES (inttypes.h HAVE_INTTYPES_H) -CHECK_INCLUDE_FILES_UNIX (langinfo.h HAVE_LANGINFO_H) +CHECK_INCLUDE_FILES (langinfo.h HAVE_LANGINFO_H) CHECK_INCLUDE_FILES (limits.h HAVE_LIMITS_H) CHECK_INCLUDE_FILES (locale.h HAVE_LOCALE_H) CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H) CHECK_INCLUDE_FILES (memory.h HAVE_MEMORY_H) -CHECK_INCLUDE_FILES_UNIX (netinet/in.h HAVE_NETINET_IN_H) -CHECK_INCLUDE_FILES_UNIX (paths.h HAVE_PATHS_H) -CHECK_INCLUDE_FILES_UNIX (port.h HAVE_PORT_H) -CHECK_INCLUDE_FILES_UNIX (pwd.h HAVE_PWD_H) -CHECK_INCLUDE_FILES_UNIX (sched.h HAVE_SCHED_H) -CHECK_INCLUDE_FILES_UNIX (select.h HAVE_SELECT_H) -CHECK_INCLUDE_FILES_UNIX (semaphore.h HAVE_SEMAPHORE_H) -CHECK_INCLUDE_FILES_UNIX (sys/dir.h HAVE_SYS_DIR_H) -CHECK_INCLUDE_FILES_UNIX (sys/pte.h HAVE_SYS_PTE_H) -CHECK_INCLUDE_FILES_UNIX (sys/ptem.h HAVE_SYS_PTEM_H) +CHECK_INCLUDE_FILES (netinet/in.h HAVE_NETINET_IN_H) +CHECK_INCLUDE_FILES (paths.h HAVE_PATHS_H) +CHECK_INCLUDE_FILES (port.h HAVE_PORT_H) +CHECK_INCLUDE_FILES (pwd.h HAVE_PWD_H) +CHECK_INCLUDE_FILES (sched.h HAVE_SCHED_H) +CHECK_INCLUDE_FILES (select.h HAVE_SELECT_H) +CHECK_INCLUDE_FILES (semaphore.h HAVE_SEMAPHORE_H) +CHECK_INCLUDE_FILES (sys/dir.h HAVE_SYS_DIR_H) +CHECK_INCLUDE_FILES (sys/pte.h HAVE_SYS_PTE_H) +CHECK_INCLUDE_FILES (sys/ptem.h HAVE_SYS_PTEM_H) CHECK_INCLUDE_FILES (stddef.h HAVE_STDDEF_H) CHECK_INCLUDE_FILES (stdint.h HAVE_STDINT_H) CHECK_INCLUDE_FILES (stdlib.h HAVE_STDLIB_H) CHECK_INCLUDE_FILES (strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILES (string.h HAVE_STRING_H) -CHECK_INCLUDE_FILES_UNIX (synch.h HAVE_SYNCH_H) -CHECK_INCLUDE_FILES_UNIX (sysent.h HAVE_SYSENT_H) -CHECK_INCLUDE_FILES_UNIX (sys/cdefs.h HAVE_SYS_CDEFS_H) -CHECK_INCLUDE_FILES_UNIX (sys/file.h HAVE_SYS_FILE_H) -CHECK_INCLUDE_FILES_UNIX (sys/fpu.h HAVE_SYS_FPU_H) -CHECK_INCLUDE_FILES_UNIX (sys/ioctl.h HAVE_SYS_IOCTL_H) -CHECK_INCLUDE_FILES_UNIX (sys/ipc.h HAVE_SYS_IPC_H) -CHECK_INCLUDE_FILES_UNIX (sys/malloc.h HAVE_SYS_MALLOC_H) -CHECK_INCLUDE_FILES_UNIX (sys/mman.h HAVE_SYS_MMAN_H) -CHECK_INCLUDE_FILES_UNIX (sys/prctl.h HAVE_SYS_PRCTL_H) -CHECK_INCLUDE_FILES_UNIX (sys/resource.h HAVE_SYS_RESOURCE_H) -CHECK_INCLUDE_FILES_UNIX (sys/select.h HAVE_SYS_SELECT_H) -CHECK_INCLUDE_FILES_UNIX (sys/shm.h HAVE_SYS_SHM_H) -CHECK_INCLUDE_FILES_UNIX (sys/socket.h HAVE_SYS_SOCKET_H) +CHECK_INCLUDE_FILES (synch.h HAVE_SYNCH_H) +CHECK_INCLUDE_FILES (sysent.h HAVE_SYSENT_H) +CHECK_INCLUDE_FILES (sys/cdefs.h HAVE_SYS_CDEFS_H) +CHECK_INCLUDE_FILES (sys/file.h HAVE_SYS_FILE_H) +CHECK_INCLUDE_FILES (sys/fpu.h HAVE_SYS_FPU_H) +CHECK_INCLUDE_FILES (sys/ioctl.h HAVE_SYS_IOCTL_H) +CHECK_INCLUDE_FILES (sys/ipc.h HAVE_SYS_IPC_H) +CHECK_INCLUDE_FILES (sys/malloc.h HAVE_SYS_MALLOC_H) +CHECK_INCLUDE_FILES (sys/mman.h HAVE_SYS_MMAN_H) +CHECK_INCLUDE_FILES (sys/prctl.h HAVE_SYS_PRCTL_H) +CHECK_INCLUDE_FILES (sys/resource.h HAVE_SYS_RESOURCE_H) +CHECK_INCLUDE_FILES (sys/select.h HAVE_SYS_SELECT_H) +CHECK_INCLUDE_FILES (sys/shm.h HAVE_SYS_SHM_H) +CHECK_INCLUDE_FILES (sys/socket.h HAVE_SYS_SOCKET_H) CHECK_INCLUDE_FILES (sys/stat.h HAVE_SYS_STAT_H) -CHECK_INCLUDE_FILES_UNIX (sys/stream.h HAVE_SYS_STREAM_H) -CHECK_INCLUDE_FILES_UNIX (sys/termcap.h HAVE_SYS_TERMCAP_H) +CHECK_INCLUDE_FILES (sys/stream.h HAVE_SYS_STREAM_H) +CHECK_INCLUDE_FILES (sys/termcap.h HAVE_SYS_TERMCAP_H) CHECK_INCLUDE_FILES ("time.h;sys/timeb.h" HAVE_SYS_TIMEB_H) -CHECK_INCLUDE_FILES_UNIX ("curses.h;term.h" HAVE_TERM_H) -CHECK_INCLUDE_FILES_UNIX (termios.h HAVE_TERMIOS_H) -CHECK_INCLUDE_FILES_UNIX (termio.h HAVE_TERMIO_H) -CHECK_INCLUDE_FILES_UNIX (termcap.h HAVE_TERMCAP_H) -CHECK_INCLUDE_FILES_UNIX (unistd.h HAVE_UNISTD_H) +CHECK_INCLUDE_FILES ("curses.h;term.h" HAVE_TERM_H) +CHECK_INCLUDE_FILES (termios.h HAVE_TERMIOS_H) +CHECK_INCLUDE_FILES (termio.h HAVE_TERMIO_H) +CHECK_INCLUDE_FILES (termcap.h HAVE_TERMCAP_H) +CHECK_INCLUDE_FILES (unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILES (utime.h HAVE_UTIME_H) CHECK_INCLUDE_FILES (varargs.h HAVE_VARARGS_H) CHECK_INCLUDE_FILES (sys/time.h HAVE_SYS_TIME_H) CHECK_INCLUDE_FILES (sys/utime.h HAVE_SYS_UTIME_H) -CHECK_INCLUDE_FILES_UNIX (sys/wait.h HAVE_SYS_WAIT_H) -CHECK_INCLUDE_FILES_UNIX (sys/param.h HAVE_SYS_PARAM_H) -CHECK_INCLUDE_FILES_UNIX (sys/vadvise.h HAVE_SYS_VADVISE_H) -CHECK_INCLUDE_FILES_UNIX (fnmatch.h HAVE_FNMATCH_H) +CHECK_INCLUDE_FILES (sys/wait.h HAVE_SYS_WAIT_H) +CHECK_INCLUDE_FILES (sys/param.h HAVE_SYS_PARAM_H) +CHECK_INCLUDE_FILES (sys/vadvise.h HAVE_SYS_VADVISE_H) +CHECK_INCLUDE_FILES (fnmatch.h HAVE_FNMATCH_H) CHECK_INCLUDE_FILES (stdarg.h HAVE_STDARG_H) -CHECK_INCLUDE_FILES_UNIX("stdlib.h;sys/un.h" HAVE_SYS_UN_H) +CHECK_INCLUDE_FILES("stdlib.h;sys/un.h" HAVE_SYS_UN_H) # Figure out threading library # @@ -268,116 +268,116 @@ FIND_PACKAGE (Threads) # Tests for functions # #CHECK_FUNCTION_EXISTS (aiowait HAVE_AIOWAIT) -CHECK_FUNCTION_EXISTS_UNIX (aio_read HAVE_AIO_READ) -CHECK_FUNCTION_EXISTS_UNIX (alarm HAVE_ALARM) +CHECK_FUNCTION_EXISTS (aio_read HAVE_AIO_READ) +CHECK_FUNCTION_EXISTS (alarm HAVE_ALARM) SET(HAVE_ALLOCA 1) -CHECK_FUNCTION_EXISTS_UNIX (backtrace HAVE_BACKTRACE) -CHECK_FUNCTION_EXISTS_UNIX (backtrace_symbols HAVE_BACKTRACE_SYMBOLS) -CHECK_FUNCTION_EXISTS_UNIX (backtrace_symbols_fd HAVE_BACKTRACE_SYMBOLS_FD) -CHECK_FUNCTION_EXISTS_UNIX (printstack HAVE_PRINTSTACK) -CHECK_FUNCTION_EXISTS_UNIX (bcmp HAVE_BCMP) -CHECK_FUNCTION_EXISTS_UNIX (bfill HAVE_BFILL) -CHECK_FUNCTION_EXISTS_UNIX (bmove HAVE_BMOVE) +CHECK_FUNCTION_EXISTS (backtrace HAVE_BACKTRACE) +CHECK_FUNCTION_EXISTS (backtrace_symbols HAVE_BACKTRACE_SYMBOLS) +CHECK_FUNCTION_EXISTS (backtrace_symbols_fd HAVE_BACKTRACE_SYMBOLS_FD) +CHECK_FUNCTION_EXISTS (printstack HAVE_PRINTSTACK) +CHECK_FUNCTION_EXISTS (bcmp HAVE_BCMP) +CHECK_FUNCTION_EXISTS (bfill HAVE_BFILL) +CHECK_FUNCTION_EXISTS (bmove HAVE_BMOVE) CHECK_FUNCTION_EXISTS (bsearch HAVE_BSEARCH) CHECK_FUNCTION_EXISTS (index HAVE_INDEX) -CHECK_FUNCTION_EXISTS_UNIX (bzero HAVE_BZERO) -CHECK_FUNCTION_EXISTS_UNIX (clock_gettime HAVE_CLOCK_GETTIME) -CHECK_FUNCTION_EXISTS_UNIX (cuserid HAVE_CUSERID) -CHECK_FUNCTION_EXISTS_UNIX (directio HAVE_DIRECTIO) -CHECK_FUNCTION_EXISTS_UNIX (_doprnt HAVE_DOPRNT) -CHECK_FUNCTION_EXISTS_UNIX (flockfile HAVE_FLOCKFILE) -CHECK_FUNCTION_EXISTS_UNIX (ftruncate HAVE_FTRUNCATE) -CHECK_FUNCTION_EXISTS_UNIX (getline HAVE_GETLINE) -CHECK_FUNCTION_EXISTS_UNIX (compress HAVE_COMPRESS) -CHECK_FUNCTION_EXISTS_UNIX (crypt HAVE_CRYPT) -CHECK_FUNCTION_EXISTS_UNIX (dlerror HAVE_DLERROR) -CHECK_FUNCTION_EXISTS_UNIX (dlopen HAVE_DLOPEN) -CHECK_FUNCTION_EXISTS_UNIX (fchmod HAVE_FCHMOD) -CHECK_FUNCTION_EXISTS_UNIX (fcntl HAVE_FCNTL) -CHECK_FUNCTION_EXISTS_UNIX (fconvert HAVE_FCONVERT) -CHECK_SYMBOL_EXISTS_UNIX(fdatasync "unistd.h" HAVE_FDATASYNC) -CHECK_FUNCTION_EXISTS_UNIX (fesetround HAVE_FESETROUND) -CHECK_FUNCTION_EXISTS_UNIX (fpsetmask HAVE_FPSETMASK) -CHECK_FUNCTION_EXISTS_UNIX (fseeko HAVE_FSEEKO) -CHECK_FUNCTION_EXISTS_UNIX (fsync HAVE_FSYNC) +CHECK_FUNCTION_EXISTS (bzero HAVE_BZERO) +CHECK_FUNCTION_EXISTS (clock_gettime HAVE_CLOCK_GETTIME) +CHECK_FUNCTION_EXISTS (cuserid HAVE_CUSERID) +CHECK_FUNCTION_EXISTS (directio HAVE_DIRECTIO) +CHECK_FUNCTION_EXISTS (_doprnt HAVE_DOPRNT) +CHECK_FUNCTION_EXISTS (flockfile HAVE_FLOCKFILE) +CHECK_FUNCTION_EXISTS (ftruncate HAVE_FTRUNCATE) +CHECK_FUNCTION_EXISTS (getline HAVE_GETLINE) +CHECK_FUNCTION_EXISTS (compress HAVE_COMPRESS) +CHECK_FUNCTION_EXISTS (crypt HAVE_CRYPT) +CHECK_FUNCTION_EXISTS (dlerror HAVE_DLERROR) +CHECK_FUNCTION_EXISTS (dlopen HAVE_DLOPEN) +CHECK_FUNCTION_EXISTS (fchmod HAVE_FCHMOD) +CHECK_FUNCTION_EXISTS (fcntl HAVE_FCNTL) +CHECK_FUNCTION_EXISTS (fconvert HAVE_FCONVERT) +CHECK_SYMBOL_EXISTS(fdatasync "unistd.h" HAVE_FDATASYNC) +CHECK_FUNCTION_EXISTS (fesetround HAVE_FESETROUND) +CHECK_FUNCTION_EXISTS (fpsetmask HAVE_FPSETMASK) +CHECK_FUNCTION_EXISTS (fseeko HAVE_FSEEKO) +CHECK_FUNCTION_EXISTS (fsync HAVE_FSYNC) CHECK_FUNCTION_EXISTS (getcwd HAVE_GETCWD) -CHECK_FUNCTION_EXISTS_UNIX (gethostbyaddr_r HAVE_GETHOSTBYADDR_R) -CHECK_FUNCTION_EXISTS_UNIX (gethostbyname_r HAVE_GETHOSTBYNAME_R) -CHECK_FUNCTION_EXISTS_UNIX (gethrtime HAVE_GETHRTIME) +CHECK_FUNCTION_EXISTS (gethostbyaddr_r HAVE_GETHOSTBYADDR_R) +CHECK_FUNCTION_EXISTS (gethostbyname_r HAVE_GETHOSTBYNAME_R) +CHECK_FUNCTION_EXISTS (gethrtime HAVE_GETHRTIME) CHECK_FUNCTION_EXISTS (getnameinfo HAVE_GETNAMEINFO) -CHECK_FUNCTION_EXISTS_UNIX (getpass HAVE_GETPASS) -CHECK_FUNCTION_EXISTS_UNIX (getpassphrase HAVE_GETPASSPHRASE) -CHECK_FUNCTION_EXISTS_UNIX (getpwnam HAVE_GETPWNAM) -CHECK_FUNCTION_EXISTS_UNIX (getpwuid HAVE_GETPWUID) -CHECK_FUNCTION_EXISTS_UNIX (getrlimit HAVE_GETRLIMIT) -CHECK_FUNCTION_EXISTS_UNIX (getrusage HAVE_GETRUSAGE) -CHECK_FUNCTION_EXISTS_UNIX (getwd HAVE_GETWD) -CHECK_FUNCTION_EXISTS_UNIX (gmtime_r HAVE_GMTIME_R) -CHECK_FUNCTION_EXISTS_UNIX (initgroups HAVE_INITGROUPS) -CHECK_FUNCTION_EXISTS_UNIX (issetugid HAVE_ISSETUGID) +CHECK_FUNCTION_EXISTS (getpass HAVE_GETPASS) +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 (getrusage HAVE_GETRUSAGE) +CHECK_FUNCTION_EXISTS (getwd HAVE_GETWD) +CHECK_FUNCTION_EXISTS (gmtime_r HAVE_GMTIME_R) +CHECK_FUNCTION_EXISTS (initgroups HAVE_INITGROUPS) +CHECK_FUNCTION_EXISTS (issetugid HAVE_ISSETUGID) CHECK_FUNCTION_EXISTS (ldiv HAVE_LDIV) -CHECK_FUNCTION_EXISTS_UNIX (localtime_r HAVE_LOCALTIME_R) +CHECK_FUNCTION_EXISTS (localtime_r HAVE_LOCALTIME_R) CHECK_FUNCTION_EXISTS (longjmp HAVE_LONGJMP) CHECK_FUNCTION_EXISTS (lstat HAVE_LSTAT) -CHECK_FUNCTION_EXISTS_UNIX (madvise HAVE_MADVISE) -CHECK_FUNCTION_EXISTS_UNIX (mallinfo HAVE_MALLINFO) +CHECK_FUNCTION_EXISTS (madvise HAVE_MADVISE) +CHECK_FUNCTION_EXISTS (mallinfo HAVE_MALLINFO) CHECK_FUNCTION_EXISTS (memcpy HAVE_MEMCPY) CHECK_FUNCTION_EXISTS (memmove HAVE_MEMMOVE) CHECK_FUNCTION_EXISTS (mkstemp HAVE_MKSTEMP) -CHECK_FUNCTION_EXISTS_UNIX (mlock HAVE_MLOCK) -CHECK_FUNCTION_EXISTS_UNIX (mlockall HAVE_MLOCKALL) -CHECK_FUNCTION_EXISTS_UNIX (mmap HAVE_MMAP) -CHECK_FUNCTION_EXISTS_UNIX (mmap64 HAVE_MMAP64) +CHECK_FUNCTION_EXISTS (mlock HAVE_MLOCK) +CHECK_FUNCTION_EXISTS (mlockall HAVE_MLOCKALL) +CHECK_FUNCTION_EXISTS (mmap HAVE_MMAP) +CHECK_FUNCTION_EXISTS (mmap64 HAVE_MMAP64) CHECK_FUNCTION_EXISTS (perror HAVE_PERROR) -CHECK_FUNCTION_EXISTS_UNIX (poll HAVE_POLL) -CHECK_FUNCTION_EXISTS_UNIX (port_create HAVE_PORT_CREATE) -CHECK_FUNCTION_EXISTS_UNIX (posix_fallocate HAVE_POSIX_FALLOCATE) -CHECK_FUNCTION_EXISTS_UNIX (pread HAVE_PREAD) -CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_create HAVE_PTHREAD_ATTR_CREATE) -CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_getstacksize HAVE_PTHREAD_ATTR_GETSTACKSIZE) -CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_setscope HAVE_PTHREAD_ATTR_SETSCOPE) -CHECK_FUNCTION_EXISTS_UNIX (pthread_attr_setstacksize HAVE_PTHREAD_ATTR_SETSTACKSIZE) -CHECK_FUNCTION_EXISTS_UNIX (pthread_condattr_create HAVE_PTHREAD_CONDATTR_CREATE) -CHECK_FUNCTION_EXISTS_UNIX (pthread_condattr_setclock HAVE_PTHREAD_CONDATTR_SETCLOCK) -CHECK_FUNCTION_EXISTS_UNIX (pthread_init HAVE_PTHREAD_INIT) -CHECK_FUNCTION_EXISTS_UNIX (pthread_key_delete HAVE_PTHREAD_KEY_DELETE) -CHECK_FUNCTION_EXISTS_UNIX (pthread_rwlock_rdlock HAVE_PTHREAD_RWLOCK_RDLOCK) -CHECK_FUNCTION_EXISTS_UNIX (pthread_sigmask HAVE_PTHREAD_SIGMASK) -CHECK_FUNCTION_EXISTS_UNIX (pthread_threadmask HAVE_PTHREAD_THREADMASK) -CHECK_FUNCTION_EXISTS_UNIX (pthread_yield_np HAVE_PTHREAD_YIELD_NP) +CHECK_FUNCTION_EXISTS (poll HAVE_POLL) +CHECK_FUNCTION_EXISTS (port_create HAVE_PORT_CREATE) +CHECK_FUNCTION_EXISTS (posix_fallocate HAVE_POSIX_FALLOCATE) +CHECK_FUNCTION_EXISTS (pread HAVE_PREAD) +CHECK_FUNCTION_EXISTS (pthread_attr_create HAVE_PTHREAD_ATTR_CREATE) +CHECK_FUNCTION_EXISTS (pthread_attr_getstacksize HAVE_PTHREAD_ATTR_GETSTACKSIZE) +CHECK_FUNCTION_EXISTS (pthread_attr_setscope HAVE_PTHREAD_ATTR_SETSCOPE) +CHECK_FUNCTION_EXISTS (pthread_attr_setstacksize HAVE_PTHREAD_ATTR_SETSTACKSIZE) +CHECK_FUNCTION_EXISTS (pthread_condattr_create HAVE_PTHREAD_CONDATTR_CREATE) +CHECK_FUNCTION_EXISTS (pthread_condattr_setclock HAVE_PTHREAD_CONDATTR_SETCLOCK) +CHECK_FUNCTION_EXISTS (pthread_init HAVE_PTHREAD_INIT) +CHECK_FUNCTION_EXISTS (pthread_key_delete HAVE_PTHREAD_KEY_DELETE) +CHECK_FUNCTION_EXISTS (pthread_rwlock_rdlock HAVE_PTHREAD_RWLOCK_RDLOCK) +CHECK_FUNCTION_EXISTS (pthread_sigmask HAVE_PTHREAD_SIGMASK) +CHECK_FUNCTION_EXISTS (pthread_threadmask HAVE_PTHREAD_THREADMASK) +CHECK_FUNCTION_EXISTS (pthread_yield_np HAVE_PTHREAD_YIELD_NP) CHECK_FUNCTION_EXISTS (putenv HAVE_PUTENV) -CHECK_FUNCTION_EXISTS_UNIX (readdir_r HAVE_READDIR_R) -CHECK_FUNCTION_EXISTS_UNIX (readlink HAVE_READLINK) -CHECK_FUNCTION_EXISTS_UNIX (re_comp HAVE_RE_COMP) -CHECK_FUNCTION_EXISTS_UNIX (regcomp HAVE_REGCOMP) -CHECK_FUNCTION_EXISTS_UNIX (realpath HAVE_REALPATH) +CHECK_FUNCTION_EXISTS (readdir_r HAVE_READDIR_R) +CHECK_FUNCTION_EXISTS (readlink HAVE_READLINK) +CHECK_FUNCTION_EXISTS (re_comp HAVE_RE_COMP) +CHECK_FUNCTION_EXISTS (regcomp HAVE_REGCOMP) +CHECK_FUNCTION_EXISTS (realpath HAVE_REALPATH) CHECK_FUNCTION_EXISTS (rename HAVE_RENAME) -CHECK_FUNCTION_EXISTS_UNIX (rwlock_init HAVE_RWLOCK_INIT) -CHECK_FUNCTION_EXISTS_UNIX (sched_yield HAVE_SCHED_YIELD) -CHECK_FUNCTION_EXISTS_UNIX (setenv HAVE_SETENV) +CHECK_FUNCTION_EXISTS (rwlock_init HAVE_RWLOCK_INIT) +CHECK_FUNCTION_EXISTS (sched_yield HAVE_SCHED_YIELD) +CHECK_FUNCTION_EXISTS (setenv HAVE_SETENV) CHECK_FUNCTION_EXISTS (setlocale HAVE_SETLOCALE) -CHECK_FUNCTION_EXISTS_UNIX (setfd HAVE_SETFD) -CHECK_FUNCTION_EXISTS_UNIX (sigaction HAVE_SIGACTION) -CHECK_FUNCTION_EXISTS_UNIX (sigthreadmask HAVE_SIGTHREADMASK) -CHECK_FUNCTION_EXISTS_UNIX (sigwait HAVE_SIGWAIT) -CHECK_FUNCTION_EXISTS_UNIX (sigaddset HAVE_SIGADDSET) -CHECK_FUNCTION_EXISTS_UNIX (sigemptyset HAVE_SIGEMPTYSET) -CHECK_FUNCTION_EXISTS_UNIX (sighold HAVE_SIGHOLD) -CHECK_FUNCTION_EXISTS_UNIX (sigset HAVE_SIGSET) -CHECK_FUNCTION_EXISTS_UNIX (sleep HAVE_SLEEP) +CHECK_FUNCTION_EXISTS (setfd HAVE_SETFD) +CHECK_FUNCTION_EXISTS (sigaction HAVE_SIGACTION) +CHECK_FUNCTION_EXISTS (sigthreadmask HAVE_SIGTHREADMASK) +CHECK_FUNCTION_EXISTS (sigwait HAVE_SIGWAIT) +CHECK_FUNCTION_EXISTS (sigaddset HAVE_SIGADDSET) +CHECK_FUNCTION_EXISTS (sigemptyset HAVE_SIGEMPTYSET) +CHECK_FUNCTION_EXISTS (sighold HAVE_SIGHOLD) +CHECK_FUNCTION_EXISTS (sigset HAVE_SIGSET) +CHECK_FUNCTION_EXISTS (sleep HAVE_SLEEP) CHECK_FUNCTION_EXISTS (snprintf HAVE_SNPRINTF) -CHECK_FUNCTION_EXISTS_UNIX (stpcpy HAVE_STPCPY) +CHECK_FUNCTION_EXISTS (stpcpy HAVE_STPCPY) CHECK_FUNCTION_EXISTS (strcoll HAVE_STRCOLL) CHECK_FUNCTION_EXISTS (strerror HAVE_STRERROR) -CHECK_FUNCTION_EXISTS_UNIX (strlcpy HAVE_STRLCPY) +CHECK_FUNCTION_EXISTS (strlcpy HAVE_STRLCPY) CHECK_FUNCTION_EXISTS (strnlen HAVE_STRNLEN) -CHECK_FUNCTION_EXISTS_UNIX (strlcat HAVE_STRLCAT) -CHECK_FUNCTION_EXISTS_UNIX (strsignal HAVE_STRSIGNAL) -CHECK_FUNCTION_EXISTS_UNIX (fgetln HAVE_FGETLN) +CHECK_FUNCTION_EXISTS (strlcat HAVE_STRLCAT) +CHECK_FUNCTION_EXISTS (strsignal HAVE_STRSIGNAL) +CHECK_FUNCTION_EXISTS (fgetln HAVE_FGETLN) CHECK_FUNCTION_EXISTS (strpbrk HAVE_STRPBRK) CHECK_FUNCTION_EXISTS (strsep HAVE_STRSEP) CHECK_FUNCTION_EXISTS (strstr HAVE_STRSTR) -CHECK_FUNCTION_EXISTS_UNIX (strtok_r HAVE_STRTOK_R) +CHECK_FUNCTION_EXISTS (strtok_r HAVE_STRTOK_R) CHECK_FUNCTION_EXISTS (strtol HAVE_STRTOL) CHECK_FUNCTION_EXISTS (strtoll HAVE_STRTOLL) CHECK_FUNCTION_EXISTS (strtoul HAVE_STRTOUL) @@ -385,46 +385,46 @@ CHECK_FUNCTION_EXISTS (strtoull HAVE_STRTOULL) CHECK_FUNCTION_EXISTS (strcasecmp HAVE_STRCASECMP) CHECK_FUNCTION_EXISTS (strncasecmp HAVE_STRNCASECMP) CHECK_FUNCTION_EXISTS (strdup HAVE_STRDUP) -CHECK_FUNCTION_EXISTS_UNIX (shmat HAVE_SHMAT) -CHECK_FUNCTION_EXISTS_UNIX (shmctl HAVE_SHMCTL) -CHECK_FUNCTION_EXISTS_UNIX (shmdt HAVE_SHMDT) -CHECK_FUNCTION_EXISTS_UNIX (shmget HAVE_SHMGET) +CHECK_FUNCTION_EXISTS (shmat HAVE_SHMAT) +CHECK_FUNCTION_EXISTS (shmctl HAVE_SHMCTL) +CHECK_FUNCTION_EXISTS (shmdt HAVE_SHMDT) +CHECK_FUNCTION_EXISTS (shmget HAVE_SHMGET) CHECK_FUNCTION_EXISTS (tell HAVE_TELL) CHECK_FUNCTION_EXISTS (tempnam HAVE_TEMPNAM) -CHECK_FUNCTION_EXISTS_UNIX (thr_setconcurrency HAVE_THR_SETCONCURRENCY) -CHECK_FUNCTION_EXISTS_UNIX (thr_yield HAVE_THR_YIELD) -CHECK_FUNCTION_EXISTS_UNIX (vasprintf HAVE_VASPRINTF) +CHECK_FUNCTION_EXISTS (thr_setconcurrency HAVE_THR_SETCONCURRENCY) +CHECK_FUNCTION_EXISTS (thr_yield HAVE_THR_YIELD) +CHECK_FUNCTION_EXISTS (vasprintf HAVE_VASPRINTF) CHECK_FUNCTION_EXISTS (vsnprintf HAVE_VSNPRINTF) -CHECK_FUNCTION_EXISTS_UNIX (vprintf HAVE_VPRINTF) -CHECK_FUNCTION_EXISTS_UNIX (valloc HAVE_VALLOC) -CHECK_FUNCTION_EXISTS_UNIX (memalign HAVE_MEMALIGN) -CHECK_FUNCTION_EXISTS_UNIX (chown HAVE_CHOWN) -CHECK_FUNCTION_EXISTS_UNIX (nl_langinfo HAVE_NL_LANGINFO) +CHECK_FUNCTION_EXISTS (vprintf HAVE_VPRINTF) +CHECK_FUNCTION_EXISTS (valloc HAVE_VALLOC) +CHECK_FUNCTION_EXISTS (memalign HAVE_MEMALIGN) +CHECK_FUNCTION_EXISTS (chown HAVE_CHOWN) +CHECK_FUNCTION_EXISTS (nl_langinfo HAVE_NL_LANGINFO) #-------------------------------------------------------------------- # Support for WL#2373 (Use cycle counter for timing) #-------------------------------------------------------------------- -CHECK_INCLUDE_FILES_UNIX(time.h HAVE_TIME_H) -CHECK_INCLUDE_FILES_UNIX(sys/time.h HAVE_SYS_TIME_H) -CHECK_INCLUDE_FILES_UNIX(sys/times.h HAVE_SYS_TIMES_H) -CHECK_INCLUDE_FILES_UNIX(asm/msr.h HAVE_ASM_MSR_H) +CHECK_INCLUDE_FILES(time.h HAVE_TIME_H) +CHECK_INCLUDE_FILES(sys/time.h HAVE_SYS_TIME_H) +CHECK_INCLUDE_FILES(sys/times.h HAVE_SYS_TIMES_H) +CHECK_INCLUDE_FILES(asm/msr.h HAVE_ASM_MSR_H) #msr.h has rdtscll() -CHECK_INCLUDE_FILES_UNIX(ia64intrin.h HAVE_IA64INTRIN_H) +CHECK_INCLUDE_FILES(ia64intrin.h HAVE_IA64INTRIN_H) -CHECK_FUNCTION_EXISTS_UNIX(times HAVE_TIMES) -CHECK_FUNCTION_EXISTS_UNIX(gettimeofday HAVE_GETTIMEOFDAY) -CHECK_FUNCTION_EXISTS_UNIX(read_real_time HAVE_READ_REAL_TIME) +CHECK_FUNCTION_EXISTS(times HAVE_TIMES) +CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) +CHECK_FUNCTION_EXISTS(read_real_time HAVE_READ_REAL_TIME) # This should work on AIX. -CHECK_FUNCTION_EXISTS_UNIX(ftime HAVE_FTIME) +CHECK_FUNCTION_EXISTS(ftime HAVE_FTIME) # This is still a normal call for milliseconds. -CHECK_FUNCTION_EXISTS_UNIX(time HAVE_TIME) +CHECK_FUNCTION_EXISTS(time HAVE_TIME) # We can use time() on Macintosh if there is no ftime(). -CHECK_FUNCTION_EXISTS_UNIX(rdtscll HAVE_RDTSCLL) +CHECK_FUNCTION_EXISTS(rdtscll HAVE_RDTSCLL) # I doubt that we'll ever reach the check for this. @@ -432,14 +432,14 @@ CHECK_FUNCTION_EXISTS_UNIX(rdtscll HAVE_RDTSCLL) # Tests for symbols # -CHECK_SYMBOL_EXISTS_UNIX(sys_errlist "stdio.h" HAVE_SYS_ERRLIST) -CHECK_SYMBOL_EXISTS_UNIX(madvise "sys/mman.h" HAVE_DECL_MADVISE) +CHECK_SYMBOL_EXISTS(sys_errlist "stdio.h" HAVE_SYS_ERRLIST) +CHECK_SYMBOL_EXISTS(madvise "sys/mman.h" HAVE_DECL_MADVISE) CHECK_SYMBOL_EXISTS(tzname "time.h" HAVE_TZNAME) -CHECK_SYMBOL_EXISTS_UNIX(lrand48 "stdlib.h" HAVE_LRAND48) -CHECK_SYMBOL_EXISTS_UNIX(getpagesize "unistd.h" HAVE_GETPAGESIZE) -CHECK_SYMBOL_EXISTS_UNIX(TIOCGWINSZ "sys/ioctl.h" GWINSZ_IN_SYS_IOCTL) -CHECK_SYMBOL_EXISTS_UNIX(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL) -CHECK_SYMBOL_EXISTS_UNIX(TIOCSTAT "sys/ioctl.h" TIOCSTAT_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS(lrand48 "stdlib.h" HAVE_LRAND48) +CHECK_SYMBOL_EXISTS(getpagesize "unistd.h" HAVE_GETPAGESIZE) +CHECK_SYMBOL_EXISTS(TIOCGWINSZ "sys/ioctl.h" GWINSZ_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS(TIOCSTAT "sys/ioctl.h" TIOCSTAT_IN_SYS_IOCTL) CHECK_SYMBOL_EXISTS(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY) CHECK_SYMBOL_EXISTS(finite "math.h" HAVE_FINITE_IN_MATH_H) @@ -655,7 +655,7 @@ ELSE() ENDIF() ENDIF() -CHECK_CXX_SOURCE_COMPILES_UNIX(" +CHECK_CXX_SOURCE_COMPILES(" #include int main() { @@ -707,8 +707,8 @@ ELSE(SIGNAL_RETURN_TYPE_IS_VOID) ENDIF(SIGNAL_RETURN_TYPE_IS_VOID) -CHECK_INCLUDE_FILES_UNIX("time.h;sys/time.h" TIME_WITH_SYS_TIME) -CHECK_SYMBOL_EXISTS_UNIX(O_NONBLOCK "unistd.h;fcntl.h" HAVE_FCNTL_NONBLOCK) +CHECK_INCLUDE_FILES("time.h;sys/time.h" TIME_WITH_SYS_TIME) +CHECK_SYMBOL_EXISTS(O_NONBLOCK "unistd.h;fcntl.h" HAVE_FCNTL_NONBLOCK) IF(NOT HAVE_FCNTL_NONBLOCK) SET(NO_FCNTL_NONBLOCK 1) ENDIF() @@ -754,8 +754,8 @@ IF(NOT CMAKE_CROSSCOMPILING AND NOT MSVC) ENDIF() ENDIF() -CHECK_SYMBOL_EXISTS_UNIX(tcgetattr "termios.h" HAVE_TCGETATTR 1) -CHECK_INCLUDE_FILES_UNIX(sys/ioctl.h HAVE_SYS_IOCTL 1) +CHECK_SYMBOL_EXISTS(tcgetattr "termios.h" HAVE_TCGETATTR 1) +CHECK_INCLUDE_FILES(sys/ioctl.h HAVE_SYS_IOCTL 1) # # Check type of signal routines (posix, 4.2bsd, 4.1bsd or v7) @@ -844,7 +844,7 @@ CHECK_CXX_SOURCE_COMPILES(" HAVE_CXX_NEW ) -CHECK_CXX_SOURCE_COMPILES_UNIX(" +CHECK_CXX_SOURCE_COMPILES(" #undef inline #if !defined(SCO) && !defined(__osf__) && !defined(_REENTRANT) #define _REENTRANT @@ -866,7 +866,7 @@ CHECK_CXX_SOURCE_COMPILES_UNIX(" " HAVE_SOLARIS_STYLE_GETHOST) -CHECK_CXX_SOURCE_COMPILES_UNIX(" +CHECK_CXX_SOURCE_COMPILES(" #undef inline #if !defined(SCO) && !defined(__osf__) && !defined(_REENTRANT) #define _REENTRANT @@ -885,7 +885,7 @@ CHECK_CXX_SOURCE_COMPILES_UNIX(" }" HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE) -CHECK_CXX_SOURCE_COMPILES_UNIX(" +CHECK_CXX_SOURCE_COMPILES(" #undef inline #if !defined(SCO) && !defined(__osf__) && !defined(_REENTRANT) #define _REENTRANT From 66e1bc46da60bcb94588e91326766ca806e2ac13 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 29 Jan 2010 03:29:33 +0100 Subject: [PATCH 323/466] Do not run checks for strtok_r on Windows - we know the function is not there.. --- cmake/os/WindowsCache.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/os/WindowsCache.cmake b/cmake/os/WindowsCache.cmake index 8cf84e9d2e7..accc6d7c86e 100644 --- a/cmake/os/WindowsCache.cmake +++ b/cmake/os/WindowsCache.cmake @@ -249,6 +249,7 @@ SET(HAVE_STRPBRK 1 CACHE INTERNAL "") SET(HAVE_STRSEP CACHE INTERNAL "") SET(HAVE_STRSIGNAL CACHE INTERNAL "") SET(HAVE_STRSTR 1 CACHE INTERNAL "") +SET(HAVE_STRTOK_R CACHE INTERNAL "") SET(HAVE_STRTOL 1 CACHE INTERNAL "") SET(HAVE_STRTOLL CACHE INTERNAL "") SET(HAVE_STRTOUL 1 CACHE INTERNAL "") From 513027f4fd79fe0fac5ab712b857b13075de9293 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 29 Jan 2010 03:49:13 +0100 Subject: [PATCH 324/466] Fix syntax errors in configure.cmake --- configure.cmake | 47 ++++++----------------------------------------- 1 file changed, 6 insertions(+), 41 deletions(-) diff --git a/configure.cmake b/configure.cmake index 9a1fdfa7942..7374575dfdc 100644 --- a/configure.cmake +++ b/configure.cmake @@ -149,41 +149,6 @@ IF(UNIX) ENDIF() ENDIF() -# System check macros that do nothing on Windows. -# Very often, it is known that some function is not available -# on Windows. In such cases it makes sense to use these macros -# as build with Visual Studio is considerably faster if irrelevant -# checks are omitted. -MACRO(CHECK_INCLUDE_FILES INCLUDES VAR) -IF(UNIX) - CHECK_INCLUDE_FILES ("${INCLUDES}" ${VAR}) -ENDIF() -ENDMACRO() - -MACRO(CHECK_C_SOURCE_COMPILES_UNIX SRC VAR) -IF(UNIX) - CHECK_C_SOURCE_COMPILES("${SRC}" ${VAR}) -ENDIF() -ENDMACRO() - -MACRO(CHECK_CXX_SOURCE_COMPILES SRC VAR) -IF(UNIX) - CHECK_CXX_SOURCE_COMPILES("${SRC}" ${VAR}) -ENDIF() -ENDMACRO() - -MACRO(CHECK_FUNCTION_EXISTS FUNC VAR) -IF(UNIX) - CHECK_FUNCTION_EXISTS("${FUNC}" ${VAR}) -ENDIF() -ENDMACRO() - -MACRO (CHECK_SYMBOL_EXISTS SYM HEADER VAR) -IF(UNIX) - CHECK_SYMBOL_EXISTS("${SYM}" "${HEADER}" ${VAR}) -ENDIF() -ENDMACRO() - # # Tests for header files # @@ -582,7 +547,7 @@ HAVE_SELECT) # Check if timespec has ts_sec and ts_nsec fields # -CHECK_C_SOURCE_COMPILES_UNIX(" +CHECK_C_SOURCE_COMPILES(" #include int main(int ac, char **av) @@ -760,7 +725,7 @@ CHECK_INCLUDE_FILES(sys/ioctl.h HAVE_SYS_IOCTL 1) # # Check type of signal routines (posix, 4.2bsd, 4.1bsd or v7) # -CHECK_C_SOURCE_COMPILES_UNIX(" +CHECK_C_SOURCE_COMPILES(" #include int main(int ac, char **av) { @@ -773,7 +738,7 @@ CHECK_C_SOURCE_COMPILES_UNIX(" HAVE_POSIX_SIGNALS) IF(NOT HAVE_POSIX_SIGNALS) - CHECK_C_SOURCE_COMPILES_UNIX(" + CHECK_C_SOURCE_COMPILES(" #include int main(int ac, char **av) { @@ -782,7 +747,7 @@ IF(NOT HAVE_POSIX_SIGNALS) }" HAVE_BSD_SIGNALS) IF (NOT HAVE_BSD_SIGNALS) - CHECK_C_SOURCE_COMPILES_UNIX(" + CHECK_C_SOURCE_COMPILES(" #include void foo() { } int main(int ac, char **av) @@ -816,7 +781,7 @@ IF(HAVE_ABI_CXA_DEMANGLE) ENDIF() ENDIF() -CHECK_C_SOURCE_COMPILES_UNIX(" +CHECK_C_SOURCE_COMPILES(" int main(int argc, char **argv) { extern char *__bss_start; @@ -824,7 +789,7 @@ CHECK_C_SOURCE_COMPILES_UNIX(" }" HAVE_BSS_START) -CHECK_C_SOURCE_COMPILES_UNIX(" +CHECK_C_SOURCE_COMPILES(" int main() { extern void __attribute__((weak)) foo(void); From c4f58dcbe5f34332d37ba898bcd097e935384d59 Mon Sep 17 00:00:00 2001 From: Omer BarNir Date: Thu, 28 Jan 2010 22:33:00 -0800 Subject: [PATCH 325/466] Modified and added tests following review of WL#4738. - Added tests for innodb and semisync plugin - Modified existing tests to include variable values in I_S tables - Updated the all_vars test to include optional checkes for INNODB and semisync plugin if loaded --- .../r/innodb_adaptive_flushing_basic.result | 92 +++++++++++++ .../r/innodb_adaptive_hash_index_basic.result | 92 +++++++++++++ .../r/innodb_change_buffering_basic.result | 63 +++++++++ .../r/innodb_file_format_basic.result | 59 ++++++++ .../r/innodb_file_format_check_basic.result | 59 ++++++++ .../r/innodb_io_capacity_basic.result | 69 ++++++++++ .../r/innodb_old_blocks_pct_basic.result | 82 +++++++++++ .../r/innodb_old_blocks_time_basic.result | 56 ++++++++ .../innodb_read_ahead_threshold_basic.result | 73 ++++++++++ .../r/innodb_read_io_threads_basic.result | 21 +++ .../r/innodb_replication_delay_basic.result | 56 ++++++++ .../r/innodb_spin_wait_delay_basic.result | 56 ++++++++ .../r/innodb_stats_on_metadata_basic.result | 92 +++++++++++++ .../r/innodb_stats_sample_pages_basic.result | 56 ++++++++ .../r/innodb_strict_mode_basic.result | 120 ++++++++++++++++ .../r/innodb_thread_sleep_delay_basic.result | 130 +++++++----------- .../r/innodb_use_sys_malloc_basic.result | 25 ++++ .../sys_vars/r/innodb_version_basic.result | 17 +++ .../r/innodb_write_io_threads_basic.result | 21 +++ .../sys_vars/r/last_insert_id_basic.result | 20 ++- .../suite/sys_vars/r/lc_messages_basic.result | 8 +- .../sys_vars/r/log_slow_queries_basic.result | 20 +++ .../r/lower_case_file_system_basic.result | 25 ++-- .../r/lower_case_table_names_basic.result | 25 ++-- .../sys_vars/r/max_join_size_basic.result | 14 +- .../sys_vars/r/old_alter_table_basic.result | 14 +- .../sys_vars/r/optimizer_switch_basic.result | 16 ++- .../suite/sys_vars/r/profiling_basic.result | 34 ++++- .../r/profiling_history_size_basic.result | 14 +- .../sys_vars/r/pseudo_thread_id_basic.result | 23 ++-- .../suite/sys_vars/r/rand_seed1_basic.result | 5 + .../suite/sys_vars/r/rand_seed2_basic.result | 5 + .../r/relay_log_recovery_basic.result | 12 ++ .../rpl_semi_sync_master_enabled_basic.result | 73 ++++++++++ .../rpl_semi_sync_master_timeout_basic.result | 54 ++++++++ ..._semi_sync_master_trace_level_basic.result | 72 ++++++++++ ...emi_sync_master_wait_no_slave_basic.result | 73 ++++++++++ .../rpl_semi_sync_slave_enabled_basic.result | 73 ++++++++++ ...l_semi_sync_slave_trace_level_basic.result | 72 ++++++++++ .../sys_vars/r/sql_log_update_basic.result | 42 +++++- .../sys_vars/r/sql_max_join_size_basic.result | 18 ++- .../sys_vars/r/sql_select_limit_basic.result | 14 +- .../sys_vars/r/thread_cache_size_basic.result | 6 + .../suite/sys_vars/t/all_vars-master.opt | 1 + mysql-test/suite/sys_vars/t/all_vars.test | 25 ++++ .../t/innodb_adaptive_flushing_basic.test | 70 ++++++++++ .../t/innodb_adaptive_hash_index_basic.test | 70 ++++++++++ .../t/innodb_change_buffering_basic.test | 59 ++++++++ .../sys_vars/t/innodb_file_format_basic.test | 55 ++++++++ .../t/innodb_file_format_check_basic.test | 55 ++++++++ .../sys_vars/t/innodb_io_capacity_basic.test | 58 ++++++++ .../t/innodb_old_blocks_pct_basic.test | 63 +++++++++ .../t/innodb_old_blocks_time_basic.test | 52 +++++++ .../t/innodb_read_ahead_threshold_basic.test | 60 ++++++++ .../t/innodb_read_io_threads_basic.test | 26 ++++ .../t/innodb_replication_delay_basic.test | 52 +++++++ .../t/innodb_spin_wait_delay_basic.test | 52 +++++++ .../t/innodb_stats_on_metadata_basic.test | 70 ++++++++++ .../t/innodb_stats_sample_pages_basic.test | 52 +++++++ .../sys_vars/t/innodb_strict_mode_basic.test | 84 +++++++++++ .../t/innodb_thread_sleep_delay_basic.test | 52 +++++++ .../t/innodb_use_sys_malloc_basic.test | 28 ++++ .../sys_vars/t/innodb_version_basic.test | 28 ++++ .../t/innodb_write_io_threads_basic.test | 26 ++++ .../sys_vars/t/last_insert_id_basic.test | 14 +- .../suite/sys_vars/t/lc_messages_basic.test | 9 +- .../sys_vars/t/log_slow_queries_basic.test | 18 ++- .../t/lower_case_file_system_basic.test | 24 ++-- .../t/lower_case_table_names_basic.test | 25 ++-- .../suite/sys_vars/t/max_join_size_basic.test | 14 +- .../sys_vars/t/old_alter_table_basic.test | 11 +- .../sys_vars/t/optimizer_switch_basic.test | 13 +- .../suite/sys_vars/t/profiling_basic.test | 19 ++- .../t/profiling_history_size_basic.test | 11 +- .../sys_vars/t/pseudo_thread_id_basic.test | 24 +++- .../suite/sys_vars/t/rand_seed1_basic.test | 6 +- .../suite/sys_vars/t/rand_seed2_basic.test | 7 +- .../sys_vars/t/relay_log_recovery_basic.test | 10 ++ ..._semi_sync_master_enabled_basic-master.opt | 1 + .../t/rpl_semi_sync_master_enabled_basic.test | 62 +++++++++ ..._semi_sync_master_timeout_basic-master.opt | 1 + .../t/rpl_semi_sync_master_timeout_basic.test | 52 +++++++ ...i_sync_master_trace_level_basic-master.opt | 1 + ...pl_semi_sync_master_trace_level_basic.test | 60 ++++++++ ...sync_master_wait_no_slave_basic-master.opt | 1 + ..._semi_sync_master_wait_no_slave_basic.test | 62 +++++++++ ...l_semi_sync_slave_enabled_basic-master.opt | 1 + .../t/rpl_semi_sync_slave_enabled_basic.test | 63 +++++++++ ...mi_sync_slave_trace_level_basic-master.opt | 1 + ...rpl_semi_sync_slave_trace_level_basic.test | 60 ++++++++ .../sys_vars/t/sql_log_update_basic.test | 20 ++- .../sys_vars/t/sql_max_join_size_basic.test | 11 +- .../sys_vars/t/sql_select_limit_basic.test | 11 +- .../sys_vars/t/thread_cache_size_basic.test | 6 + 94 files changed, 3462 insertions(+), 165 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/innodb_adaptive_flushing_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_file_format_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_io_capacity_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_old_blocks_pct_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_old_blocks_time_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_read_ahead_threshold_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_read_io_threads_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_replication_delay_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_spin_wait_delay_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_stats_on_metadata_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_stats_sample_pages_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_strict_mode_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_use_sys_malloc_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_version_basic.result create mode 100644 mysql-test/suite/sys_vars/r/innodb_write_io_threads_basic.result create mode 100644 mysql-test/suite/sys_vars/r/rpl_semi_sync_master_enabled_basic.result create mode 100644 mysql-test/suite/sys_vars/r/rpl_semi_sync_master_timeout_basic.result create mode 100644 mysql-test/suite/sys_vars/r/rpl_semi_sync_master_trace_level_basic.result create mode 100644 mysql-test/suite/sys_vars/r/rpl_semi_sync_master_wait_no_slave_basic.result create mode 100644 mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_enabled_basic.result create mode 100644 mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_trace_level_basic.result create mode 100644 mysql-test/suite/sys_vars/t/all_vars-master.opt create mode 100644 mysql-test/suite/sys_vars/t/innodb_adaptive_flushing_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_change_buffering_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_file_format_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_io_capacity_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_old_blocks_pct_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_old_blocks_time_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_read_ahead_threshold_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_read_io_threads_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_replication_delay_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_spin_wait_delay_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_stats_on_metadata_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_stats_sample_pages_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_strict_mode_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_thread_sleep_delay_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_use_sys_malloc_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_version_basic.test create mode 100644 mysql-test/suite/sys_vars/t/innodb_write_io_threads_basic.test create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic-master.opt create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic.test create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic-master.opt create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic.test create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic-master.opt create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic.test create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic-master.opt create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic.test create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic-master.opt create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic.test create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic-master.opt create mode 100644 mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic.test diff --git a/mysql-test/suite/sys_vars/r/innodb_adaptive_flushing_basic.result b/mysql-test/suite/sys_vars/r/innodb_adaptive_flushing_basic.result new file mode 100644 index 00000000000..418a693d319 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_adaptive_flushing_basic.result @@ -0,0 +1,92 @@ +SET @start_global_value = @@global.innodb_adaptive_flushing; +SELECT @start_global_value; +@start_global_value +1 +Valid values are 'ON' and 'OFF' +select @@global.innodb_adaptive_flushing in (0, 1); +@@global.innodb_adaptive_flushing in (0, 1) +1 +select @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +1 +select @@session.innodb_adaptive_flushing; +ERROR HY000: Variable 'innodb_adaptive_flushing' is a GLOBAL variable +show global variables like 'innodb_adaptive_flushing'; +Variable_name Value +innodb_adaptive_flushing ON +show session variables like 'innodb_adaptive_flushing'; +Variable_name Value +innodb_adaptive_flushing ON +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +set global innodb_adaptive_flushing='OFF'; +select @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +0 +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING OFF +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING OFF +set @@global.innodb_adaptive_flushing=1; +select @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +1 +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +set global innodb_adaptive_flushing=0; +select @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +0 +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING OFF +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING OFF +set @@global.innodb_adaptive_flushing='ON'; +select @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +1 +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +set session innodb_adaptive_flushing='OFF'; +ERROR HY000: Variable 'innodb_adaptive_flushing' is a GLOBAL variable and should be set with SET GLOBAL +set @@session.innodb_adaptive_flushing='ON'; +ERROR HY000: Variable 'innodb_adaptive_flushing' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_adaptive_flushing=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_adaptive_flushing' +set global innodb_adaptive_flushing=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_adaptive_flushing' +set global innodb_adaptive_flushing=2; +ERROR 42000: Variable 'innodb_adaptive_flushing' can't be set to the value of '2' +NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_adaptive_flushing=-3; +select @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +1 +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_FLUSHING ON +set global innodb_adaptive_flushing='AUTO'; +ERROR 42000: Variable 'innodb_adaptive_flushing' can't be set to the value of 'AUTO' +SET @@global.innodb_adaptive_flushing = @start_global_value; +SELECT @@global.innodb_adaptive_flushing; +@@global.innodb_adaptive_flushing +1 diff --git a/mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_basic.result b/mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_basic.result new file mode 100644 index 00000000000..bc908090627 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_adaptive_hash_index_basic.result @@ -0,0 +1,92 @@ +SET @start_global_value = @@global.innodb_adaptive_hash_index; +SELECT @start_global_value; +@start_global_value +1 +Valid values are 'ON' and 'OFF' +select @@global.innodb_adaptive_hash_index in (0, 1); +@@global.innodb_adaptive_hash_index in (0, 1) +1 +select @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +1 +select @@session.innodb_adaptive_hash_index; +ERROR HY000: Variable 'innodb_adaptive_hash_index' is a GLOBAL variable +show global variables like 'innodb_adaptive_hash_index'; +Variable_name Value +innodb_adaptive_hash_index ON +show session variables like 'innodb_adaptive_hash_index'; +Variable_name Value +innodb_adaptive_hash_index ON +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +set global innodb_adaptive_hash_index='OFF'; +select @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +0 +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX OFF +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX OFF +set @@global.innodb_adaptive_hash_index=1; +select @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +1 +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +set global innodb_adaptive_hash_index=0; +select @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +0 +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX OFF +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX OFF +set @@global.innodb_adaptive_hash_index='ON'; +select @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +1 +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +set session innodb_adaptive_hash_index='OFF'; +ERROR HY000: Variable 'innodb_adaptive_hash_index' is a GLOBAL variable and should be set with SET GLOBAL +set @@session.innodb_adaptive_hash_index='ON'; +ERROR HY000: Variable 'innodb_adaptive_hash_index' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_adaptive_hash_index=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_adaptive_hash_index' +set global innodb_adaptive_hash_index=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_adaptive_hash_index' +set global innodb_adaptive_hash_index=2; +ERROR 42000: Variable 'innodb_adaptive_hash_index' can't be set to the value of '2' +NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_adaptive_hash_index=-3; +select @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +1 +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_ADAPTIVE_HASH_INDEX ON +set global innodb_adaptive_hash_index='AUTO'; +ERROR 42000: Variable 'innodb_adaptive_hash_index' can't be set to the value of 'AUTO' +SET @@global.innodb_adaptive_hash_index = @start_global_value; +SELECT @@global.innodb_adaptive_hash_index; +@@global.innodb_adaptive_hash_index +1 diff --git a/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result b/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result new file mode 100644 index 00000000000..70b0425ce6f --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result @@ -0,0 +1,63 @@ +SET @start_global_value = @@global.innodb_change_buffering; +SELECT @start_global_value; +@start_global_value +inserts +Valid values are 'inserts' and 'none' +select @@global.innodb_change_buffering in ('inserts', 'none'); +@@global.innodb_change_buffering in ('inserts', 'none') +1 +select @@global.innodb_change_buffering; +@@global.innodb_change_buffering +inserts +select @@session.innodb_change_buffering; +ERROR HY000: Variable 'innodb_change_buffering' is a GLOBAL variable +show global variables like 'innodb_change_buffering'; +Variable_name Value +innodb_change_buffering inserts +show session variables like 'innodb_change_buffering'; +Variable_name Value +innodb_change_buffering inserts +select * from information_schema.global_variables where variable_name='innodb_change_buffering'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_CHANGE_BUFFERING inserts +select * from information_schema.session_variables where variable_name='innodb_change_buffering'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_CHANGE_BUFFERING inserts +set global innodb_change_buffering='none'; +select @@global.innodb_change_buffering; +@@global.innodb_change_buffering +none +select * from information_schema.global_variables where variable_name='innodb_change_buffering'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_CHANGE_BUFFERING none +select * from information_schema.session_variables where variable_name='innodb_change_buffering'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_CHANGE_BUFFERING none +set @@global.innodb_change_buffering='inserts'; +select @@global.innodb_change_buffering; +@@global.innodb_change_buffering +inserts +select * from information_schema.global_variables where variable_name='innodb_change_buffering'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_CHANGE_BUFFERING inserts +select * from information_schema.session_variables where variable_name='innodb_change_buffering'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_CHANGE_BUFFERING inserts +set session innodb_change_buffering='some'; +ERROR HY000: Variable 'innodb_change_buffering' is a GLOBAL variable and should be set with SET GLOBAL +set @@session.innodb_change_buffering='some'; +ERROR HY000: Variable 'innodb_change_buffering' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_change_buffering=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_change_buffering' +set global innodb_change_buffering=1; +ERROR 42000: Incorrect argument type to variable 'innodb_change_buffering' +set global innodb_change_buffering=-2; +ERROR 42000: Incorrect argument type to variable 'innodb_change_buffering' +set global innodb_change_buffering=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_change_buffering' +set global innodb_change_buffering='some'; +ERROR 42000: Variable 'innodb_change_buffering' can't be set to the value of 'some' +SET @@global.innodb_change_buffering = @start_global_value; +SELECT @@global.innodb_change_buffering; +@@global.innodb_change_buffering +inserts diff --git a/mysql-test/suite/sys_vars/r/innodb_file_format_basic.result b/mysql-test/suite/sys_vars/r/innodb_file_format_basic.result new file mode 100644 index 00000000000..58e009ea705 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_file_format_basic.result @@ -0,0 +1,59 @@ +SET @start_global_value = @@global.innodb_file_format; +SELECT @start_global_value; +@start_global_value +Antelope +Valid values are 'Antelope' and 'Barracuda' +select @@global.innodb_file_format in ('Antelope', 'Barracuda'); +@@global.innodb_file_format in ('Antelope', 'Barracuda') +1 +select @@global.innodb_file_format; +@@global.innodb_file_format +Antelope +select @@session.innodb_file_format; +ERROR HY000: Variable 'innodb_file_format' is a GLOBAL variable +show global variables like 'innodb_file_format'; +Variable_name Value +innodb_file_format Antelope +show session variables like 'innodb_file_format'; +Variable_name Value +innodb_file_format Antelope +select * from information_schema.global_variables where variable_name='innodb_file_format'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT Antelope +select * from information_schema.session_variables where variable_name='innodb_file_format'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT Antelope +set global innodb_file_format='Antelope'; +select @@global.innodb_file_format; +@@global.innodb_file_format +Antelope +select * from information_schema.global_variables where variable_name='innodb_file_format'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT Antelope +select * from information_schema.session_variables where variable_name='innodb_file_format'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT Antelope +set @@global.innodb_file_format='Barracuda'; +select @@global.innodb_file_format; +@@global.innodb_file_format +Barracuda +select * from information_schema.global_variables where variable_name='innodb_file_format'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT Barracuda +select * from information_schema.session_variables where variable_name='innodb_file_format'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT Barracuda +set session innodb_file_format='Salmon'; +ERROR HY000: Variable 'innodb_file_format' is a GLOBAL variable and should be set with SET GLOBAL +set @@session.innodb_file_format='Salmon'; +ERROR HY000: Variable 'innodb_file_format' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_file_format=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_file_format' +set global innodb_file_format=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_file_format' +set global innodb_file_format='Salmon'; +ERROR 42000: Variable 'innodb_file_format' can't be set to the value of 'Salmon' +SET @@global.innodb_file_format = @start_global_value; +SELECT @@global.innodb_file_format; +@@global.innodb_file_format +Antelope diff --git a/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result b/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result new file mode 100644 index 00000000000..29be30cf096 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_file_format_check_basic.result @@ -0,0 +1,59 @@ +SET @start_global_value = @@global.innodb_file_format_check; +SELECT @start_global_value; +@start_global_value +Antelope +Valid values are 'Antelope' and 'Barracuda' +select @@global.innodb_file_format_check in ('Antelope', 'Barracuda'); +@@global.innodb_file_format_check in ('Antelope', 'Barracuda') +1 +select @@global.innodb_file_format_check; +@@global.innodb_file_format_check +Antelope +select @@session.innodb_file_format_check; +ERROR HY000: Variable 'innodb_file_format_check' is a GLOBAL variable +show global variables like 'innodb_file_format_check'; +Variable_name Value +innodb_file_format_check Antelope +show session variables like 'innodb_file_format_check'; +Variable_name Value +innodb_file_format_check Antelope +select * from information_schema.global_variables where variable_name='innodb_file_format_check'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_CHECK Antelope +select * from information_schema.session_variables where variable_name='innodb_file_format_check'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_CHECK Antelope +set global innodb_file_format_check='Antelope'; +select @@global.innodb_file_format_check; +@@global.innodb_file_format_check +Antelope +select * from information_schema.global_variables where variable_name='innodb_file_format_check'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_CHECK Antelope +select * from information_schema.session_variables where variable_name='innodb_file_format_check'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_CHECK Antelope +set @@global.innodb_file_format_check='Barracuda'; +select @@global.innodb_file_format_check; +@@global.innodb_file_format_check +Barracuda +select * from information_schema.global_variables where variable_name='innodb_file_format_check'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_CHECK Barracuda +select * from information_schema.session_variables where variable_name='innodb_file_format_check'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_FILE_FORMAT_CHECK Barracuda +set session innodb_file_format_check='Salmon'; +ERROR HY000: Variable 'innodb_file_format_check' is a GLOBAL variable and should be set with SET GLOBAL +set @@session.innodb_file_format_check='Salmon'; +ERROR HY000: Variable 'innodb_file_format_check' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_file_format_check=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_file_format_check' +set global innodb_file_format_check=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_file_format_check' +set global innodb_file_format_check='Salmon'; +ERROR 42000: Variable 'innodb_file_format_check' can't be set to the value of 'Salmon' +SET @@global.innodb_file_format_check = @start_global_value; +SELECT @@global.innodb_file_format_check; +@@global.innodb_file_format_check +Antelope diff --git a/mysql-test/suite/sys_vars/r/innodb_io_capacity_basic.result b/mysql-test/suite/sys_vars/r/innodb_io_capacity_basic.result new file mode 100644 index 00000000000..25058bc6f09 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_io_capacity_basic.result @@ -0,0 +1,69 @@ +SET @start_global_value = @@global.innodb_io_capacity; +SELECT @start_global_value; +@start_global_value +200 +Valid value 100 or more +select @@global.innodb_io_capacity > 99; +@@global.innodb_io_capacity > 99 +1 +select @@global.innodb_io_capacity; +@@global.innodb_io_capacity +200 +select @@session.innodb_io_capacity; +ERROR HY000: Variable 'innodb_io_capacity' is a GLOBAL variable +show global variables like 'innodb_io_capacity'; +Variable_name Value +innodb_io_capacity 200 +show session variables like 'innodb_io_capacity'; +Variable_name Value +innodb_io_capacity 200 +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_IO_CAPACITY 200 +select * from information_schema.session_variables where variable_name='innodb_io_capacity'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_IO_CAPACITY 200 +set global innodb_io_capacity=123; +select @@global.innodb_io_capacity; +@@global.innodb_io_capacity +123 +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_IO_CAPACITY 123 +select * from information_schema.session_variables where variable_name='innodb_io_capacity'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_IO_CAPACITY 123 +set session innodb_io_capacity=444; +ERROR HY000: Variable 'innodb_io_capacity' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_io_capacity=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_io_capacity' +set global innodb_io_capacity=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_io_capacity' +set global innodb_io_capacity="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_io_capacity' +set global innodb_io_capacity=7; +Warnings: +Warning 1292 Truncated incorrect innodb_io_capacity value: '7' +select @@global.innodb_io_capacity; +@@global.innodb_io_capacity +100 +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_IO_CAPACITY 100 +set global innodb_io_capacity=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_io_capacity value: '-7' +select @@global.innodb_io_capacity; +@@global.innodb_io_capacity +100 +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_IO_CAPACITY 100 +set global innodb_io_capacity=100; +select @@global.innodb_io_capacity; +@@global.innodb_io_capacity +100 +SET @@global.innodb_io_capacity = @start_global_value; +SELECT @@global.innodb_io_capacity; +@@global.innodb_io_capacity +200 diff --git a/mysql-test/suite/sys_vars/r/innodb_old_blocks_pct_basic.result b/mysql-test/suite/sys_vars/r/innodb_old_blocks_pct_basic.result new file mode 100644 index 00000000000..bbcc2dabb22 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_old_blocks_pct_basic.result @@ -0,0 +1,82 @@ +SET @start_global_value = @@global.innodb_old_blocks_pct; +SELECT @start_global_value; +@start_global_value +37 +Valid values are between 5 and 95 +select @@global.innodb_old_blocks_pct between 5 and 95; +@@global.innodb_old_blocks_pct between 5 and 95 +1 +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +37 +select @@session.innodb_old_blocks_pct; +ERROR HY000: Variable 'innodb_old_blocks_pct' is a GLOBAL variable +show global variables like 'innodb_old_blocks_pct'; +Variable_name Value +innodb_old_blocks_pct 37 +show session variables like 'innodb_old_blocks_pct'; +Variable_name Value +innodb_old_blocks_pct 37 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 37 +select * from information_schema.session_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 37 +set global innodb_old_blocks_pct=10; +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +10 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 10 +select * from information_schema.session_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 10 +set session innodb_old_blocks_pct=1; +ERROR HY000: Variable 'innodb_old_blocks_pct' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_old_blocks_pct=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_old_blocks_pct' +set global innodb_old_blocks_pct=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_old_blocks_pct' +set global innodb_old_blocks_pct="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_old_blocks_pct' +set global innodb_old_blocks_pct=4; +Warnings: +Warning 1292 Truncated incorrect innodb_old_blocks_pct value: '4' +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +5 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 5 +set global innodb_old_blocks_pct=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_old_blocks_pct value: '-7' +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +5 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 5 +set global innodb_old_blocks_pct=96; +Warnings: +Warning 1292 Truncated incorrect innodb_old_blocks_pct value: '96' +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +95 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_PCT 95 +set global innodb_old_blocks_pct=5; +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +5 +set global innodb_old_blocks_pct=95; +select @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +95 +SET @@global.innodb_old_blocks_pct = @start_global_value; +SELECT @@global.innodb_old_blocks_pct; +@@global.innodb_old_blocks_pct +37 diff --git a/mysql-test/suite/sys_vars/r/innodb_old_blocks_time_basic.result b/mysql-test/suite/sys_vars/r/innodb_old_blocks_time_basic.result new file mode 100644 index 00000000000..a285cc14a01 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_old_blocks_time_basic.result @@ -0,0 +1,56 @@ +SET @start_global_value = @@global.innodb_old_blocks_time; +SELECT @start_global_value; +@start_global_value +0 +Valid values are zero or above +select @@global.innodb_old_blocks_time >=0; +@@global.innodb_old_blocks_time >=0 +1 +select @@global.innodb_old_blocks_time; +@@global.innodb_old_blocks_time +0 +select @@session.innodb_old_blocks_time; +ERROR HY000: Variable 'innodb_old_blocks_time' is a GLOBAL variable +show global variables like 'innodb_old_blocks_time'; +Variable_name Value +innodb_old_blocks_time 0 +show session variables like 'innodb_old_blocks_time'; +Variable_name Value +innodb_old_blocks_time 0 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_time'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_TIME 0 +select * from information_schema.session_variables where variable_name='innodb_old_blocks_time'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_TIME 0 +set global innodb_old_blocks_time=10; +select @@global.innodb_old_blocks_time; +@@global.innodb_old_blocks_time +10 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_time'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_TIME 10 +select * from information_schema.session_variables where variable_name='innodb_old_blocks_time'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_TIME 10 +set session innodb_old_blocks_time=1; +ERROR HY000: Variable 'innodb_old_blocks_time' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_old_blocks_time=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_old_blocks_time' +set global innodb_old_blocks_time=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_old_blocks_time' +set global innodb_old_blocks_time="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_old_blocks_time' +set global innodb_old_blocks_time=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_old_blocks_time value: '-7' +select @@global.innodb_old_blocks_time; +@@global.innodb_old_blocks_time +0 +select * from information_schema.global_variables where variable_name='innodb_old_blocks_time'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_OLD_BLOCKS_TIME 0 +SET @@global.innodb_old_blocks_time = @start_global_value; +SELECT @@global.innodb_old_blocks_time; +@@global.innodb_old_blocks_time +0 diff --git a/mysql-test/suite/sys_vars/r/innodb_read_ahead_threshold_basic.result b/mysql-test/suite/sys_vars/r/innodb_read_ahead_threshold_basic.result new file mode 100644 index 00000000000..65a1a8e319f --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_read_ahead_threshold_basic.result @@ -0,0 +1,73 @@ +SET @start_global_value = @@global.innodb_read_ahead_threshold; +SELECT @start_global_value; +@start_global_value +56 +Valid values are between 0 and 64 +select @@global.innodb_read_ahead_threshold between 0 and 64; +@@global.innodb_read_ahead_threshold between 0 and 64 +1 +select @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +56 +select @@session.innodb_read_ahead_threshold; +ERROR HY000: Variable 'innodb_read_ahead_threshold' is a GLOBAL variable +show global variables like 'innodb_read_ahead_threshold'; +Variable_name Value +innodb_read_ahead_threshold 56 +show session variables like 'innodb_read_ahead_threshold'; +Variable_name Value +innodb_read_ahead_threshold 56 +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_AHEAD_THRESHOLD 56 +select * from information_schema.session_variables where variable_name='innodb_read_ahead_threshold'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_AHEAD_THRESHOLD 56 +set global innodb_read_ahead_threshold=10; +select @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +10 +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_AHEAD_THRESHOLD 10 +select * from information_schema.session_variables where variable_name='innodb_read_ahead_threshold'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_AHEAD_THRESHOLD 10 +set session innodb_read_ahead_threshold=1; +ERROR HY000: Variable 'innodb_read_ahead_threshold' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_read_ahead_threshold=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_read_ahead_threshold' +set global innodb_read_ahead_threshold=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_read_ahead_threshold' +set global innodb_read_ahead_threshold="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_read_ahead_threshold' +set global innodb_read_ahead_threshold=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_read_ahead_threshold value: '-7' +select @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +0 +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_AHEAD_THRESHOLD 0 +set global innodb_read_ahead_threshold=96; +Warnings: +Warning 1292 Truncated incorrect innodb_read_ahead_threshold value: '96' +select @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +64 +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_AHEAD_THRESHOLD 64 +set global innodb_read_ahead_threshold=0; +select @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +0 +set global innodb_read_ahead_threshold=64; +select @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +64 +SET @@global.innodb_read_ahead_threshold = @start_global_value; +SELECT @@global.innodb_read_ahead_threshold; +@@global.innodb_read_ahead_threshold +56 diff --git a/mysql-test/suite/sys_vars/r/innodb_read_io_threads_basic.result b/mysql-test/suite/sys_vars/r/innodb_read_io_threads_basic.result new file mode 100644 index 00000000000..f43fa81c0d0 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_read_io_threads_basic.result @@ -0,0 +1,21 @@ +select @@global.innodb_read_io_threads; +@@global.innodb_read_io_threads +2 +select @@session.innodb_read_io_threads; +ERROR HY000: Variable 'innodb_read_io_threads' is a GLOBAL variable +show global variables like 'innodb_read_io_threads'; +Variable_name Value +innodb_read_io_threads 2 +show session variables like 'innodb_read_io_threads'; +Variable_name Value +innodb_read_io_threads 2 +select * from information_schema.global_variables where variable_name='innodb_read_io_threads'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_IO_THREADS 2 +select * from information_schema.session_variables where variable_name='innodb_read_io_threads'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_READ_IO_THREADS 2 +set global innodb_read_io_threads=1; +ERROR HY000: Variable 'innodb_read_io_threads' is a read only variable +set session innodb_read_io_threads=1; +ERROR HY000: Variable 'innodb_read_io_threads' is a read only variable diff --git a/mysql-test/suite/sys_vars/r/innodb_replication_delay_basic.result b/mysql-test/suite/sys_vars/r/innodb_replication_delay_basic.result new file mode 100644 index 00000000000..fa00baa218e --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_replication_delay_basic.result @@ -0,0 +1,56 @@ +SET @start_global_value = @@global.innodb_replication_delay; +SELECT @start_global_value; +@start_global_value +0 +Valid values are zero or above +select @@global.innodb_replication_delay >=0; +@@global.innodb_replication_delay >=0 +1 +select @@global.innodb_replication_delay; +@@global.innodb_replication_delay +0 +select @@session.innodb_replication_delay; +ERROR HY000: Variable 'innodb_replication_delay' is a GLOBAL variable +show global variables like 'innodb_replication_delay'; +Variable_name Value +innodb_replication_delay 0 +show session variables like 'innodb_replication_delay'; +Variable_name Value +innodb_replication_delay 0 +select * from information_schema.global_variables where variable_name='innodb_replication_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_REPLICATION_DELAY 0 +select * from information_schema.session_variables where variable_name='innodb_replication_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_REPLICATION_DELAY 0 +set global innodb_replication_delay=10; +select @@global.innodb_replication_delay; +@@global.innodb_replication_delay +10 +select * from information_schema.global_variables where variable_name='innodb_replication_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_REPLICATION_DELAY 10 +select * from information_schema.session_variables where variable_name='innodb_replication_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_REPLICATION_DELAY 10 +set session innodb_replication_delay=1; +ERROR HY000: Variable 'innodb_replication_delay' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_replication_delay=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_replication_delay' +set global innodb_replication_delay=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_replication_delay' +set global innodb_replication_delay="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_replication_delay' +set global innodb_replication_delay=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_replication_delay value: '-7' +select @@global.innodb_replication_delay; +@@global.innodb_replication_delay +0 +select * from information_schema.global_variables where variable_name='innodb_replication_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_REPLICATION_DELAY 0 +SET @@global.innodb_replication_delay = @start_global_value; +SELECT @@global.innodb_replication_delay; +@@global.innodb_replication_delay +0 diff --git a/mysql-test/suite/sys_vars/r/innodb_spin_wait_delay_basic.result b/mysql-test/suite/sys_vars/r/innodb_spin_wait_delay_basic.result new file mode 100644 index 00000000000..05672cbb966 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_spin_wait_delay_basic.result @@ -0,0 +1,56 @@ +SET @start_global_value = @@global.innodb_spin_wait_delay; +SELECT @start_global_value; +@start_global_value +6 +Valid values are zero or above +select @@global.innodb_spin_wait_delay >=0; +@@global.innodb_spin_wait_delay >=0 +1 +select @@global.innodb_spin_wait_delay; +@@global.innodb_spin_wait_delay +6 +select @@session.innodb_spin_wait_delay; +ERROR HY000: Variable 'innodb_spin_wait_delay' is a GLOBAL variable +show global variables like 'innodb_spin_wait_delay'; +Variable_name Value +innodb_spin_wait_delay 6 +show session variables like 'innodb_spin_wait_delay'; +Variable_name Value +innodb_spin_wait_delay 6 +select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_SPIN_WAIT_DELAY 6 +select * from information_schema.session_variables where variable_name='innodb_spin_wait_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_SPIN_WAIT_DELAY 6 +set global innodb_spin_wait_delay=10; +select @@global.innodb_spin_wait_delay; +@@global.innodb_spin_wait_delay +10 +select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_SPIN_WAIT_DELAY 10 +select * from information_schema.session_variables where variable_name='innodb_spin_wait_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_SPIN_WAIT_DELAY 10 +set session innodb_spin_wait_delay=1; +ERROR HY000: Variable 'innodb_spin_wait_delay' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_spin_wait_delay=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_spin_wait_delay' +set global innodb_spin_wait_delay=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_spin_wait_delay' +set global innodb_spin_wait_delay="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_spin_wait_delay' +set global innodb_spin_wait_delay=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_spin_wait_delay value: '-7' +select @@global.innodb_spin_wait_delay; +@@global.innodb_spin_wait_delay +0 +select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_SPIN_WAIT_DELAY 0 +SET @@global.innodb_spin_wait_delay = @start_global_value; +SELECT @@global.innodb_spin_wait_delay; +@@global.innodb_spin_wait_delay +6 diff --git a/mysql-test/suite/sys_vars/r/innodb_stats_on_metadata_basic.result b/mysql-test/suite/sys_vars/r/innodb_stats_on_metadata_basic.result new file mode 100644 index 00000000000..d3410d7b9c1 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_stats_on_metadata_basic.result @@ -0,0 +1,92 @@ +SET @start_global_value = @@global.innodb_stats_on_metadata; +SELECT @start_global_value; +@start_global_value +1 +Valid values are 'ON' and 'OFF' +select @@global.innodb_stats_on_metadata in (0, 1); +@@global.innodb_stats_on_metadata in (0, 1) +1 +select @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +1 +select @@session.innodb_stats_on_metadata; +ERROR HY000: Variable 'innodb_stats_on_metadata' is a GLOBAL variable +show global variables like 'innodb_stats_on_metadata'; +Variable_name Value +innodb_stats_on_metadata ON +show session variables like 'innodb_stats_on_metadata'; +Variable_name Value +innodb_stats_on_metadata ON +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +set global innodb_stats_on_metadata='OFF'; +select @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +0 +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA OFF +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA OFF +set @@global.innodb_stats_on_metadata=1; +select @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +1 +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +set global innodb_stats_on_metadata=0; +select @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +0 +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA OFF +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA OFF +set @@global.innodb_stats_on_metadata='ON'; +select @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +1 +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +set session innodb_stats_on_metadata='OFF'; +ERROR HY000: Variable 'innodb_stats_on_metadata' is a GLOBAL variable and should be set with SET GLOBAL +set @@session.innodb_stats_on_metadata='ON'; +ERROR HY000: Variable 'innodb_stats_on_metadata' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_stats_on_metadata=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_on_metadata' +set global innodb_stats_on_metadata=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_on_metadata' +set global innodb_stats_on_metadata=2; +ERROR 42000: Variable 'innodb_stats_on_metadata' can't be set to the value of '2' +NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_stats_on_metadata=-3; +select @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +1 +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_ON_METADATA ON +set global innodb_stats_on_metadata='AUTO'; +ERROR 42000: Variable 'innodb_stats_on_metadata' can't be set to the value of 'AUTO' +SET @@global.innodb_stats_on_metadata = @start_global_value; +SELECT @@global.innodb_stats_on_metadata; +@@global.innodb_stats_on_metadata +1 diff --git a/mysql-test/suite/sys_vars/r/innodb_stats_sample_pages_basic.result b/mysql-test/suite/sys_vars/r/innodb_stats_sample_pages_basic.result new file mode 100644 index 00000000000..153ae95a4fe --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_stats_sample_pages_basic.result @@ -0,0 +1,56 @@ +SET @start_global_value = @@global.innodb_stats_sample_pages; +SELECT @start_global_value; +@start_global_value +8 +Valid values are one or above +select @@global.innodb_stats_sample_pages >=1; +@@global.innodb_stats_sample_pages >=1 +1 +select @@global.innodb_stats_sample_pages; +@@global.innodb_stats_sample_pages +8 +select @@session.innodb_stats_sample_pages; +ERROR HY000: Variable 'innodb_stats_sample_pages' is a GLOBAL variable +show global variables like 'innodb_stats_sample_pages'; +Variable_name Value +innodb_stats_sample_pages 8 +show session variables like 'innodb_stats_sample_pages'; +Variable_name Value +innodb_stats_sample_pages 8 +select * from information_schema.global_variables where variable_name='innodb_stats_sample_pages'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_SAMPLE_PAGES 8 +select * from information_schema.session_variables where variable_name='innodb_stats_sample_pages'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_SAMPLE_PAGES 8 +set global innodb_stats_sample_pages=10; +select @@global.innodb_stats_sample_pages; +@@global.innodb_stats_sample_pages +10 +select * from information_schema.global_variables where variable_name='innodb_stats_sample_pages'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_SAMPLE_PAGES 10 +select * from information_schema.session_variables where variable_name='innodb_stats_sample_pages'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_SAMPLE_PAGES 10 +set session innodb_stats_sample_pages=1; +ERROR HY000: Variable 'innodb_stats_sample_pages' is a GLOBAL variable and should be set with SET GLOBAL +set global innodb_stats_sample_pages=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_sample_pages' +set global innodb_stats_sample_pages=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_sample_pages' +set global innodb_stats_sample_pages="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_stats_sample_pages' +set global innodb_stats_sample_pages=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_stats_sample_pages value: '-7' +select @@global.innodb_stats_sample_pages; +@@global.innodb_stats_sample_pages +1 +select * from information_schema.global_variables where variable_name='innodb_stats_sample_pages'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STATS_SAMPLE_PAGES 1 +SET @@global.innodb_stats_sample_pages = @start_global_value; +SELECT @@global.innodb_stats_sample_pages; +@@global.innodb_stats_sample_pages +8 diff --git a/mysql-test/suite/sys_vars/r/innodb_strict_mode_basic.result b/mysql-test/suite/sys_vars/r/innodb_strict_mode_basic.result new file mode 100644 index 00000000000..200f9166215 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_strict_mode_basic.result @@ -0,0 +1,120 @@ +SET @start_global_value = @@global.innodb_strict_mode; +SELECT @start_global_value; +@start_global_value +0 +Valid values are 'ON' and 'OFF' +select @@global.innodb_strict_mode in (0, 1); +@@global.innodb_strict_mode in (0, 1) +1 +select @@global.innodb_strict_mode; +@@global.innodb_strict_mode +0 +select @@session.innodb_strict_mode in (0, 1); +@@session.innodb_strict_mode in (0, 1) +1 +select @@session.innodb_strict_mode; +@@session.innodb_strict_mode +0 +show global variables like 'innodb_strict_mode'; +Variable_name Value +innodb_strict_mode OFF +show session variables like 'innodb_strict_mode'; +Variable_name Value +innodb_strict_mode OFF +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE OFF +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE OFF +set global innodb_strict_mode='OFF'; +set session innodb_strict_mode='OFF'; +select @@global.innodb_strict_mode; +@@global.innodb_strict_mode +0 +select @@session.innodb_strict_mode; +@@session.innodb_strict_mode +0 +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE OFF +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE OFF +set @@global.innodb_strict_mode=1; +set @@session.innodb_strict_mode=1; +select @@global.innodb_strict_mode; +@@global.innodb_strict_mode +1 +select @@session.innodb_strict_mode; +@@session.innodb_strict_mode +1 +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE ON +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE ON +set global innodb_strict_mode=0; +set session innodb_strict_mode=0; +select @@global.innodb_strict_mode; +@@global.innodb_strict_mode +0 +select @@session.innodb_strict_mode; +@@session.innodb_strict_mode +0 +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE OFF +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE OFF +set @@global.innodb_strict_mode='ON'; +set @@session.innodb_strict_mode='ON'; +select @@global.innodb_strict_mode; +@@global.innodb_strict_mode +1 +select @@session.innodb_strict_mode; +@@session.innodb_strict_mode +1 +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE ON +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE ON +set global innodb_strict_mode=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_strict_mode' +set session innodb_strict_mode=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_strict_mode' +set global innodb_strict_mode=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_strict_mode' +set session innodb_strict_mode=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_strict_mode' +set global innodb_strict_mode=2; +ERROR 42000: Variable 'innodb_strict_mode' can't be set to the value of '2' +set session innodb_strict_mode=2; +ERROR 42000: Variable 'innodb_strict_mode' can't be set to the value of '2' +set global innodb_strict_mode='AUTO'; +ERROR 42000: Variable 'innodb_strict_mode' can't be set to the value of 'AUTO' +set session innodb_strict_mode='AUTO'; +ERROR 42000: Variable 'innodb_strict_mode' can't be set to the value of 'AUTO' +NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_strict_mode=-3; +set session innodb_strict_mode=-7; +select @@global.innodb_strict_mode; +@@global.innodb_strict_mode +1 +select @@session.innodb_strict_mode; +@@session.innodb_strict_mode +1 +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE ON +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_STRICT_MODE ON +SET @@global.innodb_strict_mode = @start_global_value; +SELECT @@global.innodb_strict_mode; +@@global.innodb_strict_mode +0 diff --git a/mysql-test/suite/sys_vars/r/innodb_thread_sleep_delay_basic.result b/mysql-test/suite/sys_vars/r/innodb_thread_sleep_delay_basic.result index 979bfd930ca..fb7093ec3b3 100644 --- a/mysql-test/suite/sys_vars/r/innodb_thread_sleep_delay_basic.result +++ b/mysql-test/suite/sys_vars/r/innodb_thread_sleep_delay_basic.result @@ -1,84 +1,56 @@ -SET @global_start_value = @@global.innodb_thread_sleep_delay; -SELECT @global_start_value; -@global_start_value +SET @start_global_value = @@global.innodb_thread_sleep_delay; +SELECT @start_global_value; +@start_global_value 10000 -'#--------------------FN_DYNVARS_046_01------------------------#' -SET @@global.innodb_thread_sleep_delay = 0; -SET @@global.innodb_thread_sleep_delay = DEFAULT; -SELECT @@global.innodb_thread_sleep_delay; +Valid values are zero or above +select @@global.innodb_thread_sleep_delay >=0; +@@global.innodb_thread_sleep_delay >=0 +1 +select @@global.innodb_thread_sleep_delay; @@global.innodb_thread_sleep_delay 10000 -'#---------------------FN_DYNVARS_046_02-------------------------#' -SET innodb_thread_sleep_delay = 1; +select @@session.innodb_thread_sleep_delay; +ERROR HY000: Variable 'innodb_thread_sleep_delay' is a GLOBAL variable +show global variables like 'innodb_thread_sleep_delay'; +Variable_name Value +innodb_thread_sleep_delay 10000 +show session variables like 'innodb_thread_sleep_delay'; +Variable_name Value +innodb_thread_sleep_delay 10000 +select * from information_schema.global_variables where variable_name='innodb_thread_sleep_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_THREAD_SLEEP_DELAY 10000 +select * from information_schema.session_variables where variable_name='innodb_thread_sleep_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_THREAD_SLEEP_DELAY 10000 +set global innodb_thread_sleep_delay=10; +select @@global.innodb_thread_sleep_delay; +@@global.innodb_thread_sleep_delay +10 +select * from information_schema.global_variables where variable_name='innodb_thread_sleep_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_THREAD_SLEEP_DELAY 10 +select * from information_schema.session_variables where variable_name='innodb_thread_sleep_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_THREAD_SLEEP_DELAY 10 +set session innodb_thread_sleep_delay=1; ERROR HY000: Variable 'innodb_thread_sleep_delay' is a GLOBAL variable and should be set with SET GLOBAL -SELECT @@innodb_thread_sleep_delay; -@@innodb_thread_sleep_delay +set global innodb_thread_sleep_delay=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' +set global innodb_thread_sleep_delay=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' +set global innodb_thread_sleep_delay="foo"; +ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' +set global innodb_thread_sleep_delay=-7; +Warnings: +Warning 1292 Truncated incorrect innodb_thread_sleep_delay value: '-7' +select @@global.innodb_thread_sleep_delay; +@@global.innodb_thread_sleep_delay +0 +select * from information_schema.global_variables where variable_name='innodb_thread_sleep_delay'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_THREAD_SLEEP_DELAY 0 +SET @@global.innodb_thread_sleep_delay = @start_global_value; +SELECT @@global.innodb_thread_sleep_delay; +@@global.innodb_thread_sleep_delay 10000 -SELECT local.innodb_thread_sleep_delay; -ERROR 42S02: Unknown table 'local' in field list -SET global innodb_thread_sleep_delay = 0; -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -0 -'#--------------------FN_DYNVARS_046_03------------------------#' -SET @@global.innodb_thread_sleep_delay = 0; -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -0 -SET @@global.innodb_thread_sleep_delay = 1; -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -1 -SET @@global.innodb_thread_sleep_delay = 4294967295; -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -4294967295 -'#--------------------FN_DYNVARS_046_04-------------------------#' -SET @@global.innodb_thread_sleep_delay = -1; -SELECT @@global.innodb_autoextend_increment; -@@global.innodb_autoextend_increment -8 -SET @@global.innodb_thread_sleep_delay = "T"; -ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' -SELECT @@global.innodb_autoextend_increment; -@@global.innodb_autoextend_increment -8 -SET @@global.innodb_thread_sleep_delay = "Y"; -ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' -SELECT @@global.innodb_autoextend_increment; -@@global.innodb_autoextend_increment -8 -SET @@global.innodb_thread_sleep_delay = 1001; -SELECT @@global.innodb_autoextend_increment; -@@global.innodb_autoextend_increment -8 -'#----------------------FN_DYNVARS_046_05------------------------#' -SELECT @@global.innodb_thread_sleep_delay = VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME='innodb_thread_sleep_delay'; -@@global.innodb_thread_sleep_delay = VARIABLE_VALUE -1 -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -1001 -SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME='innodb_thread_sleep_delay'; -VARIABLE_VALUE -1001 -'#---------------------FN_DYNVARS_046_06-------------------------#' -SET @@global.innodb_thread_sleep_delay = OFF; -ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -1001 -SET @@global.innodb_thread_sleep_delay = ON; -ERROR 42000: Incorrect argument type to variable 'innodb_thread_sleep_delay' -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -1001 -'#---------------------FN_DYNVARS_046_07----------------------#' -SET @@global.innodb_thread_sleep_delay = TRUE; -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -1 -SET @@global.innodb_thread_sleep_delay = FALSE; -SELECT @@global.innodb_thread_sleep_delay; -@@global.innodb_thread_sleep_delay -0 diff --git a/mysql-test/suite/sys_vars/r/innodb_use_sys_malloc_basic.result b/mysql-test/suite/sys_vars/r/innodb_use_sys_malloc_basic.result new file mode 100644 index 00000000000..0c2685b1a49 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_use_sys_malloc_basic.result @@ -0,0 +1,25 @@ +Valid values are 'ON' and 'OFF' +select @@global.innodb_adaptive_flushing in (0, 1); +@@global.innodb_adaptive_flushing in (0, 1) +1 +select @@global.innodb_use_sys_malloc; +@@global.innodb_use_sys_malloc +1 +select @@session.innodb_use_sys_malloc; +ERROR HY000: Variable 'innodb_use_sys_malloc' is a GLOBAL variable +show global variables like 'innodb_use_sys_malloc'; +Variable_name Value +innodb_use_sys_malloc ON +show session variables like 'innodb_use_sys_malloc'; +Variable_name Value +innodb_use_sys_malloc ON +select * from information_schema.global_variables where variable_name='innodb_use_sys_malloc'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_USE_SYS_MALLOC ON +select * from information_schema.session_variables where variable_name='innodb_use_sys_malloc'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_USE_SYS_MALLOC ON +set global innodb_use_sys_malloc=1; +ERROR HY000: Variable 'innodb_use_sys_malloc' is a read only variable +set session innodb_use_sys_malloc=1; +ERROR HY000: Variable 'innodb_use_sys_malloc' is a read only variable diff --git a/mysql-test/suite/sys_vars/r/innodb_version_basic.result b/mysql-test/suite/sys_vars/r/innodb_version_basic.result new file mode 100644 index 00000000000..759b5048512 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_version_basic.result @@ -0,0 +1,17 @@ +select @@global.innodb_version; +@@global.innodb_version +x.y.z +select @@session.innodb_version; +ERROR HY000: Variable 'innodb_version' is a GLOBAL variable +show global variables like 'innodb_version' disabled so to not change with every version; +show session variables like 'innodb_version' disabled so to not change with every version; +select VARIABLE_VALUE=@@global.innodb_version from information_schema.global_variables where variable_name='innodb_version'; +VARIABLE_VALUE=@@global.innodb_version +1 +select VARIABLE_VALUE=@@global.innodb_version from information_schema.session_variables where variable_name='innodb_version'; +VARIABLE_VALUE=@@global.innodb_version +1 +set global innodb_version=1; +ERROR HY000: Variable 'innodb_version' is a read only variable +set session innodb_version=1; +ERROR HY000: Variable 'innodb_version' is a read only variable diff --git a/mysql-test/suite/sys_vars/r/innodb_write_io_threads_basic.result b/mysql-test/suite/sys_vars/r/innodb_write_io_threads_basic.result new file mode 100644 index 00000000000..a363f4292dd --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_write_io_threads_basic.result @@ -0,0 +1,21 @@ +select @@global.innodb_write_io_threads; +@@global.innodb_write_io_threads +2 +select @@session.innodb_write_io_threads; +ERROR HY000: Variable 'innodb_write_io_threads' is a GLOBAL variable +show global variables like 'innodb_write_io_threads'; +Variable_name Value +innodb_write_io_threads 2 +show session variables like 'innodb_write_io_threads'; +Variable_name Value +innodb_write_io_threads 2 +select * from information_schema.global_variables where variable_name='innodb_write_io_threads'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_WRITE_IO_THREADS 2 +select * from information_schema.session_variables where variable_name='innodb_write_io_threads'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_WRITE_IO_THREADS 2 +set global innodb_write_io_threads=1; +ERROR HY000: Variable 'innodb_write_io_threads' is a read only variable +set session innodb_write_io_threads=1; +ERROR HY000: Variable 'innodb_write_io_threads' is a read only variable diff --git a/mysql-test/suite/sys_vars/r/last_insert_id_basic.result b/mysql-test/suite/sys_vars/r/last_insert_id_basic.result index c29a313b9e7..a4ae20dbabb 100644 --- a/mysql-test/suite/sys_vars/r/last_insert_id_basic.result +++ b/mysql-test/suite/sys_vars/r/last_insert_id_basic.result @@ -13,12 +13,24 @@ VARIABLE_NAME VARIABLE_VALUE select * from information_schema.session_variables where variable_name='last_insert_id'; VARIABLE_NAME VARIABLE_VALUE LAST_INSERT_ID 0 -set session last_insert_id=1; +set global last_insert_id=99; +ERROR HY000: Variable 'last_insert_id' is a SESSION variable and can't be used with SET GLOBAL +set session last_insert_id=42; +select @@global.last_insert_id; +ERROR HY000: Variable 'last_insert_id' is a SESSION variable select @@session.last_insert_id; @@session.last_insert_id -1 -set global last_insert_id=1; -ERROR HY000: Variable 'last_insert_id' is a SESSION variable and can't be used with SET GLOBAL +42 +show global variables like 'last_insert_id'; +Variable_name Value +show session variables like 'last_insert_id'; +Variable_name Value +last_insert_id 42 +select * from information_schema.global_variables where variable_name='last_insert_id'; +VARIABLE_NAME VARIABLE_VALUE +select * from information_schema.session_variables where variable_name='last_insert_id'; +VARIABLE_NAME VARIABLE_VALUE +LAST_INSERT_ID 42 set session last_insert_id=1.1; ERROR 42000: Incorrect argument type to variable 'last_insert_id' set session last_insert_id=1e1; diff --git a/mysql-test/suite/sys_vars/r/lc_messages_basic.result b/mysql-test/suite/sys_vars/r/lc_messages_basic.result index e765dd53516..b9350828dba 100644 --- a/mysql-test/suite/sys_vars/r/lc_messages_basic.result +++ b/mysql-test/suite/sys_vars/r/lc_messages_basic.result @@ -29,13 +29,19 @@ select @@session.lc_messages; @@session.lc_messages ja_JP set global lc_messages="en_US"; +set session lc_messages="en_GB"; select @@global.lc_messages; @@global.lc_messages en_US -set session lc_messages="en_GB"; select @@session.lc_messages; @@session.lc_messages en_GB +select * from information_schema.global_variables where variable_name='lc_messages'; +VARIABLE_NAME VARIABLE_VALUE +LC_MESSAGES en_US +select * from information_schema.session_variables where variable_name='lc_messages'; +VARIABLE_NAME VARIABLE_VALUE +LC_MESSAGES en_GB set global lc_messages=1.1; ERROR 42000: Incorrect argument type to variable 'lc_messages' set global lc_messages=1e1; diff --git a/mysql-test/suite/sys_vars/r/log_slow_queries_basic.result b/mysql-test/suite/sys_vars/r/log_slow_queries_basic.result index ca530ebb41a..1748406b74b 100644 --- a/mysql-test/suite/sys_vars/r/log_slow_queries_basic.result +++ b/mysql-test/suite/sys_vars/r/log_slow_queries_basic.result @@ -65,12 +65,22 @@ Warning 1287 The syntax '@@log_slow_queries' is deprecated and will be removed i SELECT @@global.log_slow_queries; @@global.log_slow_queries 0 +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; +IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +1 SET @@global.log_slow_queries = 1; Warnings: Warning 1287 The syntax '@@log_slow_queries' is deprecated and will be removed in MySQL 7.0. Please use '@@slow_query_log' instead SELECT @@global.log_slow_queries; @@global.log_slow_queries 1 +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; +IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +1 '#---------------------FN_DYNVARS_004_07----------------------#' SET @@global.log_slow_queries = TRUE; Warnings: @@ -78,12 +88,22 @@ Warning 1287 The syntax '@@log_slow_queries' is deprecated and will be removed i SELECT @@global.log_slow_queries; @@global.log_slow_queries 1 +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; +IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +1 SET @@global.log_slow_queries = FALSE; Warnings: Warning 1287 The syntax '@@log_slow_queries' is deprecated and will be removed in MySQL 7.0. Please use '@@slow_query_log' instead SELECT @@global.log_slow_queries; @@global.log_slow_queries 0 +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; +IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +1 '#---------------------FN_DYNVARS_004_08----------------------#' SET @@global.log_slow_queries = ON; Warnings: diff --git a/mysql-test/suite/sys_vars/r/lower_case_file_system_basic.result b/mysql-test/suite/sys_vars/r/lower_case_file_system_basic.result index 0488ed30dc0..4ecd81685c5 100644 --- a/mysql-test/suite/sys_vars/r/lower_case_file_system_basic.result +++ b/mysql-test/suite/sys_vars/r/lower_case_file_system_basic.result @@ -3,18 +3,19 @@ select @@global.lower_case_file_system=2; 0 select @@session.lower_case_file_system; ERROR HY000: Variable 'lower_case_file_system' is a GLOBAL variable -show global variables like 'lower_case_file_system'; -Variable_name Value -lower_case_file_system # -show session variables like 'lower_case_file_system'; -Variable_name Value -lower_case_file_system # -select * from information_schema.global_variables where variable_name='lower_case_file_system'; -VARIABLE_NAME VARIABLE_VALUE -LOWER_CASE_FILE_SYSTEM # -select * from information_schema.session_variables where variable_name='lower_case_file_system'; -VARIABLE_NAME VARIABLE_VALUE -LOWER_CASE_FILE_SYSTEM # +SELECT @@global.lower_case_file_system in (0,1); +@@global.lower_case_file_system in (0,1) +1 +SELECT IF(@@global.lower_case_file_system, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lower_case_file_system'; +IF(@@global.lower_case_file_system, "ON", "OFF") = VARIABLE_VALUE +1 +SELECT IF(@@global.lower_case_file_system, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.SESSION_VARIABLES +WHERE VARIABLE_NAME='lower_case_file_system'; +IF(@@global.lower_case_file_system, "ON", "OFF") = VARIABLE_VALUE +1 set global lower_case_file_system=1; ERROR HY000: Variable 'lower_case_file_system' is a read only variable set session lower_case_file_system=1; diff --git a/mysql-test/suite/sys_vars/r/lower_case_table_names_basic.result b/mysql-test/suite/sys_vars/r/lower_case_table_names_basic.result index d7dc6d69e30..cae6ecb215c 100644 --- a/mysql-test/suite/sys_vars/r/lower_case_table_names_basic.result +++ b/mysql-test/suite/sys_vars/r/lower_case_table_names_basic.result @@ -3,18 +3,19 @@ select @@global.lower_case_table_names=20; 0 select @@session.lower_case_table_names; ERROR HY000: Variable 'lower_case_table_names' is a GLOBAL variable -show global variables like 'lower_case_table_names'; -Variable_name Value -lower_case_table_names # -show session variables like 'lower_case_table_names'; -Variable_name Value -lower_case_table_names # -select * from information_schema.global_variables where variable_name='lower_case_table_names'; -VARIABLE_NAME VARIABLE_VALUE -LOWER_CASE_TABLE_NAMES # -select * from information_schema.session_variables where variable_name='lower_case_table_names'; -VARIABLE_NAME VARIABLE_VALUE -LOWER_CASE_TABLE_NAMES # +SELECT @@global.lower_case_table_names in (0,1,2); +@@global.lower_case_table_names in (0,1,2) +1 +SELECT @@global.lower_case_table_names = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lower_case_table_names'; +@@global.lower_case_table_names = VARIABLE_VALUE +1 +SELECT @@global.lower_case_table_names = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lower_case_table_names'; +@@global.lower_case_table_names = VARIABLE_VALUE +1 set global lower_case_table_names=1; ERROR HY000: Variable 'lower_case_table_names' is a read only variable set session lower_case_table_names=1; diff --git a/mysql-test/suite/sys_vars/r/max_join_size_basic.result b/mysql-test/suite/sys_vars/r/max_join_size_basic.result index 5cebefe9ea3..acf9f123238 100644 --- a/mysql-test/suite/sys_vars/r/max_join_size_basic.result +++ b/mysql-test/suite/sys_vars/r/max_join_size_basic.result @@ -21,13 +21,25 @@ select * from information_schema.session_variables where variable_name='max_join VARIABLE_NAME VARIABLE_VALUE MAX_JOIN_SIZE 18446744073709551615 set global max_join_size=10; +set session max_join_size=20; select @@global.max_join_size; @@global.max_join_size 10 -set session max_join_size=20; select @@session.max_join_size; @@session.max_join_size 20 +show global variables like 'max_join_size'; +Variable_name Value +max_join_size 10 +show session variables like 'max_join_size'; +Variable_name Value +max_join_size 20 +select * from information_schema.global_variables where variable_name='max_join_size'; +VARIABLE_NAME VARIABLE_VALUE +MAX_JOIN_SIZE 10 +select * from information_schema.session_variables where variable_name='max_join_size'; +VARIABLE_NAME VARIABLE_VALUE +MAX_JOIN_SIZE 20 set global max_join_size=1.1; ERROR 42000: Incorrect argument type to variable 'max_join_size' set global max_join_size=1e1; diff --git a/mysql-test/suite/sys_vars/r/old_alter_table_basic.result b/mysql-test/suite/sys_vars/r/old_alter_table_basic.result index 0619235b8e0..5cc17917242 100644 --- a/mysql-test/suite/sys_vars/r/old_alter_table_basic.result +++ b/mysql-test/suite/sys_vars/r/old_alter_table_basic.result @@ -21,13 +21,25 @@ select * from information_schema.session_variables where variable_name='old_alte VARIABLE_NAME VARIABLE_VALUE OLD_ALTER_TABLE OFF set global old_alter_table=1; +set session old_alter_table=ON; select @@global.old_alter_table; @@global.old_alter_table 1 -set session old_alter_table=ON; select @@session.old_alter_table; @@session.old_alter_table 1 +show global variables like 'old_alter_table'; +Variable_name Value +old_alter_table ON +show session variables like 'old_alter_table'; +Variable_name Value +old_alter_table ON +select * from information_schema.global_variables where variable_name='old_alter_table'; +VARIABLE_NAME VARIABLE_VALUE +OLD_ALTER_TABLE ON +select * from information_schema.session_variables where variable_name='old_alter_table'; +VARIABLE_NAME VARIABLE_VALUE +OLD_ALTER_TABLE ON set global old_alter_table=1.1; ERROR 42000: Incorrect argument type to variable 'old_alter_table' set global old_alter_table=1e1; diff --git a/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result b/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result index 2d648259a26..acc8cd699f8 100644 --- a/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result +++ b/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result @@ -21,21 +21,33 @@ select * from information_schema.session_variables where variable_name='optimize VARIABLE_NAME VARIABLE_VALUE OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on set global optimizer_switch=10; +set session optimizer_switch=5; select @@global.optimizer_switch; @@global.optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,engine_condition_pushdown=off -set session optimizer_switch=5; select @@session.optimizer_switch; @@session.optimizer_switch index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,engine_condition_pushdown=off set global optimizer_switch="index_merge_sort_union=on"; +set session optimizer_switch="index_merge=off"; select @@global.optimizer_switch; @@global.optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=off -set session optimizer_switch="index_merge=off"; select @@session.optimizer_switch; @@session.optimizer_switch index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,engine_condition_pushdown=off +show global variables like 'optimizer_switch'; +Variable_name Value +optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=off +show session variables like 'optimizer_switch'; +Variable_name Value +optimizer_switch index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,engine_condition_pushdown=off +select * from information_schema.global_variables where variable_name='optimizer_switch'; +VARIABLE_NAME VARIABLE_VALUE +OPTIMIZER_SWITCH index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=off +select * from information_schema.session_variables where variable_name='optimizer_switch'; +VARIABLE_NAME VARIABLE_VALUE +OPTIMIZER_SWITCH index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,engine_condition_pushdown=off set session optimizer_switch="default"; select @@session.optimizer_switch; @@session.optimizer_switch diff --git a/mysql-test/suite/sys_vars/r/profiling_basic.result b/mysql-test/suite/sys_vars/r/profiling_basic.result index 26704b6b725..4f987d690ba 100644 --- a/mysql-test/suite/sys_vars/r/profiling_basic.result +++ b/mysql-test/suite/sys_vars/r/profiling_basic.result @@ -21,13 +21,45 @@ select * from information_schema.session_variables where variable_name='profilin VARIABLE_NAME VARIABLE_VALUE PROFILING OFF set global profiling=1; +set session profiling=ON; select @@global.profiling; @@global.profiling 1 -set session profiling=ON; select @@session.profiling; @@session.profiling 1 +show global variables like 'profiling'; +Variable_name Value +profiling ON +show session variables like 'profiling'; +Variable_name Value +profiling ON +select * from information_schema.global_variables where variable_name='profiling'; +VARIABLE_NAME VARIABLE_VALUE +PROFILING ON +select * from information_schema.session_variables where variable_name='profiling'; +VARIABLE_NAME VARIABLE_VALUE +PROFILING ON +set global profiling=0; +set session profiling=OFF; +select @@global.profiling; +@@global.profiling +0 +select @@session.profiling; +@@session.profiling +0 +show global variables like 'profiling'; +Variable_name Value +profiling OFF +show session variables like 'profiling'; +Variable_name Value +profiling OFF +select * from information_schema.global_variables where variable_name='profiling'; +VARIABLE_NAME VARIABLE_VALUE +PROFILING OFF +select * from information_schema.session_variables where variable_name='profiling'; +VARIABLE_NAME VARIABLE_VALUE +PROFILING OFF set global profiling=1.1; ERROR 42000: Incorrect argument type to variable 'profiling' set global profiling=1e1; diff --git a/mysql-test/suite/sys_vars/r/profiling_history_size_basic.result b/mysql-test/suite/sys_vars/r/profiling_history_size_basic.result index be92d075326..396d280ad18 100644 --- a/mysql-test/suite/sys_vars/r/profiling_history_size_basic.result +++ b/mysql-test/suite/sys_vars/r/profiling_history_size_basic.result @@ -21,13 +21,25 @@ select * from information_schema.session_variables where variable_name='profilin VARIABLE_NAME VARIABLE_VALUE PROFILING_HISTORY_SIZE 15 set global profiling_history_size=10; +set session profiling_history_size=20; select @@global.profiling_history_size; @@global.profiling_history_size 10 -set session profiling_history_size=20; select @@session.profiling_history_size; @@session.profiling_history_size 20 +show global variables like 'profiling_history_size'; +Variable_name Value +profiling_history_size 10 +show session variables like 'profiling_history_size'; +Variable_name Value +profiling_history_size 20 +select * from information_schema.global_variables where variable_name='profiling_history_size'; +VARIABLE_NAME VARIABLE_VALUE +PROFILING_HISTORY_SIZE 10 +select * from information_schema.session_variables where variable_name='profiling_history_size'; +VARIABLE_NAME VARIABLE_VALUE +PROFILING_HISTORY_SIZE 20 set global profiling_history_size=1.1; ERROR 42000: Incorrect argument type to variable 'profiling_history_size' set global profiling_history_size=1e1; diff --git a/mysql-test/suite/sys_vars/r/pseudo_thread_id_basic.result b/mysql-test/suite/sys_vars/r/pseudo_thread_id_basic.result index e6619e206f1..6b0a8899560 100644 --- a/mysql-test/suite/sys_vars/r/pseudo_thread_id_basic.result +++ b/mysql-test/suite/sys_vars/r/pseudo_thread_id_basic.result @@ -1,22 +1,29 @@ select @@global.pseudo_thread_id; ERROR HY000: Variable 'pseudo_thread_id' is a SESSION variable -select @@session.pseudo_thread_id=0; -@@session.pseudo_thread_id=0 -0 +select @@session.pseudo_thread_id between 1 and 1000; +@@session.pseudo_thread_id between 1 and 1000 +1 +should be empty show global variables like 'pseudo_thread_id'; Variable_name Value show session variables like 'pseudo_thread_id'; Variable_name Value pseudo_thread_id # +should be empty select * from information_schema.global_variables where variable_name='pseudo_thread_id'; VARIABLE_NAME VARIABLE_VALUE -select * from information_schema.session_variables where variable_name='pseudo_thread_id'; -VARIABLE_NAME VARIABLE_VALUE -PSEUDO_THREAD_ID # -set session pseudo_thread_id=1; +select @@session.pseudo_thread_id = variable_value from information_schema.session_variables where variable_name='pseudo_thread_id'; +@@session.pseudo_thread_id = variable_value +1 +set session pseudo_thread_id=42; select @@session.pseudo_thread_id; @@session.pseudo_thread_id -1 +42 +select * from information_schema.global_variables where variable_name='pseudo_thread_id'; +VARIABLE_NAME VARIABLE_VALUE +select variable_value from information_schema.session_variables where variable_name='pseudo_thread_id'; +variable_value +42 set global pseudo_thread_id=1; ERROR HY000: Variable 'pseudo_thread_id' is a SESSION variable and can't be used with SET GLOBAL set session pseudo_thread_id=1.1; diff --git a/mysql-test/suite/sys_vars/r/rand_seed1_basic.result b/mysql-test/suite/sys_vars/r/rand_seed1_basic.result index 7b92d533c9e..155d7169168 100644 --- a/mysql-test/suite/sys_vars/r/rand_seed1_basic.result +++ b/mysql-test/suite/sys_vars/r/rand_seed1_basic.result @@ -17,6 +17,11 @@ set session rand_seed1=1; select @@session.rand_seed1; @@session.rand_seed1 0 +select * from information_schema.global_variables where variable_name='rand_seed1'; +VARIABLE_NAME VARIABLE_VALUE +select * from information_schema.session_variables where variable_name='rand_seed1'; +VARIABLE_NAME VARIABLE_VALUE +RAND_SEED1 0 set global rand_seed1=1; ERROR HY000: Variable 'rand_seed1' is a SESSION variable and can't be used with SET GLOBAL set session rand_seed1=1.1; diff --git a/mysql-test/suite/sys_vars/r/rand_seed2_basic.result b/mysql-test/suite/sys_vars/r/rand_seed2_basic.result index 3d84aa3e37e..4974d8a53a3 100644 --- a/mysql-test/suite/sys_vars/r/rand_seed2_basic.result +++ b/mysql-test/suite/sys_vars/r/rand_seed2_basic.result @@ -17,6 +17,11 @@ set session rand_seed2=1; select @@session.rand_seed2; @@session.rand_seed2 0 +select * from information_schema.global_variables where variable_name='rand_seed2'; +VARIABLE_NAME VARIABLE_VALUE +select * from information_schema.session_variables where variable_name='rand_seed2'; +VARIABLE_NAME VARIABLE_VALUE +RAND_SEED2 0 set global rand_seed2=1; ERROR HY000: Variable 'rand_seed2' is a SESSION variable and can't be used with SET GLOBAL set session rand_seed2=1.1; diff --git a/mysql-test/suite/sys_vars/r/relay_log_recovery_basic.result b/mysql-test/suite/sys_vars/r/relay_log_recovery_basic.result index 97b991ce65b..af3b51bcffe 100644 --- a/mysql-test/suite/sys_vars/r/relay_log_recovery_basic.result +++ b/mysql-test/suite/sys_vars/r/relay_log_recovery_basic.result @@ -23,10 +23,22 @@ set global relay_log_recovery=1; select @@global.relay_log_recovery; @@global.relay_log_recovery 1 +select * from information_schema.global_variables where variable_name='relay_log_recovery'; +VARIABLE_NAME VARIABLE_VALUE +RELAY_LOG_RECOVERY ON +select * from information_schema.session_variables where variable_name='relay_log_recovery'; +VARIABLE_NAME VARIABLE_VALUE +RELAY_LOG_RECOVERY ON set global relay_log_recovery=OFF; select @@global.relay_log_recovery; @@global.relay_log_recovery 0 +select * from information_schema.global_variables where variable_name='relay_log_recovery'; +VARIABLE_NAME VARIABLE_VALUE +RELAY_LOG_RECOVERY OFF +select * from information_schema.session_variables where variable_name='relay_log_recovery'; +VARIABLE_NAME VARIABLE_VALUE +RELAY_LOG_RECOVERY OFF set session relay_log_recovery=1; ERROR HY000: Variable 'relay_log_recovery' is a GLOBAL variable and should be set with SET GLOBAL set global relay_log_recovery=1.1; diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_enabled_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_enabled_basic.result new file mode 100644 index 00000000000..ad3ca0de0fa --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_enabled_basic.result @@ -0,0 +1,73 @@ +INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; +select @@global.rpl_semi_sync_master_enabled; +@@global.rpl_semi_sync_master_enabled +0 +SET @start_global_value = @@global.rpl_semi_sync_master_enabled; +select @@global.rpl_semi_sync_master_enabled in (0,1); +@@global.rpl_semi_sync_master_enabled in (0,1) +1 +select @@session.rpl_semi_sync_master_enabled; +ERROR HY000: Variable 'rpl_semi_sync_master_enabled' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_enabled'; +Variable_name Value +rpl_semi_sync_master_enabled OFF +show session variables like 'rpl_semi_sync_master_enabled'; +Variable_name Value +rpl_semi_sync_master_enabled OFF +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_ENABLED OFF +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_ENABLED OFF +set global rpl_semi_sync_master_enabled=0; +set session rpl_semi_sync_master_enabled=0; +ERROR HY000: Variable 'rpl_semi_sync_master_enabled' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_master_enabled; +@@global.rpl_semi_sync_master_enabled +0 +select @@session.rpl_semi_sync_master_enabled; +ERROR HY000: Variable 'rpl_semi_sync_master_enabled' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_enabled'; +Variable_name Value +rpl_semi_sync_master_enabled OFF +show session variables like 'rpl_semi_sync_master_enabled'; +Variable_name Value +rpl_semi_sync_master_enabled OFF +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_ENABLED OFF +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_ENABLED OFF +set global rpl_semi_sync_master_enabled=1; +set session rpl_semi_sync_master_enabled=1; +ERROR HY000: Variable 'rpl_semi_sync_master_enabled' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_master_enabled; +@@global.rpl_semi_sync_master_enabled +-1 +select @@session.rpl_semi_sync_master_enabled; +ERROR HY000: Variable 'rpl_semi_sync_master_enabled' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_enabled'; +Variable_name Value +rpl_semi_sync_master_enabled ON +show session variables like 'rpl_semi_sync_master_enabled'; +Variable_name Value +rpl_semi_sync_master_enabled ON +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_ENABLED ON +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_ENABLED ON +set global rpl_semi_sync_master_enabled=1.1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_enabled' +set global rpl_semi_sync_master_enabled=1e1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_enabled' +set global rpl_semi_sync_master_enabled="some text"; +ERROR 42000: Variable 'rpl_semi_sync_master_enabled' can't be set to the value of 'some text' +SET @@global.rpl_semi_sync_master_enabled = @start_global_value; +select @@global.rpl_semi_sync_master_enabled; +@@global.rpl_semi_sync_master_enabled +0 +UNINSTALL PLUGIN rpl_semi_sync_master; diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_timeout_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_timeout_basic.result new file mode 100644 index 00000000000..e77bcc1c12a --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_timeout_basic.result @@ -0,0 +1,54 @@ +INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; +select @@global.rpl_semi_sync_master_timeout; +@@global.rpl_semi_sync_master_timeout +10000 +SET @start_global_value = @@global.rpl_semi_sync_master_timeout; +Assuming value will not be more then 100 sec +select @@global.rpl_semi_sync_master_timeout between 1 and 100000; +@@global.rpl_semi_sync_master_timeout between 1 and 100000 +1 +select @@session.rpl_semi_sync_master_timeout; +ERROR HY000: Variable 'rpl_semi_sync_master_timeout' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_timeout'; +Variable_name Value +rpl_semi_sync_master_timeout 10000 +show session variables like 'rpl_semi_sync_master_timeout'; +Variable_name Value +rpl_semi_sync_master_timeout 10000 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_timeout'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TIMEOUT 10000 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_timeout'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TIMEOUT 10000 +set global rpl_semi_sync_master_timeout=42; +set session rpl_semi_sync_master_timeout=99; +ERROR HY000: Variable 'rpl_semi_sync_master_timeout' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_master_timeout; +@@global.rpl_semi_sync_master_timeout +42 +select @@session.rpl_semi_sync_master_timeout; +ERROR HY000: Variable 'rpl_semi_sync_master_timeout' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_timeout'; +Variable_name Value +rpl_semi_sync_master_timeout 42 +show session variables like 'rpl_semi_sync_master_timeout'; +Variable_name Value +rpl_semi_sync_master_timeout 42 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_timeout'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TIMEOUT 42 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_timeout'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TIMEOUT 42 +set global rpl_semi_sync_master_timeout=1.1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_timeout' +set global rpl_semi_sync_master_timeout=1e1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_timeout' +set global rpl_semi_sync_master_timeout="some text"; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_timeout' +SET @@global.rpl_semi_sync_master_timeout = @start_global_value; +select @@global.rpl_semi_sync_master_timeout; +@@global.rpl_semi_sync_master_timeout +10000 +UNINSTALL PLUGIN rpl_semi_sync_master; diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_trace_level_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_trace_level_basic.result new file mode 100644 index 00000000000..55df5f57d9e --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_trace_level_basic.result @@ -0,0 +1,72 @@ +INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; +select @@global.rpl_semi_sync_master_trace_level; +@@global.rpl_semi_sync_master_trace_level +32 +SET @start_global_value = @@global.rpl_semi_sync_master_trace_level; +select @@global.rpl_semi_sync_master_trace_level in (1,16,32,64); +@@global.rpl_semi_sync_master_trace_level in (1,16,32,64) +1 +select @@session.rpl_semi_sync_master_trace_level; +ERROR HY000: Variable 'rpl_semi_sync_master_trace_level' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_trace_level'; +Variable_name Value +rpl_semi_sync_master_trace_level 32 +show session variables like 'rpl_semi_sync_master_trace_level'; +Variable_name Value +rpl_semi_sync_master_trace_level 32 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TRACE_LEVEL 32 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TRACE_LEVEL 32 +set global rpl_semi_sync_master_trace_level=16; +set session rpl_semi_sync_master_trace_level=99; +ERROR HY000: Variable 'rpl_semi_sync_master_trace_level' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_master_trace_level; +@@global.rpl_semi_sync_master_trace_level +16 +select @@session.rpl_semi_sync_master_trace_level; +ERROR HY000: Variable 'rpl_semi_sync_master_trace_level' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_trace_level'; +Variable_name Value +rpl_semi_sync_master_trace_level 16 +show session variables like 'rpl_semi_sync_master_trace_level'; +Variable_name Value +rpl_semi_sync_master_trace_level 16 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TRACE_LEVEL 16 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TRACE_LEVEL 16 +NOTE: Value can also be set to values that are combinations of values +set global rpl_semi_sync_master_trace_level=42; +select @@global.rpl_semi_sync_master_trace_level; +@@global.rpl_semi_sync_master_trace_level +42 +select @@session.rpl_semi_sync_master_trace_level; +ERROR HY000: Variable 'rpl_semi_sync_master_trace_level' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_trace_level'; +Variable_name Value +rpl_semi_sync_master_trace_level 42 +show session variables like 'rpl_semi_sync_master_trace_level'; +Variable_name Value +rpl_semi_sync_master_trace_level 42 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TRACE_LEVEL 42 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_TRACE_LEVEL 42 +set global rpl_semi_sync_master_trace_level=1.1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_trace_level' +set global rpl_semi_sync_master_trace_level=1e1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_trace_level' +set global rpl_semi_sync_master_trace_level="some text"; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_trace_level' +SET @@global.rpl_semi_sync_master_trace_level = @start_global_value; +select @@global.rpl_semi_sync_master_trace_level; +@@global.rpl_semi_sync_master_trace_level +32 +UNINSTALL PLUGIN rpl_semi_sync_master; diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_wait_no_slave_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_wait_no_slave_basic.result new file mode 100644 index 00000000000..3d951b499ed --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_master_wait_no_slave_basic.result @@ -0,0 +1,73 @@ +INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; +select @@global.rpl_semi_sync_master_wait_no_slave; +@@global.rpl_semi_sync_master_wait_no_slave +1 +SET @start_global_value = @@global.rpl_semi_sync_master_wait_no_slave; +select @@global.rpl_semi_sync_master_wait_no_slave in (0,1); +@@global.rpl_semi_sync_master_wait_no_slave in (0,1) +1 +select @@session.rpl_semi_sync_master_wait_no_slave; +ERROR HY000: Variable 'rpl_semi_sync_master_wait_no_slave' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_wait_no_slave'; +Variable_name Value +rpl_semi_sync_master_wait_no_slave ON +show session variables like 'rpl_semi_sync_master_wait_no_slave'; +Variable_name Value +rpl_semi_sync_master_wait_no_slave ON +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE ON +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE ON +set global rpl_semi_sync_master_wait_no_slave=0; +set session rpl_semi_sync_master_wait_no_slave=0; +ERROR HY000: Variable 'rpl_semi_sync_master_wait_no_slave' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_master_wait_no_slave; +@@global.rpl_semi_sync_master_wait_no_slave +0 +select @@session.rpl_semi_sync_master_wait_no_slave; +ERROR HY000: Variable 'rpl_semi_sync_master_wait_no_slave' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_wait_no_slave'; +Variable_name Value +rpl_semi_sync_master_wait_no_slave OFF +show session variables like 'rpl_semi_sync_master_wait_no_slave'; +Variable_name Value +rpl_semi_sync_master_wait_no_slave OFF +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE OFF +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE OFF +set global rpl_semi_sync_master_wait_no_slave=1; +set session rpl_semi_sync_master_wait_no_slave=1; +ERROR HY000: Variable 'rpl_semi_sync_master_wait_no_slave' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_master_wait_no_slave; +@@global.rpl_semi_sync_master_wait_no_slave +1 +select @@session.rpl_semi_sync_master_wait_no_slave; +ERROR HY000: Variable 'rpl_semi_sync_master_wait_no_slave' is a GLOBAL variable +show global variables like 'rpl_semi_sync_master_wait_no_slave'; +Variable_name Value +rpl_semi_sync_master_wait_no_slave ON +show session variables like 'rpl_semi_sync_master_wait_no_slave'; +Variable_name Value +rpl_semi_sync_master_wait_no_slave ON +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE ON +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE ON +set global rpl_semi_sync_master_wait_no_slave=1.1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_wait_no_slave' +set global rpl_semi_sync_master_wait_no_slave=1e1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_master_wait_no_slave' +set global rpl_semi_sync_master_wait_no_slave="some text"; +ERROR 42000: Variable 'rpl_semi_sync_master_wait_no_slave' can't be set to the value of 'some text' +SET @@global.rpl_semi_sync_master_wait_no_slave = @start_global_value; +select @@global.rpl_semi_sync_master_wait_no_slave; +@@global.rpl_semi_sync_master_wait_no_slave +1 +UNINSTALL PLUGIN rpl_semi_sync_master; diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_enabled_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_enabled_basic.result new file mode 100644 index 00000000000..25688f55ecd --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_enabled_basic.result @@ -0,0 +1,73 @@ +INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; +select @@global.rpl_semi_sync_slave_enabled; +@@global.rpl_semi_sync_slave_enabled +0 +SET @start_global_value = @@global.rpl_semi_sync_slave_enabled; +select @@global.rpl_semi_sync_slave_enabled in (0,1); +@@global.rpl_semi_sync_slave_enabled in (0,1) +1 +select @@session.rpl_semi_sync_slave_enabled; +ERROR HY000: Variable 'rpl_semi_sync_slave_enabled' is a GLOBAL variable +show global variables like 'rpl_semi_sync_slave_enabled'; +Variable_name Value +rpl_semi_sync_slave_enabled OFF +show session variables like 'rpl_semi_sync_slave_enabled'; +Variable_name Value +rpl_semi_sync_slave_enabled OFF +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_ENABLED OFF +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_ENABLED OFF +set global rpl_semi_sync_slave_enabled=0; +set session rpl_semi_sync_slave_enabled=0; +ERROR HY000: Variable 'rpl_semi_sync_slave_enabled' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_slave_enabled; +@@global.rpl_semi_sync_slave_enabled +0 +select @@session.rpl_semi_sync_slave_enabled; +ERROR HY000: Variable 'rpl_semi_sync_slave_enabled' is a GLOBAL variable +show global variables like 'rpl_semi_sync_slave_enabled'; +Variable_name Value +rpl_semi_sync_slave_enabled OFF +show session variables like 'rpl_semi_sync_slave_enabled'; +Variable_name Value +rpl_semi_sync_slave_enabled OFF +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_ENABLED OFF +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_ENABLED OFF +set global rpl_semi_sync_slave_enabled=1; +set session rpl_semi_sync_slave_enabled=1; +ERROR HY000: Variable 'rpl_semi_sync_slave_enabled' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_slave_enabled; +@@global.rpl_semi_sync_slave_enabled +-1 +select @@session.rpl_semi_sync_slave_enabled; +ERROR HY000: Variable 'rpl_semi_sync_slave_enabled' is a GLOBAL variable +show global variables like 'rpl_semi_sync_slave_enabled'; +Variable_name Value +rpl_semi_sync_slave_enabled ON +show session variables like 'rpl_semi_sync_slave_enabled'; +Variable_name Value +rpl_semi_sync_slave_enabled ON +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_ENABLED ON +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_enabled'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_ENABLED ON +set global rpl_semi_sync_slave_enabled=1.1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_slave_enabled' +set global rpl_semi_sync_slave_enabled=1e1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_slave_enabled' +set global rpl_semi_sync_slave_enabled="some text"; +ERROR 42000: Variable 'rpl_semi_sync_slave_enabled' can't be set to the value of 'some text' +SET @@global.rpl_semi_sync_slave_enabled = @start_global_value; +select @@global.rpl_semi_sync_slave_enabled; +@@global.rpl_semi_sync_slave_enabled +0 +UNINSTALL PLUGIN rpl_semi_sync_slave; diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_trace_level_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_trace_level_basic.result new file mode 100644 index 00000000000..f7796309aea --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_slave_trace_level_basic.result @@ -0,0 +1,72 @@ +INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; +select @@global.rpl_semi_sync_slave_trace_level; +@@global.rpl_semi_sync_slave_trace_level +32 +SET @start_global_value = @@global.rpl_semi_sync_slave_trace_level; +select @@global.rpl_semi_sync_slave_trace_level in (1,16,32,64); +@@global.rpl_semi_sync_slave_trace_level in (1,16,32,64) +1 +select @@session.rpl_semi_sync_slave_trace_level; +ERROR HY000: Variable 'rpl_semi_sync_slave_trace_level' is a GLOBAL variable +show global variables like 'rpl_semi_sync_slave_trace_level'; +Variable_name Value +rpl_semi_sync_slave_trace_level 32 +show session variables like 'rpl_semi_sync_slave_trace_level'; +Variable_name Value +rpl_semi_sync_slave_trace_level 32 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL 32 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL 32 +set global rpl_semi_sync_slave_trace_level=16; +set session rpl_semi_sync_slave_trace_level=99; +ERROR HY000: Variable 'rpl_semi_sync_slave_trace_level' is a GLOBAL variable and should be set with SET GLOBAL +select @@global.rpl_semi_sync_slave_trace_level; +@@global.rpl_semi_sync_slave_trace_level +16 +select @@session.rpl_semi_sync_slave_trace_level; +ERROR HY000: Variable 'rpl_semi_sync_slave_trace_level' is a GLOBAL variable +show global variables like 'rpl_semi_sync_slave_trace_level'; +Variable_name Value +rpl_semi_sync_slave_trace_level 16 +show session variables like 'rpl_semi_sync_slave_trace_level'; +Variable_name Value +rpl_semi_sync_slave_trace_level 16 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL 16 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL 16 +NOTE: Value can also be set to values that are combinations of values +set global rpl_semi_sync_slave_trace_level=42; +select @@global.rpl_semi_sync_slave_trace_level; +@@global.rpl_semi_sync_slave_trace_level +42 +select @@session.rpl_semi_sync_slave_trace_level; +ERROR HY000: Variable 'rpl_semi_sync_slave_trace_level' is a GLOBAL variable +show global variables like 'rpl_semi_sync_slave_trace_level'; +Variable_name Value +rpl_semi_sync_slave_trace_level 42 +show session variables like 'rpl_semi_sync_slave_trace_level'; +Variable_name Value +rpl_semi_sync_slave_trace_level 42 +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL 42 +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_trace_level'; +VARIABLE_NAME VARIABLE_VALUE +RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL 42 +set global rpl_semi_sync_slave_trace_level=1.1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_slave_trace_level' +set global rpl_semi_sync_slave_trace_level=1e1; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_slave_trace_level' +set global rpl_semi_sync_slave_trace_level="some text"; +ERROR 42000: Incorrect argument type to variable 'rpl_semi_sync_slave_trace_level' +SET @@global.rpl_semi_sync_slave_trace_level = @start_global_value; +select @@global.rpl_semi_sync_slave_trace_level; +@@global.rpl_semi_sync_slave_trace_level +32 +UNINSTALL PLUGIN rpl_semi_sync_slave; diff --git a/mysql-test/suite/sys_vars/r/sql_log_update_basic.result b/mysql-test/suite/sys_vars/r/sql_log_update_basic.result index f48cf6870da..11cd039da00 100644 --- a/mysql-test/suite/sys_vars/r/sql_log_update_basic.result +++ b/mysql-test/suite/sys_vars/r/sql_log_update_basic.result @@ -23,15 +23,51 @@ SQL_LOG_UPDATE ON set global sql_log_update=1; Warnings: Note 1315 The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored -select @@global.sql_log_update; -@@global.sql_log_update -1 set session sql_log_update=ON; Warnings: Note 1315 The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored +select @@global.sql_log_update; +@@global.sql_log_update +1 select @@session.sql_log_update; @@session.sql_log_update 1 +show global variables like 'sql_log_update'; +Variable_name Value +sql_log_update ON +show session variables like 'sql_log_update'; +Variable_name Value +sql_log_update ON +select * from information_schema.global_variables where variable_name='sql_log_update'; +VARIABLE_NAME VARIABLE_VALUE +SQL_LOG_UPDATE ON +select * from information_schema.session_variables where variable_name='sql_log_update'; +VARIABLE_NAME VARIABLE_VALUE +SQL_LOG_UPDATE ON +set global sql_log_update=0; +Warnings: +Note 1315 The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored +set session sql_log_update=OFF; +Warnings: +Note 1315 The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored +select @@global.sql_log_update; +@@global.sql_log_update +0 +select @@session.sql_log_update; +@@session.sql_log_update +0 +show global variables like 'sql_log_update'; +Variable_name Value +sql_log_update OFF +show session variables like 'sql_log_update'; +Variable_name Value +sql_log_update OFF +select * from information_schema.global_variables where variable_name='sql_log_update'; +VARIABLE_NAME VARIABLE_VALUE +SQL_LOG_UPDATE OFF +select * from information_schema.session_variables where variable_name='sql_log_update'; +VARIABLE_NAME VARIABLE_VALUE +SQL_LOG_UPDATE OFF set global sql_log_update=1.1; ERROR 42000: Incorrect argument type to variable 'sql_log_update' set global sql_log_update=1e1; diff --git a/mysql-test/suite/sys_vars/r/sql_max_join_size_basic.result b/mysql-test/suite/sys_vars/r/sql_max_join_size_basic.result index 8aaea049b94..8ec2a60887f 100644 --- a/mysql-test/suite/sys_vars/r/sql_max_join_size_basic.result +++ b/mysql-test/suite/sys_vars/r/sql_max_join_size_basic.result @@ -23,15 +23,27 @@ SQL_MAX_JOIN_SIZE 18446744073709551615 set global sql_max_join_size=10; Warnings: Warning 1287 The syntax '@@sql_max_join_size' is deprecated and will be removed in MySQL 7.0. -select @@global.sql_max_join_size; -@@global.sql_max_join_size -10 set session sql_max_join_size=20; Warnings: Warning 1287 The syntax '@@sql_max_join_size' is deprecated and will be removed in MySQL 7.0. +select @@global.sql_max_join_size; +@@global.sql_max_join_size +10 select @@session.sql_max_join_size; @@session.sql_max_join_size 20 +show global variables like 'sql_max_join_size'; +Variable_name Value +sql_max_join_size 10 +show session variables like 'sql_max_join_size'; +Variable_name Value +sql_max_join_size 20 +select * from information_schema.global_variables where variable_name='sql_max_join_size'; +VARIABLE_NAME VARIABLE_VALUE +SQL_MAX_JOIN_SIZE 10 +select * from information_schema.session_variables where variable_name='sql_max_join_size'; +VARIABLE_NAME VARIABLE_VALUE +SQL_MAX_JOIN_SIZE 20 set global sql_max_join_size=1.1; ERROR 42000: Incorrect argument type to variable 'sql_max_join_size' set global sql_max_join_size=1e1; diff --git a/mysql-test/suite/sys_vars/r/sql_select_limit_basic.result b/mysql-test/suite/sys_vars/r/sql_select_limit_basic.result index c601fa148ce..78119cd9dff 100644 --- a/mysql-test/suite/sys_vars/r/sql_select_limit_basic.result +++ b/mysql-test/suite/sys_vars/r/sql_select_limit_basic.result @@ -21,13 +21,25 @@ select * from information_schema.session_variables where variable_name='sql_sele VARIABLE_NAME VARIABLE_VALUE SQL_SELECT_LIMIT 18446744073709551615 set global sql_select_limit=10; +set session sql_select_limit=20; select @@global.sql_select_limit; @@global.sql_select_limit 10 -set session sql_select_limit=20; select @@session.sql_select_limit; @@session.sql_select_limit 20 +show global variables like 'sql_select_limit'; +Variable_name Value +sql_select_limit 10 +show session variables like 'sql_select_limit'; +Variable_name Value +sql_select_limit 20 +select * from information_schema.global_variables where variable_name='sql_select_limit'; +VARIABLE_NAME VARIABLE_VALUE +SQL_SELECT_LIMIT 10 +select * from information_schema.session_variables where variable_name='sql_select_limit'; +VARIABLE_NAME VARIABLE_VALUE +SQL_SELECT_LIMIT 20 set global sql_select_limit=1.1; ERROR 42000: Incorrect argument type to variable 'sql_select_limit' set global sql_select_limit=1e1; diff --git a/mysql-test/suite/sys_vars/r/thread_cache_size_basic.result b/mysql-test/suite/sys_vars/r/thread_cache_size_basic.result index 1c802f1afdd..83501ca929b 100644 --- a/mysql-test/suite/sys_vars/r/thread_cache_size_basic.result +++ b/mysql-test/suite/sys_vars/r/thread_cache_size_basic.result @@ -23,6 +23,12 @@ set global thread_cache_size=1; select @@global.thread_cache_size; @@global.thread_cache_size 1 +select * from information_schema.global_variables where variable_name='thread_cache_size'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_CACHE_SIZE 1 +select * from information_schema.session_variables where variable_name='thread_cache_size'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_CACHE_SIZE 1 set session thread_cache_size=1; ERROR HY000: Variable 'thread_cache_size' is a GLOBAL variable and should be set with SET GLOBAL set global thread_cache_size=1.1; diff --git a/mysql-test/suite/sys_vars/t/all_vars-master.opt b/mysql-test/suite/sys_vars/t/all_vars-master.opt new file mode 100644 index 00000000000..3eec696ce86 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/all_vars-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT --loose-innodb diff --git a/mysql-test/suite/sys_vars/t/all_vars.test b/mysql-test/suite/sys_vars/t/all_vars.test index 6a18b5e3d37..0e831319898 100644 --- a/mysql-test/suite/sys_vars/t/all_vars.test +++ b/mysql-test/suite/sys_vars/t/all_vars.test @@ -15,6 +15,22 @@ --source include/not_embedded.inc +# 2010-01-28 OBN Added support to load 'innodb' and 'semisync' if possible. +# As we need to have there variables loaded if the components exist but do +# not want the test skiped if they are not, we cannot use the 'have_xxx' mecanizm. +# Added an 'all_vars-master.opt' file that includes +# "$SEMISYNC_PLUGIN_OPT --loose-innodb" (see $SEMISYNC_PLUGIN_OPT setting in mysql-test-run.pl) +# and logic similar to 'include/have_semisync_plugin.inc' that will load semisync plugin +if (`SELECT @@have_dynamic_loading = 'YES' AND LENGTH('$SEMISYNC_MASTER_PLUGIN') > 0`) +{ + --disable_query_log + eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN'; + eval INSTALL PLUGIN rpl_semi_sync_slave SONAME '$SEMISYNC_SLAVE_PLUGIN'; + --enable_query_log +} + + + # # This test verifies that *all* system variables are tested # by the sys_vars suite. For every system variable @@ -58,3 +74,12 @@ select variable_name as `There should be *no* variables listed below:` from t2 drop table t1; drop table t2; + +# Unloading the semisync plugins in case they were loaded +if (`SELECT @@have_dynamic_loading = 'YES' AND LENGTH('$SEMISYNC_MASTER_PLUGIN') > 0`) +{ + --disable_query_log + UNINSTALL PLUGIN rpl_semi_sync_master; + UNINSTALL PLUGIN rpl_semi_sync_slave; + --enable_query_log +} diff --git a/mysql-test/suite/sys_vars/t/innodb_adaptive_flushing_basic.test b/mysql-test/suite/sys_vars/t/innodb_adaptive_flushing_basic.test new file mode 100644 index 00000000000..236b652f9c6 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_adaptive_flushing_basic.test @@ -0,0 +1,70 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_adaptive_flushing; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'ON' and 'OFF' +select @@global.innodb_adaptive_flushing in (0, 1); +select @@global.innodb_adaptive_flushing; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_adaptive_flushing; +show global variables like 'innodb_adaptive_flushing'; +show session variables like 'innodb_adaptive_flushing'; +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; + +# +# show that it's writable +# +set global innodb_adaptive_flushing='OFF'; +select @@global.innodb_adaptive_flushing; +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +set @@global.innodb_adaptive_flushing=1; +select @@global.innodb_adaptive_flushing; +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +set global innodb_adaptive_flushing=0; +select @@global.innodb_adaptive_flushing; +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +set @@global.innodb_adaptive_flushing='ON'; +select @@global.innodb_adaptive_flushing; +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +--error ER_GLOBAL_VARIABLE +set session innodb_adaptive_flushing='OFF'; +--error ER_GLOBAL_VARIABLE +set @@session.innodb_adaptive_flushing='ON'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_adaptive_flushing=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_adaptive_flushing=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_adaptive_flushing=2; +--echo NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_adaptive_flushing=-3; +select @@global.innodb_adaptive_flushing; +select * from information_schema.global_variables where variable_name='innodb_adaptive_flushing'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_flushing'; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_adaptive_flushing='AUTO'; + +# +# Cleanup +# + +SET @@global.innodb_adaptive_flushing = @start_global_value; +SELECT @@global.innodb_adaptive_flushing; diff --git a/mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_basic.test b/mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_basic.test new file mode 100644 index 00000000000..d6d48ab460c --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_adaptive_hash_index_basic.test @@ -0,0 +1,70 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_adaptive_hash_index; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'ON' and 'OFF' +select @@global.innodb_adaptive_hash_index in (0, 1); +select @@global.innodb_adaptive_hash_index; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_adaptive_hash_index; +show global variables like 'innodb_adaptive_hash_index'; +show session variables like 'innodb_adaptive_hash_index'; +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; + +# +# show that it's writable +# +set global innodb_adaptive_hash_index='OFF'; +select @@global.innodb_adaptive_hash_index; +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +set @@global.innodb_adaptive_hash_index=1; +select @@global.innodb_adaptive_hash_index; +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +set global innodb_adaptive_hash_index=0; +select @@global.innodb_adaptive_hash_index; +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +set @@global.innodb_adaptive_hash_index='ON'; +select @@global.innodb_adaptive_hash_index; +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +--error ER_GLOBAL_VARIABLE +set session innodb_adaptive_hash_index='OFF'; +--error ER_GLOBAL_VARIABLE +set @@session.innodb_adaptive_hash_index='ON'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_adaptive_hash_index=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_adaptive_hash_index=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_adaptive_hash_index=2; +--echo NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_adaptive_hash_index=-3; +select @@global.innodb_adaptive_hash_index; +select * from information_schema.global_variables where variable_name='innodb_adaptive_hash_index'; +select * from information_schema.session_variables where variable_name='innodb_adaptive_hash_index'; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_adaptive_hash_index='AUTO'; + +# +# Cleanup +# + +SET @@global.innodb_adaptive_hash_index = @start_global_value; +SELECT @@global.innodb_adaptive_hash_index; diff --git a/mysql-test/suite/sys_vars/t/innodb_change_buffering_basic.test b/mysql-test/suite/sys_vars/t/innodb_change_buffering_basic.test new file mode 100644 index 00000000000..65e36aa9cb3 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_change_buffering_basic.test @@ -0,0 +1,59 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_change_buffering; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'inserts' and 'none' +select @@global.innodb_change_buffering in ('inserts', 'none'); +select @@global.innodb_change_buffering; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_change_buffering; +show global variables like 'innodb_change_buffering'; +show session variables like 'innodb_change_buffering'; +select * from information_schema.global_variables where variable_name='innodb_change_buffering'; +select * from information_schema.session_variables where variable_name='innodb_change_buffering'; + +# +# show that it's writable +# +set global innodb_change_buffering='none'; +select @@global.innodb_change_buffering; +select * from information_schema.global_variables where variable_name='innodb_change_buffering'; +select * from information_schema.session_variables where variable_name='innodb_change_buffering'; +set @@global.innodb_change_buffering='inserts'; +select @@global.innodb_change_buffering; +select * from information_schema.global_variables where variable_name='innodb_change_buffering'; +select * from information_schema.session_variables where variable_name='innodb_change_buffering'; +--error ER_GLOBAL_VARIABLE +set session innodb_change_buffering='some'; +--error ER_GLOBAL_VARIABLE +set @@session.innodb_change_buffering='some'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_change_buffering=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_change_buffering=1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_change_buffering=-2; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_change_buffering=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_change_buffering='some'; + +# +# Cleanup +# + +SET @@global.innodb_change_buffering = @start_global_value; +SELECT @@global.innodb_change_buffering; diff --git a/mysql-test/suite/sys_vars/t/innodb_file_format_basic.test b/mysql-test/suite/sys_vars/t/innodb_file_format_basic.test new file mode 100644 index 00000000000..bfc092f2f05 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_file_format_basic.test @@ -0,0 +1,55 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_file_format; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'Antelope' and 'Barracuda' +select @@global.innodb_file_format in ('Antelope', 'Barracuda'); +select @@global.innodb_file_format; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_file_format; +show global variables like 'innodb_file_format'; +show session variables like 'innodb_file_format'; +select * from information_schema.global_variables where variable_name='innodb_file_format'; +select * from information_schema.session_variables where variable_name='innodb_file_format'; + +# +# show that it's writable +# +set global innodb_file_format='Antelope'; +select @@global.innodb_file_format; +select * from information_schema.global_variables where variable_name='innodb_file_format'; +select * from information_schema.session_variables where variable_name='innodb_file_format'; +set @@global.innodb_file_format='Barracuda'; +select @@global.innodb_file_format; +select * from information_schema.global_variables where variable_name='innodb_file_format'; +select * from information_schema.session_variables where variable_name='innodb_file_format'; +--error ER_GLOBAL_VARIABLE +set session innodb_file_format='Salmon'; +--error ER_GLOBAL_VARIABLE +set @@session.innodb_file_format='Salmon'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_file_format=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_file_format=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_file_format='Salmon'; + +# +# Cleanup +# + +SET @@global.innodb_file_format = @start_global_value; +SELECT @@global.innodb_file_format; diff --git a/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test b/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test new file mode 100644 index 00000000000..4c60957561c --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_file_format_check_basic.test @@ -0,0 +1,55 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_file_format_check; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'Antelope' and 'Barracuda' +select @@global.innodb_file_format_check in ('Antelope', 'Barracuda'); +select @@global.innodb_file_format_check; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_file_format_check; +show global variables like 'innodb_file_format_check'; +show session variables like 'innodb_file_format_check'; +select * from information_schema.global_variables where variable_name='innodb_file_format_check'; +select * from information_schema.session_variables where variable_name='innodb_file_format_check'; + +# +# show that it's writable +# +set global innodb_file_format_check='Antelope'; +select @@global.innodb_file_format_check; +select * from information_schema.global_variables where variable_name='innodb_file_format_check'; +select * from information_schema.session_variables where variable_name='innodb_file_format_check'; +set @@global.innodb_file_format_check='Barracuda'; +select @@global.innodb_file_format_check; +select * from information_schema.global_variables where variable_name='innodb_file_format_check'; +select * from information_schema.session_variables where variable_name='innodb_file_format_check'; +--error ER_GLOBAL_VARIABLE +set session innodb_file_format_check='Salmon'; +--error ER_GLOBAL_VARIABLE +set @@session.innodb_file_format_check='Salmon'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_file_format_check=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_file_format_check=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_file_format_check='Salmon'; + +# +# Cleanup +# + +SET @@global.innodb_file_format_check = @start_global_value; +SELECT @@global.innodb_file_format_check; diff --git a/mysql-test/suite/sys_vars/t/innodb_io_capacity_basic.test b/mysql-test/suite/sys_vars/t/innodb_io_capacity_basic.test new file mode 100644 index 00000000000..3f00b50cf08 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_io_capacity_basic.test @@ -0,0 +1,58 @@ + + +# 2010-01-27 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_io_capacity; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid value 100 or more +select @@global.innodb_io_capacity > 99; +select @@global.innodb_io_capacity; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_io_capacity; +show global variables like 'innodb_io_capacity'; +show session variables like 'innodb_io_capacity'; +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +select * from information_schema.session_variables where variable_name='innodb_io_capacity'; + +# +# show that it's writable +# +set global innodb_io_capacity=123; +select @@global.innodb_io_capacity; +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +select * from information_schema.session_variables where variable_name='innodb_io_capacity'; +--error ER_GLOBAL_VARIABLE +set session innodb_io_capacity=444; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_io_capacity=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_io_capacity=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_io_capacity="foo"; + +set global innodb_io_capacity=7; +select @@global.innodb_io_capacity; +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; +set global innodb_io_capacity=-7; +select @@global.innodb_io_capacity; +select * from information_schema.global_variables where variable_name='innodb_io_capacity'; + +# +# min/max values +# +set global innodb_io_capacity=100; +select @@global.innodb_io_capacity; + +SET @@global.innodb_io_capacity = @start_global_value; +SELECT @@global.innodb_io_capacity; diff --git a/mysql-test/suite/sys_vars/t/innodb_old_blocks_pct_basic.test b/mysql-test/suite/sys_vars/t/innodb_old_blocks_pct_basic.test new file mode 100644 index 00000000000..0dcef3bb09f --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_old_blocks_pct_basic.test @@ -0,0 +1,63 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_old_blocks_pct; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are between 5 and 95 +select @@global.innodb_old_blocks_pct between 5 and 95; +select @@global.innodb_old_blocks_pct; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_old_blocks_pct; +show global variables like 'innodb_old_blocks_pct'; +show session variables like 'innodb_old_blocks_pct'; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +select * from information_schema.session_variables where variable_name='innodb_old_blocks_pct'; + +# +# show that it's writable +# +set global innodb_old_blocks_pct=10; +select @@global.innodb_old_blocks_pct; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +select * from information_schema.session_variables where variable_name='innodb_old_blocks_pct'; +--error ER_GLOBAL_VARIABLE +set session innodb_old_blocks_pct=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_old_blocks_pct=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_old_blocks_pct=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_old_blocks_pct="foo"; + +set global innodb_old_blocks_pct=4; +select @@global.innodb_old_blocks_pct; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +set global innodb_old_blocks_pct=-7; +select @@global.innodb_old_blocks_pct; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; +set global innodb_old_blocks_pct=96; +select @@global.innodb_old_blocks_pct; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_pct'; + +# +# min/max values +# +set global innodb_old_blocks_pct=5; +select @@global.innodb_old_blocks_pct; +set global innodb_old_blocks_pct=95; +select @@global.innodb_old_blocks_pct; + +SET @@global.innodb_old_blocks_pct = @start_global_value; +SELECT @@global.innodb_old_blocks_pct; diff --git a/mysql-test/suite/sys_vars/t/innodb_old_blocks_time_basic.test b/mysql-test/suite/sys_vars/t/innodb_old_blocks_time_basic.test new file mode 100644 index 00000000000..3efec2bbf15 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_old_blocks_time_basic.test @@ -0,0 +1,52 @@ + +# +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_old_blocks_time; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are zero or above +select @@global.innodb_old_blocks_time >=0; +select @@global.innodb_old_blocks_time; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_old_blocks_time; +show global variables like 'innodb_old_blocks_time'; +show session variables like 'innodb_old_blocks_time'; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_time'; +select * from information_schema.session_variables where variable_name='innodb_old_blocks_time'; + +# +# show that it's writable +# +set global innodb_old_blocks_time=10; +select @@global.innodb_old_blocks_time; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_time'; +select * from information_schema.session_variables where variable_name='innodb_old_blocks_time'; +--error ER_GLOBAL_VARIABLE +set session innodb_old_blocks_time=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_old_blocks_time=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_old_blocks_time=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_old_blocks_time="foo"; + +set global innodb_old_blocks_time=-7; +select @@global.innodb_old_blocks_time; +select * from information_schema.global_variables where variable_name='innodb_old_blocks_time'; + +# +# cleanup +# +SET @@global.innodb_old_blocks_time = @start_global_value; +SELECT @@global.innodb_old_blocks_time; diff --git a/mysql-test/suite/sys_vars/t/innodb_read_ahead_threshold_basic.test b/mysql-test/suite/sys_vars/t/innodb_read_ahead_threshold_basic.test new file mode 100644 index 00000000000..1298a28b3d3 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_read_ahead_threshold_basic.test @@ -0,0 +1,60 @@ + + +# 2010-01-27 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_read_ahead_threshold; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are between 0 and 64 +select @@global.innodb_read_ahead_threshold between 0 and 64; +select @@global.innodb_read_ahead_threshold; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_read_ahead_threshold; +show global variables like 'innodb_read_ahead_threshold'; +show session variables like 'innodb_read_ahead_threshold'; +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +select * from information_schema.session_variables where variable_name='innodb_read_ahead_threshold'; + +# +# show that it's writable +# +set global innodb_read_ahead_threshold=10; +select @@global.innodb_read_ahead_threshold; +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +select * from information_schema.session_variables where variable_name='innodb_read_ahead_threshold'; +--error ER_GLOBAL_VARIABLE +set session innodb_read_ahead_threshold=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_read_ahead_threshold=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_read_ahead_threshold=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_read_ahead_threshold="foo"; + +set global innodb_read_ahead_threshold=-7; +select @@global.innodb_read_ahead_threshold; +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; +set global innodb_read_ahead_threshold=96; +select @@global.innodb_read_ahead_threshold; +select * from information_schema.global_variables where variable_name='innodb_read_ahead_threshold'; + +# +# min/max values +# +set global innodb_read_ahead_threshold=0; +select @@global.innodb_read_ahead_threshold; +set global innodb_read_ahead_threshold=64; +select @@global.innodb_read_ahead_threshold; + +SET @@global.innodb_read_ahead_threshold = @start_global_value; +SELECT @@global.innodb_read_ahead_threshold; diff --git a/mysql-test/suite/sys_vars/t/innodb_read_io_threads_basic.test b/mysql-test/suite/sys_vars/t/innodb_read_io_threads_basic.test new file mode 100644 index 00000000000..14426395d6c --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_read_io_threads_basic.test @@ -0,0 +1,26 @@ + +# +# 2010-01-27 OBN - Added (this variable is also covered in innodb_file_io_threads_basic.test) +# + +--source include/have_innodb.inc + +# +# show the global and session values; +# +select @@global.innodb_read_io_threads; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_read_io_threads; +show global variables like 'innodb_read_io_threads'; +show session variables like 'innodb_read_io_threads'; +select * from information_schema.global_variables where variable_name='innodb_read_io_threads'; +select * from information_schema.session_variables where variable_name='innodb_read_io_threads'; + +# +# show that it's read-only +# +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set global innodb_read_io_threads=1; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set session innodb_read_io_threads=1; + diff --git a/mysql-test/suite/sys_vars/t/innodb_replication_delay_basic.test b/mysql-test/suite/sys_vars/t/innodb_replication_delay_basic.test new file mode 100644 index 00000000000..e495de46611 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_replication_delay_basic.test @@ -0,0 +1,52 @@ + +# +# 2010-01-27 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_replication_delay; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are zero or above +select @@global.innodb_replication_delay >=0; +select @@global.innodb_replication_delay; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_replication_delay; +show global variables like 'innodb_replication_delay'; +show session variables like 'innodb_replication_delay'; +select * from information_schema.global_variables where variable_name='innodb_replication_delay'; +select * from information_schema.session_variables where variable_name='innodb_replication_delay'; + +# +# show that it's writable +# +set global innodb_replication_delay=10; +select @@global.innodb_replication_delay; +select * from information_schema.global_variables where variable_name='innodb_replication_delay'; +select * from information_schema.session_variables where variable_name='innodb_replication_delay'; +--error ER_GLOBAL_VARIABLE +set session innodb_replication_delay=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_replication_delay=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_replication_delay=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_replication_delay="foo"; + +set global innodb_replication_delay=-7; +select @@global.innodb_replication_delay; +select * from information_schema.global_variables where variable_name='innodb_replication_delay'; + +# +# cleanup +# +SET @@global.innodb_replication_delay = @start_global_value; +SELECT @@global.innodb_replication_delay; diff --git a/mysql-test/suite/sys_vars/t/innodb_spin_wait_delay_basic.test b/mysql-test/suite/sys_vars/t/innodb_spin_wait_delay_basic.test new file mode 100644 index 00000000000..8f2eee08b6a --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_spin_wait_delay_basic.test @@ -0,0 +1,52 @@ + +# +# 2010-01-27 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_spin_wait_delay; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are zero or above +select @@global.innodb_spin_wait_delay >=0; +select @@global.innodb_spin_wait_delay; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_spin_wait_delay; +show global variables like 'innodb_spin_wait_delay'; +show session variables like 'innodb_spin_wait_delay'; +select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; +select * from information_schema.session_variables where variable_name='innodb_spin_wait_delay'; + +# +# show that it's writable +# +set global innodb_spin_wait_delay=10; +select @@global.innodb_spin_wait_delay; +select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; +select * from information_schema.session_variables where variable_name='innodb_spin_wait_delay'; +--error ER_GLOBAL_VARIABLE +set session innodb_spin_wait_delay=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_spin_wait_delay=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_spin_wait_delay=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_spin_wait_delay="foo"; + +set global innodb_spin_wait_delay=-7; +select @@global.innodb_spin_wait_delay; +select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; + +# +# cleanup +# +SET @@global.innodb_spin_wait_delay = @start_global_value; +SELECT @@global.innodb_spin_wait_delay; diff --git a/mysql-test/suite/sys_vars/t/innodb_stats_on_metadata_basic.test b/mysql-test/suite/sys_vars/t/innodb_stats_on_metadata_basic.test new file mode 100644 index 00000000000..e6d59997ac6 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_stats_on_metadata_basic.test @@ -0,0 +1,70 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_stats_on_metadata; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are 'ON' and 'OFF' +select @@global.innodb_stats_on_metadata in (0, 1); +select @@global.innodb_stats_on_metadata; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_stats_on_metadata; +show global variables like 'innodb_stats_on_metadata'; +show session variables like 'innodb_stats_on_metadata'; +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; + +# +# show that it's writable +# +set global innodb_stats_on_metadata='OFF'; +select @@global.innodb_stats_on_metadata; +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +set @@global.innodb_stats_on_metadata=1; +select @@global.innodb_stats_on_metadata; +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +set global innodb_stats_on_metadata=0; +select @@global.innodb_stats_on_metadata; +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +set @@global.innodb_stats_on_metadata='ON'; +select @@global.innodb_stats_on_metadata; +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +--error ER_GLOBAL_VARIABLE +set session innodb_stats_on_metadata='OFF'; +--error ER_GLOBAL_VARIABLE +set @@session.innodb_stats_on_metadata='ON'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_stats_on_metadata=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_stats_on_metadata=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_stats_on_metadata=2; +--echo NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_stats_on_metadata=-3; +select @@global.innodb_stats_on_metadata; +select * from information_schema.global_variables where variable_name='innodb_stats_on_metadata'; +select * from information_schema.session_variables where variable_name='innodb_stats_on_metadata'; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_stats_on_metadata='AUTO'; + +# +# Cleanup +# + +SET @@global.innodb_stats_on_metadata = @start_global_value; +SELECT @@global.innodb_stats_on_metadata; diff --git a/mysql-test/suite/sys_vars/t/innodb_stats_sample_pages_basic.test b/mysql-test/suite/sys_vars/t/innodb_stats_sample_pages_basic.test new file mode 100644 index 00000000000..2c91f11405d --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_stats_sample_pages_basic.test @@ -0,0 +1,52 @@ + +# +# 2010-01-27 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_stats_sample_pages; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are one or above +select @@global.innodb_stats_sample_pages >=1; +select @@global.innodb_stats_sample_pages; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_stats_sample_pages; +show global variables like 'innodb_stats_sample_pages'; +show session variables like 'innodb_stats_sample_pages'; +select * from information_schema.global_variables where variable_name='innodb_stats_sample_pages'; +select * from information_schema.session_variables where variable_name='innodb_stats_sample_pages'; + +# +# show that it's writable +# +set global innodb_stats_sample_pages=10; +select @@global.innodb_stats_sample_pages; +select * from information_schema.global_variables where variable_name='innodb_stats_sample_pages'; +select * from information_schema.session_variables where variable_name='innodb_stats_sample_pages'; +--error ER_GLOBAL_VARIABLE +set session innodb_stats_sample_pages=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_stats_sample_pages=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_stats_sample_pages=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_stats_sample_pages="foo"; + +set global innodb_stats_sample_pages=-7; +select @@global.innodb_stats_sample_pages; +select * from information_schema.global_variables where variable_name='innodb_stats_sample_pages'; + +# +# cleanup +# +SET @@global.innodb_stats_sample_pages = @start_global_value; +SELECT @@global.innodb_stats_sample_pages; diff --git a/mysql-test/suite/sys_vars/t/innodb_strict_mode_basic.test b/mysql-test/suite/sys_vars/t/innodb_strict_mode_basic.test new file mode 100644 index 00000000000..53fbdca2d32 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_strict_mode_basic.test @@ -0,0 +1,84 @@ + + +# 2010-01-25 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_strict_mode; +SELECT @start_global_value; + +# +# exists as global and session +# +--echo Valid values are 'ON' and 'OFF' +select @@global.innodb_strict_mode in (0, 1); +select @@global.innodb_strict_mode; +select @@session.innodb_strict_mode in (0, 1); +select @@session.innodb_strict_mode; +show global variables like 'innodb_strict_mode'; +show session variables like 'innodb_strict_mode'; +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; + +# +# show that it's writable +# +set global innodb_strict_mode='OFF'; +set session innodb_strict_mode='OFF'; +select @@global.innodb_strict_mode; +select @@session.innodb_strict_mode; +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +set @@global.innodb_strict_mode=1; +set @@session.innodb_strict_mode=1; +select @@global.innodb_strict_mode; +select @@session.innodb_strict_mode; +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +set global innodb_strict_mode=0; +set session innodb_strict_mode=0; +select @@global.innodb_strict_mode; +select @@session.innodb_strict_mode; +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; +set @@global.innodb_strict_mode='ON'; +set @@session.innodb_strict_mode='ON'; +select @@global.innodb_strict_mode; +select @@session.innodb_strict_mode; +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_strict_mode=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set session innodb_strict_mode=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_strict_mode=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set session innodb_strict_mode=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_strict_mode=2; +--error ER_WRONG_VALUE_FOR_VAR +set session innodb_strict_mode=2; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_strict_mode='AUTO'; +--error ER_WRONG_VALUE_FOR_VAR +set session innodb_strict_mode='AUTO'; +--echo NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643) +set global innodb_strict_mode=-3; +set session innodb_strict_mode=-7; +select @@global.innodb_strict_mode; +select @@session.innodb_strict_mode; +select * from information_schema.global_variables where variable_name='innodb_strict_mode'; +select * from information_schema.session_variables where variable_name='innodb_strict_mode'; + +# +# Cleanup +# + +SET @@global.innodb_strict_mode = @start_global_value; +SELECT @@global.innodb_strict_mode; diff --git a/mysql-test/suite/sys_vars/t/innodb_thread_sleep_delay_basic.test b/mysql-test/suite/sys_vars/t/innodb_thread_sleep_delay_basic.test new file mode 100644 index 00000000000..7156f309a64 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_thread_sleep_delay_basic.test @@ -0,0 +1,52 @@ + +# +# 2010-01-27 - Added +# + +--source include/have_innodb.inc + +SET @start_global_value = @@global.innodb_thread_sleep_delay; +SELECT @start_global_value; + +# +# exists as global only +# +--echo Valid values are zero or above +select @@global.innodb_thread_sleep_delay >=0; +select @@global.innodb_thread_sleep_delay; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_thread_sleep_delay; +show global variables like 'innodb_thread_sleep_delay'; +show session variables like 'innodb_thread_sleep_delay'; +select * from information_schema.global_variables where variable_name='innodb_thread_sleep_delay'; +select * from information_schema.session_variables where variable_name='innodb_thread_sleep_delay'; + +# +# show that it's writable +# +set global innodb_thread_sleep_delay=10; +select @@global.innodb_thread_sleep_delay; +select * from information_schema.global_variables where variable_name='innodb_thread_sleep_delay'; +select * from information_schema.session_variables where variable_name='innodb_thread_sleep_delay'; +--error ER_GLOBAL_VARIABLE +set session innodb_thread_sleep_delay=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_thread_sleep_delay=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_thread_sleep_delay=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_thread_sleep_delay="foo"; + +set global innodb_thread_sleep_delay=-7; +select @@global.innodb_thread_sleep_delay; +select * from information_schema.global_variables where variable_name='innodb_thread_sleep_delay'; + +# +# cleanup +# +SET @@global.innodb_thread_sleep_delay = @start_global_value; +SELECT @@global.innodb_thread_sleep_delay; diff --git a/mysql-test/suite/sys_vars/t/innodb_use_sys_malloc_basic.test b/mysql-test/suite/sys_vars/t/innodb_use_sys_malloc_basic.test new file mode 100644 index 00000000000..21f3e904547 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_use_sys_malloc_basic.test @@ -0,0 +1,28 @@ + +# +# 2010-01-27 OBN - Added +# + +--source include/have_innodb.inc + +# +# show the global and session values; +# +--echo Valid values are 'ON' and 'OFF' +select @@global.innodb_adaptive_flushing in (0, 1); +select @@global.innodb_use_sys_malloc; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_use_sys_malloc; +show global variables like 'innodb_use_sys_malloc'; +show session variables like 'innodb_use_sys_malloc'; +select * from information_schema.global_variables where variable_name='innodb_use_sys_malloc'; +select * from information_schema.session_variables where variable_name='innodb_use_sys_malloc'; + +# +# show that it's read-only +# +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set global innodb_use_sys_malloc=1; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set session innodb_use_sys_malloc=1; + diff --git a/mysql-test/suite/sys_vars/t/innodb_version_basic.test b/mysql-test/suite/sys_vars/t/innodb_version_basic.test new file mode 100644 index 00000000000..6ee2adf6cf9 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_version_basic.test @@ -0,0 +1,28 @@ + +# +# 2010-01-27 OBN - Added +# + +--source include/have_innodb.inc + +# +# show the global and session values; +# +--let $inno_ver= `select @@global.innodb_version` +--replace_result $inno_ver x.y.z +select @@global.innodb_version; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_version; +--echo show global variables like 'innodb_version' disabled so to not change with every version; +--echo show session variables like 'innodb_version' disabled so to not change with every version; +select VARIABLE_VALUE=@@global.innodb_version from information_schema.global_variables where variable_name='innodb_version'; +select VARIABLE_VALUE=@@global.innodb_version from information_schema.session_variables where variable_name='innodb_version'; + +# +# show that it's read-only +# +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set global innodb_version=1; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set session innodb_version=1; + diff --git a/mysql-test/suite/sys_vars/t/innodb_write_io_threads_basic.test b/mysql-test/suite/sys_vars/t/innodb_write_io_threads_basic.test new file mode 100644 index 00000000000..8efa6576a66 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_write_io_threads_basic.test @@ -0,0 +1,26 @@ + +# +# 2010-01-27 OBN - Added (this variable is also covered in innodb_file_io_threads_basic.test) +# + +--source include/have_innodb.inc + +# +# show the global and session values; +# +select @@global.innodb_write_io_threads; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_write_io_threads; +show global variables like 'innodb_write_io_threads'; +show session variables like 'innodb_write_io_threads'; +select * from information_schema.global_variables where variable_name='innodb_write_io_threads'; +select * from information_schema.session_variables where variable_name='innodb_write_io_threads'; + +# +# show that it's read-only +# +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set global innodb_write_io_threads=1; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set session innodb_write_io_threads=1; + diff --git a/mysql-test/suite/sys_vars/t/last_insert_id_basic.test b/mysql-test/suite/sys_vars/t/last_insert_id_basic.test index a2e0116f11d..ca971c660ca 100644 --- a/mysql-test/suite/sys_vars/t/last_insert_id_basic.test +++ b/mysql-test/suite/sys_vars/t/last_insert_id_basic.test @@ -2,6 +2,8 @@ # # exists as a session only # +# 2010-01-20 OBN - Added check for variable value matching I_S tables +# --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@global.last_insert_id; select @@session.last_insert_id; @@ -13,10 +15,16 @@ select * from information_schema.session_variables where variable_name='last_ins # # show that it's writable # -set session last_insert_id=1; -select @@session.last_insert_id; --error ER_LOCAL_VARIABLE -set global last_insert_id=1; +set global last_insert_id=99; +set session last_insert_id=42; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@global.last_insert_id; +select @@session.last_insert_id; +show global variables like 'last_insert_id'; +show session variables like 'last_insert_id'; +select * from information_schema.global_variables where variable_name='last_insert_id'; +select * from information_schema.session_variables where variable_name='last_insert_id'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/lc_messages_basic.test b/mysql-test/suite/sys_vars/t/lc_messages_basic.test index f8a25738285..a76381e8cb4 100644 --- a/mysql-test/suite/sys_vars/t/lc_messages_basic.test +++ b/mysql-test/suite/sys_vars/t/lc_messages_basic.test @@ -1,3 +1,8 @@ +# +# 2010-01-20 OBN - Added check for variable value matching I_S tables +# + + SET @start_global_value = @@global.lc_messages; SELECT @start_global_value; @@ -19,9 +24,11 @@ select @@global.lc_messages; set session lc_messages=2; select @@session.lc_messages; set global lc_messages="en_US"; -select @@global.lc_messages; set session lc_messages="en_GB"; +select @@global.lc_messages; select @@session.lc_messages; +select * from information_schema.global_variables where variable_name='lc_messages'; +select * from information_schema.session_variables where variable_name='lc_messages'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/log_slow_queries_basic.test b/mysql-test/suite/sys_vars/t/log_slow_queries_basic.test index 95dda5b73c9..22fe4dfaa6e 100644 --- a/mysql-test/suite/sys_vars/t/log_slow_queries_basic.test +++ b/mysql-test/suite/sys_vars/t/log_slow_queries_basic.test @@ -11,7 +11,7 @@ # Creation Date: 2008-03-16 # # Author: Salman Rawala # # # -# Description: Test Cases of Dynamic System Variable "log_slow_queries" # +# Description: Test Cases of Dynamic System Variable "log_slow_queries" # # that checks behavior of this variable in the following ways # # * Default Value # # * Valid & Invalid values # @@ -21,6 +21,10 @@ # Reference: http://dev.mysql.com/doc/refman/5.1/en/ # # server-options.html#option_mysqld_event-scheduler # # # +# # +# 2010-01-20 OBN - Added check for variable value matching I_S tables after # +# variable value change # +# # ################################################################################ --source include/load_sysvars.inc @@ -116,8 +120,14 @@ WHERE VARIABLE_NAME='log_slow_queries'; SET @@global.log_slow_queries = 0; SELECT @@global.log_slow_queries; +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; SET @@global.log_slow_queries = 1; SELECT @@global.log_slow_queries; +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; --echo '#---------------------FN_DYNVARS_004_07----------------------#' ################################################################### @@ -126,8 +136,14 @@ SELECT @@global.log_slow_queries; SET @@global.log_slow_queries = TRUE; SELECT @@global.log_slow_queries; +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; SET @@global.log_slow_queries = FALSE; SELECT @@global.log_slow_queries; +SELECT IF(@@global.log_slow_queries, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='log_slow_queries'; --echo '#---------------------FN_DYNVARS_004_08----------------------#' ############################################################################## diff --git a/mysql-test/suite/sys_vars/t/lower_case_file_system_basic.test b/mysql-test/suite/sys_vars/t/lower_case_file_system_basic.test index 6c15f188880..6c2af693128 100644 --- a/mysql-test/suite/sys_vars/t/lower_case_file_system_basic.test +++ b/mysql-test/suite/sys_vars/t/lower_case_file_system_basic.test @@ -1,17 +1,25 @@ # # only global # +# 2010-01-20 OBN - Modified to include check for valid values +# - Added check for variable value matching I_S tables +# select @@global.lower_case_file_system=2; --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@session.lower_case_file_system; ---replace_column 2 # -show global variables like 'lower_case_file_system'; ---replace_column 2 # -show session variables like 'lower_case_file_system'; ---replace_column 2 # -select * from information_schema.global_variables where variable_name='lower_case_file_system'; ---replace_column 2 # -select * from information_schema.session_variables where variable_name='lower_case_file_system'; + +# Show variable has a valid value +SELECT @@global.lower_case_file_system in (0,1); + +# Show that value of the variable matches the value in the GLOBAL I_S table +SELECT IF(@@global.lower_case_file_system, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lower_case_file_system'; + +# Show that value of the variable matches the value in the SESSION I_S table +SELECT IF(@@global.lower_case_file_system, "ON", "OFF") = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.SESSION_VARIABLES +WHERE VARIABLE_NAME='lower_case_file_system'; # # show that it's read-only diff --git a/mysql-test/suite/sys_vars/t/lower_case_table_names_basic.test b/mysql-test/suite/sys_vars/t/lower_case_table_names_basic.test index 8d6a3bb5ca6..eaa6c95974e 100644 --- a/mysql-test/suite/sys_vars/t/lower_case_table_names_basic.test +++ b/mysql-test/suite/sys_vars/t/lower_case_table_names_basic.test @@ -1,17 +1,26 @@ # # only global # +# 2010-01-20 OBN - Modified to include check for valid values +# - Added check for variable value matching I_S tables +# + select @@global.lower_case_table_names=20; --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@session.lower_case_table_names; ---replace_column 2 # -show global variables like 'lower_case_table_names'; ---replace_column 2 # -show session variables like 'lower_case_table_names'; ---replace_column 2 # -select * from information_schema.global_variables where variable_name='lower_case_table_names'; ---replace_column 2 # -select * from information_schema.session_variables where variable_name='lower_case_table_names'; + +# Show variable has a valid value +SELECT @@global.lower_case_table_names in (0,1,2); + +# Show that value of the variable matches the value in the GLOBAL I_S table +SELECT @@global.lower_case_table_names = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lower_case_table_names'; + +# Show that value of the variable matches the value in the SESSION I_S table +SELECT @@global.lower_case_table_names = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lower_case_table_names'; # # show that it's read-only diff --git a/mysql-test/suite/sys_vars/t/max_join_size_basic.test b/mysql-test/suite/sys_vars/t/max_join_size_basic.test index 7566ab11520..bbe13457ee7 100644 --- a/mysql-test/suite/sys_vars/t/max_join_size_basic.test +++ b/mysql-test/suite/sys_vars/t/max_join_size_basic.test @@ -1,3 +1,11 @@ + + +# +# 2010-01-20 - OBN - Added check for variable value sameas I_S table +# after variable value change +# + + SET @start_global_value = @@global.max_join_size; SELECT @start_global_value; @@ -15,9 +23,13 @@ select * from information_schema.session_variables where variable_name='max_join # show that it's writable # set global max_join_size=10; -select @@global.max_join_size; set session max_join_size=20; +select @@global.max_join_size; select @@session.max_join_size; +show global variables like 'max_join_size'; +show session variables like 'max_join_size'; +select * from information_schema.global_variables where variable_name='max_join_size'; +select * from information_schema.session_variables where variable_name='max_join_size'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/old_alter_table_basic.test b/mysql-test/suite/sys_vars/t/old_alter_table_basic.test index 32d0f45ad25..cce180fea67 100644 --- a/mysql-test/suite/sys_vars/t/old_alter_table_basic.test +++ b/mysql-test/suite/sys_vars/t/old_alter_table_basic.test @@ -1,3 +1,8 @@ + +# +# 2010-01-20 OBN - added check of I_S values after variable value changed +# + SET @start_global_value = @@global.old_alter_table; SELECT @start_global_value; @@ -15,9 +20,13 @@ select * from information_schema.session_variables where variable_name='old_alte # show that it's writable # set global old_alter_table=1; -select @@global.old_alter_table; set session old_alter_table=ON; +select @@global.old_alter_table; select @@session.old_alter_table; +show global variables like 'old_alter_table'; +show session variables like 'old_alter_table'; +select * from information_schema.global_variables where variable_name='old_alter_table'; +select * from information_schema.session_variables where variable_name='old_alter_table'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test b/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test index 32a77c90b34..4267b3726aa 100644 --- a/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test +++ b/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test @@ -1,3 +1,8 @@ + +# +# 2010-01-20 OBN Added check for I_S values after variable value changed +# + SET @start_global_value = @@global.optimizer_switch; SELECT @start_global_value; @@ -15,13 +20,17 @@ select * from information_schema.session_variables where variable_name='optimize # show that it's writable # set global optimizer_switch=10; -select @@global.optimizer_switch; set session optimizer_switch=5; +select @@global.optimizer_switch; select @@session.optimizer_switch; set global optimizer_switch="index_merge_sort_union=on"; -select @@global.optimizer_switch; set session optimizer_switch="index_merge=off"; +select @@global.optimizer_switch; select @@session.optimizer_switch; +show global variables like 'optimizer_switch'; +show session variables like 'optimizer_switch'; +select * from information_schema.global_variables where variable_name='optimizer_switch'; +select * from information_schema.session_variables where variable_name='optimizer_switch'; set session optimizer_switch="default"; select @@session.optimizer_switch; diff --git a/mysql-test/suite/sys_vars/t/profiling_basic.test b/mysql-test/suite/sys_vars/t/profiling_basic.test index aef56e50900..da54d5a207d 100644 --- a/mysql-test/suite/sys_vars/t/profiling_basic.test +++ b/mysql-test/suite/sys_vars/t/profiling_basic.test @@ -1,3 +1,8 @@ + +# +# 2010-01-20 OBN - Added check for I_S values after variable value change +# + --source include/have_profiling.inc SET @start_global_value = @@global.profiling; @@ -17,9 +22,21 @@ select * from information_schema.session_variables where variable_name='profilin # show that it's writable # set global profiling=1; -select @@global.profiling; set session profiling=ON; +select @@global.profiling; select @@session.profiling; +show global variables like 'profiling'; +show session variables like 'profiling'; +select * from information_schema.global_variables where variable_name='profiling'; +select * from information_schema.session_variables where variable_name='profiling'; +set global profiling=0; +set session profiling=OFF; +select @@global.profiling; +select @@session.profiling; +show global variables like 'profiling'; +show session variables like 'profiling'; +select * from information_schema.global_variables where variable_name='profiling'; +select * from information_schema.session_variables where variable_name='profiling'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/profiling_history_size_basic.test b/mysql-test/suite/sys_vars/t/profiling_history_size_basic.test index 01d3533be3d..26fdc55328b 100644 --- a/mysql-test/suite/sys_vars/t/profiling_history_size_basic.test +++ b/mysql-test/suite/sys_vars/t/profiling_history_size_basic.test @@ -1,3 +1,8 @@ + +# +# 2010-01-20 OBN Added check for variable and I_S values after incorrect change attempt +# + --source include/have_profiling.inc SET @start_global_value = @@global.profiling_history_size; @@ -17,9 +22,13 @@ select * from information_schema.session_variables where variable_name='profilin # show that it's writable # set global profiling_history_size=10; -select @@global.profiling_history_size; set session profiling_history_size=20; +select @@global.profiling_history_size; select @@session.profiling_history_size; +show global variables like 'profiling_history_size'; +show session variables like 'profiling_history_size'; +select * from information_schema.global_variables where variable_name='profiling_history_size'; +select * from information_schema.session_variables where variable_name='profiling_history_size'; # # incorrect assignments diff --git a/mysql-test/suite/sys_vars/t/pseudo_thread_id_basic.test b/mysql-test/suite/sys_vars/t/pseudo_thread_id_basic.test index a948f2a8ff0..0722b42099d 100644 --- a/mysql-test/suite/sys_vars/t/pseudo_thread_id_basic.test +++ b/mysql-test/suite/sys_vars/t/pseudo_thread_id_basic.test @@ -2,21 +2,37 @@ # # exists as a session only # +# 2010-01-20 OBN - Added check for session I_S values being equal to variable value +# - Added check for session variable value being numberic +# + --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@global.pseudo_thread_id; -select @@session.pseudo_thread_id=0; + +# Check the variable has a valid numeric value (assumed to be less then 10000) +select @@session.pseudo_thread_id between 1 and 1000; + +--echo should be empty show global variables like 'pseudo_thread_id'; + +# Check that shows returns a value (can't confirm the actual one) --replace_column 2 # show session variables like 'pseudo_thread_id'; + +# Global I_S variable is empty +--echo should be empty select * from information_schema.global_variables where variable_name='pseudo_thread_id'; ---replace_column 2 # -select * from information_schema.session_variables where variable_name='pseudo_thread_id'; + +# Check that I_S value is same as variable +select @@session.pseudo_thread_id = variable_value from information_schema.session_variables where variable_name='pseudo_thread_id'; # # show that it's writable # -set session pseudo_thread_id=1; +set session pseudo_thread_id=42; select @@session.pseudo_thread_id; +select * from information_schema.global_variables where variable_name='pseudo_thread_id'; +select variable_value from information_schema.session_variables where variable_name='pseudo_thread_id'; --error ER_LOCAL_VARIABLE set global pseudo_thread_id=1; diff --git a/mysql-test/suite/sys_vars/t/rand_seed1_basic.test b/mysql-test/suite/sys_vars/t/rand_seed1_basic.test index 79c946a3d2f..3745d2fcb1e 100644 --- a/mysql-test/suite/sys_vars/t/rand_seed1_basic.test +++ b/mysql-test/suite/sys_vars/t/rand_seed1_basic.test @@ -2,6 +2,8 @@ # # exists as a session only # +# 2010-01-20 OBN - Added check for I_S value after variable value change +# --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@global.rand_seed1; select @@session.rand_seed1; @@ -11,10 +13,12 @@ select * from information_schema.global_variables where variable_name='rand_seed select * from information_schema.session_variables where variable_name='rand_seed1'; # -# show that it's writable +# show that it's writable (note value can be set but not read - returned value is zero) # set session rand_seed1=1; select @@session.rand_seed1; +select * from information_schema.global_variables where variable_name='rand_seed1'; +select * from information_schema.session_variables where variable_name='rand_seed1'; --error ER_LOCAL_VARIABLE set global rand_seed1=1; diff --git a/mysql-test/suite/sys_vars/t/rand_seed2_basic.test b/mysql-test/suite/sys_vars/t/rand_seed2_basic.test index 18e2a62557c..7a5abcd340b 100644 --- a/mysql-test/suite/sys_vars/t/rand_seed2_basic.test +++ b/mysql-test/suite/sys_vars/t/rand_seed2_basic.test @@ -2,6 +2,9 @@ # # exists as a session only # +# +# 2010-01-20 OBN - Added check for I_S value after variable value change +# --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@global.rand_seed2; select @@session.rand_seed2; @@ -11,10 +14,12 @@ select * from information_schema.global_variables where variable_name='rand_seed select * from information_schema.session_variables where variable_name='rand_seed2'; # -# show that it's writable +# show that it's writable (note value can be set but not read - returned value is zero) # set session rand_seed2=1; select @@session.rand_seed2; +select * from information_schema.global_variables where variable_name='rand_seed2'; +select * from information_schema.session_variables where variable_name='rand_seed2'; --error ER_LOCAL_VARIABLE set global rand_seed2=1; diff --git a/mysql-test/suite/sys_vars/t/relay_log_recovery_basic.test b/mysql-test/suite/sys_vars/t/relay_log_recovery_basic.test index 57c28468bd1..a6f218a59ae 100644 --- a/mysql-test/suite/sys_vars/t/relay_log_recovery_basic.test +++ b/mysql-test/suite/sys_vars/t/relay_log_recovery_basic.test @@ -1,3 +1,9 @@ + + +# +# 2010-01-10 - Added check for I_S values aver variable value change +# + --source include/not_embedded.inc SET @start_global_value = @@global.relay_log_recovery; @@ -19,8 +25,12 @@ select * from information_schema.session_variables where variable_name='relay_lo # set global relay_log_recovery=1; select @@global.relay_log_recovery; +select * from information_schema.global_variables where variable_name='relay_log_recovery'; +select * from information_schema.session_variables where variable_name='relay_log_recovery'; set global relay_log_recovery=OFF; select @@global.relay_log_recovery; +select * from information_schema.global_variables where variable_name='relay_log_recovery'; +select * from information_schema.session_variables where variable_name='relay_log_recovery'; --error ER_GLOBAL_VARIABLE set session relay_log_recovery=1; diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic-master.opt new file mode 100644 index 00000000000..58029d28ace --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic.test new file mode 100644 index 00000000000..5aea6e165ea --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_enabled_basic.test @@ -0,0 +1,62 @@ + +# +# exists as a global only +# +# 2010-01-21 OBN - Added +# +# +source include/have_semisync_plugin.inc; +eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN'; +select @@global.rpl_semi_sync_master_enabled; +SET @start_global_value = @@global.rpl_semi_sync_master_enabled; + +select @@global.rpl_semi_sync_master_enabled in (0,1); +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_enabled; +show global variables like 'rpl_semi_sync_master_enabled'; +show session variables like 'rpl_semi_sync_master_enabled'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_enabled'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_enabled'; + +# +# show that it's writable +# +set global rpl_semi_sync_master_enabled=0; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_master_enabled=0; +select @@global.rpl_semi_sync_master_enabled; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_enabled; +show global variables like 'rpl_semi_sync_master_enabled'; +show session variables like 'rpl_semi_sync_master_enabled'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_enabled'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_enabled'; +set global rpl_semi_sync_master_enabled=1; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_master_enabled=1; +select @@global.rpl_semi_sync_master_enabled; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_enabled; +show global variables like 'rpl_semi_sync_master_enabled'; +show session variables like 'rpl_semi_sync_master_enabled'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_enabled'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_enabled'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_enabled=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_enabled=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global rpl_semi_sync_master_enabled="some text"; + + +# +# Cleanup +# +SET @@global.rpl_semi_sync_master_enabled = @start_global_value; +select @@global.rpl_semi_sync_master_enabled; +UNINSTALL PLUGIN rpl_semi_sync_master; + diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic-master.opt new file mode 100644 index 00000000000..58029d28ace --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic.test new file mode 100644 index 00000000000..6c4aae8e3bf --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_timeout_basic.test @@ -0,0 +1,52 @@ + +# +# exists as a global only +# +# 2010-01-21 OBN - Added +# +source include/have_semisync_plugin.inc; +eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN'; +select @@global.rpl_semi_sync_master_timeout; +SET @start_global_value = @@global.rpl_semi_sync_master_timeout; + +--echo Assuming value will not be more then 100 sec +select @@global.rpl_semi_sync_master_timeout between 1 and 100000; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_timeout; +show global variables like 'rpl_semi_sync_master_timeout'; +show session variables like 'rpl_semi_sync_master_timeout'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_timeout'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_timeout'; + +# +# show that it's writable +# +set global rpl_semi_sync_master_timeout=42; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_master_timeout=99; +select @@global.rpl_semi_sync_master_timeout; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_timeout; +show global variables like 'rpl_semi_sync_master_timeout'; +show session variables like 'rpl_semi_sync_master_timeout'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_timeout'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_timeout'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_timeout=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_timeout=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_timeout="some text"; + + +# +# Cleanup +# +SET @@global.rpl_semi_sync_master_timeout = @start_global_value; +select @@global.rpl_semi_sync_master_timeout; +UNINSTALL PLUGIN rpl_semi_sync_master; + diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic-master.opt new file mode 100644 index 00000000000..58029d28ace --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic.test new file mode 100644 index 00000000000..c37248ffa9a --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_trace_level_basic.test @@ -0,0 +1,60 @@ + +# +# exists as a global only +# +# 2010-01-21 OBN - Added +# +source include/have_semisync_plugin.inc; +eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN'; +select @@global.rpl_semi_sync_master_trace_level; +SET @start_global_value = @@global.rpl_semi_sync_master_trace_level; + +select @@global.rpl_semi_sync_master_trace_level in (1,16,32,64); +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_trace_level; +show global variables like 'rpl_semi_sync_master_trace_level'; +show session variables like 'rpl_semi_sync_master_trace_level'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_trace_level'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_trace_level'; + +# +# show that it's writable +# +set global rpl_semi_sync_master_trace_level=16; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_master_trace_level=99; +select @@global.rpl_semi_sync_master_trace_level; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_trace_level; +show global variables like 'rpl_semi_sync_master_trace_level'; +show session variables like 'rpl_semi_sync_master_trace_level'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_trace_level'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_trace_level'; + +--echo NOTE: Value can also be set to values that are combinations of values +set global rpl_semi_sync_master_trace_level=42; +select @@global.rpl_semi_sync_master_trace_level; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_trace_level; +show global variables like 'rpl_semi_sync_master_trace_level'; +show session variables like 'rpl_semi_sync_master_trace_level'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_trace_level'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_trace_level'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_trace_level=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_trace_level=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_trace_level="some text"; + +# +# Cleanup +# +SET @@global.rpl_semi_sync_master_trace_level = @start_global_value; +select @@global.rpl_semi_sync_master_trace_level; +UNINSTALL PLUGIN rpl_semi_sync_master; + diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic-master.opt new file mode 100644 index 00000000000..58029d28ace --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic.test new file mode 100644 index 00000000000..a90024b8961 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_master_wait_no_slave_basic.test @@ -0,0 +1,62 @@ + +# +# exists as a global only +# +# 2010-01-21 OBN - Added +# +# +source include/have_semisync_plugin.inc; +eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN'; +select @@global.rpl_semi_sync_master_wait_no_slave; +SET @start_global_value = @@global.rpl_semi_sync_master_wait_no_slave; + +select @@global.rpl_semi_sync_master_wait_no_slave in (0,1); +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_wait_no_slave; +show global variables like 'rpl_semi_sync_master_wait_no_slave'; +show session variables like 'rpl_semi_sync_master_wait_no_slave'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; + +# +# show that it's writable +# +set global rpl_semi_sync_master_wait_no_slave=0; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_master_wait_no_slave=0; +select @@global.rpl_semi_sync_master_wait_no_slave; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_wait_no_slave; +show global variables like 'rpl_semi_sync_master_wait_no_slave'; +show session variables like 'rpl_semi_sync_master_wait_no_slave'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +set global rpl_semi_sync_master_wait_no_slave=1; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_master_wait_no_slave=1; +select @@global.rpl_semi_sync_master_wait_no_slave; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_master_wait_no_slave; +show global variables like 'rpl_semi_sync_master_wait_no_slave'; +show session variables like 'rpl_semi_sync_master_wait_no_slave'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_master_wait_no_slave'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_wait_no_slave=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_master_wait_no_slave=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global rpl_semi_sync_master_wait_no_slave="some text"; + + +# +# Cleanup +# +SET @@global.rpl_semi_sync_master_wait_no_slave = @start_global_value; +select @@global.rpl_semi_sync_master_wait_no_slave; +UNINSTALL PLUGIN rpl_semi_sync_master; + diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic-master.opt new file mode 100644 index 00000000000..58029d28ace --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic.test new file mode 100644 index 00000000000..5dca63d5e2d --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_enabled_basic.test @@ -0,0 +1,63 @@ + +# +# exists as a global only +# +# 2010-01-21 OBN - Added +# +# +source include/have_semisync_plugin.inc; +eval INSTALL PLUGIN rpl_semi_sync_slave SONAME '$SEMISYNC_SLAVE_PLUGIN'; +select @@global.rpl_semi_sync_slave_enabled; +SET @start_global_value = @@global.rpl_semi_sync_slave_enabled; + +select @@global.rpl_semi_sync_slave_enabled in (0,1); +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_slave_enabled; +show global variables like 'rpl_semi_sync_slave_enabled'; +show session variables like 'rpl_semi_sync_slave_enabled'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_enabled'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_enabled'; + +# +# show that it's writable +# +set global rpl_semi_sync_slave_enabled=0; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_slave_enabled=0; +select @@global.rpl_semi_sync_slave_enabled; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_slave_enabled; +show global variables like 'rpl_semi_sync_slave_enabled'; +show session variables like 'rpl_semi_sync_slave_enabled'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_enabled'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_enabled'; +set global rpl_semi_sync_slave_enabled=1; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_slave_enabled=1; +select @@global.rpl_semi_sync_slave_enabled; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_slave_enabled; +show global variables like 'rpl_semi_sync_slave_enabled'; +show session variables like 'rpl_semi_sync_slave_enabled'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_enabled'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_enabled'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_slave_enabled=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_slave_enabled=1e1; +--error ER_WRONG_VALUE_FOR_VAR +set global rpl_semi_sync_slave_enabled="some text"; +--error ER_WRONG_VALUE_FOR_VAR + + +# +# Cleanup +# +SET @@global.rpl_semi_sync_slave_enabled = @start_global_value; +select @@global.rpl_semi_sync_slave_enabled; +UNINSTALL PLUGIN rpl_semi_sync_slave; + diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic-master.opt new file mode 100644 index 00000000000..58029d28ace --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_PLUGIN_OPT diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic.test new file mode 100644 index 00000000000..afac5244eb2 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_slave_trace_level_basic.test @@ -0,0 +1,60 @@ + +# +# exists as a global only +# +# 2010-01-21 OBN - Added +# +source include/have_semisync_plugin.inc; +eval INSTALL PLUGIN rpl_semi_sync_slave SONAME '$SEMISYNC_SLAVE_PLUGIN'; +select @@global.rpl_semi_sync_slave_trace_level; +SET @start_global_value = @@global.rpl_semi_sync_slave_trace_level; + +select @@global.rpl_semi_sync_slave_trace_level in (1,16,32,64); +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_slave_trace_level; +show global variables like 'rpl_semi_sync_slave_trace_level'; +show session variables like 'rpl_semi_sync_slave_trace_level'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_trace_level'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_trace_level'; + +# +# show that it's writable +# +set global rpl_semi_sync_slave_trace_level=16; +--error ER_GLOBAL_VARIABLE +set session rpl_semi_sync_slave_trace_level=99; +select @@global.rpl_semi_sync_slave_trace_level; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_slave_trace_level; +show global variables like 'rpl_semi_sync_slave_trace_level'; +show session variables like 'rpl_semi_sync_slave_trace_level'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_trace_level'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_trace_level'; + +--echo NOTE: Value can also be set to values that are combinations of values +set global rpl_semi_sync_slave_trace_level=42; +select @@global.rpl_semi_sync_slave_trace_level; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.rpl_semi_sync_slave_trace_level; +show global variables like 'rpl_semi_sync_slave_trace_level'; +show session variables like 'rpl_semi_sync_slave_trace_level'; +select * from information_schema.global_variables where variable_name='rpl_semi_sync_slave_trace_level'; +select * from information_schema.session_variables where variable_name='rpl_semi_sync_slave_trace_level'; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_slave_trace_level=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_slave_trace_level=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global rpl_semi_sync_slave_trace_level="some text"; + +# +# Cleanup +# +SET @@global.rpl_semi_sync_slave_trace_level = @start_global_value; +select @@global.rpl_semi_sync_slave_trace_level; +UNINSTALL PLUGIN rpl_semi_sync_slave; + diff --git a/mysql-test/suite/sys_vars/t/sql_log_update_basic.test b/mysql-test/suite/sys_vars/t/sql_log_update_basic.test index c48840d4a81..9b9f6f375b6 100644 --- a/mysql-test/suite/sys_vars/t/sql_log_update_basic.test +++ b/mysql-test/suite/sys_vars/t/sql_log_update_basic.test @@ -1,3 +1,9 @@ + +# +# 2010-01-20 OBN - Added check of I_S tables after variable value changes. +# - Added value change to ON/OFF to ensure change of current value +# + --source include/have_profiling.inc SET @start_global_value = @@global.sql_log_update; @@ -17,9 +23,21 @@ select * from information_schema.session_variables where variable_name='sql_log_ # show that it's writable # set global sql_log_update=1; -select @@global.sql_log_update; set session sql_log_update=ON; +select @@global.sql_log_update; select @@session.sql_log_update; +show global variables like 'sql_log_update'; +show session variables like 'sql_log_update'; +select * from information_schema.global_variables where variable_name='sql_log_update'; +select * from information_schema.session_variables where variable_name='sql_log_update'; +set global sql_log_update=0; +set session sql_log_update=OFF; +select @@global.sql_log_update; +select @@session.sql_log_update; +show global variables like 'sql_log_update'; +show session variables like 'sql_log_update'; +select * from information_schema.global_variables where variable_name='sql_log_update'; +select * from information_schema.session_variables where variable_name='sql_log_update'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/sql_max_join_size_basic.test b/mysql-test/suite/sys_vars/t/sql_max_join_size_basic.test index bafa11379aa..9a72846b29c 100644 --- a/mysql-test/suite/sys_vars/t/sql_max_join_size_basic.test +++ b/mysql-test/suite/sys_vars/t/sql_max_join_size_basic.test @@ -1,3 +1,8 @@ + +# +# 2010-01-20 OBN - Added check of I_S values after variable value change +# + SET @start_global_value = @@global.sql_max_join_size; SELECT @start_global_value; @@ -15,9 +20,13 @@ select * from information_schema.session_variables where variable_name='sql_max_ # show that it's writable # set global sql_max_join_size=10; -select @@global.sql_max_join_size; set session sql_max_join_size=20; +select @@global.sql_max_join_size; select @@session.sql_max_join_size; +show global variables like 'sql_max_join_size'; +show session variables like 'sql_max_join_size'; +select * from information_schema.global_variables where variable_name='sql_max_join_size'; +select * from information_schema.session_variables where variable_name='sql_max_join_size'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/sql_select_limit_basic.test b/mysql-test/suite/sys_vars/t/sql_select_limit_basic.test index 0b941d25f42..c6bc5962205 100644 --- a/mysql-test/suite/sys_vars/t/sql_select_limit_basic.test +++ b/mysql-test/suite/sys_vars/t/sql_select_limit_basic.test @@ -1,3 +1,8 @@ + +# +# 2010-01-10 OBN - Added check for I_S values after change of variable value +# + SET @start_global_value = @@global.sql_select_limit; SELECT @start_global_value; @@ -15,9 +20,13 @@ select * from information_schema.session_variables where variable_name='sql_sele # show that it's writable # set global sql_select_limit=10; -select @@global.sql_select_limit; set session sql_select_limit=20; +select @@global.sql_select_limit; select @@session.sql_select_limit; +show global variables like 'sql_select_limit'; +show session variables like 'sql_select_limit'; +select * from information_schema.global_variables where variable_name='sql_select_limit'; +select * from information_schema.session_variables where variable_name='sql_select_limit'; # # incorrect types diff --git a/mysql-test/suite/sys_vars/t/thread_cache_size_basic.test b/mysql-test/suite/sys_vars/t/thread_cache_size_basic.test index 48a4c0797e5..22aaff6d426 100644 --- a/mysql-test/suite/sys_vars/t/thread_cache_size_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_cache_size_basic.test @@ -1,4 +1,8 @@ +# +# 2010-01-20 OBN - Added check of I_S values after variable value change +# + SET @start_global_value = @@global.thread_cache_size; SELECT @start_global_value; @@ -18,6 +22,8 @@ select * from information_schema.session_variables where variable_name='thread_c # set global thread_cache_size=1; select @@global.thread_cache_size; +select * from information_schema.global_variables where variable_name='thread_cache_size'; +select * from information_schema.session_variables where variable_name='thread_cache_size'; --error ER_GLOBAL_VARIABLE set session thread_cache_size=1; From ffee12205364fa225c69a48be2467f35118dfb8b Mon Sep 17 00:00:00 2001 From: "Horst.Hunger" Date: Fri, 29 Jan 2010 11:48:11 +0100 Subject: [PATCH 326/466] New patch for bug#49579, now with "have_ipv4_mapped.inc". --- mysql-test/include/have_ipv4_mapped.inc | 14 ++ mysql-test/r/ipv4_as_ipv6_win.result | 32 ---- mysql-test/r/ipv6.result | 176 ------------------ mysql-test/r/ipv6_win.result | 88 --------- mysql-test/suite/rpl/r/rpl_ip_mix.result | 102 +--------- mysql-test/suite/rpl/r/rpl_ip_mix2.result | 118 +----------- mysql-test/suite/rpl/r/rpl_ip_mix2_win.result | 84 --------- mysql-test/suite/rpl/r/rpl_ip_mix_win.result | 84 --------- .../suite/rpl/r/rpl_ipv4_as_ipv6.result | 10 +- .../suite/rpl/r/rpl_ipv4_as_ipv6_win.result | 35 ---- mysql-test/suite/rpl/r/rpl_ipv6.result | 96 +--------- mysql-test/suite/rpl/r/rpl_ipv6_win.result | 80 -------- mysql-test/suite/rpl/t/rpl_ip_mix.test | 36 +--- mysql-test/suite/rpl/t/rpl_ip_mix2.test | 40 +--- mysql-test/suite/rpl/t/rpl_ip_mix2_win.cnf | 56 ------ mysql-test/suite/rpl/t/rpl_ip_mix2_win.test | 78 -------- mysql-test/suite/rpl/t/rpl_ip_mix_win.cnf | 56 ------ mysql-test/suite/rpl/t/rpl_ip_mix_win.test | 65 ------- mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.test | 23 +-- .../suite/rpl/t/rpl_ipv4_as_ipv6_win.cnf | 56 ------ .../suite/rpl/t/rpl_ipv4_as_ipv6_win.test | 63 ------- mysql-test/suite/rpl/t/rpl_ipv6.test | 34 +--- mysql-test/suite/rpl/t/rpl_ipv6_win.cnf | 56 ------ mysql-test/suite/rpl/t/rpl_ipv6_win.test | 65 ------- mysql-test/t/ipv4_as_ipv6.test | 3 +- mysql-test/t/ipv4_as_ipv6_win-master.opt | 1 - mysql-test/t/ipv4_as_ipv6_win.test | 31 --- mysql-test/t/ipv6.test | 43 ----- mysql-test/t/ipv6_win-master.opt | 1 - mysql-test/t/ipv6_win.test | 39 ---- 30 files changed, 36 insertions(+), 1629 deletions(-) create mode 100644 mysql-test/include/have_ipv4_mapped.inc delete mode 100644 mysql-test/r/ipv4_as_ipv6_win.result delete mode 100644 mysql-test/r/ipv6_win.result delete mode 100644 mysql-test/suite/rpl/r/rpl_ip_mix2_win.result delete mode 100644 mysql-test/suite/rpl/r/rpl_ip_mix_win.result delete mode 100644 mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6_win.result delete mode 100644 mysql-test/suite/rpl/r/rpl_ipv6_win.result delete mode 100644 mysql-test/suite/rpl/t/rpl_ip_mix2_win.cnf delete mode 100644 mysql-test/suite/rpl/t/rpl_ip_mix2_win.test delete mode 100644 mysql-test/suite/rpl/t/rpl_ip_mix_win.cnf delete mode 100644 mysql-test/suite/rpl/t/rpl_ip_mix_win.test delete mode 100644 mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.cnf delete mode 100644 mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.test delete mode 100644 mysql-test/suite/rpl/t/rpl_ipv6_win.cnf delete mode 100644 mysql-test/suite/rpl/t/rpl_ipv6_win.test delete mode 100644 mysql-test/t/ipv4_as_ipv6_win-master.opt delete mode 100644 mysql-test/t/ipv4_as_ipv6_win.test delete mode 100644 mysql-test/t/ipv6_win-master.opt delete mode 100644 mysql-test/t/ipv6_win.test diff --git a/mysql-test/include/have_ipv4_mapped.inc b/mysql-test/include/have_ipv4_mapped.inc new file mode 100644 index 00000000000..d85580405cd --- /dev/null +++ b/mysql-test/include/have_ipv4_mapped.inc @@ -0,0 +1,14 @@ +# Check if ipv4 mapped to ipv6 is available. +--disable_query_log +--disable_abort_on_error +connect (checkcon123456789,::FFFF:127.0.0.1,root,,test); +if($mysql_errno) +{ +skip wrong IP; +} +connection default; +disconnect checkcon123456789; +--enable_abort_on_error +--enable_query_log +# end check + diff --git a/mysql-test/r/ipv4_as_ipv6_win.result b/mysql-test/r/ipv4_as_ipv6_win.result deleted file mode 100644 index 45e23d4d7e6..00000000000 --- a/mysql-test/r/ipv4_as_ipv6_win.result +++ /dev/null @@ -1,32 +0,0 @@ -=============Test of '127.0.0.1' (IPv4) =========================== -mysqld is alive -CREATE USER testuser@'127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'127.0.0.1'; -SHOW GRANTS FOR testuser@'127.0.0.1'; -Grants for testuser@127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'127.0.0.1' -SET @nip= inet_aton('127.0.0.1'); -SELECT @nip; -@nip -2130706433 -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -127.0.0.1 -SELECT USER(); -USER() -root@127.0.0.1 -SELECT current_user(); -current_user() -root@127.0.0.1 -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'127.0.0.1'; -RENAME USER testuser@'127.0.0.1' to testuser1@'127.0.0.1'; -SET PASSWORD FOR testuser1@'127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@127.0.0.1 -DROP USER testuser1@'127.0.0.1'; -=============Test of '::1' ======================== -connect (con1, ::1, root, , test, MASTER_MYPORT); -Got one of the listed errors diff --git a/mysql-test/r/ipv6.result b/mysql-test/r/ipv6.result index 566938296a9..1ff51fcc831 100644 --- a/mysql-test/r/ipv6.result +++ b/mysql-test/r/ipv6.result @@ -86,179 +86,3 @@ SELECT USER(); USER() root@localhost DROP USER testuser1@'0:0:0:0:0:0:0:1'; -=============Test of '127.0.0.1' (IPv4) =========================== -mysqld is alive -CREATE USER testuser@'127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'127.0.0.1'; -SHOW GRANTS FOR testuser@'127.0.0.1'; -Grants for testuser@127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'127.0.0.1' -SET @nip= inet_aton('127.0.0.1'); -SELECT @nip; -@nip -2130706433 -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -127.0.0.1 -SELECT USER(); -USER() -root@localhost -SELECT current_user(); -current_user() -root@localhost -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'127.0.0.1'; -RENAME USER testuser@'127.0.0.1' to testuser1@'127.0.0.1'; -SET PASSWORD FOR testuser1@'127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@localhost -DROP USER testuser1@'127.0.0.1'; -=============Test of '0:0:0:0:0:FFFF:127.0.0.1' =================== -mysqld is alive -CREATE USER testuser@'0:0:0:0:0:FFFF:127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'0:0:0:0:0:FFFF:127.0.0.1'; -SHOW GRANTS FOR testuser@'0:0:0:0:0:FFFF:127.0.0.1'; -Grants for testuser@0:0:0:0:0:FFFF:127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'0:0:0:0:0:FFFF:127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'0:0:0:0:0:FFFF:127.0.0.1' -SET @nip= inet_aton('0:0:0:0:0:FFFF:127.0.0.1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@localhost -SELECT current_user(); -current_user() -root@localhost -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'0:0:0:0:0:FFFF:127.0.0.1'; -RENAME USER testuser@'0:0:0:0:0:FFFF:127.0.0.1' to testuser1@'0:0:0:0:0:FFFF:127.0.0.1'; -SET PASSWORD FOR testuser1@'0:0:0:0:0:FFFF:127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@localhost -DROP USER testuser1@'0:0:0:0:0:FFFF:127.0.0.1'; -=============Test of '0000:0000:0000:0000:0000:FFFF:127.0.0.1' ==== -mysqld is alive -CREATE USER testuser@'0000:0000:0000:0000:0000:FFFF:127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -SHOW GRANTS FOR testuser@'0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -Grants for testuser@0000:0000:0000:0000:0000:FFFF:127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'0000:0000:0000:0000:0000:FFFF:127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'0000:0000:0000:0000:0000:FFFF:127.0.0.1' -SET @nip= inet_aton('0000:0000:0000:0000:0000:FFFF:127.0.0.1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@localhost -SELECT current_user(); -current_user() -root@localhost -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -RENAME USER testuser@'0000:0000:0000:0000:0000:FFFF:127.0.0.1' to testuser1@'0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -SET PASSWORD FOR testuser1@'0000:0000:0000:0000:0000:FFFF:127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@localhost -DROP USER testuser1@'0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -=============Test of '0:0000:0000:0:0000:FFFF:127.0.0.1' ==== -mysqld is alive -CREATE USER testuser@'0:0000:0000:0:0000:FFFF:127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'0:0000:0000:0:0000:FFFF:127.0.0.1'; -SHOW GRANTS FOR testuser@'0:0000:0000:0:0000:FFFF:127.0.0.1'; -Grants for testuser@0:0000:0000:0:0000:FFFF:127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'0:0000:0000:0:0000:FFFF:127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'0:0000:0000:0:0000:FFFF:127.0.0.1' -SET @nip= inet_aton('0:0000:0000:0:0000:FFFF:127.0.0.1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@localhost -SELECT current_user(); -current_user() -root@localhost -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'0:0000:0000:0:0000:FFFF:127.0.0.1'; -RENAME USER testuser@'0:0000:0000:0:0000:FFFF:127.0.0.1' to testuser1@'0:0000:0000:0:0000:FFFF:127.0.0.1'; -SET PASSWORD FOR testuser1@'0:0000:0000:0:0000:FFFF:127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@localhost -DROP USER testuser1@'0:0000:0000:0:0000:FFFF:127.0.0.1'; -=============Test of '0::0000:FFFF:127.0.0.1' ==== -mysqld is alive -CREATE USER testuser@'0::0000:FFFF:127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'0::0000:FFFF:127.0.0.1'; -SHOW GRANTS FOR testuser@'0::0000:FFFF:127.0.0.1'; -Grants for testuser@0::0000:FFFF:127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'0::0000:FFFF:127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'0::0000:FFFF:127.0.0.1' -SET @nip= inet_aton('0::0000:FFFF:127.0.0.1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@localhost -SELECT current_user(); -current_user() -root@localhost -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'0::0000:FFFF:127.0.0.1'; -RENAME USER testuser@'0::0000:FFFF:127.0.0.1' to testuser1@'0::0000:FFFF:127.0.0.1'; -SET PASSWORD FOR testuser1@'0::0000:FFFF:127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@localhost -DROP USER testuser1@'0::0000:FFFF:127.0.0.1'; -=============Test of '0:0:0:0:0:FFFF:127.0.0.1/96' ================ -=============Test of '::FFFF:127.0.0.1' =========================== -mysqld is alive -CREATE USER testuser@'::FFFF:127.0.0.1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'::FFFF:127.0.0.1'; -SHOW GRANTS FOR testuser@'::FFFF:127.0.0.1'; -Grants for testuser@::FFFF:127.0.0.1 -GRANT USAGE ON *.* TO 'testuser'@'::FFFF:127.0.0.1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'::FFFF:127.0.0.1' -SET @nip= inet_aton('::FFFF:127.0.0.1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@localhost -SELECT current_user(); -current_user() -root@localhost -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'::FFFF:127.0.0.1'; -RENAME USER testuser@'::FFFF:127.0.0.1' to testuser1@'::FFFF:127.0.0.1'; -SET PASSWORD FOR testuser1@'::FFFF:127.0.0.1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@localhost -DROP USER testuser1@'::FFFF:127.0.0.1'; -=============Test of '::FFFF:127.0.0.1/96' ======================== diff --git a/mysql-test/r/ipv6_win.result b/mysql-test/r/ipv6_win.result deleted file mode 100644 index 8082e8aa25e..00000000000 --- a/mysql-test/r/ipv6_win.result +++ /dev/null @@ -1,88 +0,0 @@ -=============Test of '::1' ======================================== -mysqld is alive -CREATE USER testuser@'::1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'::1'; -SHOW GRANTS FOR testuser@'::1'; -Grants for testuser@::1 -GRANT USAGE ON *.* TO 'testuser'@'::1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'::1' -SET @nip= inet_aton('::1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@::1 -SELECT current_user(); -current_user() -root@::1 -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'::1'; -RENAME USER testuser@'::1' to testuser1@'::1'; -SET PASSWORD FOR testuser1@'::1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@::1 -DROP USER testuser1@'::1'; -=============Test of '::1/128' ==================================== -=============Test of '0000:0000:0000:0000:0000:0000:0000:0001' ==== -mysqld is alive -CREATE USER testuser@'0000:0000:0000:0000:0000:0000:0000:0001' identified by '1234'; -GRANT ALL ON test.* TO testuser@'0000:0000:0000:0000:0000:0000:0000:0001'; -SHOW GRANTS FOR testuser@'0000:0000:0000:0000:0000:0000:0000:0001'; -Grants for testuser@0000:0000:0000:0000:0000:0000:0000:0001 -GRANT USAGE ON *.* TO 'testuser'@'0000:0000:0000:0000:0000:0000:0000:0001' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'0000:0000:0000:0000:0000:0000:0000:0001' -SET @nip= inet_aton('0000:0000:0000:0000:0000:0000:0000:0001'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@::1 -SELECT current_user(); -current_user() -root@::1 -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'0000:0000:0000:0000:0000:0000:0000:0001'; -RENAME USER testuser@'0000:0000:0000:0000:0000:0000:0000:0001' to testuser1@'0000:0000:0000:0000:0000:0000:0000:0001'; -SET PASSWORD FOR testuser1@'0000:0000:0000:0000:0000:0000:0000:0001' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@::1 -DROP USER testuser1@'0000:0000:0000:0000:0000:0000:0000:0001'; -=============Test of '0:0:0:0:0:0:0:1' ============================ -mysqld is alive -CREATE USER testuser@'0:0:0:0:0:0:0:1' identified by '1234'; -GRANT ALL ON test.* TO testuser@'0:0:0:0:0:0:0:1'; -SHOW GRANTS FOR testuser@'0:0:0:0:0:0:0:1'; -Grants for testuser@0:0:0:0:0:0:0:1 -GRANT USAGE ON *.* TO 'testuser'@'0:0:0:0:0:0:0:1' IDENTIFIED BY PASSWORD '*A4B6157319038724E3560894F7F932C8886EBFCF' -GRANT ALL PRIVILEGES ON `test`.* TO 'testuser'@'0:0:0:0:0:0:0:1' -SET @nip= inet_aton('0:0:0:0:0:0:0:1'); -SELECT @nip; -@nip -NULL -SELECT inet_ntoa(@nip); -inet_ntoa(@nip) -NULL -SELECT USER(); -USER() -root@::1 -SELECT current_user(); -current_user() -root@::1 -SHOW PROCESSLIST; -REVOKE ALL ON test.* FROM testuser@'0:0:0:0:0:0:0:1'; -RENAME USER testuser@'0:0:0:0:0:0:0:1' to testuser1@'0:0:0:0:0:0:0:1'; -SET PASSWORD FOR testuser1@'0:0:0:0:0:0:0:1' = PASSWORD ('9876'); -SELECT USER(); -USER() -root@::1 -DROP USER testuser1@'0:0:0:0:0:0:0:1'; diff --git a/mysql-test/suite/rpl/r/rpl_ip_mix.result b/mysql-test/suite/rpl/r/rpl_ip_mix.result index 11aa5a46cfa..ddd7a008a97 100644 --- a/mysql-test/suite/rpl/r/rpl_ip_mix.result +++ b/mysql-test/suite/rpl/r/rpl_ip_mix.result @@ -46,102 +46,6 @@ Master-Host: 0:0:0:0:0:0:0:1 disconnect slave; disconnect master; connection default; -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0:0:0:0:0:0:1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0:0:0:0:FFFF:127.0.0.1 ########################### -connect (master,0:0:0:0:0:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 127.0.0.1 -change master to master_host='0:0:0:0:0:FFFF:127.0.0.1'; -Master-Host: 0:0:0:0:0:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 ########################### -connect (master,0000:0000:0000:0000:0000:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0:0:0:0:FFFF:127.0.0.1 -change master to master_host='0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -Master-Host: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0000:0000:0:0000:FFFF:127.0.0.1 ########################### -connect (master,0:0000:0000:0:0000:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 -change master to master_host='0:0000:0000:0:0000:FFFF:127.0.0.1'; -Master-Host: 0:0000:0000:0:0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0::0000:FFFF:127.0.0.1 ########################### -connect (master,0::0000:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0000:0000:0:0000:FFFF:127.0.0.1 -change master to master_host='0::0000:FFFF:127.0.0.1'; -Master-Host: 0::0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::FFFF:127.0.0.1 ########################### -connect (master,::FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0::0000:FFFF:127.0.0.1 -change master to master_host='::FFFF:127.0.0.1'; -Master-Host: ::FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; #################### IP: ::1 mix ####################### connect (master,::1,root,,test,MASTER_MYPORT); connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); @@ -152,11 +56,11 @@ File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 # connection slave; reset slave; -Master-Host: ::FFFF:127.0.0.1 +Master-Host: 0:0:0:0:0:0:0:1 change master to master_host='::1'; Master-Host: ::1 -change master to master_host='::FFFF:127.0.0.1'; -Master-Host: ::FFFF:127.0.0.1 +change master to master_host='127.0.0.1'; +Master-Host: 127.0.0.1 change master to master_host='0:0:0:0:0:0:0:1'; Master-Host: 0:0:0:0:0:0:0:1 disconnect slave; diff --git a/mysql-test/suite/rpl/r/rpl_ip_mix2.result b/mysql-test/suite/rpl/r/rpl_ip_mix2.result index e5cc3e1621d..e1011bf85ee 100644 --- a/mysql-test/suite/rpl/r/rpl_ip_mix2.result +++ b/mysql-test/suite/rpl/r/rpl_ip_mix2.result @@ -14,22 +14,6 @@ Master-Host: ::1 disconnect slave; disconnect master; connection default; -#################### IP: ::1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,::1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: ::1 -change master to master_host='::1'; -Master-Host: ::1 -disconnect slave; -disconnect master; -connection default; #################### IP: 0000:0000:0000:0000:0000:0000:0000:0001 ########################### connect (master,127.0.0.1,root,,test,MASTER_MYPORT); connect (slave,0000:0000:0000:0000:0000:0000:0000:0001,root,,test,SLAVE_MYPORT); @@ -62,102 +46,6 @@ Master-Host: 0:0:0:0:0:0:0:1 disconnect slave; disconnect master; connection default; -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0:0:0:0:0:0:1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0:0:0:0:FFFF:127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0:0:0:0:0:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 127.0.0.1 -change master to master_host='0:0:0:0:0:FFFF:127.0.0.1'; -Master-Host: 0:0:0:0:0:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0000:0000:0000:0000:0000:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0:0:0:0:FFFF:127.0.0.1 -change master to master_host='0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -Master-Host: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0000:0000:0:0000:FFFF:127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0:0000:0000:0:0000:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 -change master to master_host='0:0000:0000:0:0000:FFFF:127.0.0.1'; -Master-Host: 0:0000:0000:0:0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0::0000:FFFF:127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0::0000:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0000:0000:0:0000:FFFF:127.0.0.1 -change master to master_host='0::0000:FFFF:127.0.0.1'; -Master-Host: 0::0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::FFFF:127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,::FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0::0000:FFFF:127.0.0.1 -change master to master_host='::FFFF:127.0.0.1'; -Master-Host: ::FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; #################### IP: ::1 mix ####################### connect (master,127.0.0.1,root,,test,MASTER_MYPORT); connect (slave,::1,root,,test,SLAVE_MYPORT); @@ -168,11 +56,11 @@ File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 # connection slave; reset slave; -Master-Host: ::FFFF:127.0.0.1 +Master-Host: 0:0:0:0:0:0:0:1 change master to master_host='::1'; Master-Host: ::1 -change master to master_host='::FFFF:127.0.0.1'; -Master-Host: ::FFFF:127.0.0.1 +change master to master_host='127.0.0.1'; +Master-Host: 127.0.0.1 change master to master_host='0:0:0:0:0:0:0:1'; Master-Host: 0:0:0:0:0:0:0:1 disconnect slave; diff --git a/mysql-test/suite/rpl/r/rpl_ip_mix2_win.result b/mysql-test/suite/rpl/r/rpl_ip_mix2_win.result deleted file mode 100644 index c7cec70ed6c..00000000000 --- a/mysql-test/suite/rpl/r/rpl_ip_mix2_win.result +++ /dev/null @@ -1,84 +0,0 @@ -#################### IP: ::1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,::1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: No such row -change master to master_host='::1'; -Master-Host: ::1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0000:0000:0000:0000:0000:0000:0000:0001 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0000:0000:0000:0000:0000:0000:0000:0001,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: ::1 -change master to master_host='0000:0000:0000:0000:0000:0000:0000:0001'; -Master-Host: 0000:0000:0000:0000:0000:0000:0000:0001 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0:0:0:0:0:0:1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0:0:0:0:0:0:0:1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0000:0000:0000:0000:0000:0000:0000:0001 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0:0:0:0:0:0:1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::1 mix ####################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,::1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 127.0.0.1 -change master to master_host='::1'; -Master-Host: ::1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; diff --git a/mysql-test/suite/rpl/r/rpl_ip_mix_win.result b/mysql-test/suite/rpl/r/rpl_ip_mix_win.result deleted file mode 100644 index a1d11e109e5..00000000000 --- a/mysql-test/suite/rpl/r/rpl_ip_mix_win.result +++ /dev/null @@ -1,84 +0,0 @@ -#################### IP: ::1 ########################### -connect (master,::1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: No such row -change master to master_host='::1'; -Master-Host: ::1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0000:0000:0000:0000:0000:0000:0000:0001 ########################### -connect (master,0000:0000:0000:0000:0000:0000:0000:0001,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: ::1 -change master to master_host='0000:0000:0000:0000:0000:0000:0000:0001'; -Master-Host: 0000:0000:0000:0000:0000:0000:0000:0001 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0:0:0:0:0:0:1 ########################### -connect (master,0:0:0:0:0:0:0:1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0000:0000:0000:0000:0000:0000:0000:0001 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 0:0:0:0:0:0:0:1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::1 mix ####################### -connect (master,::1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 127.0.0.1 -change master to master_host='::1'; -Master-Host: ::1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; diff --git a/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6.result b/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6.result index 4168cff1e97..0393cfa483c 100644 --- a/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6.result +++ b/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6.result @@ -88,7 +88,7 @@ Master-Host: ::FFFF:127.0.0.1 disconnect slave; disconnect master; connection default; -#################### IP: ::1 ########################### +#################### IP: ::FFFF:127.0.0.1 ########################### connect (master,127.0.0.1,root,,test,MASTER_MYPORT); connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); connection master; @@ -99,12 +99,12 @@ master-bin.000001 # connection slave; reset slave; Master-Host: ::FFFF:127.0.0.1 -change master to master_host='::1'; -Master-Host: ::1 change master to master_host='::FFFF:127.0.0.1'; Master-Host: ::FFFF:127.0.0.1 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 +change master to master_host='127.0.0.1'; +Master-Host: 127.0.0.1 +change master to master_host='0:0000:0000:0:0000:FFFF:127.0.0.1'; +Master-Host: 0:0000:0000:0:0000:FFFF:127.0.0.1 disconnect slave; disconnect master; connection default; diff --git a/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6_win.result b/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6_win.result deleted file mode 100644 index a1e48ea6610..00000000000 --- a/mysql-test/suite/rpl/r/rpl_ipv4_as_ipv6_win.result +++ /dev/null @@ -1,35 +0,0 @@ -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 127.0.0.1 -change master to master_host='::1'; -Master-Host: ::1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; diff --git a/mysql-test/suite/rpl/r/rpl_ipv6.result b/mysql-test/suite/rpl/r/rpl_ipv6.result index f46e60548a6..6ae946d197b 100644 --- a/mysql-test/suite/rpl/r/rpl_ipv6.result +++ b/mysql-test/suite/rpl/r/rpl_ipv6.result @@ -43,96 +43,6 @@ Master-Host: 0:0:0:0:0:0:0:1 disconnect slave; disconnect master; connection default; -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0:0:0:0:FFFF:127.0.0.1 ########################### -connect (master,0:0:0:0:0:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0:0:0:0:0:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='0:0:0:0:0:FFFF:127.0.0.1'; -Master-Host: 0:0:0:0:0:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 ########################### -connect (master,0000:0000:0000:0000:0000:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0000:0000:0000:0000:0000:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='0000:0000:0000:0000:0000:FFFF:127.0.0.1'; -Master-Host: 0000:0000:0000:0000:0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0000:0000:0:0000:FFFF:127.0.0.1 ########################### -connect (master,0:0000:0000:0:0000:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0:0000:0000:0:0000:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='0:0000:0000:0:0000:FFFF:127.0.0.1'; -Master-Host: 0:0000:0000:0:0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0::0000:FFFF:127.0.0.1 ########################### -connect (master,0::0000:FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,0::0000:FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='0::0000:FFFF:127.0.0.1'; -Master-Host: 0::0000:FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::FFFF:127.0.0.1 ########################### -connect (master,::FFFF:127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,::FFFF:127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='::FFFF:127.0.0.1'; -Master-Host: ::FFFF:127.0.0.1 -disconnect slave; -disconnect master; -connection default; #################### IP: ::1 mix ####################### connect (master,127.0.0.1,root,,test,MASTER_MYPORT); connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); @@ -143,11 +53,11 @@ File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 # connection slave; reset slave; -Master-Host: ::FFFF:127.0.0.1 +Master-Host: 0:0:0:0:0:0:0:1 change master to master_host='::1'; Master-Host: ::1 -change master to master_host='::FFFF:127.0.0.1'; -Master-Host: ::FFFF:127.0.0.1 +change master to master_host='127.0.0.1'; +Master-Host: 127.0.0.1 change master to master_host='0:0:0:0:0:0:0:1'; Master-Host: 0:0:0:0:0:0:0:1 disconnect slave; diff --git a/mysql-test/suite/rpl/r/rpl_ipv6_win.result b/mysql-test/suite/rpl/r/rpl_ipv6_win.result deleted file mode 100644 index 79dc8422161..00000000000 --- a/mysql-test/suite/rpl/r/rpl_ipv6_win.result +++ /dev/null @@ -1,80 +0,0 @@ -#################### IP: ::1 ########################### -connect (master,::1,root,,test,MASTER_MYPORT); -connect (slave,::1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='::1'; -Master-Host: ::1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0000:0000:0000:0000:0000:0000:0000:0001 ########################### -connect (master,0000:0000:0000:0000:0000:0000:0000:0001,root,,test,MASTER_MYPORT); -connect (slave,0000:0000:0000:0000:0000:0000:0000:0001,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='0000:0000:0000:0000:0000:0000:0000:0001'; -Master-Host: 0000:0000:0000:0000:0000:0000:0000:0001 -disconnect slave; -disconnect master; -connection default; -#################### IP: 0:0:0:0:0:0:0:1 ########################### -connect (master,0:0:0:0:0:0:0:1,root,,test,MASTER_MYPORT); -connect (slave,0:0:0:0:0:0:0:1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; -#################### IP: 127.0.0.1 ########################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -disconnect slave; -disconnect master; -connection default; -#################### IP: ::1 mix ####################### -connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connection master; -reset master; -show master status; -File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 # -connection slave; -reset slave; -Master-Host: 127.0.0.1 -change master to master_host='::1'; -Master-Host: ::1 -change master to master_host='127.0.0.1'; -Master-Host: 127.0.0.1 -change master to master_host='0:0:0:0:0:0:0:1'; -Master-Host: 0:0:0:0:0:0:0:1 -disconnect slave; -disconnect master; -connection default; diff --git a/mysql-test/suite/rpl/t/rpl_ip_mix.test b/mysql-test/suite/rpl/t/rpl_ip_mix.test index 68aa2fc87a9..0852b3f4ff7 100644 --- a/mysql-test/suite/rpl/t/rpl_ip_mix.test +++ b/mysql-test/suite/rpl/t/rpl_ip_mix.test @@ -7,8 +7,6 @@ # (see corresponding cnf file) # --source include/check_ipv6.inc -# Can't be tested with windows due to mixed format like 0::0000:FFFF:127.0.0.1 ---source include/not_windows.inc --source include/have_log_bin.inc let $IPv6= ::1; @@ -27,38 +25,6 @@ let $IPv6= 0:0:0:0:0:0:0:1; --echo #################### IP: $IPv6 ########################### --source include/rpl_ip_mix.inc -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= 0000:0000:0000:0000:0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= 0:0000:0000:0:0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= 0::0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -#let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1/96; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ip_mix.inc - -let $IPv6= ::FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -#let $IPv6= ::FFFF:127.0.0.1/96; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ip_mix.inc - let $IPv6= ::1; --echo #################### IP: $IPv6 mix ####################### --echo connect (master,$IPv6,root,,test,MASTER_MYPORT); @@ -78,7 +44,7 @@ let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); eval change master to master_host='$IPv6'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host -eval change master to master_host='::FFFF:127.0.0.1'; +eval change master to master_host='127.0.0.1'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host eval change master to master_host='0:0:0:0:0:0:0:1'; diff --git a/mysql-test/suite/rpl/t/rpl_ip_mix2.test b/mysql-test/suite/rpl/t/rpl_ip_mix2.test index d6435d710c1..11c648dece5 100644 --- a/mysql-test/suite/rpl/t/rpl_ip_mix2.test +++ b/mysql-test/suite/rpl/t/rpl_ip_mix2.test @@ -19,14 +19,8 @@ disconnect checkcon123456789; --enable_abort_on_error --enable_query_log # end check - -# Can't be tested with windows due to mixed format like 0::0000:FFFF:127.0.0.1 ---source include/not_windows.inc --source include/have_log_bin.inc -let $IPv6= ::1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc let $IPv6= ::1; --echo #################### IP: $IPv6 ########################### --source include/rpl_ip_mix2.inc @@ -43,38 +37,6 @@ let $IPv6= 0:0:0:0:0:0:0:1; --echo #################### IP: $IPv6 ########################### --source include/rpl_ip_mix2.inc -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= 0000:0000:0000:0000:0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= 0:0000:0000:0:0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= 0::0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -#let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1/96; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ip_mix2.inc - -let $IPv6= ::FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -#let $IPv6= ::FFFF:127.0.0.1/96; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ip_mix2.inc - let $IPv6= ::1; --echo #################### IP: $IPv6 mix ####################### --echo connect (master,127.0.0.1,root,,test,MASTER_MYPORT); @@ -94,7 +56,7 @@ let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); eval change master to master_host='$IPv6'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host -eval change master to master_host='::FFFF:127.0.0.1'; +eval change master to master_host='127.0.0.1'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host eval change master to master_host='0:0:0:0:0:0:0:1'; diff --git a/mysql-test/suite/rpl/t/rpl_ip_mix2_win.cnf b/mysql-test/suite/rpl/t/rpl_ip_mix2_win.cnf deleted file mode 100644 index 306df437bcc..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ip_mix2_win.cnf +++ /dev/null @@ -1,56 +0,0 @@ -# Use default setting for mysqld processes -!include include/default_mysqld.cnf -!include include/default_client.cnf - -[mysqld.1] - -# Run the master.sh script before starting this process -#!run-master-sh - -log-bin= master-bin - -loose-innodb - -skip-name-resolve -bind-address= 0.0.0.0 - - -[mysqld.2] -# Run the slave.sh script before starting this process -#!run-slave-sh - -# Append -slave.opt file to the list of argument used when -# starting the mysqld -#!use-slave-opt - -log-bin= slave-bin -relay-log= slave-relay-bin - -init-rpl-role= slave -log-slave-updates -master-retry-count= 10 - -# Values reported by slave when it connect to master -# and shows up in SHOW SLAVE STATUS; -report-host= localhost -report-port= @mysqld.2.port -report-user= root - -skip-slave-start -skip-name-resolve -bind-address= :: - -# Directory where slaves find the dumps generated by "load data" -# on the server. The path need to have constant length otherwise -# test results will vary, thus a relative path is used. -slave-load-tmpdir= ../../tmp - -loose-innodb - -[ENV] -MASTER_MYPORT= @mysqld.1.port -MASTER_MYSOCK= @mysqld.1.socket - -SLAVE_MYPORT= @mysqld.2.port -SLAVE_MYSOCK= @mysqld.2.socket - diff --git a/mysql-test/suite/rpl/t/rpl_ip_mix2_win.test b/mysql-test/suite/rpl/t/rpl_ip_mix2_win.test deleted file mode 100644 index f9a2022ddd4..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ip_mix2_win.test +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (C) 2009 SUN Microsystems -# All rights reserved. Use is subject to license terms. -# Author: Horst Hunger -# Nov. 19, 2009 -# Test of ipv6 format, especially "change master host=..." -# Options: --skip-name-resolve, master: --bind-address=0.0.0.0, slave: --bind-address=:: -# (see corresponding cnf file) -# -# Check if ipv6 is available. If not, server is crashing (see BUG#48915). ---disable_query_log ---disable_abort_on_error -connect (checkcon123456789,::1,root,,test,$SLAVE_MYPORT); -if($mysql_errno) -{ -skip wrong IP for slave; -} -connection default; -disconnect checkcon123456789; ---enable_abort_on_error ---enable_query_log -# end check - -# For windows due to missing the mixed format like 0::0000:FFFF:127.0.0.1 ---source include/windows.inc ---source include/have_log_bin.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= ::1/128; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ip_mix2.inc - -let $IPv6= 0000:0000:0000:0000:0000:0000:0000:0001; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= 0:0:0:0:0:0:0:1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix2.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 mix ####################### ---echo connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (master,127.0.0.1,root,,test,$MASTER_MYPORT); ---echo connect (slave,$IPv6,root,,test,SLAVE_MYPORT); -connect (slave,$IPv6,root,,test,$SLAVE_MYPORT); ---echo connection master; -connection master; -reset master; -source include/show_master_status.inc; -save_master_pos; ---echo connection slave; -connection slave; -reset slave; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='$IPv6'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='127.0.0.1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='0:0:0:0:0:0:0:1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host ---echo disconnect slave; -disconnect slave; ---echo disconnect master; -disconnect master; ---echo connection default; -connection default; ---exit diff --git a/mysql-test/suite/rpl/t/rpl_ip_mix_win.cnf b/mysql-test/suite/rpl/t/rpl_ip_mix_win.cnf deleted file mode 100644 index 00e2637d822..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ip_mix_win.cnf +++ /dev/null @@ -1,56 +0,0 @@ -# Use default setting for mysqld processes -!include include/default_mysqld.cnf -!include include/default_client.cnf - -[mysqld.1] - -# Run the master.sh script before starting this process -#!run-master-sh - -log-bin= master-bin - -loose-innodb - -skip-name-resolve -bind-address= :: - - -[mysqld.2] -# Run the slave.sh script before starting this process -#!run-slave-sh - -# Append -slave.opt file to the list of argument used when -# starting the mysqld -#!use-slave-opt - -log-bin= slave-bin -relay-log= slave-relay-bin - -init-rpl-role= slave -log-slave-updates -master-retry-count= 10 - -# Values reported by slave when it connect to master -# and shows up in SHOW SLAVE STATUS; -report-host= localhost -report-port= @mysqld.2.port -report-user= root - -skip-slave-start -skip-name-resolve -bind-address= 0.0.0.0 - -# Directory where slaves find the dumps generated by "load data" -# on the server. The path need to have constant length otherwise -# test results will vary, thus a relative path is used. -slave-load-tmpdir= ../../tmp - -loose-innodb - -[ENV] -MASTER_MYPORT= @mysqld.1.port -MASTER_MYSOCK= @mysqld.1.socket - -SLAVE_MYPORT= @mysqld.2.port -SLAVE_MYSOCK= @mysqld.2.socket - diff --git a/mysql-test/suite/rpl/t/rpl_ip_mix_win.test b/mysql-test/suite/rpl/t/rpl_ip_mix_win.test deleted file mode 100644 index 7d78f71bd1b..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ip_mix_win.test +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (C) 2009 SUN Microsystems -# All rights reserved. Use is subject to license terms. -# Author: Horst Hunger -# Nov. 19, 2009 -# Test of ipv6 format, especially "change master host=..." -# Options: --skip-name-resolve, master: --bind-address=::, slave: --bind-address=0.0.0.0 -# (see corresponding cnf file) -# ---source include/check_ipv6.inc -# For windows due to missing the mixed format like 0::0000:FFFF:127.0.0.1 ---source include/windows.inc ---source include/have_log_bin.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= ::1/128; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ip_mix.inc - -let $IPv6= 0000:0000:0000:0000:0000:0000:0000:0001; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= 0:0:0:0:0:0:0:1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ip_mix.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 mix ####################### ---echo connect (master,$IPv6,root,,test,MASTER_MYPORT); -connect (master,$IPv6,root,,test,$MASTER_MYPORT); ---echo connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT); ---echo connection master; -connection master; -reset master; -source include/show_master_status.inc; -save_master_pos; ---echo connection slave; -connection slave; -reset slave; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='$IPv6'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='127.0.0.1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='0:0:0:0:0:0:0:1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host ---echo disconnect slave; -disconnect slave; ---echo disconnect master; -disconnect master; ---echo connection default; -connection default; ---exit diff --git a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.test b/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.test index f4e5ad6af7b..ecd5a754fdc 100644 --- a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.test +++ b/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.test @@ -6,22 +6,7 @@ # Options: --skip-name-resolve, --bind-address=0.0.0.0 (see corresponding cnf file) # for master and slave # -# Check if ipv4 is available. ---disable_query_log ---disable_abort_on_error -connect (checkcon123456789,127.0.0.1,root,,test); -if($mysql_errno) -{ -skip wrong IP; -} -connection default; -disconnect checkcon123456789; ---enable_abort_on_error ---enable_query_log -# end check - -# Can't be tested with windows due to mixed format like 0::0000:FFFF:127.0.0.1 ---source include/not_windows.inc +--source include/have_ipv4_mapped.inc --source include/have_log_bin.inc let $IPv6= 127.0.0.1; @@ -56,7 +41,7 @@ let $IPv6= ::FFFF:127.0.0.1; #--echo #################### IP: $IPv6 ########################### #--source include/rpl_ipv6.inc -let $IPv6= ::1; +let $IPv6= ::FFFF:127.0.0.1; --echo #################### IP: $IPv6 ########################### --echo connect (master,127.0.0.1,root,,test,MASTER_MYPORT); connect (master,127.0.0.1,root,,test,$MASTER_MYPORT); @@ -75,10 +60,10 @@ let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); eval change master to master_host='$IPv6'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host -eval change master to master_host='::FFFF:127.0.0.1'; +eval change master to master_host='127.0.0.1'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host -eval change master to master_host='0:0:0:0:0:0:0:1'; +eval change master to master_host='0:0000:0000:0:0000:FFFF:127.0.0.1'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host --echo disconnect slave; diff --git a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.cnf b/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.cnf deleted file mode 100644 index b646a4088ff..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.cnf +++ /dev/null @@ -1,56 +0,0 @@ -# Use default setting for mysqld processes -!include include/default_mysqld.cnf -!include include/default_client.cnf - -[mysqld.1] - -# Run the master.sh script before starting this process -#!run-master-sh - -log-bin= master-bin - -loose-innodb - -skip-name-resolve -bind-address= 0.0.0.0 - - -[mysqld.2] -# Run the slave.sh script before starting this process -#!run-slave-sh - -# Append -slave.opt file to the list of argument used when -# starting the mysqld -#!use-slave-opt - -log-bin= slave-bin -relay-log= slave-relay-bin - -init-rpl-role= slave -log-slave-updates -master-retry-count= 10 - -# Values reported by slave when it connect to master -# and shows up in SHOW SLAVE STATUS; -report-host= localhost -report-port= @mysqld.2.port -report-user= root - -skip-slave-start -skip-name-resolve -bind-address= 0.0.0.0 - -# Directory where slaves find the dumps generated by "load data" -# on the server. The path need to have constant length otherwise -# test results will vary, thus a relative path is used. -slave-load-tmpdir= ../../tmp - -loose-innodb - -[ENV] -MASTER_MYPORT= @mysqld.1.port -MASTER_MYSOCK= @mysqld.1.socket - -SLAVE_MYPORT= @mysqld.2.port -SLAVE_MYSOCK= @mysqld.2.socket - diff --git a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.test b/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.test deleted file mode 100644 index db786536c2d..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6_win.test +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) 2009 SUN Microsystems -# All rights reserved. Use is subject to license terms. -# Author: Horst Hunger -# Nov. 19, 2009 -# Test of ipv4 (127.0.0.1) in ipv6 format, especially "change master host=..." -# Options: --skip-name-resolve, --bind-address=0.0.0.0 (see corresponding cnf file) -# for master and slave -# -# Check if ipv4 is available. ---disable_query_log ---disable_abort_on_error -connect (checkcon123456789,127.0.0.1,root,,test); -if($mysql_errno) -{ -skip wrong IP; -} -connection default; -disconnect checkcon123456789; ---enable_abort_on_error ---enable_query_log -# end check - -# For windows due to missing the mixed format like 0::0000:FFFF:127.0.0.1 ---source include/windows.inc ---source include/have_log_bin.inc - -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 ########################### ---echo connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (master,127.0.0.1,root,,test,$MASTER_MYPORT); ---echo connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT); ---echo connection master; -connection master; -reset master; -source include/show_master_status.inc; -save_master_pos; ---echo connection slave; -connection slave; -reset slave; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='$IPv6'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='127.0.0.1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='0:0:0:0:0:0:0:1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host ---echo disconnect slave; -disconnect slave; ---echo disconnect master; -disconnect master; ---echo connection default; -connection default; ---exit - diff --git a/mysql-test/suite/rpl/t/rpl_ipv6.test b/mysql-test/suite/rpl/t/rpl_ipv6.test index 71fedcb4208..3eceedfd7a4 100644 --- a/mysql-test/suite/rpl/t/rpl_ipv6.test +++ b/mysql-test/suite/rpl/t/rpl_ipv6.test @@ -27,38 +27,6 @@ let $IPv6= 0:0:0:0:0:0:0:1; --echo #################### IP: $IPv6 ########################### --source include/rpl_ipv6.inc -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= 0000:0000:0000:0000:0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= 0:0000:0000:0:0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= 0::0000:FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -#let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1/96; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ipv6.inc - -let $IPv6= ::FFFF:127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -#let $IPv6= ::FFFF:127.0.0.1/96; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ipv6.inc - let $IPv6= ::1; --echo #################### IP: $IPv6 mix ####################### --echo connect (master,127.0.0.1,root,,test,MASTER_MYPORT); @@ -78,7 +46,7 @@ let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); eval change master to master_host='$IPv6'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host -eval change master to master_host='::FFFF:127.0.0.1'; +eval change master to master_host='127.0.0.1'; let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); --echo Master-Host: $master_host eval change master to master_host='0:0:0:0:0:0:0:1'; diff --git a/mysql-test/suite/rpl/t/rpl_ipv6_win.cnf b/mysql-test/suite/rpl/t/rpl_ipv6_win.cnf deleted file mode 100644 index c657e7c5115..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ipv6_win.cnf +++ /dev/null @@ -1,56 +0,0 @@ -# Use default setting for mysqld processes -!include include/default_mysqld.cnf -!include include/default_client.cnf - -[mysqld.1] - -# Run the master.sh script before starting this process -#!run-master-sh - -log-bin= master-bin - -loose-innodb - -skip-name-resolve -bind-address= :: - - -[mysqld.2] -# Run the slave.sh script before starting this process -#!run-slave-sh - -# Append -slave.opt file to the list of argument used when -# starting the mysqld -#!use-slave-opt - -log-bin= slave-bin -relay-log= slave-relay-bin - -init-rpl-role= slave -log-slave-updates -master-retry-count= 10 - -# Values reported by slave when it connect to master -# and shows up in SHOW SLAVE STATUS; -report-host= localhost -report-port= @mysqld.2.port -report-user= root - -skip-slave-start -skip-name-resolve -bind-address= :: - -# Directory where slaves find the dumps generated by "load data" -# on the server. The path need to have constant length otherwise -# test results will vary, thus a relative path is used. -slave-load-tmpdir= ../../tmp - -loose-innodb - -[ENV] -MASTER_MYPORT= @mysqld.1.port -MASTER_MYSOCK= @mysqld.1.socket - -SLAVE_MYPORT= @mysqld.2.port -SLAVE_MYSOCK= @mysqld.2.socket - diff --git a/mysql-test/suite/rpl/t/rpl_ipv6_win.test b/mysql-test/suite/rpl/t/rpl_ipv6_win.test deleted file mode 100644 index 7c9d437541a..00000000000 --- a/mysql-test/suite/rpl/t/rpl_ipv6_win.test +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (C) 2009 SUN Microsystems -# All rights reserved. Use is subject to license terms. -# Author: Horst Hunger -# Nov. 19, 2009 -# Test of ipv6 format, especially "change master host=..." -# Options: --skip-name-resolve, --bind-address=:: (see corresponding cnf file) -# for master and slave. -# ---source include/check_ipv6.inc -# For windows due to missing the mixed format like 0::0000:FFFF:127.0.0.1 ---source include/windows.inc ---source include/have_log_bin.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= ::1/128; -#--echo #################### IP: $IPv6 ########################### -#--source include/rpl_ipv6.inc - -let $IPv6= 0000:0000:0000:0000:0000:0000:0000:0001; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= 0:0:0:0:0:0:0:1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= 127.0.0.1; ---echo #################### IP: $IPv6 ########################### ---source include/rpl_ipv6.inc - -let $IPv6= ::1; ---echo #################### IP: $IPv6 mix ####################### ---echo connect (master,127.0.0.1,root,,test,MASTER_MYPORT); -connect (master,127.0.0.1,root,,test,$MASTER_MYPORT); ---echo connect (slave,127.0.0.1,root,,test,SLAVE_MYPORT); -connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT); ---echo connection master; -connection master; -reset master; -source include/show_master_status.inc; -save_master_pos; ---echo connection slave; -connection slave; -reset slave; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='$IPv6'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='127.0.0.1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host -eval change master to master_host='0:0:0:0:0:0:0:1'; -let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1); ---echo Master-Host: $master_host ---echo disconnect slave; -disconnect slave; ---echo disconnect master; -disconnect master; ---echo connection default; -connection default; ---exit diff --git a/mysql-test/t/ipv4_as_ipv6.test b/mysql-test/t/ipv4_as_ipv6.test index 4f4e68e8bfc..ace3c286643 100644 --- a/mysql-test/t/ipv4_as_ipv6.test +++ b/mysql-test/t/ipv4_as_ipv6.test @@ -5,8 +5,7 @@ # Test of ipv4 (127.0.0.1) in ipv6 format # Options: --skip-name-resolve, --bind-address=0.0.0.0 (see corresponding opt file). # -# Can't be tested with windows due to mixed format like 0::0000:FFFF:127.0.0.1 ---source include/not_windows.inc +--source include/have_ipv4_mapped.inc # Can't be tested with embedded server --source include/not_embedded.inc diff --git a/mysql-test/t/ipv4_as_ipv6_win-master.opt b/mysql-test/t/ipv4_as_ipv6_win-master.opt deleted file mode 100644 index f55a8e9209d..00000000000 --- a/mysql-test/t/ipv4_as_ipv6_win-master.opt +++ /dev/null @@ -1 +0,0 @@ ---skip-name-resolve --bind-address=0.0.0.0 diff --git a/mysql-test/t/ipv4_as_ipv6_win.test b/mysql-test/t/ipv4_as_ipv6_win.test deleted file mode 100644 index 04f908a119d..00000000000 --- a/mysql-test/t/ipv4_as_ipv6_win.test +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2009 SUN Microsystems -# All rights reserved. Use is subject to license terms. -# Author: Horst Hunger -# Nov. 19, 2009 -# Test of ipv4 (127.0.0.1) in ipv6 format -# Options: --skip-name-resolve, --bind-address=0.0.0.0 (see corresponding opt file). -# -# For windows due to missing the mixed format like 0::0000:FFFF:127.0.0.1 ---source include/windows.inc -# Can't be tested with embedded server ---source include/not_embedded.inc - -# Save the initial number of concurrent sessions ---source include/count_sessions.inc - -echo =============Test of '127.0.0.1' (IPv4) ===========================; -let $IPv6= 127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '::1' ========================; -let $IPv6= ::1; ---echo connect (con1, $IPv6, root, , test, MASTER_MYPORT); ---disable_query_log ---error 2003,2006 -connect (con1, $IPv6, root, , test, $MASTER_MYPORT); ---enable_query_log -connection default; - -# Wait till all disconnects are completed ---source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/ipv6.test b/mysql-test/t/ipv6.test index a1515e9d533..5c08cde3746 100644 --- a/mysql-test/t/ipv6.test +++ b/mysql-test/t/ipv6.test @@ -6,9 +6,6 @@ # Options: --skip-name-resolve, --bind-address=:: (see corresponding opt file). # --source include/check_ipv6.inc - -# Can't be tested with windows due to the mixed format like 0:0:0:0:0:FFFF:127.0.0.1 ---source include/not_windows.inc # Can't be tested with embedded server --source include/not_embedded.inc @@ -35,45 +32,5 @@ let $IPv6= 0:0:0:0:0:0:0:1; --source include/ipv6_clients.inc --source include/ipv6.inc -echo =============Test of '127.0.0.1' (IPv4) ===========================; -let $IPv6= 127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '0:0:0:0:0:FFFF:127.0.0.1' ===================; -let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '0000:0000:0000:0000:0000:FFFF:127.0.0.1' ====; -let $IPv6= 0000:0000:0000:0000:0000:FFFF:127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '0:0000:0000:0:0000:FFFF:127.0.0.1' ====; -let $IPv6= 0:0000:0000:0:0000:FFFF:127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '0::0000:FFFF:127.0.0.1' ====; -let $IPv6= 0::0000:FFFF:127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '0:0:0:0:0:FFFF:127.0.0.1/96' ================; -let $IPv6= 0:0:0:0:0:FFFF:127.0.0.1/96; -#--source include/ipv6_clients.inc -#--source include/ipv6.inc - -echo =============Test of '::FFFF:127.0.0.1' ===========================; -let $IPv6= ::FFFF:127.0.0.1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '::FFFF:127.0.0.1/96' ========================; -let $IPv6= ::FFFF:127.0.0.1/96; -#--source include/ipv6_clients.inc -#--source include/ipv6.inc - # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/ipv6_win-master.opt b/mysql-test/t/ipv6_win-master.opt deleted file mode 100644 index d705811808c..00000000000 --- a/mysql-test/t/ipv6_win-master.opt +++ /dev/null @@ -1 +0,0 @@ ---skip-name-resolve --bind-address=:: diff --git a/mysql-test/t/ipv6_win.test b/mysql-test/t/ipv6_win.test deleted file mode 100644 index 86d867cc448..00000000000 --- a/mysql-test/t/ipv6_win.test +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2009 SUN Microsystems -# All rights reserved. Use is subject to license terms. -# Author: Horst Hunger -# Nov. 19, 2009 -# Test of ipv6 format -# Options: --skip-name-resolve, --bind-address=:: (see corresponding opt file). -# ---source include/check_ipv6.inc - -# For windows due to missing the mixed format like 0::0000:FFFF:127.0.0.1 ---source include/windows.inc -# Can't be tested with embedded server ---source include/not_embedded.inc - -# Save the initial number of concurrent sessions ---source include/count_sessions.inc - -echo =============Test of '::1' ========================================; -let $IPv6= ::1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '::1/128' ====================================; -let $IPv6= ::1/128; -#--source include/ipv6_clients.inc -#--source include/ipv6.inc - -echo =============Test of '0000:0000:0000:0000:0000:0000:0000:0001' ====; -let $IPv6= 0000:0000:0000:0000:0000:0000:0000:0001; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -echo =============Test of '0:0:0:0:0:0:0:1' ============================; -let $IPv6= 0:0:0:0:0:0:0:1; ---source include/ipv6_clients.inc ---source include/ipv6.inc - -# Wait till all disconnects are completed ---source include/wait_until_count_sessions.inc From 7db8e764714ddb7901400000fa1dfe45263b4773 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Fri, 29 Jan 2010 15:55:35 +0200 Subject: [PATCH 327/466] Bug #50192 Strange effect in replication test, trigger, auto_increment The auto-inc unsafe warning makes sense even though it's just one auto-inc table could be involved via a trigger or a stored function. However its content was not updated by bug@45677 fixes continuing to mention two tables whereas the fixes refined semantics of replication of auto_increment in stored routine. Fixed with updating the error message, renaming the error and an internal unsafe-condition constants. A documentation notice ====================== Inserting into an autoincrement column in a stored function or a trigger is unsafe for replication. Even with just one autoincrement column, if the routine is invoked more than once slave is not guaranteed to execute the statement graph same way as the master. And since it's impossible to estimate how many times a routine can be invoked at the query pre-execution phase (see lock_tables), the statement is marked pessimistically unsafe. --- .../binlog/r/binlog_stm_unsafe_warning.result | 27 ++++++++++++++++ .../binlog/t/binlog_stm_unsafe_warning.test | 32 +++++++++++++++++++ sql/share/errmsg-utf8.txt | 4 +-- sql/share/errmsg.txt | 4 +-- sql/sql_base.cc | 2 +- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 14 ++++---- 7 files changed, 73 insertions(+), 12 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result b/mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result index c2445aa1d1a..e8fd7b82bc4 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result +++ b/mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result @@ -1,5 +1,6 @@ call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted. .*"); call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement uses a system function whose value may differ on slave. .*"); +call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column"); ### NOT filtered database => assertion: warnings ARE shown DROP TABLE IF EXISTS t1; CREATE TABLE t1 (a int, b int, primary key (a)); @@ -50,3 +51,29 @@ SET GLOBAL log_warnings = @old_log_warnings; # Count the number of times the "Unsafe" message was printed # to the error log. Occurrences: 1 +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int auto_increment primary key, b int); +CREATE TRIGGER tr_bug50192 AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t2 (b) VALUES (1); +CREATE FUNCTION sf_bug50192() RETURNS INTEGER +BEGIN +INSERT INTO t2(b) VALUES(2); +RETURN 1; +END | +INSERT INTO t1 VALUES (0); +Warnings: +Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column which is unsafe to binlog in STATEMENT format because slave may execute it non-deterministically. +SHOW WARNINGS; +Level Code Message +Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column which is unsafe to binlog in STATEMENT format because slave may execute it non-deterministically. +SELECT sf_bug50192(); +sf_bug50192() +1 +Warnings: +Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column which is unsafe to binlog in STATEMENT format because slave may execute it non-deterministically. +SHOW WARNINGS; +Level Code Message +Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column which is unsafe to binlog in STATEMENT format because slave may execute it non-deterministically. +DROP FUNCTION sf_bug50192; +DROP TRIGGER tr_bug50192; +DROP TABLE t1, t2; diff --git a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test index 35235ce951a..874bb015a07 100644 --- a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test +++ b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test @@ -27,6 +27,7 @@ -- source include/have_binlog_format_statement.inc call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted. .*"); call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement uses a system function whose value may differ on slave. .*"); +call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column"); -- echo ### NOT filtered database => assertion: warnings ARE shown @@ -117,3 +118,34 @@ perl; print "Occurrences: $count\n"; close(FILE); EOF + +# bug#50192: diplaying the unsafe warning comes out to the user warning stack + +-- disable_warnings +DROP TABLE IF EXISTS t1, t2; +-- enable_warnings + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int auto_increment primary key, b int); +CREATE TRIGGER tr_bug50192 AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t2 (b) VALUES (1); + +DELIMITER |; + +CREATE FUNCTION sf_bug50192() RETURNS INTEGER +BEGIN + INSERT INTO t2(b) VALUES(2); + RETURN 1; +END | + +DELIMITER ;| + +INSERT INTO t1 VALUES (0); +SHOW WARNINGS; +SELECT sf_bug50192(); +SHOW WARNINGS; + +# cleanup + +DROP FUNCTION sf_bug50192; +DROP TRIGGER tr_bug50192; +DROP TABLE t1, t2; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 92c86cf6e00..f7094e8734d 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6280,8 +6280,8 @@ ER_BINLOG_UNSAFE_INSERT_DELAYED eng "Statement uses INSERT DELAYED. This is unsafe because the time when rows are inserted cannot be predicted." ER_BINLOG_UNSAFE_SYSTEM_TABLE eng "Statement uses the general_log, slow_log or performance_schema table(s). This is unsafe because system tables may differ on slave." -ER_BINLOG_UNSAFE_TWO_AUTOINC_COLUMNS - eng "Statement updates two AUTO_INCREMENT columns. This is unsafe because the generated value cannot be predicted by slave." +ER_BINLOG_UNSAFE_AUTOINC_COLUMNS + eng "Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column which is unsafe to binlog in STATEMENT format because slave may execute it non-deterministically." ER_BINLOG_UNSAFE_UDF eng "Statement uses a UDF. It cannot be determined if the UDF will return the same value on slave." ER_BINLOG_UNSAFE_SYSTEM_VARIABLE diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 31e630d5fea..60be5723559 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6221,8 +6221,8 @@ ER_BINLOG_UNSAFE_INSERT_DELAYED eng "Statement uses INSERT DELAYED. This is unsafe because the time when rows are inserted cannot be predicted." ER_BINLOG_UNSAFE_SYSTEM_TABLE eng "Statement uses the general_log, slow_log or performance_schema table(s). This is unsafe because system tables may differ on slave." -ER_BINLOG_UNSAFE_TWO_AUTOINC_COLUMNS - eng "Statement updates two AUTO_INCREMENT columns. This is unsafe because the generated value cannot be predicted by slave." +ER_BINLOG_UNSAFE_AUTOINC_COLUMNS + eng "Statement invokes a trigger or a stored function that inserts into AUTO_INCREMENT column which is unsafe to binlog in STATEMENT format because slave may execute it non-deterministically." ER_BINLOG_UNSAFE_UDF eng "Statement uses a UDF. It cannot be determined if the UDF will return the same value on slave." ER_BINLOG_UNSAFE_SYSTEM_VARIABLE diff --git a/sql/sql_base.cc b/sql/sql_base.cc index eb59600b360..e7f014d39a5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5181,7 +5181,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables && has_write_table_with_auto_increment(thd->lex->first_not_own_table())) { - thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_TWO_AUTOINC_COLUMNS); + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS); thd->set_current_stmt_binlog_format_row_if_mixed(); } } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 0d423ba85eb..6da734592dc 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -48,7 +48,7 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] = ER_BINLOG_UNSAFE_LIMIT, ER_BINLOG_UNSAFE_INSERT_DELAYED, ER_BINLOG_UNSAFE_SYSTEM_TABLE, - ER_BINLOG_UNSAFE_TWO_AUTOINC_COLUMNS, + ER_BINLOG_UNSAFE_AUTOINC_COLUMNS, ER_BINLOG_UNSAFE_UDF, ER_BINLOG_UNSAFE_SYSTEM_VARIABLE, ER_BINLOG_UNSAFE_SYSTEM_FUNCTION, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 800a16cf2b6..923e924dfbc 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1101,13 +1101,15 @@ public: */ BINLOG_STMT_UNSAFE_SYSTEM_TABLE, /** - Update of two autoincrement columns is unsafe. With one - autoincrement column, we store the counter in the binlog so that - slave can restore the correct value. But we can only store one - such counter per statement, so updating more than one - autoincrement column is not safe. + Inserting into an autoincrement column in a stored routine is unsafe. + Even with just one autoincrement column, if the routine is invoked more than + once slave is not guaranteed to execute the statement graph same way as + the master. + And since it's impossible to estimate how many times a routine can be invoked at + the query pre-execution phase (see lock_tables), the statement is marked + pessimistically unsafe. */ - BINLOG_STMT_UNSAFE_TWO_AUTOINC_COLUMNS, + BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS, /** Using a UDF (user-defined function) is unsafe. */ From 85589577e728fa3ada5591a6988c1017c105be83 Mon Sep 17 00:00:00 2001 From: Date: Sun, 31 Jan 2010 02:26:51 +0800 Subject: [PATCH 328/466] BUG#50157 Assertion !active_tranxs_->is_tranx_end_pos(..) in ReplSemiSyncMaster::commitTrx The root cause of the crash is that a TranxNode is freed before it is used. A TranxNode is allocated and inserted into the active list each time a log event is written and flushed into the binlog file. The memory for TranxNode is allocated with thd_alloc and will be freed at the end of the statement. The after_commit/after_rollback callback was supposed to be called before the end of each statement and remove the node from the active list. However this assumption is not correct in all cases(e.g. call 'CREATE TEMPORARY TABLE myisam_t SELECT * FROM innodb_t' in a transaction and delete all temporary tables automatically when a session closed), and can cause the memory allocated for TranxNode be freed before it was removed from the active list. So The TranxNode pointer in the active list would become a wild pointer and cause the crash. After this patch, We have a class called a TranxNodeAllocate which manages the memory for allocating and freeing TranxNode. It uses my_malloc to allocate memory. --- mysql-test/suite/rpl/r/rpl_semi_sync.result | 31 ++- mysql-test/suite/rpl/t/rpl_semi_sync.test | 33 ++- plugin/semisync/semisync_master.cc | 25 +- plugin/semisync/semisync_master.h | 284 ++++++++++++++++++-- sql/rpl_handler.cc | 10 +- 5 files changed, 335 insertions(+), 48 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync.result b/mysql-test/suite/rpl/r/rpl_semi_sync.result index 1e220b28d78..74eb14d33e0 100644 --- a/mysql-test/suite/rpl/r/rpl_semi_sync.result +++ b/mysql-test/suite/rpl/r/rpl_semi_sync.result @@ -120,8 +120,27 @@ min(a) select max(a) from t1; max(a) 300 + +# BUG#50157 +# semi-sync replication crashes when replicating a transaction which +# include 'CREATE TEMPORARY TABLE `MyISAM_t` SELECT * FROM `Innodb_t` ; +[ on master ] +SET SESSION AUTOCOMMIT= 0; +CREATE TABLE t2(c1 INT) ENGINE=innodb; +BEGIN; + +# Even though it is in a transaction, this statement is binlogged into binlog +# file immediately. +CREATE TEMPORARY TABLE t3 SELECT c1 FROM t2 where 1=1; + +# These statements will not be binlogged until the transaction is committed +INSERT INTO t2 VALUES(11); +INSERT INTO t2 VALUES(22); +COMMIT; +DROP TABLE t2, t3; +SET SESSION AUTOCOMMIT= 1; # -# Test semi-sync master will switch OFF after one transacton +# Test semi-sync master will switch OFF after one transaction # timeout waiting for slave reply. # include/stop_slave.inc @@ -135,7 +154,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 0 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 1 @@ -150,7 +169,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 1 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 insert into t1 values (100); [ master status should be OFF ] show status like 'Rpl_semi_sync_master_status'; @@ -161,7 +180,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 302 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 # # Test semi-sync status on master will be ON again when slave catches up # @@ -194,7 +213,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 302 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 1 @@ -213,7 +232,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 302 SHOW STATUS LIKE 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 302 +Rpl_semi_sync_master_yes_tx 305 FLUSH NO_WRITE_TO_BINLOG STATUS; [ Semi-sync master status variables after FLUSH STATUS ] SHOW STATUS LIKE 'Rpl_semi_sync_master_no_tx'; diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync.test b/mysql-test/suite/rpl/t/rpl_semi_sync.test index 4900acc1e91..b04541aba21 100644 --- a/mysql-test/suite/rpl/t/rpl_semi_sync.test +++ b/mysql-test/suite/rpl/t/rpl_semi_sync.test @@ -11,6 +11,7 @@ disable_query_log; connection master; call mtr.add_suppression("Timeout waiting for reply of binlog"); call mtr.add_suppression("Read semi-sync reply"); +call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT."); connection slave; call mtr.add_suppression("Master server does not support semi-sync"); call mtr.add_suppression("Semi-sync slave .* reply"); @@ -193,8 +194,38 @@ select count(distinct a) from t1; select min(a) from t1; select max(a) from t1; +--echo +--echo # BUG#50157 +--echo # semi-sync replication crashes when replicating a transaction which +--echo # include 'CREATE TEMPORARY TABLE `MyISAM_t` SELECT * FROM `Innodb_t` ; + +connection master; +echo [ on master ]; +SET SESSION AUTOCOMMIT= 0; +CREATE TABLE t2(c1 INT) ENGINE=innodb; +sync_slave_with_master; + +connection master; +BEGIN; +--echo +--echo # Even though it is in a transaction, this statement is binlogged into binlog +--echo # file immediately. +--disable_warnings +CREATE TEMPORARY TABLE t3 SELECT c1 FROM t2 where 1=1; +--enable_warnings +--echo +--echo # These statements will not be binlogged until the transaction is committed +INSERT INTO t2 VALUES(11); +INSERT INTO t2 VALUES(22); +COMMIT; + +DROP TABLE t2, t3; +SET SESSION AUTOCOMMIT= 1; +sync_slave_with_master; + + --echo # ---echo # Test semi-sync master will switch OFF after one transacton +--echo # Test semi-sync master will switch OFF after one transaction --echo # timeout waiting for slave reply. --echo # connection slave; diff --git a/plugin/semisync/semisync_master.cc b/plugin/semisync/semisync_master.cc index c2e329e1fe4..bbbc2a669c4 100644 --- a/plugin/semisync/semisync_master.cc +++ b/plugin/semisync/semisync_master.cc @@ -65,7 +65,7 @@ static int gettimeofday(struct timeval *tv, void *tz) ActiveTranx::ActiveTranx(pthread_mutex_t *lock, unsigned long trace_level) - : Trace(trace_level), + : Trace(trace_level), allocator_(max_connections), num_entries_(max_connections << 1), /* Transaction hash table size * is set to double the size * of max_connections */ @@ -115,25 +115,6 @@ unsigned int ActiveTranx::get_hash_value(const char *log_file_name, return (hash1 + hash2) % num_entries_; } -ActiveTranx::TranxNode* ActiveTranx::alloc_tranx_node() -{ - MYSQL_THD thd= (MYSQL_THD)current_thd; - /* The memory allocated for TranxNode will be automatically freed at - the end of the command of current THD. And because - ha_autocommit_or_rollback() will always be called before that, so - we are sure that the node will be removed from the active list - before it get freed. */ - TranxNode *trx_node = (TranxNode *)thd_alloc(thd, sizeof(TranxNode)); - if (trx_node) - { - trx_node->log_name_[0] = '\0'; - trx_node->log_pos_= 0; - trx_node->next_= 0; - trx_node->hash_next_= 0; - } - return trx_node; -} - int ActiveTranx::compare(const char *log_file_name1, my_off_t log_file_pos1, const char *log_file_name2, my_off_t log_file_pos2) { @@ -159,7 +140,7 @@ int ActiveTranx::insert_tranx_node(const char *log_file_name, function_enter(kWho); - ins_node = alloc_tranx_node(); + ins_node = allocator_.allocate_node(); if (!ins_node) { sql_print_error("%s: transaction node allocation failed for: (%s, %lu)", @@ -271,6 +252,7 @@ int ActiveTranx::clear_active_tranx_nodes(const char *log_file_name, /* Clear the hash table. */ memset(trx_htb_, 0, num_entries_ * sizeof(TranxNode *)); + allocator_.free_all_nodes(); /* Clear the active transaction list. */ if (trx_front_ != NULL) @@ -311,6 +293,7 @@ int ActiveTranx::clear_active_tranx_nodes(const char *log_file_name, } trx_front_ = new_front; + allocator_.free_nodes_before(trx_front_); if (trace_level_ & kTraceDetail) sql_print_information("%s: cleared %d nodes back until pos (%s, %lu)", diff --git a/plugin/semisync/semisync_master.h b/plugin/semisync/semisync_master.h index bfb1cb74cd0..982a7f77a59 100644 --- a/plugin/semisync/semisync_master.h +++ b/plugin/semisync/semisync_master.h @@ -20,6 +20,267 @@ #include "semisync.h" +struct TranxNode { + char log_name_[FN_REFLEN]; + my_off_t log_pos_; + struct TranxNode *next_; /* the next node in the sorted list */ + struct TranxNode *hash_next_; /* the next node during hash collision */ +}; + +/** + @class TranxNodeAllocator + + This class provides memory allocating and freeing methods for + TranxNode. The main target is performance. + + @section ALLOCATE How to allocate a node + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + The list starts up empty (ie, there is no allocated Block). + + After some nodes are freed, there probably are some free nodes before + the sequence of the allocated nodes, but we do not reuse it. It is better + to keep the allocated nodes are in the sequence, for it is more efficient + for allocating and freeing TranxNode. + + @section FREENODE How to free nodes + There are two methods for freeing nodes. They are free_all_nodes and + free_nodes_before. + + 'A Block is free' means all of its nodes are free. + @subsection free_nodes_before + As all allocated nodes are in the sequence, 'Before one node' means all + nodes before given node in the same Block and all Blocks before the Block + which containing the given node. As such, all Blocks before the given one + ('node') are free Block and moved into the rear of the Block link table. + The Block containing the given 'node', however, is not. For at least the + given 'node' is still in use. This will waste at most one Block, but it is + more efficient. + */ +#define BLOCK_TRANX_NODES 16 +class TranxNodeAllocator +{ +public: + /** + @param reserved_nodes + The number of reserved TranxNodes. It is used to set 'reserved_blocks' + which can contain at least 'reserved_nodes' number of TranxNodes. When + freeing memory, we will reserve at least reserved_blocks of Blocks not + freed. + */ + TranxNodeAllocator(uint reserved_nodes) : + reserved_blocks(reserved_nodes/BLOCK_TRANX_NODES + + (reserved_nodes%BLOCK_TRANX_NODES > 1 ? 2 : 1)), + first_block(NULL), last_block(NULL), + current_block(NULL), last_node(-1), block_num(0) {} + + ~TranxNodeAllocator() + { + Block *block= first_block; + while (block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + } + + /** + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + @return Return a TranxNode *, or NULL if an error occured. + */ + TranxNode *allocate_node() + { + TranxNode *trx_node; + Block *block= current_block; + + if (last_node == BLOCK_TRANX_NODES-1) + { + current_block= current_block->next; + last_node= -1; + } + + if (current_block == NULL && allocate_block()) + { + current_block= block; + if (current_block) + last_node= BLOCK_TRANX_NODES-1; + return NULL; + } + + trx_node= &(current_block->nodes[++last_node]); + trx_node->log_name_[0] = '\0'; + trx_node->log_pos_= 0; + trx_node->next_= 0; + trx_node->hash_next_= 0; + return trx_node; + } + + /** + All nodes are freed. + + @return Return 0, or 1 if an error occured. + */ + int free_all_nodes() + { + current_block= first_block; + last_node= -1; + free_blocks(); + return 0; + } + + /** + All Blocks before the given 'node' are free Block and moved into the rear + of the Block link table. + + @param node All nodes before 'node' will be freed + + @return Return 0, or 1 if an error occured. + */ + int free_nodes_before(TranxNode* node) + { + Block *block; + Block *prev_block; + + block= first_block; + while (block != current_block->next) + { + /* Find the Block containing the given node */ + if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node) + { + /* All Blocks before the given node are put into the rear */ + if (first_block != block) + { + last_block->next= first_block; + first_block= block; + last_block= prev_block; + last_block->next= NULL; + free_blocks(); + } + return 0; + } + prev_block= block; + block= block->next; + } + + /* Node does not find should never happen */ + DBUG_ASSERT(0); + return 1; + } + +private: + uint reserved_blocks; + + /** + A sequence memory which contains BLOCK_TRANX_NODES TranxNodes. + + BLOCK_TRANX_NODES The number of TranxNodes which are in a Block. + + next Every Block has a 'next' pointer which points to the next Block. + These linking Blocks constitute a Block link table. + */ + struct Block { + Block *next; + TranxNode nodes[BLOCK_TRANX_NODES]; + }; + + /** + The 'first_block' is the head of the Block link table; + */ + Block *first_block; + /** + The 'last_block' is the rear of the Block link table; + */ + Block *last_block; + + /** + current_block always points the Block in the Block link table in + which the last allocated node is. The Blocks before it are all in use + and the Blocks after it are all free. + */ + Block *current_block; + + /** + It always points to the last node which has been allocated in the + current_block. + */ + int last_node; + + /** + How many Blocks are in the Block link table. + */ + uint block_num; + + /** + Allocate a block and then assign it to current_block. + */ + int allocate_block() + { + Block *block= (Block *)my_malloc(sizeof(Block), MYF(0)); + if (block) + { + block->next= NULL; + + if (first_block == NULL) + first_block= block; + else + last_block->next= block; + + /* New Block is always put into the rear */ + last_block= block; + /* New Block is always the current_block */ + current_block= block; + ++block_num; + return 0; + } + return 1; + } + + /** + Free a given Block. + @param block The Block will be freed. + */ + void free_block(Block *block) + { + my_free(block, MYF(0)); + --block_num; + } + + + /** + If there are some free Blocks and the total number of the Blocks in the + Block link table is larger than the 'reserved_blocks', Some free Blocks + will be freed until the total number of the Blocks is equal to the + 'reserved_blocks' or there is only one free Block behind the + 'current_block'. + */ + void free_blocks() + { + if (current_block == NULL || current_block->next == NULL) + return; + + /* One free Block is always kept behind the current block */ + Block *block= current_block->next->next; + while (block_num > reserved_blocks && block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + current_block->next->next= block; + if (block == NULL) + last_block= current_block->next; + } +}; + + /** This class manages memory for active transaction list. @@ -31,13 +292,8 @@ class ActiveTranx :public Trace { private: - struct TranxNode { - char log_name_[FN_REFLEN]; - my_off_t log_pos_; - struct TranxNode *next_; /* the next node in the sorted list */ - struct TranxNode *hash_next_; /* the next node during hash collision */ - }; + TranxNodeAllocator allocator_; /* These two record the active transaction list in sort order. */ TranxNode *trx_front_, *trx_rear_; @@ -48,24 +304,22 @@ private: inline void assert_lock_owner(); - inline TranxNode* alloc_tranx_node(); - inline unsigned int calc_hash(const unsigned char *key,unsigned int length); unsigned int get_hash_value(const char *log_file_name, my_off_t log_file_pos); int compare(const char *log_file_name1, my_off_t log_file_pos1, - const TranxNode *node2) { + const TranxNode *node2) { return compare(log_file_name1, log_file_pos1, - node2->log_name_, node2->log_pos_); + node2->log_name_, node2->log_pos_); } int compare(const TranxNode *node1, - const char *log_file_name2, my_off_t log_file_pos2) { + const char *log_file_name2, my_off_t log_file_pos2) { return compare(node1->log_name_, node1->log_pos_, - log_file_name2, log_file_pos2); + log_file_name2, log_file_pos2); } int compare(const TranxNode *node1, const TranxNode *node2) { return compare(node1->log_name_, node1->log_pos_, - node2->log_name_, node2->log_pos_); + node2->log_name_, node2->log_pos_); } public: @@ -88,7 +342,7 @@ public: * 0: success; non-zero: error */ int clear_active_tranx_nodes(const char *log_file_name, - my_off_t log_file_pos); + my_off_t log_file_pos); /* Given a position, check to see whether the position is an active * transaction's ending position by probing the hash table. @@ -99,7 +353,7 @@ public: * (file_name, file_position). */ static int compare(const char *log_file_name1, my_off_t log_file_pos1, - const char *log_file_name2, my_off_t log_file_pos2); + const char *log_file_name2, my_off_t log_file_pos2); }; diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc index c4b55e3d068..b347b7c751d 100644 --- a/sql/rpl_handler.cc +++ b/sql/rpl_handler.cc @@ -190,8 +190,8 @@ int Trans_delegate::after_commit(THD *thd, bool all) { Trans_param param; bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - if (is_real_trans) - param.flags |= TRANS_IS_REAL_TRANS; + + param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; Trans_binlog_info *log_info= my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); @@ -218,8 +218,8 @@ int Trans_delegate::after_rollback(THD *thd, bool all) { Trans_param param; bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - if (is_real_trans) - param.flags |= TRANS_IS_REAL_TRANS; + + param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; Trans_binlog_info *log_info= my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); @@ -228,7 +228,7 @@ int Trans_delegate::after_rollback(THD *thd, bool all) param.log_pos= log_info ? log_info->log_pos : 0; int ret= 0; - FOREACH_OBSERVER(ret, after_commit, thd, (¶m)); + FOREACH_OBSERVER(ret, after_rollback, thd, (¶m)); /* This is the end of a real transaction or autocommit statement, we From afd15c43a9103c47389835105489acd07d64e014 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Mon, 1 Feb 2010 14:43:06 +0300 Subject: [PATCH 329/466] Implement new type-of-operation-aware metadata locks. Add a wait-for graph based deadlock detector to the MDL subsystem. Fixes bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock" and bug #37346 "innodb does not detect deadlock between update and alter table". The first bug manifested itself as an unwarranted abort of a transaction with ER_LOCK_DEADLOCK error by a concurrent ALTER statement, when this transaction tried to repeat use of a table, which it has already used in a similar fashion before ALTER started. The second bug showed up as a deadlock between table-level locks and InnoDB row locks, which was "detected" only after innodb_lock_wait_timeout timeout. A transaction would start using the table and modify a few rows. Then ALTER TABLE would come in, and start copying rows into a temporary table. Eventually it would stumble on the modified records and get blocked on a row lock. The first transaction would try to do more updates, and get blocked on thr_lock.c lock. This situation of circular wait would only get resolved by a timeout. Both these bugs stemmed from inadequate solutions to the problem of deadlocks occurring between different locking subsystems. In the first case we tried to avoid deadlocks between metadata locking and table-level locking subsystems, when upgrading shared metadata lock to exclusive one. Transactions holding the shared lock on the table and waiting for some table-level lock used to be aborted too aggressively. We also allowed ALTER TABLE to start in presence of transactions that modify the subject table. ALTER TABLE acquires TL_WRITE_ALLOW_READ lock at start, and that block all writes against the table (naturally, we don't want any writes to be lost when switching the old and the new table). TL_WRITE_ALLOW_READ lock, in turn, would block the started transaction on thr_lock.c lock, should they do more updates. This, again, lead to the need to abort such transactions. The second bug occurred simply because we didn't have any mechanism to detect deadlocks between the table-level locks in thr_lock.c and row-level locks in InnoDB, other than innodb_lock_wait_timeout. This patch solves both these problems by moving lock conflicts which are causing these deadlocks into the metadata locking subsystem, thus making it possible to avoid or detect such deadlocks inside MDL. To do this we introduce new type-of-operation-aware metadata locks, which allow MDL subsystem to know not only the fact that transaction has used or is going to use some object but also what kind of operation it has carried out or going to carry out on the object. This, along with the addition of a special kind of upgradable metadata lock, allows ALTER TABLE to wait until all transactions which has updated the table to go away. This solves the second issue. Another special type of upgradable metadata lock is acquired by LOCK TABLE WRITE. This second lock type allows to solve the first issue, since abortion of table-level locks in event of DDL under LOCK TABLES becomes also unnecessary. Below follows the list of incompatible changes introduced by this patch: - From now on, ALTER TABLE and CREATE/DROP TRIGGER SQL (i.e. those statements that acquire TL_WRITE_ALLOW_READ lock) wait for all transactions which has *updated* the table to complete. - From now on, LOCK TABLES ... WRITE, REPAIR/OPTIMIZE TABLE (i.e. all statements which acquire TL_WRITE table-level lock) wait for all transaction which *updated or read* from the table to complete. As a consequence, innodb_table_locks=0 option no longer applies to LOCK TABLES ... WRITE. - DROP DATABASE, DROP TABLE, RENAME TABLE no longer abort statements or transactions which use tables being dropped or renamed, and instead wait for these transactions to complete. - Since LOCK TABLES WRITE now takes a special metadata lock, not compatible with with reads or writes against the subject table and transaction-wide, thr_lock.c deadlock avoidance algorithm that used to ensure absence of deadlocks between LOCK TABLES WRITE and other statements is no longer sufficient, even for MyISAM. The wait-for graph based deadlock detector of MDL subsystem may sometimes be necessary and is involved. This may lead to ER_LOCK_DEADLOCK error produced for multi-statement transactions even if these only use MyISAM: session 1: session 2: begin; update t1 ... lock table t2 write, t1 write; -- gets a lock on t2, blocks on t1 update t2 ... (ER_LOCK_DEADLOCK) - Finally, support of LOW_PRIORITY option for LOCK TABLES ... WRITE was abandoned. LOCK TABLE ... LOW_PRIORITY WRITE from now on has the same priority as the usual LOCK TABLE ... WRITE. SELECT HIGH PRIORITY no longer trumps LOCK TABLE ... WRITE in the wait queue. - We do not take upgradable metadata locks on implicitly locked tables. So if one has, say, a view v1 that uses table t1, and issues: LOCK TABLE v1 WRITE; FLUSH TABLE t1; -- (or just 'FLUSH TABLES'), an error is produced. In order to be able to perform DDL on a table under LOCK TABLES, the table must be locked explicitly in the LOCK TABLES list. --- mysql-test/include/handler.inc | 180 +- mysql-test/r/debug_sync.result | 2 +- mysql-test/r/handler_innodb.result | 154 +- mysql-test/r/handler_myisam.result | 154 +- mysql-test/r/innodb-lock.result | 41 +- mysql-test/r/innodb_mysql_lock.result | 32 + mysql-test/r/lock.result | 79 + mysql-test/r/lock_multi.result | 67 +- mysql-test/r/lock_sync.result | 7 +- mysql-test/r/mdl_sync.result | 1854 ++++++++++++- mysql-test/r/sp-threads.result | 2 +- mysql-test/r/truncate_coverage.result | 21 +- .../funcs_1/datadict/processlist_val.inc | 2 +- .../funcs_1/r/processlist_val_no_prot.result | 4 +- mysql-test/suite/rpl/t/rpl_sp.test | 2 +- mysql-test/t/debug_sync.test | 2 +- mysql-test/t/innodb-lock.test | 49 +- mysql-test/t/innodb_mysql_lock.test | 54 + mysql-test/t/lock.test | 76 + mysql-test/t/lock_multi.test | 113 +- mysql-test/t/lock_sync.test | 9 +- mysql-test/t/mdl_sync.test | 2472 ++++++++++++++++- mysql-test/t/multi_update.test | 11 +- mysql-test/t/truncate_coverage.test | 39 +- sql/handler.cc | 4 +- sql/lock.cc | 86 +- sql/mdl.cc | 1958 ++++++------- sql/mdl.h | 390 ++- sql/mysql_priv.h | 15 +- sql/set_var.cc | 10 +- sql/sp_head.cc | 6 +- sql/sql_base.cc | 385 +-- sql/sql_class.cc | 20 +- sql/sql_class.h | 55 +- sql/sql_db.cc | 12 +- sql/sql_delete.cc | 24 +- sql/sql_handler.cc | 19 +- sql/sql_parse.cc | 41 +- sql/sql_prepare.cc | 27 +- sql/sql_rename.cc | 4 +- sql/sql_show.cc | 6 +- sql/sql_table.cc | 53 +- sql/sql_trigger.cc | 18 +- sql/sql_view.cc | 4 +- sql/table.cc | 12 +- sql/table.h | 4 +- sql/transaction.cc | 4 +- 47 files changed, 6740 insertions(+), 1843 deletions(-) diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 8342a072ef9..e16c53bc1ee 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -732,7 +732,31 @@ connection default; --disable_warnings drop table if exists t1; --enable_warnings -create table t1 (a int, key a (a)); +--echo # First test case which is supposed trigger the execution +--echo # path on which problem was discovered. +create table t1 (a int); +insert into t1 values (1); +handler t1 open; +connection con1; +lock table t1 write; +send alter table t1 engine=memory; +connection con2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 engine=memory"; +--source include/wait_condition.inc +connection default; +--error ER_ILLEGAL_HA +handler t1 read a next; +handler t1 close; +connection con1; +--reap +unlock tables; +drop table t1; +--echo # Now test case which was reported originally but which no longer +--echo # triggers execution path which has caused the problem. +connection default; +create table t1 (a int, key(a)); insert into t1 values (1); handler t1 open; connection con1; @@ -743,10 +767,13 @@ let $wait_condition= where state = "Waiting for table" and info = "alter table t1 engine=memory"; --source include/wait_condition.inc connection default; +--echo # Since S metadata lock was already acquired at HANDLER OPEN time +--echo # and TL_READ lock requested by HANDLER READ is compatible with +--echo # ALTER's TL_WRITE_ALLOW_READ the below statement should succeed +--echo # without waiting. The old version of table should be used in it. handler t1 read a next; handler t1 close; connection con1; ---reap drop table t1; disconnect con1; --source include/wait_until_disconnected.inc @@ -1228,15 +1255,27 @@ create table t2 like t1; handler t1 open; --echo # --> connection con1 connection con1; -lock table t2 read; +lock table t1 write, t2 write; --echo # --> connection default connection default; +send drop table t2; +--echo # --> connection con2 +connection con2; +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # --> connection con1 +connection con1; --error ER_LOCK_DEADLOCK -drop table t2; ---error ER_LOCK_DEADLOCK -rename table t2 to t3; +drop table t1; +unlock tables; +--echo # --> connection default +connection default; +reap; + --echo # Demonstrate that there is no deadlock with FLUSH TABLE, --echo # even though it is waiting for the other table to go away +create table t2 like t1; --echo # Sending: --send flush table t2 --echo # --> connection con2 @@ -1256,6 +1295,7 @@ drop table t2; --echo # lead to deadlocks --echo # create table t1 (a int, key a(a)); +insert into t1 values (1), (2); --echo # --> connection default connection default; @@ -1265,7 +1305,31 @@ handler t1 open; --echo # --> connection con1 connection con1; -lock tables t1 write; +--echo # Sending: +--send lock tables t1 write + +--echo # --> connection con2 +connection con2; +--echo # Check that 'lock tables t1 write' waits until transaction which +--echo # has read from the table commits. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "lock tables t1 write"; +--source include/wait_condition.inc + +--echo # --> connection default +connection default; +--echo # The below 'handler t1 read ...' should not be blocked as +--echo # 'lock tables t1 write' has not succeeded yet. +handler t1 read a next; + +--echo # Unblock 'lock tables t1 write'. +commit; + +--echo # --> connection con1 +connection con1; +--echo # Reap 'lock tables t1 write'. +--reap --echo # --> connection default connection default; @@ -1279,29 +1343,18 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Table lock" and info = "handler t1 read a next"; --source include/wait_condition.inc ---echo # Sending: ---send drop table t1 ---echo # --> connection con2 -connection con2; ---echo # Waiting for 'drop table t1' to get blocked... -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and info = "drop table t1"; ---source include/wait_condition.inc +--echo # The below 'drop table t1' should be able to proceed without +--echo # waiting as it will force HANDLER to be closed. +drop table t1; +unlock tables; --echo # --> connection default connection default; --echo # Reaping 'handler t1 read a next'... ---error ER_LOCK_DEADLOCK +--error ER_NO_SUCH_TABLE --reap handler t1 close; -commit; - ---echo # --> connection con1 -connection con1; ---echo # Reaping 'drop table t1'... ---reap --echo # --> connection con1 connection con1; @@ -1357,3 +1410,84 @@ rename table t4 to t5, t3 to t4, t5 to t3; handler t1 read first; handler t2 read first; drop table t1, t2, t3, t4; + +--echo # +--echo # A test for FLUSH TABLES WITH READ LOCK and HANDLER statements. +--echo # +set autocommit=0; +create table t1 (a int, b int, key a (a)); +insert into t1 (a, b) values (1, 1), (2, 1), (3, 2), (4, 2), (5, 5); +create table t2 like t1; +insert into t2 (a, b) select a, b from t1; +create table t3 like t1; +insert into t3 (a, b) select a, b from t1; +commit; +flush tables with read lock; +handler t1 open; +lock table t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t1 read next; +--echo # This implicitly leaves LOCK TABLES but doesn't drop the GLR +--error ER_NO_SUCH_TABLE +lock table not_exists_write read; +--echo # We still have the read lock. +--error ER_CANT_UPDATE_WITH_READLOCK +drop table t1; +handler t1 open; +select a from t2; +handler t1 read next; +flush tables with read lock; +handler t2 open; +flush tables with read lock; +handler t1 read next; +select a from t3; +handler t2 read next; +handler t1 close; +rollback; +handler t2 close; +--error ER_CANT_UPDATE_WITH_READLOCK +drop table t1; +commit; +flush tables; +--error ER_CANT_UPDATE_WITH_READLOCK +drop table t1; +unlock tables; +drop table t1; +set autocommit=default; +drop table t2, t3; + +--echo # +--echo # HANDLER statement and operation-type aware metadata locks. +--echo # Check that when we clone a ticket for HANDLER we downrade +--echo # the lock. +--echo # +--echo # Establish an auxiliary connection con1. +connect (con1,localhost,root,,); +--echo # -> connection default +connection default; +create table t1 (a int, b int, key a (a)); +insert into t1 (a, b) values (1, 1), (2, 1), (3, 2), (4, 2), (5, 5); +begin; +insert into t1 (a, b) values (6, 6); +handler t1 open; +handler t1 read a last; +insert into t1 (a, b) values (7, 7); +handler t1 read a last; +commit; +--echo # -> connection con1 +connection con1; +--echo # Demonstrate that the HANDLER doesn't hold MDL_SHARED_WRITE. +lock table t1 write; +unlock tables; +--echo # -> connection default +connection default; +handler t1 read a prev; +handler t1 close; +--echo # Cleanup. +drop table t1; +--echo # -> connection con1 +connection con1; +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # -> connection default +connection default; diff --git a/mysql-test/r/debug_sync.result b/mysql-test/r/debug_sync.result index 8b46334204c..25fdf523200 100644 --- a/mysql-test/r/debug_sync.result +++ b/mysql-test/r/debug_sync.result @@ -263,7 +263,7 @@ DROP TABLE t1; SET DEBUG_SYNC= 'RESET'; DROP TABLE IF EXISTS t1; CREATE TABLE t1 (c1 INT); -LOCK TABLE t1 WRITE; +LOCK TABLE t1 READ; connection con1 SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2'; INSERT INTO t1 VALUES (1); diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index a3e3e325e7d..6ed7b572403 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -745,10 +745,28 @@ drop table t1; handler t1 read a next; ERROR 42S02: Unknown table 't1' in HANDLER drop table if exists t1; -create table t1 (a int, key a (a)); +# First test case which is supposed trigger the execution +# path on which problem was discovered. +create table t1 (a int); +insert into t1 values (1); +handler t1 open; +lock table t1 write; +alter table t1 engine=memory; +handler t1 read a next; +ERROR HY000: Table storage engine for 't1' doesn't have this option +handler t1 close; +unlock tables; +drop table t1; +# Now test case which was reported originally but which no longer +# triggers execution path which has caused the problem. +create table t1 (a int, key(a)); insert into t1 values (1); handler t1 open; alter table t1 engine=memory; +# Since S metadata lock was already acquired at HANDLER OPEN time +# and TL_READ lock requested by HANDLER READ is compatible with +# ALTER's TL_WRITE_ALLOW_READ the below statement should succeed +# without waiting. The old version of table should be used in it. handler t1 read a next; a 1 @@ -1217,14 +1235,19 @@ create table t1 (a int, key a(a)); create table t2 like t1; handler t1 open; # --> connection con1 -lock table t2 read; +lock table t1 write, t2 write; # --> connection default drop table t2; +# --> connection con2 +# Waiting for 'drop table t2' to get blocked... +# --> connection con1 +drop table t1; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -rename table t2 to t3; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default # Demonstrate that there is no deadlock with FLUSH TABLE, # even though it is waiting for the other table to go away +create table t2 like t1; # Sending: flush table t2; # --> connection con2 @@ -1239,29 +1262,43 @@ drop table t2; # lead to deadlocks # create table t1 (a int, key a(a)); +insert into t1 values (1), (2); # --> connection default begin; select * from t1; a +1 +2 handler t1 open; # --> connection con1 +# Sending: lock tables t1 write; +# --> connection con2 +# Check that 'lock tables t1 write' waits until transaction which +# has read from the table commits. +# --> connection default +# The below 'handler t1 read ...' should not be blocked as +# 'lock tables t1 write' has not succeeded yet. +handler t1 read a next; +a +1 +# Unblock 'lock tables t1 write'. +commit; +# --> connection con1 +# Reap 'lock tables t1 write'. # --> connection default # Sending: handler t1 read a next; # --> connection con1 # Waiting for 'handler t1 read a next' to get blocked... -# Sending: +# The below 'drop table t1' should be able to proceed without +# waiting as it will force HANDLER to be closed. drop table t1; -# --> connection con2 -# Waiting for 'drop table t1' to get blocked... +unlock tables; # --> connection default # Reaping 'handler t1 read a next'... -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +ERROR 42S02: Table 'test.t1' doesn't exist handler t1 close; -commit; -# --> connection con1 -# Reaping 'drop table t1'... # --> connection con1 # --> connection con2 # --> connection con3 @@ -1324,3 +1361,98 @@ a b handler t2 read first; a b drop table t1, t2, t3, t4; +# +# A test for FLUSH TABLES WITH READ LOCK and HANDLER statements. +# +set autocommit=0; +create table t1 (a int, b int, key a (a)); +insert into t1 (a, b) values (1, 1), (2, 1), (3, 2), (4, 2), (5, 5); +create table t2 like t1; +insert into t2 (a, b) select a, b from t1; +create table t3 like t1; +insert into t3 (a, b) select a, b from t1; +commit; +flush tables with read lock; +handler t1 open; +lock table t1 read; +handler t1 read next; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# This implicitly leaves LOCK TABLES but doesn't drop the GLR +lock table not_exists_write read; +ERROR 42S02: Table 'test.not_exists_write' doesn't exist +# We still have the read lock. +drop table t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +handler t1 open; +select a from t2; +a +1 +2 +3 +4 +5 +handler t1 read next; +a b +1 1 +flush tables with read lock; +handler t2 open; +flush tables with read lock; +handler t1 read next; +a b +1 1 +select a from t3; +a +1 +2 +3 +4 +5 +handler t2 read next; +a b +1 1 +handler t1 close; +rollback; +handler t2 close; +drop table t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +commit; +flush tables; +drop table t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +drop table t1; +set autocommit=default; +drop table t2, t3; +# +# HANDLER statement and operation-type aware metadata locks. +# Check that when we clone a ticket for HANDLER we downrade +# the lock. +# +# Establish an auxiliary connection con1. +# -> connection default +create table t1 (a int, b int, key a (a)); +insert into t1 (a, b) values (1, 1), (2, 1), (3, 2), (4, 2), (5, 5); +begin; +insert into t1 (a, b) values (6, 6); +handler t1 open; +handler t1 read a last; +a b +6 6 +insert into t1 (a, b) values (7, 7); +handler t1 read a last; +a b +7 7 +commit; +# -> connection con1 +# Demonstrate that the HANDLER doesn't hold MDL_SHARED_WRITE. +lock table t1 write; +unlock tables; +# -> connection default +handler t1 read a prev; +a b +6 6 +handler t1 close; +# Cleanup. +drop table t1; +# -> connection con1 +# -> connection default diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index f5c5bfebd15..0dd039eeb23 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -743,10 +743,28 @@ drop table t1; handler t1 read a next; ERROR 42S02: Unknown table 't1' in HANDLER drop table if exists t1; -create table t1 (a int, key a (a)); +# First test case which is supposed trigger the execution +# path on which problem was discovered. +create table t1 (a int); +insert into t1 values (1); +handler t1 open; +lock table t1 write; +alter table t1 engine=memory; +handler t1 read a next; +ERROR HY000: Table storage engine for 't1' doesn't have this option +handler t1 close; +unlock tables; +drop table t1; +# Now test case which was reported originally but which no longer +# triggers execution path which has caused the problem. +create table t1 (a int, key(a)); insert into t1 values (1); handler t1 open; alter table t1 engine=memory; +# Since S metadata lock was already acquired at HANDLER OPEN time +# and TL_READ lock requested by HANDLER READ is compatible with +# ALTER's TL_WRITE_ALLOW_READ the below statement should succeed +# without waiting. The old version of table should be used in it. handler t1 read a next; a 1 @@ -1214,14 +1232,19 @@ create table t1 (a int, key a(a)); create table t2 like t1; handler t1 open; # --> connection con1 -lock table t2 read; +lock table t1 write, t2 write; # --> connection default drop table t2; +# --> connection con2 +# Waiting for 'drop table t2' to get blocked... +# --> connection con1 +drop table t1; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -rename table t2 to t3; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default # Demonstrate that there is no deadlock with FLUSH TABLE, # even though it is waiting for the other table to go away +create table t2 like t1; # Sending: flush table t2; # --> connection con2 @@ -1236,29 +1259,43 @@ drop table t2; # lead to deadlocks # create table t1 (a int, key a(a)); +insert into t1 values (1), (2); # --> connection default begin; select * from t1; a +1 +2 handler t1 open; # --> connection con1 +# Sending: lock tables t1 write; +# --> connection con2 +# Check that 'lock tables t1 write' waits until transaction which +# has read from the table commits. +# --> connection default +# The below 'handler t1 read ...' should not be blocked as +# 'lock tables t1 write' has not succeeded yet. +handler t1 read a next; +a +1 +# Unblock 'lock tables t1 write'. +commit; +# --> connection con1 +# Reap 'lock tables t1 write'. # --> connection default # Sending: handler t1 read a next; # --> connection con1 # Waiting for 'handler t1 read a next' to get blocked... -# Sending: +# The below 'drop table t1' should be able to proceed without +# waiting as it will force HANDLER to be closed. drop table t1; -# --> connection con2 -# Waiting for 'drop table t1' to get blocked... +unlock tables; # --> connection default # Reaping 'handler t1 read a next'... -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +ERROR 42S02: Table 'test.t1' doesn't exist handler t1 close; -commit; -# --> connection con1 -# Reaping 'drop table t1'... # --> connection con1 # --> connection con2 # --> connection con3 @@ -1322,6 +1359,101 @@ handler t2 read first; a b drop table t1, t2, t3, t4; # +# A test for FLUSH TABLES WITH READ LOCK and HANDLER statements. +# +set autocommit=0; +create table t1 (a int, b int, key a (a)); +insert into t1 (a, b) values (1, 1), (2, 1), (3, 2), (4, 2), (5, 5); +create table t2 like t1; +insert into t2 (a, b) select a, b from t1; +create table t3 like t1; +insert into t3 (a, b) select a, b from t1; +commit; +flush tables with read lock; +handler t1 open; +lock table t1 read; +handler t1 read next; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# This implicitly leaves LOCK TABLES but doesn't drop the GLR +lock table not_exists_write read; +ERROR 42S02: Table 'test.not_exists_write' doesn't exist +# We still have the read lock. +drop table t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +handler t1 open; +select a from t2; +a +1 +2 +3 +4 +5 +handler t1 read next; +a b +1 1 +flush tables with read lock; +handler t2 open; +flush tables with read lock; +handler t1 read next; +a b +1 1 +select a from t3; +a +1 +2 +3 +4 +5 +handler t2 read next; +a b +1 1 +handler t1 close; +rollback; +handler t2 close; +drop table t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +commit; +flush tables; +drop table t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +drop table t1; +set autocommit=default; +drop table t2, t3; +# +# HANDLER statement and operation-type aware metadata locks. +# Check that when we clone a ticket for HANDLER we downrade +# the lock. +# +# Establish an auxiliary connection con1. +# -> connection default +create table t1 (a int, b int, key a (a)); +insert into t1 (a, b) values (1, 1), (2, 1), (3, 2), (4, 2), (5, 5); +begin; +insert into t1 (a, b) values (6, 6); +handler t1 open; +handler t1 read a last; +a b +6 6 +insert into t1 (a, b) values (7, 7); +handler t1 read a last; +a b +7 7 +commit; +# -> connection con1 +# Demonstrate that the HANDLER doesn't hold MDL_SHARED_WRITE. +lock table t1 write; +unlock tables; +# -> connection default +handler t1 read a prev; +a b +6 6 +handler t1 close; +# Cleanup. +drop table t1; +# -> connection con1 +# -> connection default +# # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash # CREATE TABLE t1 AS SELECT 1 AS f1; diff --git a/mysql-test/r/innodb-lock.result b/mysql-test/r/innodb-lock.result index 4ace4065c34..ab7e9aa7b25 100644 --- a/mysql-test/r/innodb-lock.result +++ b/mysql-test/r/innodb-lock.result @@ -25,6 +25,12 @@ id x 0 2 commit; drop table t1; +# +# Old lock method (where LOCK TABLE was ignored by InnoDB) no longer +# works due to fix for bugs #46272 "MySQL 5.4.4, new MDL: unnecessary +# deadlock" and bug #37346 "innodb does not detect deadlock between +# update and alter table". +# set @@innodb_table_locks=0; create table t1 (id integer primary key, x integer) engine=INNODB; insert into t1 values(0, 0),(1,1),(2,2); @@ -32,26 +38,27 @@ commit; SELECT * from t1 where id = 0 FOR UPDATE; id x 0 0 +# Connection 'con2'. set autocommit=0; set @@innodb_table_locks=0; -lock table t1 write; -update t1 set x=10 where id = 2; -SELECT * from t1 where id = 2; -id x -2 2 -UPDATE t1 set x=3 where id = 2; -commit; -SELECT * from t1; -id x -0 0 -1 1 -2 3 -commit; -unlock tables; -commit; +# The following statement should block because SQL-level lock +# is taken on t1 which will wait until concurrent transaction +# is commited. +# Sending: +lock table t1 write;; +# Connection 'con1'. +# Wait until LOCK TABLE is blocked on SQL-level lock. +# We should be able to do UPDATEs and SELECTs within transaction. +update t1 set x=1 where id = 0; select * from t1; id x -0 0 +0 1 1 1 -2 10 +2 2 +# Unblock LOCK TABLE. +commit; +# Connection 'con2'. +# Reap LOCK TABLE. +unlock tables; +# Connection 'con1'. drop table t1; diff --git a/mysql-test/r/innodb_mysql_lock.result b/mysql-test/r/innodb_mysql_lock.result index 374f67358eb..375ae8aeb12 100644 --- a/mysql-test/r/innodb_mysql_lock.result +++ b/mysql-test/r/innodb_mysql_lock.result @@ -26,6 +26,38 @@ commit; set @@autocommit=1; set @@autocommit=1; # +# Test for bug #37346 "innodb does not detect deadlock between update +# and alter table". +# +drop table if exists t1; +create table t1 (c1 int primary key, c2 int, c3 int) engine=InnoDB; +insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); +begin; +# Run statement which acquires X-lock on one of table's rows. +update t1 set c3=c3+1 where c2=3; +# +# Switching to connection 'con37346'. +# The below ALTER TABLE statement should wait till transaction +# in connection 'default' is complete and then succeed. +# It should not deadlock or fail with ER_LOCK_DEADLOCK error. +# Sending: +alter table t1 add column c4 int;; +# +# Switching to connection 'default'. +# Wait until the above ALTER TABLE gets blocked because this +# connection holds SW metadata lock on table to be altered. +# The below statement should succeed. It should not +# deadlock or end with ER_LOCK_DEADLOCK error. +update t1 set c3=c3+1 where c2=4; +# Unblock ALTER TABLE by committing transaction. +commit; +# +# Switching to connection 'con37346'. +# Reaping ALTER TABLE. +# +# Switching to connection 'default'. +drop table t1; +# # Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB # table cause warnings in errlog # diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 5dbfa66b43e..c1e1ccb5bce 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -151,6 +151,12 @@ select * from t2; a select * from t3; ERROR HY000: Table 't3' was not locked with LOCK TABLES +Dropping of implicitly locked table is disallowed. +drop table t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +unlock tables; +Now let us also lock table explicitly and drop it. +lock tables t1 write, v_bug5719 write; drop table t1; sic: left LOCK TABLES mode @@ -282,6 +288,79 @@ insert into t1 values (1); # Ensure that metadata locks held by the transaction are released. drop table t1; # +# Coverage for situations when we try to execute DDL on tables +# which are locked by LOCK TABLES only implicitly. +# +drop tables if exists t1, t2; +drop view if exists v1; +drop function if exists f1; +create table t1 (i int); +create table t2 (j int); +# +# Try to perform DDL on table which is locked through view. +create view v1 as select * from t2; +lock tables t1 write, v1 write; +flush table t2; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +drop table t2; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +alter table t2 add column k int; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +create trigger t2_bi before insert on t2 for each row set @a:=1; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +# Repair produces error as part of its result set. +repair table t2; +Table Op Msg_type Msg_text +test.t2 repair Error Table 't2' was locked with a READ lock and can't be updated +test.t2 repair status Operation failed +unlock tables; +drop view v1; +# +# Now, try DDL on table which is locked through routine. +create function f1 () returns int +begin +insert into t2 values (1); +return 0; +end| +create view v1 as select f1() from t1; +lock tables v1 read; +flush table t2; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +drop table t2; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +alter table t2 add column k int; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +create trigger t2_bi before insert on t2 for each row set @a:=1; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +# Repair produces error as part of its result set. +repair table t2; +Table Op Msg_type Msg_text +test.t2 repair Error Table 't2' was locked with a READ lock and can't be updated +test.t2 repair status Operation failed +unlock tables; +drop view v1; +drop function f1; +# +# Finally, try DDL on table which is locked thanks to trigger. +create trigger t1_ai after insert on t1 for each row insert into t2 values (1); +lock tables t1 write; +flush table t2; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +drop table t2; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +alter table t2 add column k int; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +create trigger t2_bi before insert on t2 for each row set @a:=1; +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +# Repair produces error as part of its result set. +repair table t2; +Table Op Msg_type Msg_text +test.t2 repair Error Table 't2' was locked with a READ lock and can't be updated +test.t2 repair status Operation failed +unlock tables; +drop trigger t1_ai; +drop tables t1, t2; +# # Bug#45035 " Altering table under LOCK TABLES results in # "Error 1213 Deadlock found..." # diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 5d12e0efd64..4b08c175ee2 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -1,21 +1,39 @@ drop table if exists t1,t2; create table t1(n int); insert into t1 values (1); -lock tables t1 write; +select get_lock("mysqltest_lock", 100); +get_lock("mysqltest_lock", 100) +1 +update t1 set n = 2 and get_lock('mysqltest_lock', 100); update low_priority t1 set n = 4; select n from t1; -unlock tables; +select release_lock("mysqltest_lock"); +release_lock("mysqltest_lock") +1 +select release_lock("mysqltest_lock"); +release_lock("mysqltest_lock") +1 n 4 drop table t1; create table t1(n int); insert into t1 values (1); -lock tables t1 read; +select get_lock("mysqltest_lock", 100); +get_lock("mysqltest_lock", 100) +1 +select n from t1 where get_lock('mysqltest_lock', 100); update low_priority t1 set n = 4; select n from t1; n 1 -unlock tables; +select release_lock("mysqltest_lock"); +release_lock("mysqltest_lock") +1 +n +1 +select release_lock("mysqltest_lock"); +release_lock("mysqltest_lock") +1 drop table t1; create table t1 (a int, b int); create table t2 (c int, d int); @@ -35,6 +53,7 @@ create table t2 (a int); lock table t1 write, t2 write; insert t1 select * from t2; drop table t2; +unlock tables; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; create table t1 (a int); @@ -42,6 +61,7 @@ create table t2 (a int); lock table t1 write, t2 write, t1 as t1_2 write, t2 as t2_2 write; insert t1 select * from t2; drop table t2; +unlock tables; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; End of 4.1 tests @@ -221,6 +241,36 @@ connection: default flush tables; drop table t1; # +# Test for bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock". +# +drop table if exists t1; +create table t1 (c1 int primary key, c2 int, c3 int); +insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); +begin; +update t1 set c3=c3+1 where c2=3; +# +# Switching to connection 'con46272'. +# The below ALTER TABLE statement should wait till transaction +# in connection 'default' is complete and then succeed. +# It should not deadlock or fail with ER_LOCK_DEADLOCK error. +# Sending: +alter table t1 add column c4 int;; +# +# Switching to connection 'default'. +# Wait until the above ALTER TABLE gets blocked because this +# connection holds SW metadata lock on table to be altered. +# The below statement should succeed. It should not +# deadlock or end with ER_LOCK_DEADLOCK error. +update t1 set c3=c3+1 where c2=4; +# Unblock ALTER TABLE by committing transaction. +commit; +# +# Switching to connection 'con46272'. +# Reaping ALTER TABLE. +# +# Switching to connection 'default'. +drop table t1; +# # Bug#47249 assert in MDL_global_lock::is_lock_type_compatible # DROP TABLE IF EXISTS t1; @@ -228,14 +278,15 @@ DROP VIEW IF EXISTS v1; # # Test 1: LOCK TABLES v1 WRITE, t1 READ; # +# Thanks to the fact that we no longer allow DDL on tables +# which are locked for write implicitly, the exact scenario +# in which assert was failing is no longer repeatable. CREATE TABLE t1 ( f1 integer ); CREATE VIEW v1 AS SELECT f1 FROM t1 ; -# Connection 2 LOCK TABLES v1 WRITE, t1 READ; FLUSH TABLE t1; -# Connection 1 -LOCK TABLES t1 WRITE; -FLUSH TABLE t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +UNLOCK TABLES; DROP TABLE t1; DROP VIEW v1; # diff --git a/mysql-test/r/lock_sync.result b/mysql-test/r/lock_sync.result index fc4e8c850f6..0b57b38f5ec 100644 --- a/mysql-test/r/lock_sync.result +++ b/mysql-test/r/lock_sync.result @@ -6,6 +6,7 @@ # statements which tried to acquire stronger write lock (TL_WRITE, # TL_WRITE_ALLOW_READ) on this table might have led to deadlock. drop table if exists t1; +drop view if exists v1; # Create auxiliary connections used through the test. # Reset DEBUG_SYNC facility before using it. set debug_sync= 'RESET'; @@ -14,6 +15,9 @@ set debug_sync= 'RESET'; set @old_general_log = @@global.general_log; set @@global.general_log= OFF; create table t1 (i int) engine=InnoDB; +# We have to use view in order to make LOCK TABLES avoid +# acquiring SNRW metadata lock on table. +create view v1 as select * from t1; insert into t1 values (1); # Prepare user lock which will be used for resuming execution of # the first statement after it acquires TL_WRITE_ALLOW_WRITE lock. @@ -36,7 +40,7 @@ select count(*) > 0 from t1 as a, t1 as b for update;; # acquiring lock for the the first instance of 't1'. set debug_sync= 'now WAIT_FOR parked'; # Send LOCK TABLE statement which will try to get TL_WRITE lock on 't1': -lock table t1 write;; +lock table v1 write;; # Switch to connection 'default'. # Wait until this LOCK TABLES statement starts waiting for table lock. # Allow SELECT ... FOR UPDATE to resume. @@ -63,4 +67,5 @@ unlock tables; # Do clean-up. set debug_sync= 'RESET'; set @@global.general_log= @old_general_log; +drop view v1; drop table t1; diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 8c4d7272e29..8d8672377f0 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -10,7 +10,7 @@ alter table t1 rename t3; connection: default set debug_sync= 'now WAIT_FOR parked'; connection: con2 -set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +set debug_sync='mdl_acquire_lock_wait SIGNAL go'; drop table t1,t2; connection: con1 connection: default @@ -20,6 +20,1740 @@ ERROR 42S02: Unknown table 't1' drop table t3; SET DEBUG_SYNC= 'RESET'; # +# Basic test coverage for type-of-operation aware metadata locks. +# +drop table if exists t1, t2, t3; +set debug_sync= 'RESET'; +create table t1 (c1 int); +# +# A) First let us check compatibility rules between differend kinds of +# type-of-operation aware metadata locks. +# Of course, these rules are already covered by the tests scattered +# across the test suite. But it still makes sense to have one place +# which covers all of them. +# +# 1) Acquire S (simple shared) lock on the table (by using HANDLER): +# +handler t1 open; +# +# Switching to connection 'mdl_con1'. +# Check that S, SH, SR and SW locks are compatible with it. +handler t1 open t; +handler t close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +0 +insert into t1 values (1), (1); +# Check that SNW lock is compatible with it. To do this use ALTER TABLE +# which will fail after opening the table and thus obtaining SNW metadata +# lock. +alter table t1 add primary key (c1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# Check that SNRW lock is compatible with S lock. +lock table t1 write; +insert into t1 values (1); +unlock tables; +# Check that X lock is incompatible with S lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME is blocked because of S lock. +# +# Switching to connection 'default'. +# Unblock RENAME TABLE. +handler t1 close; +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE. +# Restore the original state of the things. +rename table t2 to t1; +# +# Switching to connection 'default'. +handler t1 open; +# +# Switching to connection 'mdl_con1'. +# Check that upgrade from SNW to X is blocked by presence of S lock. +# Sending: +alter table t1 add column c2 int;; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER TABLE is blocked because of S lock. +# +# Switching to connection 'default'. +# Unblock ALTER TABLE. +handler t1 close; +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +# Restore the original state of the things. +alter table t1 drop column c2; +# +# Switching to connection 'default'. +handler t1 open; +# +# Switching to connection 'mdl_con1'. +# Check that upgrade from SNRW to X is blocked by presence of S lock. +lock table t1 write; +# Sending: +alter table t1 add column c2 int;; +# +# Switching to connection 'mdl_con2'. +# Check that the above upgrade of SNRW to X in ALTER TABLE is blocked +# because of S lock. +# +# Switching to connection 'default'. +# Unblock ALTER TABLE. +handler t1 close; +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +# Restore the original state of the things. +alter table t1 drop column c2; +unlock tables; +# +# Switching to connection 'default'. +# +# 2) Acquire SH (shared high-priority) lock on the table. +# We have to involve DEBUG_SYNC facility for this as usually +# such kind of locks are short-lived. +# +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t1';; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that S, SH, SR and SW locks are compatible with it. +handler t1 open; +handler t1 close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +3 +insert into t1 values (1); +# Check that SNW lock is compatible with it. To do this use ALTER TABLE +# which will fail after opening the table and thus obtaining SNW metadata +# lock. +alter table t1 add primary key (c1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# Check that SNRW lock is compatible with SH lock. +lock table t1 write; +delete from t1 limit 1; +unlock tables; +# Check that X lock is incompatible with SH lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME is blocked because of SH lock. +# Unblock RENAME TABLE. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping SELECT ... FROM I_S. +table_name table_type auto_increment table_comment +t1 BASE TABLE NULL +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE. +# Restore the original state of the things. +rename table t2 to t1; +# +# Switching to connection 'default'. +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t1';; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that upgrade from SNW to X is blocked by presence of SH lock. +# Sending: +alter table t1 add column c2 int;; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER TABLE is blocked because of SH lock. +# Unblock RENAME TABLE. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping SELECT ... FROM I_S. +table_name table_type auto_increment table_comment +t1 BASE TABLE NULL +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +# Restore the original state of the things. +alter table t1 drop column c2; +# +# Switching to connection 'default'. +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t1';; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that upgrade from SNRW to X is blocked by presence of S lock. +lock table t1 write; +# Sending: +alter table t1 add column c2 int;; +# +# Switching to connection 'mdl_con2'. +# Check that the above upgrade of SNRW to X in ALTER TABLE is blocked +# because of S lock. +# Unblock RENAME TABLE. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping SELECT ... FROM I_S. +table_name table_type auto_increment table_comment +t1 BASE TABLE NULL +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +# Restore the original state of the things. +alter table t1 drop column c2; +unlock tables; +# +# Switching to connection 'default'. +# +# +# 3) Acquire SR lock on the table. +# +# +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Check that S, SH, SR and SW locks are compatible with it. +handler t1 open; +handler t1 close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +3 +insert into t1 values (1); +# Check that SNW lock is compatible with it. To do this use ALTER TABLE +# which will fail after opening the table and thus obtaining SNW metadata +# lock. +alter table t1 add primary key (c1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# Check that SNRW lock is not compatible with SR lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'default'. +# Check that the above LOCK TABLES is blocked because of SR lock. +# Unblock LOCK TABLES. +commit; +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLES. +delete from t1 limit 1; +unlock tables; +# +# Switching to connection 'default'. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Check that X lock is incompatible with SR lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME is blocked because of SR lock. +# +# Switching to connection 'default'. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE. +# Restore the original state of the things. +rename table t2 to t1; +# +# Switching to connection 'default'. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Check that upgrade from SNW to X is blocked by presence of SR lock. +# Sending: +alter table t1 add column c2 int;; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER TABLE is blocked because of SR lock. +# +# Switching to connection 'default'. +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +# Restore the original state of the things. +alter table t1 drop column c2; +# +# There is no need to check that upgrade from SNRW to X is blocked +# by presence of SR lock because SNRW is incompatible with SR anyway. +# +# +# Switching to connection 'default'. +# +# +# 4) Acquire SW lock on the table. +# +# +begin; +insert into t1 values (1); +# +# Switching to connection 'mdl_con1'. +# Check that S, SH, SR and SW locks are compatible with it. +handler t1 open; +handler t1 close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +4 +insert into t1 values (1); +# Check that SNW lock is not compatible with SW lock. +# Again we use ALTER TABLE which fails after opening +# the table to avoid upgrade of SNW -> X. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'default'. +# Check that the above ALTER TABLE is blocked because of SW lock. +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +begin; +insert into t1 values (1); +# +# Switching to connection 'mdl_con1'. +# Check that SNRW lock is not compatible with SW lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'default'. +# Check that the above LOCK TABLES is blocked because of SW lock. +# Unblock LOCK TABLES. +commit; +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLES. +delete from t1 limit 2; +unlock tables; +# +# Switching to connection 'default'. +begin; +insert into t1 values (1); +# +# Switching to connection 'mdl_con1'. +# Check that X lock is incompatible with SW lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME is blocked because of SW lock. +# +# Switching to connection 'default'. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE. +# Restore the original state of the things. +rename table t2 to t1; +# +# There is no need to check that upgrade from SNW/SNRW to X is +# blocked by presence of SW lock because SNW/SNRW is incompatible +# with SW anyway. +# +# +# Switching to connection 'default'. +# +# +# 5) Acquire SNW lock on the table. We have to use DEBUG_SYNC for +# this, to prevent SNW from being immediately upgraded to X. +# +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that S, SH and SR locks are compatible with it. +handler t1 open; +handler t1 close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +5 +# Check that SW lock is incompatible with SNW lock. +# Sending: +delete from t1 limit 2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above DELETE is blocked because of SNW lock. +# Unblock ALTER and thus DELETE. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping DELETE. +# +# Switching to connection 'default'. +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that SNW lock is incompatible with SNW lock. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER is blocked because of SNW lock. +# Unblock ALTERs. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping first ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping another ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that SNRW lock is incompatible with SNW lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con2'. +# Check that the above LOCK TABLES is blocked because of SNW lock. +# Unblock ALTER and thus LOCK TABLES. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLES +insert into t1 values (1); +unlock tables; +# +# Switching to connection 'default'. +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that X lock is incompatible with SNW lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME is blocked because of SNW lock. +# Unblock ALTER and thus RENAME TABLE. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE +# Revert back to original state of things. +rename table t2 to t1; +# +# There is no need to check that upgrade from SNW/SNRW to X is +# blocked by presence of another SNW lock because SNW/SNRW is +# incompatible with SNW anyway. +# +# Switching to connection 'default'. +# +# +# 6) Acquire SNRW lock on the table. +# +# +lock table t1 write; +# +# Switching to connection 'mdl_con1'. +# Check that S and SH locks are compatible with it. +handler t1 open; +handler t1 close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +# Check that SR lock is incompatible with SNRW lock. +# Sending: +select count(*) from t1;; +# +# Switching to connection 'default'. +# Check that the above SELECT is blocked because of SNRW lock. +# Unblock SELECT. +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping SELECT. +count(*) +4 +# +# Switching to connection 'default'. +lock table t1 write; +# +# Switching to connection 'mdl_con1'. +# Check that SW lock is incompatible with SNRW lock. +# Sending: +delete from t1 limit 1;; +# +# Switching to connection 'default'. +# Check that the above DELETE is blocked because of SNRW lock. +# Unblock DELETE. +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping DELETE. +# +# Switching to connection 'default'. +lock table t1 write; +# +# Switching to connection 'mdl_con1'. +# Check that SNW lock is incompatible with SNRW lock. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'default'. +# Check that the above ALTER is blocked because of UNWR lock. +# Unblock ALTER. +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +lock table t1 write; +# +# Switching to connection 'mdl_con1'. +# Check that SNRW lock is incompatible with SNRW lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'default'. +# Check that the above LOCK TABLES is blocked because of SNRW lock. +# Unblock waiting LOCK TABLES. +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLES +insert into t1 values (1); +unlock tables; +# +# Switching to connection 'default'. +lock table t1 write; +# +# Switching to connection 'mdl_con1'. +# Check that X lock is incompatible with SNRW lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'default'. +# Check that the above RENAME is blocked because of SNRW lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE +# Revert back to original state of things. +rename table t2 to t1; +# +# There is no need to check that upgrade from SNW/SNRW to X is +# blocked by presence of another SNRW lock because SNW/SNRW is +# incompatible with SNRW anyway. +# +# Switching to connection 'default'. +# +# +# 7) Now do the same round of tests for X lock. We use additional +# table to get long-lived lock of this type. +# +create table t2 (c1 int); +# +# Switching to connection 'mdl_con2'. +# Take a lock on t2, so RENAME TABLE t1 TO t2 will get blocked +# after acquiring X lock on t1. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that S lock in incompatible with X lock. +# Sending: +handler t1 open;; +# +# Switching to connection 'mdl_con2'. +# Check that the above HANDLER statement is blocked because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping HANDLER. +handler t1 close; +# +# Switching to connection 'mdl_con2'. +# Prepare for blocking RENAME TABLE. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that SH lock in incompatible with X lock. +# Sending: +select column_name from information_schema.columns where table_schema='test' and table_name='t1';; +# +# Switching to connection 'mdl_con2'. +# Check that the above SELECT ... FROM I_S ... statement is blocked +# because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping SELECT ... FROM I_S. +column_name +c1 +# +# Switching to connection 'mdl_con2'. +# Prepare for blocking RENAME TABLE. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that SR lock in incompatible with X lock. +# Sending: +select count(*) from t1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above SELECT statement is blocked +# because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping SELECT. +count(*) +4 +# +# Switching to connection 'mdl_con2'. +# Prepare for blocking RENAME TABLE. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that SW lock in incompatible with X lock. +# Sending: +delete from t1 limit 1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above DELETE statement is blocked +# because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping DELETE. +# +# Switching to connection 'mdl_con2'. +# Prepare for blocking RENAME TABLE. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that SNW lock is incompatible with X lock. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER statement is blocked +# because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con2'. +# Prepare for blocking RENAME TABLE. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that SNRW lock is incompatible with X lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con2'. +# Check that the above LOCK TABLE statement is blocked +# because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLE. +unlock tables; +# +# Switching to connection 'mdl_con2'. +# Prepare for blocking RENAME TABLE. +lock tables t2 read; +# +# Switching to connection 'default'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME has acquired X lock on t1 and is waiting for t2. +# Check that X lock is incompatible with X lock. +# Sending: +rename table t1 to t3;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME statement is blocked +# because of X lock. +# Unblock RENAME TABLE +unlock tables; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME. +rename table t3 to t1; +# +# B) Now let us test compatibility in cases when both locks +# are pending. I.e. let us test rules for priorities between +# different types of metadata locks. +# +# +# Switching to connection 'mdl_con2'. +# +# 1) Check compatibility for pending SNW lock. +# +# Acquire SW lock in order to create pending SNW lock later. +begin; +insert into t1 values (1); +# +# Switching to connection 'default'. +# Add pending SNW lock. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +# Check that ALTER TABLE is waiting with pending SNW lock. +# Check that S, SH and SR locks are compatible with pending SNW +handler t1 open t; +handler t close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +4 +# Check that SW is incompatible with pending SNW +# Sending: +delete from t1 limit 1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above DELETE is blocked because of pending SNW lock. +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping ALTER. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping DELETE. +# +# We can't do similar check for SNW, SNRW and X locks because +# they will also be blocked by active SW lock. +# +# +# Switching to connection 'mdl_con2'. +# +# 2) Check compatibility for pending SNRW lock. +# +# Acquire SR lock in order to create pending SNRW lock. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'default'. +# Add pending SNRW lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con1'. +# Check that LOCK TABLE is waiting with pending SNRW lock. +# Check that S and SH locks are compatible with pending SNRW +handler t1 open t; +handler t close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +# Check that SR is incompatible with pending SNRW +# Sending: +select count(*) from t1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above SELECT is blocked because of pending SNRW lock. +# Unblock LOCK TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping LOCK TABLE. +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping SELECT. +count(*) +3 +# Restore pending SNRW lock. +# +# Switching to connection 'mdl_con2'. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'default'. +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con1'. +# Check that LOCK TABLE is waiting with pending SNRW lock. +# Check that SW is incompatible with pending SNRW +# Sending: +insert into t1 values (1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above INSERT is blocked because of pending SNRW lock. +# Unblock LOCK TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping LOCK TABLE. +unlock tables; +# +# Switching to connection 'mdl_con1'. +# Reaping INSERT. +# Restore pending SNRW lock. +# +# Switching to connection 'mdl_con2'. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'default'. +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con1'. +# Check that LOCK TABLE is waiting with pending SNRW lock. +# Check that SNW is compatible with pending SNRW +# So ALTER TABLE statements are not starved by LOCK TABLEs. +alter table t1 add primary key (c1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con2'. +# Unblock LOCK TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping LOCK TABLE. +unlock tables; +# +# We can't do similar check for SNRW and X locks because +# they will also be blocked by active SR lock. +# +# +# Switching to connection 'mdl_con2'. +# +# 3) Check compatibility for pending X lock. +# +# Acquire SR lock in order to create pending X lock. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'default'. +# Add pending X lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME TABLE is waiting with pending X lock. +# Check that SH locks are compatible with pending X +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +# Check that S is incompatible with pending X +# Sending: +handler t1 open;; +# +# Switching to connection 'mdl_con2'. +# Check that the above HANDLER OPEN is blocked because of pending X lock. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping HANDLER t1 OPEN. +handler t1 close; +# Restore pending X lock. +# +# Switching to connection 'mdl_con2'. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'default'. +# Add pending X lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME TABLE is waiting with pending X lock. +# Check that SR is incompatible with pending X +# Sending: +select count(*) from t1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above SELECT is blocked because of pending X lock. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping SELECT. +count(*) +4 +# Restore pending X lock. +# +# Switching to connection 'mdl_con2'. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'default'. +# Add pending X lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME TABLE is waiting with pending X lock. +# Check that SW is incompatible with pending X +# Sending: +delete from t1 limit 1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above DELETE is blocked because of pending X lock. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping DELETE. +# Restore pending X lock. +# +# Switching to connection 'mdl_con2'. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'default'. +# Add pending X lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME TABLE is waiting with pending X lock. +# Check that SNW is incompatible with pending X +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER TABLE is blocked because of pending X lock. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# Restore pending X lock. +# +# Switching to connection 'mdl_con2'. +handler t1 open; +# +# Switching to connection 'default'. +# Add pending X lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con1'. +# Check that RENAME TABLE is waiting with pending X lock. +# Check that SNRW is incompatible with pending X +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con3'. +# Check that the above LOCK TABLES is blocked because of pending X lock. +# +# Switching to connection 'mdl_con2'. +# Unblock RENAME TABLE. +handler t1 close; +# +# Switching to connection 'default'. +# Reaping RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLES. +unlock tables; +# +# Switching to connection 'default'. +# +# +# C) Now let us test how type-of-operation locks are handled in +# transactional context. Obviously we are mostly interested +# in conflicting types of locks. +# +# +# 1) Let us check how various locks used within transactional +# context interact with active/pending SNW lock. +# +# We start with case when we are acquiring lock on the table +# which was not used in the transaction before. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Create an active SNW lock on t2. +# We have to use DEBUG_SYNC facility as otherwise SNW lock +# will be immediately released (or upgraded to X lock). +insert into t2 values (1), (1); +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t2 add primary key (c1);; +# +# Switching to connection 'default'. +set debug_sync= 'now WAIT_FOR locked'; +# SR lock should be acquired without any waiting. +select count(*) from t2; +count(*) +2 +commit; +# Now let us check that we will wait in case of SW lock. +begin; +select count(*) from t1; +count(*) +3 +# Sending: +insert into t2 values (1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above INSERT is blocked. +# Unblock ALTER TABLE and thus INSERT. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'mdl_con1'. +# Reap ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +# Reap INSERT. +commit; +# +# Now let us see what happens when we are acquiring lock on the table +# which is already used in transaction. +# +# *) First, case when transaction which has SR lock on the table also +# locked in SNW mode acquires yet another SR lock and then tries +# to acquire SW lock. +begin; +select count(*) from t1; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Create an active SNW lock on t1. +set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'default'. +set debug_sync= 'now WAIT_FOR locked'; +# We should still be able to get SR lock without waiting. +select count(*) from t1; +count(*) +3 +# Since the above ALTER TABLE is not upgrading SNW lock to X by waiting +# for SW lock we won't create deadlock. +# So the below INSERT should not end-up with ER_LOCK_DEADLOCK error. +# Sending: +insert into t1 values (1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above INSERT is blocked. +# Unblock ALTER TABLE and thus INSERT. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'mdl_con1'. +# Reap ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +# Reap INSERT. +commit; +# +# **) Now test in which transaction that has SW lock on the table +# against which there is pending SNW lock acquires SR and SW +# locks on this table. +# +begin; +insert into t1 values (1); +# +# Switching to connection 'mdl_con1'. +# Create pending SNW lock on t1. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'default'. +# Wait until ALTER TABLE starts waiting for SNW lock. +# We should still be able to get both SW and SR locks without waiting. +select count(*) from t1; +count(*) +5 +delete from t1 limit 1; +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +# +# 2) Now similar tests for active SNW lock which is being upgraded +# to X lock. +# +# Again we start with case when we are acquiring lock on the +# table which was not used in the transaction before. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con2'. +# Start transaction which will prevent SNW -> X upgrade from +# completing immediately. +begin; +select count(*) from t2; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Create SNW lock pending upgrade to X on t2. +# Sending: +alter table t2 add column c2 int;; +# +# Switching to connection 'default'. +# Wait until ALTER TABLE starts waiting X lock. +# Check that attempt to acquire SR lock on t2 causes waiting. +# Sending: +select count(*) from t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above SELECT is blocked. +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap ALTER TABLE. +# +# Switching to connection 'default'. +# Reap SELECT. +count(*) +3 +commit; +# Do similar check for SW lock. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con2'. +# Start transaction which will prevent SNW -> X upgrade from +# completing immediately. +begin; +select count(*) from t2; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Create SNW lock pending upgrade to X on t2. +# Sending: +alter table t2 drop column c2;; +# +# Switching to connection 'default'. +# Wait until ALTER TABLE starts waiting X lock. +# Check that attempt to acquire SW lock on t2 causes waiting. +# Sending: +insert into t2 values (1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above INSERT is blocked. +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap ALTER TABLE. +# +# Switching to connection 'default'. +# Reap INSERT. +commit; +# +# Test for the case in which we are acquiring lock on the table +# which is already used in transaction. +# +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con1'. +# Create SNW lock pending upgrade to X. +# Sending: +alter table t1 add column c2 int;; +# +# Switching to connection 'default'. +# Wait until ALTER TABLE starts waiting X lock. +# Check that transaction is still able to acquire SR lock. +select count(*) from t1; +count(*) +4 +# Waiting trying to acquire SW lock will cause deadlock and +# therefore should cause an error. +delete from t1 limit 1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Unblock ALTER TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap ALTER TABLE. +# +# Switching to connection 'default'. +# +# 3) Check how various locks used within transactional context +# interact with active/pending SNRW lock. +# +# Once again we start with case when we are acquiring lock on +# the table which was not used in the transaction before. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con1'. +lock table t2 write; +# +# Switching to connection 'default'. +# Attempt to acquire SR should be blocked. It should +# not cause errors as it does not creates deadlock. +# Sending: +select count(*) from t2;; +# +# Switching to connection 'mdl_con1'. +# Check that the above SELECT is blocked +# Unblock SELECT. +unlock tables; +# +# Switching to connection 'default'. +# Reap SELECT. +count(*) +4 +commit; +# Repeat the same test for SW lock. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con1'. +lock table t2 write; +# +# Switching to connection 'default'. +# Again attempt to acquire SW should be blocked and should +# not cause any errors. +# Sending: +delete from t2 limit 1;; +# +# Switching to connection 'mdl_con1'. +# Check that the above DELETE is blocked +# Unblock DELETE. +unlock tables; +# +# Switching to connection 'default'. +# Reap DELETE. +commit; +# +# Now coverage for the case in which we are acquiring lock on +# the table which is already used in transaction and against +# which there is a pending SNRW lock request. +# +# *) Let us start with case when transaction has only a SR lock. +# +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con1'. +# Sending: +lock table t1 write;; +# +# Switching to connection 'default'. +# Wait until LOCK TABLE is blocked creating pending request for X lock. +# Check that another instance of SR lock is granted without waiting. +select count(*) from t1; +count(*) +4 +# Attempt to wait for SW lock will lead to deadlock, thus +# the below statement should end with ER_LOCK_DEADLOCK error. +delete from t1 limit 1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Unblock LOCK TABLES. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap LOCK TABLES. +unlock tables; +# +# Switching to connection 'default'. +# +# **) Now case when transaction has a SW lock. +# +begin; +delete from t1 limit 1; +# +# Switching to connection 'mdl_con1'. +# Sending: +lock table t1 write;; +# +# Switching to connection 'default'. +# Wait until LOCK TABLE is blocked creating pending request for X lock. +# Check that both SR and SW locks are granted without waiting +# and errors. +select count(*) from t1; +count(*) +3 +insert into t1 values (1, 1); +# Unblock LOCK TABLES. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap LOCK TABLES. +unlock tables; +# +# Switching to connection 'default'. +# +# 4) Check how various locks used within transactional context +# interact with active/pending X lock. +# +# As usual we start with case when we are acquiring lock on +# the table which was not used in the transaction before. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con2'. +# Start transaction which will prevent X lock from going away +# immediately. +begin; +select count(*) from t2; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Create pending X lock on t2. +# Sending: +rename table t2 to t3;; +# +# Switching to connection 'default'. +# Wait until RENAME TABLE starts waiting with pending X lock. +# Check that attempt to acquire SR lock on t2 causes waiting. +# Sending: +select count(*) from t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above SELECT is blocked. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap RENAME TABLE. +# +# Switching to connection 'default'. +# Reap SELECT. +ERROR 42S02: Table 'test.t2' doesn't exist +commit; +rename table t3 to t2; +# The same test for SW lock. +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con2'. +# Start transaction which will prevent X lock from going away +# immediately. +begin; +select count(*) from t2; +count(*) +3 +# +# Switching to connection 'mdl_con1'. +# Create pending X lock on t2. +# Sending: +rename table t2 to t3;; +# +# Switching to connection 'default'. +# Wait until RENAME TABLE starts waiting with pending X lock. +# Check that attempt to acquire SW lock on t2 causes waiting. +# Sending: +delete from t2 limit 1;; +# +# Switching to connection 'mdl_con2'. +# Check that the above DELETE is blocked. +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap RENAME TABLE. +# +# Switching to connection 'default'. +# Reap DELETE. +ERROR 42S02: Table 'test.t2' doesn't exist +commit; +rename table t3 to t2; +# +# Coverage for the case in which we are acquiring lock on +# the table which is already used in transaction and against +# which there is a pending X lock request. +# +# *) The first case is when transaction has only a SR lock. +# +begin; +select count(*) from t1; +count(*) +4 +# +# Switching to connection 'mdl_con1'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'default'. +# Wait until RENAME TABLE is blocked creating pending request for X lock. +# Check that another instance of SR lock is granted without waiting. +select count(*) from t1; +count(*) +4 +# Attempt to wait for SW lock will lead to deadlock, thus +# the below statement should end with ER_LOCK_DEADLOCK error. +delete from t1 limit 1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'default'. +# +# **) The second case is when transaction has a SW lock. +# +begin; +delete from t1 limit 1; +# +# Switching to connection 'mdl_con1'. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'default'. +# Wait until RENAME TABLE is blocked creating pending request for X lock. +# Check that both SR and SW locks are granted without waiting +# and errors. +select count(*) from t1; +count(*) +3 +insert into t1 values (1, 1); +# Unblock RENAME TABLE. +commit; +# +# Switching to connection 'mdl_con1'. +# Reap RENAME TABLE. +ERROR 42S01: Table 't2' already exists +# +# Switching to connection 'default'. +# Clean-up. +set debug_sync= 'RESET'; +drop table t1, t2; +# +# Additional coverage for some scenarios in which not quite +# correct use of S metadata locks by HANDLER statement might +# have caused deadlocks. +# +drop table if exists t1, t2; +create table t1 (i int); +create table t2 (j int); +insert into t1 values (1); +# +# First, check scenario in which we upgrade SNRW lock to X lock +# on a table while having HANDLER READ trying to acquire TL_READ +# on the same table. +# +handler t1 open; +# +# Switching to connection 'handler_con1'. +lock table t1 write; +# Upgrade SNRW to X lock. +# Sending: +alter table t1 add column j int;; +# +# Switching to connection 'handler_con2'. +# Wait until ALTER is blocked during upgrade. +# +# Switching to connection 'default'. +# The below statement should not cause deadlock. +handler t1 read first;; +# +# Switching to connection 'handler_con1'. +# Reap ALTER TABLE. +unlock tables; +# +# Switching to connection 'default'. +# Reap HANDLER READ. +i j +1 NULL +handler t1 close; +# +# Now, check scenario in which upgrade of SNRW lock to X lock +# can be blocked by HANDLER which is open in connection currently +# waiting to get table-lock owned by connection doing upgrade. +# +handler t1 open; +# +# Switching to connection 'handler_con1'. +lock table t1 write, t2 read; +# +# Switching to connection 'default'. +# Execute statement which will be blocked on table-level lock +# owned by connection 'handler_con1'. +# Sending: +insert into t2 values (1);; +# +# Switching to connection 'handler_con1'. +# Wait until INSERT is blocked on table-level lock. +# The below statement should not cause deadlock. +alter table t1 drop column j; +unlock tables; +# +# Switching to connection 'default'. +# Reap INSERT. +handler t1 close; +# +# Then, check the scenario in which upgrade of SNRW lock to X +# lock is blocked by HANDLER which is open in connection currently +# waiting to get SW lock on the same table. +# +handler t1 open; +# +# Switching to connection 'handler_con1'. +lock table t1 write; +# +# Switching to connection 'default'. +# The below insert should be blocked because active SNRW lock on 't1'. +# Sending: +insert into t1 values (1);; +# +# Switching to connection 'handler_con1'. +# Wait until INSERT is blocked because of SNRW lock. +# The below ALTER TABLE will be blocked because of presence of HANDLER. +# Sending: +alter table t1 add column j int;; +# +# Switching to connection 'default'. +# INSERT should be chosen as victim for resolving deadlock. +# Reaping INSERT. +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Close HANDLER to unblock ALTER TABLE. +handler t1 close; +# +# Switching to connection 'handler_con1'. +# Reaping ALTER TABLE. +unlock tables; +# +# Switching to connection 'default'. +# +# Finally, test in which upgrade of SNRW lock to X lock is blocked +# by HANDLER which is open in connection currently waiting to get +# SR lock on the table on which lock is upgraded. +# +handler t1 open; +# +# Switching to connection 'handler_con1'. +lock table t1 write, t2 write; +# +# Switching to connection 'default'. +# The below insert should be blocked because active SNRW lock on 't1'. +# Sending: +insert into t2 values (1);; +# +# Switching to connection 'handler_con1'. +# Wait until INSERT is blocked because of SNRW lock. +# The below ALTER TABLE will be blocked because of presence of HANDLER. +# Sending: +alter table t1 drop column j;; +# +# Switching to connection 'default'. +# INSERT should be chosen as victim for resolving deadlock. +# Reaping INSERT. +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Close HANDLER to unblock ALTER TABLE. +handler t1 close; +# +# Switching to connection 'handler_con1'. +# Reaping ALTER TABLE. +unlock tables; +# +# Switching to connection 'default'. +# Clean-up. +drop tables t1, t2; +# # Test coverage for basic deadlock detection in metadata # locking subsystem. # @@ -118,53 +1852,46 @@ commit; # # Switching to connection 'deadlock_con1'. begin; -insert into t1 values (1); -# -# Switching to connection 'deadlock_con2'. -begin; -insert into t3 values (1); +insert into t2 values (1); # # Switching to connection 'default'. -# Send: -rename table t2 to t0, t3 to t2, t0 to t3;; +lock table t1 write; # # Switching to connection 'deadlock_con1'. -# Wait until the above RENAME TABLE is blocked because it has to wait -# for 'deadlock_con2' which holds shared metadata lock on 't3'. # The below SELECT statement should wait for metadata lock -# on table 't2' and should not produce ER_LOCK_DEADLOCK +# on table 't1' and should not produce ER_LOCK_DEADLOCK # immediately as no deadlock is possible at the moment. -select * from t2;; +select * from t1;; # -# Switching to connection 'deadlock_con3'. -# Wait until the above SELECT * FROM t2 is starts waiting -# for an exclusive metadata lock to go away. +# Switching to connection 'deadlock_con2'. +# Wait until the above SELECT * FROM t1 is starts waiting +# for an UNRW metadata lock to go away. # Send RENAME TABLE statement that will deadlock with the # SELECT statement and thus should abort the latter. -rename table t1 to t5, t2 to t1, t5 to t2;; +rename table t1 to t0, t2 to t1, t0 to t2;; +# +# Switching to connection 'default'. +# Wait till above RENAME TABLE is blocked while holding +# pending X lock on t1. +# Allow the above RENAME TABLE to acquire lock on t1 and +# create pending lock on t2 thus creating deadlock. +unlock tables; # # Switching to connection 'deadlock_con1'. # Since the latest RENAME TABLE entered in deadlock with SELECT # statement the latter should be aborted and emit ER_LOCK_DEADLOCK # error. -# Reap SELECT * FROM t2. +# Reap SELECT * FROM t1. ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # # Again let us check that failure of the SELECT statement has not -# released metadata lock on table 't1', i.e. that the latest RENAME +# released metadata lock on table 't2', i.e. that the latest RENAME # is blocked. # Commit transaction to unblock this RENAME TABLE. commit; # # Switching to connection 'deadlock_con2'. -# Commit transaction to unblock the first RENAME TABLE. -commit; -# -# Switching to connection 'default'. -# Reap RENAME TABLE t2 TO t0 ... . -# -# Switching to connection 'deadlock_con3'. -# Reap RENAME TABLE t1 TO t5 ... . +# Reap RENAME TABLE ... . # # Switching to connection 'default'. drop tables t1, t2, t3, t4; @@ -173,10 +1900,19 @@ drop tables t1, t2, t3, t4; # also takes into account requests for metadata lock upgrade. # create table t1 (i int); +insert into t1 values (1); +# Avoid race which occurs when SELECT in 'deadlock_con1' connection +# accesses table before the above INSERT unlocks the table and thus +# its result becomes visible to other connections. +select * from t1; +i +1 # # Switching to connection 'deadlock_con1'. begin; -insert into t1 values (1); +select * from t1; +i +1 # # Switching to connection 'default'. # Send: @@ -200,42 +1936,6 @@ commit; # Reap ALTER TABLE ... RENAME. drop table t2; # -# Finally, test case in which deadlock (or potentially livelock) occurs -# between metadata locking subsystem and table definition cache/table -# locks, but which should still be detected by our empiric. -# -create table t1 (i int); -# -# Switching to connection 'deadlock_con1'. -begin; -insert into t1 values (1); -# -# Switching to connection 'default'. -lock tables t1 write; -# -# Switching to connection 'deadlock_con1'. -# Send: -insert into t1 values (2);; -# -# Switching to connection 'default'. -# Wait until INSERT in connection 'deadlock_con1' is blocked on -# table-level lock. -# Send: -alter table t1 add column j int;; -# -# Switching to connection 'deadlock_con1'. -# The above ALTER TABLE statement should cause INSERT statement in -# this connection to be aborted and emit ER_LOCK_DEADLOCK error. -# Reap INSERT -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -# Commit transaction to unblock ALTER TABLE. -commit; -# -# Switching to connection 'default'. -# Reap ALTER TABLE. -unlock tables; -drop table t1; -# # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() # on INSERT + CREATE TRIGGER". # @@ -297,7 +1997,7 @@ SET DEBUG_SYNC= 'now WAIT_FOR locked'; # Now INSERT has a MDL on the non-existent table t1. # # Continue the INSERT once CREATE waits for exclusive lock -SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL finish'; # Try to create that table. CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1)); # Connection 2 @@ -323,7 +2023,7 @@ SET DEBUG_SYNC= 'now WAIT_FOR locked'; # Now INSERT has a MDL on the non-existent table t1. # # Continue the INSERT once CREATE waits for exclusive lock -SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL finish'; # Try to create that table. CREATE TABLE t1 LIKE t2; # Connection 2 @@ -347,10 +2047,10 @@ create table t1 (i int); # Let us check that we won't deadlock if during filling # of I_S table we encounter conflicting metadata lock # which owner is in its turn waiting for our connection. -lock tables t1 write; +lock tables t1 read; # Switching to connection 'con46044'. # Sending: -create table t2 select * from t1;; +create table t2 select * from t1 for update;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # First let us check that SHOW FIELDS/DESCRIBE doesn't @@ -386,10 +2086,10 @@ drop table t2; # # We check same three queries to I_S in this new situation. # Switching to connection 'con46044_2'. -lock tables t1 write; +lock tables t1 read; # Switching to connection 'con46044'. # Sending: -create table t2 select * from t1;; +create table t2 select * from t1 for update;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # Let us check that SHOW FIELDS/DESCRIBE gets blocked. @@ -406,10 +2106,10 @@ Field Type Null Key Default Extra i int(11) YES NULL drop table t2; # Switching to connection 'con46044_2'. -lock tables t1 write; +lock tables t1 read; # Switching to connection 'con46044'. # Sending: -create table t2 select * from t1;; +create table t2 select * from t1 for update;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # Check that I_S query which reads only .FRMs gets blocked. @@ -426,10 +2126,10 @@ column_name i drop table t2; # Switching to connection 'con46044_2'. -lock tables t1 write; +lock tables t1 read; # Switching to connection 'con46044'. # Sending: -create table t2 select * from t1;; +create table t2 select * from t1 for update;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # Finally, check that I_S query which does full-blown table open @@ -458,7 +2158,9 @@ set debug_sync= 'RESET'; create table t1 (c1 int primary key, c2 int, c3 int); insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); begin; -update t1 set c3=c3+1 where c2=3; +select * from t1 where c2 = 3; +c1 c2 c3 +3 3 0 # # Switching to connection 'con46273'. set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR alter_go'; @@ -466,11 +2168,11 @@ alter table t1 add column e int, rename to t2;; # # Switching to connection 'default'. set debug_sync='now WAIT_FOR alter_table_locked'; -set debug_sync='wait_for_lock SIGNAL alter_go'; +set debug_sync='before_open_table_wait_refresh SIGNAL alter_go'; # The below statement should get ER_LOCK_DEADLOCK error # (i.e. it should not allow ALTER to proceed, and then # fail due to 't1' changing its name to 't2'). -update t1 set c3=c3+1 where c2=4; +update t1 set c3=c3+1 where c2 = 3; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # # Let us check that failure of the above statement has not released diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index d974cfb9605..a14d099c673 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -35,7 +35,7 @@ call bug9486(); show processlist; Id User Host db Command Time State Info # root localhost test Sleep # NULL -# root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2 +# root localhost test Query # Waiting for table update t1, t2 set val= 1 where id1=id2 # root localhost test Query # NULL show processlist # root localhost test Sleep # NULL unlock tables; diff --git a/mysql-test/r/truncate_coverage.result b/mysql-test/r/truncate_coverage.result index bb036329f6f..7a5021f55e2 100644 --- a/mysql-test/r/truncate_coverage.result +++ b/mysql-test/r/truncate_coverage.result @@ -7,18 +7,20 @@ CREATE TABLE t1 (c1 INT); INSERT INTO t1 VALUES (1); # # connection con1 -START TRANSACTION; -INSERT INTO t1 VALUES (2); +HANDLER t1 OPEN; # # connection default LOCK TABLE t1 WRITE; SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; TRUNCATE TABLE t1; # -# connection con1 +# connection con2 SET DEBUG_SYNC='now WAIT_FOR waiting'; KILL QUERY @id; -COMMIT; +# +# connection con1 +# Release shared metadata lock by closing HANDLER. +HANDLER t1 CLOSE; # # connection default ERROR 70100: Query execution was interrupted @@ -29,17 +31,18 @@ CREATE TABLE t1 (c1 INT); INSERT INTO t1 VALUES (1); # # connection con1 -START TRANSACTION; -INSERT INTO t1 VALUES (2); +HANDLER t1 OPEN; # # connection default LOCK TABLE t1 WRITE; SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; TRUNCATE TABLE t1; # -# connection con1 +# connection con2 SET DEBUG_SYNC='now WAIT_FOR waiting'; -COMMIT; +# +# connection con1 +HANDLER t1 CLOSE; # # connection default ERROR 42S02: Table 'test.t1' doesn't exist @@ -55,7 +58,7 @@ START TRANSACTION; INSERT INTO t1 VALUES (2); # # connection default -SET DEBUG_SYNC='mdl_acquire_exclusive_locks_wait SIGNAL waiting'; +SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting'; TRUNCATE TABLE t1; # # connection con1 diff --git a/mysql-test/suite/funcs_1/datadict/processlist_val.inc b/mysql-test/suite/funcs_1/datadict/processlist_val.inc index 8b10cfc5e97..6fcaf45c848 100644 --- a/mysql-test/suite/funcs_1/datadict/processlist_val.inc +++ b/mysql-test/suite/funcs_1/datadict/processlist_val.inc @@ -425,7 +425,7 @@ echo # Poll till INFO is no more NULL and State = 'Table Lock'. ; let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE INFO IS NOT NULL AND STATE = 'Table Lock'; + WHERE INFO IS NOT NULL AND STATE = 'Waiting for table'; --source include/wait_condition.inc echo # Expect result: diff --git a/mysql-test/suite/funcs_1/r/processlist_val_no_prot.result b/mysql-test/suite/funcs_1/r/processlist_val_no_prot.result index 34b2e48fc7e..e8ee784bec4 100644 --- a/mysql-test/suite/funcs_1/r/processlist_val_no_prot.result +++ b/mysql-test/suite/funcs_1/r/processlist_val_no_prot.result @@ -195,9 +195,11 @@ SELECT COUNT(*) FROM test.t1; # Poll till INFO is no more NULL and State = 'Table Lock'. +Timeout in wait_condition.inc for SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST +WHERE INFO IS NOT NULL AND STATE = 'Table Lock' SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST; ID USER HOST DB COMMAND TIME STATE INFO - test_user information_schema Query