diff options
401 files changed, 16556 insertions, 9520 deletions
diff --git a/.bzrignore b/.bzrignore index 095c04b36ee..57fcbdd8f73 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1770,3 +1770,4 @@ vio/viotest-sslconnect.cpp vio/viotest.cpp zlib/*.ds? zlib/*.vcproj +libmysqld/event_scheduler.cc diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index 589e609beeb..02d160158b3 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -82,8 +82,11 @@ path=`dirname $0` export AM_MAKEFLAGS AM_MAKEFLAGS="-j 4" -# SSL library to use. -SSL_LIBRARY=--with-yassl +# SSL library to use.--with-ssl will select our bundled yaSSL +# implementation of SSL. To use openSSl you will nee too point out +# the location of openSSL headers and lbs on your system. +# Ex --with-ssl=/usr +SSL_LIBRARY=--with-ssl if [ "x$warning_mode" != "xpedantic" ]; then # Both C and C++ warnings diff --git a/BUILD/compile-dist b/BUILD/compile-dist index 99ddccf7f10..326de19da13 100755 --- a/BUILD/compile-dist +++ b/BUILD/compile-dist @@ -54,7 +54,7 @@ fi --with-federated-storage-engine \ --with-berkeley-db \ --with-innodb \ - --with-yassl \ + --with-ssl \ --enable-thread-safe-client \ --with-extra-charsets=complex \ --with-ndbcluster \ diff --git a/BUILD/compile-pentium-debug-openssl b/BUILD/compile-pentium-debug-openssl index 91c3b448c7e..2e24dbfd2f1 100755 --- a/BUILD/compile-pentium-debug-openssl +++ b/BUILD/compile-pentium-debug-openssl @@ -6,6 +6,6 @@ path=`dirname $0` extra_flags="$pentium_cflags $debug_cflags" extra_configs="$pentium_configs $debug_configs" -extra_configs="$extra_configs --with-debug=full --with-openssl" +extra_configs="$extra_configs --with-debug=full --with-ssl=/usr" . "$path/FINISH.sh" diff --git a/BUILD/compile-pentium-debug-yassl b/BUILD/compile-pentium-debug-yassl index 84ca489903d..61ad2937c4a 100755 --- a/BUILD/compile-pentium-debug-yassl +++ b/BUILD/compile-pentium-debug-yassl @@ -6,6 +6,6 @@ path=`dirname $0` extra_flags="$pentium_cflags $debug_cflags" extra_configs="$pentium_configs $debug_configs" -extra_configs="$extra_configs --with-debug=full --with-yassl" +extra_configs="$extra_configs --with-debug=full --with-ssl" . "$path/FINISH.sh" diff --git a/BUILD/compile-pentium-gcov b/BUILD/compile-pentium-gcov index 05cb0bb0d78..b024bba49bf 100755 --- a/BUILD/compile-pentium-gcov +++ b/BUILD/compile-pentium-gcov @@ -3,8 +3,19 @@ path=`dirname $0` . "$path/SETUP.sh" -extra_flags="$pentium_cflags -fprofile-arcs -ftest-coverage" +# Need to disable ccache, or we loose the gcov-needed compiler output files. +CCACHE_DISABLE=1 +export CCACHE_DISABLE + +# GCC4 needs -fprofile-arcs -ftest-coverage on the linker command line (as well +# as on the compiler command line), and this requires setting LDFLAGS for BDB. +export LDFLAGS="-fprofile-arcs -ftest-coverage" + +# The -fprofile-arcs and -ftest-coverage options cause GCC to instrument the +# code with profiling information used by gcov. +# the -DDISABLE_TAO_ASM is needed to avoid build failures in Yassl. +extra_flags="$pentium_cflags -fprofile-arcs -ftest-coverage -DDISABLE_TAO_ASM" extra_configs="$pentium_configs $debug_configs --disable-shared $static_link" -extra_configs="$extra_configs --with-innodb --with-berkeley-db" +extra_configs="$extra_configs $max_configs" . "$path/FINISH.sh" diff --git a/client/client_priv.h.rej b/client/client_priv.h.rej new file mode 100644 index 00000000000..ac3818bb1e1 --- /dev/null +++ b/client/client_priv.h.rej @@ -0,0 +1,15 @@ +*************** +*** 50,55 **** + OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, + #endif + OPT_TRIGGERS, + OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, +! OPT_TZ_UTC, OPT_AUTO_CLOSE + }; +--- 50,55 ---- + OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, + #endif + OPT_TRIGGERS, + OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, +! OPT_TZ_UTC, OPT_AUTO_CLOSE, OPT_SSL_VERIFY_SERVER_CERT + }; diff --git a/client/get_password.c b/client/get_password.c index 1b7b4e65a9f..b643b760718 100644 --- a/client/get_password.c +++ b/client/get_password.c @@ -64,7 +64,7 @@ /* were just going to fake it here and get input from the keyboard */ -char *get_tty_password(char *opt_message) +char *get_tty_password(const char *opt_message) { char to[80]; char *pos=to,*end=to+sizeof(to)-1; @@ -150,7 +150,7 @@ static void get_password(char *to,uint length,int fd,bool echo) #endif /* ! HAVE_GETPASS */ -char *get_tty_password(char *opt_message) +char *get_tty_password(const char *opt_message) { #ifdef HAVE_GETPASS char *passbuff; diff --git a/client/mysql.cc b/client/mysql.cc index ae1010f0a72..96df1fafc3b 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -448,6 +448,14 @@ int main(int argc,char *argv[]) MYF(MY_WME)); if (histfile) sprintf(histfile,"%s/.mysql_history",getenv("HOME")); + char link_name[FN_REFLEN]; + if (my_readlink(link_name, histfile, 0) == 0 && + strncmp(link_name, "/dev/null", 10) == 0) + { + /* The .mysql_history file is a symlink to /dev/null, don't use it */ + my_free(histfile, MYF(MY_ALLOW_ZERO_PTR)); + histfile= 0; + } } if (histfile) { @@ -484,7 +492,7 @@ sig_handler mysql_end(int sig) { mysql_close(&mysql); #ifdef HAVE_READLINE - if (!status.batch && !quick && !opt_html && !opt_xml) + if (!status.batch && !quick && !opt_html && !opt_xml && histfile) { /* write-history */ if (verbose) @@ -2338,8 +2346,7 @@ print_table_data(MYSQL_RES *result) uint visible_length; uint extra_padding; - /* If this column may have a null value, use "NULL" for empty. */ - if (! not_null_flag[off] && (lengths[off] == 0)) + if (! not_null_flag[off] && (cur[off] == NULL)) { buffer= "NULL"; data_length= 4; diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 490a74e6035..4b2527c25ef 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -815,7 +815,8 @@ static void die(const char* fmt, ...) fprintf(stderr, "\n"); va_end(args); cleanup(); - my_end(0); + /* We cannot free DBUG, it is used in global destructors after exit(). */ + my_end(MY_DONT_FREE_DBUG); exit(1); } @@ -1487,7 +1488,8 @@ int main(int argc, char** argv) cleanup(); free_defaults(defaults_argv); my_free_open_file_info(); - my_end(0); + /* We cannot free DBUG, it is used in global destructors after exit(). */ + my_end(MY_DONT_FREE_DBUG); exit(exit_value); DBUG_RETURN(exit_value); // Keep compilers happy } diff --git a/config/ac-macros/ha_ndbcluster.m4 b/config/ac-macros/ha_ndbcluster.m4 index aee445f8d58..505d000c196 100644 --- a/config/ac-macros/ha_ndbcluster.m4 +++ b/config/ac-macros/ha_ndbcluster.m4 @@ -237,7 +237,7 @@ AC_DEFUN([MYSQL_SETUP_NDBCLUSTER], [ if test X"$have_ndb_binlog" = Xyes then - AC_DEFINE([HAVE_NDB_BINLOG], [1], + AC_DEFINE([WITH_NDB_BINLOG], [1], [Including Ndb Cluster Binlog]) AC_MSG_RESULT([Including Ndb Cluster Binlog]) else diff --git a/config/ac-macros/openssl.m4 b/config/ac-macros/openssl.m4 deleted file mode 100644 index a23c46eed00..00000000000 --- a/config/ac-macros/openssl.m4 +++ /dev/null @@ -1,136 +0,0 @@ -AC_DEFUN([MYSQL_FIND_OPENSSL], [ - incs="$1" - libs="$2" - eval shrexts=\"$shrext_cmds\" - case "$incs---$libs" in - ---) - for d in /usr/ssl/include /usr/local/ssl/include /usr/include \ -/usr/include/ssl /opt/ssl/include /opt/openssl/include \ -/usr/local/ssl/include /usr/local/include /usr/freeware/include ; do - if test -f $d/openssl/ssl.h ; then - OPENSSL_INCLUDE=-I$d - fi - done - - for d in /usr/ssl/lib /usr/local/ssl/lib /usr/lib/openssl \ -/usr/lib /usr/lib64 /opt/ssl/lib /opt/openssl/lib \ -/usr/freeware/lib32 /usr/local/lib/ ; do - # Just to be safe, we test for ".so" anyway - if test -f $d/libssl.a || test -f $d/libssl.so || test -f $d/libssl$shrext ; then - OPENSSL_LIB=$d - fi - done - ;; - ---* | *---) - AC_MSG_ERROR([if either 'includes' or 'libs' is specified, both must be specified]) - ;; - * ) - if test -f $incs/openssl/ssl.h ; then - OPENSSL_INCLUDE=-I$incs - fi - # Just to be safe, we test for ".so" anyway - if test -f $libs/libssl.a || test -f $libs/libssl.so || test -f $libs/libssl$shrext ; then - OPENSSL_LIB=$libs - fi - ;; - esac - - # On RedHat 9 we need kerberos to compile openssl - for d in /usr/kerberos/include - do - if test -f $d/krb5.h ; then - OPENSSL_KERBEROS_INCLUDE="$d" - fi - done - - - if test -z "$OPENSSL_LIB" -o -z "$OPENSSL_INCLUDE" ; then - echo "Could not find an installation of OpenSSL" - if test -n "$OPENSSL_LIB" ; then - if test "$TARGET_LINUX" = "true"; then - echo "Looks like you've forgotten to install OpenSSL development RPM" - fi - fi - exit 1 - fi - -]) - -AC_DEFUN([MYSQL_CHECK_OPENSSL], [ -AC_MSG_CHECKING(for OpenSSL) - AC_ARG_WITH([openssl], - [ --with-openssl[=DIR] Include the OpenSSL support], - [openssl="$withval"], - [openssl=no]) - - AC_ARG_WITH([openssl-includes], - [ - --with-openssl-includes=DIR - Find OpenSSL headers in DIR], - [openssl_includes="$withval"], - [openssl_includes=""]) - - AC_ARG_WITH([openssl-libs], - [ - --with-openssl-libs=DIR - Find OpenSSL libraries in DIR], - [openssl_libs="$withval"], - [openssl_libs=""]) - - if test "$openssl" != "no" - then - if test "$openssl" != "yes" - then - if test -z "$openssl_includes" - then - openssl_includes="$openssl/include" - fi - if test -z "$openssl_libs" - then - openssl_libs="$openssl/lib" - fi - fi - MYSQL_FIND_OPENSSL([$openssl_includes], [$openssl_libs]) - #force VIO use - AC_MSG_RESULT(yes) - openssl_libs="-L$OPENSSL_LIB -lssl -lcrypto" - # Don't set openssl_includes to /usr/include as this gives us a lot of - # compiler warnings when using gcc 3.x - openssl_includes="" - if test "$OPENSSL_INCLUDE" != "-I/usr/include" - then - openssl_includes="$OPENSSL_INCLUDE" - fi - if test "$OPENSSL_KERBEROS_INCLUDE" - then - openssl_includes="$openssl_includes -I$OPENSSL_KERBEROS_INCLUDE" - fi - AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL]) - - # openssl-devel-0.9.6 requires dlopen() and we can't link staticly - # on many platforms (We should actually test this here, but it's quite - # hard) to do as we are doing libtool for linking. - using_static="" - case "$CLIENT_EXTRA_LDFLAGS $MYSQLD_EXTRA_LDFLAGS" in - *-all-static*) using_static="yes" ;; - esac - if test "$using_static" = "yes" - then - echo "You can't use the --all-static link option when using openssl." - exit 1 - fi - NON_THREADED_CLIENT_LIBS="$NON_THREADED_CLIENT_LIBS $openssl_libs" - else - AC_MSG_RESULT(no) - if test ! -z "$openssl_includes" - then - AC_MSG_ERROR(Can't have --with-openssl-includes without --with-openssl); - fi - if test ! -z "$openssl_libs" - then - AC_MSG_ERROR(Can't have --with-openssl-libs without --with-openssl); - fi - fi - AC_SUBST(openssl_libs) - AC_SUBST(openssl_includes) -]) diff --git a/config/ac-macros/replication.m4 b/config/ac-macros/replication.m4 index 3a0c83d6498..babfa000a82 100644 --- a/config/ac-macros/replication.m4 +++ b/config/ac-macros/replication.m4 @@ -15,7 +15,7 @@ AC_DEFUN([MYSQL_CHECK_REPLICATION], [ case "$row_based" in yes ) - AC_DEFINE([HAVE_ROW_BASED_REPLICATION], [1], [Define to have row-based replication]) + AC_DEFINE([WITH_ROW_BASED_REPLICATION], [1], [Define to have row-based replication]) AC_MSG_RESULT([-- including row-based replication]) [have_row_based=yes] ;; diff --git a/config/ac-macros/ssl.m4 b/config/ac-macros/ssl.m4 new file mode 100644 index 00000000000..0f2f207c36f --- /dev/null +++ b/config/ac-macros/ssl.m4 @@ -0,0 +1,210 @@ +dnl =========================================================================== +dnl Support for SSL +dnl =========================================================================== +dnl +dnl + +dnl --------------------------------------------------------------------------- +dnl Macro: MYSQL_USE_BUNDLED_YASSL +dnl +dnl SYNOPSIS +dnl MYSQL_USE_BUNDLED_YASSL() +dnl +dnl DESCRIPTION +dnl Add defines so yassl is built and linked with +dnl --------------------------------------------------------------------------- +AC_DEFUN([MYSQL_USE_BUNDLED_YASSL], [ + + AC_CONFIG_FILES(extra/yassl/Makefile dnl + extra/yassl/taocrypt/Makefile dnl + extra/yassl/taocrypt/benchmark/Makefile dnl + extra/yassl/taocrypt/src/Makefile dnl + extra/yassl/taocrypt/test/Makefile dnl + extra/yassl/src/Makefile dnl + extra/yassl/testsuite/Makefile) + + with_bundled_yassl="yes" + + yassl_dir="yassl" + AC_SUBST([yassl_dir]) + + yassl_libs="-L\$(top_srcdir)/extra/yassl/src -lyassl -L\$(top_srcdir)/extra/yassl/taocrypt/src -ltaocrypt" + AC_SUBST(yassl_libs) + yassl_includes="-I\$(top_srcdir)/extra/yassl/include" + AC_SUBST(yassl_includes) + + AC_DEFINE([HAVE_OPENSSL], [1], [Defined by configure. Using yaSSL for SSL.]) + AC_DEFINE([HAVE_YASSL], [1], [Defined by configure. Using yaSSL for SSL.]) + + # System specific checks + yassl_integer_extra_cxxflags="" + case $host_cpu--$CXX_VERSION in + sparc*--*Sun*C++*5.6*) + # Disable inlining when compiling taocrypt/src/ + yassl_taocrypt_extra_cxxflags="+d" + AC_MSG_NOTICE([disabling inlining for yassl/taocrypt/src/]) + ;; + esac + AC_SUBST([yassl_taocrypt_extra_cxxflags]) + + # Link extra/yassl/include/openssl subdir to include/ + yassl_h_ln_cmd="\$(LN) -s \$(top_srcdir)/extra/yassl/include/openssl openssl" + AC_SUBST(yassl_h_ln_cmd) + + AC_MSG_RESULT([using bundled yaSSL]) +]) + + +dnl --------------------------------------------------------------------------- +dnl Macro: MYSQL_CHECK_SSL_DIR +dnl +dnl SYNOPSIS +dnl MYSQL_CHECK_SSL_DIR(includes, libs) +dnl +dnl DESCRIPTION +dnl Auxiliary macro to check for ssl at given path +dnl +dnl --------------------------------------------------------------------------- + +AC_DEFUN([MYSQL_CHECK_SSL_DIR], [ +ssl_incs="$1" +ssl_libs="$2" +save_CPPFLAGS="$CPPFLAGS" +save_LIBS="$LIBS" +CPPFLAGS="$ssl_incs $CPPFLAGS" +LIBS="$LIBS $ssl_libs" +AC_TRY_LINK([#include <openssl/ssl.h>], + [return SSL_library_init();], + [mysql_ssl_found="yes"], + [mysql_ssl_found="no"]) +CPPFLAGS="$save_CPPFLAGS" +LIBS="$save_LIBS" +]) + + +dnl --------------------------------------------------------------------------- +dnl Macro: MYSQL_FIND_OPENSSL +dnl +dnl SYNOPSIS +dnl MYSQL_FIND_OPENSSL(location) +dnl +dnl DESCRIPTION +dnl Search the location for OpenSSL support +dnl +dnl --------------------------------------------------------------------------- +AC_DEFUN([MYSQL_FIND_OPENSSL], [ + location="$1" + + # + # Set include paths + # + openssl_include="$location/include" + openssl_includes="" + + # Don't set ssl_includes to /usr/include as this gives us a lot of + # compiler warnings when using gcc 3.x + if test "$openssl_include" != "/usr/include" + then + openssl_includes="-I$ssl_include" + fi + + # + # Try to link with openSSL libs in <location> + # + openssl_libs="-L$location/lib/ -lssl -lcrypto" + MYSQL_CHECK_SSL_DIR([$openssl_includes], [$openssl_libs]) + + if test "$mysql_ssl_found" == "no" + then + # + # BUG 764: Compile failure with OpenSSL on Red Hat Linux (krb5.h missing) + # Try to link with include paths to kerberos set + # + openssl_includes="$openssl_includes -I/usr/kerberos/include" + MYSQL_CHECK_SSL_DIR([$openssl_includes], [$openssl_libs]) + fi + + if test "$mysql_ssl_found" == "no" + then + AC_MSG_ERROR([Could not link with SSL libs at $location]) + fi + + # openssl-devel-0.9.6 requires dlopen() and we can't link staticly + # on many platforms (We should actually test this here, but it's quite + # hard to do as we are doing libtool for linking.) + case "$CLIENT_EXTRA_LDFLAGS $MYSQLD_EXTRA_LDFLAGS" in + *-all-static*) + AC_MSG_ERROR([You can't use the --all-static link option when using openssl.]) + ;; + esac + + AC_SUBST(openssl_includes) + AC_SUBST(openssl_libs) + + NON_THREADED_CLIENT_LIBS="$NON_THREADED_CLIENT_LIBS $openssl_libs" + + AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL]) + AC_MSG_RESULT([using openSSL from $location]) +]) + + + +dnl ------------------------------------------------------------------------ +dnl Macro: MYSQL_CHECK_SSL +dnl +dnl SYNOPSIS +dnl MYSQL_CHECK_SSL +dnl +dnl Provides the following configure options: +dnl --with-ssl=DIR +dnl Possible DIR values are: +dnl - no - the macro will disable use of ssl +dnl - bundled, empty or not specified - means use ssl lib +dnl bundled along with MySQL sources +dnl - ssl location prefix - given location prefix, the macro expects +dnl to find the header files in $prefix/include/, and libraries in +dnl $prefix/lib. If headers or libraries weren't found at $prefix, the +dnl macro bails out with error. +dnl +dnl ------------------------------------------------------------------------ +AC_DEFUN([MYSQL_CHECK_SSL], [ +AC_MSG_CHECKING(for SSL) + AC_ARG_WITH([ssl], + [ --with-ssl[=DIR] Include SSL support], + [mysql_ssl_dir="$withval"], + [mysql_ssl_dir=no]) + + if test "$with_yassl" + then + AC_MSG_ERROR([The flag --with-yassl is deprecated, use --with-ssl]) + fi + + if test "$with_openssl" + then + AC_MSG_ERROR([The flag --with-openssl is deprecated, use --with-ssl]) + fi + + case "$mysql_ssl_dir" in + "no") + # + # Don't include SSL support + # + AC_MSG_RESULT([disabled]) + ;; + + "bundled"|"yes") + # + # Use the bundled SSL implementation (yaSSL) + # + MYSQL_USE_BUNDLED_YASSL + ;; + + *) + # + # A location where to search for OpenSSL was specified + # + MYSQL_FIND_OPENSSL([$mysql_ssl_dir]) + ;; + esac + AM_CONDITIONAL([HAVE_YASSL], [ test "$with_bundled_yassl" = "yes" ]) +]) diff --git a/config/ac-macros/yassl.m4 b/config/ac-macros/yassl.m4 deleted file mode 100644 index f6eee32ff84..00000000000 --- a/config/ac-macros/yassl.m4 +++ /dev/null @@ -1,45 +0,0 @@ -AC_DEFUN([MYSQL_CHECK_YASSL], [ - AC_MSG_CHECKING(for yaSSL) - AC_ARG_WITH([yassl], [ --with-yassl Include the yaSSL support],,) - - if test "$with_yassl" = "yes" - then - if test "$openssl" != "no" - then - AC_MSG_ERROR([Cannot configure MySQL to use yaSSL and OpenSSL simultaneously.]) - fi - AC_MSG_RESULT([using bundled yaSSL]) - AC_CONFIG_FILES(extra/yassl/Makefile dnl - extra/yassl/taocrypt/Makefile dnl - extra/yassl/taocrypt/benchmark/Makefile dnl - extra/yassl/taocrypt/src/Makefile dnl - extra/yassl/taocrypt/test/Makefile dnl - extra/yassl/src/Makefile dnl - extra/yassl/testsuite/Makefile) - yassl_dir="yassl" - yassl_libs="-L\$(top_srcdir)/extra/yassl/src -lyassl -L\$(top_srcdir)/extra/yassl/taocrypt/src -ltaocrypt" - yassl_includes="-I\$(top_srcdir)/extra/yassl/include" - AC_DEFINE([HAVE_OPENSSL], [1], [Defined by configure. Using yaSSL for OpenSSL emulation.]) - AC_DEFINE([HAVE_YASSL], [1], [Defined by configure. Using yaSSL for OpenSSL emulation.]) - # System specific checks - yassl_integer_extra_cxxflags="" - case $host_cpu--$CXX_VERSION in - sparc*--*Sun*C++*5.6*) - # Disable inlining when compiling taocrypt/src/ - yassl_taocrypt_extra_cxxflags="+d" - AC_MSG_NOTICE([disabling inlining for yassl/taocrypt/src/]) - ;; - esac - AC_SUBST([yassl_taocrypt_extra_cxxflags]) - # Link extra/yassl/include/openssl subdir to include/ - yassl_h_ln_cmd="\$(LN) -s \$(top_srcdir)/extra/yassl/include/openssl openssl" - AC_SUBST(yassl_h_ln_cmd) - else - yassl_dir="" - AC_MSG_RESULT(no) - fi - AC_SUBST(yassl_libs) - AC_SUBST(yassl_includes) - AC_SUBST(yassl_dir) - AM_CONDITIONAL([HAVE_YASSL], [ test "$with_yassl" = "yes" ]) -]) diff --git a/config/ac-macros/zlib.m4 b/config/ac-macros/zlib.m4 index 713e7072c6f..17b951d4723 100644 --- a/config/ac-macros/zlib.m4 +++ b/config/ac-macros/zlib.m4 @@ -89,10 +89,11 @@ case $SYSTEM_TYPE in fi ;; *) - # Just to be safe, we test for ".so" anyway - eval shrexts=\"$shrext_cmds\" - if test \( -f "$mysql_zlib_dir/lib/libz.a" -o -f "$mysql_zlib_dir/lib/libz.so" -o \ - -f "$mysql_zlib_dir/lib/libz$shrext" \) \ + # Test for libz using all known library file endings + if test \( -f "$mysql_zlib_dir/lib/libz.a" -o \ + -f "$mysql_zlib_dir/lib/libz.so" -o \ + -f "$mysql_zlib_dir/lib/libz.sl" -o \ + -f "$mysql_zlib_dir/lib/libz.dylib" \) \ -a -f "$mysql_zlib_dir/include/zlib.h"; then ZLIB_INCLUDES="-I$mysql_zlib_dir/include" ZLIB_LIBS="-L$mysql_zlib_dir/lib -lz" diff --git a/configure.in b/configure.in index 4f356971a49..0f0649fd3bc 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.1.11-beta) +AM_INIT_AUTOMAKE(mysql, 5.1.12-beta) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -36,10 +36,9 @@ sinclude(config/ac-macros/ha_berkeley.m4) sinclude(config/ac-macros/ha_ndbcluster.m4) sinclude(config/ac-macros/large_file.m4) sinclude(config/ac-macros/misc.m4) -sinclude(config/ac-macros/openssl.m4) sinclude(config/ac-macros/readline.m4) sinclude(config/ac-macros/replication.m4) -sinclude(config/ac-macros/yassl.m4) +sinclude(config/ac-macros/ssl.m4) sinclude(config/ac-macros/zlib.m4) # Remember to add a directory sql/share/LANGUAGE @@ -1088,7 +1087,7 @@ dnl Is this the right match for DEC OSF on alpha? # Edit Makefile.in files. # echo -n "configuring Makefile.in files for NetWare... " - for file in sql/Makefile.in libmysql/Makefile.in libmysql_r/Makefile.in sql/share/Makefile.in strings/Makefile.in client/Makefile.in + for file in sql/Makefile.in extra/Makefile.in client/Makefile.in do # echo "#### $file ####" filedir="`dirname $file`" @@ -1109,32 +1108,13 @@ dnl Is this the right match for DEC OSF on alpha? # Add library dependencies to mysqld_DEPENDENCIES lib_DEPENDENCIES="\$(pstack_libs) \$(openssl_libs) \$(yassl_libs)" cat > $filesed << EOF -s,\(^.*\$(MAKE) gen_lex_hash\)\$(EXEEXT),#\1, s,\(\./gen_lex_hash\)\$(EXEEXT),\1.linux, -s%\(mysqld_DEPENDENCIES = \) %\1$lib_DEPENDENCIES % +s%\(mysqld_DEPENDENCIES = \)%\1$lib_DEPENDENCIES % EOF ;; - sql/share/Makefile.in) + extra/Makefile.in) cat > $filesed << EOF -s,\(extra/comp_err\),\1.linux, -EOF - ;; - libmysql/Makefile.in) - cat > $filesed << EOF -s,\(\./conf_to_src\)\( \$(top_srcdir)\),\1.linux\2, -s,\(: conf_to_src\),\1.linux, -EOF - ;; - libmysql_r/Makefile.in) - cat > $filesed << EOF -s,\(\./conf_to_src\)\( \$(top_srcdir)\),\1.linux\2, -s,\(: conf_to_src\),\1.linux, -EOF - ;; - strings/Makefile.in) - cat > $filesed << EOF -s,\(\./conf_to_src\)\( \$(top_srcdir)\),\1.linux\2, -s,\(: conf_to_src\),\1.linux, +s,\(extra/comp_err\)\$(EXEEXT),\1.linux, EOF ;; client/Makefile.in) @@ -2203,8 +2183,7 @@ MYSQL_CHECK_BIG_TABLES MYSQL_CHECK_MAX_INDEXES MYSQL_CHECK_REPLICATION MYSQL_CHECK_VIO -MYSQL_CHECK_OPENSSL -MYSQL_CHECK_YASSL +MYSQL_CHECK_SSL #-------------------------------------------------------------------- # Declare our plugin modules diff --git a/dbug/dbug.c b/dbug/dbug.c index 52de4b4a92d..07f72a3e758 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -255,6 +255,8 @@ static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int); static void DBUGCloseFile(CODE_STATE *cs, FILE *fp); /* Push current debug settings */ static void PushState(CODE_STATE *cs); + /* Free memory associated with debug state. */ +static void FreeState (CODE_STATE *cs, struct settings *state); /* Test for tracing enabled */ static BOOLEAN DoTrace(CODE_STATE *cs); @@ -742,19 +744,7 @@ void _db_pop_() if (discard->next != NULL) { cs->stack= discard->next; - if (!is_shared(discard, keywords)) - FreeList(discard->keywords); - if (!is_shared(discard, functions)) - FreeList(discard->functions); - if (!is_shared(discard, processes)) - FreeList(discard->processes); - if (!is_shared(discard, p_functions)) - FreeList(discard->p_functions); - if (!is_shared(discard, out_file)) - DBUGCloseFile(cs, discard->out_file); - if (discard->prof_file) - DBUGCloseFile(cs, discard->prof_file); - free((char *) discard); + FreeState(cs, discard); } } @@ -1425,6 +1415,74 @@ static void PushState(CODE_STATE *cs) cs->stack= new_malloc; } +/* + * FUNCTION + * + * FreeState Free memory associated with a struct state. + * + * SYNOPSIS + * + * static void FreeState (state) + * struct state *state; + * + * DESCRIPTION + * + * Deallocates the memory allocated for various information in a + * state. + * + */ +static void FreeState ( +CODE_STATE *cs, +struct settings *state) +{ + if (!is_shared(state, keywords)) + FreeList(state->keywords); + if (!is_shared(state, functions)) + FreeList(state->functions); + if (!is_shared(state, processes)) + FreeList(state->processes); + if (!is_shared(state, p_functions)) + FreeList(state->p_functions); + if (!is_shared(state, out_file)) + DBUGCloseFile(cs, state->out_file); + if (state->prof_file) + DBUGCloseFile(cs, state->prof_file); + free((char *) state); +} + + +/* + * FUNCTION + * + * _db_end_ End debugging, freeing state stack memory. + * + * SYNOPSIS + * + * static VOID _db_end_ () + * + * DESCRIPTION + * + * Ends debugging, de-allocating the memory allocated to the + * state stack. + * + * To be called at the very end of the program. + * + */ +void _db_end_ () +{ + struct settings *discard; + CODE_STATE *cs=0; + + get_code_state_or_return; + + while((discard= cs->stack) != NULL) { + if(discard == &init_settings) + break; + cs->stack= discard->next; + FreeState (cs, discard); + } +} + /* * FUNCTION diff --git a/extra/yassl/examples/echoserver/echoserver.cpp b/extra/yassl/examples/echoserver/echoserver.cpp index 3243cc21a7c..8e23ead20ab 100644 --- a/extra/yassl/examples/echoserver/echoserver.cpp +++ b/extra/yassl/examples/echoserver/echoserver.cpp @@ -65,7 +65,8 @@ THREAD_RETURN YASSL_API echoserver_test(void* args) while (!shutdown) { sockaddr_in client; socklen_t client_len = sizeof(client); - int clientfd = accept(sockfd, (sockaddr*)&client, &client_len); + int clientfd = accept(sockfd, (sockaddr*)&client, + (ACCEPT_THIRD_T)&client_len); if (clientfd == -1) err_sys("tcp accept failed"); SSL* ssl = SSL_new(ctx); diff --git a/extra/yassl/include/openssl/ssl.h b/extra/yassl/include/openssl/ssl.h index a7eca9138a2..23e48d2011f 100644 --- a/extra/yassl/include/openssl/ssl.h +++ b/extra/yassl/include/openssl/ssl.h @@ -273,6 +273,7 @@ int SSL_pending(SSL*); enum { /* ssl Constants */ + SSL_WOULD_BLOCK = -8, SSL_BAD_STAT = -7, SSL_BAD_PATH = -6, SSL_BAD_FILETYPE = -5, @@ -494,7 +495,7 @@ ASN1_TIME* X509_get_notAfter(X509* x); typedef struct MD4_CTX { - void* ptr; + int buffer[32]; /* big enough to hold, check size in Init */ } MD4_CTX; void MD4_Init(MD4_CTX*); diff --git a/extra/yassl/include/socket_wrapper.hpp b/extra/yassl/include/socket_wrapper.hpp index d2258a93723..16db142b3a2 100644 --- a/extra/yassl/include/socket_wrapper.hpp +++ b/extra/yassl/include/socket_wrapper.hpp @@ -66,6 +66,7 @@ typedef unsigned char byte; // Wraps Windows Sockets and BSD Sockets class Socket { socket_t socket_; // underlying socket descriptor + bool wouldBlock_; // for non-blocking data public: explicit Socket(socket_t s = INVALID_SOCKET); ~Socket(); @@ -75,9 +76,10 @@ public: socket_t get_fd() const; uint send(const byte* buf, unsigned int len, int flags = 0) const; - uint receive(byte* buf, unsigned int len, int flags = 0) const; + uint receive(byte* buf, unsigned int len, int flags = 0); - bool wait() const; + bool wait(); + bool WouldBlock() const; void closeSocket(); void shutDown(int how = SD_SEND); diff --git a/extra/yassl/mySTL/stdexcept.hpp b/extra/yassl/mySTL/stdexcept.hpp index 33ea43bf0e0..b50dd35edae 100644 --- a/extra/yassl/mySTL/stdexcept.hpp +++ b/extra/yassl/mySTL/stdexcept.hpp @@ -46,8 +46,10 @@ public: // for compiler generated call, never used static void operator delete(void*) { assert(0); } private: +#if defined(__hpux) // don't allow dynamic creation of exceptions static void* operator new(size_t); +#endif }; diff --git a/extra/yassl/src/handshake.cpp b/extra/yassl/src/handshake.cpp index 2603365e41a..2b099af930c 100644 --- a/extra/yassl/src/handshake.cpp +++ b/extra/yassl/src/handshake.cpp @@ -656,7 +656,7 @@ mySTL::auto_ptr<input_buffer> DoProcessReply(SSL& ssl, mySTL::auto_ptr<input_buffer> buffered) { // wait for input if blocking - if (!ssl.getSocket().wait()) { + if (!ssl.useSocket().wait()) { ssl.SetError(receive_error); buffered.reset(0); return buffered; @@ -673,7 +673,7 @@ DoProcessReply(SSL& ssl, mySTL::auto_ptr<input_buffer> buffered) } // add new data - uint read = ssl.getSocket().receive(buffer.get_buffer() + buffSz, ready); + uint read = ssl.useSocket().receive(buffer.get_buffer() + buffSz, ready); buffer.add_size(read); uint offset = 0; const MessageFactory& mf = ssl.getFactory().getMessage(); @@ -858,6 +858,9 @@ void sendFinished(SSL& ssl, ConnectionEnd side, BufferOutput buffer) // send data int sendData(SSL& ssl, const void* buffer, int sz) { + if (ssl.GetError() == YasslError(SSL_ERROR_WANT_READ)) + ssl.SetError(no_error); + ssl.verfiyHandShakeComplete(); if (ssl.GetError()) return 0; int sent = 0; @@ -893,6 +896,9 @@ int sendAlert(SSL& ssl, const Alert& alert) // process input data int receiveData(SSL& ssl, Data& data) { + if (ssl.GetError() == YasslError(SSL_ERROR_WANT_READ)) + ssl.SetError(no_error); + ssl.verfiyHandShakeComplete(); if (ssl.GetError()) return 0; @@ -902,6 +908,11 @@ int receiveData(SSL& ssl, Data& data) ssl.useLog().ShowData(data.get_length()); if (ssl.GetError()) return 0; + + if (data.get_length() == 0 && ssl.getSocket().WouldBlock()) { + ssl.SetError(YasslError(SSL_ERROR_WANT_READ)); + return SSL_WOULD_BLOCK; + } return data.get_length(); } diff --git a/extra/yassl/src/socket_wrapper.cpp b/extra/yassl/src/socket_wrapper.cpp index c6611803421..803f4b01249 100644 --- a/extra/yassl/src/socket_wrapper.cpp +++ b/extra/yassl/src/socket_wrapper.cpp @@ -58,7 +58,7 @@ namespace yaSSL { Socket::Socket(socket_t s) - : socket_(s) + : socket_(s), wouldBlock_(false) {} @@ -123,17 +123,21 @@ uint Socket::send(const byte* buf, unsigned int sz, int flags) const } -uint Socket::receive(byte* buf, unsigned int sz, int flags) const +uint Socket::receive(byte* buf, unsigned int sz, int flags) { assert(socket_ != INVALID_SOCKET); + wouldBlock_ = false; + int recvd = ::recv(socket_, reinterpret_cast<char *>(buf), sz, flags); // idea to seperate error from would block by arnetheduck@gmail.com if (recvd == -1) { if (get_lastError() == SOCKET_EWOULDBLOCK || - get_lastError() == SOCKET_EAGAIN) + get_lastError() == SOCKET_EAGAIN) { + wouldBlock_ = true; return 0; } + } else if (recvd == 0) return static_cast<uint>(-1); @@ -142,7 +146,7 @@ uint Socket::receive(byte* buf, unsigned int sz, int flags) const // wait if blocking for input, return false for error -bool Socket::wait() const +bool Socket::wait() { byte b; return receive(&b, 1, MSG_PEEK) != static_cast<uint>(-1); @@ -166,6 +170,12 @@ int Socket::get_lastError() } +bool Socket::WouldBlock() const +{ + return wouldBlock_; +} + + void Socket::set_lastError(int errorCode) { #ifdef _WIN32 diff --git a/extra/yassl/src/ssl.cpp b/extra/yassl/src/ssl.cpp index 66196514a87..747305730df 100644 --- a/extra/yassl/src/ssl.cpp +++ b/extra/yassl/src/ssl.cpp @@ -37,6 +37,7 @@ #include "handshake.hpp" #include "yassl_int.hpp" #include "md5.hpp" // for TaoCrypt MD5 size assert +#include "md4.hpp" // for TaoCrypt MD4 size assert #include <stdio.h> #ifdef _WIN32 @@ -1131,17 +1132,26 @@ void* X509_get_ext_d2i(X509* x, int nid, int* crit, int* idx) void MD4_Init(MD4_CTX* md4) { - assert(0); // not yet supported, build compat. only + // make sure we have a big enough buffer + typedef char ok[sizeof(md4->buffer) >= sizeof(TaoCrypt::MD4) ? 1 : -1]; + (void) sizeof(ok); + + // using TaoCrypt since no dynamic memory allocated + // and no destructor will be called + new (reinterpret_cast<yassl_pointer>(md4->buffer)) TaoCrypt::MD4(); } void MD4_Update(MD4_CTX* md4, const void* data, unsigned long sz) { + reinterpret_cast<TaoCrypt::MD4*>(md4->buffer)->Update( + static_cast<const byte*>(data), static_cast<unsigned int>(sz)); } void MD4_Final(unsigned char* hash, MD4_CTX* md4) { + reinterpret_cast<TaoCrypt::MD4*>(md4->buffer)->Final(hash); } diff --git a/extra/yassl/src/template_instnt.cpp b/extra/yassl/src/template_instnt.cpp index 43b80d59a4d..134deb00c75 100644 --- a/extra/yassl/src/template_instnt.cpp +++ b/extra/yassl/src/template_instnt.cpp @@ -31,6 +31,7 @@ #include "hmac.hpp" #include "md5.hpp" #include "sha.hpp" +#include "ripemd.hpp" #include "openssl/ssl.h" #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION diff --git a/extra/yassl/src/timer.cpp b/extra/yassl/src/timer.cpp index 4fe0d3aa4f9..8b7d2d17a84 100644 --- a/extra/yassl/src/timer.cpp +++ b/extra/yassl/src/timer.cpp @@ -26,13 +26,17 @@ #include "runtime.hpp" #include "timer.hpp" +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#else +#include <sys/time.h> +#endif + namespace yaSSL { #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include <windows.h> - timer_d timer() { static bool init(false); @@ -57,8 +61,6 @@ namespace yaSSL { #else // _WIN32 - #include <sys/time.h> - timer_d timer() { struct timeval tv; diff --git a/extra/yassl/src/yassl_error.cpp b/extra/yassl/src/yassl_error.cpp index 59113d7438c..1973c54d781 100644 --- a/extra/yassl/src/yassl_error.cpp +++ b/extra/yassl/src/yassl_error.cpp @@ -26,6 +26,7 @@ #include "runtime.hpp" #include "yassl_error.hpp" #include "error.hpp" // TaoCrypt error numbers +#include "openssl/ssl.h" // SSL_ERROR_WANT_READ namespace yaSSL { @@ -117,6 +118,11 @@ void SetErrorString(YasslError error, char* buffer) strncpy(buffer, "unable to proccess cerificate", max); break; + // openssl errors + case SSL_ERROR_WANT_READ : + strncpy(buffer, "the read operation would block", max); + break; + // TaoCrypt errors case NO_ERROR : strncpy(buffer, "not in error state", max); diff --git a/extra/yassl/src/yassl_int.cpp b/extra/yassl/src/yassl_int.cpp index f7fb1abfa3f..a715d32f282 100644 --- a/extra/yassl/src/yassl_int.cpp +++ b/extra/yassl/src/yassl_int.cpp @@ -1415,15 +1415,6 @@ BulkCipher* CryptProvider::NewDesEde() } -extern "C" void yaSSL_CleanUp() -{ - TaoCrypt::CleanUp(); - ysDelete(cryptProviderInstance); - ysDelete(sslFactoryInstance); - ysDelete(sessionsInstance); -} - - typedef Mutex::Lock Lock; @@ -2109,9 +2100,18 @@ ASN1_STRING* StringHolder::GetString() } - } // namespace + +extern "C" void yaSSL_CleanUp() +{ + TaoCrypt::CleanUp(); + ysDelete(yaSSL::cryptProviderInstance); + ysDelete(yaSSL::sslFactoryInstance); + ysDelete(yaSSL::sessionsInstance); +} + + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION namespace mySTL { template yaSSL::yassl_int_cpp_local1::SumData for_each<mySTL::list<yaSSL::input_buffer*>::iterator, yaSSL::yassl_int_cpp_local1::SumData>(mySTL::list<yaSSL::input_buffer*>::iterator, mySTL::list<yaSSL::input_buffer*>::iterator, yaSSL::yassl_int_cpp_local1::SumData); diff --git a/extra/yassl/taocrypt/CMakeLists.txt b/extra/yassl/taocrypt/CMakeLists.txt index 3ad9195b372..0af0a242e5d 100644 --- a/extra/yassl/taocrypt/CMakeLists.txt +++ b/extra/yassl/taocrypt/CMakeLists.txt @@ -2,7 +2,7 @@ INCLUDE_DIRECTORIES(../mySTL include) ADD_LIBRARY(taocrypt 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/md5.cpp src/misc.cpp src/random.cpp src/ripemd.cpp src/rsa.cpp src/sha.cpp + src/md4.cpp src/md5.cpp src/misc.cpp src/random.cpp src/ripemd.cpp src/rsa.cpp src/sha.cpp include/aes.hpp include/algebra.hpp include/arc4.hpp include/asn.hpp include/block.hpp include/coding.hpp include/des.hpp include/dh.hpp include/dsa.hpp include/dsa.hpp include/error.hpp include/file.hpp include/hash.hpp include/hmac.hpp include/integer.hpp diff --git a/extra/yassl/taocrypt/include/block.hpp b/extra/yassl/taocrypt/include/block.hpp index 4c262e1a540..76836615ce6 100644 --- a/extra/yassl/taocrypt/include/block.hpp +++ b/extra/yassl/taocrypt/include/block.hpp @@ -96,7 +96,7 @@ public: pointer allocate(size_type n, const void* = 0) { - CheckSize(n); + this->CheckSize(n); if (n == 0) return 0; return NEW_TC T[n]; diff --git a/extra/yassl/taocrypt/include/md4.hpp b/extra/yassl/taocrypt/include/md4.hpp new file mode 100644 index 00000000000..aac930d7498 --- /dev/null +++ b/extra/yassl/taocrypt/include/md4.hpp @@ -0,0 +1,65 @@ +/* md4.hpp + * + * Copyright (C) 2003 Sawtooth Consulting Ltd. + * + * This file is part of yaSSL. + * + * yaSSL 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; either version 2 of the License, or + * (at your option) any later version. + * + * yaSSL 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 + */ + +/* md4.hpp provides MD4 digest support + * WANRING: MD4 is considered insecure, only use if you have to, e.g., yaSSL + * libcurl supports needs this for NTLM authentication +*/ + +#ifndef TAO_CRYPT_MD4_HPP +#define TAO_CRYPT_MD4_HPP + +#include "hash.hpp" + +namespace TaoCrypt { + + +// MD4 digest +class MD4 : public HASHwithTransform { +public: + enum { BLOCK_SIZE = 64, DIGEST_SIZE = 16, PAD_SIZE = 56, + TAO_BYTE_ORDER = LittleEndianOrder }; // in Bytes + MD4() : HASHwithTransform(DIGEST_SIZE / sizeof(word32), BLOCK_SIZE) + { Init(); } + ByteOrder getByteOrder() const { return ByteOrder(TAO_BYTE_ORDER); } + word32 getBlockSize() const { return BLOCK_SIZE; } + word32 getDigestSize() const { return DIGEST_SIZE; } + word32 getPadSize() const { return PAD_SIZE; } + + MD4(const MD4&); + MD4& operator= (const MD4&); + + void Init(); + void Swap(MD4&); +private: + void Transform(); +}; + +inline void swap(MD4& a, MD4& b) +{ + a.Swap(b); +} + + +} // namespace + +#endif // TAO_CRYPT_MD4_HPP + diff --git a/extra/yassl/taocrypt/src/Makefile.am b/extra/yassl/taocrypt/src/Makefile.am index d3e72346110..1110ed335b8 100644 --- a/extra/yassl/taocrypt/src/Makefile.am +++ b/extra/yassl/taocrypt/src/Makefile.am @@ -4,7 +4,7 @@ noinst_LTLIBRARIES = libtaocrypt.la libtaocrypt_la_SOURCES = aes.cpp aestables.cpp algebra.cpp arc4.cpp \ asn.cpp bftables.cpp blowfish.cpp coding.cpp des.cpp dh.cpp \ - dsa.cpp file.cpp hash.cpp integer.cpp md2.cpp md5.cpp misc.cpp \ + dsa.cpp file.cpp hash.cpp integer.cpp md2.cpp md4.cpp md5.cpp misc.cpp \ random.cpp ripemd.cpp rsa.cpp sha.cpp template_instnt.cpp \ tftables.cpp twofish.cpp diff --git a/extra/yassl/taocrypt/src/md4.cpp b/extra/yassl/taocrypt/src/md4.cpp new file mode 100644 index 00000000000..dfc2b079141 --- /dev/null +++ b/extra/yassl/taocrypt/src/md4.cpp @@ -0,0 +1,154 @@ +/* md4.cpp + * + * Copyright (C) 2003 Sawtooth Consulting Ltd. + * + * This file is part of yaSSL. + * + * yaSSL 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; either version 2 of the License, or + * (at your option) any later version. + * + * yaSSL 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 + */ + + +/* based on Wei Dai's md4.cpp from CryptoPP */ + +#include "runtime.hpp" +#include "md4.hpp" +#include "algorithm.hpp" // mySTL::swap + + + +namespace TaoCrypt { + +void MD4::Init() +{ + digest_[0] = 0x67452301L; + digest_[1] = 0xefcdab89L; + digest_[2] = 0x98badcfeL; + digest_[3] = 0x10325476L; + + buffLen_ = 0; + loLen_ = 0; + hiLen_ = 0; +} + + +MD4::MD4(const MD4& that) : HASHwithTransform(DIGEST_SIZE / sizeof(word32), + BLOCK_SIZE) +{ + buffLen_ = that.buffLen_; + loLen_ = that.loLen_; + hiLen_ = that.hiLen_; + + memcpy(digest_, that.digest_, DIGEST_SIZE); + memcpy(buffer_, that.buffer_, BLOCK_SIZE); +} + +MD4& MD4::operator= (const MD4& that) +{ + MD4 tmp(that); + Swap(tmp); + + return *this; +} + + +void MD4::Swap(MD4& other) +{ + mySTL::swap(loLen_, other.loLen_); + mySTL::swap(hiLen_, other.hiLen_); + mySTL::swap(buffLen_, other.buffLen_); + + memcpy(digest_, other.digest_, DIGEST_SIZE); + memcpy(buffer_, other.buffer_, BLOCK_SIZE); +} + + +void MD4::Transform() +{ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + + word32 A, B, C, D; + + A = digest_[0]; + B = digest_[1]; + C = digest_[2]; + D = digest_[3]; + +#define function(a,b,c,d,k,s) a=rotlFixed(a+F(b,c,d)+buffer_[k],s); + function(A,B,C,D, 0, 3); + function(D,A,B,C, 1, 7); + function(C,D,A,B, 2,11); + function(B,C,D,A, 3,19); + function(A,B,C,D, 4, 3); + function(D,A,B,C, 5, 7); + function(C,D,A,B, 6,11); + function(B,C,D,A, 7,19); + function(A,B,C,D, 8, 3); + function(D,A,B,C, 9, 7); + function(C,D,A,B,10,11); + function(B,C,D,A,11,19); + function(A,B,C,D,12, 3); + function(D,A,B,C,13, 7); + function(C,D,A,B,14,11); + function(B,C,D,A,15,19); + +#undef function +#define function(a,b,c,d,k,s) a=rotlFixed(a+G(b,c,d)+buffer_[k]+0x5a827999,s); + function(A,B,C,D, 0, 3); + function(D,A,B,C, 4, 5); + function(C,D,A,B, 8, 9); + function(B,C,D,A,12,13); + function(A,B,C,D, 1, 3); + function(D,A,B,C, 5, 5); + function(C,D,A,B, 9, 9); + function(B,C,D,A,13,13); + function(A,B,C,D, 2, 3); + function(D,A,B,C, 6, 5); + function(C,D,A,B,10, 9); + function(B,C,D,A,14,13); + function(A,B,C,D, 3, 3); + function(D,A,B,C, 7, 5); + function(C,D,A,B,11, 9); + function(B,C,D,A,15,13); + +#undef function +#define function(a,b,c,d,k,s) a=rotlFixed(a+H(b,c,d)+buffer_[k]+0x6ed9eba1,s); + function(A,B,C,D, 0, 3); + function(D,A,B,C, 8, 9); + function(C,D,A,B, 4,11); + function(B,C,D,A,12,15); + function(A,B,C,D, 2, 3); + function(D,A,B,C,10, 9); + function(C,D,A,B, 6,11); + function(B,C,D,A,14,15); + function(A,B,C,D, 1, 3); + function(D,A,B,C, 9, 9); + function(C,D,A,B, 5,11); + function(B,C,D,A,13,15); + function(A,B,C,D, 3, 3); + function(D,A,B,C,11, 9); + function(C,D,A,B, 7,11); + function(B,C,D,A,15,15); + + digest_[0] += A; + digest_[1] += B; + digest_[2] += C; + digest_[3] += D; +} + + +} // namespace + diff --git a/extra/yassl/taocrypt/src/template_instnt.cpp b/extra/yassl/taocrypt/src/template_instnt.cpp index 5efd2d32a10..12bcd8238f2 100644 --- a/extra/yassl/taocrypt/src/template_instnt.cpp +++ b/extra/yassl/taocrypt/src/template_instnt.cpp @@ -30,11 +30,11 @@ #include "sha.hpp" #include "md5.hpp" #include "hmac.hpp" +#include "ripemd.hpp" #include "pwdbased.hpp" #include "algebra.hpp" #include "vector.hpp" #include "hash.hpp" -#include "ripemd.hpp" #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION namespace TaoCrypt { diff --git a/extra/yassl/taocrypt/taocrypt.dsp b/extra/yassl/taocrypt/taocrypt.dsp index b741cef0096..19edf7b2f22 100644 --- a/extra/yassl/taocrypt/taocrypt.dsp +++ b/extra/yassl/taocrypt/taocrypt.dsp @@ -146,6 +146,10 @@ SOURCE=.\src\md2.cpp # End Source File # Begin Source File +SOURCE=.\src\md4.cpp +# End Source File +# Begin Source File + SOURCE=.\src\md5.cpp # End Source File # Begin Source File @@ -246,6 +250,10 @@ SOURCE=.\include\md2.hpp # End Source File # Begin Source File +SOURCE=.\include\md4.hpp +# End Source File +# Begin Source File + SOURCE=.\include\md5.hpp # End Source File # Begin Source File diff --git a/extra/yassl/taocrypt/test/test.cpp b/extra/yassl/taocrypt/test/test.cpp index b8618b18d47..28ef73dfac8 100644 --- a/extra/yassl/taocrypt/test/test.cpp +++ b/extra/yassl/taocrypt/test/test.cpp @@ -8,6 +8,7 @@ #include "sha.hpp" #include "md5.hpp" #include "md2.hpp" +#include "md4.hpp" #include "ripemd.hpp" #include "hmac.hpp" #include "arc4.hpp" @@ -30,6 +31,7 @@ using TaoCrypt::word32; using TaoCrypt::SHA; using TaoCrypt::MD5; using TaoCrypt::MD2; +using TaoCrypt::MD4; using TaoCrypt::RIPEMD160; using TaoCrypt::HMAC; using TaoCrypt::ARC4; @@ -89,6 +91,7 @@ void file_test(int, char**); int sha_test(); int md5_test(); int md2_test(); +int md4_test(); int ripemd_test(); int hmac_test(); int arc4_test(); @@ -165,6 +168,11 @@ void taocrypt_test(void* args) else printf( "MD2 test passed!\n"); + if ( (ret = md4_test()) ) + err_sys("MD4 test failed!\n", ret); + else + printf( "MD4 test passed!\n"); + if ( (ret = ripemd_test()) ) err_sys("RIPEMD test failed!\n", ret); else @@ -348,6 +356,51 @@ int md5_test() } +int md4_test() +{ + MD4 md4; + byte hash[MD4::DIGEST_SIZE]; + + testVector test_md4[] = + { + testVector("", + "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31\xb7\x3c\x59\xd7\xe0\xc0\x89" + "\xc0"), + testVector("a", + "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46\x24\x5e\x05\xfb\xdb\xd6\xfb" + "\x24"), + testVector("abc", + "\xa4\x48\x01\x7a\xaf\x21\xd8\x52\x5f\xc1\x0a\xe8\x7a\xa6\x72" + "\x9d"), + testVector("message digest", + "\xd9\x13\x0a\x81\x64\x54\x9f\xe8\x18\x87\x48\x06\xe1\xc7\x01" + "\x4b"), + testVector("abcdefghijklmnopqrstuvwxyz", + "\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd\xee\xa8\xed\x63\xdf\x41\x2d" + "\xa9"), + testVector("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345" + "6789", + "\x04\x3f\x85\x82\xf2\x41\xdb\x35\x1c\xe6\x27\xe1\x53\xe7\xf0" + "\xe4"), + testVector("1234567890123456789012345678901234567890123456789012345678" + "9012345678901234567890", + "\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19\x9c\x3e\x7b\x16\x4f\xcc\x05" + "\x36") + }; + + int times( sizeof(test_md4) / sizeof(testVector) ); + for (int i = 0; i < times; ++i) { + md4.Update(test_md4[i].input_, test_md4[i].inLen_); + md4.Final(hash); + + if (memcmp(hash, test_md4[i].output_, MD4::DIGEST_SIZE) != 0) + return -5 - i; + } + + return 0; +} + + int md2_test() { MD2 md5; diff --git a/extra/yassl/testsuite/test.hpp b/extra/yassl/testsuite/test.hpp index 259975fba0b..c80e3ad23da 100644 --- a/extra/yassl/testsuite/test.hpp +++ b/extra/yassl/testsuite/test.hpp @@ -33,10 +33,16 @@ // HPUX doesn't use socklent_t for third parameter to accept -#if !defined(__hpux__) +#if !defined(__hpux) typedef socklen_t* ACCEPT_THIRD_T; #else typedef int* ACCEPT_THIRD_T; + +// HPUX does not define _POSIX_THREADS as it's not _fully_ implemented +#ifndef _POSIX_THREADS +#define _POSIX_THREADS +#endif + #endif diff --git a/include/config-win.h b/include/config-win.h index 6d0f40adbd3..aa372762191 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -25,6 +25,7 @@ functions */ #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Avoid endless warnings about sprintf() etc. being unsafe. */ #define _CRT_SECURE_NO_DEPRECATE 1 +#define _USE_32BIT_TIME_T 1 /* force time_t to be 32 bit */ #endif #include <sys/locking.h> @@ -95,7 +96,7 @@ functions */ #define O_SHORT_LIVED 0 #define SH_DENYNO _SH_DENYNO #else -#define O_BINARY _O_BINARY /* compability with MSDOS */ +#define O_BINARY _O_BINARY /* compability with older style names */ #define FILE_BINARY _O_BINARY /* my_fopen in binary mode */ #define O_TEMPORARY _O_TEMPORARY #define O_SHORT_LIVED _O_SHORT_LIVED diff --git a/include/heap.h b/include/heap.h index 855cff117e2..4c003462a45 100644 --- a/include/heap.h +++ b/include/heap.h @@ -111,7 +111,7 @@ struct st_heap_info; /* For referense */ typedef struct st_hp_keydef /* Key definition with open */ { - uint flag; /* HA_NOSAME | HA_NULL_PART_KEY */ + uint flag; /* HA_NOSAME | HA_NULL_PART_KEY */ uint keysegs; /* Number of key-segment */ uint length; /* Length of key (automatic) */ uint8 algorithm; /* HASH / BTREE */ diff --git a/include/m_string.h b/include/m_string.h index e73f5c11487..9f7ec220f2c 100644 --- a/include/m_string.h +++ b/include/m_string.h @@ -106,12 +106,6 @@ extern char NEAR _dig_vec_lower[]; #define memcpy_fixed(A,B,C) memcpy((A),(B),(C)) #endif -#ifdef MSDOS -#undef bmove_align -#define bmove512(A,B,C) bmove_align(A,B,C) -extern void bmove_align(gptr dst,const gptr src,uint len); -#endif - #if (!defined(USE_BMOVE512) || defined(HAVE_purify)) && !defined(bmove512) #define bmove512(A,B,C) memcpy(A,B,C) #endif @@ -246,4 +240,22 @@ extern int my_snprintf(char* to, size_t n, const char* fmt, ...); #if defined(__cplusplus) } #endif + +/* + LEX_STRING -- a pair of a C-string and its length. + + NOTE: this exactly form of declaration is required for some C-compilers + (for one, Sun C 5.7 2005/01/07). Unfortunatelt with such declaration + LEX_STRING can not be forward declared. +*/ + +typedef struct +{ + char *str; + uint length; +} LEX_STRING; + +#define STRING_WITH_LEN(X) (X), ((uint) (sizeof(X) - 1)) +#define C_STRING_WITH_SIZE(X) ((char *) (X)), ((uint) (sizeof(X) - 1)) + #endif diff --git a/include/my_base.h b/include/my_base.h index 0390106880a..e77bffeaf94 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -33,9 +33,6 @@ #define EOVERFLOW 84 #endif -#ifdef MSDOS -#include <share.h> /* Neaded for sopen() */ -#endif #if !defined(USE_MY_FUNC) && !defined(THREAD) #include <my_nosys.h> /* For faster code, after test */ #endif /* USE_MY_FUNC */ diff --git a/include/my_dbug.h b/include/my_dbug.h index 65bb7b55d0d..a397861c1af 100644 --- a/include/my_dbug.h +++ b/include/my_dbug.h @@ -42,6 +42,7 @@ extern void _db_pargs_(uint _line_,const char *keyword); extern void _db_doprnt_ _VARARGS((const char *format,...)); extern void _db_dump_(uint _line_,const char *keyword,const char *memory, uint length); +extern void _db_end_(void); extern void _db_lock_file_(void); extern void _db_unlock_file_(void); extern FILE *_db_fp_(void); @@ -73,6 +74,7 @@ extern FILE *_db_fp_(void); #define DBUG_SETJMP(a1) (_db_setjmp_ (), setjmp (a1)) #define DBUG_LONGJMP(a1,a2) (_db_longjmp_ (), longjmp (a1, a2)) #define DBUG_DUMP(keyword,a1,a2) _db_dump_(__LINE__,keyword,a1,a2) +#define DBUG_END() _db_end_ () #define DBUG_LOCK_FILE _db_lock_file_() #define DBUG_UNLOCK_FILE _db_unlock_file_() #define DBUG_ASSERT(A) assert(A) @@ -97,6 +99,7 @@ extern FILE *_db_fp_(void); #define DBUG_SETJMP(a1) setjmp(a1) #define DBUG_LONGJMP(a1) longjmp(a1) #define DBUG_DUMP(keyword,a1,a2) +#define DBUG_END() #define DBUG_ASSERT(A) #define DBUG_LOCK_FILE #define DBUG_FILE (stderr) diff --git a/include/my_global.h b/include/my_global.h index 0fb11738758..100e695a0de 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -78,6 +78,15 @@ #endif #endif /* _WIN32... */ +#ifndef EMBEDDED_LIBRARY +#ifdef WITH_ROW_BASED_REPLICATION +#define HAVE_ROW_BASED_REPLICATION 1 +#endif +#ifdef WITH_NDB_BINLOG +#define HAVE_NDB_BINLOG 1 +#endif +#endif /* !EMBEDDED_LIBRARY */ + /* Some defines to avoid ifdefs in the code */ #ifndef NETWARE_YIELD #define NETWARE_YIELD diff --git a/include/my_net.h b/include/my_net.h index f953a832e6b..b26e525016b 100644 --- a/include/my_net.h +++ b/include/my_net.h @@ -44,7 +44,7 @@ C_MODE_START #include <sys/ioctl.h> #endif -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__NETWARE__) +#if !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__NETWARE__) #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> @@ -53,7 +53,7 @@ C_MODE_START #endif #endif -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #define O_NONBLOCK 1 /* For emulation of fcntl() */ #endif diff --git a/include/my_nosys.h b/include/my_nosys.h index 605906f0e07..41b919c1a72 100644 --- a/include/my_nosys.h +++ b/include/my_nosys.h @@ -27,9 +27,6 @@ extern "C" { #ifndef __MY_NOSYS__ #define __MY_NOSYS__ -#ifdef MSDOS -#include <io.h> /* Get prototypes for read()... */ -#endif #ifndef HAVE_STDLIB_H #include <malloc.h> #endif diff --git a/include/my_pthread.h b/include/my_pthread.h index acd1d2b558b..0cb38d29be8 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -226,12 +226,14 @@ int sigwait(sigset_t *setp, int *sigp); /* Use our implemention */ we want to make sure that no such flags are set. */ #if defined(HAVE_SIGACTION) && !defined(my_sigset) -#define my_sigset(A,B) do { struct sigaction s; sigset_t set; \ +#define my_sigset(A,B) do { struct sigaction s; sigset_t set; int rc; \ + DBUG_ASSERT((A) != 0); \ sigemptyset(&set); \ s.sa_handler = (B); \ s.sa_mask = set; \ s.sa_flags = 0; \ - sigaction((A), &s, (struct sigaction *) NULL); \ + rc= sigaction((A), &s, (struct sigaction *) NULL);\ + DBUG_ASSERT(rc == 0); \ } while (0) #elif defined(HAVE_SIGSET) && !defined(my_sigset) #define my_sigset(A,B) sigset((A),(B)) diff --git a/include/my_sys.h b/include/my_sys.h index 1540d820777..5024505a821 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -75,6 +75,11 @@ extern int NEAR my_errno; /* Last error in mysys */ #define MY_CHECK_ERROR 1 /* Params to my_end; Check open-close */ #define MY_GIVE_INFO 2 /* Give time info about process*/ +#define MY_DONT_FREE_DBUG 4 /* Do not call DBUG_END() in my_end() */ + +#define MY_REMOVE_NONE 0 /* Params for modify_defaults_file */ +#define MY_REMOVE_OPTION 1 +#define MY_REMOVE_SECTION 2 #define ME_HIGHBYTE 8 /* Shift for colours */ #define ME_NOCUR 1 /* Don't use curses message */ @@ -196,22 +201,6 @@ extern void my_large_free(gptr ptr, myf my_flags); #define my_afree(PTR) my_free(PTR,MYF(MY_WME)) #endif /* HAVE_ALLOCA */ -#ifdef MSDOS -#ifdef __ZTC__ -void * __CDECL halloc(long count,size_t length); -void __CDECL hfree(void *ptr); -#endif -#if defined(USE_HALLOC) -#if defined(_VCM_) || defined(M_IC80386) -#undef USE_HALLOC -#endif -#endif -#ifdef USE_HALLOC -#define malloc(a) halloc((long) (a),1) -#define free(a) hfree(a) -#endif -#endif /* MSDOS */ - #ifndef errno /* did we already get it? */ #ifdef HAVE_ERRNO_AS_DEFINE #include <errno.h> /* errno is a define */ diff --git a/include/my_time.h b/include/my_time.h index 2b0dc4ac6ff..df500dc501b 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -38,7 +38,10 @@ typedef long my_time_t; #define MY_TIME_T_MAX LONG_MAX #define MY_TIME_T_MIN LONG_MIN +/* two-digit years < this are 20..; >= this are 19.. */ #define YY_PART_YEAR 70 +/* apply above magic to years < this */ +#define YY_MAGIC_BELOW 200 /* Flags to str_to_datetime */ #define TIME_FUZZY_DATE 1 diff --git a/include/my_tree.h b/include/my_tree.h index 03dc9d5c829..e9746ca1b2c 100644 --- a/include/my_tree.h +++ b/include/my_tree.h @@ -40,19 +40,11 @@ typedef int (*tree_walk_action)(void *,element_count,void *); typedef enum { free_init, free_free, free_end } TREE_FREE; typedef void (*tree_element_free)(void*, TREE_FREE, void *); -#ifdef MSDOS -typedef struct st_tree_element { - struct st_tree_element *left,*right; - unsigned long count; - uchar colour; /* black is marked as 1 */ -} TREE_ELEMENT; -#else typedef struct st_tree_element { struct st_tree_element *left,*right; uint32 count:31, colour:1; /* black is marked as 1 */ } TREE_ELEMENT; -#endif /* MSDOS */ #define ELEMENT_CHILD(element, offs) (*(TREE_ELEMENT**)((char*)element + offs)) diff --git a/include/mysql_com.h b/include/mysql_com.h index 2af0fb86906..bff5fcc47d2 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -423,17 +423,11 @@ char *octet2hex(char *to, const char *str, unsigned int len); /* end of password.c */ -char *get_tty_password(char *opt_message); +char *get_tty_password(const char *opt_message); const char *mysql_errno_to_sqlstate(unsigned int mysql_errno); /* Some other useful functions */ -my_bool my_init(void); -extern int modify_defaults_file(const char *file_location, const char *option, - const char *option_value, - const char *section_name, int remove_option); -int load_defaults(const char *conf_file, const char **groups, - int *argc, char ***argv); my_bool my_thread_init(void); void my_thread_end(void); diff --git a/include/sslopt-longopts.h b/include/sslopt-longopts.h index f444a7eb7ce..0435ddb815a 100644 --- a/include/sslopt-longopts.h +++ b/include/sslopt-longopts.h @@ -20,12 +20,6 @@ "Enable SSL for connection (automatically enabled with other flags). Disable with --skip-ssl.", (gptr*) &opt_use_ssl, (gptr*) &opt_use_ssl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"ssl-key", OPT_SSL_KEY, "X509 key in PEM format (implies --ssl).", - (gptr*) &opt_ssl_key, (gptr*) &opt_ssl_key, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, - {"ssl-cert", OPT_SSL_CERT, "X509 cert in PEM format (implies --ssl).", - (gptr*) &opt_ssl_cert, (gptr*) &opt_ssl_cert, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, {"ssl-ca", OPT_SSL_CA, "CA file in PEM format (check OpenSSL docs, implies --ssl).", (gptr*) &opt_ssl_ca, (gptr*) &opt_ssl_ca, 0, GET_STR, REQUIRED_ARG, @@ -34,12 +28,18 @@ "CA directory (check OpenSSL docs, implies --ssl).", (gptr*) &opt_ssl_capath, (gptr*) &opt_ssl_capath, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"ssl-cert", OPT_SSL_CERT, "X509 cert in PEM format (implies --ssl).", + (gptr*) &opt_ssl_cert, (gptr*) &opt_ssl_cert, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, {"ssl-cipher", OPT_SSL_CIPHER, "SSL cipher to use (implies --ssl).", (gptr*) &opt_ssl_cipher, (gptr*) &opt_ssl_cipher, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"ssl-key", OPT_SSL_KEY, "X509 key in PEM format (implies --ssl).", + (gptr*) &opt_ssl_key, (gptr*) &opt_ssl_key, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, #ifdef MYSQL_CLIENT {"ssl-verify-server-cert", OPT_SSL_VERIFY_SERVER_CERT, - "Verify servers \"Common Name\" in it's cert against hostname used when connecting. This option is disabled by default.", + "Verify server's \"Common Name\" in its cert against hostname used when connecting. This option is disabled by default.", (gptr*) &opt_ssl_verify_server_cert, (gptr*) &opt_ssl_verify_server_cert, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif diff --git a/include/sslopt-vars.h b/include/sslopt-vars.h index 8e5f3434396..7204145fc28 100644 --- a/include/sslopt-vars.h +++ b/include/sslopt-vars.h @@ -15,13 +15,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_OPENSSL -static my_bool opt_use_ssl = 0; -static char *opt_ssl_key = 0; -static char *opt_ssl_cert = 0; -static char *opt_ssl_ca = 0; -static char *opt_ssl_capath = 0; -static char *opt_ssl_cipher = 0; +#ifdef SSL_VARS_NOT_STATIC +#define SSL_STATIC +#else +#define SSL_STATIC static +#endif +SSL_STATIC my_bool opt_use_ssl = 0; +SSL_STATIC char *opt_ssl_ca = 0; +SSL_STATIC char *opt_ssl_capath = 0; +SSL_STATIC char *opt_ssl_cert = 0; +SSL_STATIC char *opt_ssl_cipher = 0; +SSL_STATIC char *opt_ssl_key = 0; #ifdef MYSQL_CLIENT -static my_bool opt_ssl_verify_server_cert= 0; +SSL_STATIC my_bool opt_ssl_verify_server_cert= 0; #endif #endif diff --git a/libmysql/get_password.c b/libmysql/get_password.c index a48cb6d7a6e..4c251677a66 100644 --- a/libmysql/get_password.c +++ b/libmysql/get_password.c @@ -75,7 +75,7 @@ #define _cputs(A) putstring(A) #endif -char *get_tty_password(char *opt_message) +char *get_tty_password(const char *opt_message) { char to[80]; char *pos=to,*end=to+sizeof(to)-1; @@ -159,7 +159,7 @@ static void get_password(char *to,uint length,int fd,bool echo) #endif /* ! HAVE_GETPASS */ -char *get_tty_password(char *opt_message) +char *get_tty_password(const char *opt_message) { #ifdef HAVE_GETPASS char *passbuff; diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index d117688a140..225b3926aa7 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -178,7 +178,7 @@ void STDCALL mysql_server_end() /* If library called my_init(), free memory allocated by it */ if (!org_my_init_done) { - my_end(0); + my_end(MY_DONT_FREE_DBUG); /* Remove TRACING, if enabled by mysql_debug() */ DBUG_POP(); } diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 8bd0e0baa32..ff8ceda5c5b 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -33,7 +33,7 @@ ADD_LIBRARY(mysqldemb emb_qcache.cc libmysqld.c lib_sql.cc ../sql/field_conv.cc ../sql/field.cc ../sql/filesort.cc ../sql/gstream.cc ../sql/ha_heap.cc ../sql/ha_myisam.cc ../sql/ha_myisammrg.cc ${mysql_se_ha_src} - ../sql/handler.cc ../sql/handlerton-win.cc ../sql/hash_filo.cc + ../sql/handler.cc ../sql/hash_filo.cc ../sql/hostname.cc ../sql/init.cc ../sql/item_buff.cc ../sql/item_cmpfunc.cc ../sql/item.cc ../sql/item_create.cc ../sql/item_func.cc ../sql/item_geofunc.cc ../sql/item_row.cc diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 09176097be0..a3af2d43bd5 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -68,7 +68,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.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 \ - event_executor.cc event.cc event_timed.cc \ + event_scheduler.cc event.cc event_timed.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ sql_tablespace.cc \ rpl_injector.cc my_user.c partition_info.cc @@ -88,11 +88,11 @@ INC_LIB= $(top_builddir)/regex/libregex.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/vio/libvio.a \ @mysql_plugin_libs@ \ - $(yassl_las) + $(yassl_inc_libs) if HAVE_YASSL -yassl_las = $(top_srcdir)/extra/yassl/src/libyassl.la \ - $(top_srcdir)/extra/yassl/taocrypt/src/libtaocrypt.la +yassl_inc_libs= $(top_srcdir)/extra/yassl/src/.libs/libyassl.a \ + $(top_srcdir)/extra/yassl/taocrypt/src/.libs/libtaocrypt.a endif # Storage engine specific compilation options @@ -135,12 +135,12 @@ else (for arc in ./libmysqld_int.a $(INC_LIB); do \ arpath=`echo $$arc|sed 's|[^/]*$$||'|sed 's|\.libs/$$||'`; \ artmp=`echo $$arc|sed 's|^.*/|tmp/lib-|'`; \ - for F in `$(AR) t $$arc`; do \ + for F in `$(AR) t $$arc | grep -v SYMDEF`; do \ if test -e "$$arpath/$$F" ; then echo "$$arpath/$$F"; else \ mkdir $$artmp; cd $$artmp > /dev/null; \ $(AR) x ../../$$arc; \ cd $$current_dir > /dev/null; \ - ls $$artmp/*; \ + ls $$artmp/* | grep -v SYMDEF; \ continue 2; fi; done; \ done; echo $(libmysqld_a_DEPENDENCIES) ) | sort -u | xargs $(AR) cq libmysqld.a ; \ $(RANLIB) libmysqld.a ; \ @@ -167,10 +167,13 @@ link_sources: rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/libmysql/$$f $$f; \ done; \ - for f in $(sqlstoragesources); do \ - rm -f $$f; \ - @LN_CP_F@ `find $(srcdir)/../sql -name $$f` $$f; \ - done; \ + if test -n "$(sqlstoragesources)" ; \ + then \ + for f in "$(sqlstoragesources)"; do \ + rm -f "$$f"; \ + @LN_CP_F@ `find $(srcdir)/../sql -name "$$f"` "$$f"; \ + done; \ + fi; \ rm -f client_settings.h; \ @LN_CP_F@ $(top_srcdir)/libmysql/client_settings.h client_settings.h diff --git a/mysql-test/extra/binlog_tests/blackhole.test b/mysql-test/extra/binlog_tests/blackhole.test index 71aec90e9a0..97243015aba 100644 --- a/mysql-test/extra/binlog_tests/blackhole.test +++ b/mysql-test/extra/binlog_tests/blackhole.test @@ -147,3 +147,15 @@ set autocommit=1; --replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ show binlog events; + +# +# BUG#10952 - alter table ... lost data without errors and warnings +# +drop table if exists t1; +create table t1 (c char(20)) engine=MyISAM; +insert into t1 values ("Monty"),("WAX"),("Walrus"); +--error 1031 +alter table t1 engine=blackhole; +drop table t1; + +# End of 5.0 tests diff --git a/mysql-test/extra/rpl_tests/rpl_log.test b/mysql-test/extra/rpl_tests/rpl_log.test index c9d9a4214d2..20ee7cd0d8f 100644 --- a/mysql-test/extra/rpl_tests/rpl_log.test +++ b/mysql-test/extra/rpl_tests/rpl_log.test @@ -77,12 +77,8 @@ connection slave; # to go into the relay log (the master always sends a fake one when replication # starts). start slave; ---disable_query_log ---disable_result_log -let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000007%Yes%Yes%0%0%None%'; +let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000005%Yes%Yes%0%0%None%'; --source include/wait_slave_status.inc ---enable_query_log ---enable_result_log sync_with_master; flush logs; stop slave; diff --git a/mysql-test/lib/mtr_misc.pl b/mysql-test/lib/mtr_misc.pl index b5a2e5a4a68..bc44c440c32 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -108,7 +108,14 @@ sub mtr_exe_exists (@) { map {$_.= ".exe"} @path if $::glob_win32; foreach my $path ( @path ) { - return $path if -x $path; + if($::glob_win32) + { + return $path if -f $path; + } + else + { + return $path if -x $path; + } } if ( @path == 1 ) { diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index a3fb7ec0183..d6fb9382b90 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -20,6 +20,7 @@ sub mtr_record_dead_children (); sub mtr_exit ($); sub sleep_until_file_created ($$$); sub mtr_kill_processes ($); +sub mtr_kill_process ($$$$); # static in C sub spawn_impl ($$$$$$$$); @@ -875,6 +876,25 @@ sub mtr_kill_processes ($) { } } + +sub mtr_kill_process ($$$$) { + my $pid= shift; + my $signal= shift; + my $retries= shift; + my $timeout= shift; + + while (1) + { + kill($signal, $pid); + + last unless kill (0, $pid) and $retries--; + + mtr_debug("Sleep $timeout second waiting for processes to die"); + + sleep($timeout); + } +} + ############################################################################## # # When we exit, we kill off all children diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c14e6d4d534..8923c032dff 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -929,6 +929,7 @@ sub command_line_setup () { path_err => "$opt_vardir/log/im.err", path_log => "$opt_vardir/log/im.log", path_pid => "$opt_vardir/run/im.pid", + path_angel_pid => "$opt_vardir/run/im.angel.pid", path_sock => "$sockdir/im.sock", port => $im_port, start_timeout => $master->[0]->{'start_timeout'}, @@ -1219,8 +1220,12 @@ sub environment_setup () { $ENV{'NDBCLUSTER_PORT_SLAVE'}=$opt_ndbcluster_port_slave; $ENV{'NDB_STATUS_OK'}= "YES"; + $ENV{'IM_EXE'}= $exe_im; $ENV{'IM_PATH_PID'}= $instance_manager->{path_pid}; + $ENV{'IM_PATH_ANGEL_PID'}= $instance_manager->{path_angel_pid}; $ENV{'IM_PORT'}= $instance_manager->{port}; + $ENV{'IM_DEFAULTS_PATH'}= $instance_manager->{defaults_file}; + $ENV{'IM_PASSWORD_PATH'}= $instance_manager->{password_file}; $ENV{'IM_MYSQLD1_SOCK'}= $instance_manager->{instances}->[0]->{path_sock}; $ENV{'IM_MYSQLD1_PORT'}= $instance_manager->{instances}->[0]->{port}; @@ -1999,6 +2004,7 @@ sub im_create_defaults_file($) { [manager] pid-file = $instance_manager->{path_pid} +angel-pid-file = $instance_manager->{path_angel_pid} socket = $instance_manager->{path_sock} port = $instance_manager->{port} password-file = $instance_manager->{password_file} @@ -2023,7 +2029,7 @@ log-slow-queries = $instance->{path_datadir}/mysqld$server_id.slow.log language = $path_language character-sets-dir = $path_charsetsdir basedir = $path_my_basedir -server_id =$server_id +server_id = $server_id skip-stack-trace skip-innodb skip-bdb @@ -3061,6 +3067,18 @@ sub im_start($$) { sub im_stop($) { my $instance_manager = shift; + # Obtain mysqld-process pids before we start stopping IM (it can delete pid + # files). + + my @mysqld_pids = (); + my $instances = $instance_manager->{'instances'}; + + push(@mysqld_pids, mtr_get_pid_from_file($instances->[0]->{'path_pid'})) + if -r $instances->[0]->{'path_pid'}; + + push(@mysqld_pids, mtr_get_pid_from_file($instances->[1]->{'path_pid'})) + if -r $instances->[1]->{'path_pid'}; + # Re-read pid from the file, since during tests Instance Manager could have # been restarted, so its pid could have been changed. @@ -3068,34 +3086,79 @@ sub im_stop($) { mtr_get_pid_from_file($instance_manager->{'path_pid'}) if -f $instance_manager->{'path_pid'}; + if (-f $instance_manager->{'path_angel_pid'}) + { + $instance_manager->{'angel_pid'} = + mtr_get_pid_from_file($instance_manager->{'path_angel_pid'}) + } + else + { + $instance_manager->{'angel_pid'} = undef; + } + # Inspired from mtr_stop_mysqld_servers(). start_reap_all(); - # Create list of pids. We should stop Instance Manager and all started - # mysqld-instances. Some of them may be nonguarded, so IM will not stop them - # on shutdown. + # Try graceful shutdown. - my @pids = ( $instance_manager->{'pid'} ); - my $instances = $instance_manager->{'instances'}; + mtr_kill_process($instance_manager->{'pid'}, 'TERM', 10, 1); - if ( -r $instances->[0]->{'path_pid'} ) + # Check that all processes died. + + my $clean_shutdown= 0; + + while (1) { - push(@pids, mtr_get_pid_from_file($instances->[0]->{'path_pid'})); + last if kill (0, $instance_manager->{'pid'}); + + last if (defined $instance_manager->{'angel_pid'}) && + kill (0, $instance_manager->{'angel_pid'}); + + foreach my $pid (@mysqld_pids) + { + last if kill (0, $pid); + } + + $clean_shutdown= 1; + last; } - if ( -r $instances->[1]->{'path_pid'} ) + # Kill leftovers (the order is important). + + unless ($clean_shutdown) { - push(@pids, mtr_get_pid_from_file($instances->[1]->{'path_pid'})); - } + mtr_kill_process($instance_manager->{'angel_pid'}, 'KILL', 10, 1) + if defined $instance_manager->{'angel_pid'}; + + mtr_kill_process($instance_manager->{'pid'}, 'KILL', 10, 1); - # Kill processes. + # Shutdown managed mysqld-processes. Some of them may be nonguarded, so IM + # will not stop them on shutdown. So, we should firstly try to end them + # legally. + + mtr_kill_processes(\@mysqld_pids); + + # Complain in error log so that a warning will be shown. + + my $errlog= "$opt_vardir/log/mysql-test-run.pl.err"; + + open (ERRLOG, ">>$errlog") || + mtr_error("Can not open error log ($errlog)"); + + my $ts= localtime(); + print ERRLOG + "Warning: [$ts] Instance Manager did not shutdown gracefully.\n"; + + close ERRLOG; + } - mtr_kill_processes(\@pids); + # That's all. stop_reap_all(); $instance_manager->{'pid'} = undef; + $instance_manager->{'angel_pid'} = undef; } diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 1ad9cd6d1e9..f4332ea9888 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -647,3 +647,13 @@ SELECT LENGTH(s) FROM t1; LENGTH(s) 10 DROP TABLE t1; +CREATE TABLE t1 (v VARCHAR(3), b INT); +INSERT INTO t1 VALUES ('abc', 5); +SELECT * FROM t1; +v b +abc 5 +ALTER TABLE t1 MODIFY COLUMN v VARCHAR(4); +SELECT * FROM t1; +v b +abc 5 +DROP TABLE t1; diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 09813458069..19cb0c9768f 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -1,4 +1,4 @@ -drop table if exists t1,t2; +drop table if exists t1,t2,t3; CREATE TABLE t1 ( Period smallint(4) unsigned zerofill DEFAULT '0000' NOT NULL, Varor_period smallint(4) unsigned DEFAULT '0' NOT NULL diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index 525f64ea902..d9e9392f618 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -418,3 +418,9 @@ a val 2 1 3 1 drop table t1; +CREATE TABLE t1 (t1 INT(10) PRIMARY KEY, t2 INT(10)); +INSERT INTO t1 VALUES(0, 0); +INSERT INTO t1 VALUES(1, 1); +ALTER TABLE t1 CHANGE t1 t1 INT(10) auto_increment; +ERROR 23000: ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '1' for key 'PRIMARY' +DROP TABLE t1; diff --git a/mysql-test/r/binlog_row_blackhole.result b/mysql-test/r/binlog_row_blackhole.result index aa5c943de00..42bf7a10888 100644 --- a/mysql-test/r/binlog_row_blackhole.result +++ b/mysql-test/r/binlog_row_blackhole.result @@ -156,3 +156,9 @@ master-bin.000001 # Query 1 # use `test`; BEGIN master-bin.000001 # Table_map 1 # table_id: # (test.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `test`; COMMIT +drop table if exists t1; +create table t1 (c char(20)) engine=MyISAM; +insert into t1 values ("Monty"),("WAX"),("Walrus"); +alter table t1 engine=blackhole; +ERROR HY000: Table storage engine for 't1' doesn't have this option +drop table t1; diff --git a/mysql-test/r/binlog_stm_blackhole.result b/mysql-test/r/binlog_stm_blackhole.result index 90eabc6fa66..64569993a7b 100644 --- a/mysql-test/r/binlog_stm_blackhole.result +++ b/mysql-test/r/binlog_stm_blackhole.result @@ -149,3 +149,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) engine=blackho master-bin.000001 # Query 1 # use `test`; BEGIN master-bin.000001 # Query 1 # use `test`; insert into t1 values(1) master-bin.000001 # Query 1 # use `test`; COMMIT +drop table if exists t1; +create table t1 (c char(20)) engine=MyISAM; +insert into t1 values ("Monty"),("WAX"),("Walrus"); +alter table t1 engine=blackhole; +ERROR HY000: Table storage engine for 't1' doesn't have this option +drop table t1; diff --git a/mysql-test/r/case.result b/mysql-test/r/case.result index 9d2706f64fa..1d2041432fe 100644 --- a/mysql-test/r/case.result +++ b/mysql-test/r/case.result @@ -103,8 +103,8 @@ t1 CREATE TABLE `t1` ( `c2` varchar(1) CHARACTER SET latin1 COLLATE latin1_danish_ci NOT NULL DEFAULT '', `c3` varbinary(1) NOT NULL DEFAULT '', `c4` varbinary(1) NOT NULL DEFAULT '', - `c5` varbinary(3) NOT NULL DEFAULT '', - `c6` varbinary(3) NOT NULL DEFAULT '', + `c5` varbinary(4) NOT NULL DEFAULT '', + `c6` varbinary(4) NOT NULL DEFAULT '', `c7` decimal(2,1) NOT NULL DEFAULT '0.0', `c8` decimal(2,1) NOT NULL DEFAULT '0.0', `c9` decimal(2,1) DEFAULT NULL, @@ -152,11 +152,11 @@ SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `COALESCE(1)` int(1) NOT NULL DEFAULT '0', - `COALESCE(1.0)` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `COALESCE(1.0)` decimal(2,1) NOT NULL DEFAULT '0.0', `COALESCE('a')` varchar(1) NOT NULL DEFAULT '', `COALESCE(1,1.0)` decimal(2,1) NOT NULL DEFAULT '0.0', `COALESCE(1,'1')` varbinary(1) NOT NULL DEFAULT '', - `COALESCE(1.1,'1')` varbinary(3) NOT NULL DEFAULT '', + `COALESCE(1.1,'1')` varbinary(4) NOT NULL DEFAULT '', `COALESCE('a' COLLATE latin1_bin,'b')` varchar(1) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1; diff --git a/mysql-test/r/contributors.result b/mysql-test/r/contributors.result new file mode 100644 index 00000000000..5739c2244c3 --- /dev/null +++ b/mysql-test/r/contributors.result @@ -0,0 +1,5 @@ +SHOW CONTRIBUTORS; +Name Location Comment +Ronald Bradford Brisbane, Australia EFF contribution for UC2006 Auction +Sheeri Kritzer Boston, Mass. USA EFF contribution for UC2006 Auction +Mark Shuttleworth London, UK. EFF contribution for UC2006 Auction diff --git a/mysql-test/r/ctype_sjis.result b/mysql-test/r/ctype_sjis.result index eaea2e7479a..1c9d540d574 100644 --- a/mysql-test/r/ctype_sjis.result +++ b/mysql-test/r/ctype_sjis.result @@ -172,6 +172,6 @@ c2h ab_def drop table t1; SET NAMES sjis; -SELECT HEX('²“‘@\Œ\') FROM DUAL; -HEX('²“‘@_Œ\') +SELECT HEX('²“‘@Œ\') FROM DUAL; +HEX('²“‘@Œ\') 8DB2939181408C5C diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index 01d206be7cb..d02a2af3c9f 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -17,13 +17,13 @@ db_x SHOW TABLES FROM db_x; Tables_in_db_x x_table -SET GLOBAL event_scheduler=0; +SET GLOBAL event_scheduler=2; DROP EVENT e_x1; DROP EVENT e_x2; DROP DATABASE db_x; DROP USER pauline@localhost; USE events_test; -SET GLOBAL event_scheduler=0; +SET GLOBAL event_scheduler=2; drop event if exists event1; Warnings: Note 1305 Event event1 does not exist @@ -100,7 +100,7 @@ a 800219 drop event non_qualif_ev; drop table non_qualif; -set global event_scheduler = 0; +set global event_scheduler = 2; create table t_event3 (a int, b float); drop event if exists event3; Warnings: @@ -324,18 +324,18 @@ events_test one_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED "This should show us only 3 events:"; -SHOW FULL EVENTS; +SHOW EVENTS; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status events_test one_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED "This should show us only 2 events:"; -SHOW FULL EVENTS LIKE 't%event'; +SHOW EVENTS LIKE 't%event'; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED "This should show us no events:"; -SHOW FULL EVENTS FROM test LIKE '%'; +SHOW EVENTS FROM test LIKE '%'; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status DROP DATABASE events_test2; "should see 1 event:"; @@ -343,11 +343,8 @@ SHOW EVENTS; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED "we should see 4 events now:"; -SHOW FULL EVENTS; +SHOW EVENTS; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test one_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT @@ -373,12 +370,12 @@ ERROR HY000: Incorrect AT value: 'definitely not a datetime' set names utf8; create event задачка on schedule every 123 minute starts now() ends now() + interval 1 month do select 1; drop event задачка; -set event_scheduler=0; +set event_scheduler=2; ERROR HY000: Variable 'event_scheduler' is a GLOBAL variable and should be set with SET GLOBAL -set global event_scheduler=2; -ERROR 42000: Variable 'event_scheduler' can't be set to the value of '2' +set global event_scheduler=3; +ERROR 42000: Variable 'event_scheduler' can't be set to the value of '3' "DISABLE the scheduler. Testing that it does not work when the variable is 0" -set global event_scheduler=0; +set global event_scheduler=2; select definer, name, db from mysql.event; definer name db select get_lock("test_lock1", 20); @@ -389,9 +386,10 @@ create event закачка on schedule every 10 hour do select get_lock("test_l select definer, name, db from mysql.event; definer name db root@localhost закачка events_test -"Should be 0 processes" +"Should be only 1 process" select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info +event_scheduler localhost NULL Connect Suspended NULL select release_lock("test_lock1"); release_lock("test_lock1") 1 @@ -409,11 +407,12 @@ get_lock("test_lock2", 20) create event закачка on schedule every 10 hour do select get_lock("test_lock2", 20); "Let some time pass to the event starts" "Should have only 2 processes: the scheduler and the locked event" -select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info event_scheduler localhost NULL Connect Sleeping NULL root localhost events_test Connect User lock select get_lock("test_lock2", 20) "Release the mutex, the event worker should finish." +"Release the mutex, the event worker should finish." select release_lock("test_lock2"); release_lock("test_lock2") 1 @@ -423,21 +422,17 @@ select get_lock("test_lock2_1", 20); get_lock("test_lock2_1", 20) 1 create event закачка21 on schedule every 10 hour do select get_lock("test_lock2_1", 20); -"Should see 1 process, locked on get_lock(" -"Shutting down the scheduler, it should wait for the running event" -set global event_scheduler=0; -"Should have only 2 processes: the scheduler and the locked event" -select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +"Should have only 3 processes: the scheduler, our conn and the locked event" +select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info event_scheduler localhost NULL Connect Sleeping NULL root localhost events_test Connect User lock select get_lock("test_lock2_1", 20) -"Release the lock so the child process should finish. Hence the scheduler also" -select release_lock("test_lock2_1"); -release_lock("test_lock2_1") -1 -"Should see 0 processes now:" -select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +set global event_scheduler=2; +"Should have only our process now:" +select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info +event_scheduler localhost NULL Connect Suspended NULL +root localhost events_test Connect User lock select get_lock("test_lock2_1", 20) drop event закачка21; create table t_16 (s1 int); create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5; @@ -457,6 +452,9 @@ select 2; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; event_schema event_name definer event_body events_test white_space root@localhost select 2 +select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; +event_schema event_name definer event_body +events_test white_space root@localhost select 2 drop event white_space; create event white_space on schedule every 10 hour disable do select 3; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index ef1ccfadecb..bc89c692f9a 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -35,7 +35,7 @@ create event e_55 on schedule every 10 hour starts 99990101000000 do drop table ERROR HY000: Incorrect STARTS value: '99990101000000' create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t; ERROR HY000: ENDS is either invalid or before STARTS -set global event_scheduler=0; +set global event_scheduler=2; "Wait a bit to settle down" delete from mysql.event; set global event_scheduler= 1; @@ -57,7 +57,7 @@ root localhost events_test Connect User lock select get_lock('test_bug16407', 60 select release_lock('test_bug16407'); release_lock('test_bug16407') 1 -set global event_scheduler= 0; +set global event_scheduler= 2; select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; event_schema event_name sql_mode events_test e_16407 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI @@ -115,7 +115,7 @@ release_lock('ee_16407_2') select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info event_scheduler localhost NULL Connect Sleeping NULL -set global event_scheduler= 0; +set global event_scheduler= 2; select * from events_smode_test order by ev_name, a; ev_name a ee_16407_3 1980-02-19 @@ -175,7 +175,7 @@ drop event ee_16407_5; drop event ee_16407_6; drop procedure ee_16407_5_pendant; drop procedure ee_16407_6_pendant; -set global event_scheduler= 0; +set global event_scheduler= 2; drop table events_smode_test; set sql_mode=@old_sql_mode; drop database events_test; diff --git a/mysql-test/r/events_logs_tests.result b/mysql-test/r/events_logs_tests.result index ab1666fefb9..ce58e0ec059 100644 --- a/mysql-test/r/events_logs_tests.result +++ b/mysql-test/r/events_logs_tests.result @@ -8,7 +8,7 @@ BEGIN SELECT user_host, argument FROM mysql.general_log WHERE argument LIKE '%alabala%'; END| "Check General Query Log" -SET GLOBAL event_scheduler=0; +SET GLOBAL event_scheduler=2; create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual; TRUNCATE mysql.general_log; "1 row, the current statement!" @@ -19,10 +19,10 @@ SET GLOBAL event_scheduler=1; "Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log" call select_general_log(); user_host argument -root[root] @ localhost [localhost] SELect 'alabala', sleep(3) from dual +USER_HOST SELect 'alabala', sleep(3) from dual DROP PROCEDURE select_general_log; DROP EVENT log_general; -SET GLOBAL event_scheduler=0; +SET GLOBAL event_scheduler=2; "Check slow query log" "Save the values" SET @old_global_long_query_time:=(select get_value()); @@ -36,14 +36,14 @@ SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; user_host query_time db sql_text "Set new values" SET GLOBAL long_query_time=4; -SET SESSION long_query_time=2; +SET SESSION long_query_time=1; "Check that logging is working" -SELECT SLEEP(3); -SLEEP(3) +SELECT SLEEP(2); +SLEEP(2) 0 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; user_host query_time db sql_text -root[root] @ localhost [] SLEEPVAL events_test SELECT SLEEP(3) +USER_HOST SLEEPVAL events_test SELECT SLEEP(2) TRUNCATE mysql.slow_log; CREATE TABLE slow_event_test (slo_val tinyint, val tinyint); "This won't go to the slow log" @@ -54,7 +54,7 @@ SET GLOBAL event_scheduler=1; "Sleep some more time than the actual event run will take" SHOW VARIABLES LIKE 'event_scheduler'; Variable_name Value -event_scheduler ON +event_scheduler 1 "Check our table. Should see 1 row" SELECT * FROM slow_event_test; slo_val val @@ -64,18 +64,19 @@ SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; user_host query_time db sql_text "This should go to the slow log" SET SESSION long_query_time=10; +SET GLOBAL long_query_time=1; DROP EVENT long_event; -CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(5); +CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2); "Sleep some more time than the actual event run will take" "Check our table. Should see 2 rows" SELECT * FROM slow_event_test; slo_val val 4 0 -4 0 -"Check slow log. Should see 1 row because 5 is over the threshold of 4 for GLOBAL, though under SESSION which is 10" +1 0 +"Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10" SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; user_host query_time db sql_text -root[root] @ localhost [localhost] SLEEPVAL events_test INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(5) +USER_HOST SLEEPVAL events_test INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2) DROP EVENT long_event2; SET GLOBAL long_query_time =@old_global_long_query_time; SET SESSION long_query_time =@old_session_long_query_time; diff --git a/mysql-test/r/events_microsec.result b/mysql-test/r/events_microsec.result index ed15b066b93..b96bd551511 100644 --- a/mysql-test/r/events_microsec.result +++ b/mysql-test/r/events_microsec.result @@ -10,50 +10,4 @@ CREATE EVENT micro_test ON SCHEDULE EVERY 100 MINUTE_MICROSECOND DO SELECT 1; ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' CREATE EVENT micro_test ON SCHEDULE EVERY 100 SECOND_MICROSECOND DO SELECT 1; ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' -"Now create normal event and change it on SQL level" -CREATE EVENT micro_test2 ON SCHEDULE EVERY 1 MONTH DO SELECT 1; -UPDATE mysql.event SET interval_field='MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; -SHOW CREATE EVENT micro_test2; -ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' -SET GLOBAL event_scheduler=0; -"Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -Variable_name Value -event_scheduler OFF -UPDATE mysql.event SET interval_field='DAY_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; -SHOW CREATE EVENT micro_test2; -ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' -SET GLOBAL event_scheduler=0; -"Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -Variable_name Value -event_scheduler OFF -UPDATE mysql.event SET interval_field='SECOND_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; -SHOW CREATE EVENT micro_test2; -ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' -SET GLOBAL event_scheduler=0; -"Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -Variable_name Value -event_scheduler OFF -UPDATE mysql.event SET interval_field='HOUR_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; -SHOW CREATE EVENT micro_test2; -ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' -SET GLOBAL event_scheduler=0; -"Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -Variable_name Value -event_scheduler OFF -UPDATE mysql.event SET interval_field='MINUTE_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; -SHOW CREATE EVENT micro_test2; -ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' -SET GLOBAL event_scheduler=0; -"Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -Variable_name Value -event_scheduler OFF -SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER='event_scheduler'; -COUNT(*) -0 -DROP EVENT micro_test2; drop database events_test; diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result index 8b1f29d320f..aec2053f0e7 100644 --- a/mysql-test/r/events_scheduling.result +++ b/mysql-test/r/events_scheduling.result @@ -14,7 +14,7 @@ ENDS NOW() + INTERVAL 6 SECOND ON COMPLETION PRESERVE DO INSERT INTO table_2 VALUES(1); CREATE EVENT only_one_time ON SCHEDULE EVERY 2 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_3 VALUES(1); -CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_4 VALUES(1); +CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND ON COMPLETION PRESERVE DO INSERT INTO table_4 VALUES(1); SELECT IF(SUM(a) >= 4, 'OK', 'ERROR') FROM table_1; IF(SUM(a) >= 4, 'OK', 'ERROR') OK @@ -38,9 +38,12 @@ DROP EVENT start_n_end; "Already dropped because ended. Therefore an error." DROP EVENT only_one_time; ERROR HY000: Unknown event 'only_one_time' -"Already dropped because ended. Therefore an error." +"Should be preserved" +SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS; +EVENT_NAME STATUS +E19170 ENABLED +two_time DISABLED DROP EVENT two_time; -ERROR HY000: Unknown event 'two_time' DROP TABLE table_1; DROP TABLE table_2; DROP TABLE table_3; diff --git a/mysql-test/r/events_stress.result b/mysql-test/r/events_stress.result index 9f95cfad75d..ead618e8136 100644 --- a/mysql-test/r/events_stress.result +++ b/mysql-test/r/events_stress.result @@ -1,46 +1,61 @@ CREATE DATABASE IF NOT EXISTS events_test; -CREATE DATABASE events_test2; -USE events_test2; +CREATE DATABASE events_conn1_test2; +CREATE TABLE events_test.fill_it(test_name varchar(20), occur datetime); +CREATE USER event_user2@localhost; +CREATE DATABASE events_conn2_db; +GRANT ALL ON *.* TO event_user2@localhost; +CREATE USER event_user3@localhost; +CREATE DATABASE events_conn3_db; +GRANT ALL ON *.* TO event_user3@localhost; +"In the second connection we create some events which won't be dropped till the end" +"In the second connection we create some events which won't be dropped till the end" +USE events_conn1_test2; CREATE EVENT ev_drop1 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; CREATE EVENT ev_drop2 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; CREATE EVENT ev_drop3 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; USE events_test; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS; +COUNT(*) +103 +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; COUNT(*) 3 -DROP DATABASE events_test2; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +DROP DATABASE events_conn1_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; COUNT(*) 0 "Now testing stability - dropping db -> events while they are running" -CREATE DATABASE events_test2; -USE events_test2; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +CREATE DATABASE events_conn1_test2; +USE events_conn1_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; COUNT(*) -1000 +50 SET GLOBAL event_scheduler=1; -DROP DATABASE events_test2; -SET GLOBAL event_scheduler=0; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +DROP DATABASE events_conn1_test2; +SET GLOBAL event_scheduler=2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; COUNT(*) 0 -CREATE DATABASE events_test3; -USE events_test3; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test3'; +CREATE DATABASE events_conn1_test3; +USE events_conn1_test3; +SET GLOBAL event_scheduler=1; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test3'; COUNT(*) -950 -CREATE DATABASE events_test4; -USE events_test4; -CREATE DATABASE events_test2; -USE events_test2; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +50 +CREATE DATABASE events_conn1_test4; +USE events_conn1_test4; +CREATE DATABASE events_conn1_test2; +USE events_conn1_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; COUNT(*) -1050 -DROP DATABASE events_test2; -SET GLOBAL event_scheduler=0; -DROP DATABASE events_test3; -SET GLOBAL event_scheduler=1; -DROP DATABASE events_test4; +50 +DROP DATABASE events_conn2_db; +DROP DATABASE events_conn3_db; +DROP DATABASE events_conn1_test2; +DROP DATABASE events_conn1_test3; +SET GLOBAL event_scheduler=2; +DROP DATABASE events_conn1_test4; SET GLOBAL event_scheduler=1; USE events_test; +DROP TABLE fill_it; DROP DATABASE events_test; diff --git a/mysql-test/r/explain.result b/mysql-test/r/explain.result index d66dec741bd..75e1548cdee 100644 --- a/mysql-test/r/explain.result +++ b/mysql-test/r/explain.result @@ -53,3 +53,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE ÔÁ ref ÉÎÄ0,ÉÎÄ01 ÉÎÄ0 5 const 1 Using where; Using index drop table ÔÁÂ; set names latin1; +select 3 into @v1; +explain select 3 into @v1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 58bf3b36469..824edbbe3a6 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -821,144 +821,6 @@ SELECT MAX(id) FROM t1 WHERE id < 3 AND a=2 AND b=6; MAX(id) NULL DROP TABLE t1; -create table t1m (a int) engine=myisam; -create table t1i (a int) engine=innodb; -create table t2m (a int) engine=myisam; -create table t2i (a int) engine=innodb; -insert into t2m values (5); -insert into t2i values (5); -select min(a) from t1m; -min(a) -NULL -select min(7) from t1m; -min(7) -NULL -select min(7) from DUAL; -min(7) -NULL -explain select min(7) from t2m join t1m; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away -select min(7) from t2m join t1m; -min(7) -NULL -select max(a) from t1m; -max(a) -NULL -select max(7) from t1m; -max(7) -NULL -select max(7) from DUAL; -max(7) -NULL -explain select max(7) from t2m join t1m; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away -select max(7) from t2m join t1m; -max(7) -NULL -select 1, min(a) from t1m where a=99; -1 min(a) -1 NULL -select 1, min(a) from t1m where 1=99; -1 min(a) -1 NULL -select 1, min(1) from t1m where a=99; -1 min(1) -1 NULL -select 1, min(1) from t1m where 1=99; -1 min(1) -1 NULL -select 1, max(a) from t1m where a=99; -1 max(a) -1 NULL -select 1, max(a) from t1m where 1=99; -1 max(a) -1 NULL -select 1, max(1) from t1m where a=99; -1 max(1) -1 NULL -select 1, max(1) from t1m where 1=99; -1 max(1) -1 NULL -select min(a) from t1i; -min(a) -NULL -select min(7) from t1i; -min(7) -NULL -select min(7) from DUAL; -min(7) -NULL -explain select min(7) from t2i join t1i; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2i ALL NULL NULL NULL NULL 1 -1 SIMPLE t1i ALL NULL NULL NULL NULL 1 -select min(7) from t2i join t1i; -min(7) -NULL -select max(a) from t1i; -max(a) -NULL -select max(7) from t1i; -max(7) -NULL -select max(7) from DUAL; -max(7) -NULL -explain select max(7) from t2i join t1i; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2i ALL NULL NULL NULL NULL 1 -1 SIMPLE t1i ALL NULL NULL NULL NULL 1 -select max(7) from t2i join t1i; -max(7) -NULL -select 1, min(a) from t1i where a=99; -1 min(a) -1 NULL -select 1, min(a) from t1i where 1=99; -1 min(a) -1 NULL -select 1, min(1) from t1i where a=99; -1 min(1) -1 NULL -select 1, min(1) from t1i where 1=99; -1 min(1) -1 NULL -select 1, max(a) from t1i where a=99; -1 max(a) -1 NULL -select 1, max(a) from t1i where 1=99; -1 max(a) -1 NULL -select 1, max(1) from t1i where a=99; -1 max(1) -1 NULL -select 1, max(1) from t1i where 1=99; -1 max(1) -1 NULL -explain select count(*), min(7), max(7) from t1m, t1i; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1m system NULL NULL NULL NULL 0 const row not found -1 SIMPLE t1i ALL NULL NULL NULL NULL 1 -select count(*), min(7), max(7) from t1m, t1i; -count(*) min(7) max(7) -0 NULL NULL -explain select count(*), min(7), max(7) from t1m, t2i; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1m system NULL NULL NULL NULL 0 const row not found -1 SIMPLE t2i ALL NULL NULL NULL NULL 1 -select count(*), min(7), max(7) from t1m, t2i; -count(*) min(7) max(7) -0 NULL NULL -explain select count(*), min(7), max(7) from t2m, t1i; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2m system NULL NULL NULL NULL 1 -1 SIMPLE t1i ALL NULL NULL NULL NULL 1 -select count(*), min(7), max(7) from t2m, t1i; -count(*) min(7) max(7) -0 NULL NULL -drop table t1m, t1i, t2m, t2i; create table t2 (ff double); insert into t2 values (2.2); select cast(sum(distinct ff) as decimal(5,2)) from t2; diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index e3257ce5fd0..e38e2624e19 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -326,3 +326,20 @@ deallocate prepare s; set @str=NULL; drop table t2; drop table t1; +create table t1 ( +some_id smallint(5) unsigned, +key (some_id) +); +insert into t1 values (1),(2); +select some_id from t1 where some_id not in(2,-1); +some_id +1 +select some_id from t1 where some_id not in(-4,-1,-4); +some_id +1 +2 +select some_id from t1 where some_id not in(-4,-1,3423534,2342342); +some_id +1 +2 +drop table t1; diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 0b706b24488..354c886b19b 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1017,6 +1017,13 @@ t 1000000 1 drop table t1; +select load_file("lkjlkj"); +load_file("lkjlkj") +NULL +select ifnull(load_file("lkjlkj"),"it's null"); +ifnull(load_file("lkjlkj"),"it's null") +it's null +End of 4.1 tests create table t1 (d decimal default null); insert into t1 values (null); select format(d, 2) from t1; @@ -1039,4 +1046,10 @@ cast(ltrim(' 20.06 ') as decimal(19,2)) select cast(rtrim(ltrim(' 20.06 ')) as decimal(19,2)); cast(rtrim(ltrim(' 20.06 ')) as decimal(19,2)) 20.06 +select conv("18383815659218730760",10,10) + 0; +conv("18383815659218730760",10,10) + 0 +1.8383815659219e+19 +select "18383815659218730760" + 0; +"18383815659218730760" + 0 +1.8383815659219e+19 End of 5.0 tests diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index b5fd35d926f..516809b8906 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -732,12 +732,6 @@ SELECT count(*) FROM t1 WHERE d>FROM_DAYS(TO_DAYS(@TMP)) AND d<=FROM_DAYS(TO_DAY count(*) 3 DROP TABLE t1; -explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1, -timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used -Warnings: -Note 1003 select timestampdiff(WEEK,_latin1'2001-02-01',_latin1'2001-05-01') AS `a1`,timestampdiff(SECOND_FRAC,_latin1'2001-02-01 12:59:59.120000',_latin1'2001-05-01 12:58:58.119999') AS `a2` select last_day('2005-00-00'); last_day('2005-00-00') NULL @@ -753,6 +747,17 @@ last_day('2005-01-00') NULL Warnings: Warning 1292 Incorrect datetime value: '2005-01-00' +select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), +monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); +monthname(str_to_date(null, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(1, '%m')) monthname(str_to_date(0, '%m')) +NULL NULL January NULL +End of 4.1 tests +explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1, +timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select timestampdiff(WEEK,_latin1'2001-02-01',_latin1'2001-05-01') AS `a1`,timestampdiff(SECOND_FRAC,_latin1'2001-02-01 12:59:59.120000',_latin1'2001-05-01 12:58:58.119999') AS `a2` select time_format('100:00:00', '%H %k %h %I %l'); time_format('100:00:00', '%H %k %h %I %l') 100 100 04 04 4 @@ -872,3 +877,38 @@ id day id day 1 2005-06-01 3 2005-07-15 3 2005-07-01 3 2005-07-15 DROP TABLE t1,t2; +End of 5.0 tests +select date_sub("0050-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("0050-01-01 00:00:01",INTERVAL 2 SECOND) +NULL +select date_sub("0199-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("0199-01-01 00:00:01",INTERVAL 2 SECOND) +NULL +select date_add("0199-12-31 23:59:59",INTERVAL 2 SECOND); +date_add("0199-12-31 23:59:59",INTERVAL 2 SECOND) +NULL +select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND) +0199-12-31 23:59:59 +select date_sub("0200-01-01 00:00:01",INTERVAL 1 SECOND); +date_sub("0200-01-01 00:00:01",INTERVAL 1 SECOND) +0200-01-01 00:00:00 +select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND) +0199-12-31 23:59:59 +select date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR); +date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR) +0001-01-01 23:59:59 +select date_sub("50-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("50-01-01 00:00:01",INTERVAL 2 SECOND) +2049-12-31 23:59:59 +select date_sub("90-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("90-01-01 00:00:01",INTERVAL 2 SECOND) +1989-12-31 23:59:59 +select date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND) +NULL +select date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND); +date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND) +NULL +End of 5.1 tests diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index a37f260ff31..68b13b5fc0a 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -12,7 +12,7 @@ explain extended select count(a) as b from t1 where a=0 having b >=0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: -Note 1003 select count(`test`.`t1`.`a`) AS `b` from `test`.`t1` where 0 having (count(`test`.`t1`.`a`) >= 0) +Note 1003 select count(`test`.`t1`.`a`) AS `b` from `test`.`t1` where 0 having (`b` >= 0) drop table t1; CREATE TABLE t1 ( raw_id int(10) NOT NULL default '0', diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result index 2c0e8123667..a75f6c96b20 100644 --- a/mysql-test/r/heap.result +++ b/mysql-test/r/heap.result @@ -718,3 +718,16 @@ Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length I t1 MEMORY 10 Fixed 0 11 0 # 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL t2 MEMORY 10 Fixed 0 12 0 # 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL drop table t1, t2; +CREATE TABLE t1(a VARCHAR(1), b VARCHAR(2), c VARCHAR(256), +KEY(a), KEY(b), KEY(c)) ENGINE=MEMORY; +INSERT INTO t1 VALUES('a','aa',REPEAT('a', 256)),('a','aa',REPEAT('a',256)); +SELECT COUNT(*) FROM t1 WHERE a='a'; +COUNT(*) +2 +SELECT COUNT(*) FROM t1 WHERE b='aa'; +COUNT(*) +2 +SELECT COUNT(*) FROM t1 WHERE c=REPEAT('a',256); +COUNT(*) +2 +DROP TABLE t1; diff --git a/mysql-test/r/im_cmd_line.result b/mysql-test/r/im_cmd_line.result new file mode 100644 index 00000000000..5b289549a3f --- /dev/null +++ b/mysql-test/r/im_cmd_line.result @@ -0,0 +1,40 @@ +--> Listing users... +im_admin + +==> Adding user 'testuser'... + +--> IM password file: +testuser:*0D3CED9BEC10A777AEC23CCC353A8C08A633045E +im_admin:*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295 +--> EOF + +--> Printing out line for 'testuser'... +testuser:*0D3CED9BEC10A777AEC23CCC353A8C08A633045E + +--> Listing users... +im_admin +testuser + +==> Changing the password of 'testuser'... + +--> IM password file: +im_admin:*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295 +testuser:*39C549BDECFBA8AFC3CE6B948C9359A0ECE08DE2 +--> EOF + +--> Printing out line for 'testuser'... +testuser:*39C549BDECFBA8AFC3CE6B948C9359A0ECE08DE2 + +--> Listing users... +testuser +im_admin + +==> Dropping user 'testuser'... + +--> IM password file: +im_admin:*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295 +--> EOF + +--> Listing users... +im_admin + diff --git a/mysql-test/r/im_daemon_life_cycle.result b/mysql-test/r/im_daemon_life_cycle.result index d0a76b450fe..29c9ea2047d 100644 --- a/mysql-test/r/im_daemon_life_cycle.result +++ b/mysql-test/r/im_daemon_life_cycle.result @@ -1,5 +1,5 @@ SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline Killing the process... diff --git a/mysql-test/r/im_instance_conf.result b/mysql-test/r/im_instance_conf.result new file mode 100644 index 00000000000..efda9439f38 --- /dev/null +++ b/mysql-test/r/im_instance_conf.result @@ -0,0 +1,196 @@ +-------------------------------------------------------------------- +server_id = 1 +server_id = 2 +-------------------------------------------------------------------- +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline + +---> connection: mysql1_con +SHOW VARIABLES LIKE 'server_id'; +Variable_name Value +server_id 1 + +---> connection: default +CREATE INSTANCE mysqld3; +SHOW INSTANCES; +instance_name state +mysqld3 offline +mysqld2 offline +mysqld1 online +-------------------------------------------------------------------- +server_id = 1 +server_id = 2 +-------------------------------------------------------------------- +CREATE INSTANCE mysqld1; +ERROR HY000: Instance already exists +CREATE INSTANCE mysqld2; +ERROR HY000: Instance already exists +CREATE INSTANCE mysqld3; +ERROR HY000: Instance already exists +-------------------------------------------------------------------- +nonguarded +-------------------------------------------------------------------- +CREATE INSTANCE mysqld4 nonguarded; +SHOW INSTANCES; +instance_name state +mysqld3 offline +mysqld4 offline +mysqld1 online +mysqld2 offline +-------------------------------------------------------------------- +nonguarded +nonguarded +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +CREATE INSTANCE mysqld5 test-A = 000, test-B = test; +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld4 offline +mysqld5 offline +mysqld2 offline +mysqld3 offline +-------------------------------------------------------------------- +test-A=000 +-------------------------------------------------------------------- +test-B=test +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +CREATE INSTANCE mysqld6 test-C1 = 10 , test-C2 = 02 ; +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +-------------------------------------------------------------------- +test-C1=10 +-------------------------------------------------------------------- +test-C2=02 +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +CREATE INSTANCE mysqld7 test-D = test-D-value ; +ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +CREATE INSTANCE mysqld8 test-E 0 ; +ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +CREATE INSTANCE mysqld8 test-F = ; +ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +CREATE INSTANCE mysqld9 test-1=" hello world ", test-2=' '; +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +mysqld9 offline +CREATE INSTANCE mysqld9a test-3='\b\babc\sdef'; +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld9a offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +mysqld9 offline +mysqld2 offline +CREATE INSTANCE mysqld9b test-4='abc\tdef', test-5='abc\ndef'; +SHOW INSTANCES; +instance_name state +mysqld9b offline +mysqld9a offline +mysqld5 offline +mysqld6 offline +mysqld3 offline +mysqld4 offline +mysqld9 offline +mysqld2 offline +mysqld1 online +CREATE INSTANCE mysqld9c test-6="abc\rdef", test-7="abc\\def"; +SHOW INSTANCES; +instance_name state +mysqld9b offline +mysqld6 offline +mysqld5 offline +mysqld9c offline +mysqld3 offline +mysqld4 offline +mysqld9 offline +mysqld2 offline +mysqld1 online +mysqld9a offline +CREATE INSTANCE mysqld10 test-bad=' \ '; +ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use +SHOW INSTANCES; +instance_name state +mysqld9b offline +mysqld6 offline +mysqld5 offline +mysqld9c offline +mysqld3 offline +mysqld4 offline +mysqld9 offline +mysqld2 offline +mysqld1 online +mysqld9a offline +-------------------------------------------------------------------- +test-1= hello world +-------------------------------------------------------------------- +test-2= +-------------------------------------------------------------------- +test-3=abc def +-------------------------------------------------------------------- +test-4=abc def +-------------------------------------------------------------------- +test-5=abc +-------------------------------------------------------------------- +test-6=abc
def +-------------------------------------------------------------------- +test-7=abc\def +-------------------------------------------------------------------- +-------------------------------------------------------------------- +CREATE INSTANCE qqq1; +ERROR HY000: Malformed instance name. diff --git a/mysql-test/r/im_life_cycle.result b/mysql-test/r/im_life_cycle.result index e208ccb9f00..876fbb38eee 100644 --- a/mysql-test/r/im_life_cycle.result +++ b/mysql-test/r/im_life_cycle.result @@ -1,69 +1,93 @@ + +-------------------------------------------------------------------- +-- 1.1.1. +-------------------------------------------------------------------- SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline -SHOW INSTANCE STATUS mysqld1; -instance_name status version_number version -mysqld1 online VERSION_NUMBER VERSION -SHOW INSTANCE STATUS mysqld2; -instance_name status version_number version -mysqld2 offline VERSION_NUMBER VERSION + +-------------------------------------------------------------------- +-- 1.1.2. +-------------------------------------------------------------------- START INSTANCE mysqld2; SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 online -SHOW INSTANCE STATUS mysqld1; -instance_name status version_number version -mysqld1 online VERSION_NUMBER VERSION -SHOW INSTANCE STATUS mysqld2; -instance_name status version_number version -mysqld2 online VERSION_NUMBER VERSION SHOW VARIABLES LIKE 'port'; Variable_name Value -port IM_MYSQLD1_PORT +port IM_MYSQLD2_PORT + +-------------------------------------------------------------------- +-- 1.1.3. +-------------------------------------------------------------------- STOP INSTANCE mysqld2; SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline SHOW INSTANCE STATUS mysqld1; -instance_name status version_number version -mysqld1 online VERSION_NUMBER VERSION +instance_name state version_number version mysqld_compatible +mysqld1 online VERSION_NUMBER VERSION no SHOW INSTANCE STATUS mysqld2; -instance_name status version_number version -mysqld2 offline VERSION_NUMBER VERSION +instance_name state version_number version mysqld_compatible +mysqld2 offline VERSION_NUMBER VERSION no + +-------------------------------------------------------------------- +-- 1.1.4. +-------------------------------------------------------------------- START INSTANCE mysqld3; ERROR HY000: Bad instance name. Check that the instance with such a name exists START INSTANCE mysqld1; ERROR HY000: The instance is already started + +-------------------------------------------------------------------- +-- 1.1.5. +-------------------------------------------------------------------- STOP INSTANCE mysqld3; ERROR HY000: Bad instance name. Check that the instance with such a name exists + +-------------------------------------------------------------------- +-- 1.1.6. +-------------------------------------------------------------------- SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline Killing the process... Sleeping... Success: the process was restarted. + +-------------------------------------------------------------------- +-- 1.1.7. +-------------------------------------------------------------------- SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline START INSTANCE mysqld2; SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 online Killing the process... Sleeping... Success: the process was killed. SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline + +-------------------------------------------------------------------- +-- 1.1.8. +-------------------------------------------------------------------- SHOW INSTANCE STATUS; ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use + +-------------------------------------------------------------------- +-- BUG#12813 +-------------------------------------------------------------------- START INSTANCE mysqld1,mysqld2,mysqld3; ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use STOP INSTANCE mysqld1,mysqld2,mysqld3; diff --git a/mysql-test/r/im_options.result b/mysql-test/r/im_options.result new file mode 100644 index 00000000000..8039333b7d9 --- /dev/null +++ b/mysql-test/r/im_options.result @@ -0,0 +1,150 @@ +-------------------------------------------------------------------- +server_id = 1 +server_id = 2 +-------------------------------------------------------------------- +SHOW VARIABLES LIKE 'server_id'; +Variable_name Value +server_id 1 +SHOW INSTANCES; +instance_name state +mysqld1 starting +mysqld2 offline +UNSET mysqld1.server_id; +ERROR HY000: The instance is active. Stop the instance first +SET mysqld1.server_id = 11; +ERROR HY000: The instance is active. Stop the instance first +CREATE INSTANCE mysqld3 datadir = '/'; +START INSTANCE mysqld3; +UNSET mysqld3.server_id; +ERROR HY000: The instance is active. Stop the instance first +SET mysqld3.server_id = 11; +ERROR HY000: The instance is active. Stop the instance first +STOP INSTANCE mysqld3; +SHOW INSTANCE STATUS mysqld3; +instance_name state version_number version mysqld_compatible +mysqld3 offline VERSION_NUMBER VERSION no +UNSET mysqld2.server_id; +UNSET mysqld2.server_id; +SHOW INSTANCE OPTIONS mysqld2; +option_name value +instance_name option_value +socket option_value +pid-file option_value +port option_value +datadir option_value +log option_value +log-error option_value +log-slow-queries option_value +language option_value +character-sets-dir option_value +basedir option_value +skip-stack-trace option_value +skip-innodb option_value +skip-bdb option_value +skip-ndbcluster option_value +nonguarded option_value +log-output option_value +SET mysqld2.server_id = 2; +SET mysqld2.server_id = 2; +SHOW INSTANCE OPTIONS mysqld2; +option_name value +instance_name option_value +socket option_value +pid-file option_value +port option_value +datadir option_value +log option_value +log-error option_value +log-slow-queries option_value +language option_value +character-sets-dir option_value +basedir option_value +skip-stack-trace option_value +skip-innodb option_value +skip-bdb option_value +skip-ndbcluster option_value +nonguarded option_value +log-output option_value +server_id option_value +UNSET mysqld2.server_id = 11; +ERROR 42000: You have an error in your command syntax. Check the manual that corresponds to your MySQL Instance Manager version for the right syntax to use +SET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc = 0010, mysqld3.ddd = 0020; +-------------------------------------------------------------------- +aaa +-------------------------------------------------------------------- +bbb +-------------------------------------------------------------------- +ccc=0010 +-------------------------------------------------------------------- +ddd=0020 +-------------------------------------------------------------------- +UNSET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc, mysqld3.ddd; +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +SET mysqld2.aaa, mysqld3.bbb, mysqld.ccc = 0010; +ERROR HY000: Bad instance name. Check that the instance with such a name exists +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +SET mysqld2.aaa, mysqld3.bbb, mysqld1.ccc = 0010; +ERROR HY000: The instance is active. Stop the instance first +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +UNSET mysqld2.server_id, mysqld3.server_id, mysqld.ccc; +ERROR HY000: Bad instance name. Check that the instance with such a name exists +-------------------------------------------------------------------- +server_id = 1 +server_id=2 +-------------------------------------------------------------------- +UNSET mysqld2.server_id, mysqld3.server_id, mysqld1.ccc; +ERROR HY000: The instance is active. Stop the instance first +-------------------------------------------------------------------- +server_id = 1 +server_id=2 +-------------------------------------------------------------------- +DROP INSTANCE mysqld3; +SET mysqld2.server_id=222; +SET mysqld2.server_id = 222; +SET mysqld2.server_id = 222 ; +SET mysqld2 . server_id = 222 ; +SET mysqld2 . server_id = 222 , mysqld2 . aaa , mysqld2 . bbb ; +-------------------------------------------------------------------- +server_id = 1 +server_id=222 +-------------------------------------------------------------------- +aaa +-------------------------------------------------------------------- +bbb +-------------------------------------------------------------------- +UNSET mysqld2 . aaa , mysqld2 . bbb ; +-------------------------------------------------------------------- +server_id = 1 +server_id=222 +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +-------------------------------------------------------------------- +server_id = 1 +server_id=222 +-------------------------------------------------------------------- +SHOW VARIABLES LIKE 'server_id'; +Variable_name Value +server_id 1 +SHOW INSTANCES; +instance_name state +mysqld1 online +mysqld2 offline +FLUSH INSTANCES; +ERROR HY000: At least one instance is active. Stop all instances first +STOP INSTANCE mysqld1; +SHOW INSTANCES; +instance_name state +mysqld1 offline +mysqld2 offline +FLUSH INSTANCES; diff --git a/mysql-test/r/im_options_set.result b/mysql-test/r/im_options_set.result deleted file mode 100644 index 0d2fa699fc7..00000000000 --- a/mysql-test/r/im_options_set.result +++ /dev/null @@ -1,20 +0,0 @@ -server_id =1 -server_id =2 -SHOW VARIABLES LIKE 'server_id'; -Variable_name Value -server_id 1 -SET mysqld1.server_id = 11; -server_id =11 -server_id =2 -SHOW VARIABLES LIKE 'server_id'; -Variable_name Value -server_id 1 -SET mysqld2.server_id = 12; -server_id =11 -server_id =12 -FLUSH INSTANCES; -server_id =11 -server_id =12 -SHOW VARIABLES LIKE 'server_id'; -Variable_name Value -server_id 1 diff --git a/mysql-test/r/im_options_unset.result b/mysql-test/r/im_options_unset.result deleted file mode 100644 index 834152c35d2..00000000000 --- a/mysql-test/r/im_options_unset.result +++ /dev/null @@ -1,15 +0,0 @@ -server_id =1 -server_id =2 -SHOW VARIABLES LIKE 'server_id'; -Variable_name Value -server_id 1 -UNSET mysqld1.server_id; -server_id =2 -SHOW VARIABLES LIKE 'server_id'; -Variable_name Value -server_id 1 -UNSET mysqld2.server_id; -FLUSH INSTANCES; -SHOW VARIABLES LIKE 'server_id'; -Variable_name Value -server_id 1 diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result index 504b2efe4af..ae8e03bf8ea 100644 --- a/mysql-test/r/im_utils.result +++ b/mysql-test/r/im_utils.result @@ -1,11 +1,10 @@ SHOW INSTANCES; -instance_name status +instance_name state mysqld1 online mysqld2 offline SHOW INSTANCE OPTIONS mysqld1; option_name value instance_name VALUE -mysqld-path VALUE socket VALUE pid-file VALUE port VALUE @@ -25,8 +24,6 @@ log-output VALUE SHOW INSTANCE OPTIONS mysqld2; option_name value instance_name VALUE -mysqld-path VALUE -nonguarded VALUE socket VALUE pid-file VALUE port VALUE @@ -42,6 +39,7 @@ skip-stack-trace VALUE skip-innodb VALUE skip-bdb VALUE skip-ndbcluster VALUE +nonguarded VALUE log-output VALUE START INSTANCE mysqld2; STOP INSTANCE mysqld2; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 7524827310a..603b7815d5b 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1137,6 +1137,23 @@ group by column_type order by num; column_type group_concat(table_schema, '.', table_name) num varchar(7) information_schema.ROUTINES,information_schema.VIEWS 2 varchar(20) information_schema.COLUMNS,information_schema.FILES,information_schema.FILES,information_schema.PLUGINS,information_schema.PLUGINS,information_schema.PLUGINS 6 +create table t1(f1 char(1) not null, f2 char(9) not null) +default character set utf8; +select CHARACTER_MAXIMUM_LENGTH, CHARACTER_OCTET_LENGTH from +information_schema.columns where table_schema='test' and table_name = 't1'; +CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH +1 3 +9 27 +drop table t1; +use mysql; +INSERT INTO `proc` VALUES ('test','','PROCEDURE','','SQL','CONTAINS_SQL', +'NO','DEFINER','','','BEGIN\r\n \r\nEND','root@%','2006-03-02 18:40:03', +'2006-03-02 18:40:03','',''); +select routine_name from information_schema.routines; +routine_name + +delete from proc where name=''; +use test; select * from information_schema.engines WHERE ENGINE="MyISAM"; ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS MyISAM ENABLED Default engine as of MySQL 3.23 with great performance NO NO NO @@ -1145,3 +1162,16 @@ select user,db from information_schema.processlist; user db user3148 test drop user user3148@localhost; +grant select on test.* to mysqltest_1@localhost; +create table t1 (id int); +create view v1 as select * from t1; +create definer = mysqltest_1@localhost +sql security definer view v2 as select 1; +select * from information_schema.views +where table_name='v1' or table_name='v2'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE +NULL test v1 NONE YES root@localhost DEFINER +NULL test v2 select 1 AS `1` NONE NO mysqltest_1@localhost DEFINER +drop view v1, v2; +drop table t1; +drop user mysqltest_1@localhost; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 2a4e3555e3b..bbd9550196f 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -54,3 +54,141 @@ c.c_id = 218 and expiredate is null; slai_id 12 drop table t1, t2; +create table t1m (a int) engine=myisam; +create table t1i (a int) engine=innodb; +create table t2m (a int) engine=myisam; +create table t2i (a int) engine=innodb; +insert into t2m values (5); +insert into t2i values (5); +select min(a) from t1m; +min(a) +NULL +select min(7) from t1m; +min(7) +NULL +select min(7) from DUAL; +min(7) +NULL +explain select min(7) from t2m join t1m; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +select min(7) from t2m join t1m; +min(7) +NULL +select max(a) from t1m; +max(a) +NULL +select max(7) from t1m; +max(7) +NULL +select max(7) from DUAL; +max(7) +NULL +explain select max(7) from t2m join t1m; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +select max(7) from t2m join t1m; +max(7) +NULL +select 1, min(a) from t1m where a=99; +1 min(a) +1 NULL +select 1, min(a) from t1m where 1=99; +1 min(a) +1 NULL +select 1, min(1) from t1m where a=99; +1 min(1) +1 NULL +select 1, min(1) from t1m where 1=99; +1 min(1) +1 NULL +select 1, max(a) from t1m where a=99; +1 max(a) +1 NULL +select 1, max(a) from t1m where 1=99; +1 max(a) +1 NULL +select 1, max(1) from t1m where a=99; +1 max(1) +1 NULL +select 1, max(1) from t1m where 1=99; +1 max(1) +1 NULL +select min(a) from t1i; +min(a) +NULL +select min(7) from t1i; +min(7) +NULL +select min(7) from DUAL; +min(7) +NULL +explain select min(7) from t2i join t1i; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2i ALL NULL NULL NULL NULL 1 +1 SIMPLE t1i ALL NULL NULL NULL NULL 1 +select min(7) from t2i join t1i; +min(7) +NULL +select max(a) from t1i; +max(a) +NULL +select max(7) from t1i; +max(7) +NULL +select max(7) from DUAL; +max(7) +NULL +explain select max(7) from t2i join t1i; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2i ALL NULL NULL NULL NULL 1 +1 SIMPLE t1i ALL NULL NULL NULL NULL 1 +select max(7) from t2i join t1i; +max(7) +NULL +select 1, min(a) from t1i where a=99; +1 min(a) +1 NULL +select 1, min(a) from t1i where 1=99; +1 min(a) +1 NULL +select 1, min(1) from t1i where a=99; +1 min(1) +1 NULL +select 1, min(1) from t1i where 1=99; +1 min(1) +1 NULL +select 1, max(a) from t1i where a=99; +1 max(a) +1 NULL +select 1, max(a) from t1i where 1=99; +1 max(a) +1 NULL +select 1, max(1) from t1i where a=99; +1 max(1) +1 NULL +select 1, max(1) from t1i where 1=99; +1 max(1) +1 NULL +explain select count(*), min(7), max(7) from t1m, t1i; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1m system NULL NULL NULL NULL 0 const row not found +1 SIMPLE t1i ALL NULL NULL NULL NULL 1 +select count(*), min(7), max(7) from t1m, t1i; +count(*) min(7) max(7) +0 NULL NULL +explain select count(*), min(7), max(7) from t1m, t2i; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1m system NULL NULL NULL NULL 0 const row not found +1 SIMPLE t2i ALL NULL NULL NULL NULL 1 +select count(*), min(7), max(7) from t1m, t2i; +count(*) min(7) max(7) +0 NULL NULL +explain select count(*), min(7), max(7) from t2m, t1i; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2m system NULL NULL NULL NULL 1 +1 SIMPLE t1i ALL NULL NULL NULL NULL 1 +select count(*), min(7), max(7) from t2m, t1i; +count(*) min(7) max(7) +0 NULL NULL +drop table t1m, t1i, t2m, t2i; diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index ecf76d477a0..86288caf398 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -700,8 +700,8 @@ ERROR 42S22: Unknown column 't1.b' in 'on clause' select * from information_schema.statistics join information_schema.columns using(table_name,column_name) where table_name='user'; TABLE_NAME COLUMN_NAME TABLE_CATALOG TABLE_SCHEMA NON_UNIQUE INDEX_SCHEMA INDEX_NAME SEQ_IN_INDEX COLLATION CARDINALITY SUB_PART PACKED NULLABLE INDEX_TYPE COMMENT TABLE_CATALOG TABLE_SCHEMA 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 -user Host NULL mysql 0 mysql PRIMARY 1 A NULL NULL NULL BTREE NULL mysql 1 NO char 20 60 NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references -user User NULL mysql 0 mysql PRIMARY 2 A 5 NULL NULL BTREE NULL mysql 2 NO char 5 16 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references +user Host NULL mysql 0 mysql PRIMARY 1 A NULL NULL NULL BTREE NULL mysql 1 NO char 60 180 NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references +user User NULL mysql 0 mysql PRIMARY 2 A 5 NULL NULL BTREE NULL mysql 2 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references drop table t1; drop table t2; drop table t3; diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 8a30f3c18ec..f4ec997e50c 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -1151,6 +1151,63 @@ EXPLAIN SELECT COUNT(*) FROM t2 LEFT JOIN t1 ON t2.fkey = t1.id WHERE t1.name LIKE 'A%' OR FALSE; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index PRIMARY,name name 23 NULL 3 Using where; Using index -1 SIMPLE t2 ref fkey fkey 5 test.t1.id 1 Using where; Using index +1 SIMPLE t2 index NULL fkey 5 NULL 5 Using index +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.fkey 1 Using where +DROP TABLE t1,t2; +DROP VIEW IF EXISTS v1,v2; +DROP TABLE IF EXISTS t1,t2; +CREATE TABLE t1 (a int); +CREATE table t2 (b int); +INSERT INTO t1 VALUES (1), (2), (3), (4), (1), (1), (3); +INSERT INTO t2 VALUES (2), (3); +CREATE VIEW v1 AS SELECT a FROM t1 JOIN t2 ON t1.a=t2.b; +CREATE VIEW v2 AS SELECT b FROM t2 JOIN t1 ON t2.b=t1.a; +SELECT v1.a, v2. b +FROM v1 LEFT OUTER JOIN v2 ON (v1.a=v2.b) AND (v1.a >= 3) +GROUP BY v1.a; +a b +2 NULL +3 3 +SELECT v1.a, v2. b +FROM { OJ v1 LEFT OUTER JOIN v2 ON (v1.a=v2.b) AND (v1.a >= 3) } +GROUP BY v1.a; +a b +2 NULL +3 3 +DROP VIEW v1,v2; +DROP TABLE t1,t2; +CREATE TABLE t1 (a int); +CREATE TABLE t2 (b int); +INSERT INTO t1 VALUES (1), (2), (3), (4); +INSERT INTO t2 VALUES (2), (3); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1=1); +a b +1 NULL +2 2 +3 3 +4 NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1 OR 1); +a b +1 NULL +2 2 +3 3 +4 NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (0 OR 1); +a b +1 NULL +2 2 +3 3 +4 NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1=1 OR 2=2); +a b +1 NULL +2 2 +3 3 +4 NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1=1 OR 1=0); +a b +1 NULL +2 2 +3 3 +4 NULL DROP TABLE t1,t2; diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result index 2164c18823f..c61dd2247ee 100644 --- a/mysql-test/r/log_tables.result +++ b/mysql-test/r/log_tables.result @@ -2,14 +2,14 @@ use mysql; truncate table general_log; select * from general_log; event_time user_host thread_id server_id command_type argument -TIMESTAMP root[root] @ localhost [] 1 1 Query select * from general_log +TIMESTAMP USER_HOST THREAD_ID 1 Query select * from general_log truncate table slow_log; select * from slow_log; start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text truncate table general_log; select * from general_log where argument like '%general_log%'; event_time user_host thread_id server_id command_type argument -TIMESTAMP root[root] @ localhost [] 1 1 Query select * from general_log where argument like '%general_log%' +TIMESTAMP USER_HOST THREAD_ID 1 Query select * from general_log where argument like '%general_log%' create table join_test (verbose_comment varchar (80), command_type varchar(64)); insert into join_test values ("User performed a usual SQL query", "Query"); insert into join_test values ("New DB connection was registered", "Connect"); @@ -18,12 +18,12 @@ select verbose_comment, user_host, argument from mysql.general_log join join_test on (mysql.general_log.command_type = join_test.command_type); verbose_comment user_host argument -User performed a usual SQL query root[root] @ localhost [] select * from general_log where argument like '%general_log%' -User performed a usual SQL query root[root] @ localhost [] create table join_test (verbose_comment varchar (80), command_type varchar(64)) -User performed a usual SQL query root[root] @ localhost [] insert into join_test values ("User performed a usual SQL query", "Query") -User performed a usual SQL query root[root] @ localhost [] insert into join_test values ("New DB connection was registered", "Connect") -User performed a usual SQL query root[root] @ localhost [] insert into join_test values ("Get the table info", "Field List") -User performed a usual SQL query root[root] @ localhost [] select verbose_comment, user_host, argument +User performed a usual SQL query USER_HOST select * from general_log where argument like '%general_log%' +User performed a usual SQL query USER_HOST create table join_test (verbose_comment varchar (80), command_type varchar(64)) +User performed a usual SQL query USER_HOST insert into join_test values ("User performed a usual SQL query", "Query") +User performed a usual SQL query USER_HOST insert into join_test values ("New DB connection was registered", "Connect") +User performed a usual SQL query USER_HOST insert into join_test values ("Get the table info", "Field List") +User performed a usual SQL query USER_HOST select verbose_comment, user_host, argument from mysql.general_log join join_test on (mysql.general_log.command_type = join_test.command_type) drop table join_test; @@ -59,10 +59,10 @@ create table bug16905 (s char(15) character set utf8 default 'пуÑто'); insert into bug16905 values ('новое'); select * from mysql.general_log; event_time user_host thread_id server_id command_type argument -TIMESTAMP root[root] @ localhost [] 2 1 Query set names utf8 -TIMESTAMP root[root] @ localhost [] 2 1 Query create table bug16905 (s char(15) character set utf8 default 'пуÑто') -TIMESTAMP root[root] @ localhost [] 2 1 Query insert into bug16905 values ('новое') -TIMESTAMP root[root] @ localhost [] 2 1 Query select * from mysql.general_log +TIMESTAMP USER_HOST THREAD_ID 1 Query set names utf8 +TIMESTAMP USER_HOST THREAD_ID 1 Query create table bug16905 (s char(15) character set utf8 default 'пуÑто') +TIMESTAMP USER_HOST THREAD_ID 1 Query insert into bug16905 values ('новое') +TIMESTAMP USER_HOST THREAD_ID 1 Query select * from mysql.general_log drop table bug16905; truncate table mysql.slow_log; set session long_query_time=1; @@ -71,4 +71,4 @@ sleep(2) 0 select * from mysql.slow_log; start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text -TIMESTAMP, root[root] @ localhost [] USER_HOST, QUERY_TIME 1 0 test 0 0 1 select sleep(2) +TIMESTAMP USER_HOST QUERY_TIME 00:00:00 1 0 test 0 0 1 select sleep(2) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 538f1fa1a20..2f5f382028a 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -771,3 +771,11 @@ Table Op Msg_type Msg_text test.t1 check status OK test.t2 check status OK drop table t1, t2, t3; +drop table if exists t1; +Warnings: +Note 1051 Unknown table 't1' +create table t1 (c char(20)) engine=MyISAM; +insert into t1 values ("Monty"),("WAX"),("Walrus"); +alter table t1 engine=MERGE; +ERROR HY000: Table storage engine for 't1' doesn't have this option +drop table t1; diff --git a/mysql-test/r/metadata.result b/mysql-test/r/metadata.result index 0a170e16188..50b0b6ae294 100644 --- a/mysql-test/r/metadata.result +++ b/mysql-test/r/metadata.result @@ -2,7 +2,7 @@ drop table if exists t1,t2; select 1, 1.0, -1, "hello", NULL; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def 1 8 1 1 N 32897 0 63 -def 1.0 246 3 3 N 161 1 63 +def 1.0 246 4 3 N 129 1 63 def -1 8 2 2 N 32897 0 63 def hello 253 5 5 N 1 31 8 def NULL 6 0 0 Y 32896 0 63 diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result index a067d3ad0f8..4b7084e813c 100644 --- a/mysql-test/r/mysql.result +++ b/mysql-test/r/mysql.result @@ -74,7 +74,7 @@ c_cp932 +----------------------+------------+--------+ | >a < | b | 123421 | | >a < | 0123456789 | 4 | -| >abcd< | NULL | 4 | +| >abcd< | | 4 | +----------------------+------------+--------+ +------+------+---------------------------+ | i | j | k | @@ -94,6 +94,14 @@ c_cp932 | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | i | int(11) | YES | | NULL | | -| j | int(11) | NO | | NULL | | +| j | int(11) | NO | | | | | k | int(11) | YES | | NULL | | +-------+---------+------+-----+---------+-------+ ++------+------+ +| i | s1 | ++------+------+ +| 1 | x | +| 2 | NULL | +| 3 | | ++------+------+ +End of 5.0 tests diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 50e4cc28d93..664833fab2a 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -189,4 +189,5 @@ HEX(f) select HEX(f) from t4; HEX(f) 835C +flush logs; drop table t1, t2, t03, t04, t3, t4; diff --git a/mysql-test/r/ndb_alter_table.result b/mysql-test/r/ndb_alter_table.result index 7c687ff33a1..e506973f347 100644 --- a/mysql-test/r/ndb_alter_table.result +++ b/mysql-test/r/ndb_alter_table.result @@ -1,4 +1,4 @@ -DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1, t2; drop database if exists mysqltest; CREATE TABLE t1 ( a INT NOT NULL, @@ -328,3 +328,24 @@ select 'no_copy' from ndb_show_tables where id = @t1_id and name like '%t1%'; no_copy no_copy DROP TABLE t1, ndb_show_tables; +create table t1 (a int primary key auto_increment, b int) engine=ndb; +insert into t1 (b) values (101),(102),(103); +select * from t1 where a = 3; +a b +3 103 +alter table t1 rename t2; +insert into t2 (b) values (201),(202),(203); +select * from t2 where a = 6; +a b +6 203 +alter table t2 add c int; +insert into t2 (b) values (301),(302),(303); +select * from t2 where a = 9; +a b c +9 303 NULL +alter table t2 rename t1; +insert into t1 (b) values (401),(402),(403); +select * from t1 where a = 12; +a b c +12 403 NULL +drop table t1; diff --git a/mysql-test/r/ndb_autodiscover3.result b/mysql-test/r/ndb_autodiscover3.result new file mode 100644 index 00000000000..86495ebb3eb --- /dev/null +++ b/mysql-test/r/ndb_autodiscover3.result @@ -0,0 +1,53 @@ +drop table if exists t1, t2; +create table t1 (a int key) engine=ndbcluster; +begin; +insert into t1 values (1); +insert into t1 values (2); +ERROR HY000: Got temporary error 4025 'Node failure caused abort of transaction' from NDBCLUSTER +commit; +ERROR HY000: Got error 4350 'Transaction already aborted' from NDBCLUSTER +drop table t1; +create table t2 (a int, b int, primary key(a,b)) engine=ndbcluster; +insert into t2 values (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1); +select * from t2 order by a limit 3; +a b +1 1 +2 1 +3 1 +select * from t2; +ERROR 42S02: Table 'test.t2' doesn't exist +show tables like 't2'; +Tables_in_test (t2) +reset master; +create table t2 (a int key) engine=ndbcluster; +insert into t2 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +select * from t2 order by a limit 3; +a +1 +2 +3 +select * from t2 order by a limit 3; +a +1 +2 +3 +reset master; +select * from t2; +ERROR 42S02: Table 'test.t2' doesn't exist +show tables like 't2'; +Tables_in_test (t2) +reset master; +create table t2 (a int key) engine=ndbcluster; +insert into t2 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +select * from t2 order by a limit 3; +a +1 +2 +3 +select * from t2 order by a limit 3; +a +1 +2 +3 +reset master; +drop table t2; diff --git a/mysql-test/r/ndb_basic.result b/mysql-test/r/ndb_basic.result index 9477caf97ab..b8bafd398fe 100644 --- a/mysql-test/r/ndb_basic.result +++ b/mysql-test/r/ndb_basic.result @@ -6,13 +6,6 @@ attr1 INT NOT NULL, attr2 INT, attr3 VARCHAR(10) ) ENGINE=ndbcluster; -drop table t1; -CREATE TABLE t1 ( -pk1 INT NOT NULL PRIMARY KEY, -attr1 INT NOT NULL, -attr2 INT, -attr3 VARCHAR(10) -) ENGINE=ndbcluster; SHOW INDEX FROM t1; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t1 0 PRIMARY 1 pk1 A 0 NULL NULL BTREE @@ -649,30 +642,30 @@ counter datavalue 6 newval 7 newval 8 newval -35 newval -36 newval -37 newval -38 newval -39 newval -40 newval -41 newval -42 newval -43 newval -44 newval -45 newval -46 newval -47 newval -48 newval -49 newval -50 newval -51 newval -52 newval -53 newval -54 newval -55 newval -56 newval -57 newval -58 newval +9 newval +10 newval +11 newval +12 newval +13 newval +14 newval +15 newval +16 newval +17 newval +18 newval +19 newval +20 newval +21 newval +22 newval +23 newval +24 newval +25 newval +26 newval +27 newval +28 newval +29 newval +30 newval +31 newval +32 newval drop table t1; CREATE TABLE t1 ( b INT ) PACK_KEYS = 0 ENGINE = ndb; select * from t1; diff --git a/mysql-test/r/ndb_bitfield.result b/mysql-test/r/ndb_bitfield.result index 9a8c571cfba..59c4d420b22 100644 --- a/mysql-test/r/ndb_bitfield.result +++ b/mysql-test/r/ndb_bitfield.result @@ -9,7 +9,7 @@ t1 CREATE TABLE `t1` ( `pk1` int(11) NOT NULL, `b` bit(64) DEFAULT NULL, PRIMARY KEY (`pk1`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 insert into t1 values (0,b'1111111111111111111111111111111111111111111111111111111111111111'), (1,b'1000000000000000000000000000000000000000000000000000000000000000'), diff --git a/mysql-test/r/ndb_blob.result b/mysql-test/r/ndb_blob.result index 7a781ae3bde..829c67905bf 100644 --- a/mysql-test/r/ndb_blob.result +++ b/mysql-test/r/ndb_blob.result @@ -500,3 +500,69 @@ select count(*) from t1; count(*) 0 drop table t1; +create table t1 ( +a varchar(40) not null, +b mediumint not null, +t text, +c varchar(2) not null, +d bigint not null, +primary key (a,b,c), +key (c,a), +unique key (d) +) engine=ndb; +insert into t1 (a,b,c,d,t) values ('a',1110,'a',1,@v1); +insert into t1 (a,b,c,d,t) values ('b',1110,'a',2,@v2); +insert into t1 (a,b,c,d,t) values ('a',1110,'b',3,@v3); +insert into t1 (a,b,c,d,t) values ('b',1110,'b',4,@v4); +select a,b,c,d,sha1(t) from t1 order by c,a; +a b c d sha1(t) +a 1110 a 1 558a30713786aa72f66abc1e6a521d55aacdeeb5 +b 1110 a 2 b238654911689bfb626a3ef9dba4a1ca074e6a5e +a 1110 b 3 2b6515f29c20b8e9e17cc597527e516c0de8d612 +b 1110 b 4 NULL +select a,b,c,d,sha1(t) from t1 where a='a' and b=1110 and c='a'; +a b c d sha1(t) +a 1110 a 1 558a30713786aa72f66abc1e6a521d55aacdeeb5 +select a,b,c,d,sha1(t) from t1 where a='a' and b=1110 and c='b'; +a b c d sha1(t) +a 1110 b 3 2b6515f29c20b8e9e17cc597527e516c0de8d612 +update t1 set t=@v4 where a='b' and b=1110 and c='a'; +update t1 set t=@v2 where a='b' and b=1110 and c='b'; +select a,b,c,d,sha1(t) from t1 order by c,a; +a b c d sha1(t) +a 1110 a 1 558a30713786aa72f66abc1e6a521d55aacdeeb5 +b 1110 a 2 NULL +a 1110 b 3 2b6515f29c20b8e9e17cc597527e516c0de8d612 +b 1110 b 4 b238654911689bfb626a3ef9dba4a1ca074e6a5e +update t1 set t=@v2 where d=2; +update t1 set t=@v4 where d=4; +select a,b,c,d,sha1(t) from t1 order by c,a; +a b c d sha1(t) +a 1110 a 1 558a30713786aa72f66abc1e6a521d55aacdeeb5 +b 1110 a 2 b238654911689bfb626a3ef9dba4a1ca074e6a5e +a 1110 b 3 2b6515f29c20b8e9e17cc597527e516c0de8d612 +b 1110 b 4 NULL +update t1 set t=@v4 where a='b' and c='a'; +update t1 set t=@v2 where a='b' and c='b'; +select a,b,c,d,sha1(t) from t1 order by c,a; +a b c d sha1(t) +a 1110 a 1 558a30713786aa72f66abc1e6a521d55aacdeeb5 +b 1110 a 2 NULL +a 1110 b 3 2b6515f29c20b8e9e17cc597527e516c0de8d612 +b 1110 b 4 b238654911689bfb626a3ef9dba4a1ca074e6a5e +update t1 set t=@v2 where b+d=1112; +update t1 set t=@v4 where b+d=1114; +select a,b,c,d,sha1(t) from t1 order by c,a; +a b c d sha1(t) +a 1110 a 1 558a30713786aa72f66abc1e6a521d55aacdeeb5 +b 1110 a 2 b238654911689bfb626a3ef9dba4a1ca074e6a5e +a 1110 b 3 2b6515f29c20b8e9e17cc597527e516c0de8d612 +b 1110 b 4 NULL +delete from t1 where a='a' and b=1110 and c='a'; +delete from t1 where a='b' and c='a'; +delete from t1 where d=3; +delete from t1 where b+d=1114; +select count(*) from t1; +count(*) +0 +drop table t1; diff --git a/mysql-test/r/ndb_blob_partition.result b/mysql-test/r/ndb_blob_partition.result deleted file mode 100644 index b08a91f0cdd..00000000000 --- a/mysql-test/r/ndb_blob_partition.result +++ /dev/null @@ -1,104 +0,0 @@ -drop table if exists t1; -create table t1 ( -a mediumint not null, -b text not null, -c int not null, -d longblob, -primary key using hash (a,c), -unique key (c) -) -engine=ndb -partition by range (c) -partitions 3 -( partition p1 values less than (200), -partition p2 values less than (300), -partition p3 values less than (400)); -insert into t1 values (1, @v1, 101, @v2); -insert into t1 values (1, @v2, 102, @v3); -insert into t1 values (1, @v3, 103, @v4); -insert into t1 values (2, @v4, 201, @v5); -insert into t1 values (2, @v5, 202, @v6); -insert into t1 values (2, @v6, 203, @v7); -insert into t1 values (3, @v7, 301, @v8); -insert into t1 values (3, @v8, 302, @v9); -insert into t1 values (3, @v9, 303, @v1); -select a, sha1(b), c, sha1(d) from t1 order by a; -a sha1(b) c sha1(d) -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 101 10d3c783026b310218d10b7188da96a2401648c6 -1 10d3c783026b310218d10b7188da96a2401648c6 102 a33549d9844092289a58ac348dd59f09fc28406a -1 a33549d9844092289a58ac348dd59f09fc28406a 103 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 201 70fc9a7d08beebc522258bfb02000a30c77a8f1d -2 70fc9a7d08beebc522258bfb02000a30c77a8f1d 202 090565c580809efed3d369481a4bbb168b20713e -2 090565c580809efed3d369481a4bbb168b20713e 203 1e0070bec426871a46291de27b9bd6e4255ab4e5 -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 301 acbaba01bc2e682f015f40e79d9cbe475db3002e -3 acbaba01bc2e682f015f40e79d9cbe475db3002e 302 9ee30d99162574f79c66ae95cdf132dcf9cbc259 -3 9ee30d99162574f79c66ae95cdf132dcf9cbc259 303 1d42dd9090cf78314a06665d4ea938c35cc760f4 -select a, sha1(b), c, sha1(d) from t1 where a = 1 and c = 101; -a sha1(b) c sha1(d) -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 101 10d3c783026b310218d10b7188da96a2401648c6 -select a, sha1(b), c, sha1(d) from t1 where a = 2 and c = 201; -a sha1(b) c sha1(d) -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 201 70fc9a7d08beebc522258bfb02000a30c77a8f1d -select a, sha1(b), c, sha1(d) from t1 where a = 3 and c = 301; -a sha1(b) c sha1(d) -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 301 acbaba01bc2e682f015f40e79d9cbe475db3002e -update t1 set b = @v3, d = @v4 where a = 1 and c = 102; -update t1 set b = @v6, d = @v7 where a = 2 and c = 202; -update t1 set b = @v9, d = @v1 where a = 3 and c = 302; -select a, sha1(b), c, sha1(d) from t1 order by a; -a sha1(b) c sha1(d) -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 101 10d3c783026b310218d10b7188da96a2401648c6 -1 a33549d9844092289a58ac348dd59f09fc28406a 102 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c -1 a33549d9844092289a58ac348dd59f09fc28406a 103 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 201 70fc9a7d08beebc522258bfb02000a30c77a8f1d -2 090565c580809efed3d369481a4bbb168b20713e 202 1e0070bec426871a46291de27b9bd6e4255ab4e5 -2 090565c580809efed3d369481a4bbb168b20713e 203 1e0070bec426871a46291de27b9bd6e4255ab4e5 -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 301 acbaba01bc2e682f015f40e79d9cbe475db3002e -3 9ee30d99162574f79c66ae95cdf132dcf9cbc259 302 1d42dd9090cf78314a06665d4ea938c35cc760f4 -3 9ee30d99162574f79c66ae95cdf132dcf9cbc259 303 1d42dd9090cf78314a06665d4ea938c35cc760f4 -update t1 set b = @v4, d = @v5 where c = 103; -update t1 set b = @v7, d = @v8 where c = 203; -update t1 set b = @v1, d = @v2 where c = 303; -select a, sha1(b), c, sha1(d) from t1 order by a; -a sha1(b) c sha1(d) -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 101 10d3c783026b310218d10b7188da96a2401648c6 -1 a33549d9844092289a58ac348dd59f09fc28406a 102 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c -1 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 103 70fc9a7d08beebc522258bfb02000a30c77a8f1d -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 201 70fc9a7d08beebc522258bfb02000a30c77a8f1d -2 090565c580809efed3d369481a4bbb168b20713e 202 1e0070bec426871a46291de27b9bd6e4255ab4e5 -2 1e0070bec426871a46291de27b9bd6e4255ab4e5 203 acbaba01bc2e682f015f40e79d9cbe475db3002e -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 301 acbaba01bc2e682f015f40e79d9cbe475db3002e -3 9ee30d99162574f79c66ae95cdf132dcf9cbc259 302 1d42dd9090cf78314a06665d4ea938c35cc760f4 -3 1d42dd9090cf78314a06665d4ea938c35cc760f4 303 10d3c783026b310218d10b7188da96a2401648c6 -update t1 set b = @v5, d = @v6; -select a, sha1(b), c, sha1(d) from t1 order by a; -a sha1(b) c sha1(d) -1 70fc9a7d08beebc522258bfb02000a30c77a8f1d 101 090565c580809efed3d369481a4bbb168b20713e -1 70fc9a7d08beebc522258bfb02000a30c77a8f1d 102 090565c580809efed3d369481a4bbb168b20713e -1 70fc9a7d08beebc522258bfb02000a30c77a8f1d 103 090565c580809efed3d369481a4bbb168b20713e -2 70fc9a7d08beebc522258bfb02000a30c77a8f1d 201 090565c580809efed3d369481a4bbb168b20713e -2 70fc9a7d08beebc522258bfb02000a30c77a8f1d 202 090565c580809efed3d369481a4bbb168b20713e -2 70fc9a7d08beebc522258bfb02000a30c77a8f1d 203 090565c580809efed3d369481a4bbb168b20713e -3 70fc9a7d08beebc522258bfb02000a30c77a8f1d 301 090565c580809efed3d369481a4bbb168b20713e -3 70fc9a7d08beebc522258bfb02000a30c77a8f1d 302 090565c580809efed3d369481a4bbb168b20713e -3 70fc9a7d08beebc522258bfb02000a30c77a8f1d 303 090565c580809efed3d369481a4bbb168b20713e -update t1 set b = @v1, d = @v2 where 100 < c and c < 200; -update t1 set b = @v4, d = @v5 where 200 < c and c < 300; -update t1 set b = @v7, d = @v8 where 300 < c and c < 400; -select a, sha1(b), c, sha1(d) from t1 order by a; -a sha1(b) c sha1(d) -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 101 10d3c783026b310218d10b7188da96a2401648c6 -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 102 10d3c783026b310218d10b7188da96a2401648c6 -1 1d42dd9090cf78314a06665d4ea938c35cc760f4 103 10d3c783026b310218d10b7188da96a2401648c6 -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 201 70fc9a7d08beebc522258bfb02000a30c77a8f1d -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 202 70fc9a7d08beebc522258bfb02000a30c77a8f1d -2 daa61c6de36a0526f0d47dc29d6b9de7e6d2630c 203 70fc9a7d08beebc522258bfb02000a30c77a8f1d -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 301 acbaba01bc2e682f015f40e79d9cbe475db3002e -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 302 acbaba01bc2e682f015f40e79d9cbe475db3002e -3 1e0070bec426871a46291de27b9bd6e4255ab4e5 303 acbaba01bc2e682f015f40e79d9cbe475db3002e -delete from t1 where a = 1 and c = 101; -delete from t1 where c = 102; -delete from t1; -select a, sha1(b), c, sha1(d) from t1 order by a; -a sha1(b) c sha1(d) -drop table t1; diff --git a/mysql-test/r/ndb_dd_basic.result b/mysql-test/r/ndb_dd_basic.result index ee1b717e6f1..008be3aa79f 100644 --- a/mysql-test/r/ndb_dd_basic.result +++ b/mysql-test/r/ndb_dd_basic.result @@ -49,7 +49,7 @@ t1 CREATE TABLE `t1` ( `b` int(11) NOT NULL, `c` int(11) NOT NULL, PRIMARY KEY (`pk1`) -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES (0, 0, 0); SELECT * FROM t1; pk1 b c diff --git a/mysql-test/r/ndb_dd_disk2memory.result b/mysql-test/r/ndb_dd_disk2memory.result index 80056b7269d..bd5bbda42f3 100644 --- a/mysql-test/r/ndb_dd_disk2memory.result +++ b/mysql-test/r/ndb_dd_disk2memory.result @@ -218,7 +218,7 @@ t2 CREATE TABLE `t2` ( `b2` int(11) NOT NULL, `c2` int(11) NOT NULL, PRIMARY KEY (`pk2`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 SHOW CREATE TABLE test.t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -226,7 +226,7 @@ t1 CREATE TABLE `t1` ( `b` int(11) NOT NULL, `c` int(11) NOT NULL, PRIMARY KEY (`pk1`) -) TABLESPACE table_space1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) TABLESPACE table_space1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 ALTER TABLE test.t2 TABLESPACE table_space1 STORAGE DISK ENGINE=NDB; SHOW CREATE TABLE test.t2; @@ -236,7 +236,7 @@ t2 CREATE TABLE `t2` ( `b2` int(11) NOT NULL, `c2` int(11) NOT NULL, PRIMARY KEY (`pk2`) -) TABLESPACE table_space1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) TABLESPACE table_space1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 ALTER TABLE test.t1 ENGINE=NDBCLUSTER; SHOW CREATE TABLE test.t1; Table Create Table @@ -245,7 +245,7 @@ t1 CREATE TABLE `t1` ( `b` int(11) NOT NULL, `c` int(11) NOT NULL, PRIMARY KEY (`pk1`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 DROP TABLE test.t1; DROP TABLE test.t2; diff --git a/mysql-test/r/ndb_gis.result b/mysql-test/r/ndb_gis.result index f9b9ceb76a5..67447a69242 100644 --- a/mysql-test/r/ndb_gis.result +++ b/mysql-test/r/ndb_gis.result @@ -14,7 +14,7 @@ gis_point CREATE TABLE `gis_point` ( `fid` int(11) NOT NULL AUTO_INCREMENT, `g` point DEFAULT NULL, PRIMARY KEY (`fid`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 SHOW FIELDS FROM gis_point; Field Type Null Key Default Extra fid int(11) NO PRI NULL auto_increment @@ -476,7 +476,7 @@ gis_point CREATE TABLE `gis_point` ( `fid` int(11) NOT NULL AUTO_INCREMENT, `g` point DEFAULT NULL, PRIMARY KEY (`fid`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 SHOW FIELDS FROM gis_point; Field Type Null Key Default Extra fid int(11) NO PRI NULL auto_increment diff --git a/mysql-test/r/ndb_partition_error.result b/mysql-test/r/ndb_partition_error.result deleted file mode 100644 index d86dc382185..00000000000 --- a/mysql-test/r/ndb_partition_error.result +++ /dev/null @@ -1,47 +0,0 @@ -drop table if exists t1; -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(a,b), -index (a)) -engine = ndb -partition by range (a) -partitions 3 -(partition x1 values less than (5) nodegroup 12, -partition x2 values less than (10) nodegroup 13, -partition x3 values less than (20) nodegroup 14); -ERROR HY000: Can't create table 'test.t1' (errno: 140) -show warnings; -Level Code Message -Error 1296 Got error 771 'Given NODEGROUP doesn't exist in this cluster' from NDB -Error 1005 Can't create table 'test.t1' (errno: 140) -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(a)) -engine = ndb -partition by range (a) -partitions 3 -(partition x1 values less than (5), -partition x2 values less than (10), -partition x3 values less than (20)); -drop table t1; -CREATE TABLE t1 (id INT) ENGINE=NDB -PARTITION BY LIST(id) -(PARTITION p0 VALUES IN (2, 4), -PARTITION p1 VALUES IN (42, 142)); -INSERT INTO t1 VALUES (2); -UPDATE t1 SET id=5 WHERE id=2; -ERROR HY000: Table has no partition for value 5 -DROP TABLE t1; -create table t1 (a int,b int, c int) -engine = ndb -partition by list(a) -partitions 2 -(partition x123 values in (11, 12), -partition x234 values in (5, 1)); -insert into t1 values (NULL,1,1); -ERROR HY000: Table has no partition for value NULL -drop table t1; diff --git a/mysql-test/r/ndb_partition_key.result b/mysql-test/r/ndb_partition_key.result deleted file mode 100644 index 45afcec94c7..00000000000 --- a/mysql-test/r/ndb_partition_key.result +++ /dev/null @@ -1,199 +0,0 @@ -DROP TABLE IF EXISTS t1; -CREATE TABLE t1 (a int, b int, c int, d int, PRIMARY KEY(a,b,c)) -ENGINE = NDB -PARTITION BY KEY (a,b); -insert into t1 values (1,1,1,1); -select * from t1; -a b c d -1 1 1 1 -update t1 set d = 2 where a = 1 and b = 1 and c = 1; -select * from t1; -a b c d -1 1 1 2 -delete from t1; -select * from t1; -a b c d -drop table t1; -CREATE TABLE t1 (a int, b int, c int, d int, PRIMARY KEY(a,b)) -ENGINE = NDB -PARTITION BY KEY (c); -ERROR HY000: A PRIMARY KEY need to include all fields in the partition function -CREATE TABLE t1 (a int, b int, c int, PRIMARY KEY(a,b)) -ENGINE = NDB -PARTITION BY KEY (a); -insert into t1 values -(1,1,3),(1,2,3),(1,3,3),(1,4,3),(1,5,3),(1,6,3), -(1,7,3),(1,8,3),(1,9,3),(1,10,3),(1,11,3),(1,12,3); -select * from t1 order by b; -a b c -1 1 3 -1 2 3 -1 3 3 -1 4 3 -1 5 3 -1 6 3 -1 7 3 -1 8 3 -1 9 3 -1 10 3 -1 11 3 -1 12 3 -DROP TABLE t1; -CREATE TABLE t1 (a INT, b CHAR(10) COLLATE latin1_bin, c INT, d INT, -PRIMARY KEY (a,b,c) USING HASH) -ENGINE=NDB -DEFAULT CHARSET=latin1 -PARTITION BY KEY (b); -insert into t1 values (1,"a",1,1),(2,"a",1,1),(3,"a",1,1); --- t1 -- - -Fragment type: 5 -K Value: 6 -Min load factor: 78 -Max load factor: 80 -Temporary table: no -Number of attributes: 4 -Number of primary keys: 3 -Length of frm data: # -Row Checksum: 1 -Row GCI: 1 -TableStatus: Retrieved --- Attributes -- -a Int PRIMARY KEY AT=FIXED ST=MEMORY -b Char(10;latin1_bin) PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY -c Int PRIMARY KEY AT=FIXED ST=MEMORY -d Int NULL AT=FIXED ST=MEMORY - --- Indexes -- -PRIMARY KEY(a, b, c) - UniqueHashIndex - - -NDBT_ProgramExit: 0 - OK - -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) NOT NULL DEFAULT '0', - `b` char(10) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '', - `c` int(11) NOT NULL DEFAULT '0', - `d` int(11) DEFAULT NULL, - PRIMARY KEY (`a`,`b`,`c`) USING HASH -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (b) -DROP TABLE t1; -CREATE TABLE t1 (a int not null primary key) -PARTITION BY KEY(a) -(PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); -drop table t1; -CREATE TABLE t1 (a int not null primary key); -ALTER TABLE t1 -PARTITION BY KEY(a) -(PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); -drop table t1; -create table t1 (a int) -engine=ndb -partition by key(a) -(partition p0, partition p1); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -alter table t1 engine=heap; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) -alter table t1 engine=ndb; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -alter table t1 engine=heap remove partitioning; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 -alter table t1 engine=ndb -partition by key(a) -(partition p0, partition p1 engine = ndb); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -alter table t1 -partition by key (a) -(partition p0 engine=ndb, partition p1 engine=ndb); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -alter table t1 remove partitioning; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () -alter table t1 -partition by key(a) -(partition p0 engine=ndb, partition p1); -ERROR HY000: The mix of handlers in the partitions is not allowed in this version of MySQL -alter table t1 -engine=ndb -partition by key(a) -(partition p0 engine=ndb, partition p1 engine = ndb); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -drop table t1; -CREATE TABLE t1 ( -c1 MEDIUMINT NOT NULL AUTO_INCREMENT, -c2 TEXT NOT NULL, -c3 INT NOT NULL, -c4 BIT NOT NULL, -c5 FLOAT, -c6 VARCHAR(255), -c7 TIMESTAMP, -PRIMARY KEY(c1,c3)) -ENGINE=NDB -PARTITION BY KEY(c3) PARTITIONS 5; -ALTER TABLE t1 COALESCE PARTITION 4; -DROP TABLE t1; -CREATE TABLE t1 (a int primary key) -ENGINE=NDB -PARTITION BY KEY(a); -ALTER TABLE t1 OPTIMIZE PARTITION p0; -ERROR HY000: Table storage engine for 't1' doesn't have this option -ALTER TABLE t1 CHECK PARTITION p0; -ERROR HY000: Table storage engine for 't1' doesn't have this option -ALTER TABLE t1 REPAIR PARTITION p0; -ERROR HY000: Table storage engine for 't1' doesn't have this option -ALTER TABLE t1 ANALYZE PARTITION p0; -ERROR HY000: Table storage engine for 't1' doesn't have this option -ALTER TABLE t1 REBUILD PARTITION p0; -ERROR HY000: Table storage engine for 't1' doesn't have this option -DROP TABLE t1; -CREATE TABLE t1 ( -c1 MEDIUMINT NOT NULL AUTO_INCREMENT, -c2 TEXT NOT NULL, -c3 INT NOT NULL, -PRIMARY KEY(c1,c3)) -ENGINE=NDB -PARTITION BY KEY(c3) PARTITIONS 5; -ALTER TABLE t1 ADD COLUMN c4 INT AFTER c1; -DROP TABLE t1; -CREATE TABLE t1 ( -c1 MEDIUMINT NOT NULL AUTO_INCREMENT, -c2 TEXT NOT NULL, -c3 INT NOT NULL, -PRIMARY KEY(c1,c3)) -ENGINE=NDB -PARTITION BY KEY(c3); -ALTER TABLE t1 ADD COLUMN c4 INT AFTER c1; -DROP TABLE t1; diff --git a/mysql-test/r/ndb_partition_list.result b/mysql-test/r/ndb_partition_list.result deleted file mode 100644 index ce2574ddcc4..00000000000 --- a/mysql-test/r/ndb_partition_list.result +++ /dev/null @@ -1,51 +0,0 @@ -drop table if exists t1; -CREATE TABLE t1 ( f_int1 INTEGER NOT NULL, f_int2 INTEGER NOT NULL, -f_char1 CHAR(10), -f_char2 CHAR(10), f_charbig VARCHAR(1000), -PRIMARY KEY (f_int1,f_int2)) -ENGINE = NDB -PARTITION BY LIST(MOD(f_int1 + f_int2,4)) -(PARTITION part_3 VALUES IN (-3), -PARTITION part_2 VALUES IN (-2), -PARTITION part_1 VALUES IN (-1), -PARTITION part0 VALUES IN (0), -PARTITION part1 VALUES IN (1), -PARTITION part2 VALUES IN (2), -PARTITION part3 VALUES IN (3,4,5)); -INSERT INTO t1 SET f_int1 = -2, f_int2 = 20, f_char1 = '20', f_char2 = '20', f_charbig = '===20==='; -INSERT INTO t1 SET f_int1 = 1, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 2, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 3, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 4, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 5, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 20, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -SELECT * FROM t1 ORDER BY f_int1; -f_int1 f_int2 f_char1 f_char2 f_charbig --2 20 20 20 ===20=== -1 1 1 1 ===1=== -2 1 1 1 ===1=== -3 1 1 1 ===1=== -4 1 1 1 ===1=== -5 1 1 1 ===1=== -20 1 1 1 ===1=== -DROP TABLE t1; -CREATE TABLE t1 ( f_int1 INTEGER, f_int2 INTEGER, f_char1 CHAR(10), -f_char2 CHAR(10), f_charbig VARCHAR(1000)) -ENGINE = NDB -PARTITION BY LIST(f_int1) -(PARTITION part_1 VALUES IN (-1), -PARTITION part0 VALUES IN (0,1), -PARTITION part1 VALUES IN (2)); -INSERT INTO t1 SET f_int1 = -1, f_int2 = 20, f_char1 = '20', f_char2 = '20', f_charbig = '===20==='; -INSERT INTO t1 SET f_int1 = 0, f_int2 = 20, f_char1 = '20', f_char2 = '20', f_charbig = '===20==='; -INSERT INTO t1 SET f_int1 = 1, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 2, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 20, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -ERROR HY000: Table has no partition for value 20 -SELECT * FROM t1 ORDER BY f_int1; -f_int1 f_int2 f_char1 f_char2 f_charbig --1 20 20 20 ===20=== -0 20 20 20 ===20=== -1 1 1 1 ===1=== -2 1 1 1 ===1=== -DROP TABLE t1; diff --git a/mysql-test/r/ndb_partition_range.result b/mysql-test/r/ndb_partition_range.result deleted file mode 100644 index cb79f04873e..00000000000 --- a/mysql-test/r/ndb_partition_range.result +++ /dev/null @@ -1,263 +0,0 @@ -drop table if exists t1; -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(a,b), -index (a)) -engine = ndb -partition by range (a) -partitions 3 -(partition x1 values less than (5), -partition x2 values less than (10), -partition x3 values less than (20)); -INSERT into t1 values (1, 1, 1); -INSERT into t1 values (6, 1, 1); -INSERT into t1 values (10, 1, 1); -INSERT into t1 values (15, 1, 1); -select * from information_schema.partitions where table_name= 't1'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t1 x1 NULL 1 NULL RANGE NULL a NULL 5 0 0 0 # 0 0 # # NULL NULL default 0 default -NULL test t1 x2 NULL 2 NULL RANGE NULL a NULL 10 0 0 0 # 0 0 # # NULL NULL default 0 default -NULL test t1 x3 NULL 3 NULL RANGE NULL a NULL 20 0 0 0 # 0 0 # # NULL NULL default 0 default -select * from t1 order by a; -a b c -1 1 1 -6 1 1 -10 1 1 -15 1 1 -select * from t1 where a=1 order by a; -a b c -1 1 1 -select * from t1 where a=15 and b=1 order by a; -a b c -15 1 1 -select * from t1 where a=21 and b=1 order by a; -a b c -select * from t1 where a=21 order by a; -a b c -select * from t1 where a in (1,6,10,21) order by a; -a b c -1 1 1 -6 1 1 -10 1 1 -select * from t1 where b=1 and a in (1,6,10,21) order by a; -a b c -1 1 1 -6 1 1 -10 1 1 -drop table t1; -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(b), -unique (a)) -engine = ndb -partition by range (b) -partitions 3 -(partition x1 values less than (5), -partition x2 values less than (10), -partition x3 values less than (20)); -INSERT into t1 values (1, 1, 1); -INSERT into t1 values (2, 6, 1); -INSERT into t1 values (3, 10, 1); -INSERT into t1 values (4, 15, 1); -select * from t1 order by a; -a b c -1 1 1 -2 6 1 -3 10 1 -4 15 1 -UPDATE t1 set a = 5 WHERE b = 15; -select * from t1 order by a; -a b c -1 1 1 -2 6 1 -3 10 1 -5 15 1 -UPDATE t1 set a = 6 WHERE a = 5; -select * from t1 order by a; -a b c -1 1 1 -2 6 1 -3 10 1 -6 15 1 -select * from t1 where b=1 order by b; -a b c -1 1 1 -select * from t1 where b=15 and a=1 order by b; -a b c -select * from t1 where b=21 and a=1 order by b; -a b c -select * from t1 where b=21 order by b; -a b c -select * from t1 where b in (1,6,10,21) order by b; -a b c -1 1 1 -2 6 1 -3 10 1 -select * from t1 where a in (1,2,5,6) order by b; -a b c -1 1 1 -2 6 1 -6 15 1 -select * from t1 where a=1 and b in (1,6,10,21) order by b; -a b c -1 1 1 -DELETE from t1 WHERE b = 6; -DELETE from t1 WHERE a = 6; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` int(11) NOT NULL, - `b` int(11) NOT NULL, - `c` int(11) NOT NULL, - PRIMARY KEY (`b`), - UNIQUE KEY `a` (`a`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (b) (PARTITION x1 VALUES LESS THAN (5) ENGINE = ndbcluster, PARTITION x2 VALUES LESS THAN (10) ENGINE = ndbcluster, PARTITION x3 VALUES LESS THAN (20) ENGINE = ndbcluster) -drop table t1; -CREATE TABLE t1 -(id MEDIUMINT NOT NULL, -b1 BIT(8), -vc VARCHAR(255), -bc CHAR(255), -d DECIMAL(10,4) DEFAULT 0, -f FLOAT DEFAULT 0, -total BIGINT UNSIGNED, -y YEAR, -t DATE) ENGINE=NDB -PARTITION BY RANGE (YEAR(t)) -(PARTITION p0 VALUES LESS THAN (1901), -PARTITION p1 VALUES LESS THAN (1946), -PARTITION p2 VALUES LESS THAN (1966), -PARTITION p3 VALUES LESS THAN (1986), -PARTITION p4 VALUES LESS THAN (2005), -PARTITION p5 VALUES LESS THAN MAXVALUE); -INSERT INTO t1 VALUES (0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -SELECT * FROM t1; -id b1 vc bc d f total y t -0 NULL NULL NULL NULL NULL NULL NULL NULL -ALTER TABLE t1 ENGINE=MYISAM; -SELECT * FROM t1; -id b1 vc bc d f total y t -0 NULL NULL NULL NULL NULL NULL NULL NULL -DROP TABLE t1; -CREATE LOGFILE GROUP lg1 -ADD UNDOFILE 'undofile.dat' - INITIAL_SIZE 16M -UNDO_BUFFER_SIZE=1M -ENGINE=NDB; -CREATE TABLESPACE ts1 -ADD DATAFILE 'datafile.dat' - USE LOGFILE GROUP lg1 -INITIAL_SIZE 12M -ENGINE NDB; -CREATE TABLE test.t1 ( -a1 INT, -a2 TEXT NOT NULL, -a3 BIT NOT NULL, -a4 DECIMAL(8,3), -a5 INT NOT NULL, -a6 INT, -PRIMARY KEY(a1)) -TABLESPACE ts1 STORAGE DISK ENGINE=NDB -PARTITION BY LIST (a1) -(PARTITION p0 VALUES IN (1,2,3,4,5), -PARTITION p1 VALUES IN (6,7,8,9, 10), -PARTITION p2 VALUES IN (11, 12, 13, 14, 15)); -ALTER TABLE test.t1 DROP COLUMN a6; -ALTER TABLE test.t1 ADD COLUMN a6 VARCHAR(255); -SELECT COUNT(*) FROM test.t1; -COUNT(*) -15 -ALTER TABLE test.t1 DROP COLUMN a4; -SELECT COUNT(*) FROM test.t1; -COUNT(*) -15 -DROP TABLE t1; -CREATE TABLE test.t1 ( -a1 INT, -a2 TEXT NOT NULL, -a3 BIT NOT NULL, -a4 DECIMAL(8,3), -a5 INT NOT NULL, -a6 VARCHAR(255), -PRIMARY KEY(a1)) -TABLESPACE ts1 STORAGE DISK ENGINE=NDB -PARTITION BY HASH(a1) -PARTITIONS 4; -SELECT COUNT(*) FROM test.t1; -COUNT(*) -15 -ALTER TABLE test.t1 DROP COLUMN a4; -SELECT COUNT(*) FROM test.t1; -COUNT(*) -15 -DROP TABLE t1; -ALTER TABLESPACE ts1 -DROP DATAFILE 'datafile.dat' -ENGINE=NDB; -DROP TABLESPACE ts1 ENGINE=NDB; -DROP LOGFILE GROUP lg1 ENGINE=NDB; -CREATE TABLE t1 -(id MEDIUMINT NOT NULL, -b1 BIT(8), -vc VARCHAR(255), -bc CHAR(255), -d DECIMAL(10,4) DEFAULT 0, -f FLOAT DEFAULT 0, -total BIGINT UNSIGNED, -y YEAR, -t DATE) ENGINE=NDB -PARTITION BY LIST(id) -(PARTITION p0 VALUES IN (2, 4), -PARTITION p1 VALUES IN (42, 142)); -INSERT INTO t1 VALUES (2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -SELECT * FROM t1; -id b1 vc bc d f total y t -2 NULL NULL NULL NULL NULL NULL NULL NULL -ALTER TABLE t1 ADD PARTITION -(PARTITION p2 VALUES IN (412)); -SELECT * FROM t1; -id b1 vc bc d f total y t -2 NULL NULL NULL NULL NULL NULL NULL NULL -DROP TABLE t1; -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null) -partition by list(a) -partitions 2 -(partition x123 values in (1,5,6), -partition x234 values in (4,7,8)); -INSERT into t1 VALUES (5,1,1); -select * from t1; -a b c -5 1 1 -UPDATE t1 SET a=8 WHERE a=5 AND b=1; -select * from t1; -a b c -8 1 1 -drop table t1; -CREATE TABLE t1 ( f1 INTEGER, f2 char(20)) engine=ndb -PARTITION BY RANGE(f1) -( PARTITION part1 VALUES LESS THAN (2), -PARTITION part2 VALUES LESS THAN (1000)); -INSERT INTO t1 VALUES(1, '---1---'); -INSERT INTO t1 VALUES(2, '---2---'); -select * from t1 order by f1; -f1 f2 -1 ---1--- -2 ---2--- -UPDATE t1 SET f1 = f1 + 4 WHERE f1 = 2; -select * from t1 order by f1; -f1 f2 -1 ---1--- -6 ---2--- -UPDATE t1 SET f1 = f1 + 4 WHERE f1 = 1; -select * from t1 order by f1; -f1 f2 -5 ---1--- -6 ---2--- -drop table t1; diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index e471b5a3afa..7cbe91b3753 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -1,5 +1,6 @@ prepare stmt1 from ' show full processlist '; execute stmt1; Id User Host db Command Time State Info +number event_scheduler localhost NULL Connect time Suspended NULL number root localhost test Query time NULL show full processlist deallocate prepare stmt1; diff --git a/mysql-test/r/outfile.result b/mysql-test/r/outfile.result Binary files differindex ee8c3249c9d..040dff576f8 100644 --- a/mysql-test/r/outfile.result +++ b/mysql-test/r/outfile.result diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 9a430ed39f6..8bf86056e24 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1,4 +1,8 @@ drop table if exists t1; +create table t1 (a int) +partition by key(a) +(partition p0 engine = MEMORY); +drop table t1; CREATE TABLE t1 ( a int not null, b int not null, @@ -896,5 +900,50 @@ alter table t1 rebuild partition p1; alter table t1 rebuild partition p1; alter table t1 rebuild partition p1; alter table t1 rebuild partition p1; +create table t1 (a int) engine=memory +partition by key(a); +insert into t1 values (1); +create index inx1 on t1(a); +drop table t1; +create table t1 (a int) +partition by key (a) +(partition p0 engine = MERGE); +ERROR HY000: MyISAM Merge handler cannot be used in partitioned tables +create table t1 (a varchar(1)) +partition by key (a) +as select 'a'; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(1) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) +drop table t1; +CREATE TABLE t1 (a int) ENGINE = MYISAM PARTITION BY KEY(a); +INSERT into t1 values (1), (2); +SHOW TABLE STATUS; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 10 Fixed 2 7 14 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL partitioned +DELETE from t1 where a = 1; +SHOW TABLE STATUS; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 10 Fixed 1 14 14 0 0 7 NULL NULL NULL NULL latin1_swedish_ci NULL partitioned +ALTER TABLE t1 OPTIMIZE PARTITION p0; +SHOW TABLE STATUS; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 10 Fixed 1 7 7 0 1024 0 NULL NULL NULL NULL latin1_swedish_ci NULL partitioned +DROP TABLE t1; +CREATE TABLE t1 (a int, index(a)) PARTITION BY KEY(a); +ALTER TABLE t1 DISABLE KEYS; +ALTER TABLE t1 ENABLE KEYS; +DROP TABLE t1; +create table t1 (a int) +engine=MEMORY +partition by key (a); +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair note The storage engine for the table doesn't support repair +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize note The storage engine for the table doesn't support optimize drop table t1; End of 5.1 tests diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result index d0b773dfe34..1a1d6432411 100644 --- a/mysql-test/r/ps_1general.result +++ b/mysql-test/r/ps_1general.result @@ -299,7 +299,7 @@ t9 MyISAM 10 Dynamic 2 216 432 # 2048 0 NULL # # # latin1_swedish_ci NULL prepare stmt4 from ' show status like ''Threads_running'' '; execute stmt4; Variable_name Value -Threads_running 1 +Threads_running 2 prepare stmt4 from ' show variables like ''sql_mode'' '; execute stmt4; Variable_name Value diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result index ffe2b4d6409..5ba601bf305 100644 --- a/mysql-test/r/ps_2myisam.result +++ b/mysql-test/r/ps_2myisam.result @@ -1777,7 +1777,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL DEFAULT '0', `param01` bigint(20) DEFAULT NULL, - `const02` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `const02` decimal(2,1) NOT NULL DEFAULT '0.0', `param02` decimal(65,30) DEFAULT NULL, `const03` double NOT NULL DEFAULT '0', `param03` double DEFAULT NULL, @@ -1807,7 +1807,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result index 707e5ed681f..836fb7b58c2 100644 --- a/mysql-test/r/ps_3innodb.result +++ b/mysql-test/r/ps_3innodb.result @@ -1760,7 +1760,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL DEFAULT '0', `param01` bigint(20) DEFAULT NULL, - `const02` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `const02` decimal(2,1) NOT NULL DEFAULT '0.0', `param02` decimal(65,30) DEFAULT NULL, `const03` double NOT NULL DEFAULT '0', `param03` double DEFAULT NULL, @@ -1790,7 +1790,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result index 06429f92580..150820d92f0 100644 --- a/mysql-test/r/ps_4heap.result +++ b/mysql-test/r/ps_4heap.result @@ -1761,7 +1761,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL DEFAULT '0', `param01` bigint(20) DEFAULT NULL, - `const02` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `const02` decimal(2,1) NOT NULL DEFAULT '0.0', `param02` decimal(65,30) DEFAULT NULL, `const03` double NOT NULL DEFAULT '0', `param03` double DEFAULT NULL, @@ -1791,7 +1791,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result index c1002970932..72573ce3294 100644 --- a/mysql-test/r/ps_5merge.result +++ b/mysql-test/r/ps_5merge.result @@ -1697,7 +1697,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL DEFAULT '0', `param01` bigint(20) DEFAULT NULL, - `const02` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `const02` decimal(2,1) NOT NULL DEFAULT '0.0', `param02` decimal(65,30) DEFAULT NULL, `const03` double NOT NULL DEFAULT '0', `param03` double DEFAULT NULL, @@ -1727,7 +1727,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 @@ -4711,7 +4711,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL DEFAULT '0', `param01` bigint(20) DEFAULT NULL, - `const02` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `const02` decimal(2,1) NOT NULL DEFAULT '0.0', `param02` decimal(65,30) DEFAULT NULL, `const03` double NOT NULL DEFAULT '0', `param03` double DEFAULT NULL, @@ -4741,7 +4741,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 diff --git a/mysql-test/r/ps_6bdb.result b/mysql-test/r/ps_6bdb.result index ea1212addf5..44b4ebf33d4 100644 --- a/mysql-test/r/ps_6bdb.result +++ b/mysql-test/r/ps_6bdb.result @@ -1760,7 +1760,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL DEFAULT '0', `param01` bigint(20) DEFAULT NULL, - `const02` decimal(2,1) unsigned NOT NULL DEFAULT '0.0', + `const02` decimal(2,1) NOT NULL DEFAULT '0.0', `param02` decimal(65,30) DEFAULT NULL, `const03` double NOT NULL DEFAULT '0', `param03` double DEFAULT NULL, @@ -1790,7 +1790,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 diff --git a/mysql-test/r/ps_7ndb.result b/mysql-test/r/ps_7ndb.result index 65cf7c1f88c..543435e4cd9 100644 --- a/mysql-test/r/ps_7ndb.result +++ b/mysql-test/r/ps_7ndb.result @@ -1760,7 +1760,7 @@ Table Create Table t5 CREATE TABLE `t5` ( `const01` bigint(1) NOT NULL default '0', `param01` bigint(20) default NULL, - `const02` decimal(2,1) unsigned NOT NULL default '0.0', + `const02` decimal(2,1) NOT NULL default '0.0', `param02` decimal(65,30) default NULL, `const03` double NOT NULL default '0', `param03` double default NULL, @@ -1790,7 +1790,7 @@ select * from t5 ; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr def test t5 t5 const01 const01 8 1 1 N 32769 0 63 def test t5 t5 param01 param01 8 20 1 Y 32768 0 63 -def test t5 t5 const02 const02 246 3 3 N 33 1 63 +def test t5 t5 const02 const02 246 4 3 N 1 1 63 def test t5 t5 param02 param02 246 67 32 Y 0 30 63 def test t5 t5 const03 const03 5 17 1 N 32769 31 63 def test t5 t5 param03 param03 5 23 1 Y 32768 31 63 diff --git a/mysql-test/r/rpl_bit_npk.result b/mysql-test/r/rpl_bit_npk.result index 2850e63aabd..9599660f18f 100644 --- a/mysql-test/r/rpl_bit_npk.result +++ b/mysql-test/r/rpl_bit_npk.result @@ -56,86 +56,90 @@ INSERT INTO test.t1 VALUES (8,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,4 UNLOCK TABLES; UPDATE test.t1 set x034 = 50 where bit3 = b'000000'; UPDATE test.t1 set VNotSupp = 33 where bit1 = b'0'; -SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 FROM test.t1; +SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 +FROM test.t1 +ORDER BY oSupp, sSuppD, GSuppDf, VNotSupp, x034; oSupp sSuppD GSuppDf VNotSupp x034 5 5 3 NULL 1 5 5 3 2 1 -5 5 3 33 1 5 5 3 2 50 5 5 3 33 1 5 5 3 33 1 5 5 3 33 1 5 5 3 33 1 -SELECT hex(bit1) from test.t1; +5 5 3 33 1 +SELECT hex(bit1) from test.t1 ORDER BY bit1; hex(bit1) -3F -3F -0 -2A 0 0 0 0 -SELECT hex(bit2) from test.t1; -hex(bit2) -3E 0 2A 3F +3F +SELECT hex(bit2) from test.t1 ORDER BY bit2; +hex(bit2) +0 1 1 1 1 -SELECT hex(bit3) from test.t1; +2A +3E +3F +SELECT hex(bit3) from test.t1 ORDER BY bit3; hex(bit3) -35 -24 -15 0 1 1 1 1 -SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 FROM test.t1; +15 +24 +35 +SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 +FROM test.t1 +ORDER BY oSupp, sSuppD, GSuppDf, VNotSupp, x034; oSupp sSuppD GSuppDf VNotSupp x034 5 5 3 NULL 1 5 5 3 2 1 -5 5 3 33 1 5 5 3 2 50 5 5 3 33 1 5 5 3 33 1 5 5 3 33 1 5 5 3 33 1 -SELECT hex(bit1) from test.t1; +5 5 3 33 1 +SELECT hex(bit1) from test.t1 ORDER BY bit1; hex(bit1) -3F -3F -0 -2A 0 0 0 0 -SELECT hex(bit2) from test.t1; -hex(bit2) -3E 0 2A 3F +3F +SELECT hex(bit2) from test.t1 ORDER BY bit2; +hex(bit2) +0 1 1 1 1 -SELECT hex(bit3) from test.t1; +2A +3E +3F +SELECT hex(bit3) from test.t1 ORDER BY bit3; hex(bit3) -35 -24 -15 0 1 1 1 1 +15 +24 +35 CREATE TABLE test.t2 (a INT, b BIT(1)); INSERT INTO test.t2 VALUES (1, b'0'); INSERT INTO test.t2 VALUES (1, b'1'); @@ -144,19 +148,19 @@ CREATE TABLE test.t3 (a INT, b INT); INSERT INTO test.t3 VALUES (1, NULL); INSERT INTO test.t3 VALUES (1, 0); UPDATE test.t3 SET a = 2 WHERE b = 0; -SELECT a, hex(b) FROM test.t2; +SELECT a, hex(b) FROM test.t2 ORDER BY a,b; a hex(b) 1 0 2 1 -SELECT * FROM test.t3; +SELECT * FROM test.t3 ORDER BY a,b; a b 1 NULL 2 0 -SELECT a, hex(b) FROM test.t2; +SELECT a, hex(b) FROM test.t2 ORDER BY a,b; a hex(b) 1 0 2 1 -SELECT * FROM test.t3; +SELECT * FROM test.t3 ORDER BY a,b; a b 1 NULL 2 0 diff --git a/mysql-test/r/rpl_ndb_UUID.result b/mysql-test/r/rpl_ndb_UUID.result index c768779c49b..422379d4f55 100644 --- a/mysql-test/r/rpl_ndb_UUID.result +++ b/mysql-test/r/rpl_ndb_UUID.result @@ -33,7 +33,7 @@ t1 CREATE TABLE `t1` ( `blob_column` longblob, `vchar_column` varchar(100) DEFAULT NULL, PRIMARY KEY (`a`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 DROP PROCEDURE test.p1; DROP FUNCTION test.fn1; DROP TABLE test.t1; diff --git a/mysql-test/r/rpl_ndb_dd_advance.result b/mysql-test/r/rpl_ndb_dd_advance.result index ba89592b532..2cb1cce649b 100644 --- a/mysql-test/r/rpl_ndb_dd_advance.result +++ b/mysql-test/r/rpl_ndb_dd_advance.result @@ -69,7 +69,7 @@ t1 CREATE TABLE `t1` ( PRIMARY KEY (`c1`), KEY `t1_i` (`c2`,`c3`), KEY `c5` (`c5`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 **** Show first set of ALTERs on SLAVE **** SHOW CREATE TABLE t1; Table Create Table @@ -82,7 +82,7 @@ t1 CREATE TABLE `t1` ( PRIMARY KEY (`c1`), KEY `t1_i` (`c2`,`c3`), KEY `c5` (`c5`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 **** Second set of alters test 1 **** ALTER TABLE t1 RENAME t2; ALTER TABLE t2 DROP INDEX c5; @@ -101,7 +101,7 @@ t1 CREATE TABLE `t1` ( `c5` double DEFAULT NULL, PRIMARY KEY (`c1`), KEY `t1_i` (`c2`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 **** Show second set of ALTERs on SLAVE **** SHOW CREATE TABLE t1; Table Create Table @@ -113,7 +113,7 @@ t1 CREATE TABLE `t1` ( `c5` double DEFAULT NULL, PRIMARY KEY (`c1`), KEY `t1_i` (`c2`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 **** Third and last set of alters for test1 **** ALTER TABLE t1 CHANGE c1 c1 DOUBLE; ALTER TABLE t1 CHANGE c2 c2 DECIMAL(10,2); @@ -135,7 +135,7 @@ t1 CREATE TABLE `t1` ( `c5` double DEFAULT NULL, PRIMARY KEY (`c1`), KEY `t1_i` (`c2`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 SELECT * FROM t1 ORDER BY c1 LIMIT 5; c1 c2 c3 c5 1 2.00 b1b1b1b1b1b1b1b1b1b1 NULL @@ -153,7 +153,7 @@ t1 CREATE TABLE `t1` ( `c5` double DEFAULT NULL, PRIMARY KEY (`c1`), KEY `t1_i` (`c2`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 SELECT * FROM t1 where c1 = 1; c1 c2 c3 c5 1 2.00 b1b1b1b1b1b1b1b1b1b1 NULL diff --git a/mysql-test/r/rpl_ndb_dd_partitions.result b/mysql-test/r/rpl_ndb_dd_partitions.result deleted file mode 100644 index ece6b84c227..00000000000 --- a/mysql-test/r/rpl_ndb_dd_partitions.result +++ /dev/null @@ -1,726 +0,0 @@ -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; ---- Doing pre test cleanup --- -DROP TABLE IF EXISTS t1; -CREATE LOGFILE GROUP lg1 -ADD UNDOFILE 'undofile.dat' -INITIAL_SIZE 16M -UNDO_BUFFER_SIZE = 1M -ENGINE=NDB; -ALTER LOGFILE GROUP lg1 -ADD UNDOFILE 'undofile02.dat' -INITIAL_SIZE = 4M -ENGINE=NDB; -CREATE TABLESPACE ts1 -ADD DATAFILE 'datafile.dat' -USE LOGFILE GROUP lg1 -INITIAL_SIZE 12M -ENGINE NDB; -ALTER TABLESPACE ts1 -ADD DATAFILE 'datafile02.dat' -INITIAL_SIZE = 4M -ENGINE=NDB; ---- Start test 2 partition RANGE testing -- ---- Do setup -- -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), -bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, -f FLOAT DEFAULT 0, total BIGINT UNSIGNED, -y YEAR, t DATE) -TABLESPACE ts1 STORAGE DISK -ENGINE=NDB -PARTITION BY RANGE (YEAR(t)) -(PARTITION p0 VALUES LESS THAN (1901), -PARTITION p1 VALUES LESS THAN (1946), -PARTITION p2 VALUES LESS THAN (1966), -PARTITION p3 VALUES LESS THAN (1986), -PARTITION p4 VALUES LESS THAN (2005), -PARTITION p5 VALUES LESS THAN MAXVALUE); ---- Show table on master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(t)) (PARTITION p0 VALUES LESS THAN (1901) ENGINE = ndbcluster, PARTITION p1 VALUES LESS THAN (1946) ENGINE = ndbcluster, PARTITION p2 VALUES LESS THAN (1966) ENGINE = ndbcluster, PARTITION p3 VALUES LESS THAN (1986) ENGINE = ndbcluster, PARTITION p4 VALUES LESS THAN (2005) ENGINE = ndbcluster, PARTITION p5 VALUES LESS THAN MAXVALUE ENGINE = ndbcluster) ---- Show table on slave -- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(t)) (PARTITION p0 VALUES LESS THAN (1901) ENGINE = ndbcluster, PARTITION p1 VALUES LESS THAN (1946) ENGINE = ndbcluster, PARTITION p2 VALUES LESS THAN (1966) ENGINE = ndbcluster, PARTITION p3 VALUES LESS THAN (1986) ENGINE = ndbcluster, PARTITION p4 VALUES LESS THAN (2005) ENGINE = ndbcluster, PARTITION p5 VALUES LESS THAN MAXVALUE ENGINE = ndbcluster) ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- Check that simple Alter statements are replicated correctly --- -ALTER TABLE t1 MODIFY vc VARCHAR(255); ---- Show the new improved table on the master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(t)) (PARTITION p0 VALUES LESS THAN (1901) ENGINE = ndbcluster, PARTITION p1 VALUES LESS THAN (1946) ENGINE = ndbcluster, PARTITION p2 VALUES LESS THAN (1966) ENGINE = ndbcluster, PARTITION p3 VALUES LESS THAN (1986) ENGINE = ndbcluster, PARTITION p4 VALUES LESS THAN (2005) ENGINE = ndbcluster, PARTITION p5 VALUES LESS THAN MAXVALUE ENGINE = ndbcluster) ---- Make sure that our tables on slave are still same engine --- ---- and that the alter statements replicated correctly --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(t)) (PARTITION p0 VALUES LESS THAN (1901) ENGINE = ndbcluster, PARTITION p1 VALUES LESS THAN (1946) ENGINE = ndbcluster, PARTITION p2 VALUES LESS THAN (1966) ENGINE = ndbcluster, PARTITION p3 VALUES LESS THAN (1986) ENGINE = ndbcluster, PARTITION p4 VALUES LESS THAN (2005) ENGINE = ndbcluster, PARTITION p5 VALUES LESS THAN MAXVALUE ENGINE = ndbcluster) ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- End test 2 partition RANGE testing --- ---- Do Cleanup --- -DROP TABLE IF EXISTS t1; ---- Start test 3 partition LIST testing --- ---- Do setup --- -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), -bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, -f FLOAT DEFAULT 0, total BIGINT UNSIGNED, -y YEAR, t DATE) -TABLESPACE ts1 STORAGE DISK -ENGINE=NDB -PARTITION BY LIST(id) -(PARTITION p0 VALUES IN (2, 4), -PARTITION p1 VALUES IN (42, 142)); ---- Test 3 Alter to add partition --- -ALTER TABLE t1 ADD PARTITION (PARTITION p2 VALUES IN (412)); ---- Show table on master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY LIST (id) (PARTITION p0 VALUES IN (2,4) ENGINE = ndbcluster, PARTITION p1 VALUES IN (42,142) ENGINE = ndbcluster, PARTITION p2 VALUES IN (412) ENGINE = ndbcluster) ---- Show table on slave --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY LIST (id) (PARTITION p0 VALUES IN (2,4) ENGINE = ndbcluster, PARTITION p1 VALUES IN (42,142) ENGINE = ndbcluster, PARTITION p2 VALUES IN (412) ENGINE = ndbcluster) ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- Check that simple Alter statements are replicated correctly --- -ALTER TABLE t1 MODIFY vc VARCHAR(255); ---- Show the new improved table on the master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY LIST (id) (PARTITION p0 VALUES IN (2,4) ENGINE = ndbcluster, PARTITION p1 VALUES IN (42,142) ENGINE = ndbcluster, PARTITION p2 VALUES IN (412) ENGINE = ndbcluster) ---- Make sure that our tables on slave are still same engine --- ---- and that the alter statements replicated correctly --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY LIST (id) (PARTITION p0 VALUES IN (2,4) ENGINE = ndbcluster, PARTITION p1 VALUES IN (42,142) ENGINE = ndbcluster, PARTITION p2 VALUES IN (412) ENGINE = ndbcluster) ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- End test 3 partition LIST testing --- ---- Do Cleanup -- -DROP TABLE IF EXISTS t1; ---- Start test 4 partition HASH testing --- ---- Do setup --- -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), -bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, -f FLOAT DEFAULT 0, total BIGINT UNSIGNED, -y YEAR, t DATE) -TABLESPACE ts1 STORAGE DISK -ENGINE=NDB -PARTITION BY HASH( YEAR(t) ) -PARTITIONS 4; ---- show that tables have been created correctly --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH ( YEAR(t)) PARTITIONS 4 -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH ( YEAR(t)) PARTITIONS 4 ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- Check that simple Alter statements are replicated correctly --- -ALTER TABLE t1 MODIFY vc VARCHAR(255); ---- Show the new improved table on the master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH ( YEAR(t)) PARTITIONS 4 ---- Make sure that our tables on slave are still same engine --- ---- and that the alter statements replicated correctly --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH ( YEAR(t)) PARTITIONS 4 ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- End test 4 partition HASH testing --- ---- Do Cleanup -- -DROP TABLE IF EXISTS t1; ---- Start test 5 partition by key testing --- ---- Create Table Section --- -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), -bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, -f FLOAT DEFAULT 0, total BIGINT UNSIGNED, -y YEAR, t DATE,PRIMARY KEY(id)) -TABLESPACE ts1 STORAGE DISK -ENGINE=NDB -PARTITION BY KEY() -PARTITIONS 4; ---- Show that tables on master are ndbcluster tables --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL, - PRIMARY KEY (`id`) -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () PARTITIONS 4 ---- Show that tables on slave --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned DEFAULT NULL, - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL, - PRIMARY KEY (`id`) -) TABLESPACE ts1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () PARTITIONS 4 ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- Check that simple Alter statements are replicated correctly --- -ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(id, total); ---- Show the new improved table on the master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned NOT NULL DEFAULT '0', - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL, - PRIMARY KEY (`id`,`total`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () PARTITIONS 4 ---- Make sure that our tables on slave are still right type --- ---- and that the alter statements replicated correctly --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(63) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned NOT NULL DEFAULT '0', - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL, - PRIMARY KEY (`id`,`total`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () PARTITIONS 4 ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- Check that simple Alter statements are replicated correctly --- -ALTER TABLE t1 MODIFY vc VARCHAR(255); ---- Show the new improved table on the master --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned NOT NULL DEFAULT '0', - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL, - PRIMARY KEY (`id`,`total`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () PARTITIONS 4 ---- Make sure that our tables on slave are still same engine --- ---- and that the alter statements replicated correctly --- -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `id` mediumint(9) NOT NULL, - `b1` bit(8) DEFAULT NULL, - `vc` varchar(255) DEFAULT NULL, - `bc` char(63) DEFAULT NULL, - `d` decimal(10,4) DEFAULT '0.0000', - `f` float DEFAULT '0', - `total` bigint(20) unsigned NOT NULL DEFAULT '0', - `y` year(4) DEFAULT NULL, - `t` date DEFAULT NULL, - PRIMARY KEY (`id`,`total`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () PARTITIONS 4 ---- Perform basic operation on master --- ---- and ensure replicated correctly --- -"--- Insert into t1 --" as ""; ---- Select from t1 on master --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Select from t1 on slave --- -select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; -id hex(b1) vc bc d f total y t -2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 -4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 -42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 -142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 -412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 ---- Update t1 on master -- -UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412; ---- Check the update on master --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Check Update on slave --- -SELECT id,hex(b1),vc,bc,d,f,total,y,t FROM t1 WHERE id = 412; -id hex(b1) vc bc d f total y t -412 0 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2006-02-22 ---- Remove a record from t1 on master --- -DELETE FROM t1 WHERE id = 42; ---- Show current count on master for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 ---- Show current count on slave for t1 --- -SELECT COUNT(*) FROM t1; -COUNT(*) -4 -DELETE FROM t1; ---- End test 5 key partition testing --- ---- Do Cleanup --- -DROP TABLE IF EXISTS t1; -alter tablespace ts1 -drop datafile 'datafile.dat' -engine=ndb; -alter tablespace ts1 -drop datafile 'datafile02.dat' -engine=ndb; -DROP TABLESPACE ts1 ENGINE=NDB; -DROP LOGFILE GROUP lg1 ENGINE=NDB; diff --git a/mysql-test/r/rpl_row_log.result b/mysql-test/r/rpl_row_log.result index 12b573b8bf6..65e9ee9fb9f 100644 --- a/mysql-test/r/rpl_row_log.result +++ b/mysql-test/r/rpl_row_log.result @@ -39,6 +39,10 @@ master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F flush logs; create table t3 (a int)ENGINE=MyISAM; start slave; + +let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000005%Yes%Yes%0%0%None%' ; + +--source include/wait_slave_status.inc flush logs; stop slave; create table t2 (n int)ENGINE=MyISAM; diff --git a/mysql-test/r/rpl_row_log_innodb.result b/mysql-test/r/rpl_row_log_innodb.result index 48c79c9ab11..2c89ca5f8ff 100644 --- a/mysql-test/r/rpl_row_log_innodb.result +++ b/mysql-test/r/rpl_row_log_innodb.result @@ -41,6 +41,10 @@ master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F flush logs; create table t3 (a int)ENGINE=InnoDB; start slave; + +let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000005%Yes%Yes%0%0%None%' ; + +--source include/wait_slave_status.inc flush logs; stop slave; create table t2 (n int)ENGINE=InnoDB; diff --git a/mysql-test/r/rpl_stm_log.result b/mysql-test/r/rpl_stm_log.result index af774c5075f..02a861ceb53 100644 --- a/mysql-test/r/rpl_stm_log.result +++ b/mysql-test/r/rpl_stm_log.result @@ -39,6 +39,10 @@ master-bin.000001 # Query 1 # use `test`; insert into t1 values (NULL) flush logs; create table t3 (a int)ENGINE=MyISAM; start slave; + +let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000005%Yes%Yes%0%0%None%' ; + +--source include/wait_slave_status.inc flush logs; stop slave; create table t2 (n int)ENGINE=MyISAM; diff --git a/mysql-test/r/rpl_stm_until.result b/mysql-test/r/rpl_stm_until.result index 11b69f55f82..e8e33b66864 100644 --- a/mysql-test/r/rpl_stm_until.result +++ b/mysql-test/r/rpl_stm_until.result @@ -19,9 +19,40 @@ n 2 3 4 -show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 780 slave-relay-bin.000004 # master-bin.000001 # No 0 0 323 # Master master-bin.000001 323 No # +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 780 +Relay_Log_File slave-relay-bin.000004 +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running # +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 323 +Relay_Log_Space # +Until_Condition Master +Until_Log_File master-bin.000001 +Until_Log_Pos 323 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # start slave until master_log_file='master-no-such-bin.000001', master_log_pos=291; select * from t1; n @@ -29,23 +60,116 @@ n 2 3 4 -show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 780 slave-relay-bin.000004 # master-bin.000001 # No 0 0 323 # Master master-no-such-bin.000001 291 No # +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 780 +Relay_Log_File slave-relay-bin.000004 +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running # +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 323 +Relay_Log_Space # +Until_Condition Master +Until_Log_File master-no-such-bin.000001 +Until_Log_Pos 291 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # start slave until relay_log_file='slave-relay-bin.000004', relay_log_pos=746; select * from t2; n 1 2 -show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 780 slave-relay-bin.000004 # master-bin.000001 # No 0 0 612 # Relay slave-relay-bin.000004 746 No # +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 780 +Relay_Log_File slave-relay-bin.000004 +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running # +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 612 +Relay_Log_Space # +Until_Condition Relay +Until_Log_File slave-relay-bin.000004 +Until_Log_Pos 746 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # start slave; stop slave; start slave until master_log_file='master-bin.000001', master_log_pos=776; -show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 780 slave-relay-bin.000004 # master-bin.000001 Yes No 0 0 780 # Master master-bin.000001 776 No # +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 780 +Relay_Log_File slave-relay-bin.000004 +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 780 +Relay_Log_Space # +Until_Condition Master +Until_Log_File master-bin.000001 +Until_Log_Pos 776 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # start slave until master_log_file='master-bin', master_log_pos=561; ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL start slave until master_log_file='master-bin.000001', master_log_pos=561, relay_log_pos=12; diff --git a/mysql-test/r/rpl_temporary.result b/mysql-test/r/rpl_temporary.result index 929dc33c447..01882c683a4 100644 --- a/mysql-test/r/rpl_temporary.result +++ b/mysql-test/r/rpl_temporary.result @@ -88,17 +88,23 @@ f 1 drop temporary table t4; drop table t5; -set @session.pseudo_thread_id=100; +set @@session.pseudo_thread_id=100; create temporary table t101 (id int); create temporary table t102 (id int); -set @session.pseudo_thread_id=200; +set @@session.pseudo_thread_id=200; create temporary table t201 (id int); -create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); -set @con1_id=connection_id(); -kill @con1_id; +create temporary table `#sql_not_user_table202` (id int); +set @@session.pseudo_thread_id=300; +create temporary table t301 (id int); +create temporary table t302 (id int); +create temporary table `#sql_not_user_table303` (id int); create table t1(f int); insert into t1 values (1); select * from t1 /* must be 1 */; f 1 drop table t1; +select * from t1; +a +1 +drop table t1; diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index e9092fc8a69..f45e16f66c1 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3411,3 +3411,38 @@ SELECT * FROM t1; i 255 DROP TABLE t1; +create table t1 (a int); +insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t2 (a int, b int, c int, e int, primary key(a,b,c)); +insert into t2 select A.a, B.a, C.a, C.a from t1 A, t1 B, t1 C; +analyze table t2; +Table Op Msg_type Msg_text +test.t2 analyze status OK +select 'In next EXPLAIN, B.rows must be exactly 10:' Z; +Z +In next EXPLAIN, B.rows must be exactly 10: +explain select * from t2 A, t2 B where A.a=5 and A.b=5 and A.C<5 +and B.a=5 and B.b=A.e and (B.b =1 or B.b = 3 or B.b=5); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE A range PRIMARY PRIMARY 12 NULL 3 Using where +1 SIMPLE B ref PRIMARY PRIMARY 8 const,test.A.e 10 +drop table t1, t2; +CREATE TABLE t1 (a int PRIMARY KEY, b int, INDEX(b)); +INSERT INTO t1 VALUES (1, 3), (9,4), (7,5), (4,5), (6,2), +(3,1), (5,1), (8,9), (2,2), (0,9); +CREATE TABLE t2 (c int, d int, f int, INDEX(c,f)); +INSERT INTO t2 VALUES +(1,0,0), (1,0,1), (2,0,0), (2,0,1), (3,0,0), (4,0,1), +(5,0,0), (5,0,1), (6,0,0), (0,0,1), (7,0,0), (7,0,1), +(0,0,0), (0,0,1), (8,0,0), (8,0,1), (9,0,0), (9,0,1); +EXPLAIN +SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where +1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where +EXPLAIN +SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where +1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where +DROP TABLE t1, t2; diff --git a/mysql-test/r/skip_name_resolve.result b/mysql-test/r/skip_name_resolve.result index 8ef52e75238..855876825ad 100644 --- a/mysql-test/r/skip_name_resolve.result +++ b/mysql-test/r/skip_name_resolve.result @@ -10,5 +10,6 @@ user() # show processlist; Id User Host db Command Time State Info +<id> event_scheduler <host> NULL <command> <time> <state> <info> <id> root <host> test <command> <time> <state> <info> <id> root <host> test <command> <time> <state> <info> diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 931fa2ee402..fde273c55ee 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -282,9 +282,9 @@ select @tmp_x, @tmp_y, @tmp_z| @tmp_x @tmp_y @tmp_z 42 45 87 call p(42, 43, @tmp_z)| -ERROR 42000: OUT or INOUT argument 2 for routine test.p is not a variable +ERROR 42000: OUT or INOUT argument 2 for routine test.p is not a variable or NEW pseudo-variable in BEFORE trigger call p(42, @tmp_y, 43)| -ERROR 42000: OUT or INOUT argument 3 for routine test.p is not a variable +ERROR 42000: OUT or INOUT argument 3 for routine test.p is not a variable or NEW pseudo-variable in BEFORE trigger drop procedure p| create procedure p() begin end| lock table t1 read| diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index c516d7a643f..3cba437e0a6 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -34,6 +34,7 @@ lock tables t2 write; call bug9486(); show processlist; Id User Host db Command Time State Info +# event_scheduler localhost NULL Connect # Suspended NULL # root localhost test Sleep # NULL # root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2 # root localhost test Query # NULL show processlist diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 5aa984bfa4e..8330c391715 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4914,4 +4914,60 @@ schema_name select routine_name,routine_schema from information_schema.routines where routine_schema like 'bug18344%'| routine_name routine_schema +drop function if exists bug12472| +create function bug12472() returns int return (select count(*) from t1)| +create table t3 as select bug12472() as i| +show create table t3| +Table Create Table +t3 CREATE TABLE `t3` ( + `i` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +select * from t3| +i +0 +drop table t3| +create view v1 as select bug12472() as j| +create table t3 as select * from v1| +show create table t3| +Table Create Table +t3 CREATE TABLE `t3` ( + `j` bigint(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +select * from t3| +j +0 +drop table t3| +drop view v1| +drop function bug12472| +DROP FUNCTION IF EXISTS bug18589_f1| +DROP PROCEDURE IF EXISTS bug18589_p1| +DROP PROCEDURE IF EXISTS bug18589_p2| +CREATE FUNCTION bug18589_f1(arg TEXT) RETURNS TEXT +BEGIN +RETURN CONCAT(arg, ""); +END| +CREATE PROCEDURE bug18589_p1(arg TEXT, OUT ret TEXT) +BEGIN +SET ret = CONCAT(arg, ""); +END| +CREATE PROCEDURE bug18589_p2(arg TEXT) +BEGIN +DECLARE v TEXT; +CALL bug18589_p1(arg, v); +SELECT v; +END| +SELECT bug18589_f1(REPEAT("a", 767))| +bug18589_f1(REPEAT("a", 767)) +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +SET @bug18589_v1 = ""| +CALL bug18589_p1(REPEAT("a", 767), @bug18589_v1)| +SELECT @bug18589_v1| +@bug18589_v1 +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +CALL bug18589_p2(REPEAT("a", 767))| +v +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +DROP FUNCTION bug18589_f1| +DROP PROCEDURE bug18589_p1| +DROP PROCEDURE bug18589_p2| drop table t1,t2; diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result index c8cafe5ace1..c5d60446e0a 100644 --- a/mysql-test/r/sp_notembedded.result +++ b/mysql-test/r/sp_notembedded.result @@ -18,9 +18,11 @@ show processlist; end| call bug4902_2()| Id User Host db Command Time State Info +# event_scheduler localhost NULL Connect # Suspended NULL # root localhost test Query # NULL show processlist call bug4902_2()| Id User Host db Command Time State Info +# event_scheduler localhost NULL Connect # Suspended NULL # root localhost test Query # NULL show processlist drop procedure bug4902_2| drop function if exists bug5278| diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result index ca21b333a6a..e83ade78cf6 100644 --- a/mysql-test/r/status.result +++ b/mysql-test/r/status.result @@ -26,20 +26,20 @@ Last_query_cost 0.000000 FLUSH STATUS; SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 1 +Max_used_connections 2 SET @save_thread_cache_size=@@thread_cache_size; SET GLOBAL thread_cache_size=3; SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 3 +Max_used_connections 4 FLUSH STATUS; SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 2 -SHOW STATUS LIKE 'max_used_connections'; -Variable_name Value Max_used_connections 3 SHOW STATUS LIKE 'max_used_connections'; Variable_name Value Max_used_connections 4 +SHOW STATUS LIKE 'max_used_connections'; +Variable_name Value +Max_used_connections 5 SET GLOBAL thread_cache_size=@save_thread_cache_size; diff --git a/mysql-test/r/strict.result b/mysql-test/r/strict.result index 92f1ba3a23e..5ca185d6abc 100644 --- a/mysql-test/r/strict.result +++ b/mysql-test/r/strict.result @@ -1288,3 +1288,13 @@ ERROR 22001: Data too long for column 'a' at row 1 select * from t1; a drop table t1; +set sql_mode='traditional'; +create table t1 (date date not null); +create table t2 select date from t1; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `date` date NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t2,t1; +set @@sql_mode= @org_mode; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 0a464d055c2..71a71a14ad2 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1480,7 +1480,7 @@ Note 1003 select `test`.`t1`.`s1` AS `s1`,not(<in_optimizer>(`test`.`t1`.`s1`,<e explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index -2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 1 Using index; Using where +2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Using where Warnings: Note 1003 select `test`.`t1`.`s1` AS `s1`,not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2'))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` drop table t1,t2; @@ -3169,3 +3169,17 @@ create table t2 (a int, b int); insert into t2 values (2, 1), (1, 0); delete from t1 where c <= 1140006215 and (select b from t2 where a = 2) = 1; drop table t1, t2; +CREATE TABLE t1 (a INT); +CREATE VIEW v1 AS SELECT * FROM t1 WHERE no_such_column = ANY (SELECT 1); +ERROR 42S22: Unknown column 'no_such_column' in 'where clause' +CREATE VIEW v2 AS SELECT * FROM t1 WHERE no_such_column = (SELECT 1); +ERROR 42S22: Unknown column 'no_such_column' in 'where clause' +SELECT * FROM t1 WHERE no_such_column = ANY (SELECT 1); +ERROR 42S22: Unknown column 'no_such_column' in 'IN/ALL/ANY subquery' +DROP TABLE t1; +create table t1 (i int, j bigint); +insert into t1 values (1, 2), (2, 2), (3, 2); +select * from (select min(i) from t1 where j=(select * from (select min(j) from t1) t2)) t3; +min(i) +1 +drop table t1; diff --git a/mysql-test/r/trigger-grant.result b/mysql-test/r/trigger-grant.result index 10f1e08eded..fec5d4812d9 100644 --- a/mysql-test/r/trigger-grant.result +++ b/mysql-test/r/trigger-grant.result @@ -364,3 +364,87 @@ SELECT @mysqltest_var; Hello, world! DROP USER mysqltest_u1@localhost; DROP DATABASE mysqltest_db1; +DELETE FROM mysql.user WHERE User LIKE 'mysqltest_%'; +DELETE FROM mysql.db WHERE User LIKE 'mysqltest_%'; +DELETE FROM mysql.tables_priv WHERE User LIKE 'mysqltest_%'; +DELETE FROM mysql.columns_priv WHERE User LIKE 'mysqltest_%'; +FLUSH PRIVILEGES; +DROP DATABASE IF EXISTS mysqltest_db1; +CREATE DATABASE mysqltest_db1; +USE mysqltest_db1; +CREATE TABLE t1 (i1 INT); +CREATE TABLE t2 (i1 INT); +CREATE USER mysqltest_dfn@localhost; +CREATE USER mysqltest_inv@localhost; +GRANT EXECUTE, CREATE ROUTINE, TRIGGER ON *.* TO mysqltest_dfn@localhost; +GRANT INSERT ON mysqltest_db1.* TO mysqltest_inv@localhost; +CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 3; +CREATE PROCEDURE p2(INOUT i INT) DETERMINISTIC NO SQL SET i = i * 5; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW +CALL p2(NEW.i1); +INSERT INTO t1 VALUES (7); +ERROR 42000: UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1' +INSERT INTO t2 VALUES (11); +ERROR 42000: SELECT,UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't2' +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; +GRANT SELECT ON mysqltest_db1.* TO mysqltest_dfn@localhost; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW +CALL p2(NEW.i1); +INSERT INTO t1 VALUES (13); +ERROR 42000: UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1' +INSERT INTO t2 VALUES (17); +ERROR 42000: UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't2' +REVOKE SELECT ON mysqltest_db1.* FROM mysqltest_dfn@localhost; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; +GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW +CALL p2(NEW.i1); +INSERT INTO t1 VALUES (19); +INSERT INTO t2 VALUES (23); +ERROR 42000: SELECT command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't2' +REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; +GRANT SELECT, UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW +CALL p2(NEW.i1); +INSERT INTO t1 VALUES (29); +INSERT INTO t2 VALUES (31); +REVOKE SELECT, UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; +DROP PROCEDURE p2; +DROP PROCEDURE p1; +GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost; +CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 37; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +CALL p1(NEW.i1); +INSERT INTO t1 VALUES (41); +DROP PROCEDURE p1; +CREATE PROCEDURE p1(IN i INT) DETERMINISTIC NO SQL SET @v1 = i + 43; +INSERT INTO t1 VALUES (47); +ERROR 42000: SELECT command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1' +DROP PROCEDURE p1; +CREATE PROCEDURE p1(INOUT i INT) DETERMINISTIC NO SQL SET i = i + 51; +INSERT INTO t1 VALUES (53); +ERROR 42000: SELECT command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1' +DROP PROCEDURE p1; +REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost; +DROP TRIGGER t1_bi; +DROP USER mysqltest_inv@localhost; +DROP USER mysqltest_dfn@localhost; +DROP TABLE t2; +DROP TABLE t1; +DROP DATABASE mysqltest_db1; +USE test; +End of 5.0 tests. diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 2c39711a724..24675f2cd66 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -998,3 +998,95 @@ SELECT * FROM t1 WHERE conn_id != trigger_conn_id; conn_id trigger_conn_id DROP TRIGGER t1_bi; DROP TABLE t1; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i1 INT); +SET @save_sql_mode=@@sql_mode; +SET SQL_MODE=''; +CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW +SET @x = 5/0; +SET SQL_MODE='traditional'; +CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW +SET @x = 5/0; +SET @x=1; +INSERT INTO t1 VALUES (@x); +SELECT @x; +@x +NULL +SET @x=2; +UPDATE t1 SET i1 = @x; +ERROR 22012: Division by 0 +SELECT @x; +@x +2 +SET SQL_MODE=''; +SET @x=3; +INSERT INTO t1 VALUES (@x); +SELECT @x; +@x +NULL +SET @x=4; +UPDATE t1 SET i1 = @x; +ERROR 22012: Division by 0 +SELECT @x; +@x +4 +SET @@sql_mode=@save_sql_mode; +DROP TRIGGER t1_ai; +DROP TRIGGER t1_au; +DROP TABLE t1; +DROP TABLE IF EXISTS t1; +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; +CREATE TABLE t1 (i1 INT); +INSERT INTO t1 VALUES (3); +CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET i1 = 5; +CREATE PROCEDURE p2(INOUT i1 INT) DETERMINISTIC NO SQL SET i1 = i1 * 7; +CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW +BEGIN +CALL p1(NEW.i1); +CALL p2(NEW.i1); +END// +UPDATE t1 SET i1 = 11 WHERE i1 = 3; +DROP TRIGGER t1_bu; +DROP PROCEDURE p2; +DROP PROCEDURE p1; +INSERT INTO t1 VALUES (13); +CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 17; +CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW +CALL p1(OLD.i1); +UPDATE t1 SET i1 = 19 WHERE i1 = 13; +ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger +DROP TRIGGER t1_bu; +DROP PROCEDURE p1; +INSERT INTO t1 VALUES (23); +CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 29; +CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW +CALL p1(OLD.i1); +UPDATE t1 SET i1 = 31 WHERE i1 = 23; +ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger +DROP TRIGGER t1_bu; +DROP PROCEDURE p1; +INSERT INTO t1 VALUES (37); +CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 41; +CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW +CALL p1(NEW.i1); +UPDATE t1 SET i1 = 43 WHERE i1 = 37; +ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger +DROP TRIGGER t1_au; +DROP PROCEDURE p1; +INSERT INTO t1 VALUES (47); +CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 49; +CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW +CALL p1(NEW.i1); +UPDATE t1 SET i1 = 51 WHERE i1 = 47; +ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger +DROP TRIGGER t1_au; +DROP PROCEDURE p1; +SELECT * FROM t1; +i1 +35 +13 +23 +43 +51 +DROP TABLE t1; diff --git a/mysql-test/r/type_float.result b/mysql-test/r/type_float.result index d3c80a7c80e..cfae61d5e91 100644 --- a/mysql-test/r/type_float.result +++ b/mysql-test/r/type_float.result @@ -245,22 +245,22 @@ show warnings; Level Code Message desc t1; Field Type Null Key Default Extra -x decimal(21,2) unsigned NO 0.00 +x decimal(21,2) NO 0.00 drop table t1; create table t1 select 0.0 x; desc t1; Field Type Null Key Default Extra -x decimal(2,1) unsigned NO 0.0 +x decimal(2,1) NO 0.0 create table t2 select 105213674794682365.00 y; desc t2; Field Type Null Key Default Extra -y decimal(20,2) unsigned NO 0.00 +y decimal(20,2) NO 0.00 create table t3 select x+y a from t1,t2; show warnings; Level Code Message desc t3; Field Type Null Key Default Extra -a decimal(21,2) unsigned NO 0.00 +a decimal(21,2) NO 0.00 drop table t1,t2,t3; create table t1 (s1 float(0,2)); ERROR 42000: For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column 's1'). diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index f96d5e383f4..702b589c911 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -68,10 +68,10 @@ NULL 1.1 NULL NULL NULL 1 show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `nullif(1.1, 1.1)` decimal(2,1) unsigned DEFAULT NULL, - `nullif(1.1, 1.2)` decimal(2,1) unsigned DEFAULT NULL, - `nullif(1.1, 0.11e1)` decimal(2,1) unsigned DEFAULT NULL, - `nullif(1.0, 1)` decimal(2,1) unsigned DEFAULT NULL, + `nullif(1.1, 1.1)` decimal(2,1) DEFAULT NULL, + `nullif(1.1, 1.2)` decimal(2,1) DEFAULT NULL, + `nullif(1.1, 0.11e1)` decimal(2,1) DEFAULT NULL, + `nullif(1.0, 1)` decimal(2,1) DEFAULT NULL, `nullif(1, 1.0)` int(1) DEFAULT NULL, `nullif(1, 1.1)` int(1) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 @@ -174,9 +174,9 @@ create table t1 select round(15.4,-1), truncate(-5678.123451,-3), abs(-1.1), -(- show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `round(15.4,-1)` decimal(3,0) unsigned NOT NULL DEFAULT '0', + `round(15.4,-1)` decimal(3,0) NOT NULL DEFAULT '0', `truncate(-5678.123451,-3)` decimal(4,0) NOT NULL DEFAULT '0', - `abs(-1.1)` decimal(2,1) NOT NULL DEFAULT '0.0', + `abs(-1.1)` decimal(3,1) NOT NULL DEFAULT '0.0', `-(-1.1)` decimal(2,1) NOT NULL DEFAULT '0.0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; @@ -771,7 +771,7 @@ create table t1 as select 0.5; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `0.5` decimal(2,1) unsigned NOT NULL DEFAULT '0.0' + `0.5` decimal(2,1) NOT NULL DEFAULT '0.0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; select round(1.5),round(2.5); diff --git a/mysql-test/r/type_ranges.result b/mysql-test/r/type_ranges.result index 0ba8916f5b1..ba401a22945 100644 --- a/mysql-test/r/type_ranges.result +++ b/mysql-test/r/type_ranges.result @@ -241,7 +241,7 @@ Field Type Collation Null Key Default Extra Privileges Comment auto int(5) unsigned NULL NO 0 # string char(10) latin1_swedish_ci YES newdefault # tiny tinyint(4) NULL NO 0 # -short smallint(6) NULL NO 0 # +short smallint(6) NULL NO # medium mediumint(8) NULL NO 0 # long_int int(11) NULL NO 0 # longlong bigint(13) NULL NO 0 # @@ -259,7 +259,7 @@ date_time datetime NULL YES NULL # new_blob_col varchar(20) latin1_swedish_ci YES NULL # tinyblob_col tinyblob NULL YES NULL # mediumblob_col mediumblob NULL NO # -options enum('one','two','tree') latin1_swedish_ci NO one # +options enum('one','two','tree') latin1_swedish_ci NO # flags set('one','two','tree') latin1_swedish_ci NO # new_field char(10) latin1_swedish_ci NO new # select t1.auto,t2.auto from t1,t2 where t1.auto=t2.auto and ((t1.string<>t2.string and (t1.string is not null or t2.string is not null)) or (t1.tiny<>t2.tiny and (t1.tiny is not null or t2.tiny is not null)) or (t1.short<>t2.short and (t1.short is not null or t2.short is not null)) or (t1.medium<>t2.medium and (t1.medium is not null or t2.medium is not null)) or (t1.long_int<>t2.long_int and (t1.long_int is not null or t2.long_int is not null)) or (t1.longlong<>t2.longlong and (t1.longlong is not null or t2.longlong is not null)) or (t1.real_float<>t2.real_float and (t1.real_float is not null or t2.real_float is not null)) or (t1.real_double<>t2.real_double and (t1.real_double is not null or t2.real_double is not null)) or (t1.utiny<>t2.utiny and (t1.utiny is not null or t2.utiny is not null)) or (t1.ushort<>t2.ushort and (t1.ushort is not null or t2.ushort is not null)) or (t1.umedium<>t2.umedium and (t1.umedium is not null or t2.umedium is not null)) or (t1.ulong<>t2.ulong and (t1.ulong is not null or t2.ulong is not null)) or (t1.ulonglong<>t2.ulonglong and (t1.ulonglong is not null or t2.ulonglong is not null)) or (t1.time_stamp<>t2.time_stamp and (t1.time_stamp is not null or t2.time_stamp is not null)) or (t1.date_field<>t2.date_field and (t1.date_field is not null or t2.date_field is not null)) or (t1.time_field<>t2.time_field and (t1.time_field is not null or t2.time_field is not null)) or (t1.date_time<>t2.date_time and (t1.date_time is not null or t2.date_time is not null)) or (t1.new_blob_col<>t2.new_blob_col and (t1.new_blob_col is not null or t2.new_blob_col is not null)) or (t1.tinyblob_col<>t2.tinyblob_col and (t1.tinyblob_col is not null or t2.tinyblob_col is not null)) or (t1.mediumblob_col<>t2.mediumblob_col and (t1.mediumblob_col is not null or t2.mediumblob_col is not null)) or (t1.options<>t2.options and (t1.options is not null or t2.options is not null)) or (t1.flags<>t2.flags and (t1.flags is not null or t2.flags is not null)) or (t1.new_field<>t2.new_field and (t1.new_field is not null or t2.new_field is not null))); diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index 43b32782c63..e4b3fc2c2e2 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -1,4 +1,31 @@ drop table if exists t1,t2; +set @my_binlog_cache_size =@@global.binlog_cache_size; +set @my_connect_timeout =@@global.connect_timeout; +set @my_delayed_insert_timeout =@@global.delayed_insert_timeout; +set @my_delayed_queue_size =@@global.delayed_queue_size; +set @my_flush =@@global.flush; +set @my_flush_time =@@global.flush_time; +set @my_key_buffer_size =@@global.key_buffer_size; +set @my_max_binlog_cache_size =@@global.max_binlog_cache_size; +set @my_max_binlog_size =@@global.max_binlog_size; +set @my_max_connect_errors =@@global.max_connect_errors; +set @my_max_delayed_threads =@@global.max_delayed_threads; +set @my_max_heap_table_size =@@global.max_heap_table_size; +set @my_max_insert_delayed_threads=@@global.max_insert_delayed_threads; +set @my_max_join_size =@@global.max_join_size; +set @my_max_user_connections =@@global.max_user_connections; +set @my_max_write_lock_count =@@global.max_write_lock_count; +set @my_myisam_data_pointer_size =@@global.myisam_data_pointer_size; +set @my_net_buffer_length =@@global.net_buffer_length; +set @my_net_write_timeout =@@global.net_write_timeout; +set @my_net_read_timeout =@@global.net_read_timeout; +set @my_query_cache_limit =@@global.query_cache_limit; +set @my_query_cache_type =@@global.query_cache_type; +set @my_rpl_recovery_rank =@@global.rpl_recovery_rank; +set @my_server_id =@@global.server_id; +set @my_slow_launch_time =@@global.slow_launch_time; +set @my_storage_engine =@@global.storage_engine; +set @my_thread_cache_size =@@global.thread_cache_size; set @`test`=1; select @test, @`test`, @TEST, @`TEST`, @"teSt"; @test @`test` @TEST @`TEST` @"teSt" @@ -612,4 +639,59 @@ select @@version, @@version_comment, @@version_compile_machine, @@version_compile_os; @@version @@version_comment @@version_compile_machine @@version_compile_os # # # # +select @@basedir, @@datadir, @@tmpdir; +@@basedir @@datadir @@tmpdir +# # # +show variables like 'basedir'; +Variable_name Value +basedir # +show variables like 'datadir'; +Variable_name Value +datadir # +show variables like 'tmpdir'; +Variable_name Value +tmpdir # +select @@ssl_ca, @@ssl_capath, @@ssl_cert, @@ssl_cipher, @@ssl_key; +@@ssl_ca @@ssl_capath @@ssl_cert @@ssl_cipher @@ssl_key +# # # # # +show variables like 'ssl%'; +Variable_name Value +ssl_ca # +ssl_capath # +ssl_cert # +ssl_cipher # +ssl_key # +select @@log_queries_not_using_indexes; +@@log_queries_not_using_indexes +0 +show variables like 'log_queries_not_using_indexes'; +Variable_name Value +log_queries_not_using_indexes OFF End of 5.0 tests +set global binlog_cache_size =@my_binlog_cache_size; +set global connect_timeout =@my_connect_timeout; +set global delayed_insert_timeout =@my_delayed_insert_timeout; +set global delayed_queue_size =@my_delayed_queue_size; +set global flush =@my_flush; +set global flush_time =@my_flush_time; +set global key_buffer_size =@my_key_buffer_size; +set global max_binlog_cache_size =default; +set global max_binlog_size =@my_max_binlog_size; +set global max_connect_errors =@my_max_connect_errors; +set global max_delayed_threads =@my_max_delayed_threads; +set global max_heap_table_size =@my_max_heap_table_size; +set global max_insert_delayed_threads=@my_max_insert_delayed_threads; +set global max_join_size =@my_max_join_size; +set global max_user_connections =@my_max_user_connections; +set global max_write_lock_count =@my_max_write_lock_count; +set global myisam_data_pointer_size =@my_myisam_data_pointer_size; +set global net_buffer_length =@my_net_buffer_length; +set global net_write_timeout =@my_net_write_timeout; +set global net_read_timeout =@my_net_read_timeout; +set global query_cache_limit =@my_query_cache_limit; +set global query_cache_type =@my_query_cache_type; +set global rpl_recovery_rank =@my_rpl_recovery_rank; +set global server_id =@my_server_id; +set global slow_launch_time =@my_slow_launch_time; +set global storage_engine =@my_storage_engine; +set global thread_cache_size =@my_thread_cache_size; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index c5446ac314e..10a9ac87748 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -706,7 +706,7 @@ create view v1 as select a from t1; create view v2 as select a from t2 where a in (select a from v1); show create view v2; View Create View -v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t2`.`a` AS `a` from `t2` where `a` in (select `v1`.`a` AS `a` from `v1`) +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t2`.`a` AS `a` from `t2` where `t2`.`a` in (select `v1`.`a` AS `a` from `v1`) drop view v2, v1; drop table t1, t2; CREATE VIEW `v 1` AS select 5 AS `5`; @@ -2649,3 +2649,90 @@ ldt 2006-01-01 03:00:00 drop view v1, v2; drop table t1; +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, d datetime); +CREATE VIEW v1 AS +SELECT id, date(d) + INTERVAL TIME_TO_SEC(d) SECOND AS t, COUNT(*) +FROM t1 GROUP BY id, t; +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`id` AS `id`,(cast(`t1`.`d` as date) + interval time_to_sec(`t1`.`d`) second) AS `t`,count(0) AS `COUNT(*)` from `t1` group by `t1`.`id`,(cast(`t1`.`d` as date) + interval time_to_sec(`t1`.`d`) second) +SELECT * FROM v1; +id t COUNT(*) +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (i INT, j BIGINT); +INSERT INTO t1 VALUES (1, 2), (2, 2), (3, 2); +CREATE VIEW v1 AS SELECT MIN(j) AS j FROM t1; +CREATE VIEW v2 AS SELECT MIN(i) FROM t1 WHERE j = ( SELECT * FROM v1 ); +SELECT * FROM v2; +MIN(i) +1 +DROP VIEW v2, v1; +DROP TABLE t1; +CREATE TABLE t1( +fName varchar(25) NOT NULL, +lName varchar(25) NOT NULL, +DOB date NOT NULL, +uID int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY); +INSERT INTO t1(fName, lName, DOB) VALUES +('Hank', 'Hill', '1964-09-29'), +('Tom', 'Adams', '1908-02-14'), +('Homer', 'Simpson', '1968-03-05'); +CREATE VIEW v1 AS +SELECT (year(now())-year(DOB)) AS Age +FROM t1 HAVING Age < 75; +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_no_cache (year(now()) - year(`t1`.`DOB`)) AS `Age` from `t1` having (`Age` < 75) +SELECT (year(now())-year(DOB)) AS Age FROM t1 HAVING Age < 75; +Age +42 +38 +SELECT * FROM v1; +Age +42 +38 +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a char(6) DEFAULT 'xxx'); +INSERT INTO t1(id) VALUES (1), (2), (3), (4); +INSERT INTO t1 VALUES (5,'yyy'), (6,'yyy'); +SELECT * FROM t1; +id a +1 xxx +2 xxx +3 xxx +4 xxx +5 yyy +6 yyy +CREATE VIEW v1(a, m) AS SELECT a, MIN(id) FROM t1 GROUP BY a; +SELECT * FROM v1; +a m +xxx 1 +yyy 5 +CREATE TABLE t2 SELECT * FROM v1; +INSERT INTO t2(m) VALUES (0); +SELECT * FROM t2; +a m +xxx 1 +yyy 5 +xxx 0 +DROP VIEW v1; +DROP TABLE t1,t2; +CREATE TABLE t1 (id int PRIMARY KEY, e ENUM('a','b') NOT NULL DEFAULT 'b'); +INSERT INTO t1(id) VALUES (1), (2), (3); +INSERT INTO t1 VALUES (4,'a'); +SELECT * FROM t1; +id e +1 b +2 b +3 b +4 a +CREATE VIEW v1(m, e) AS SELECT MIN(id), e FROM t1 GROUP BY e; +CREATE TABLE t2 SELECT * FROM v1; +SELECT * FROM t2; +m e +4 a +1 b +DROP VIEW v1; +DROP TABLE IF EXISTS t1,t2; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 7cddb8bafa9..efe58579785 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -469,3 +469,16 @@ ALTER TABLE t1 MODIFY s BINARY(10); SELECT HEX(s) FROM t1; SELECT LENGTH(s) FROM t1; DROP TABLE t1; + +# +# Bug#19386: Multiple alter causes crashed table +# The trailing column would get corrupted data, or server could not even read +# it. +# + +CREATE TABLE t1 (v VARCHAR(3), b INT); +INSERT INTO t1 VALUES ('abc', 5); +SELECT * FROM t1; +ALTER TABLE t1 MODIFY COLUMN v VARCHAR(4); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index 7e091991475..c89d31c69b9 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -5,7 +5,7 @@ -- source include/have_archive.inc --disable_warnings -drop table if exists t1,t2; +drop table if exists t1,t2,t3; --enable_warnings CREATE TABLE t1 ( diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index 26b103b0107..e0b024d021b 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -275,3 +275,14 @@ update t1 set a=2 where a=1; insert into t1 (val) values (1); select * from t1; drop table t1; + +# +# Test key duplications with auto-increment in ALTER TABLE +# bug #14573 +# +CREATE TABLE t1 (t1 INT(10) PRIMARY KEY, t2 INT(10)); +INSERT INTO t1 VALUES(0, 0); +INSERT INTO t1 VALUES(1, 1); +--error ER_DUP_ENTRY +ALTER TABLE t1 CHANGE t1 t1 INT(10) auto_increment; +DROP TABLE t1; diff --git a/mysql-test/t/contributors.test b/mysql-test/t/contributors.test new file mode 100644 index 00000000000..e463c4a888b --- /dev/null +++ b/mysql-test/t/contributors.test @@ -0,0 +1 @@ +SHOW CONTRIBUTORS; diff --git a/mysql-test/t/ctype_sjis.test b/mysql-test/t/ctype_sjis.test index 1d807b5e9a8..01e0b334554 100644 --- a/mysql-test/t/ctype_sjis.test +++ b/mysql-test/t/ctype_sjis.test @@ -78,6 +78,6 @@ SET collation_connection='sjis_bin'; --character_set sjis SET NAMES sjis; -SELECT HEX('²“‘@\Œ\') FROM DUAL; +SELECT HEX('²“‘@Œ\') FROM DUAL; # End of 4.1 tests diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 43ce4157cdb..d6083ab8bfe 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -9,13 +9,13 @@ # Do not use any TAB characters for whitespace. # ############################################################################## -events_bugs : BUG#17619 2006-02-21 andrey Race conditions -events_stress : BUG#17619 2006-02-21 andrey Race conditions -events : BUG#17619 2006-02-21 andrey Race conditions -events_scheduling : BUG#19170 2006-04-26 andrey Test case of 19170 fails on some platforms. Has to be checked. +#events_bugs : BUG#17619 2006-02-21 andrey Race conditions +#events_stress : BUG#17619 2006-02-21 andrey Race conditions +#events : BUG#17619 2006-02-21 andrey Race conditions +#events_scheduling : BUG#19170 2006-04-26 andrey Test case of 19170 fails on some platforms. Has to be checked. ndb_autodiscover : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t binlog ndb_autodiscover2 : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t binlog -ndb_binlog_discover : BUG#19395 2006-04-28 tomas/knielsen mysqld does not always detect cluster shutdown +#ndb_binlog_discover : BUG#19395 2006-04-28 tomas/knielsen mysqld does not always detect cluster shutdown #ndb_cache2 : BUG#18597 2006-03-28 brian simultaneous drop table and ndb statistics update triggers node failure #ndb_cache_multi2 : BUG#18597 2006-04-10 kent simultaneous drop table and ndb statistics update triggers node failure ndb_load : BUG#17233 2006-05-04 tomas failed load data from infile causes mysqld dbug_assert, binlog not flushed @@ -27,9 +27,9 @@ rpl_ndb_auto_inc : BUG#17086 2006-02-16 jmiller CR: auto_increment_incre rpl_ndb_commit_afterflush : BUG#19328 2006-05-04 tomas Slave timeout with COM_REGISTER_SLAVE error causing stop rpl_ndb_dd_partitions : BUG#19259 2006-04-21 rpl_ndb_dd_partitions fails on s/AMD rpl_ndb_ddl : BUG#18946 result file needs update + test needs to checked -rpl_ndb_innodb2ndb : BUG#17400 2006-04-19 tomas Cluster Replication: delete & update of rows in table without pk fails on slave. +rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_ndb_log : BUG#18947 2006-03-21 tomas CRBR: order in binlog of create table and insert (on different table) not determ -rpl_ndb_myisam2ndb : BUG#17400 2006-04-19 tomas Cluster Replication: delete & update of rows in table without pk fails on slave. +rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_switch_stm_row_mixed : BUG#18590 2006-03-28 brian rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly rpl_row_func003 : BUG#19074 2006-13-04 andrei test failed diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index fbcd4924d56..819d64ccf14 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -15,11 +15,10 @@ CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; connection default; SHOW DATABASES LIKE 'db_x'; SET GLOBAL event_scheduler=1; ---sleep 2 +--sleep 1.5 SHOW DATABASES LIKE 'db_x'; SHOW TABLES FROM db_x; -SET GLOBAL event_scheduler=0; ---sleep 1 +SET GLOBAL event_scheduler=2; connection priv_conn; DROP EVENT e_x1; DROP EVENT e_x2; @@ -31,8 +30,7 @@ USE events_test; # # END: BUG #17289 Events: missing privilege check for drop database # -SET GLOBAL event_scheduler=0; ---sleep 1 +SET GLOBAL event_scheduler=2; drop event if exists event1; create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2 enable; @@ -92,11 +90,11 @@ drop event e_43; --echo "Let's check whether we can use non-qualified names" create table non_qualif(a int); create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); ---sleep 2 +--sleep 1 select * from non_qualif; drop event non_qualif_ev; drop table non_qualif; -set global event_scheduler = 0; +set global event_scheduler = 2; create table t_event3 (a int, b float); drop event if exists event3; @@ -281,15 +279,15 @@ SHOW EVENTS; --echo "This should show us only 3 events:"; --replace_column 8 # 9 # -SHOW FULL EVENTS; +SHOW EVENTS; --echo "This should show us only 2 events:"; --replace_column 8 # 9 # -SHOW FULL EVENTS LIKE 't%event'; +SHOW EVENTS LIKE 't%event'; --echo "This should show us no events:"; --replace_column 8 # 9 # -SHOW FULL EVENTS FROM test LIKE '%'; +SHOW EVENTS FROM test LIKE '%'; #ok, we are back connection default; DROP DATABASE events_test2; @@ -300,7 +298,7 @@ SHOW EVENTS; --echo "we should see 4 events now:"; --replace_column 8 # 9 # -SHOW FULL EVENTS; +SHOW EVENTS; SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; connection ev_con1; @@ -330,21 +328,21 @@ create event задачка on schedule every 123 minute starts now() ends now() drop event задачка; # event_scheduler is a global var ---error 1229 -set event_scheduler=0; -# event_scheduler could be only either 0 or 1 ---error 1231 -set global event_scheduler=2; +--error ER_GLOBAL_VARIABLE +set event_scheduler=2; +# event_scheduler could be only either 1 or 2 +--error ER_WRONG_VALUE_FOR_VAR +set global event_scheduler=3; --echo "DISABLE the scheduler. Testing that it does not work when the variable is 0" -set global event_scheduler=0; +set global event_scheduler=2; select definer, name, db from mysql.event; select get_lock("test_lock1", 20); create event закачка on schedule every 10 hour do select get_lock("test_lock1", 20); --echo "Should return 1 row" select definer, name, db from mysql.event; ---echo "Should be 0 processes" +--echo "Should be only 1 process" select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock("test_lock1"); drop event закачка; @@ -362,7 +360,7 @@ create event закачка on schedule every 10 hour do select get_lock("test_l --echo "Let some time pass to the event starts" --sleep 2 --echo "Should have only 2 processes: the scheduler and the locked event" -select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;--echo "Release the mutex, the event worker should finish." --echo "Release the mutex, the event worker should finish." select release_lock("test_lock2"); drop event закачка; @@ -379,18 +377,11 @@ set global event_scheduler=1; select get_lock("test_lock2_1", 20); create event закачка21 on schedule every 10 hour do select get_lock("test_lock2_1", 20); --sleep 1 ---echo "Should see 1 process, locked on get_lock(" -#select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; ---echo "Shutting down the scheduler, it should wait for the running event" -set global event_scheduler=0; ---sleep 1 ---echo "Should have only 2 processes: the scheduler and the locked event" +--echo "Should have only 3 processes: the scheduler, our conn and the locked event" +select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +set global event_scheduler=2; +--echo "Should have only our process now:" select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; ---echo "Release the lock so the child process should finish. Hence the scheduler also" -select release_lock("test_lock2_1"); ---sleep 1 ---echo "Should see 0 processes now:" -select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; drop event закачка21; #### @@ -418,6 +409,7 @@ create event white_space on schedule every 10 hour disable do select 2; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; +select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; drop event white_space; create event white_space on schedule every 10 hour disable do select 3; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; @@ -426,7 +418,7 @@ drop event white_space; # END: BUG #17453: Creating Event crash the server # -##set global event_scheduler=1; +# # Bug#17403 "Events: packets out of order with show create event" # create event e1 on schedule every 1 year do set @a = 5; @@ -440,7 +432,7 @@ drop event e1; ##select get_lock("test_lock3", 20); ##create event закачка on schedule every 10 hour do select get_lock("test_lock3", 20); ##select sleep(2); -##show processlist; +##select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; ##drop event закачка; ##select release_lock("test_lock3"); @@ -450,14 +442,14 @@ drop event e1; ##select get_lock("test_lock4", 20); ##create event закачка4 on schedule every 1 second do select get_lock("test_lock4", 20); ##select sleep(3); -##--replace_column 1 # 6 # +##select /*6*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; ##drop event закачка4; ##select release_lock("test_lock4"); -##set global event_scheduler=0; +##set global event_scheduler=2; ##select sleep(2); ##--replace_column 1 # 6 # +##show processlist; ##select count(*) from mysql.event; drop database events_test; - diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index 3f339ff0398..e3b79a6bd13 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -30,13 +30,13 @@ set @a=3; CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5; call p_16(); --echo "Here we used to crash!" ---error 1516 +--error ER_EVENT_ALREADY_EXISTS call p_16(); ---error 1516 +--error ER_EVENT_ALREADY_EXISTS call p_16(); DROP EVENT e_16; CALL p_16(); ---error 1516 +--error ER_EVENT_ALREADY_EXISTS CALL p_16(); DROP PROCEDURE p_16; DROP EVENT e_16; @@ -47,9 +47,9 @@ DROP EVENT e_16; # # Start - 16396: Events: Distant-future dates become past dates # ---error 1504 +--error ER_WRONG_VALUE create event e_55 on schedule at 99990101000000 do drop table t; ---error 1504 +--error ER_WRONG_VALUE create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t; --error ER_EVENT_ENDS_BEFORE_STARTS create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t; @@ -60,7 +60,7 @@ create event e_55 on schedule every 10 minute ends 99990101000000 do drop table # # Start - 16407: Events: Changes in sql_mode won't be taken into account # -set global event_scheduler=0; +set global event_scheduler=2; --echo "Wait a bit to settle down" --sleep 1 delete from mysql.event; @@ -79,7 +79,7 @@ delimiter ;| --echo "Now if everything is fine the event has compiled and is locked select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock('test_bug16407'); -set global event_scheduler= 0; +set global event_scheduler= 2; select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; --echo "Let's check whether we change the sql_mode on ALTER EVENT" set sql_mode='traditional'; @@ -121,9 +121,9 @@ set global event_scheduler= 1; --sleep 1 select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock('ee_16407_2'); ---sleep 3 +--sleep 2 select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; -set global event_scheduler= 0; +set global event_scheduler= 2; select * from events_smode_test order by ev_name, a; --echo "OK, last check before we drop them" select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; @@ -156,7 +156,7 @@ set global event_scheduler= 1; --echo "Should have 2 locked processes" select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock('ee_16407_5'); ---sleep 3 +--sleep 2 --echo "Should have 0 processes locked" select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select * from events_smode_test order by ev_name, a; @@ -166,7 +166,7 @@ drop event ee_16407_5; drop event ee_16407_6; drop procedure ee_16407_5_pendant; drop procedure ee_16407_6_pendant; -set global event_scheduler= 0; +set global event_scheduler= 2; drop table events_smode_test; set sql_mode=@old_sql_mode; # diff --git a/mysql-test/t/events_logs_tests.test b/mysql-test/t/events_logs_tests.test index 21adc17d5b8..6d3b3292630 100644 --- a/mysql-test/t/events_logs_tests.test +++ b/mysql-test/t/events_logs_tests.test @@ -10,19 +10,21 @@ BEGIN END| delimiter ;| --echo "Check General Query Log" -SET GLOBAL event_scheduler=0; +SET GLOBAL event_scheduler=2; create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual; TRUNCATE mysql.general_log; --echo "1 row, the current statement!" +--replace_column 1 USER_HOST call select_general_log(); SET GLOBAL event_scheduler=1; --echo "Wait the scheduler to start" --echo "Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log" --sleep 2 +--replace_column 1 USER_HOST call select_general_log(); DROP PROCEDURE select_general_log; DROP EVENT log_general; -SET GLOBAL event_scheduler=0; +SET GLOBAL event_scheduler=2; --sleep 1 --echo "Check slow query log" @@ -50,13 +52,14 @@ SET @old_session_long_query_time:=@@long_query_time; SHOW VARIABLES LIKE 'log_slow_queries'; DROP FUNCTION get_value; TRUNCATE mysql.slow_log; +--replace_column 1 USER_HOST SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; --echo "Set new values" SET GLOBAL long_query_time=4; -SET SESSION long_query_time=2; +SET SESSION long_query_time=1; --echo "Check that logging is working" -SELECT SLEEP(3); ---replace_regex /00:00:0[3-5]/SLEEPVAL/ +SELECT SLEEP(2); +--replace_column 1 USER_HOST 2 SLEEPVAL SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; TRUNCATE mysql.slow_log; CREATE TABLE slow_event_test (slo_val tinyint, val tinyint); @@ -73,14 +76,15 @@ SELECT * FROM slow_event_test; SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; --echo "This should go to the slow log" SET SESSION long_query_time=10; +SET GLOBAL long_query_time=1; DROP EVENT long_event; -CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(5); +CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2); --echo "Sleep some more time than the actual event run will take" ---sleep 7 +--sleep 3 --echo "Check our table. Should see 2 rows" SELECT * FROM slow_event_test; ---echo "Check slow log. Should see 1 row because 5 is over the threshold of 4 for GLOBAL, though under SESSION which is 10" ---replace_regex /00:00:0[5-7]/SLEEPVAL/ +--echo "Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10" +--replace_column 1 USER_HOST 2 SLEEPVAL SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; DROP EVENT long_event2; SET GLOBAL long_query_time =@old_global_long_query_time; diff --git a/mysql-test/t/events_microsec.test b/mysql-test/t/events_microsec.test index 34855fdadff..e01120a0756 100644 --- a/mysql-test/t/events_microsec.test +++ b/mysql-test/t/events_microsec.test @@ -1,55 +1,15 @@ create database if not exists events_test; use events_test; ---error 1235 +--error ER_NOT_SUPPORTED_YET CREATE EVENT micro_test ON SCHEDULE EVERY 100 MICROSECOND DO SELECT 1; ---error 1235 +--error ER_NOT_SUPPORTED_YET CREATE EVENT micro_test ON SCHEDULE EVERY 100 DAY_MICROSECOND DO SELECT 1; ---error 1235 +--error ER_NOT_SUPPORTED_YET CREATE EVENT micro_test ON SCHEDULE EVERY 100 HOUR_MICROSECOND DO SELECT 1; ---error 1235 +--error ER_NOT_SUPPORTED_YET CREATE EVENT micro_test ON SCHEDULE EVERY 100 MINUTE_MICROSECOND DO SELECT 1; ---error 1235 +--error ER_NOT_SUPPORTED_YET CREATE EVENT micro_test ON SCHEDULE EVERY 100 SECOND_MICROSECOND DO SELECT 1; ---echo "Now create normal event and change it on SQL level" -CREATE EVENT micro_test2 ON SCHEDULE EVERY 1 MONTH DO SELECT 1; -UPDATE mysql.event SET interval_field='MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; ---error 1235 -SHOW CREATE EVENT micro_test2; -SET GLOBAL event_scheduler=0; ---sleep 1 ---echo "Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -UPDATE mysql.event SET interval_field='DAY_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; ---error 1235 -SHOW CREATE EVENT micro_test2; -SET GLOBAL event_scheduler=0; ---sleep 1 ---echo "Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -UPDATE mysql.event SET interval_field='SECOND_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; ---error 1235 -SHOW CREATE EVENT micro_test2; -SET GLOBAL event_scheduler=0; ---sleep 1 ---echo "Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -UPDATE mysql.event SET interval_field='HOUR_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; ---error 1235 -SHOW CREATE EVENT micro_test2; -SET GLOBAL event_scheduler=0; ---sleep 1 ---echo "Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -UPDATE mysql.event SET interval_field='MINUTE_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2'; ---error 1235 -SHOW CREATE EVENT micro_test2; -SET GLOBAL event_scheduler=0; ---sleep 1 ---echo "Should not be running:" -SHOW VARIABLES like 'event_scheduler'; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER='event_scheduler'; -DROP EVENT micro_test2; - drop database events_test; diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test index ae3cc7d5fac..a73d25cd8ee 100644 --- a/mysql-test/t/events_scheduling.test +++ b/mysql-test/t/events_scheduling.test @@ -15,7 +15,7 @@ CREATE EVENT start_n_end DO INSERT INTO table_2 VALUES(1); --sleep 5 CREATE EVENT only_one_time ON SCHEDULE EVERY 2 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_3 VALUES(1); -CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_4 VALUES(1); +CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND ON COMPLETION PRESERVE DO INSERT INTO table_4 VALUES(1); --sleep 5 SELECT IF(SUM(a) >= 4, 'OK', 'ERROR') FROM table_1; SELECT IF(SUM(a) >= 5, 'OK', 'ERROR') FROM table_2; @@ -28,8 +28,8 @@ DROP EVENT start_n_end; --echo "Already dropped because ended. Therefore an error." --error ER_EVENT_DOES_NOT_EXIST DROP EVENT only_one_time; ---echo "Already dropped because ended. Therefore an error." ---error ER_EVENT_DOES_NOT_EXIST +--echo "Should be preserved" +SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS; DROP EVENT two_time; DROP TABLE table_1; DROP TABLE table_2; diff --git a/mysql-test/t/events_stress.test b/mysql-test/t/events_stress.test index f6eed79425c..8d0034c232e 100644 --- a/mysql-test/t/events_stress.test +++ b/mysql-test/t/events_stress.test @@ -2,78 +2,124 @@ CREATE DATABASE IF NOT EXISTS events_test; # # DROP DATABASE test start (bug #16406) # -CREATE DATABASE events_test2; -USE events_test2; +CREATE DATABASE events_conn1_test2; +CREATE TABLE events_test.fill_it(test_name varchar(20), occur datetime); +CREATE USER event_user2@localhost; +CREATE DATABASE events_conn2_db; +GRANT ALL ON *.* TO event_user2@localhost; +CREATE USER event_user3@localhost; +CREATE DATABASE events_conn3_db; +GRANT ALL ON *.* TO event_user3@localhost; +connect (conn2,localhost,event_user2,,events_conn2_db); +--echo "In the second connection we create some events which won't be dropped till the end" +--disable_query_log +let $1= 50; +while ($1) +{ + eval CREATE EVENT conn2_ev$1 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO events_test.fill_it VALUES("conn2_ev$1", NOW()); + dec $1; +} +--enable_query_log +connect (conn3,localhost,event_user3,,events_conn3_db); +--echo "In the second connection we create some events which won't be dropped till the end" +--disable_query_log +let $1= 50; +while ($1) +{ + eval CREATE EVENT conn3_ev$1 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO events_test.fill_it VALUES("conn3_ev$1", NOW()); + dec $1; +} +--enable_query_log +connection default; +USE events_conn1_test2; CREATE EVENT ev_drop1 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; CREATE EVENT ev_drop2 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; CREATE EVENT ev_drop3 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; USE events_test; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; -DROP DATABASE events_test2; -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; +DROP DATABASE events_conn1_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; --echo "Now testing stability - dropping db -> events while they are running" -CREATE DATABASE events_test2; -USE events_test2; +CREATE DATABASE events_conn1_test2; +USE events_conn1_test2; --disable_query_log -let $1= 1000; +let $1= 50; while ($1) { - eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + eval CREATE EVENT conn1_round1_ev$1 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO events_test.fill_it VALUES("conn1_round1_ev$1", NOW()); dec $1; } --enable_query_log -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; SET GLOBAL event_scheduler=1; ---sleep 4 -DROP DATABASE events_test2; +--sleep 6 +DROP DATABASE events_conn1_test2; -SET GLOBAL event_scheduler=0; ---sleep 2 -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; -CREATE DATABASE events_test3; -USE events_test3; +SET GLOBAL event_scheduler=2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; +CREATE DATABASE events_conn1_test3; +USE events_conn1_test3; --disable_query_log -let $1= 950; +let $1= 50; while ($1) { - eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + eval CREATE EVENT conn1_round2_ev$1 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO events_test.fill_it VALUES("conn1_round2_ev$1", NOW()); dec $1; } --enable_query_log -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test3'; ---sleep 3 -CREATE DATABASE events_test4; -USE events_test4; +SET GLOBAL event_scheduler=1; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test3'; +CREATE DATABASE events_conn1_test4; +USE events_conn1_test4; --disable_query_log -let $1= 860; +let $1= 50; while ($1) { - eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + eval CREATE EVENT conn1_round3_ev$1 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO events_test.fill_it VALUES("conn1_round3_ev$1", NOW()); dec $1; } --enable_query_log - -CREATE DATABASE events_test2; -USE events_test2; +CREATE DATABASE events_conn1_test2; +USE events_conn1_test2; --disable_query_log -let $1= 1050; +let $1= 50; while ($1) { - eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + eval CREATE EVENT ev_round4_drop$1 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO events_test.fill_it VALUES("conn1_round4_ev$1", NOW()); dec $1; } --enable_query_log -SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; --sleep 6 -DROP DATABASE events_test2; -SET GLOBAL event_scheduler=0; -DROP DATABASE events_test3; -SET GLOBAL event_scheduler=1; -DROP DATABASE events_test4; +connection conn2; +--send +DROP DATABASE events_conn2_db; +connection conn3; +--send +DROP DATABASE events_conn3_db; +connection default; +--send +DROP DATABASE events_conn1_test2; +DROP DATABASE events_conn1_test3; +SET GLOBAL event_scheduler=2; +DROP DATABASE events_conn1_test4; SET GLOBAL event_scheduler=1; +connection conn2; +reap; +disconnect conn2; +connection conn3; +reap; +disconnect conn3; +connection default; USE events_test; +DROP TABLE fill_it; +--disable_query_log +DROP USER event_user2@localhost; +DROP USER event_user3@localhost; +--enable_query_log # # DROP DATABASE test end (bug #16406) # diff --git a/mysql-test/t/explain.test b/mysql-test/t/explain.test index 2a3a23c5f96..a38771db233 100644 --- a/mysql-test/t/explain.test +++ b/mysql-test/t/explain.test @@ -43,3 +43,12 @@ drop table ÔÁÂ; set names latin1; # End of 4.1 tests + + +# +# Bug#15463: EXPLAIN SELECT..INTO hangs the client (QB, command line) +# +select 3 into @v1; +explain select 3 into @v1; + +# End of 5.0 tests. diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index c667f90940c..fb9470c16dd 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -539,77 +539,6 @@ INSERT INTO t1 VALUES SELECT MAX(id) FROM t1 WHERE id < 3 AND a=2 AND b=6; DROP TABLE t1; -# -# Bug #12882 min/max inconsistent on empty table -# - ---disable_warnings -create table t1m (a int) engine=myisam; -create table t1i (a int) engine=innodb; -create table t2m (a int) engine=myisam; -create table t2i (a int) engine=innodb; ---enable_warnings -insert into t2m values (5); -insert into t2i values (5); - -# test with MyISAM -select min(a) from t1m; -select min(7) from t1m; -select min(7) from DUAL; -explain select min(7) from t2m join t1m; -select min(7) from t2m join t1m; - -select max(a) from t1m; -select max(7) from t1m; -select max(7) from DUAL; -explain select max(7) from t2m join t1m; -select max(7) from t2m join t1m; - -select 1, min(a) from t1m where a=99; -select 1, min(a) from t1m where 1=99; -select 1, min(1) from t1m where a=99; -select 1, min(1) from t1m where 1=99; - -select 1, max(a) from t1m where a=99; -select 1, max(a) from t1m where 1=99; -select 1, max(1) from t1m where a=99; -select 1, max(1) from t1m where 1=99; - -# test with InnoDB -select min(a) from t1i; -select min(7) from t1i; -select min(7) from DUAL; -explain select min(7) from t2i join t1i; -select min(7) from t2i join t1i; - -select max(a) from t1i; -select max(7) from t1i; -select max(7) from DUAL; -explain select max(7) from t2i join t1i; -select max(7) from t2i join t1i; - -select 1, min(a) from t1i where a=99; -select 1, min(a) from t1i where 1=99; -select 1, min(1) from t1i where a=99; -select 1, min(1) from t1i where 1=99; - -select 1, max(a) from t1i where a=99; -select 1, max(a) from t1i where 1=99; -select 1, max(1) from t1i where a=99; -select 1, max(1) from t1i where 1=99; - -# mixed MyISAM/InnoDB test -explain select count(*), min(7), max(7) from t1m, t1i; -select count(*), min(7), max(7) from t1m, t1i; - -explain select count(*), min(7), max(7) from t1m, t2i; -select count(*), min(7), max(7) from t1m, t2i; - -explain select count(*), min(7), max(7) from t2m, t1i; -select count(*), min(7), max(7) from t2m, t1i; - -drop table t1m, t1i, t2m, t2i; - # End of 4.1 tests # diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 351d1fc2c92..8ddf1fbe314 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -220,3 +220,15 @@ set @str=NULL; drop table t2; drop table t1; +# BUG#19618: Crash in range optimizer for +# "unsigned_keypart NOT IN(negative_number,...)" +# (introduced in fix BUG#15872) +create table t1 ( + some_id smallint(5) unsigned, + key (some_id) +); +insert into t1 values (1),(2); +select some_id from t1 where some_id not in(2,-1); +select some_id from t1 where some_id not in(-4,-1,-4); +select some_id from t1 where some_id not in(-4,-1,3423534,2342342); +drop table t1; diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 85cedee0f4a..7f809dbc4a1 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -666,7 +666,14 @@ select rpad(i, 7, ' ') as t from t1; --disable_metadata drop table t1; -# End of 4.1 tests +# +# Bug #10418: LOAD_FILE does not behave like in manual if file does not exist +# + +select load_file("lkjlkj"); +select ifnull(load_file("lkjlkj"),"it's null"); + +--echo End of 4.1 tests # # Bug #13361: SELECT FORMAT(<decimal field with null>, 2) crashes @@ -691,4 +698,10 @@ select cast(rtrim(' 20.06 ') as decimal(19,2)); select cast(ltrim(' 20.06 ') as decimal(19,2)); select cast(rtrim(ltrim(' 20.06 ')) as decimal(19,2)); +# +# Bug #13975: "same string" + 0 has 2 different results +# +select conv("18383815659218730760",10,10) + 0; +select "18383815659218730760" + 0; + --echo End of 5.0 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 4e4fb8f777a..5c993028cb4 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -352,12 +352,6 @@ INSERT INTO t1 VALUES (NOW()); SELECT count(*) FROM t1 WHERE d>FROM_DAYS(TO_DAYS(@TMP)) AND d<=FROM_DAYS(TO_DAYS(@TMP)+1); DROP TABLE t1; - -# End of 4.1 tests - -explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1, - timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2; - # # Bug #10568 # @@ -367,6 +361,18 @@ select last_day('2005-00-01'); select last_day('2005-01-00'); # +# Bug #18501: monthname and NULLs +# + +select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), + monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); + +--echo End of 4.1 tests + +explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1, + timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2; + +# # Bug #10590: %h, %I, and %l format specifies should all return results in # the 0-11 range # @@ -468,4 +474,23 @@ SELECT * FROM t1, t2 DROP TABLE t1,t2; -# End of 5.0 tests +--echo End of 5.0 tests + +# +# Bug #18997 +# + +select date_sub("0050-01-01 00:00:01",INTERVAL 2 SECOND); +select date_sub("0199-01-01 00:00:01",INTERVAL 2 SECOND); +select date_add("0199-12-31 23:59:59",INTERVAL 2 SECOND); +select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND); +select date_sub("0200-01-01 00:00:01",INTERVAL 1 SECOND); +select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND); +select date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR); +select date_sub("50-01-01 00:00:01",INTERVAL 2 SECOND); +select date_sub("90-01-01 00:00:01",INTERVAL 2 SECOND); +select date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND); +select date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND); + + +--echo End of 5.1 tests diff --git a/mysql-test/t/heap.test b/mysql-test/t/heap.test index 82294db336d..e501fce1eeb 100644 --- a/mysql-test/t/heap.test +++ b/mysql-test/t/heap.test @@ -458,4 +458,16 @@ create table t2 (c varchar(10)) engine=memory; show table status like 't_'; drop table t1, t2; +# +# BUG#18233 - Memory tables INDEX USING HASH (a,b) returns 1 row on +# SELECT WHERE a= AND b= +# +CREATE TABLE t1(a VARCHAR(1), b VARCHAR(2), c VARCHAR(256), + KEY(a), KEY(b), KEY(c)) ENGINE=MEMORY; +INSERT INTO t1 VALUES('a','aa',REPEAT('a', 256)),('a','aa',REPEAT('a',256)); +SELECT COUNT(*) FROM t1 WHERE a='a'; +SELECT COUNT(*) FROM t1 WHERE b='aa'; +SELECT COUNT(*) FROM t1 WHERE c=REPEAT('a',256); +DROP TABLE t1; + # End of 5.0 tests diff --git a/mysql-test/t/im_cmd_line.imtest b/mysql-test/t/im_cmd_line.imtest new file mode 100644 index 00000000000..00e8351535e --- /dev/null +++ b/mysql-test/t/im_cmd_line.imtest @@ -0,0 +1,68 @@ +########################################################################### +# +# Tests for user-management command-line options. +# +########################################################################### + +--source include/im_check_os.inc + +########################################################################### + +# List users so we are sure about starting conditions. + +--echo --> Listing users... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null +--echo + +# Add a new user. + +--echo ==> Adding user 'testuser'... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --add-user --username=testuser --password=abc 2>&1 >/dev/null +--echo + +--echo --> IM password file: +--exec cat $IM_PASSWORD_PATH +--echo --> EOF +--echo + +--echo --> Printing out line for 'testuser'... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --passwd --username=testuser --password=abc | tail -1 +--echo + +--echo --> Listing users... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null +--echo + +# Edit user's attributes. + +--echo ==> Changing the password of 'testuser'... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --edit-user --username=testuser --password=xyz 2>&1 >/dev/null +--echo + +--echo --> IM password file: +--exec cat $IM_PASSWORD_PATH +--echo --> EOF +--echo + +--echo --> Printing out line for 'testuser'... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --passwd --username=testuser --password=xyz | tail -1 +--echo + +--echo --> Listing users... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null +--echo + +# Drop user. + +--echo ==> Dropping user 'testuser'... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --drop-user --username=testuser 2>&1 >/dev/null +--echo + +--echo --> IM password file: +--exec cat $IM_PASSWORD_PATH +--echo --> EOF +--echo + +--echo --> Listing users... +--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --list-users 2>&1 >/dev/null +--echo diff --git a/mysql-test/t/im_daemon_life_cycle-im.opt b/mysql-test/t/im_daemon_life_cycle-im.opt index 21c01191e4c..3a45c7a41f7 100644 --- a/mysql-test/t/im_daemon_life_cycle-im.opt +++ b/mysql-test/t/im_daemon_life_cycle-im.opt @@ -1,2 +1,3 @@ --run-as-service --log=$MYSQLTEST_VARDIR/log/im.log +--monitoring-interval=1 diff --git a/mysql-test/t/im_daemon_life_cycle.imtest b/mysql-test/t/im_daemon_life_cycle.imtest index 87388d7c1e6..d173ce2a6e2 100644 --- a/mysql-test/t/im_daemon_life_cycle.imtest +++ b/mysql-test/t/im_daemon_life_cycle.imtest @@ -10,6 +10,9 @@ ########################################################################### +--sleep 3 +# should be longer than monitoring interval and enough to start instance. + SHOW INSTANCES; --exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted diff --git a/mysql-test/t/im_instance_conf-im.opt b/mysql-test/t/im_instance_conf-im.opt new file mode 100644 index 00000000000..34b74ce0c95 --- /dev/null +++ b/mysql-test/t/im_instance_conf-im.opt @@ -0,0 +1 @@ +--monitoring-interval=1 diff --git a/mysql-test/t/im_instance_conf.imtest b/mysql-test/t/im_instance_conf.imtest new file mode 100644 index 00000000000..17703fdd303 --- /dev/null +++ b/mysql-test/t/im_instance_conf.imtest @@ -0,0 +1,228 @@ +########################################################################### +# +# This test suite checks the following statements: +# - CREATE INSTANCE <instance_name> [option1[=option1_value], ...]; +# - DROP INSTANCE <instance_name>; +# +# For CREATE INSTANCE we check that: +# - CREATE INSTANCE succeeds for non-existing instance; +# - CREATE INSTANCE fails for existing instance; +# - CREATE INSTANCE can get additional options with and w/o values; +# - CREATE INSTANCE parses options and handles grammar errors correctly. +# Check that strings with spaces are handled correctly, unknown (for +# mysqld) options should also be handled; +# - CREATE INSTANCE updates both config file and internal configuration cache; +# - CREATE INSTANCE allows to create instances only with properly formed +# names (mysqld*); +# +# For DROP INSTANCE we check that: +# - DROP INSTANCE succeeds for existing instance; +# - DROP INSTANCE fails for non-existing instance; +# - DROP INSTANCE fails for active instance. +# - DROP INSTANCE updates both config file and internal configuration cache; +# +########################################################################### + +--source include/im_check_os.inc + +########################################################################### +# +# Check starting conditions. +# +########################################################################### + +# Check that the configuration file contains only instances that we expect. + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +# Check that mysqld1 is reported as running. + +--sleep 3 +# should be longer than monitoring interval and enough to start instance. + +SHOW INSTANCES; + +# Check that the expected mysqld instance is actually run (check that we can +# connect and execute something). + +--echo +--echo ---> connection: mysql1_con +--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) +--connection mysql1_con + +SHOW VARIABLES LIKE 'server_id'; + +--disconnect mysql1_con + +--echo +--echo ---> connection: default +--connection default + +########################################################################### +# +# CREATE INSTANCE tests. +# +########################################################################### + +# Check that CREATE INSTANCE succeeds for non-existing instance and also check +# that both config file and internal configuration cache have been updated. + +CREATE INSTANCE mysqld3; + +SHOW INSTANCES; + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- + +# Check that CREATE INSTANCE fails for existing instance. Let's all three +# existing instances (running one, stopped one and just created one). Just in +# case... + +--error 3012 # ER_CREATE_EXISTING_INSTANCE +CREATE INSTANCE mysqld1; + +--error 3012 # ER_CREATE_EXISTING_INSTANCE +CREATE INSTANCE mysqld2; + +--error 3012 # ER_CREATE_EXISTING_INSTANCE +CREATE INSTANCE mysqld3; + +# Check that CREATE INSTANCE can get additional options with and w/o values. +# Ensure that config file is updated properly. + +# - without values; + +--echo -------------------------------------------------------------------- +--exec grep nonguarded $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +CREATE INSTANCE mysqld4 nonguarded; + +SHOW INSTANCES; + +--echo -------------------------------------------------------------------- +--exec grep nonguarded $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +# - with value; + +--echo -------------------------------------------------------------------- +--exec grep test-A $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep test-B $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +CREATE INSTANCE mysqld5 test-A = 000, test-B = test; + +SHOW INSTANCES; + +--echo -------------------------------------------------------------------- +--exec grep test-A $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-B $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +# Check that CREATE INSTANCE parses options and handles grammar errors +# correctly. Check that strings with spaces are handled correctly, +# unknown (for mysqld) options should also be handled. + +# - check handling of extra spaces; + +--echo -------------------------------------------------------------------- +--exec grep test-C $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +CREATE INSTANCE mysqld6 test-C1 = 10 , test-C2 = 02 ; + +SHOW INSTANCES; + +--echo -------------------------------------------------------------------- +--exec grep test-C1 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-C2 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +# - check handling of grammar error; + +--echo -------------------------------------------------------------------- +--exec grep test-D $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep test-E $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +--error ER_SYNTAX_ERROR +CREATE INSTANCE mysqld7 test-D = test-D-value ; +SHOW INSTANCES; + +--error ER_SYNTAX_ERROR +CREATE INSTANCE mysqld8 test-E 0 ; +SHOW INSTANCES; + +--error ER_SYNTAX_ERROR +CREATE INSTANCE mysqld8 test-F = ; +SHOW INSTANCES; + +--echo -------------------------------------------------------------------- +--exec grep test-D $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep test-E $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +# - check parsing of string option values + +--echo -------------------------------------------------------------------- +--exec grep test-1 $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep test-2 $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep test-3 $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep test-4 $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +CREATE INSTANCE mysqld9 test-1=" hello world ", test-2=' '; +SHOW INSTANCES; + +CREATE INSTANCE mysqld9a test-3='\b\babc\sdef'; +# test-3='abc def' +SHOW INSTANCES; + +CREATE INSTANCE mysqld9b test-4='abc\tdef', test-5='abc\ndef'; +SHOW INSTANCES; + +CREATE INSTANCE mysqld9c test-6="abc\rdef", test-7="abc\\def"; +# test-6=abc +SHOW INSTANCES; + +--error ER_SYNTAX_ERROR +CREATE INSTANCE mysqld10 test-bad=' \ '; +SHOW INSTANCES; + +--echo -------------------------------------------------------------------- +--exec grep test-1 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-2 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-3 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-4 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-5 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-6 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-7 $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- +--exec grep test-bad $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + + +# Check that CREATE INSTANCE allows to create instances only with properly +# formed names (mysqld*). + +--error 3014 # ER_MALFORMED_INSTANCE_NAME +CREATE INSTANCE qqq1; + diff --git a/mysql-test/t/im_life_cycle-im.opt b/mysql-test/t/im_life_cycle-im.opt new file mode 100644 index 00000000000..34b74ce0c95 --- /dev/null +++ b/mysql-test/t/im_life_cycle-im.opt @@ -0,0 +1 @@ +--monitoring-interval=1 diff --git a/mysql-test/t/im_life_cycle.imtest b/mysql-test/t/im_life_cycle.imtest index 246843a022b..d71cdc86624 100644 --- a/mysql-test/t/im_life_cycle.imtest +++ b/mysql-test/t/im_life_cycle.imtest @@ -17,11 +17,15 @@ # ########################################################################### +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.1. +--echo -------------------------------------------------------------------- + +--sleep 3 +# should be longer than monitoring interval and enough to start instance. + SHOW INSTANCES; ---replace_column 3 VERSION_NUMBER 4 VERSION -SHOW INSTANCE STATUS mysqld1; ---replace_column 3 VERSION_NUMBER 4 VERSION -SHOW INSTANCE STATUS mysqld2; ########################################################################### # @@ -33,20 +37,22 @@ SHOW INSTANCE STATUS mysqld2; # ########################################################################### +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.2. +--echo -------------------------------------------------------------------- + START INSTANCE mysqld2; -# FIXME +# FIXME: START INSTANCE should be synchronous. --sleep 3 +# should be longer than monitoring interval and enough to start instance. SHOW INSTANCES; ---replace_column 3 VERSION_NUMBER 4 VERSION -SHOW INSTANCE STATUS mysqld1; ---replace_column 3 VERSION_NUMBER 4 VERSION -SHOW INSTANCE STATUS mysqld2; ---connect (mysql_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) +--connect (mysql_con,localhost,root,,mysql,$IM_MYSQLD2_PORT,$IM_MYSQLD2_SOCK) --connection mysql_con ---replace_result $IM_MYSQLD1_PORT IM_MYSQLD1_PORT +--replace_result $IM_MYSQLD2_PORT IM_MYSQLD2_PORT SHOW VARIABLES LIKE 'port'; --connection default @@ -61,9 +67,15 @@ SHOW VARIABLES LIKE 'port'; # ########################################################################### +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.3. +--echo -------------------------------------------------------------------- + STOP INSTANCE mysqld2; -# FIXME +# FIXME: STOP INSTANCE should be synchronous. --sleep 3 +# should be longer than monitoring interval and enough to stop instance. SHOW INSTANCES; --replace_column 3 VERSION_NUMBER 4 VERSION @@ -81,16 +93,17 @@ SHOW INSTANCE STATUS mysqld2; # ########################################################################### ---error 3000 +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.4. +--echo -------------------------------------------------------------------- + +--error 3000 # ER_BAD_INSTANCE_NAME START INSTANCE mysqld3; ---error 3002 +--error 3002 # ER_INSTANCE_ALREADY_STARTED START INSTANCE mysqld1; -# FIXME TODO -# BUG#12813: START/STOP INSTANCE commands accept a list as argument -# START INSTANCE mysqld1, mysqld2; - ########################################################################### # # 1.1.5. Check that Instance Manager reports correct errors for 'STOP INSTANCE' @@ -101,39 +114,54 @@ START INSTANCE mysqld1; # ########################################################################### ---error 3000 +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.5. +--echo -------------------------------------------------------------------- + +--error 3000 # ER_BAD_INSTANCE_NAME STOP INSTANCE mysqld3; # TODO: IM should be fixed. # BUG#12673: Instance Manager allows to stop the instance many times -# --error 3002 +# --error 3002 # ER_INSTANCE_ALREADY_STARTED # STOP INSTANCE mysqld2; -# FIXME TODO -# BUG#12813: START/STOP INSTANCE commands accept a list as argument -# STOP INSTANCE mysqld1, mysqld2; - ########################################################################### # # 1.1.6. Check that Instance Manager is able to restart guarded instances. # ########################################################################### +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.6. +--echo -------------------------------------------------------------------- + SHOW INSTANCES; --exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted +--sleep 3 +# should be longer than monitoring interval and enough to start instance. + ########################################################################### # # 1.1.7. Check that Instance Manager does not restart non-guarded instance. # ########################################################################### +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.7. +--echo -------------------------------------------------------------------- + SHOW INSTANCES; START INSTANCE mysqld2; -# FIXME +# FIXME: START INSTANCE should be synchronous. --sleep 3 +# should be longer than monitoring interval and enough to start instance. SHOW INSTANCES; @@ -147,7 +175,13 @@ SHOW INSTANCES; # incomplete SHOW INSTANCE STATUS command. # ########################################################################### ---error 1149 + +--echo +--echo -------------------------------------------------------------------- +--echo -- 1.1.8. +--echo -------------------------------------------------------------------- + +--error ER_SYNTAX_ERROR SHOW INSTANCE STATUS; # @@ -159,8 +193,13 @@ SHOW INSTANCE STATUS; # a list as argument. # ---error 1149 +--echo +--echo -------------------------------------------------------------------- +--echo -- BUG#12813 +--echo -------------------------------------------------------------------- + +--error ER_SYNTAX_ERROR START INSTANCE mysqld1,mysqld2,mysqld3; ---error 1149 +--error ER_SYNTAX_ERROR STOP INSTANCE mysqld1,mysqld2,mysqld3; diff --git a/mysql-test/t/im_options.imtest b/mysql-test/t/im_options.imtest new file mode 100644 index 00000000000..cd905416cda --- /dev/null +++ b/mysql-test/t/im_options.imtest @@ -0,0 +1,268 @@ +########################################################################### +# +# This test suite checks the following statements: +# - SET <instance id>.<option name> = <option value>; +# - UNSET <instance id>.<option name> = <option value>; +# - FLUSH INSTANCES; +# +# For SET/UNSET we check that: +# - SET ignores spaces correctly; +# - UNSET does not allow option-value part (= <option value>); +# - SET/UNSET can be applied several times w/o error; +# - SET/UNSET is allowed only for stopped instances; +# - SET/UNSET updates both the configuration cache in IM and +# the configuration file; +# +# For FLUSH INSTANCES we check that: +# - FLUSH INSTANCES is allowed only when all instances are stopped; +# +# According to the IM implementation details, we should play at least with the +# following options: +# - server_id +# - port +# - nonguarded + +# Let's test SET statement on the option 'server_id'. It's expected that +# originally the instances have the following server ids and states: +# - mysqld1: server_id: 1; running (online) +# - mysqld2: server_id: 2; stopped (offline) +# +########################################################################### + +--source include/im_check_os.inc + +########################################################################### +# +# Check starting conditions. +# +########################################################################### + +# - check the configuration file; + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- + +# - check the running instances. + +--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) + +--connection mysql1_con + +SHOW VARIABLES LIKE 'server_id'; + +--connection default + +# - check the internal cache. + +SHOW INSTANCES; + +########################################################################### +# +# Check that SET/UNSET is allowed only for stopped instances. +# +########################################################################### + +# - check that SET/UNSET is denied for running instances; + +--error 3015 # ER_INSTANCE_IS_ACTIVE +UNSET mysqld1.server_id; + +--error 3015 # ER_INSTANCE_IS_ACTIVE +SET mysqld1.server_id = 11; + +# - check that SET/UNSET is denied for active instances: +# - create dummy misconfigured instance; +# - start it; +# - try to set/unset options; + +CREATE INSTANCE mysqld3 datadir = '/'; +START INSTANCE mysqld3; + +# FIXME: START INSTANCE should be synchronous. +--sleep 3 +# should be longer than monitoring interval and enough to start instance. + +# NOTE: We can not analyze state of the instance here -- it can be Failed or +# Starting because Instance Manager is trying to start the misconfigured +# instance several times. + +--error 3015 # ER_INSTANCE_IS_ACTIVE +UNSET mysqld3.server_id; + +--error 3015 # ER_INSTANCE_IS_ACTIVE +SET mysqld3.server_id = 11; + +STOP INSTANCE mysqld3; + +# FIXME: STOP INSTANCE should be synchronous. +--sleep 3 +# should be longer than monitoring interval and enough to stop instance. + +--replace_column 3 VERSION_NUMBER 4 VERSION +SHOW INSTANCE STATUS mysqld3; + +# - check that SET/UNSET succeed for stopped instances; +# - check that SET/UNSET can be applied multiple times; + +UNSET mysqld2.server_id; +UNSET mysqld2.server_id; + +--replace_column 2 option_value +SHOW INSTANCE OPTIONS mysqld2; + +SET mysqld2.server_id = 2; +SET mysqld2.server_id = 2; + +--replace_column 2 option_value +SHOW INSTANCE OPTIONS mysqld2; + +# - check that UNSET does not allow option-value part (= <option value>); + +--error ER_SYNTAX_ERROR +UNSET mysqld2.server_id = 11; + +# - check that SET/UNSET working properly with multiple options; + +SET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc = 0010, mysqld3.ddd = 0020; + +--echo -------------------------------------------------------------------- +--exec grep aaa $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- +--exec grep bbb $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- +--exec grep ccc $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- +--exec grep ddd $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- + +UNSET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc, mysqld3.ddd; + +--echo -------------------------------------------------------------------- +--exec grep aaa $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep bbb $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep ccc $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep ddd $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +# - check that if some instance name is invalid or the active is active, +# whole SET-statement will not be executed; + +--error 3000 # ER_BAD_INSTANCE_NAME +SET mysqld2.aaa, mysqld3.bbb, mysqld.ccc = 0010; + +--echo -------------------------------------------------------------------- +--exec grep aaa $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep bbb $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep ccc $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +--error 3015 # ER_INSTANCE_IS_ACTIVE +SET mysqld2.aaa, mysqld3.bbb, mysqld1.ccc = 0010; + +--echo -------------------------------------------------------------------- +--exec grep aaa $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep bbb $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep ccc $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +# - check that if some instance name is invalid or the active is active, +# whole UNSET-statement will not be executed; + +--error 3000 # ER_BAD_INSTANCE_NAME +UNSET mysqld2.server_id, mysqld3.server_id, mysqld.ccc; + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +--error 3015 # ER_INSTANCE_IS_ACTIVE +UNSET mysqld2.server_id, mysqld3.server_id, mysqld1.ccc; + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf; +--echo -------------------------------------------------------------------- + +DROP INSTANCE mysqld3; + +# - check that spaces are handled correctly; + +SET mysqld2.server_id=222; +SET mysqld2.server_id = 222; +SET mysqld2.server_id = 222 ; +SET mysqld2 . server_id = 222 ; +SET mysqld2 . server_id = 222 , mysqld2 . aaa , mysqld2 . bbb ; + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- +--exec grep aaa $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- +--exec grep bbb $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- + +UNSET mysqld2 . aaa , mysqld2 . bbb ; + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- +--exec grep aaa $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- +--exec grep bbb $MYSQLTEST_VARDIR/im.cnf || true; +--echo -------------------------------------------------------------------- + +########################################################################### +# +# Check that SET/UNSET updates both the configuration cache in IM and +# the configuration file. +# +########################################################################### + +# - check that the configuration file has been updated (i.e. contains +# server_id=SERVER_ID for mysqld2); + +--echo -------------------------------------------------------------------- +--exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; +--echo -------------------------------------------------------------------- + +# - (for mysqld1) check that the running instance has not been affected: +# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' +# returns zero; + +--connection mysql1_con + +SHOW VARIABLES LIKE 'server_id'; + +--connection default + +# - check that internal cache of Instance Manager has been affected; +# TODO: we should check only server_id option here. + +# SHOW INSTANCE OPTIONS mysqld2; + +########################################################################### +# +# Check that FLUSH INSTANCES is allowed only when all instances are stopped. +# +########################################################################### + +SHOW INSTANCES; + +--error 3016 # ER_THERE_IS_ACTIVE_INSTACE +FLUSH INSTANCES; + +STOP INSTANCE mysqld1; +# FIXME: STOP INSTANCE should be synchronous. +--sleep 3 +# should be longer than monitoring interval and enough to stop instance. + +SHOW INSTANCES; + +FLUSH INSTANCES; diff --git a/mysql-test/t/im_options_set.imtest b/mysql-test/t/im_options_set.imtest deleted file mode 100644 index a9b64861f99..00000000000 --- a/mysql-test/t/im_options_set.imtest +++ /dev/null @@ -1,142 +0,0 @@ -########################################################################### -# -# This file contains test for (3) test suite. -# -# Consult WL#2789 for more information. -# -########################################################################### - -# -# Check the options-management commands: -# - SET; -# - FLUSH INSTANCES; -# -# Let's test the commands on the option 'server_id'. It's expected that -# originally the instances have the following server ids: -# - mysqld1: 1 -# - mysqld2: 2 -# -# 1. SET <instance_id>.server_id= SERVER_ID); where SERVER_ID is 11 or 12. -# 1.1. check that the configuration file has been updated (i.e. contains -# server_id=SERVER_ID for the instance); -# 1.2. (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns zero; -# 1.3. check that internal cache of Instance Manager has not been affected -# (i.e. SHOW INSTANCE OPTIONS <instance> does not contain updated value). -# -# 2. FLUSH INSTANCES; -# 2.1. check that the configuration file has not been updated; -# 2.2. (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns zero value; -# 2.3. check that internal cache of Instance Manager has been updated (i.e. -# SHOW INSTANCE OPTIONS <instance> contains 'server_id=SERVER_ID' line). -# -# 3. Restore options. -# - -########################################################################### - ---source include/im_check_os.inc - -########################################################################### -# -# 0. Check starting conditions. -# -########################################################################### - -# - check the configuration file; - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - check the running instances. - ---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check the internal cache. -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; -# SHOW INSTANCE OPTIONS mysqld2; - -########################################################################### -# -# 1. SET <instance_id>.server_id= SERVER_ID); where SERVER_ID is 11 or 12. -# -########################################################################### - -# * mysqld1 - -SET mysqld1.server_id = 11; - -# - check that the configuration file has been updated (i.e. contains -# server_id=SERVER_ID for the instance); - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns zero; - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check that internal cache of Instance Manager has not been affected -# (i.e. SHOW INSTANCE OPTIONS <instance> does not contain updated value). -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; - -# * mysqld2 - -SET mysqld2.server_id = 12; - -# - check that the configuration file has been updated (i.e. contains -# server_id=SERVER_ID for the instance); - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - check that internal cache of Instance Manager has not been affected -# (i.e. SHOW INSTANCE OPTIONS <instance> does not contain updated value). -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld2; - -########################################################################### -# -# 2. FLUSH INSTANCES; -# -########################################################################### - -FLUSH INSTANCES; - -# - check that the configuration file has not been updated; - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns zero value; - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check that internal cache of Instance Manager has been updated (i.e. -# SHOW INSTANCE OPTIONS <instance> contains 'server_id=' line). -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; -# SHOW INSTANCE OPTIONS mysqld2; diff --git a/mysql-test/t/im_options_unset.imtest b/mysql-test/t/im_options_unset.imtest deleted file mode 100644 index 40629805d45..00000000000 --- a/mysql-test/t/im_options_unset.imtest +++ /dev/null @@ -1,150 +0,0 @@ -########################################################################### -# -# This file contains test for (3) test suite. -# -# Consult WL#2789 for more information. -# -########################################################################### - -# -# Check the options-management commands: -# - UNSET; -# - FLUSH INSTANCES; -# -# Let's test the commands on the option 'server_id'. It's expected that -# originally the instances have the following server ids: -# - mysqld1: 1 -# - mysqld2: 2 -# -# The test case: -# -# 1. UNSET <instance_id>.server_id; -# -# Do the step for both instances. -# -# 1.1. check that the configuration file has been updated (i.e. does not -# contain 'server_id=' line for the instance); -# 1.2. (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns non-zero value; -# 1.3. check that internal cache of Instance Manager is not affected (i.e. -# SHOW INSTANCE OPTIONS <instance> contains non-zero value for server_id); -# -# 2. FLUSH INSTANCES; -# -# Do the step for both instances. -# -# 2.1. check that the configuration file has not been updated (i.e. does not -# contain 'server_id=' for the instance); -# 2.2. (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns non-zero value; -# 2.3. check that internal cache of Instance Manager has been updated (i.e. -# SHOW INSTANCE OPTIONS <instance> does not contain 'server_id=' line). -# - -########################################################################### - ---source include/im_check_os.inc - -########################################################################### -# -# 0. Check starting conditions. -# -########################################################################### - -# - check the configuration file; - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - check the running instances. - ---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check the internal cache. -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; -# SHOW INSTANCE OPTIONS mysqld2; - -########################################################################### -# -# 1. UNSET <instance_id>.server_id; -# -########################################################################### - -# * mysqld1 - -UNSET mysqld1.server_id; - -# - check that the configuration file has been updated (i.e. does not -# contain 'server_id=' line for the instance); - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - check that the running instance has not been affected: connect to the -# instance and check that 'SHOW VARIABLES LIKE 'server_id'' returns non-zero -# value; - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check that internal cache of Instance Manager is not affected (i.e. SHOW -# INSTANCE OPTIONS <instance> contains non-zero value for server_id); -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; - -# * mysqld2 - -UNSET mysqld2.server_id; - -# - check that the configuration file has been updated (i.e. does not -# contain 'server_id=' line for the instance); - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf || true; - -# - check that internal cache of Instance Manager is not affected (i.e. SHOW -# INSTANCE OPTIONS <instance> contains non-zero value for server_id); -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld2; - -########################################################################### -# -# 2. FLUSH INSTANCES; -# -########################################################################### - -FLUSH INSTANCES; - -# - check that the configuration file has not been updated (i.e. does not -# contain 'server_id=' for the instance); - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf || true; - -# - (for mysqld1) check that the running instance has not been affected: -# connect to the instance and check that 'SHOW VARIABLES LIKE 'server_id'' -# returns non-zero value; - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check that internal cache of Instance Manager has been updated (i.e. -# SHOW INSTANCE OPTIONS <instance> does not contain 'server_id=' line). -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; -# SHOW INSTANCE OPTIONS mysqld2; diff --git a/mysql-test/t/im_utils-im.opt b/mysql-test/t/im_utils-im.opt new file mode 100644 index 00000000000..34b74ce0c95 --- /dev/null +++ b/mysql-test/t/im_utils-im.opt @@ -0,0 +1 @@ +--monitoring-interval=1 diff --git a/mysql-test/t/im_utils.imtest b/mysql-test/t/im_utils.imtest index dc6fb93c4ff..8e8d475cfee 100644 --- a/mysql-test/t/im_utils.imtest +++ b/mysql-test/t/im_utils.imtest @@ -17,6 +17,9 @@ # - the second instance is offline; # +--sleep 3 +# should be longer than monitoring interval and enough to start instance. + SHOW INSTANCES; # @@ -41,8 +44,9 @@ SHOW INSTANCE OPTIONS mysqld2; START INSTANCE mysqld2; -# FIXME --- sleep 3 +# FIXME: START INSTANCE should be synchronous. +--sleep 3 +# should be longer than monitoring interval and enough to start instance. STOP INSTANCE mysqld2; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index edb376bc73b..4db108e1b40 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -808,6 +808,26 @@ table_schema='information_schema' and group by column_type order by num; # +# Bug#19236 bad COLUMNS.CHARACTER_MAXIMUM_LENGHT and CHARACTER_OCTET_LENGTH +# +create table t1(f1 char(1) not null, f2 char(9) not null) +default character set utf8; +select CHARACTER_MAXIMUM_LENGTH, CHARACTER_OCTET_LENGTH from +information_schema.columns where table_schema='test' and table_name = 't1'; +drop table t1; + +# +# Bug#18177 any access to INFORMATION_SCHEMA.ROUTINES crashes +# +use mysql; +INSERT INTO `proc` VALUES ('test','','PROCEDURE','','SQL','CONTAINS_SQL', +'NO','DEFINER','','','BEGIN\r\n \r\nEND','root@%','2006-03-02 18:40:03', +'2006-03-02 18:40:03','',''); +select routine_name from information_schema.routines; +delete from proc where name=''; +use test; + +# # End of 5.0 tests. # # Show engines @@ -826,4 +846,22 @@ select user,db from information_schema.processlist; connection default; drop user user3148@localhost; +# +# Bug#16681 information_schema shows forbidden VIEW details +# +grant select on test.* to mysqltest_1@localhost; +create table t1 (id int); +create view v1 as select * from t1; +create definer = mysqltest_1@localhost +sql security definer view v2 as select 1; + +connect (con16681,localhost,mysqltest_1,,test); +connection con16681; + +select * from information_schema.views +where table_name='v1' or table_name='v2'; +connection default; +drop view v1, v2; +drop table t1; +drop user mysqltest_1@localhost; diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index f31e4d64789..3de55e1c403 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -57,3 +57,75 @@ where c.c_id = 218 and expiredate is null; drop table t1, t2; + +# +# Bug #12882 min/max inconsistent on empty table +# + +--disable_warnings +create table t1m (a int) engine=myisam; +create table t1i (a int) engine=innodb; +create table t2m (a int) engine=myisam; +create table t2i (a int) engine=innodb; +--enable_warnings +insert into t2m values (5); +insert into t2i values (5); + +# test with MyISAM +select min(a) from t1m; +select min(7) from t1m; +select min(7) from DUAL; +explain select min(7) from t2m join t1m; +select min(7) from t2m join t1m; + +select max(a) from t1m; +select max(7) from t1m; +select max(7) from DUAL; +explain select max(7) from t2m join t1m; +select max(7) from t2m join t1m; + +select 1, min(a) from t1m where a=99; +select 1, min(a) from t1m where 1=99; +select 1, min(1) from t1m where a=99; +select 1, min(1) from t1m where 1=99; + +select 1, max(a) from t1m where a=99; +select 1, max(a) from t1m where 1=99; +select 1, max(1) from t1m where a=99; +select 1, max(1) from t1m where 1=99; + +# test with InnoDB +select min(a) from t1i; +select min(7) from t1i; +select min(7) from DUAL; +explain select min(7) from t2i join t1i; +select min(7) from t2i join t1i; + +select max(a) from t1i; +select max(7) from t1i; +select max(7) from DUAL; +explain select max(7) from t2i join t1i; +select max(7) from t2i join t1i; + +select 1, min(a) from t1i where a=99; +select 1, min(a) from t1i where 1=99; +select 1, min(1) from t1i where a=99; +select 1, min(1) from t1i where 1=99; + +select 1, max(a) from t1i where a=99; +select 1, max(a) from t1i where 1=99; +select 1, max(1) from t1i where a=99; +select 1, max(1) from t1i where 1=99; + +# mixed MyISAM/InnoDB test +explain select count(*), min(7), max(7) from t1m, t1i; +select count(*), min(7), max(7) from t1m, t1i; + +explain select count(*), min(7), max(7) from t1m, t2i; +select count(*), min(7), max(7) from t1m, t2i; + +explain select count(*), min(7), max(7) from t2m, t1i; +select count(*), min(7), max(7) from t2m, t1i; + +drop table t1m, t1i, t2m, t2i; + diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test index c194213e0c9..dc4e240750c 100644 --- a/mysql-test/t/join_outer.test +++ b/mysql-test/t/join_outer.test @@ -779,3 +779,47 @@ SELECT COUNT(*) FROM t2 LEFT JOIN t1 ON t2.fkey = t1.id WHERE t1.name LIKE 'A%' OR FALSE; DROP TABLE t1,t2; + +# +# Bug 19396: LEFT OUTER JOIN over views in curly braces +# +--disable_warnings +DROP VIEW IF EXISTS v1,v2; +DROP TABLE IF EXISTS t1,t2; +--enable_warnings + +CREATE TABLE t1 (a int); +CREATE table t2 (b int); +INSERT INTO t1 VALUES (1), (2), (3), (4), (1), (1), (3); +INSERT INTO t2 VALUES (2), (3); + +CREATE VIEW v1 AS SELECT a FROM t1 JOIN t2 ON t1.a=t2.b; +CREATE VIEW v2 AS SELECT b FROM t2 JOIN t1 ON t2.b=t1.a; + +SELECT v1.a, v2. b + FROM v1 LEFT OUTER JOIN v2 ON (v1.a=v2.b) AND (v1.a >= 3) + GROUP BY v1.a; +SELECT v1.a, v2. b + FROM { OJ v1 LEFT OUTER JOIN v2 ON (v1.a=v2.b) AND (v1.a >= 3) } + GROUP BY v1.a; + +DROP VIEW v1,v2; +DROP TABLE t1,t2; + +# +# Bug 19816: LEFT OUTER JOIN with constant ORed predicates in WHERE clause +# + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (b int); +INSERT INTO t1 VALUES (1), (2), (3), (4); +INSERT INTO t2 VALUES (2), (3); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1=1); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1 OR 1); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (0 OR 1); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1=1 OR 2=2); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.b WHERE (1=1 OR 1=0); + +DROP TABLE t1,t2; diff --git a/mysql-test/t/log_tables.test b/mysql-test/t/log_tables.test index 048da802d02..5b79e5e4625 100644 --- a/mysql-test/t/log_tables.test +++ b/mysql-test/t/log_tables.test @@ -16,10 +16,10 @@ use mysql; # truncate table general_log; ---replace_column 1 TIMESTAMP +--replace_column 1 TIMESTAMP 2 USER_HOST 3 THREAD_ID select * from general_log; truncate table slow_log; ---replace_column 1 TIMESTAMP +--replace_column 1 TIMESTAMP 2 USER_HOST select * from slow_log; # @@ -31,7 +31,7 @@ select * from slow_log; # truncate table general_log; ---replace_column 1 TIMESTAMP +--replace_column 1 TIMESTAMP 2 USER_HOST 3 THREAD_ID select * from general_log where argument like '%general_log%'; @@ -47,6 +47,7 @@ insert into join_test values ("User performed a usual SQL query", "Query"); insert into join_test values ("New DB connection was registered", "Connect"); insert into join_test values ("Get the table info", "Field List"); +--replace_column 2 USER_HOST select verbose_comment, user_host, argument from mysql.general_log join join_test on (mysql.general_log.command_type = join_test.command_type); @@ -156,7 +157,7 @@ truncate table mysql.general_log; set names utf8; create table bug16905 (s char(15) character set utf8 default 'пуÑто'); insert into bug16905 values ('новое'); ---replace_column 1 TIMESTAMP +--replace_column 1 TIMESTAMP 2 USER_HOST 3 THREAD_ID select * from mysql.general_log; drop table bug16905; @@ -167,7 +168,7 @@ drop table bug16905; truncate table mysql.slow_log; set session long_query_time=1; select sleep(2); ---replace_column 1 TIMESTAMP, 3 USER_HOST, 4 QUERY_TIME +--replace_column 1 TIMESTAMP 2 USER_HOST 3 QUERY_TIME select * from mysql.slow_log; # kill all connections diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 9fe7bb787cb..9fee4a03b81 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -382,3 +382,15 @@ check table t1, t2; drop table t1, t2, t3; # End of 4.1 tests + +# +# BUG#10952 - alter table ... lost data without errors and warnings +# +drop table if exists t1; +create table t1 (c char(20)) engine=MyISAM; +insert into t1 values ("Monty"),("WAX"),("Walrus"); +--error 1031 +alter table t1 engine=MERGE; +drop table t1; + +# End of 5.0 tests diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index e76553f42e7..ac4c323f51e 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -72,3 +72,11 @@ drop table t1; # --exec $MYSQL -t --default-character-set utf8 test -e "create table t1 (i int, j int not null, k int); insert into t1 values (null, 1, null); select * from t1; describe t1; drop table t1;" +# +# Bug#19564: mysql displays NULL instead of space +# +--exec $MYSQL -t test -e "create table b19564 (i int, s1 char(1)); insert into b19564 values (1, 'x'); insert into b19564 values (2, NULL); insert into b19564 values (3, ' '); select * from b19564 order by i; drop table b19564;" + +--echo End of 5.0 tests + + diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index 1bede1d5c9a..b76a5103120 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -124,9 +124,22 @@ select HEX(f) from t3; select HEX(f) from t04; select HEX(f) from t4; +# +# BUG#14157: utf8 encoding in binlog without set character_set_client +# +# BUG: +# This test only works on the MySQL-internal rpl machines. +# Needs to be fixed. Problem is that koi8r is not installed +# on many machines. +# +flush logs; +# --exec $MYSQL --character-sets-dir=../sql/share/charsets/ --default-character-set=koi8r test -e 'create table if not exists t5 (a int); set names koi8r; create temporary table `ÑÝÉË` (a int); insert into `ÑÝÉË` values (1); insert into t5 select * from `ÑÝÉË`' +# resulted log is client charset insensitive (latin1 not koi8r) as it must be +# --exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000006 | $MYSQL --default-character-set=latin1 +#select * from t5 /* must be (1),(1) */; # clean up drop table t1, t2, t03, t04, t3, t4; -# End of 4.1 tests +# End of 5.0 tests diff --git a/mysql-test/t/ndb_alter_table.test b/mysql-test/t/ndb_alter_table.test index 29deea4aa0d..8e3b4a6ca89 100644 --- a/mysql-test/t/ndb_alter_table.test +++ b/mysql-test/t/ndb_alter_table.test @@ -3,7 +3,7 @@ -- source include/not_embedded.inc --disable_warnings -DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1, t2; drop database if exists mysqltest; --enable_warnings @@ -383,3 +383,18 @@ LOAD DATA INFILE 'tmp.dat' INTO TABLE ndb_show_tables; select 'no_copy' from ndb_show_tables where id = @t1_id and name like '%t1%'; DROP TABLE t1, ndb_show_tables; + +# simple test that auto incr is not lost at rename or alter +create table t1 (a int primary key auto_increment, b int) engine=ndb; +insert into t1 (b) values (101),(102),(103); +select * from t1 where a = 3; +alter table t1 rename t2; +insert into t2 (b) values (201),(202),(203); +select * from t2 where a = 6; +alter table t2 add c int; +insert into t2 (b) values (301),(302),(303); +select * from t2 where a = 9; +alter table t2 rename t1; +insert into t1 (b) values (401),(402),(403); +select * from t1 where a = 12; +drop table t1; diff --git a/mysql-test/t/ndb_autodiscover3.test b/mysql-test/t/ndb_autodiscover3.test new file mode 100644 index 00000000000..ed75c89cdd1 --- /dev/null +++ b/mysql-test/t/ndb_autodiscover3.test @@ -0,0 +1,76 @@ +-- source include/have_ndb.inc +-- source include/have_multi_ndb.inc +-- source include/not_embedded.inc + + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings + +# +# Transaction ongoing while cluster is restarted +# +--connection server1 +create table t1 (a int key) engine=ndbcluster; + +begin; +insert into t1 values (1); + +--exec $NDB_MGM --no-defaults -e "all restart" >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_waiter --no-defaults >> $NDB_TOOLS_OUTPUT + +--error 1297 +insert into t1 values (2); +--error 1296 +commit; + +drop table t1; + +# +# Stale cache after restart -i +# +--connection server1 +create table t2 (a int, b int, primary key(a,b)) engine=ndbcluster; +insert into t2 values (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1); +select * from t2 order by a limit 3; + +--exec $NDB_MGM --no-defaults -e "all restart -i" >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_waiter --no-defaults >> $NDB_TOOLS_OUTPUT +# to ensure mysqld has connected again, and recreated system tables +--exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -r 30 -d cluster apply_status >> $NDB_TOOLS_OUTPUT + +--connection server2 +--error ER_NO_SUCH_TABLE +select * from t2; +show tables like 't2'; +reset master; +create table t2 (a int key) engine=ndbcluster; +insert into t2 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +select * from t2 order by a limit 3; + +# server 1 should have a stale cache, and in this case wrong frm, transaction must be retried +--connection server1 +select * from t2 order by a limit 3; +reset master; + +--exec $NDB_MGM --no-defaults -e "all restart -i" >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_waiter --no-defaults >> $NDB_TOOLS_OUTPUT +# to ensure mysqld has connected again, and recreated system tables +--exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -r 30 -d cluster apply_status >> $NDB_TOOLS_OUTPUT + +--connection server1 +--error ER_NO_SUCH_TABLE +select * from t2; +show tables like 't2'; +reset master; +create table t2 (a int key) engine=ndbcluster; +insert into t2 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +select * from t2 order by a limit 3; + +# server 2 should have a stale cache, but with right frm, transaction need not be retried +--connection server2 +select * from t2 order by a limit 3; +reset master; + +drop table t2; +# End of 4.1 tests diff --git a/mysql-test/t/ndb_basic.test b/mysql-test/t/ndb_basic.test index edf74ab1df9..d11e30ae97d 100644 --- a/mysql-test/t/ndb_basic.test +++ b/mysql-test/t/ndb_basic.test @@ -6,17 +6,16 @@ DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7; drop database if exists mysqltest; --enable_warnings -# workaround for bug#16445 -# remove to reproduce bug and run tests from ndb start -# and with ndb_autodiscover disabled. Fails on Linux 50 % of the times - -CREATE TABLE t1 ( - pk1 INT NOT NULL PRIMARY KEY, - attr1 INT NOT NULL, - attr2 INT, - attr3 VARCHAR(10) -) ENGINE=ndbcluster; -drop table t1; +## workaround for bug#16445 +## remove to reproduce bug and run tests from ndb start +## and with ndb_autodiscover disabled. Fails on Linux 50 % of the times +#CREATE TABLE t1 ( +# pk1 INT NOT NULL PRIMARY KEY, +# attr1 INT NOT NULL, +# attr2 INT, +# attr3 VARCHAR(10) +#) ENGINE=ndbcluster; +#drop table t1; # # Basic test to show that the NDB diff --git a/mysql-test/t/ndb_blob.test b/mysql-test/t/ndb_blob.test index bf82a793049..d6e0edc89f0 100644 --- a/mysql-test/t/ndb_blob.test +++ b/mysql-test/t/ndb_blob.test @@ -428,4 +428,60 @@ truncate t1; select count(*) from t1; drop table t1; +# -- bug#19956 - var* key, complex key + +create table t1 ( + a varchar(40) not null, + b mediumint not null, + t text, + c varchar(2) not null, + d bigint not null, + primary key (a,b,c), + key (c,a), + unique key (d) +) engine=ndb; + +--disable_query_log +set @s1 = 'rggurloniukyehuxdbfkkyzlceixzrehqhvxvxbpwizzvjzpucqmzrhzxzfau'; +set @s2 = 'ykyymbzqgqlcjhlhmyqelfoaaohvtbekvifukdtnvcrrjveevfakxarxexomz'; +set @s3 = 'dbnfqyzgtqxalcrwtfsqabknvtfcbpoonxsjiqvmhnfikxxhcgoexlkoezvah'; +set @v1 = repeat(@s1,123); +set @v2 = repeat(@s2,234); +set @v3 = repeat(@s3,345); +set @v4 = NULL; +--enable_query_log + +insert into t1 (a,b,c,d,t) values ('a',1110,'a',1,@v1); +insert into t1 (a,b,c,d,t) values ('b',1110,'a',2,@v2); +insert into t1 (a,b,c,d,t) values ('a',1110,'b',3,@v3); +insert into t1 (a,b,c,d,t) values ('b',1110,'b',4,@v4); +select a,b,c,d,sha1(t) from t1 order by c,a; + +select a,b,c,d,sha1(t) from t1 where a='a' and b=1110 and c='a'; +select a,b,c,d,sha1(t) from t1 where a='a' and b=1110 and c='b'; + +update t1 set t=@v4 where a='b' and b=1110 and c='a'; +update t1 set t=@v2 where a='b' and b=1110 and c='b'; +select a,b,c,d,sha1(t) from t1 order by c,a; + +update t1 set t=@v2 where d=2; +update t1 set t=@v4 where d=4; +select a,b,c,d,sha1(t) from t1 order by c,a; + +update t1 set t=@v4 where a='b' and c='a'; +update t1 set t=@v2 where a='b' and c='b'; +select a,b,c,d,sha1(t) from t1 order by c,a; + +update t1 set t=@v2 where b+d=1112; +update t1 set t=@v4 where b+d=1114; +select a,b,c,d,sha1(t) from t1 order by c,a; + +delete from t1 where a='a' and b=1110 and c='a'; +delete from t1 where a='b' and c='a'; +delete from t1 where d=3; +delete from t1 where b+d=1114; +select count(*) from t1; + +drop table t1; + # End of 4.1 tests diff --git a/mysql-test/t/ndb_blob_partition.test b/mysql-test/t/ndb_blob_partition.test deleted file mode 100644 index a3948cc9491..00000000000 --- a/mysql-test/t/ndb_blob_partition.test +++ /dev/null @@ -1,93 +0,0 @@ ---source include/have_ndb.inc --- source include/not_embedded.inc - ---disable_warnings -drop table if exists t1; ---enable_warnings - -# -# Minimal NDB blobs test with range partitions. -# - -create table t1 ( - a mediumint not null, - b text not null, - c int not null, - d longblob, - primary key using hash (a,c), - unique key (c) -) - engine=ndb - partition by range (c) - partitions 3 - ( partition p1 values less than (200), - partition p2 values less than (300), - partition p3 values less than (400)); - ---disable_query_log -sleep 1; - -# length 61 -set @s0 = 'rggurloniukyehuxdbfkkyzlceixzrehqhvxvxbpwizzvjzpucqmzrhzxzfau'; -set @s1 = 'ykyymbzqgqlcjhlhmyqelfoaaohvtbekvifukdtnvcrrjveevfakxarxexomz'; -set @s2 = 'dbnfqyzgtqxalcrwtfsqabknvtfcbpoonxsjiqvmhnfikxxhcgoexlkoezvah'; - -set @v1 = repeat(@s0, 100); -- 1d42dd9090cf78314a06665d4ea938c35cc760f4 -set @v2 = repeat(@s1, 200); -- 10d3c783026b310218d10b7188da96a2401648c6 -set @v3 = repeat(@s2, 300); -- a33549d9844092289a58ac348dd59f09fc28406a -set @v4 = repeat(@s0, 400); -- daa61c6de36a0526f0d47dc29d6b9de7e6d2630c -set @v5 = repeat(@s1, 500); -- 70fc9a7d08beebc522258bfb02000a30c77a8f1d -set @v6 = repeat(@s2, 600); -- 090565c580809efed3d369481a4bbb168b20713e -set @v7 = repeat(@s0, 700); -- 1e0070bec426871a46291de27b9bd6e4255ab4e5 -set @v8 = repeat(@s1, 800); -- acbaba01bc2e682f015f40e79d9cbe475db3002e -set @v9 = repeat(@s2, 900); -- 9ee30d99162574f79c66ae95cdf132dcf9cbc259 ---enable_query_log - -# -- insert -- -insert into t1 values (1, @v1, 101, @v2); -insert into t1 values (1, @v2, 102, @v3); -insert into t1 values (1, @v3, 103, @v4); -insert into t1 values (2, @v4, 201, @v5); -insert into t1 values (2, @v5, 202, @v6); -insert into t1 values (2, @v6, 203, @v7); -insert into t1 values (3, @v7, 301, @v8); -insert into t1 values (3, @v8, 302, @v9); -insert into t1 values (3, @v9, 303, @v1); -select a, sha1(b), c, sha1(d) from t1 order by a; - -# -- pk read -- -select a, sha1(b), c, sha1(d) from t1 where a = 1 and c = 101; -select a, sha1(b), c, sha1(d) from t1 where a = 2 and c = 201; -select a, sha1(b), c, sha1(d) from t1 where a = 3 and c = 301; - -# -- pk update -- -update t1 set b = @v3, d = @v4 where a = 1 and c = 102; -update t1 set b = @v6, d = @v7 where a = 2 and c = 202; -update t1 set b = @v9, d = @v1 where a = 3 and c = 302; -select a, sha1(b), c, sha1(d) from t1 order by a; - -# -- hash index update -- -update t1 set b = @v4, d = @v5 where c = 103; -update t1 set b = @v7, d = @v8 where c = 203; -update t1 set b = @v1, d = @v2 where c = 303; -select a, sha1(b), c, sha1(d) from t1 order by a; - -# -- full scan update -- -update t1 set b = @v5, d = @v6; -select a, sha1(b), c, sha1(d) from t1 order by a; - -# -- range scan update -update t1 set b = @v1, d = @v2 where 100 < c and c < 200; -update t1 set b = @v4, d = @v5 where 200 < c and c < 300; -update t1 set b = @v7, d = @v8 where 300 < c and c < 400; -select a, sha1(b), c, sha1(d) from t1 order by a; - -# -- delete -- -delete from t1 where a = 1 and c = 101; -delete from t1 where c = 102; -# delete from t1 where c < 300; # XXX coredump -delete from t1; -select a, sha1(b), c, sha1(d) from t1 order by a; - -# -- clean up -- -drop table t1; diff --git a/mysql-test/t/ndb_partition_error.test b/mysql-test/t/ndb_partition_error.test deleted file mode 100644 index 06581f1270f..00000000000 --- a/mysql-test/t/ndb_partition_error.test +++ /dev/null @@ -1,71 +0,0 @@ --- source include/have_ndb.inc -#--disable_abort_on_error -# -# Simple test for the partition storage engine -# Focuses on range partitioning tests -# -#-- source include/have_partition.inc - ---disable_warnings -drop table if exists t1; ---enable_warnings - -# -# Partition by range, generate node group error -# ---error 1005 -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(a,b), -index (a)) -engine = ndb -partition by range (a) -partitions 3 -(partition x1 values less than (5) nodegroup 12, - partition x2 values less than (10) nodegroup 13, - partition x3 values less than (20) nodegroup 14); -show warnings; - -# -# Partition by range, create normal valid table -# -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(a)) -engine = ndb -partition by range (a) -partitions 3 -(partition x1 values less than (5), - partition x2 values less than (10), - partition x3 values less than (20)); - -drop table t1; - -# -# Bug #17763 mysqld cores with list partitioning if update to missing partition -# -CREATE TABLE t1 (id INT) ENGINE=NDB - PARTITION BY LIST(id) - (PARTITION p0 VALUES IN (2, 4), - PARTITION p1 VALUES IN (42, 142)); -INSERT INTO t1 VALUES (2); ---error ER_NO_PARTITION_FOR_GIVEN_VALUE -UPDATE t1 SET id=5 WHERE id=2; -DROP TABLE t1; - -# -# NULL for LIST partition -# -create table t1 (a int,b int, c int) -engine = ndb -partition by list(a) -partitions 2 -(partition x123 values in (11, 12), - partition x234 values in (5, 1)); ---error ER_NO_PARTITION_FOR_GIVEN_VALUE -insert into t1 values (NULL,1,1); -drop table t1; diff --git a/mysql-test/t/ndb_partition_key.test b/mysql-test/t/ndb_partition_key.test deleted file mode 100644 index fb0581eb6f6..00000000000 --- a/mysql-test/t/ndb_partition_key.test +++ /dev/null @@ -1,198 +0,0 @@ --- source include/have_ndb.inc - ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_warnings - -# -# Basic syntax test -# - -# Support for partition key verified -CREATE TABLE t1 (a int, b int, c int, d int, PRIMARY KEY(a,b,c)) - ENGINE = NDB - PARTITION BY KEY (a,b); - -insert into t1 values (1,1,1,1); -select * from t1; -update t1 set d = 2 where a = 1 and b = 1 and c = 1; -select * from t1; -delete from t1; -select * from t1; - -drop table t1; - -# only support for partition key on primary key ---error ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF -CREATE TABLE t1 (a int, b int, c int, d int, PRIMARY KEY(a,b)) - ENGINE = NDB - PARTITION BY KEY (c); - -CREATE TABLE t1 (a int, b int, c int, PRIMARY KEY(a,b)) - ENGINE = NDB - PARTITION BY KEY (a); - -insert into t1 values - (1,1,3),(1,2,3),(1,3,3),(1,4,3),(1,5,3),(1,6,3), - (1,7,3),(1,8,3),(1,9,3),(1,10,3),(1,11,3),(1,12,3); - -select * from t1 order by b; - -DROP TABLE t1; - -# -# Test partition and char support -# - -CREATE TABLE t1 (a INT, b CHAR(10) COLLATE latin1_bin, c INT, d INT, - PRIMARY KEY (a,b,c) USING HASH) - ENGINE=NDB - DEFAULT CHARSET=latin1 - PARTITION BY KEY (b); - -insert into t1 values (1,"a",1,1),(2,"a",1,1),(3,"a",1,1); - -# should show only one attribute with DISTRIBUTION KEY ---exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -d test t1 | sed 's/Version: [0-9]*//' | sed 's/\(Length of frm data: \)[0-9]*/\1#/' - -# -# Test that explicit partition info is not shown in show create table -# result should not contain (PARTITION P0 ... etc) since this is what shows up in -# mysqldump, and we don't want that info there -# -show create table t1; - -DROP TABLE t1; - -# -# Bug #13155: Problem in Create Table using SHOW CREATE TABLE syntax -# -CREATE TABLE t1 (a int not null primary key) -PARTITION BY KEY(a) -(PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); - -drop table t1; - -CREATE TABLE t1 (a int not null primary key); -ALTER TABLE t1 -PARTITION BY KEY(a) -(PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); - -drop table t1; - -# -# Bug #17754 Improper handling of removal of partitioning in ALTER TABLE -# Also added a number of general test cases in the same area -# -create table t1 (a int) -engine=ndb -partition by key(a) -(partition p0, partition p1); -show create table t1; - -alter table t1 engine=heap; -show create table t1; - -alter table t1 engine=ndb; -show create table t1; - -alter table t1 engine=heap remove partitioning; -show create table t1; - -alter table t1 engine=ndb -partition by key(a) -(partition p0, partition p1 engine = ndb); -show create table t1; - -alter table t1 -partition by key (a) -(partition p0 engine=ndb, partition p1 engine=ndb); -show create table t1; - -alter table t1 remove partitioning; -show create table t1; - ---error ER_MIX_HANDLER_ERROR -alter table t1 -partition by key(a) -(partition p0 engine=ndb, partition p1); - -alter table t1 -engine=ndb -partition by key(a) -(partition p0 engine=ndb, partition p1 engine = ndb); -show create table t1; - -drop table t1; - -# -# BUG 16810 Out of memory when coalesce partition -# -CREATE TABLE t1 ( - c1 MEDIUMINT NOT NULL AUTO_INCREMENT, - c2 TEXT NOT NULL, - c3 INT NOT NULL, - c4 BIT NOT NULL, - c5 FLOAT, - c6 VARCHAR(255), - c7 TIMESTAMP, - PRIMARY KEY(c1,c3)) - ENGINE=NDB - PARTITION BY KEY(c3) PARTITIONS 5; - -let $j= 11; ---disable_query_log -while ($j) -{ - eval INSERT INTO t1 VALUES (NULL, "Tested Remotely from Texas, USA", $j, -b'0', - $j.00,"By JBM $j","2006-01-26"); - dec $j; -} ---enable_query_log -ALTER TABLE t1 COALESCE PARTITION 4; - -DROP TABLE t1; - -# -# Bug 16822: OPTIMIZE TABLE hangs test -# -CREATE TABLE t1 (a int primary key) -ENGINE=NDB -PARTITION BY KEY(a); ---error 1031 -ALTER TABLE t1 OPTIMIZE PARTITION p0; ---error 1031 -ALTER TABLE t1 CHECK PARTITION p0; ---error 1031 -ALTER TABLE t1 REPAIR PARTITION p0; ---error 1031 -ALTER TABLE t1 ANALYZE PARTITION p0; ---error 1031 -ALTER TABLE t1 REBUILD PARTITION p0; -DROP TABLE t1; - -# -# BUG 16806: ALTER TABLE fails -# -CREATE TABLE t1 ( - c1 MEDIUMINT NOT NULL AUTO_INCREMENT, - c2 TEXT NOT NULL, - c3 INT NOT NULL, - PRIMARY KEY(c1,c3)) - ENGINE=NDB - PARTITION BY KEY(c3) PARTITIONS 5; - -ALTER TABLE t1 ADD COLUMN c4 INT AFTER c1; -DROP TABLE t1; - -CREATE TABLE t1 ( - c1 MEDIUMINT NOT NULL AUTO_INCREMENT, - c2 TEXT NOT NULL, - c3 INT NOT NULL, - PRIMARY KEY(c1,c3)) - ENGINE=NDB - PARTITION BY KEY(c3); - -ALTER TABLE t1 ADD COLUMN c4 INT AFTER c1; -DROP TABLE t1; diff --git a/mysql-test/t/ndb_partition_list.test b/mysql-test/t/ndb_partition_list.test deleted file mode 100644 index 2ad37b8768c..00000000000 --- a/mysql-test/t/ndb_partition_list.test +++ /dev/null @@ -1,64 +0,0 @@ ---source include/have_ndb.inc -# -# Simple test for the partition storage engine -# Focuses on range partitioning tests -# -#-- source include/have_partition.inc - ---disable_warnings -drop table if exists t1; ---enable_warnings - -# -# Partition by list, basic -# - -CREATE TABLE t1 ( f_int1 INTEGER NOT NULL, f_int2 INTEGER NOT NULL, - f_char1 CHAR(10), - f_char2 CHAR(10), f_charbig VARCHAR(1000), -PRIMARY KEY (f_int1,f_int2)) -ENGINE = NDB -PARTITION BY LIST(MOD(f_int1 + f_int2,4)) -(PARTITION part_3 VALUES IN (-3), - PARTITION part_2 VALUES IN (-2), - PARTITION part_1 VALUES IN (-1), - PARTITION part0 VALUES IN (0), - PARTITION part1 VALUES IN (1), - PARTITION part2 VALUES IN (2), - PARTITION part3 VALUES IN (3,4,5)); - -INSERT INTO t1 SET f_int1 = -2, f_int2 = 20, f_char1 = '20', f_char2 = '20', f_charbig = '===20==='; -INSERT INTO t1 SET f_int1 = 1, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 2, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 3, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 4, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 5, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 20, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; - -SELECT * FROM t1 ORDER BY f_int1; - -DROP TABLE t1; - -# -# Partition by list, no pk -# - -CREATE TABLE t1 ( f_int1 INTEGER, f_int2 INTEGER, f_char1 CHAR(10), - f_char2 CHAR(10), f_charbig VARCHAR(1000)) -ENGINE = NDB -PARTITION BY LIST(f_int1) -(PARTITION part_1 VALUES IN (-1), - PARTITION part0 VALUES IN (0,1), - PARTITION part1 VALUES IN (2)); - -INSERT INTO t1 SET f_int1 = -1, f_int2 = 20, f_char1 = '20', f_char2 = '20', f_charbig = '===20==='; -INSERT INTO t1 SET f_int1 = 0, f_int2 = 20, f_char1 = '20', f_char2 = '20', f_charbig = '===20==='; -INSERT INTO t1 SET f_int1 = 1, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; -INSERT INTO t1 SET f_int1 = 2, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; ---error ER_NO_PARTITION_FOR_GIVEN_VALUE -INSERT INTO t1 SET f_int1 = 20, f_int2 = 1, f_char1 = '1', f_char2 = '1', f_charbig = '===1==='; - -SELECT * FROM t1 ORDER BY f_int1; - -DROP TABLE t1; - diff --git a/mysql-test/t/ndb_partition_range.test b/mysql-test/t/ndb_partition_range.test deleted file mode 100644 index 981467d4055..00000000000 --- a/mysql-test/t/ndb_partition_range.test +++ /dev/null @@ -1,260 +0,0 @@ --- source include/have_ndb.inc -#--disable_abort_on_error -# -# Simple test for the partition storage engine -# Focuses on range partitioning tests -# -#-- source include/have_partition.inc - ---disable_warnings -drop table if exists t1; ---enable_warnings - -# -# Partition by range, basic -# -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(a,b), -index (a)) -engine = ndb -partition by range (a) -partitions 3 -(partition x1 values less than (5), - partition x2 values less than (10), - partition x3 values less than (20)); - -# Simple insert and verify test -INSERT into t1 values (1, 1, 1); -INSERT into t1 values (6, 1, 1); -INSERT into t1 values (10, 1, 1); -INSERT into t1 values (15, 1, 1); - ---replace_column 16 # 19 # 20 # -select * from information_schema.partitions where table_name= 't1'; - -select * from t1 order by a; - -select * from t1 where a=1 order by a; -select * from t1 where a=15 and b=1 order by a; -select * from t1 where a=21 and b=1 order by a; -select * from t1 where a=21 order by a; -select * from t1 where a in (1,6,10,21) order by a; -select * from t1 where b=1 and a in (1,6,10,21) order by a; - -drop table t1; - -# -# Partition by range, basic -# -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null, -primary key(b), -unique (a)) -engine = ndb -partition by range (b) -partitions 3 -(partition x1 values less than (5), - partition x2 values less than (10), - partition x3 values less than (20)); - -# Simple insert and verify test -INSERT into t1 values (1, 1, 1); -INSERT into t1 values (2, 6, 1); -INSERT into t1 values (3, 10, 1); -INSERT into t1 values (4, 15, 1); - -select * from t1 order by a; -UPDATE t1 set a = 5 WHERE b = 15; -select * from t1 order by a; -UPDATE t1 set a = 6 WHERE a = 5; -select * from t1 order by a; - -select * from t1 where b=1 order by b; -select * from t1 where b=15 and a=1 order by b; -select * from t1 where b=21 and a=1 order by b; -select * from t1 where b=21 order by b; -select * from t1 where b in (1,6,10,21) order by b; -select * from t1 where a in (1,2,5,6) order by b; -select * from t1 where a=1 and b in (1,6,10,21) order by b; - -DELETE from t1 WHERE b = 6; -DELETE from t1 WHERE a = 6; - -# -# Test that explicit partition info _is_ shown in show create table -# result _should_ contain (PARTITION x1 ... etc) -# -show create table t1; - -drop table t1; - -# -# Bug #17499, #17687 -# Alter partitioned NDB table causes mysqld to core -# - -CREATE TABLE t1 - (id MEDIUMINT NOT NULL, - b1 BIT(8), - vc VARCHAR(255), - bc CHAR(255), - d DECIMAL(10,4) DEFAULT 0, - f FLOAT DEFAULT 0, - total BIGINT UNSIGNED, - y YEAR, - t DATE) ENGINE=NDB - PARTITION BY RANGE (YEAR(t)) - (PARTITION p0 VALUES LESS THAN (1901), - PARTITION p1 VALUES LESS THAN (1946), - PARTITION p2 VALUES LESS THAN (1966), - PARTITION p3 VALUES LESS THAN (1986), - PARTITION p4 VALUES LESS THAN (2005), - PARTITION p5 VALUES LESS THAN MAXVALUE); - -INSERT INTO t1 VALUES (0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -SELECT * FROM t1; -ALTER TABLE t1 ENGINE=MYISAM; -SELECT * FROM t1; -DROP TABLE t1; - -CREATE LOGFILE GROUP lg1 - ADD UNDOFILE 'undofile.dat' - INITIAL_SIZE 16M - UNDO_BUFFER_SIZE=1M - ENGINE=NDB; - -CREATE TABLESPACE ts1 - ADD DATAFILE 'datafile.dat' - USE LOGFILE GROUP lg1 - INITIAL_SIZE 12M - ENGINE NDB; - -CREATE TABLE test.t1 ( - a1 INT, - a2 TEXT NOT NULL, - a3 BIT NOT NULL, - a4 DECIMAL(8,3), - a5 INT NOT NULL, - a6 INT, - PRIMARY KEY(a1)) - TABLESPACE ts1 STORAGE DISK ENGINE=NDB - PARTITION BY LIST (a1) - (PARTITION p0 VALUES IN (1,2,3,4,5), - PARTITION p1 VALUES IN (6,7,8,9, 10), - PARTITION p2 VALUES IN (11, 12, 13, 14, 15)); - -# Alter table directly without any statements inbetween -ALTER TABLE test.t1 DROP COLUMN a6; -ALTER TABLE test.t1 ADD COLUMN a6 VARCHAR(255); - -let $j= 15; ---disable_query_log -while ($j) -{ -eval INSERT INTO test.t1 VALUES ($j, "Tested Remotely from Texas, USA", -b'1',$j.00,$j+1,"By NIK $j"); -dec $j; -} ---enable_query_log -SELECT COUNT(*) FROM test.t1; - -ALTER TABLE test.t1 DROP COLUMN a4; -SELECT COUNT(*) FROM test.t1; - -DROP TABLE t1; - -CREATE TABLE test.t1 ( - a1 INT, - a2 TEXT NOT NULL, - a3 BIT NOT NULL, - a4 DECIMAL(8,3), - a5 INT NOT NULL, - a6 VARCHAR(255), - PRIMARY KEY(a1)) - TABLESPACE ts1 STORAGE DISK ENGINE=NDB - PARTITION BY HASH(a1) - PARTITIONS 4; - -let $j= 15; ---disable_query_log -while ($j) -{ -eval INSERT INTO test.t1 VALUES ($j, "Tested Remotely from Texas, USA", -b'1',$j.00,$j+1,"By NIK $j"); -dec $j; -} ---enable_query_log -SELECT COUNT(*) FROM test.t1; - -ALTER TABLE test.t1 DROP COLUMN a4; -SELECT COUNT(*) FROM test.t1; - -DROP TABLE t1; - -ALTER TABLESPACE ts1 - DROP DATAFILE 'datafile.dat' - ENGINE=NDB; -DROP TABLESPACE ts1 ENGINE=NDB; -DROP LOGFILE GROUP lg1 ENGINE=NDB; - - -# -# Bug #17701 ALTER TABLE t1 ADD PARTITION for PARTITION BY LIST hangs test -# - -CREATE TABLE t1 - (id MEDIUMINT NOT NULL, - b1 BIT(8), - vc VARCHAR(255), - bc CHAR(255), - d DECIMAL(10,4) DEFAULT 0, - f FLOAT DEFAULT 0, - total BIGINT UNSIGNED, - y YEAR, - t DATE) ENGINE=NDB - PARTITION BY LIST(id) - (PARTITION p0 VALUES IN (2, 4), - PARTITION p1 VALUES IN (42, 142)); - -INSERT INTO t1 VALUES (2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -SELECT * FROM t1; -ALTER TABLE t1 ADD PARTITION - (PARTITION p2 VALUES IN (412)); -SELECT * FROM t1; -DROP TABLE t1; - -# -# Bug #17806 Update on NDB table with list partition causes mysqld to core -# Bug #16385 Partitions: crash when updating a range partitioned NDB table -# -CREATE TABLE t1 ( -a int not null, -b int not null, -c int not null) -partition by list(a) -partitions 2 -(partition x123 values in (1,5,6), - partition x234 values in (4,7,8)); -INSERT into t1 VALUES (5,1,1); -select * from t1; -UPDATE t1 SET a=8 WHERE a=5 AND b=1; -select * from t1; -drop table t1; - -CREATE TABLE t1 ( f1 INTEGER, f2 char(20)) engine=ndb -PARTITION BY RANGE(f1) -( PARTITION part1 VALUES LESS THAN (2), -PARTITION part2 VALUES LESS THAN (1000)); -INSERT INTO t1 VALUES(1, '---1---'); -INSERT INTO t1 VALUES(2, '---2---'); -select * from t1 order by f1; -UPDATE t1 SET f1 = f1 + 4 WHERE f1 = 2; -select * from t1 order by f1; -UPDATE t1 SET f1 = f1 + 4 WHERE f1 = 1; -select * from t1 order by f1; -drop table t1; diff --git a/mysql-test/t/outfile.test b/mysql-test/t/outfile.test index d404a6fff9f..c48e6c9730d 100644 --- a/mysql-test/t/outfile.test +++ b/mysql-test/t/outfile.test @@ -40,7 +40,6 @@ eval select * into dumpfile "../tmp/outfile-test.2" from t1; --error 1086 eval select * into dumpfile "../tmp/outfile-test.3" from t1; enable_query_log; ---error 13,2 select load_file(concat(@tmpdir,"/outfile-test.not-exist")); --exec rm $MYSQLTEST_VARDIR/tmp/outfile-test.1 --exec rm $MYSQLTEST_VARDIR/tmp/outfile-test.2 diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index d6b96167214..03c3f6e0a7c 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -9,6 +9,11 @@ drop table if exists t1; --enable_warnings +create table t1 (a int) +partition by key(a) +(partition p0 engine = MEMORY); +drop table t1; + # # Partition by key no partition defined => OK # @@ -1010,6 +1015,15 @@ select * from t1; drop table t1; # +# BUG 19140 Partitions: Create index for partitioned table crashes +# +create table t1 (a int) engine=memory +partition by key(a); +insert into t1 values (1); +create index inx1 on t1(a); +drop table t1; + +# # BUG 19122 Crash after ALTER TABLE t1 REBUILD PARTITION p1 # create table t1 (a int) @@ -1024,6 +1038,58 @@ alter table t1 rebuild partition p1; alter table t1 rebuild partition p1; alter table t1 rebuild partition p1; +# +# BUG 19304 Partitions: MERGE handler not allowed in partitioned tables +# +--error ER_PARTITION_MERGE_ERROR +create table t1 (a int) +partition by key (a) +(partition p0 engine = MERGE); + +# +# BUG 19062 Partition clause ignored if CREATE TABLE ... AS SELECT ...; +# +create table t1 (a varchar(1)) +partition by key (a) +as select 'a'; + +show create table t1; +drop table t1; + +# +# BUG 19501 Partitions: SHOW TABLE STATUS shows wrong Data_free +# +CREATE TABLE t1 (a int) ENGINE = MYISAM PARTITION BY KEY(a); +INSERT into t1 values (1), (2); +--replace_column 9 0 12 NULL 13 NULL 14 NULL +SHOW TABLE STATUS; +DELETE from t1 where a = 1; +--replace_column 9 0 12 NULL 13 NULL 14 NULL +SHOW TABLE STATUS; +ALTER TABLE t1 OPTIMIZE PARTITION p0; +--replace_column 12 NULL 13 NULL 14 NULL +SHOW TABLE STATUS; +DROP TABLE t1; + +# +# BUG 19502: ENABLE/DISABLE Keys don't work for partitioned tables +# +CREATE TABLE t1 (a int, index(a)) PARTITION BY KEY(a); +ALTER TABLE t1 DISABLE KEYS; +ALTER TABLE t1 ENABLE KEYS; +DROP TABLE t1; + +# +# Bug 17455 Partitions: Wrong message and error when using Repair/Optimize +# table on partitioned table +# +create table t1 (a int) +engine=MEMORY +partition by key (a); + +REPAIR TABLE t1; +OPTIMIZE TABLE t1; + drop table t1; --echo End of 5.1 tests diff --git a/mysql-test/t/rename.test b/mysql-test/t/rename.test index 5caecef176e..86e4b6eed0a 100644 --- a/mysql-test/t/rename.test +++ b/mysql-test/t/rename.test @@ -61,9 +61,15 @@ connection con2; sleep 1; show tables; UNLOCK TABLES; -sleep 1; +connection con1; +reap; +connection con2; show tables; drop table t2, t4; +disconnect con2; +disconnect con1; +connection default; + # End of 4.1 tests diff --git a/mysql-test/t/rpl_bit_npk.test b/mysql-test/t/rpl_bit_npk.test index 07fc42b09ef..12b587919f9 100644 --- a/mysql-test/t/rpl_bit_npk.test +++ b/mysql-test/t/rpl_bit_npk.test @@ -70,18 +70,22 @@ UNLOCK TABLES; UPDATE test.t1 set x034 = 50 where bit3 = b'000000'; UPDATE test.t1 set VNotSupp = 33 where bit1 = b'0'; -SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 FROM test.t1; -SELECT hex(bit1) from test.t1; -SELECT hex(bit2) from test.t1; -SELECT hex(bit3) from test.t1; +SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 + FROM test.t1 + ORDER BY oSupp, sSuppD, GSuppDf, VNotSupp, x034; +SELECT hex(bit1) from test.t1 ORDER BY bit1; +SELECT hex(bit2) from test.t1 ORDER BY bit2; +SELECT hex(bit3) from test.t1 ORDER BY bit3; save_master_pos; connection slave; sync_with_master; -SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 FROM test.t1; -SELECT hex(bit1) from test.t1; -SELECT hex(bit2) from test.t1; -SELECT hex(bit3) from test.t1; +SELECT oSupp, sSuppD, GSuppDf, VNotSupp, x034 + FROM test.t1 + ORDER BY oSupp, sSuppD, GSuppDf, VNotSupp, x034; +SELECT hex(bit1) from test.t1 ORDER BY bit1; +SELECT hex(bit2) from test.t1 ORDER BY bit2; +SELECT hex(bit3) from test.t1 ORDER BY bit3; connection master; CREATE TABLE test.t2 (a INT, b BIT(1)); @@ -94,14 +98,14 @@ INSERT INTO test.t3 VALUES (1, NULL); INSERT INTO test.t3 VALUES (1, 0); UPDATE test.t3 SET a = 2 WHERE b = 0; -SELECT a, hex(b) FROM test.t2; -SELECT * FROM test.t3; +SELECT a, hex(b) FROM test.t2 ORDER BY a,b; +SELECT * FROM test.t3 ORDER BY a,b; save_master_pos; connection slave; sync_with_master; -SELECT a, hex(b) FROM test.t2; -SELECT * FROM test.t3; +SELECT a, hex(b) FROM test.t2 ORDER BY a,b; +SELECT * FROM test.t3 ORDER BY a,b; connection master; DROP TABLE IF EXISTS test.t1; diff --git a/mysql-test/t/rpl_ndb_dd_partitions.test b/mysql-test/t/rpl_ndb_dd_partitions.test deleted file mode 100644 index 9291f38e8db..00000000000 --- a/mysql-test/t/rpl_ndb_dd_partitions.test +++ /dev/null @@ -1,310 +0,0 @@ -####################################### -# Author: JBM # -# Date: 2006-03-09 # -# Purpose: To test the replication of # -# Cluster Disk Data using partitions # -####################################### - ---source include/have_ndb.inc ---source include/have_binlog_format_row.inc ---source include/master-slave.inc - ---echo --- Doing pre test cleanup --- - -connection master; ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_query_log - - -# Start by creating a logfile group -################################## - -CREATE LOGFILE GROUP lg1 -ADD UNDOFILE 'undofile.dat' -INITIAL_SIZE 16M -UNDO_BUFFER_SIZE = 1M -ENGINE=NDB; - -ALTER LOGFILE GROUP lg1 -ADD UNDOFILE 'undofile02.dat' -INITIAL_SIZE = 4M -ENGINE=NDB; - -################################################### -# Create a tablespace connected to the logfile group -################################################### - -CREATE TABLESPACE ts1 -ADD DATAFILE 'datafile.dat' -USE LOGFILE GROUP lg1 -INITIAL_SIZE 12M -ENGINE NDB; - -ALTER TABLESPACE ts1 -ADD DATAFILE 'datafile02.dat' -INITIAL_SIZE = 4M -ENGINE=NDB; - -################################################################# - ---echo --- Start test 2 partition RANGE testing -- ---echo --- Do setup -- - - -################################################# -# Requirment: Create table that is partitioned # -# by range on year i.e. year(t) and replicate # -# basice operations such at insert, update # -# delete between 2 different storage engines # -# Alter table and ensure table is handled # -# Correctly on the slave # -################################################# - -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), - bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, - f FLOAT DEFAULT 0, total BIGINT UNSIGNED, - y YEAR, t DATE) - TABLESPACE ts1 STORAGE DISK - ENGINE=NDB - PARTITION BY RANGE (YEAR(t)) - (PARTITION p0 VALUES LESS THAN (1901), - PARTITION p1 VALUES LESS THAN (1946), - PARTITION p2 VALUES LESS THAN (1966), - PARTITION p3 VALUES LESS THAN (1986), - PARTITION p4 VALUES LESS THAN (2005), - PARTITION p5 VALUES LESS THAN MAXVALUE); - ---echo --- Show table on master --- - -SHOW CREATE TABLE t1; - ---echo --- Show table on slave -- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- Check that simple Alter statements are replicated correctly --- - -ALTER TABLE t1 MODIFY vc VARCHAR(255); - ---echo --- Show the new improved table on the master --- - -SHOW CREATE TABLE t1; - ---echo --- Make sure that our tables on slave are still same engine --- ---echo --- and that the alter statements replicated correctly --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- ---enable_query_log - ---source include/rpl_multi_engine3.inc - ---echo --- End test 2 partition RANGE testing --- ---echo --- Do Cleanup --- - -DROP TABLE IF EXISTS t1; - -######################################################## - ---echo --- Start test 3 partition LIST testing --- ---echo --- Do setup --- -################################################# - - -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), - bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, - f FLOAT DEFAULT 0, total BIGINT UNSIGNED, - y YEAR, t DATE) - TABLESPACE ts1 STORAGE DISK - ENGINE=NDB - PARTITION BY LIST(id) - (PARTITION p0 VALUES IN (2, 4), - PARTITION p1 VALUES IN (42, 142)); - ---echo --- Test 3 Alter to add partition --- - -ALTER TABLE t1 ADD PARTITION (PARTITION p2 VALUES IN (412)); - ---echo --- Show table on master --- - -SHOW CREATE TABLE t1; - ---echo --- Show table on slave --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- Check that simple Alter statements are replicated correctly --- - -ALTER TABLE t1 MODIFY vc VARCHAR(255); - ---echo --- Show the new improved table on the master --- - -SHOW CREATE TABLE t1; - ---echo --- Make sure that our tables on slave are still same engine --- ---echo --- and that the alter statements replicated correctly --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- End test 3 partition LIST testing --- ---echo --- Do Cleanup -- - -DROP TABLE IF EXISTS t1; - -######################################################## - ---echo --- Start test 4 partition HASH testing --- ---echo --- Do setup --- -################################################# - - -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), - bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, - f FLOAT DEFAULT 0, total BIGINT UNSIGNED, - y YEAR, t DATE) - TABLESPACE ts1 STORAGE DISK - ENGINE=NDB - PARTITION BY HASH( YEAR(t) ) - PARTITIONS 4; - ---echo --- show that tables have been created correctly --- - -SHOW CREATE TABLE t1; -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- Check that simple Alter statements are replicated correctly --- - -ALTER TABLE t1 MODIFY vc VARCHAR(255); - ---echo --- Show the new improved table on the master --- - -SHOW CREATE TABLE t1; - ---echo --- Make sure that our tables on slave are still same engine --- ---echo --- and that the alter statements replicated correctly --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- End test 4 partition HASH testing --- ---echo --- Do Cleanup -- - -DROP TABLE IF EXISTS t1; - -######################################################## - ---echo --- Start test 5 partition by key testing --- ---echo --- Create Table Section --- - -################################################# - -CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc VARCHAR(63), - bc CHAR(63), d DECIMAL(10,4) DEFAULT 0, - f FLOAT DEFAULT 0, total BIGINT UNSIGNED, - y YEAR, t DATE,PRIMARY KEY(id)) - TABLESPACE ts1 STORAGE DISK - ENGINE=NDB - PARTITION BY KEY() - PARTITIONS 4; - ---echo --- Show that tables on master are ndbcluster tables --- - -SHOW CREATE TABLE t1; - ---echo --- Show that tables on slave --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - -# Okay lets see how it holds up to table changes ---echo --- Check that simple Alter statements are replicated correctly --- - -ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(id, total); - ---echo --- Show the new improved table on the master --- - -SHOW CREATE TABLE t1; - ---echo --- Make sure that our tables on slave are still right type --- ---echo --- and that the alter statements replicated correctly --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- Check that simple Alter statements are replicated correctly --- - -ALTER TABLE t1 MODIFY vc VARCHAR(255); - ---echo --- Show the new improved table on the master --- - -SHOW CREATE TABLE t1; - ---echo --- Make sure that our tables on slave are still same engine --- ---echo --- and that the alter statements replicated correctly --- - -sync_slave_with_master; -SHOW CREATE TABLE t1; - ---echo --- Perform basic operation on master --- ---echo --- and ensure replicated correctly --- - ---source include/rpl_multi_engine3.inc - ---echo --- End test 5 key partition testing --- ---echo --- Do Cleanup --- - -DROP TABLE IF EXISTS t1; -alter tablespace ts1 -drop datafile 'datafile.dat' -engine=ndb; -alter tablespace ts1 -drop datafile 'datafile02.dat' -engine=ndb; -DROP TABLESPACE ts1 ENGINE=NDB; -DROP LOGFILE GROUP lg1 ENGINE=NDB; ---sync_slave_with_master - -# End of 5.1 test case diff --git a/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt b/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt index f47e5a7e98f..d6f11dcd7bc 100644 --- a/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt +++ b/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt @@ -1 +1 @@ ---binlog-format=row --default-storage-engine=ndb +--binlog-format=row --default-storage-engine=ndbcluster diff --git a/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt b/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt index f31d53c3cb1..b7990823676 100644 --- a/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt +++ b/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt @@ -1 +1 @@ ---default-storage-engine=ndb --binlog-format=row +--default-storage-engine=ndbcluster --binlog-format=row diff --git a/mysql-test/t/rpl_stm_until.test b/mysql-test/t/rpl_stm_until.test index 1bd87db88cb..9a4e4471fe1 100644 --- a/mysql-test/t/rpl_stm_until.test +++ b/mysql-test/t/rpl_stm_until.test @@ -32,7 +32,7 @@ wait_for_slave_to_stop; select * from t1; --replace_result $MASTER_MYPORT MASTER_MYPORT --replace_column 1 # 9 # 11 # 23 # 33 # -show slave status; +--query_vertical SHOW SLAVE STATUS # this should fail right after start start slave until master_log_file='master-no-such-bin.000001', master_log_pos=291; @@ -42,7 +42,7 @@ sleep 2; wait_for_slave_to_stop; --replace_result $MASTER_MYPORT MASTER_MYPORT --replace_column 1 # 9 # 11 # 23 # 33 # -show slave status; +--query_vertical SHOW SLAVE STATUS # try replicate all up to and not including the second insert to t2; start slave until relay_log_file='slave-relay-bin.000004', relay_log_pos=746; @@ -51,7 +51,7 @@ wait_for_slave_to_stop; select * from t2; --replace_result $MASTER_MYPORT MASTER_MYPORT --replace_column 1 # 9 # 11 # 23 # 33 # -show slave status; +--query_vertical SHOW SLAVE STATUS # clean up start slave; @@ -68,7 +68,7 @@ wait_for_slave_to_stop; # here the sql slave thread should be stopped --replace_result $MASTER_MYPORT MASTER_MYPORT bin.000005 bin.000004 bin.000006 bin.000004 bin.000007 bin.000004 --replace_column 1 # 9 # 23 # 33 # -show slave status; +--query_vertical SHOW SLAVE STATUS #testing various error conditions --error 1277 diff --git a/mysql-test/t/rpl_temporary.test b/mysql-test/t/rpl_temporary.test index 612ab54dff1..0d91a9f8e91 100644 --- a/mysql-test/t/rpl_temporary.test +++ b/mysql-test/t/rpl_temporary.test @@ -165,14 +165,19 @@ drop table t5; # value was set up at the moment of temp table creation # connection con1; -set @session.pseudo_thread_id=100; +set @@session.pseudo_thread_id=100; create temporary table t101 (id int); create temporary table t102 (id int); -set @session.pseudo_thread_id=200; +set @@session.pseudo_thread_id=200; create temporary table t201 (id int); -create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); -set @con1_id=connection_id(); -kill @con1_id; +#create temporary table `t``201` (id int); +# emulate internal temp table not to come to binlog +create temporary table `#sql_not_user_table202` (id int); +set @@session.pseudo_thread_id=300; +create temporary table t301 (id int); +create temporary table t302 (id int); +create temporary table `#sql_not_user_table303` (id int); +disconnect con1; #now do something to show that slave is ok after DROP temp tables connection master; @@ -185,4 +190,17 @@ select * from t1 /* must be 1 */; connection master; drop table t1; -# End of 5.1 tests + +# +#14157: utf8 encoding in binlog without set character_set_client +# +--exec $MYSQL --character-sets-dir=../sql/share/charsets/ --default-character-set=latin1 test -e 'create table t1 (a int); set names latin1; create temporary table `äöüÄÖÜ` (a int); insert into `äöüÄÖÜ` values (1); insert into t1 select * from `äöüÄÖÜ`' + +sync_slave_with_master; +#connection slave; +select * from t1; + +connection master; +drop table t1; + +# End of 5.0 tests diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index cc0dd60d01b..08717fce114 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -1,8 +1,4 @@ ################################### -# Change Author: JBM -# Change Date: 2006-01-17 -# Change: Added order by -################################### # # Test of replicating user variables # diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 8e3c5847846..4b6ae921b9b 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2886,3 +2886,37 @@ SELECT * FROM t1; UPDATE t1 SET i = i - 1; SELECT * FROM t1; DROP TABLE t1; + +# BUG#17379 + +create table t1 (a int); +insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t2 (a int, b int, c int, e int, primary key(a,b,c)); +insert into t2 select A.a, B.a, C.a, C.a from t1 A, t1 B, t1 C; +analyze table t2; +select 'In next EXPLAIN, B.rows must be exactly 10:' Z; + +explain select * from t2 A, t2 B where A.a=5 and A.b=5 and A.C<5 + and B.a=5 and B.b=A.e and (B.b =1 or B.b = 3 or B.b=5); +drop table t1, t2; + +# +#Bug #18940: selection of optimal execution plan caused by equality +# propagation (the bug was fixed by the patch for bug #17379) + +CREATE TABLE t1 (a int PRIMARY KEY, b int, INDEX(b)); +INSERT INTO t1 VALUES (1, 3), (9,4), (7,5), (4,5), (6,2), + (3,1), (5,1), (8,9), (2,2), (0,9); + +CREATE TABLE t2 (c int, d int, f int, INDEX(c,f)); +INSERT INTO t2 VALUES + (1,0,0), (1,0,1), (2,0,0), (2,0,1), (3,0,0), (4,0,1), + (5,0,0), (5,0,1), (6,0,0), (0,0,1), (7,0,0), (7,0,1), + (0,0,0), (0,0,1), (8,0,0), (8,0,1), (9,0,0), (9,0,1); + +EXPLAIN +SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6; +EXPLAIN +SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0; + +DROP TABLE t1, t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 8ccd954eeea..90d43bf7cc0 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5780,6 +5780,76 @@ select routine_name,routine_schema from information_schema.routines where # +# BUG#12472/BUG#15137 'CREATE TABLE ... SELECT ... which explicitly or +# implicitly uses stored function gives "Table not locked" error'. +# +--disable_warnings +drop function if exists bug12472| +--enable_warnings +create function bug12472() returns int return (select count(*) from t1)| +# Check case when function is used directly +create table t3 as select bug12472() as i| +show create table t3| +select * from t3| +drop table t3| +# Check case when function is used indirectly through view +create view v1 as select bug12472() as j| +create table t3 as select * from v1| +show create table t3| +select * from t3| +drop table t3| +drop view v1| +drop function bug12472| + + +# +# BUG#18587: Function that accepts and returns TEXT garbles data if longer than +# 766 chars +# + +# Prepare. + +--disable_warnings +DROP FUNCTION IF EXISTS bug18589_f1| +DROP PROCEDURE IF EXISTS bug18589_p1| +DROP PROCEDURE IF EXISTS bug18589_p2| +--enable_warnings + +CREATE FUNCTION bug18589_f1(arg TEXT) RETURNS TEXT +BEGIN + RETURN CONCAT(arg, ""); +END| + +CREATE PROCEDURE bug18589_p1(arg TEXT, OUT ret TEXT) +BEGIN + SET ret = CONCAT(arg, ""); +END| + +CREATE PROCEDURE bug18589_p2(arg TEXT) +BEGIN + DECLARE v TEXT; + CALL bug18589_p1(arg, v); + SELECT v; +END| + +# Test case. + +SELECT bug18589_f1(REPEAT("a", 767))| + +SET @bug18589_v1 = ""| +CALL bug18589_p1(REPEAT("a", 767), @bug18589_v1)| +SELECT @bug18589_v1| + +CALL bug18589_p2(REPEAT("a", 767))| + +# Cleanup. + +DROP FUNCTION bug18589_f1| +DROP PROCEDURE bug18589_p1| +DROP PROCEDURE bug18589_p2| + + +# # BUG#NNNN: New bug synopsis # #--disable_warnings diff --git a/mysql-test/t/strict.test b/mysql-test/t/strict.test index 54260b09331..212150e057c 100644 --- a/mysql-test/t/strict.test +++ b/mysql-test/t/strict.test @@ -1145,3 +1145,13 @@ create table t1(a bit(2)); insert into t1 values(b'101'); select * from t1; drop table t1; + +# +# Bug#17626 CREATE TABLE ... SELECT failure with TRADITIONAL SQL mode +# +set sql_mode='traditional'; +create table t1 (date date not null); +create table t2 select date from t1; +show create table t2; +drop table t2,t1; +set @@sql_mode= @org_mode; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 1ef80bdd7ac..3f48b648f40 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2085,3 +2085,27 @@ create table t2 (a int, b int); insert into t2 values (2, 1), (1, 0); delete from t1 where c <= 1140006215 and (select b from t2 where a = 2) = 1; drop table t1, t2; + +# +# Bug #7549: Missing error message for invalid view selection with subquery +# + +CREATE TABLE t1 (a INT); + +--error 1054 +CREATE VIEW v1 AS SELECT * FROM t1 WHERE no_such_column = ANY (SELECT 1); +--error 1054 +CREATE VIEW v2 AS SELECT * FROM t1 WHERE no_such_column = (SELECT 1); +--error 1054 +SELECT * FROM t1 WHERE no_such_column = ANY (SELECT 1); + +DROP TABLE t1; + +# +# Bug#19077: A nested materialized derived table is used before being populated. +# +create table t1 (i int, j bigint); +insert into t1 values (1, 2), (2, 2), (3, 2); +select * from (select min(i) from t1 where j=(select * from (select min(j) from t1) t2)) t3; +drop table t1; + diff --git a/mysql-test/t/trigger-grant.test b/mysql-test/t/trigger-grant.test index 67aec1496dd..8ca8ffe904b 100644 --- a/mysql-test/t/trigger-grant.test +++ b/mysql-test/t/trigger-grant.test @@ -690,3 +690,176 @@ SELECT @mysqltest_var; DROP USER mysqltest_u1@localhost; DROP DATABASE mysqltest_db1; + + +# +# Test for bug #14635 Accept NEW.x as INOUT parameters to stored +# procedures from within triggers +# +# We require UPDATE privilege when NEW.x passed as OUT parameter, and +# SELECT and UPDATE when NEW.x passed as INOUT parameter. +# +DELETE FROM mysql.user WHERE User LIKE 'mysqltest_%'; +DELETE FROM mysql.db WHERE User LIKE 'mysqltest_%'; +DELETE FROM mysql.tables_priv WHERE User LIKE 'mysqltest_%'; +DELETE FROM mysql.columns_priv WHERE User LIKE 'mysqltest_%'; +FLUSH PRIVILEGES; + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest_db1; +--enable_warnings + +CREATE DATABASE mysqltest_db1; +USE mysqltest_db1; + +CREATE TABLE t1 (i1 INT); +CREATE TABLE t2 (i1 INT); + +CREATE USER mysqltest_dfn@localhost; +CREATE USER mysqltest_inv@localhost; + +GRANT EXECUTE, CREATE ROUTINE, TRIGGER ON *.* TO mysqltest_dfn@localhost; +GRANT INSERT ON mysqltest_db1.* TO mysqltest_inv@localhost; + +connect (definer,localhost,mysqltest_dfn,,mysqltest_db1); +connect (invoker,localhost,mysqltest_inv,,mysqltest_db1); + +connection definer; +CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 3; +CREATE PROCEDURE p2(INOUT i INT) DETERMINISTIC NO SQL SET i = i * 5; + +# Check that having no privilege won't work. +connection definer; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW + CALL p2(NEW.i1); + +connection invoker; +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t1 VALUES (7); +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t2 VALUES (11); + +connection definer; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; + +# Check that having only SELECT privilege is not enough. +connection default; +GRANT SELECT ON mysqltest_db1.* TO mysqltest_dfn@localhost; + +connection definer; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW + CALL p2(NEW.i1); + +connection invoker; +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t1 VALUES (13); +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t2 VALUES (17); + +connection default; +REVOKE SELECT ON mysqltest_db1.* FROM mysqltest_dfn@localhost; + +connection definer; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; + +# Check that having only UPDATE privilege is enough for OUT parameter, +# but not for INOUT parameter. +connection default; +GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost; + +connection definer; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW + CALL p2(NEW.i1); + +connection invoker; +INSERT INTO t1 VALUES (19); +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t2 VALUES (23); + +connection default; +REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost; + +connection definer; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; + +# Check that having SELECT and UPDATE privileges is enough. +connection default; +GRANT SELECT, UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost; + +connection definer; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + CALL p1(NEW.i1); +CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW + CALL p2(NEW.i1); + +connection invoker; +INSERT INTO t1 VALUES (29); +INSERT INTO t2 VALUES (31); + +connection default; +REVOKE SELECT, UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost; + +connection definer; +DROP TRIGGER t2_bi; +DROP TRIGGER t1_bi; + +connection default; +DROP PROCEDURE p2; +DROP PROCEDURE p1; + +# Check that late procedure redefining won't open a security hole. +connection default; +GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost; + +connection definer; +CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 37; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + CALL p1(NEW.i1); + +connection invoker; +INSERT INTO t1 VALUES (41); + +connection definer; +DROP PROCEDURE p1; +CREATE PROCEDURE p1(IN i INT) DETERMINISTIC NO SQL SET @v1 = i + 43; + +connection invoker; +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t1 VALUES (47); + +connection definer; +DROP PROCEDURE p1; +CREATE PROCEDURE p1(INOUT i INT) DETERMINISTIC NO SQL SET i = i + 51; + +connection invoker; +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t1 VALUES (53); + +connection default; +DROP PROCEDURE p1; +REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost; + +connection definer; +DROP TRIGGER t1_bi; + +# Cleanup. +disconnect definer; +disconnect invoker; +connection default; +DROP USER mysqltest_inv@localhost; +DROP USER mysqltest_dfn@localhost; +DROP TABLE t2; +DROP TABLE t1; +DROP DATABASE mysqltest_db1; +USE test; + +--echo End of 5.0 tests. diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index f13a94513b6..a87f289e94e 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -1165,4 +1165,126 @@ SELECT * FROM t1 WHERE conn_id != trigger_conn_id; DROP TRIGGER t1_bi; DROP TABLE t1; + +# +# Bug#6951: Triggers/Traditional: SET @ result wrong +# +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i1 INT); + +SET @save_sql_mode=@@sql_mode; + +SET SQL_MODE=''; + +CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW + SET @x = 5/0; + +SET SQL_MODE='traditional'; + +CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW + SET @x = 5/0; + +SET @x=1; +INSERT INTO t1 VALUES (@x); +SELECT @x; + +SET @x=2; +--error 1365 +UPDATE t1 SET i1 = @x; +SELECT @x; + +SET SQL_MODE=''; + +SET @x=3; +INSERT INTO t1 VALUES (@x); +SELECT @x; + +SET @x=4; +--error 1365 +UPDATE t1 SET i1 = @x; +SELECT @x; + +SET @@sql_mode=@save_sql_mode; + +DROP TRIGGER t1_ai; +DROP TRIGGER t1_au; +DROP TABLE t1; + + +# +# Test for bug #14635 Accept NEW.x as INOUT parameters to stored +# procedures from within triggers +# +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; +--enable_warnings + +CREATE TABLE t1 (i1 INT); + +# Check that NEW.x pseudo variable is accepted as INOUT and OUT +# parameter to stored routine. +INSERT INTO t1 VALUES (3); +CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET i1 = 5; +CREATE PROCEDURE p2(INOUT i1 INT) DETERMINISTIC NO SQL SET i1 = i1 * 7; +delimiter //; +CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW +BEGIN + CALL p1(NEW.i1); + CALL p2(NEW.i1); +END// +delimiter ;// +UPDATE t1 SET i1 = 11 WHERE i1 = 3; +DROP TRIGGER t1_bu; +DROP PROCEDURE p2; +DROP PROCEDURE p1; + +# Check that OLD.x pseudo variable is not accepted as INOUT and OUT +# parameter to stored routine. +INSERT INTO t1 VALUES (13); +CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 17; +CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW + CALL p1(OLD.i1); +--error ER_SP_NOT_VAR_ARG +UPDATE t1 SET i1 = 19 WHERE i1 = 13; +DROP TRIGGER t1_bu; +DROP PROCEDURE p1; + +INSERT INTO t1 VALUES (23); +CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 29; +CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW + CALL p1(OLD.i1); +--error ER_SP_NOT_VAR_ARG +UPDATE t1 SET i1 = 31 WHERE i1 = 23; +DROP TRIGGER t1_bu; +DROP PROCEDURE p1; + +# Check that NEW.x pseudo variable is read-only in the AFTER TRIGGER. +INSERT INTO t1 VALUES (37); +CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 41; +CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW + CALL p1(NEW.i1); +--error ER_SP_NOT_VAR_ARG +UPDATE t1 SET i1 = 43 WHERE i1 = 37; +DROP TRIGGER t1_au; +DROP PROCEDURE p1; + +INSERT INTO t1 VALUES (47); +CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 49; +CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW + CALL p1(NEW.i1); +--error ER_SP_NOT_VAR_ARG +UPDATE t1 SET i1 = 51 WHERE i1 = 47; +DROP TRIGGER t1_au; +DROP PROCEDURE p1; + +# Post requisite. +SELECT * FROM t1; + +DROP TABLE t1; + # End of 5.0 tests diff --git a/mysql-test/t/variables-master.opt b/mysql-test/t/variables-master.opt deleted file mode 100644 index abd826bc7fa..00000000000 --- a/mysql-test/t/variables-master.opt +++ /dev/null @@ -1 +0,0 @@ ---max_join_size=100 diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 018337f2631..7aa79f0eb40 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -5,6 +5,37 @@ drop table if exists t1,t2; --enable_warnings +# +# Bug #19263: variables.test doesn't clean up after itself (I/II -- save) +# +set @my_binlog_cache_size =@@global.binlog_cache_size; +set @my_connect_timeout =@@global.connect_timeout; +set @my_delayed_insert_timeout =@@global.delayed_insert_timeout; +set @my_delayed_queue_size =@@global.delayed_queue_size; +set @my_flush =@@global.flush; +set @my_flush_time =@@global.flush_time; +set @my_key_buffer_size =@@global.key_buffer_size; +set @my_max_binlog_cache_size =@@global.max_binlog_cache_size; +set @my_max_binlog_size =@@global.max_binlog_size; +set @my_max_connect_errors =@@global.max_connect_errors; +set @my_max_delayed_threads =@@global.max_delayed_threads; +set @my_max_heap_table_size =@@global.max_heap_table_size; +set @my_max_insert_delayed_threads=@@global.max_insert_delayed_threads; +set @my_max_join_size =@@global.max_join_size; +set @my_max_user_connections =@@global.max_user_connections; +set @my_max_write_lock_count =@@global.max_write_lock_count; +set @my_myisam_data_pointer_size =@@global.myisam_data_pointer_size; +set @my_net_buffer_length =@@global.net_buffer_length; +set @my_net_write_timeout =@@global.net_write_timeout; +set @my_net_read_timeout =@@global.net_read_timeout; +set @my_query_cache_limit =@@global.query_cache_limit; +set @my_query_cache_type =@@global.query_cache_type; +set @my_rpl_recovery_rank =@@global.rpl_recovery_rank; +set @my_server_id =@@global.server_id; +set @my_slow_launch_time =@@global.slow_launch_time; +set @my_storage_engine =@@global.storage_engine; +set @my_thread_cache_size =@@global.thread_cache_size; + # case insensitivity tests (new in 5.0) set @`test`=1; select @test, @`test`, @TEST, @`TEST`, @"teSt"; @@ -508,4 +539,68 @@ select @@system_time_zone; select @@version, @@version_comment, @@version_compile_machine, @@version_compile_os; +# +# Bug #1039: make tmpdir and datadir available as @@variables (also included +# basedir) +# +# Don't actually output, since it depends on the system +--replace_column 1 # 2 # 3 # +select @@basedir, @@datadir, @@tmpdir; +--replace_column 2 # +show variables like 'basedir'; +--replace_column 2 # +show variables like 'datadir'; +--replace_column 2 # +show variables like 'tmpdir'; + +# +# Bug #19606: make ssl settings available via SHOW VARIABLES and @@variables +# +# Don't actually output, since it depends on the system +--replace_column 1 # 2 # 3 # 4 # 5 # +select @@ssl_ca, @@ssl_capath, @@ssl_cert, @@ssl_cipher, @@ssl_key; +--replace_column 2 # +show variables like 'ssl%'; + +# +# Bug #19616: make log_queries_not_using_indexes available in SHOW VARIABLES +# and as @@log_queries_not_using_indexes +# +select @@log_queries_not_using_indexes; +show variables like 'log_queries_not_using_indexes'; + --echo End of 5.0 tests + +# This is at the very after the versioned tests, since it involves doing +# cleanup +# +# Bug #19263: variables.test doesn't clean up after itself (II/II -- +# restore) +# +set global binlog_cache_size =@my_binlog_cache_size; +set global connect_timeout =@my_connect_timeout; +set global delayed_insert_timeout =@my_delayed_insert_timeout; +set global delayed_queue_size =@my_delayed_queue_size; +set global flush =@my_flush; +set global flush_time =@my_flush_time; +set global key_buffer_size =@my_key_buffer_size; +set global max_binlog_cache_size =default; #@my_max_binlog_cache_size; +set global max_binlog_size =@my_max_binlog_size; +set global max_connect_errors =@my_max_connect_errors; +set global max_delayed_threads =@my_max_delayed_threads; +set global max_heap_table_size =@my_max_heap_table_size; +set global max_insert_delayed_threads=@my_max_insert_delayed_threads; +set global max_join_size =@my_max_join_size; +set global max_user_connections =@my_max_user_connections; +set global max_write_lock_count =@my_max_write_lock_count; +set global myisam_data_pointer_size =@my_myisam_data_pointer_size; +set global net_buffer_length =@my_net_buffer_length; +set global net_write_timeout =@my_net_write_timeout; +set global net_read_timeout =@my_net_read_timeout; +set global query_cache_limit =@my_query_cache_limit; +set global query_cache_type =@my_query_cache_type; +set global rpl_recovery_rank =@my_rpl_recovery_rank; +set global server_id =@my_server_id; +set global slow_launch_time =@my_slow_launch_time; +set global storage_engine =@my_storage_engine; +set global thread_cache_size =@my_thread_cache_size; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 6094382c094..535ba3da7fa 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2517,3 +2517,87 @@ create view v2 as select convert_tz(dt, 'UTC', 'Europe/Moscow') as ldt from v1; select * from v2; drop view v1, v2; drop table t1; + +# +# Bug #19490: usage of view specified by a query with GROUP BY +# an expression containing non-constant interval + +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, d datetime); + +CREATE VIEW v1 AS +SELECT id, date(d) + INTERVAL TIME_TO_SEC(d) SECOND AS t, COUNT(*) + FROM t1 GROUP BY id, t; + +SHOW CREATE VIEW v1; +SELECT * FROM v1; + +DROP VIEW v1; +DROP TABLE t1; + +# +# Bug#19077: A nested materialized view is used before being populated. +# +CREATE TABLE t1 (i INT, j BIGINT); +INSERT INTO t1 VALUES (1, 2), (2, 2), (3, 2); +CREATE VIEW v1 AS SELECT MIN(j) AS j FROM t1; +CREATE VIEW v2 AS SELECT MIN(i) FROM t1 WHERE j = ( SELECT * FROM v1 ); +SELECT * FROM v2; +DROP VIEW v2, v1; +DROP TABLE t1; + +# +# Bug #19573: VIEW with HAVING that refers an alias name +# + +CREATE TABLE t1( + fName varchar(25) NOT NULL, + lName varchar(25) NOT NULL, + DOB date NOT NULL, + uID int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY); + +INSERT INTO t1(fName, lName, DOB) VALUES + ('Hank', 'Hill', '1964-09-29'), + ('Tom', 'Adams', '1908-02-14'), + ('Homer', 'Simpson', '1968-03-05'); + +CREATE VIEW v1 AS + SELECT (year(now())-year(DOB)) AS Age + FROM t1 HAVING Age < 75; +SHOW CREATE VIEW v1; + +SELECT (year(now())-year(DOB)) AS Age FROM t1 HAVING Age < 75; +SELECT * FROM v1; + +DROP VIEW v1; +DROP TABLE t1; + +# +# Bug #19089: wrong inherited dafault values in temp table views +# + +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a char(6) DEFAULT 'xxx'); +INSERT INTO t1(id) VALUES (1), (2), (3), (4); +INSERT INTO t1 VALUES (5,'yyy'), (6,'yyy'); +SELECT * FROM t1; + +CREATE VIEW v1(a, m) AS SELECT a, MIN(id) FROM t1 GROUP BY a; +SELECT * FROM v1; + +CREATE TABLE t2 SELECT * FROM v1; +INSERT INTO t2(m) VALUES (0); +SELECT * FROM t2; + +DROP VIEW v1; +DROP TABLE t1,t2; + +CREATE TABLE t1 (id int PRIMARY KEY, e ENUM('a','b') NOT NULL DEFAULT 'b'); +INSERT INTO t1(id) VALUES (1), (2), (3); +INSERT INTO t1 VALUES (4,'a'); +SELECT * FROM t1; + +CREATE VIEW v1(m, e) AS SELECT MIN(id), e FROM t1 GROUP BY e; +CREATE TABLE t2 SELECT * FROM v1; +SELECT * FROM t2; + +DROP VIEW v1; +DROP TABLE IF EXISTS t1,t2; diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 69e4ee933b8..6939dd489cd 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -11,26 +11,65 @@ fun:calloc fun:_dl_allocate_tls fun:allocate_stack + fun:pthread_create* +} + +{ + pthread allocate_tls memory loss 2 + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls fun:pthread_create@@GLIBC_2.1 } { - pthread allocate_dtv memory loss + pthead_exit memory loss 1 + Memcheck:Leak + fun:malloc + fun:_dl_new_object + fun:_dl_map_object_from_fd +} + +{ + pthread_exit memory loss 2 + Memcheck:Leak + fun:malloc + fun:_dl_map_object + fun:dl_open_worker +} + +{ + pthread_exit memory loss 3 + Memcheck:Leak + fun:malloc + fun:_dl_map_object_deps + fun:dl_open_worker +} + +{ + pthread_exit memory loss 4 Memcheck:Leak fun:calloc - fun:allocate_dtv - fun:_dl_allocate_tls_storage - fun:__GI__dl_allocate_tls - fun:pthread_create + fun:_dl_check_map_versions + fun:dl_open_worker } { - pthread allocate_dtv memory loss second + pthread_exit memory loss 5 + Memcheck:Leak + fun:calloc + fun:_dl_new_object + fun:_dl_map_object_from_fd +} + +{ + pthread allocate_dtv memory loss Memcheck:Leak fun:calloc fun:allocate_dtv - fun:_dl_allocate_tls - fun:pthread_create* + fun:_dl_allocate_tls_storage + fun:__GI__dl_allocate_tls + fun:pthread_create } { @@ -83,6 +122,15 @@ obj:/lib/ld-*.so } +{ + strlen/_dl_init_paths/dl_main/_dl_sysdep_start(Cond) + Memcheck:Cond + fun:strlen + fun:_dl_init_paths + fun:dl_main + fun:_dl_sysdep_start +} + { pthread errno Memcheck:Leak @@ -135,6 +183,24 @@ } { + libz longest_match3 + Memcheck:Cond + fun:longest_match + fun:deflate_slow + fun:deflate + fun:azflush +} + +{ + libz longest_match3 + Memcheck:Cond + fun:longest_match + fun:deflate_slow + fun:deflate + fun:azclose +} + +{ libz deflate Memcheck:Cond obj:*/libz.so.* @@ -173,3 +239,169 @@ fun:my_thread_init fun:kill_server_thread } + +# Red Hat AS 4 32 bit +{ + dl_relocate_object + Memcheck:Cond + fun:_dl_relocate_object +} + +# +# Leaks reported in _dl_* internal functions on Linux amd64 / glibc2.3.2. +# + +{ + _dl_start invalid write8 + Memcheck:Addr8 + fun:_dl_start +} + +{ + _dl_start invalid write4 + Memcheck:Addr4 + fun:_dl_start +} + +{ + _dl_start/_dl_setup_hash invalid read8 + Memcheck:Addr8 + fun:_dl_setup_hash + fun:_dl_start +} + +{ + _dl_sysdep_start invalid write8 + Memcheck:Addr8 + fun:_dl_sysdep_start +} + +{ + _dl_init invalid write8 + Memcheck:Addr8 + fun:_dl_init +} + +{ + _dl_init invalid write4 + Memcheck:Addr4 + fun:_dl_init +} + +{ + _dl_init/_dl_init invalid read8 + Memcheck:Addr8 + fun:_dl_debug_initialize + fun:_dl_init +} + +{ + _dl_init/_dl_debug_state invalid read8 + Memcheck:Addr8 + fun:_dl_debug_state + fun:_dl_init +} + +{ + init invalid write8 + Memcheck:Addr8 + fun:init +} + +{ + fixup invalid write8 + Memcheck:Addr8 + fun:fixup +} + +{ + fixup/_dl_lookup_versioned_symbol invalid read8 + Memcheck:Addr8 + fun:_dl_lookup_versioned_symbol + fun:fixup +} + +{ + _dl_runtime_resolve invalid read8 + Memcheck:Addr8 + fun:_dl_runtime_resolve +} + +{ + __libc_start_main invalid write8 + Memcheck:Addr8 + fun:__libc_start_main +} + +{ + __libc_start_main/__sigjmp_save invalid write4 + Memcheck:Addr4 + fun:__sigjmp_save + fun:__libc_start_main +} + +# +# These seem to be libc threading stuff, not related to MySQL code (allocations +# during pthread_exit()). Googling shows other projects also using these +# suppressions. +# +# Note that these all stem from pthread_exit() deeper in the call stack, but +# Valgrind only allows the top four calls in the suppressions. +# + +{ + libc pthread_exit 1 + Memcheck:Leak + fun:malloc + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + libc pthread_exit 2 + Memcheck:Leak + fun:malloc + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error +} + +{ + libc pthread_exit 3 + Memcheck:Leak + fun:malloc + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error +} + +{ + libc pthread_exit 4 + Memcheck:Leak + fun:calloc + fun:_dl_check_map_versions + fun:dl_open_worker + fun:_dl_catch_error +} + +{ + libc pthread_exit 5 + Memcheck:Leak + fun:calloc + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + + +# +# This is seen internally in the system libraries on 64-bit RHAS3. +# + +{ + __lll_mutex_unlock_wake uninitialized + Memcheck:Param + futex(utime) + fun:__lll_mutex_unlock_wake +} diff --git a/mysys/default.c b/mysys/default.c index 580bcc19eca..3a80d7b37b9 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -244,7 +244,8 @@ err: handle_option_ctx structure. group_name The name of the group the option belongs to. option The very option to be processed. It is already - prepared to be used in argv (has -- prefix) + prepared to be used in argv (has -- prefix). If it + is NULL, we are handling a new group (section). DESCRIPTION This handler checks whether a group is one of the listed and adds an option @@ -263,6 +264,9 @@ static int handle_default_option(void *in_ctx, const char *group_name, char *tmp; struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx; + if (!option) + return 0; + if (find_type((char *)group_name, ctx->group, 3)) { if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1))) @@ -719,6 +723,10 @@ static int search_default_file_with_ext(Process_option_func opt_handler, end[0]=0; strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096)); + + /* signal that a new group is found */ + opt_handler(handler_ctx, curr_gr, NULL); + continue; } if (!found_group) diff --git a/mysys/default_modify.c b/mysys/default_modify.c index 0f58b8a930c..8dbcac699ea 100644 --- a/mysys/default_modify.c +++ b/mysys/default_modify.c @@ -40,11 +40,13 @@ static char *add_option(char *dst, const char *option_value, SYNOPSYS modify_defaults_file() file_location The location of configuration file to edit - option option to look for - option value The value of the option we would like to set - section_name the name of the section - remove_option This is true if we want to remove the option. - False otherwise. + option The name of the option to look for (can be NULL) + option value The value of the option we would like to set (can be NULL) + section_name The name of the section (must be NOT NULL) + remove_option This defines what we want to remove: + - MY_REMOVE_NONE -- nothing to remove; + - MY_REMOVE_OPTION -- remove the specified option; + - MY_REMOVE_SECTION -- remove the specified section; IMPLEMENTATION We open the option file first, then read the file line-by-line, looking for the section we need. At the same time we put these lines @@ -67,7 +69,9 @@ int modify_defaults_file(const char *file_location, const char *option, FILE *cnf_file; MY_STAT file_stat; char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer; - uint opt_len, optval_len, sect_len, nr_newlines= 0, buffer_size; + uint opt_len= 0; + uint optval_len= 0; + uint sect_len, nr_newlines= 0, buffer_size; my_bool in_section= FALSE, opt_applied= 0; uint reserve_extended; uint new_opt_len; @@ -81,8 +85,11 @@ int modify_defaults_file(const char *file_location, const char *option, if (my_fstat(fileno(cnf_file), &file_stat, MYF(0))) goto malloc_err; - opt_len= (uint) strlen(option); - optval_len= (uint) strlen(option_value); + if (option && option_value) + { + opt_len= (uint) strlen(option); + optval_len= (uint) strlen(option_value); + } new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN; @@ -119,8 +126,8 @@ int modify_defaults_file(const char *file_location, const char *option, continue; } - /* correct the option */ - if (in_section && !strncmp(src_ptr, option, opt_len) && + /* correct the option (if requested) */ + if (option && in_section && !strncmp(src_ptr, option, opt_len) && (*(src_ptr + opt_len) == '=' || my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) || *(src_ptr + opt_len) == '\0')) @@ -143,7 +150,12 @@ int modify_defaults_file(const char *file_location, const char *option, } else { - /* If going to new group and we have option to apply, do it now */ + /* + If we are going to the new group and have an option to apply, do + it now. If we are removing a single option or the whole section + this will only trigger opt_applied flag. + */ + if (in_section && !opt_applied && *src_ptr == '[') { dst_ptr= add_option(dst_ptr, option_value, option, remove_option); @@ -153,7 +165,10 @@ int modify_defaults_file(const char *file_location, const char *option, for (; nr_newlines; nr_newlines--) dst_ptr= strmov(dst_ptr, NEWLINE); - dst_ptr= strmov(dst_ptr, linebuff); + + /* Skip the section if MY_REMOVE_SECTION was given */ + if (!in_section || remove_option != MY_REMOVE_SECTION) + dst_ptr= strmov(dst_ptr, linebuff); } /* Look for a section */ if (*src_ptr == '[') @@ -167,18 +182,31 @@ int modify_defaults_file(const char *file_location, const char *option, {} if (*src_ptr != ']') + { + in_section= FALSE; continue; /* Missing closing parenthesis. Assume this was no group */ + } + + if (remove_option == MY_REMOVE_SECTION) + dst_ptr= dst_ptr - strlen(linebuff); + in_section= TRUE; } else in_section= FALSE; /* mark that this section is of no interest to us */ } } - /* File ended. */ - if (!opt_applied && !remove_option && in_section) + + /* + File ended. Apply an option or set opt_applied flag (in case of + MY_REMOVE_SECTION) so that the changes are saved. Do not do anything + if we are removing non-existent option. + */ + + if (!opt_applied && in_section && (remove_option != MY_REMOVE_OPTION)) { /* New option still remains to apply at the end */ - if (*(dst_ptr - 1) != '\n') + if (!remove_option && *(dst_ptr - 1) != '\n') dst_ptr= strmov(dst_ptr, NEWLINE); dst_ptr= add_option(dst_ptr, option_value, option, remove_option); opt_applied= 1; diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 1dab9a47ed8..71d73048a7b 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -84,14 +84,6 @@ #define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log" */ -#if defined(MSDOS) && !defined(M_IC80386) -/* we nead much memory */ -#undef my_malloc_lock -#undef my_free_lock -#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE) -#define my_free_lock(A,B) hfree(A) -#endif /* defined(MSDOS) && !defined(M_IC80386) */ - #define STRUCT_PTR(TYPE, MEMBER, a) \ (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER)) diff --git a/mysys/mf_path.c b/mysys/mf_path.c index cdaee74dd2c..571c4aa9fba 100644 --- a/mysys/mf_path.c +++ b/mysys/mf_path.c @@ -73,7 +73,7 @@ my_string my_path(my_string to, const char *progname, /* test if file without filename is found in path */ /* Returns to if found and to has dirpart if found, else NullS */ -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #define F_OK 0 #define PATH_SEP ';' #define PROGRAM_EXTENSION ".exe" diff --git a/mysys/mf_tempfile.c b/mysys/mf_tempfile.c index b079b9ec8e3..4f6cbd2f243 100644 --- a/mysys/mf_tempfile.c +++ b/mysys/mf_tempfile.c @@ -24,7 +24,7 @@ #endif #ifdef HAVE_TEMPNAM -#if !defined(MSDOS) && !defined(__NETWARE__) +#if !defined(__NETWARE__) extern char **environ; #endif #endif diff --git a/mysys/my_access.c b/mysys/my_access.c index 871de8de0b8..9255d716919 100644 --- a/mysys/my_access.c +++ b/mysys/my_access.c @@ -156,7 +156,7 @@ int check_if_legal_tablename(const char *name) } -#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) +#if defined(__WIN__) || defined(__EMX__) /* @@ -199,4 +199,4 @@ int check_if_legal_filename(const char *path) DBUG_RETURN(0); } -#endif /* defined(MSDOS) || defined(__WIN__) || defined(__EMX__) */ +#endif /* defined(__WIN__) || defined(__EMX__) */ diff --git a/mysys/my_clock.c b/mysys/my_clock.c index 384239bb2b2..41d659c0ffe 100644 --- a/mysys/my_clock.c +++ b/mysys/my_clock.c @@ -24,7 +24,7 @@ long my_clock(void) { -#if !defined(MSDOS) && !defined(__WIN__) && !defined(__NETWARE__) +#if !defined(__WIN__) && !defined(__NETWARE__) struct tms tmsbuf; VOID(times(&tmsbuf)); return (tmsbuf.tms_utime + tmsbuf.tms_stime); diff --git a/mysys/my_copy.c b/mysys/my_copy.c index 342b1cdc104..a8a3a775040 100644 --- a/mysys/my_copy.c +++ b/mysys/my_copy.c @@ -95,7 +95,7 @@ int my_copy(const char *from, const char *to, myf MyFlags) if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat) DBUG_RETURN(0); /* File copyed but not stat */ VOID(chmod(to, stat_buff.st_mode & 07777)); /* Copy modes */ -#if !defined(MSDOS) && !defined(__WIN__) && !defined(__NETWARE__) +#if !defined(__WIN__) && !defined(__NETWARE__) VOID(chown(to, stat_buff.st_uid,stat_buff.st_gid)); /* Copy ownership */ #endif #if !defined(VMS) && !defined(__ZTC__) diff --git a/mysys/my_create.c b/mysys/my_create.c index d42e7e0db69..d27edb31d32 100644 --- a/mysys/my_create.c +++ b/mysys/my_create.c @@ -19,7 +19,7 @@ #include <my_dir.h> #include "mysys_err.h" #include <errno.h> -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #include <share.h> #endif @@ -47,13 +47,6 @@ File my_create(const char *FileName, int CreateFlags, int access_flags, #elif defined(VMS) fd = open((my_string) FileName, access_flags | O_CREAT, 0, "ctx=stm","ctx=bin"); -#elif defined(MSDOS) - if (access_flags & O_SHARE) - fd = sopen((my_string) FileName, access_flags | O_CREAT | O_BINARY, - SH_DENYNO, MY_S_IREAD | MY_S_IWRITE); - else - fd = open((my_string) FileName, access_flags | O_CREAT | O_BINARY, - MY_S_IREAD | MY_S_IWRITE); #elif defined(__WIN__) fd= my_sopen((my_string) FileName, access_flags | O_CREAT | O_BINARY, SH_DENYNO, MY_S_IREAD | MY_S_IWRITE); diff --git a/mysys/my_dup.c b/mysys/my_dup.c index f4c420eb459..9666f5b1858 100644 --- a/mysys/my_dup.c +++ b/mysys/my_dup.c @@ -19,7 +19,7 @@ #include "mysys_err.h" #include <my_dir.h> #include <errno.h> -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #include <share.h> #endif diff --git a/mysys/my_gethostbyname.c b/mysys/my_gethostbyname.c index 27281f3489d..0644ba02bbd 100644 --- a/mysys/my_gethostbyname.c +++ b/mysys/my_gethostbyname.c @@ -18,7 +18,7 @@ /* Thread safe version of gethostbyname_r() */ #include "mysys_priv.h" -#if !defined(MSDOS) && !defined(__WIN__) +#if !defined(__WIN__) #include <netdb.h> #endif #include <my_net.h> diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 95dc5afeae9..4de2984d9b9 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -486,7 +486,7 @@ invalid value '%s'\n", } get_one_option(optp->id, optp, argument); - (*argc)--; /* option handled (short or long), decrease argument count */ + (*argc)--; /* option handled (short or long), decrease argument count */ } else /* non-option found */ (*argv)[argvpos++]= cur_arg; diff --git a/mysys/my_getwd.c b/mysys/my_getwd.c index 765c30bc281..de8ad108ba4 100644 --- a/mysys/my_getwd.c +++ b/mysys/my_getwd.c @@ -22,7 +22,7 @@ #ifdef HAVE_GETWD #include <sys/param.h> #endif -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #include <m_ctype.h> #include <dos.h> #include <direct.h> @@ -39,11 +39,9 @@ int my_getwd(my_string buf, uint size, myf MyFlags) DBUG_ENTER("my_getwd"); DBUG_PRINT("my",("buf: 0x%lx size: %d MyFlags %d", buf,size,MyFlags)); -#if ! defined(MSDOS) if (curr_dir[0]) /* Current pos is saved here */ VOID(strmake(buf,&curr_dir[0],size-1)); else -#endif { #if defined(HAVE_GETCWD) if (!getcwd(buf,size-2) && MyFlags & MY_WME) @@ -87,43 +85,13 @@ int my_setwd(const char *dir, myf MyFlags) int res; size_s length; my_string start,pos; -#if defined(VMS) || defined(MSDOS) +#if defined(VMS) char buff[FN_REFLEN]; #endif DBUG_ENTER("my_setwd"); DBUG_PRINT("my",("dir: '%s' MyFlags %d", dir, MyFlags)); start=(my_string) dir; -#if defined(MSDOS) /* MSDOS chdir can't change drive */ -#if !defined(_DDL) && !defined(WIN32) - if ((pos=(char*) strchr(dir,FN_DEVCHAR)) != 0) - { - uint drive,drives; - - pos++; /* Skip FN_DEVCHAR */ - drive=(uint) (my_toupper(&my_charset_latin1,dir[0])-'A'+1); - drives= (uint) -1; - if ((pos-(byte*) dir) == 2 && drive > 0 && drive < 32) - { - _dos_setdrive(drive,&drives); - _dos_getdrive(&drives); - } - if (drive != drives) - { - *pos='\0'; /* Dir is now only drive */ - my_errno=errno; - my_error(EE_SETWD,MYF(ME_BELL+ME_WAITTANG),dir,ENOENT); - DBUG_RETURN(-1); - } - dir=pos; /* drive changed, change now path */ - } -#endif - if (*((pos=strend(dir)-1)) == FN_LIBCHAR && pos != dir) - { - strmov(buff,dir)[-1]=0; /* Remove last '/' */ - dir=buff; - } -#endif /* MSDOS*/ if (! dir[0] || (dir[0] == FN_LIBCHAR && dir[1] == 0)) dir=FN_ROOTDIR; #ifdef VMS diff --git a/mysys/my_init.c b/mysys/my_init.c index 4d7299c7cb1..588bb6f46d6 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -181,7 +181,7 @@ Voluntary context switches %ld, Involuntary context switches %ld\n", rus.ru_msgsnd, rus.ru_msgrcv, rus.ru_nsignals, rus.ru_nvcsw, rus.ru_nivcsw); #endif -#if ( defined(MSDOS) || defined(__NETWARE__) ) && !defined(__WIN__) +#if defined(__NETWARE__) && !defined(__WIN__) fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC); #endif #if defined(SAFEMALLOC) @@ -197,8 +197,10 @@ Voluntary context switches %ld, Involuntary context switches %ld\n", _CrtDumpMemoryLeaks(); #endif } + + if (!(infoflag & MY_DONT_FREE_DBUG)) + DBUG_END(); /* Must be done before my_thread_end */ #ifdef THREAD - DBUG_POP(); /* Must be done before my_thread_end */ my_thread_end(); my_thread_global_end(); #if defined(SAFE_MUTEX) diff --git a/mysys/my_lib.c b/mysys/my_lib.c index e96286a94a8..42c57e9d3c4 100644 --- a/mysys/my_lib.c +++ b/mysys/my_lib.c @@ -15,7 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* TODO: check for overun of memory for names. */ -/* Convert MSDOS-TIME to standar time_t */ +/* Convert MSDOS-TIME to standar time_t (still needed?) */ #define USES_TYPES /* sys/types is included */ #include "mysys_priv.h" @@ -37,7 +37,7 @@ # if defined(HAVE_NDIR_H) # include <ndir.h> # endif -# if defined(MSDOS) || defined(__WIN__) +# if defined(__WIN__) # include <dos.h> # ifdef __BORLANDC__ # include <dir.h> @@ -94,7 +94,7 @@ static int comp_names(struct fileinfo *a, struct fileinfo *b) } /* comp_names */ -#if !defined(MSDOS) && !defined(__WIN__) +#if !defined(__WIN__) MY_DIR *my_dir(const char *path, myf MyFlags) { @@ -347,7 +347,7 @@ my_string directory_file_name (my_string dst, const char *src) #endif /* VMS */ } /* directory_file_name */ -#elif defined(WIN32) +#else /* ***************************************************************************** @@ -386,7 +386,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags) *tmp_file++= '.'; /* From current dev-dir */ if (tmp_file[-1] != FN_LIBCHAR) *tmp_file++ =FN_LIBCHAR; - tmp_file[0]='*'; /* MSDOS needs this !??? */ + tmp_file[0]='*'; /* Windows needs this !??? */ tmp_file[1]='.'; tmp_file[2]='*'; tmp_file[3]='\0'; @@ -509,109 +509,7 @@ error: DBUG_RETURN((MY_DIR *) NULL); } /* my_dir */ -#else /* MSDOS and not WIN32 */ - - -/****************************************************************************** -** At MSDOS you always get stat of files, but time is in packed MSDOS-format -******************************************************************************/ - -MY_DIR *my_dir(const char* path, myf MyFlags) -{ - char *buffer; - MY_DIR *result= 0; - FILEINFO finfo; - DYNAMIC_ARRAY *dir_entries_storage; - MEM_ROOT *names_storage; - struct find_t find; - ushort mode; - char tmp_path[FN_REFLEN],*tmp_file,attrib; - DBUG_ENTER("my_dir"); - DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags)); - - /* Put LIB-CHAR as last path-character if not there */ - - tmp_file=tmp_path; - if (!*path) - *tmp_file++ ='.'; /* From current dir */ - tmp_file= strmov(tmp_file,path); - if (tmp_file[-1] == FN_DEVCHAR) - *tmp_file++= '.'; /* From current dev-dir */ - if (tmp_file[-1] != FN_LIBCHAR) - *tmp_file++ =FN_LIBCHAR; - tmp_file[0]='*'; /* MSDOS needs this !??? */ - tmp_file[1]='.'; - tmp_file[2]='*'; - tmp_file[3]='\0'; - - if (_dos_findfirst(tmp_path,_A_NORMAL | _A_SUBDIR, &find)) - goto error; - - if (!(buffer= my_malloc(ALIGN_SIZE(sizeof(MY_DIR)) + - ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)) + - sizeof(MEM_ROOT), MyFlags))) - goto error; - - dir_entries_storage= (DYNAMIC_ARRAY*)(buffer + ALIGN_SIZE(sizeof(MY_DIR))); - names_storage= (MEM_ROOT*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)) + - ALIGN_SIZE(sizeof(DYNAMIC_ARRAY))); - - if (my_init_dynamic_array(dir_entries_storage, sizeof(FILEINFO), - ENTRIES_START_SIZE, ENTRIES_INCREMENT)) - { - my_free((gptr) buffer,MYF(0)); - goto error; - } - init_alloc_root(names_storage, NAMES_START_SIZE, NAMES_START_SIZE); - - /* MY_DIR structure is allocated and completly initialized at this point */ - result= (MY_DIR*)buffer; - - do - { - if (!(finfo.name= strdup_root(names_storage, find.name))) - goto error; - - if (MyFlags & MY_WANT_STAT) - { - if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage, - sizeof(MY_STAT)))) - goto error; - - bzero(finfo.mystat, sizeof(MY_STAT)); - finfo.mystat->st_size= find.size; - mode= MY_S_IREAD; attrib= find.attrib; - if (!(attrib & _A_RDONLY)) - mode|= MY_S_IWRITE; - if (attrib & _A_SUBDIR) - mode|= MY_S_IFDIR; - finfo.mystat->st_mode= mode; - finfo.mystat->st_mtime= ((uint32) find.wr_date << 16) + find.wr_time; - } - else - finfo.mystat= NULL; - - if (push_dynamic(dir_entries_storage, (gptr)&finfo)) - goto error; - - } while (_dos_findnext(&find) == 0); - - result->dir_entry= (FILEINFO *)dir_entries_storage->buffer; - result->number_off_files= dir_entries_storage->elements; - - if (!(MyFlags & MY_DONT_SORT)) - qsort((void *) result->dir_entry, result->number_off_files, - sizeof(FILEINFO), (qsort_cmp) comp_names); - DBUG_RETURN(result); - -error: - my_dirend(result); - if (MyFlags & MY_FAE+MY_WME) - my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,errno); - DBUG_RETURN((MY_DIR *) NULL); -} /* my_dir */ - -#endif /* WIN32 && MSDOS */ +#endif /* __WIN__ */ /**************************************************************************** ** File status diff --git a/mysys/my_net.c b/mysys/my_net.c index be92adae353..09c09b280d1 100644 --- a/mysys/my_net.c +++ b/mysys/my_net.c @@ -20,7 +20,7 @@ #include <m_string.h> /* for thread safe my_inet_ntoa */ -#if !defined(MSDOS) && !defined(__WIN__) +#if !defined(__WIN__) #include <netdb.h> #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> @@ -31,7 +31,7 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#endif /* !defined(MSDOS) && !defined(__WIN__) */ +#endif /* !defined(__WIN__) */ void my_inet_ntoa(struct in_addr in, char *buf) { diff --git a/mysys/my_open.c b/mysys/my_open.c index ed323b3b1ad..6041ddde9fc 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -19,7 +19,7 @@ #include "mysys_err.h" #include <my_dir.h> #include <errno.h> -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #include <share.h> #endif @@ -45,7 +45,7 @@ File my_open(const char *FileName, int Flags, myf MyFlags) DBUG_ENTER("my_open"); DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d", FileName, Flags, MyFlags)); -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) /* Check that we don't try to open or create a file name that may cause problems for us in the future (like PRN) diff --git a/mysys/my_redel.c b/mysys/my_redel.c index 02d3db8b860..abfe84102ef 100644 --- a/mysys/my_redel.c +++ b/mysys/my_redel.c @@ -90,14 +90,14 @@ int my_copystat(const char *from, const char *to, int MyFlags) return 1; VOID(chmod(to, statbuf.st_mode & 07777)); /* Copy modes */ -#if !defined(MSDOS) && !defined(__WIN__) && !defined(__NETWARE__) +#if !defined(__WIN__) && !defined(__NETWARE__) if (statbuf.st_nlink > 1 && MyFlags & MY_LINK_WARNING) { if (MyFlags & MY_LINK_WARNING) my_error(EE_LINK_WARNING,MYF(ME_BELL+ME_WAITTANG),from,statbuf.st_nlink); } VOID(chown(to, statbuf.st_uid, statbuf.st_gid)); /* Copy ownership */ -#endif /* MSDOS */ +#endif /* !__WIN__ && !__NETWARE__ */ #ifndef VMS #ifndef __ZTC__ diff --git a/plugin/fulltext/Makefile.am b/plugin/fulltext/Makefile.am index 4df5a1dc78a..7b4ae22cbd2 100644 --- a/plugin/fulltext/Makefile.am +++ b/plugin/fulltext/Makefile.am @@ -6,4 +6,4 @@ noinst_LTLIBRARIES= mypluglib.la #pkglib_LTLIBRARIES= mypluglib.la mypluglib_la_SOURCES= plugin_example.c mypluglib_la_LDFLAGS= -module -rpath $(pkglibdir) - +mypluglib_la_CFLAGS= -DMYSQL_DYNAMIC_PLUGIN diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 56a2d4a7bc6..af3cbc19cb5 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -30,7 +30,6 @@ bin_SCRIPTS = @server_scripts@ \ mysql_find_rows \ mysqlhotcopy \ mysqldumpslow \ - mysql_explain_log \ mysqld_multi \ mysql_create_system_tables @@ -55,7 +54,6 @@ EXTRA_SCRIPTS = make_binary_distribution.sh \ mysql_find_rows.sh \ mysqlhotcopy.sh \ mysqldumpslow.sh \ - mysql_explain_log.sh \ mysqld_multi.sh \ mysqld_safe.sh \ mysql_create_system_tables.sh @@ -83,7 +81,6 @@ CLEANFILES = @server_scripts@ \ mysql_find_rows \ mysqlhotcopy \ mysqldumpslow \ - mysql_explain_log \ mysql_tableinfo \ mysqld_multi \ make_win_src_distribution \ diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index aa29d48d50d..1b071a294ed 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -148,7 +148,7 @@ BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \ if [ $BASE_SYSTEM = "netware" ] ; then BIN_FILES="$BIN_FILES \ netware/mysqld_safe$BS netware/mysql_install_db$BS \ - netware/init_db.sql netware/test_db.sql netware/mysql_explain_log$BS \ + netware/init_db.sql netware/test_db.sql$BS \ netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql \ "; # For all other platforms: diff --git a/scripts/mysql_explain_log.sh b/scripts/mysql_explain_log.sh deleted file mode 100644 index 973d9e8a363..00000000000 --- a/scripts/mysql_explain_log.sh +++ /dev/null @@ -1,392 +0,0 @@ -#!@PERL@ -w -use strict; -use DBI; - -use Getopt::Long; -$Getopt::Long::ignorecase=0; - -print "explain_log provided by http://www.mobile.de\n"; -print "=========== ================================\n"; - -my $Param={}; - -$Param->{host}=''; -$Param->{user}=''; -$Param->{password}=''; -$Param->{PrintError}=0; -$Param->{socket}=''; - -if (!GetOptions ('date|d:i' => \$Param->{ViewDate}, - 'host|h:s' => \$Param->{host}, - 'user|u:s' => \$Param->{user}, - 'password|p:s' => \$Param->{password}, - 'printerror|e:s' => \$Param->{PrintError}, - 'socket|s:s' => \$Param->{socket}, - )) { - ShowOptions(); -} -else { - $Param->{UpdateCount} = 0; - $Param->{SelectCount} = 0; - $Param->{IdxUseCount} = 0; - $Param->{LineCount} = 0; - - $Param->{Init} = 0; - $Param->{Field} = 0; - $Param->{Refresh} = 0; - $Param->{QueryCount} = 0; - $Param->{Statistics} =0; - - $Param->{Query} = undef; - $Param->{ALL} = undef ; - $Param->{Comment} = undef ; - - @{$Param->{Rows}} = (qw|possible_keys key type|); - - if ($Param->{ViewDate}) { - $Param->{View} = 0; - } - else { - $Param->{View} = 1; - } - - #print "Date=$Param->{ViewDate}, host=$Param->{host}, user=$Param->{user}, password=$Param->{password}\n"; - - $Param->{dbh}=DBI->connect("DBI:mysql:host=$Param->{host}".($Param->{socket}?";mysql_socket=$Param->{socket}":""),$Param->{user},$Param->{password},{PrintError=>0}); - if (DBI::err()) { - print "Error: " . DBI::errstr() . "\n"; - } - else { - $Param->{Start} = time; - while(<STDIN>) { - $Param->{LineCount} ++ ; - - if ($Param->{ViewDate} ) { - if (m/^(\d{6})\s+\d{1,2}:\d\d:\d\d\s.*$/) { # get date - #print "# $1 #\n"; - if ($1 == $Param->{ViewDate}) { - $Param->{View} = 1; - } - else { - $Param->{View} = 0; - } - } - } - if ($Param->{View} ) { - #print "->>>$_"; - - if (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Connect.+\s+on\s+(.*)$/i) { # get connection ID($2) and database($3) - #print "C-$1--$2--$3------\n"; - RunQuery($Param); - if (defined $3) { - $Param->{CID}->{$2} = $3 ; - #print "DB:$Param->{CID}->{$2} .. $2 .. $3 \n"; - } - } - - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Connect.+$/i) { # get connection ID($2) and database($3) - #print "\n <<<<<<<<<<<<<<<<<<----------------------------<<<<<<<<<<<<<<<< \n"; - #print "Connect \n"; - RunQuery($Param); - } - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Change user .*\s+on\s+(.*)$/i) { # get connection ID($2) and database($3) - #print "C-$1--$2--$3------\n"; - RunQuery($Param); - if (defined $3) { - $Param->{CID}->{$2} = $3 ; - #print "DB:$Param->{CID}->{$2} .. $2 .. $3 \n"; - } - } - - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Quit\s+$/i) { # remove connection ID($2) and querystring - #print "Q-$1--$2--------\n"; - RunQuery($Param); - delete $Param->{CID}->{$2} ; - } - - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Query\s+(select.+)$/i) { # get connection ID($2) and querystring - #print "S1-$1--$2--$3------\n"; - RunQuery($Param); - unless ($Param->{CID}->{$2}) { - #print "Error: No Database for Handle: $2 found\n"; - } - else { - $Param->{DB}=$Param->{CID}->{$2}; - - my $s = "$3"; - $s =~ s/from\s/from $Param->{DB}./i; - $Param->{Query}="EXPLAIN $s"; - - #$s =~ m/from\s+(\w+[.]\w+)/i; - #$Param->{tab} =$1; - #print "-- $Param->{tab} -- $s --\n"; - } - } - - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Query\s+(update.+)$/i) { # get connection ID($2) and querystring - #print "S2--$1--$2--$3------\n"; - RunQuery($Param); - unless ($Param->{CID}->{$2}) { - #print "Error: No Database for Handle: $2 found\n"; - } - else { - $Param->{DB}=$Param->{CID}->{$2}; - - my $ud = $3; - $ud =~ m/^update\s+(\w+).+(where.+)$/i; - $Param->{Query} ="EXPLAIN SELECT * FROM $1 $2"; - $Param->{Query} =~ s/from\s/from $Param->{DB}./i; - - #$Param->{Query} =~ m/from\s+(\w+[.]\w+)/i; - #$Param->{tab} =$1; - } - } - - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Statistics\s+(.*)$/i) { # get connection ID($2) and info? - $Param->{Statistics} ++; - #print "Statistics--$1--$2--$3------\n"; - RunQuery($Param); - } - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Query\s+(.+)$/i) { # get connection ID($2) - $Param->{QueryCount} ++; - #print "Query-NULL $3\n"; - RunQuery($Param); - } - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Refresh\s+(.+)$/i) { # get connection ID($2) - $Param->{Refresh} ++; - #print "Refresh\n"; - RunQuery($Param); - } - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Init\s+(.+)$/i) { # get connection ID($2) - $Param->{Init} ++; - #print "Init $3\n"; - RunQuery($Param); - } - elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Field\s+(.+)$/i) { # get connection ID($2) - $Param->{Field} ++; - #print "Field $3\n"; - RunQuery($Param); - } - - elsif (m/^\s+(.+)$/ ) { # command could be some lines ... - #print "multi-lined ($1)\n"; - my ($A)=$1; - chomp $A; - $Param->{Query} .= " $1"; - #print "multi-lined ($1)<<$Param->{Query}>>\n"; - } - - - } - - } - - $Param->{dbh}->disconnect(); - - if (1 == 0) { - print "\nunclosed handles----------------------------------------\n"; - my $count=0; - foreach (sort keys %{$Param->{CID}}) { - print "$count | $_ : $Param->{CID}->{$_} \n"; - $count ++; - } - } - - print "\nIndex usage ------------------------------------\n"; - foreach my $t (sort keys %{$Param->{Data}}) { - print "\nTable\t$t: ---\n"; - foreach my $k (sort keys %{$Param->{Data}->{$t}}) { - print " count\t$k:\n"; - my %h = %{$Param->{Data}->{$t}->{$k}}; - foreach (sort {$h{$a} <=> $h{$b}} keys %h) { - print " $Param->{Data}->{$t}->{$k}->{$_}\t$_\n"; - } - } - } - - $Param->{AllCount}=0; - print "\nQueries causing table scans -------------------\n\n"; - foreach (@{$Param->{ALL}}) { - $Param->{AllCount} ++; - print "$_\n"; - } - print "Sum: $Param->{AllCount} table scans\n"; - - print "\nSummary ---------------------------------------\n\n"; - print "Select: \t$Param->{SelectCount} queries\n"; - print "Update: \t$Param->{UpdateCount} queries\n"; - print "\n"; - - print "Init: \t$Param->{Init} times\n"; - print "Field: \t$Param->{Field} times\n"; - print "Refresh: \t$Param->{Refresh} times\n"; - print "Query: \t$Param->{QueryCount} times\n"; - print "Statistics:\t$Param->{Statistics} times\n"; - print "\n"; - - print "Logfile: \t$Param->{LineCount} lines\n"; - print "Started: \t".localtime($Param->{Start})."\n"; - print "Finished: \t".localtime(time)."\n"; - - } -} - - -########################################################################### -# -# -# -sub RunQuery { - my $Param = shift ; - - if (defined $Param->{Query}) { - if (defined $Param->{DB} ) { - - $Param->{Query} =~ m/from\s+(\w+[.]\w+|\w+)/i; - $Param->{tab} =$1; - #print "||$Param->{tab} -- $Param->{Query}\n"; - - my $sth=$Param->{dbh}->prepare("USE $Param->{DB}"); - if (DBI::err()) { - if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} - } - else { - $sth->execute(); - if (DBI::err()) { - if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} - } - else { - $sth->finish(); - - $sth=$Param->{dbh}->prepare($Param->{Query}); - if (DBI::err()) { - if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} - } - else { - #print "$Param->{Query}\n"; - $sth->execute(); - if (DBI::err()) { - if ($Param->{PrintError}) {print "[$Param->{LineCount}]<<$Param->{Query}>>\n";} - if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} - } - else { - my $row = undef; - while ($row = $sth->fetchrow_hashref()) { - $Param->{SelectCount} ++; - - if (defined $row->{Comment}) { - push (@{$Param->{Comment}}, "$row->{Comment}; $_; $Param->{DB}; $Param->{Query}"); - } - foreach (@{$Param->{Rows}}) { - if (defined $row->{$_}) { - #if (($_ eq 'type' ) and ($row->{$_} eq 'ALL')) { - if ($row->{type} eq 'ALL') { - push (@{$Param->{ALL}}, "$Param->{Query}"); - #print ">> $row->{$_} $_ $Param->{DB} $Param->{Query}\n"; - } - $Param->{IdxUseCount} ++; - $Param->{Data}->{$Param->{tab}}->{$_}->{$row->{$_}} ++; - } - } - } - } - } - } - } - $sth->finish(); - } - $Param->{Query} = undef ; - } -} - -########################################################################### -# -# -# -sub ShowOptions { - print <<EOF; -Usage: $0 [OPTIONS] < LOGFILE - ---date=YYMMDD select only entrys of date --d=YYMMDD ---host=HOSTNAME db-host to ask --h=HOSTNAME ---user=USERNAME db-user --u=USERNAME ---password=PASSWORD password of db-user --p=PASSWORD ---socket=SOCKET mysqld socket file to connect --s=SOCKET - -Read logfile from STDIN an try to EXPLAIN all SELECT statements. All UPDATE statements are rewritten to an EXPLAIN SELECT statement. The results of the EXPLAIN statement are collected and counted. All results with type=ALL are collected in an separete list. Results are printed to STDOUT. - -EOF -} - -1; - -__END__ - -=pod - -=head1 NAME - -explain_log.pl - -Feed a mysqld general logfile (created with mysqld --log) back into mysql -and collect statistics about index usage with EXPLAIN. - -=head1 DISCUSSION - -To optimize your indices, you have to know which ones are actually -used and what kind of queries are causing table scans. Especially -if you are generating your queries dynamically and you have a huge -amount of queries going on, this isn't easy. - -Use this tool to take a look at the effects of your real life queries. -Then add indices to avoid table scans and remove those which aren't used. - -=head1 USAGE - -explain_log.pl [--date=YYMMDD] --host=dbhost] [--user=dbuser] [--password=dbpw] [--socket=/path/to/socket] < logfile - ---date=YYMMDD select only entrys of date - --d=YYMMDD - ---host=HOSTNAME db-host to ask - --h=HOSTNAME - ---user=USERNAME db-user - --u=USERNAME - ---password=PASSWORD password of db-user - --p=PASSWORD - ---socket=SOCKET change path to the socket - --s=SOCKET - -=head1 EXAMPLE - -explain_log.pl --host=localhost --user=foo --password=bar < /var/lib/mysql/mobile.log - -=head1 AUTHORS - - Stefan Nitz - Jan Willamowius <jan@mobile.de>, http://www.mobile.de - Dennis Haney <davh@davh.dk> (Added socket support) - -=head1 RECRUITING - -If you are looking for a MySQL or Perl job, take a look at http://www.mobile.de -and send me an email with your resume (you must be speaking German!). - -=head1 SEE ALSO - -mysql documentation - -=cut diff --git a/server-tools/instance-manager/CMakeLists.txt b/server-tools/instance-manager/CMakeLists.txt index c20b9c7f9df..1983d459ce2 100644 --- a/server-tools/instance-manager/CMakeLists.txt +++ b/server-tools/instance-manager/CMakeLists.txt @@ -9,6 +9,7 @@ ADD_EXECUTABLE(mysqlmanager buffer.cc command.cc commands.cc guardian.cc instanc instance_options.cc listener.cc log.cc manager.cc messages.cc mysql_connection.cc mysqlmanager.cc options.cc parse.cc parse_output.cc priv.cc protocol.cc thread_registry.cc user_map.cc imservice.cpp windowsservice.cpp + user_management_commands.cc ../../sql/net_serv.cc ../../sql-common/pack.c ../../sql/password.c ../../sql/sql_state.c ../../sql-common/client.c ../../libmysql/get_password.c ../../libmysql/errmsg.c) diff --git a/server-tools/instance-manager/IMService.cpp b/server-tools/instance-manager/IMService.cpp index b7ea8e7eb81..f9ea7ee471d 100644 --- a/server-tools/instance-manager/IMService.cpp +++ b/server-tools/instance-manager/IMService.cpp @@ -20,7 +20,7 @@ IMService::~IMService(void) void IMService::Stop() { ReportStatus(SERVICE_STOP_PENDING); - + // stop the IM work raise(SIGTERM); } @@ -30,15 +30,14 @@ void IMService::Run(DWORD argc, LPTSTR *argv) // report to the SCM that we're about to start ReportStatus((DWORD)SERVICE_START_PENDING); - Options o; - o.load(argc, argv); - + Options::load(argc, argv); + // init goes here ReportStatus((DWORD)SERVICE_RUNNING); // wait for main loop to terminate - manager(o); - o.cleanup(); + manager(); + Options::cleanup(); } void IMService::Log(const char *msg) @@ -46,13 +45,13 @@ void IMService::Log(const char *msg) log_info(msg); } -int HandleServiceOptions(Options options) +int HandleServiceOptions() { int ret_val= 0; IMService winService; - if (options.install_as_service) + if (Options::Service::install_as_service) { if (winService.IsInstalled()) log_info("Service is already installed"); @@ -64,7 +63,7 @@ int HandleServiceOptions(Options options) ret_val= 1; } } - else if (options.remove_service) + else if (Options::Service::remove_service) { if (! winService.IsInstalled()) log_info("Service is not installed"); @@ -77,6 +76,19 @@ int HandleServiceOptions(Options options) } } else - ret_val= !winService.Init(); + { + log_info("Initializing Instance Manager service..."); + + if (!winService.Init()) + { + log_info("Service failed to initialize."); + fprintf(stderr, + "The service should be started by Windows Service Manager.\n" + "The MySQL Manager should be started with '--standalone'\n" + "to run from command line."); + ret_val= 1; + } + } + return ret_val; } diff --git a/server-tools/instance-manager/IMService.h b/server-tools/instance-manager/IMService.h index cad38bebdaf..94d59c2af31 100644 --- a/server-tools/instance-manager/IMService.h +++ b/server-tools/instance-manager/IMService.h @@ -1,3 +1,21 @@ +/* + Copyright (C) 2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + #pragma once #include "windowsservice.h" @@ -12,3 +30,5 @@ protected: void Stop(); void Run(DWORD argc, LPTSTR *argv); }; + +extern int HandleServiceOptions(); diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 6ab5c3d1bfc..4139bf2eb10 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -76,7 +76,10 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ guardian.cc guardian.h \ parse_output.cc parse_output.h \ mysql_manager_error.h \ - portability.h + portability.h \ + exit_codes.h \ + user_management_commands.h \ + user_management_commands.cc mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \ liboptions.la \ diff --git a/server-tools/instance-manager/WindowsService.cpp b/server-tools/instance-manager/WindowsService.cpp index 192045b7a4c..8a36a2f2fdd 100644 --- a/server-tools/instance-manager/WindowsService.cpp +++ b/server-tools/instance-manager/WindowsService.cpp @@ -7,9 +7,9 @@ static WindowsService *gService; WindowsService::WindowsService(void) : statusCheckpoint(0), serviceName(NULL), - inited(false), + inited(FALSE), dwAcceptedControls(SERVICE_ACCEPT_STOP), - debugging(false) + debugging(FALSE) { gService= this; status.dwServiceType= SERVICE_WIN32_OWN_PROCESS; @@ -22,11 +22,12 @@ WindowsService::~WindowsService(void) BOOL WindowsService::Install() { - bool ret_val= false; + bool ret_val= FALSE; SC_HANDLE newService; SC_HANDLE scm; - if (IsInstalled()) return true; + if (IsInstalled()) + return TRUE; // determine the name of the currently executing file char szFilePath[_MAX_PATH]; @@ -34,7 +35,7 @@ BOOL WindowsService::Install() // open a connection to the SCM if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE))) - return false; + return FALSE; newService= CreateService(scm, serviceName, displayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, @@ -45,7 +46,7 @@ BOOL WindowsService::Install() if (newService) { CloseServiceHandle(newService); - ret_val= true; + ret_val= TRUE; } CloseServiceHandle(scm); @@ -56,34 +57,35 @@ BOOL WindowsService::Init() { assert(serviceName != NULL); - if (inited) return true; + if (inited) + return TRUE; SERVICE_TABLE_ENTRY stb[] = { { (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, { NULL, NULL } }; - inited= true; + inited= TRUE; return StartServiceCtrlDispatcher(stb); //register with the Service Manager } BOOL WindowsService::Remove() { - bool ret_val= false; + bool ret_val= FALSE; - if (! IsInstalled()) - return true; + if (!IsInstalled()) + return TRUE; // open a connection to the SCM SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE); - if (! scm) - return false; + if (!scm) + return FALSE; SC_HANDLE service= OpenService(scm, serviceName, DELETE); if (service) { if (DeleteService(service)) - ret_val= true; + ret_val= TRUE; DWORD dw= ::GetLastError(); CloseServiceHandle(service); } @@ -116,7 +118,8 @@ void WindowsService::SetAcceptedControls(DWORD acceptedControls) BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint, DWORD dwError) { - if(debugging) return TRUE; + if (debugging) + return TRUE; if(currentState == SERVICE_START_PENDING) status.dwControlsAccepted= 0; diff --git a/server-tools/instance-manager/WindowsService.h b/server-tools/instance-manager/WindowsService.h index 1a034ce1351..3af7cdf39a7 100644 --- a/server-tools/instance-manager/WindowsService.h +++ b/server-tools/instance-manager/WindowsService.h @@ -1,3 +1,21 @@ +/* + Copyright (C) 2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + #pragma once class WindowsService diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h index b84cc6a8e9e..f31ea404867 100644 --- a/server-tools/instance-manager/command.h +++ b/server-tools/instance-manager/command.h @@ -22,10 +22,12 @@ #pragma interface #endif -/* Class responsible for allocation of im commands. */ +/* Class responsible for allocation of IM commands. */ class Instance_map; +struct st_net; + /* Command - entry point for any command. GangOf4: 'Command' design pattern @@ -37,8 +39,18 @@ public: Command(Instance_map *instance_map_arg= 0); virtual ~Command(); - /* method of executing: */ - virtual int execute(struct st_net *net, ulong connection_id) = 0; + /* + This operation incapsulates behaviour of the command. + + SYNOPSYS + net The network connection to the client. + connection_id Client connection ID + + RETURN + 0 On success + non 0 On error. Client error code is returned. + */ + virtual int execute(st_net *net, ulong connection_id) = 0; protected: Instance_map *instance_map; diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 7b999f61503..07e1e9a18f3 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -14,36 +14,53 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION) +#pragma implementation +#endif + #include "commands.h" +#include <my_global.h> +#include <m_ctype.h> +#include <mysql.h> +#include <my_dir.h> + +#include "buffer.h" +#include "guardian.h" #include "instance_map.h" +#include "log.h" +#include "manager.h" #include "messages.h" #include "mysqld_error.h" #include "mysql_manager_error.h" -#include "protocol.h" -#include "buffer.h" #include "options.h" +#include "priv.h" +#include "protocol.h" -#include <m_string.h> -#include <m_ctype.h> -#include <mysql.h> -#include <my_dir.h> + +/* + modify_defaults_to_im_error -- a map of error codes of + mysys::modify_defaults_file() into Instance Manager error codes. +*/ + +static const int modify_defaults_to_im_error[]= { 0, ER_OUT_OF_RESOURCES, + ER_ACCESS_OPTION_FILE }; /* - Add a string to a buffer + Add a string to a buffer. SYNOPSYS put_to_buff() buff buffer to add the string str string to add - uint offset in the buff to add a string + position offset in the buff to add a string DESCRIPTION Function to add a string to the buffer. It is different from - store_to_protocol_packet, which is used in the protocol.cc. The last - one also stores the length of the string in a special way. + store_to_protocol_packet, which is used in the protocol.cc. + The last one also stores the length of the string in a special way. This is required for MySQL client/server protocol support only. RETURN @@ -51,7 +68,6 @@ 1 - error occured */ - static inline int put_to_buff(Buffer *buff, const char *str, uint *position) { uint len= strlen(str); @@ -88,749 +104,1615 @@ static int parse_version_number(const char *version_str, char *version, } -/* implementation for Show_instances: */ +/************************************************************************** + Implementation of Instance_name. +**************************************************************************/ +Instance_name::Instance_name(const LEX_STRING *name) +{ + str.str= str_buffer; + str.length= name->length; -/* - The method sends a list of instances in the instance map to the client. + if (str.length > MAX_INSTANCE_NAME_SIZE - 1) + str.length= MAX_INSTANCE_NAME_SIZE - 1; - SYNOPSYS - Show_instances::execute() - net The network connection to the client. - connection_id Client connection ID + strmake(str.str, name->str, str.length); +} - RETURN - 0 - ok - 1 - error occured +/************************************************************************** + Implementation of Show_instances. +**************************************************************************/ + +/* + Implementation of SHOW INSTANCES statement. + + Possible error codes: + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Show_instances::execute(struct st_net *net, ulong connection_id) +int Show_instances::execute(st_net *net, ulong connection_id) { - Buffer send_buff; /* buffer for packets */ - LIST name, status; - NAME_WITH_LENGTH name_field, status_field; + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net))) + return err_code; + + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instances::write_header(st_net *net) +{ + LIST name, state; + LEX_STRING name_field, state_field; LIST *field_list; - uint position=0; - name_field.name= (char*) "instance_name"; + name_field.str= (char *) "instance_name"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - status_field.name= (char*) "status"; - status_field.length= DEFAULT_FIELD_LENGTH; - status.data= &status_field; - field_list= list_add(NULL, &status); + + state_field.str= (char *) "state"; + state_field.length= DEFAULT_FIELD_LENGTH; + state.data= &state_field; + + field_list= list_add(NULL, &state); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} + +int Show_instances::write_data(st_net *net) +{ + my_bool err_status= FALSE; + + Instance *instance; + Instance_map::Iterator iterator(instance_map); + + instance_map->guardian->lock(); + instance_map->lock(); + + while ((instance= iterator.next())) { - Instance *instance; - Instance_map::Iterator iterator(instance_map); + Buffer send_buf; /* buffer for packets */ + uint pos= 0; + + const char *instance_name= instance->options.instance_name.str; + const char *state_name= instance_map->get_instance_state_name(instance); - instance_map->lock(); - while ((instance= iterator.next())) + if (store_to_protocol_packet(&send_buf, instance_name, &pos) || + store_to_protocol_packet(&send_buf, state_name, &pos) || + my_net_write(net, send_buf.buffer, pos)) { - position= 0; - store_to_protocol_packet(&send_buff, instance->options.instance_name, - &position); - if (instance->is_running()) - store_to_protocol_packet(&send_buff, (char*) "online", &position); - else - store_to_protocol_packet(&send_buff, (char*) "offline", &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + err_status= TRUE; + break; } - instance_map->unlock(); } - if (send_eof(net)) - goto err; - if (net_flush(net)) - goto err; - return 0; -err: - return ER_OUT_OF_RESOURCES; + instance_map->unlock(); + instance_map->guardian->unlock(); + + return err_status ? ER_OUT_OF_RESOURCES : 0; } -/* implementation for Flush_instances: */ +/************************************************************************** + Implementation of Flush_instances. +**************************************************************************/ -int Flush_instances::execute(struct st_net *net, ulong connection_id) +/* + Implementation of FLUSH INSTANCES statement. + + Possible error codes: + ER_OUT_OF_RESOURCES Not enough resources to complete the operation + ER_THERE_IS_ACTIVE_INSTACE If there is an active instance +*/ + +int Flush_instances::execute(st_net *net, ulong connection_id) { - if (instance_map->flush_instances() || - net_send_ok(net, connection_id, NULL)) + instance_map->guardian->lock(); + instance_map->lock(); + + if (instance_map->is_there_active_instance()) + { + instance_map->unlock(); + instance_map->guardian->unlock(); + return ER_THERE_IS_ACTIVE_INSTACE; + } + + if (instance_map->flush_instances()) + { + instance_map->unlock(); + instance_map->guardian->unlock(); return ER_OUT_OF_RESOURCES; + } - return 0; + instance_map->unlock(); + instance_map->guardian->unlock(); + + return net_send_ok(net, connection_id, NULL) ? ER_OUT_OF_RESOURCES : 0; } -/* implementation for Show_instance_status: */ +/************************************************************************** + Implementation of Abstract_instance_cmd. +**************************************************************************/ -Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, - const char *name, uint len) - :Command(instance_map_arg) +Abstract_instance_cmd::Abstract_instance_cmd( + Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg) + :Command(instance_map_arg), + instance_name(instance_name_arg) { - Instance *instance; + /* + MT-NOTE: we can not make a search for Instance object here, + because it can dissappear after releasing the lock. + */ +} + + +int Abstract_instance_cmd::execute(st_net *net, ulong connection_id) +{ + int err_code; + + instance_map->lock(); + + { + Instance *instance= instance_map->find(get_instance_name()); + + if (!instance) + { + instance_map->unlock(); + return ER_BAD_INSTANCE_NAME; + } + + err_code= execute_impl(net, instance); + } + + instance_map->unlock(); + + if (!err_code) + err_code= send_ok_response(net, connection_id); - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; + return err_code; } -/* - The method sends a table with a status of requested instance to the client. +/************************************************************************** + Implementation of Show_instance_status. +**************************************************************************/ - SYNOPSYS - Show_instance_status::do_command() - net The network connection to the client. - instance_name The name of the instance. +Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ +} - RETURN - 0 - ok - 1 - error occured + +/* + Implementation of SHOW INSTANCE STATUS statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ +int Show_instance_status::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; -int Show_instance_status::execute(struct st_net *net, - ulong connection_id) + return 0; +} + + +int Show_instance_status::send_ok_response(st_net *net, ulong connection_id) { - enum { MAX_VERSION_LENGTH= 40 }; - Buffer send_buff; /* buffer for packets */ - LIST name, status, version, version_number; + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instance_status::write_header(st_net *net) +{ + LIST name, state, version, version_number, mysqld_compatible; LIST *field_list; - NAME_WITH_LENGTH name_field, status_field, version_field, - version_number_field; - uint position=0; + LEX_STRING name_field, state_field, version_field, + version_number_field, mysqld_compatible_field; - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + /* Create list of the fileds to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "instance_name"; + name_field.str= (char *) "instance_name"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - status_field.name= (char*) "status"; - status_field.length= DEFAULT_FIELD_LENGTH; - status.data= &status_field; - version_field.name= (char*) "version"; + + state_field.str= (char *) "state"; + state_field.length= DEFAULT_FIELD_LENGTH; + state.data= &state_field; + + version_field.str= (char *) "version"; version_field.length= MAX_VERSION_LENGTH; version.data= &version_field; - version_number_field.name= (char*) "version_number"; + + version_number_field.str= (char *) "version_number"; version_number_field.length= MAX_VERSION_LENGTH; version_number.data= &version_number_field; - field_list= list_add(NULL, &version); + + mysqld_compatible_field.str= (char *) "mysqld_compatible"; + mysqld_compatible_field.length= DEFAULT_FIELD_LENGTH; + mysqld_compatible.data= &mysqld_compatible_field; + + field_list= list_add(NULL, &mysqld_compatible); + field_list= list_add(field_list, &version); field_list= list_add(field_list, &version_number); - field_list= list_add(field_list, &status); + field_list= list_add(field_list, &state); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} - { - Instance *instance; - store_to_protocol_packet(&send_buff, (char*) instance_name, &position); - if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) - goto err; - if (instance->is_running()) - store_to_protocol_packet(&send_buff, (char*) "online", &position); - else - store_to_protocol_packet(&send_buff, (char*) "offline", &position); +int Show_instance_status::write_data(st_net *net, Instance *instance) +{ + Buffer send_buf; /* buffer for packets */ + char version_num_buf[MAX_VERSION_LENGTH]; + uint pos= 0; - if (instance->options.mysqld_version) - { - char parsed_version[MAX_VERSION_LENGTH]; + const char *state_name; + const char *version_tag= "unknown"; + const char *version_num= "unknown"; + const char *mysqld_compatible_status; - parse_version_number(instance->options.mysqld_version, parsed_version, - sizeof(parsed_version)); - store_to_protocol_packet(&send_buff, parsed_version, &position); + instance_map->guardian->lock(); + state_name= instance_map->get_instance_state_name(instance); + mysqld_compatible_status= instance->is_mysqld_compatible() ? "yes" : "no"; + instance_map->guardian->unlock(); - store_to_protocol_packet(&send_buff, instance->options.mysqld_version, - &position); - } - else - { - store_to_protocol_packet(&send_buff, (char*) "unknown", &position); - store_to_protocol_packet(&send_buff, (char*) "unknown", &position); - } + if (instance->options.mysqld_version) + { + if (parse_version_number(instance->options.mysqld_version, version_num_buf, + sizeof(version_num_buf))) + return ER_OUT_OF_RESOURCES; - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + version_num= version_num_buf; + version_tag= instance->options.mysqld_version; } - if (send_eof(net) || net_flush(net)) - goto err; + if (store_to_protocol_packet(&send_buf, get_instance_name()->str, &pos) || + store_to_protocol_packet(&send_buf, state_name, &pos) || + store_to_protocol_packet(&send_buf, version_num, &pos) || + store_to_protocol_packet(&send_buf, version_tag, &pos) || + store_to_protocol_packet(&send_buf, mysqld_compatible_status, &pos) || + my_net_write(net, send_buf.buffer, (uint) pos)) + { + return ER_OUT_OF_RESOURCES; + } return 0; +} -err: - return ER_OUT_OF_RESOURCES; + +/************************************************************************** + Implementation of Show_instance_options. +**************************************************************************/ + +Show_instance_options::Show_instance_options( + Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ } -/* Implementation for Show_instance_options */ +/* + Implementation of SHOW INSTANCE OPTIONS statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ -Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, - const char *name, uint len): - Command(instance_map_arg) +int Show_instance_options::execute_impl(st_net *net, Instance *instance) { - Instance *instance; + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; + return 0; } -int Show_instance_options::execute(struct st_net *net, ulong connection_id) +int Show_instance_options::send_ok_response(st_net *net, ulong connection_id) +{ + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instance_options::write_header(st_net *net) { - Buffer send_buff; /* buffer for packets */ LIST name, option; LIST *field_list; - NAME_WITH_LENGTH name_field, option_field; - uint position=0; + LEX_STRING name_field, option_field; - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + /* Create list of the fileds to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "option_name"; + name_field.str= (char *) "option_name"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - option_field.name= (char*) "value"; + + option_field.str= (char *) "value"; option_field.length= DEFAULT_FIELD_LENGTH; option.data= &option_field; + field_list= list_add(NULL, &option); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} + +int Show_instance_options::write_data(st_net *net, Instance *instance) +{ + Buffer send_buff; /* buffer for packets */ + uint pos= 0; + + if (store_to_protocol_packet(&send_buff, "instance_name", &pos) || + store_to_protocol_packet(&send_buff, get_instance_name()->str, &pos) || + my_net_write(net, send_buff.buffer, pos)) { - Instance *instance; - - if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) - goto err; - store_to_protocol_packet(&send_buff, (char*) "instance_name", &position); - store_to_protocol_packet(&send_buff, (char*) instance_name, &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - if ((instance->options.mysqld_path)) - { - position= 0; - store_to_protocol_packet(&send_buff, (char*) "mysqld-path", &position); - store_to_protocol_packet(&send_buff, - (char*) instance->options.mysqld_path, - &position); - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - } + return ER_OUT_OF_RESOURCES; + } - if ((instance->options.nonguarded)) + /* Loop through the options. */ + + for (int i= 0; i < instance->options.get_num_options(); i++) + { + Named_value option= instance->options.get_option(i); + const char *option_value= option.get_value()[0] ? option.get_value() : ""; + + pos= 0; + + if (store_to_protocol_packet(&send_buff, option.get_name(), &pos) || + store_to_protocol_packet(&send_buff, option_value, &pos) || + my_net_write(net, send_buff.buffer, pos)) { - position= 0; - store_to_protocol_packet(&send_buff, (char*) "nonguarded", &position); - store_to_protocol_packet(&send_buff, "", &position); - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + return ER_OUT_OF_RESOURCES; } + } + + return 0; +} + + +/************************************************************************** + Implementation of Start_instance. +**************************************************************************/ - /* loop through the options stored in DYNAMIC_ARRAY */ - for (uint i= 0; i < instance->options.options_array.elements; i++) +Start_instance::Start_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ +} + + +/* + Implementation of START INSTANCE statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Start_instance::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= instance->start())) + return err_code; + + if (!(instance->options.nonguarded)) + instance_map->guardian->guard(instance); + + return 0; +} + + +int Start_instance::send_ok_response(st_net *net, ulong connection_id) +{ + if (net_send_ok(net, connection_id, "Instance started")) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation of Stop_instance. +**************************************************************************/ + +Stop_instance::Stop_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ +} + + +/* + Implementation of STOP INSTANCE statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Stop_instance::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if (!(instance->options.nonguarded)) + instance_map->guardian->stop_guard(instance); + + if ((err_code= instance->stop())) + return err_code; + + return 0; +} + + +int Stop_instance::send_ok_response(st_net *net, ulong connection_id) +{ + if (net_send_ok(net, connection_id, NULL)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation for Create_instance. +**************************************************************************/ + +Create_instance::Create_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Command(instance_map_arg), + instance_name(instance_name_arg) +{ +} + + +/* + This operation initializes Create_instance object. + + SYNOPSYS + text [IN/OUT] a pointer to the text containing instance options. + + RETURN + FALSE On success. + TRUE On error. +*/ + +bool Create_instance::init(const char **text) +{ + return options.init() || parse_args(text); +} + + +/* + This operation parses CREATE INSTANCE options. + + SYNOPSYS + text [IN/OUT] a pointer to the text containing instance options. + + RETURN + FALSE On success. + TRUE On syntax error. +*/ + +bool Create_instance::parse_args(const char **text) +{ + uint len; + + /* Check if we have something (and trim leading spaces). */ + + get_word(text, &len, NONSPACE); + + if (len == 0) + return FALSE; /* OK: no option. */ + + /* Main parsing loop. */ + + while (TRUE) + { + LEX_STRING option_name; + char *option_name_str; + char *option_value_str= NULL; + + /* Looking for option name. */ + + get_word(text, &option_name.length, OPTION_NAME); + + if (option_name.length == 0) + return TRUE; /* Syntax error: option name expected. */ + + option_name.str= (char *) *text; + *text+= option_name.length; + + /* Looking for equal sign. */ + + skip_spaces(text); + + if (**text == '=') { - char *tmp_option, *option_value; - get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); - option_value= strchr(tmp_option, '='); - /* split the option string into two parts if it has a value */ + ++(*text); /* Skip an equal sign. */ + + /* Looking for option value. */ - position= 0; - if (option_value != NULL) + skip_spaces(text); + + if (!**text) + return TRUE; /* Syntax error: EOS when option value expected. */ + + if (**text != '\'' && **text != '"') { - *option_value= 0; - store_to_protocol_packet(&send_buff, tmp_option + 2, &position); - store_to_protocol_packet(&send_buff, option_value + 1, &position); - /* join name and the value into the same option again */ - *option_value= '='; + /* Option value is a simple token. */ + + LEX_STRING option_value; + + get_word(text, &option_value.length, ALPHANUM); + + if (option_value.length == 0) + return TRUE; /* internal parser error. */ + + option_value.str= (char *) *text; + *text+= option_value.length; + + if (!(option_value_str= Named_value::alloc_str(&option_value))) + return TRUE; /* out of memory during parsing. */ } else { - store_to_protocol_packet(&send_buff, tmp_option + 2, &position); - store_to_protocol_packet(&send_buff, "", &position); + /* Option value is a string. */ + + if (parse_option_value(*text, &len, &option_value_str)) + return TRUE; /* Syntax error: invalid string specification. */ + + *text+= len; } + } + + if (!option_value_str) + { + LEX_STRING empty_str= { C_STRING_WITH_SIZE("") }; + + if (!(option_value_str= Named_value::alloc_str(&empty_str))) + return TRUE; /* out of memory during parsing. */ + } + + if (!(option_name_str= Named_value::alloc_str(&option_name))) + { + Named_value::free_str(&option_value_str); + return TRUE; /* out of memory during parsing. */ + } + + { + Named_value option(option_name_str, option_value_str); - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + if (options.add_element(&option)) + { + option.free(); + return TRUE; /* out of memory during parsing. */ + } } + + skip_spaces(text); + + if (!**text) + return FALSE; /* OK: end of options. */ + + if (**text != ',') + return TRUE; /* Syntax error: comma expected. */ + + ++(*text); } +} - if (send_eof(net) || net_flush(net)) - goto err; - return 0; +/* + Implementation of CREATE INSTANCE statement. + + Possible error codes: + ER_MALFORMED_INSTANCE_NAME Instance name is malformed + ER_CREATE_EXISTING_INSTANCE There is an instance with the given name + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Create_instance::execute(st_net *net, ulong connection_id) +{ + int err_code; + + /* Check that the name is valid and there is no instance with such name. */ + + if (!Instance::is_name_valid(get_instance_name())) + return ER_MALFORMED_INSTANCE_NAME; + + /* + NOTE: In order to prevent race condition, we should perform all operations + on under acquired lock. + */ + + instance_map->lock(); -err: - return ER_OUT_OF_RESOURCES; + if (instance_map->find(get_instance_name())) + { + instance_map->unlock(); + return ER_CREATE_EXISTING_INSTANCE; + } + + if ((err_code= instance_map->create_instance(get_instance_name(), &options))) + { + instance_map->unlock(); + return err_code; + } + + if ((err_code= create_instance_in_file(get_instance_name(), &options))) + { + Instance *instance= instance_map->find(get_instance_name()); + + if (instance) + instance_map->remove_instance(instance); /* instance is deleted here. */ + + instance_map->unlock(); + return err_code; + } + + /* That's all. */ + + instance_map->unlock(); + + /* Send the result. */ + + if (net_send_ok(net, connection_id, NULL)) + return ER_OUT_OF_RESOURCES; + + return 0; } -/* Implementation for Start_instance */ +/************************************************************************** + Implementation for Drop_instance. +**************************************************************************/ -Start_instance::Start_instance(Instance_map *instance_map_arg, - const char *name, uint len) - :Command(instance_map_arg) +Drop_instance::Drop_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) { - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; } -int Start_instance::execute(struct st_net *net, ulong connection_id) +/* + Implementation of DROP INSTANCE statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_DROP_ACTIVE_INSTANCE The specified instance is active + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Drop_instance::execute_impl(st_net *net, Instance *instance) { - uint err_code; - if (instance == 0) - return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ - else - { - if ((err_code= instance->start())) - return err_code; + int err_code; + + /* Check that the instance is offline. */ + + if (instance_map->guardian->is_active(instance)) + return ER_DROP_ACTIVE_INSTANCE; - if (!(instance->options.nonguarded)) - instance_map->guardian->guard(instance); + err_code= modify_defaults_file(Options::Main::config_file, NULL, NULL, + get_instance_name()->str, MY_REMOVE_SECTION); + DBUG_ASSERT(err_code >= 0 && err_code <= 2); - net_send_ok(net, connection_id, "Instance started"); - return 0; + if (err_code) + { + log_error("Can not remove instance '%s' from defaults file (%s). " + "Original error code: %d.", + (const char *) get_instance_name()->str, + (const char *) Options::Main::config_file, + (int) err_code); } + + if (err_code) + return modify_defaults_to_im_error[err_code]; + + /* Remove instance from the instance map hash and Guardian's list. */ + + if (!instance->options.nonguarded) + instance_map->guardian->stop_guard(instance); + + if ((err_code= instance->stop())) + return err_code; + + instance_map->remove_instance(instance); + + return 0; } -/* implementation for Show_instance_log: */ +int Drop_instance::send_ok_response(st_net *net, ulong connection_id) +{ + if (net_send_ok(net, connection_id, "Instance dropped")) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation for Show_instance_log. +**************************************************************************/ Show_instance_log::Show_instance_log(Instance_map *instance_map_arg, - const char *name, uint len, + const LEX_STRING *instance_name_arg, Log_type log_type_arg, - const char *size_arg, - const char *offset_arg) - :Command(instance_map_arg) + uint size_arg, uint offset_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg), + log_type(log_type_arg), + size(size_arg), + offset(offset_arg) { - Instance *instance; +} + + +/* + Implementation of SHOW INSTANCE LOG statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OFFSET_ERROR We were requested to read negative number of + bytes from the log + ER_NO_SUCH_LOG The specified type of log is not available for + the given instance + ER_GUESS_LOGFILE IM wasn't able to figure out the log + placement, while it is enabled. Probably user + should specify the path to the logfile + explicitly. + ER_OPEN_LOGFILE Cannot open the logfile + ER_READ_FILE Cannot read the logfile + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Show_instance_log::execute_impl(st_net *net, Instance *instance) +{ + int err_code; - if (offset_arg != NULL) - offset= atoi(offset_arg); - else - offset= 0; - size= atoi(size_arg); - log_type= log_type_arg; + if ((err_code= check_params(instance))) + return err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; + return 0; } +int Show_instance_log::send_ok_response(st_net *net, ulong connection_id) +{ + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; -/* - Open the logfile, read requested part of the log and send the info - to the client. + return 0; +} - SYNOPSYS - Show_instance_log::execute() - net The network connection to the client. - connection_id Client connection ID - DESCRIPTION +int Show_instance_log::check_params(Instance *instance) +{ + const char *logpath= instance->options.logs[log_type]; - Send a table with the content of the log requested. The function also - deals with errro handling, to be verbose. + /* Cannot read negative number of bytes. */ - RETURN - ER_OFFSET_ERROR We were requested to read negative number of bytes - from the log - ER_NO_SUCH_LOG The kind log being read is not enabled in the instance - ER_GUESS_LOGFILE IM wasn't able to figure out the log placement, while - it is enabled. Probably user should specify the path - to the logfile explicitly. - ER_OPEN_LOGFILE Cannot open the logfile - ER_READ_FILE Cannot read the logfile - ER_OUT_OF_RESOURCES We weren't able to allocate some resources -*/ + if (offset > size) + return ER_OFFSET_ERROR; + + /* Instance has no such log. */ + + if (logpath == NULL) + return ER_NO_SUCH_LOG; -int Show_instance_log::execute(struct st_net *net, ulong connection_id) + if (*logpath == '\0') + return ER_GUESS_LOGFILE; + + return 0; +} + + +int Show_instance_log::write_header(st_net *net) { - Buffer send_buff; /* buffer for packets */ LIST name; LIST *field_list; - NAME_WITH_LENGTH name_field; - uint position= 0; + LEX_STRING name_field; - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "Log"; + /* Create list of the fields to be passed to send_fields(). */ + + name_field.str= (char *) "Log"; name_field.length= DEFAULT_FIELD_LENGTH; - name.data= &name_field; - field_list= list_add(NULL, &name); - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + name.data= &name_field; - /* cannot read negative number of bytes */ - if (offset > size) - return ER_OFFSET_ERROR; + field_list= list_add(NULL, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} - { - Instance *instance; - const char *logpath; - File fd; - if ((instance= instance_map->find(instance_name, - strlen(instance_name))) == NULL) - goto err; +int Show_instance_log::write_data(st_net *net, Instance *instance) +{ + Buffer send_buff; /* buffer for packets */ + uint pos= 0; - logpath= instance->options.logs[log_type]; + const char *logpath= instance->options.logs[log_type]; + File fd; - /* Instance has no such log */ - if (logpath == NULL) - return ER_NO_SUCH_LOG; + size_t buff_size; + int read_len; - if (*logpath == '\0') - return ER_GUESS_LOGFILE; + MY_STAT file_stat; + Buffer read_buff; + if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) <= 0) + return ER_OPEN_LOGFILE; - if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) >= 0) - { - size_t buff_size; - int read_len; - /* calculate buffer size */ - MY_STAT file_stat; - Buffer read_buff; + /* my_fstat doesn't use the flag parameter */ + if (my_fstat(fd, &file_stat, MYF(0))) + { + close(fd); + return ER_OUT_OF_RESOURCES; + } - /* my_fstat doesn't use the flag parameter */ - if (my_fstat(fd, &file_stat, MYF(0))) - goto err; + /* calculate buffer size */ + buff_size= (size - offset); - buff_size= (size - offset); + read_buff.reserve(0, buff_size); - read_buff.reserve(0, buff_size); + /* read in one chunk */ + read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0)); - /* read in one chunk */ - read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0)); + if ((read_len= my_read(fd, (byte*) read_buff.buffer, + buff_size, MYF(0))) < 0) + { + close(fd); + return ER_READ_FILE; + } - if ((read_len= my_read(fd, (byte*) read_buff.buffer, - buff_size, MYF(0))) < 0) - return ER_READ_FILE; - store_to_protocol_packet(&send_buff, read_buff.buffer, - &position, read_len); - close(fd); - } - else - return ER_OPEN_LOGFILE; + close(fd); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + if (store_to_protocol_packet(&send_buff, read_buff.buffer, &pos, read_len) || + my_net_write(net, send_buff.buffer, pos)) + { + return ER_OUT_OF_RESOURCES; } - if (send_eof(net) || net_flush(net)) - goto err; - return 0; - -err: - return ER_OUT_OF_RESOURCES; } -/* implementation for Show_instance_log_files: */ +/************************************************************************** + Implementation of Show_instance_log_files. +**************************************************************************/ Show_instance_log_files::Show_instance_log_files - (Instance_map *instance_map_arg, const char *name, uint len) - :Command(instance_map_arg) + (Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) { - Instance *instance; - - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; } /* - The method sends a table with a list of log files - used by the instance. + Implementation of SHOW INSTANCE LOG FILES statement. - SYNOPSYS - Show_instance_log_files::execute() - net The network connection to the client. - connection_id The ID of the client connection - - RETURN - ER_BAD_INSTANCE_NAME The instance name specified is not valid - ER_OUT_OF_RESOURCES some error occured - 0 - ok + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Show_instance_log_files::execute(struct st_net *net, ulong connection_id) +int Show_instance_log_files::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; + + return 0; +} + + +int Show_instance_log_files::send_ok_response(st_net *net, ulong connection_id) +{ + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instance_log_files::write_header(st_net *net) { - Buffer send_buff; /* buffer for packets */ LIST name, path, size; LIST *field_list; - NAME_WITH_LENGTH name_field, path_field, size_field; - uint position= 0; + LEX_STRING name_field, path_field, size_field; - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + /* Create list of the fileds to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "Logfile"; + name_field.str= (char *) "Logfile"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - path_field.name= (char*) "Path"; + + path_field.str= (char *) "Path"; path_field.length= DEFAULT_FIELD_LENGTH; path.data= &path_field; - size_field.name= (char*) "File size"; + + size_field.str= (char *) "File size"; size_field.length= DEFAULT_FIELD_LENGTH; size.data= &size_field; + field_list= list_add(NULL, &size); field_list= list_add(field_list, &path); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} - Instance *instance; - if ((instance= instance_map-> - find(instance_name, strlen(instance_name))) == NULL) - goto err; +int Show_instance_log_files::write_data(st_net *net, Instance *instance) +{ + Buffer send_buff; /* buffer for packets */ + /* + We have alike structure in instance_options.cc. We use such to be able + to loop through the options, which we need to handle in some common way. + */ + struct log_files_st + { + const char *name; + const char *value; + } logs[]= + { + {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]}, + {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]}, + {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]}, + {NULL, NULL} + }; + struct log_files_st *log_files; + + for (log_files= logs; log_files->name; log_files++) { + if (!log_files->value) + continue; + + struct stat file_stat; /* - We have alike structure in instance_options.cc. We use such to be able - to loop through the options, which we need to handle in some common way. + Save some more space for the log file names. In fact all + we need is strlen("GENERAL_LOG") + 1 */ - struct log_files_st - { - const char *name; - const char *value; - } logs[]= - { - {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]}, - {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]}, - {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]}, - {NULL, NULL} - }; - struct log_files_st *log_files; - - for (log_files= logs; log_files->name; log_files++) + enum { LOG_NAME_BUFFER_SIZE= 20 }; + char buff[LOG_NAME_BUFFER_SIZE]; + + uint pos= 0; + + const char *log_path= ""; + const char *log_size= "0"; + + if (!stat(log_files->value, &file_stat) && + MY_S_ISREG(file_stat.st_mode)) { - if (log_files->value != NULL) - { - struct stat file_stat; - /* - Save some more space for the log file names. In fact all - we need is srtlen("GENERAL_LOG") + 1 - */ - enum { LOG_NAME_BUFFER_SIZE= 20 }; - char buff[LOG_NAME_BUFFER_SIZE]; - - position= 0; - /* store the type of the log in the send buffer */ - store_to_protocol_packet(&send_buff, log_files->name, &position); - if (stat(log_files->value, &file_stat)) - { - store_to_protocol_packet(&send_buff, "", &position); - store_to_protocol_packet(&send_buff, (char*) "0", &position); - } - else if (MY_S_ISREG(file_stat.st_mode)) - { - store_to_protocol_packet(&send_buff, - (char*) log_files->value, - &position); - int10_to_str(file_stat.st_size, buff, 10); - store_to_protocol_packet(&send_buff, (char*) buff, &position); - } - - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - } + int10_to_str(file_stat.st_size, buff, 10); + + log_path= log_files->value; + log_size= buff; } - } - if (send_eof(net) || net_flush(net)) - goto err; + if (store_to_protocol_packet(&send_buff, log_files->name, &pos) || + store_to_protocol_packet(&send_buff, log_path, &pos) || + store_to_protocol_packet(&send_buff, log_size, &pos) || + my_net_write(net, send_buff.buffer, pos)) + return ER_OUT_OF_RESOURCES; + } return 0; - -err: - return ER_OUT_OF_RESOURCES; } -/* implementation for SET instance_name.option=option_value: */ +/************************************************************************** + Implementation of Abstract_option_cmd. +**************************************************************************/ + +/* + Instance_options_list -- a data class representing a list of options for + some instance. +*/ -Set_option::Set_option(Instance_map *instance_map_arg, - const char *name, uint len, - const char *option_arg, uint option_len_arg, - const char *option_value_arg, uint option_value_len_arg) - :Command(instance_map_arg) +class Instance_options_list { - Instance *instance; +public: + Instance_options_list(const LEX_STRING *instance_name_arg); - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - { - instance_name= instance->options.instance_name; +public: + bool init(); - /* add prefix for add_option */ - if ((option_len_arg < MAX_OPTION_LEN - 1) || - (option_value_len_arg < MAX_OPTION_LEN - 1)) - { - strmake(option, option_arg, option_len_arg); - strmake(option_value, option_value_arg, option_value_len_arg); - } - else - { - option[0]= 0; - option_value[0]= 0; - } - instance_name_len= len; - } - else + const LEX_STRING *get_instance_name() const { - instance_name= NULL; - instance_name_len= 0; + return instance_name.get_str(); } + +public: + /* + This member is set and used only in Abstract_option_cmd::execute_impl(). + Normally it is not used (and should not). + + The problem is that construction and execution of commands are made not + in one transaction (not under one lock session). So, we can not initialize + instance in constructor and use it in execution. + */ + Instance *instance; + + Named_value_arr options; + +private: + Instance_name instance_name; +}; + + +/**************************************************************************/ + +Instance_options_list::Instance_options_list( + const LEX_STRING *instance_name_arg) + :instance(NULL), + instance_name(instance_name_arg) +{ } -/* - The method sends a table with a list of log files - used by the instance. +bool Instance_options_list::init() +{ + return options.init(); +} - SYNOPSYS - Set_option::correct_file() - skip Skip the option, being searched while writing the result file. - That is, to delete it. - DESCRIPTION +/**************************************************************************/ + +C_MODE_START + +static byte* get_item_key(const byte* item, uint* len, + my_bool __attribute__((unused)) t) +{ + const Instance_options_list *lst= (const Instance_options_list *) item; + *len= lst->get_instance_name()->length; + return (byte *) lst->get_instance_name()->str; +} + +static void delete_item(void *item) +{ + delete (Instance_options_list *) item; +} + +C_MODE_END + + +/**************************************************************************/ + +Abstract_option_cmd::Abstract_option_cmd(Instance_map *instance_map_arg) + :Command(instance_map_arg), + initialized(FALSE) +{ +} + + +Abstract_option_cmd::~Abstract_option_cmd() +{ + if (initialized) + hash_free(&instance_options_map); +} + + +bool Abstract_option_cmd::add_option(const LEX_STRING *instance_name, + Named_value *option) +{ + Instance_options_list *lst= get_instance_options_list(instance_name); + + if (!lst) + return TRUE; + lst->options.add_element(option); + + return FALSE; +} + + +bool Abstract_option_cmd::init(const char **text) +{ + static const int INITIAL_HASH_SIZE= 16; + + if (hash_init(&instance_options_map, default_charset_info, + INITIAL_HASH_SIZE, 0, 0, get_item_key, delete_item, 0)) + return TRUE; + + if (parse_args(text)) + return TRUE; + + initialized= TRUE; + + return FALSE; +} + + +/* Correct the option file. The "skip" option is used to remove the found option. + SYNOPSYS + Abstract_option_cmd::correct_file() + skip Skip the option, being searched while writing the result file. + That is, to delete it. + RETURN - ER_OUT_OF_RESOURCES out of resources + 0 Success + ER_OUT_OF_RESOURCES Not enough resources to complete the operation ER_ACCESS_OPTION_FILE Cannot access the option file - 0 - ok */ -int Set_option::correct_file(int skip) +int Abstract_option_cmd::correct_file(Instance *instance, Named_value *option, + bool skip) { - static const int mysys_to_im_error[]= { 0, ER_OUT_OF_RESOURCES, - ER_ACCESS_OPTION_FILE }; - int error; + int err_code= modify_defaults_file(Options::Main::config_file, + option->get_name(), + option->get_value(), + instance->get_name()->str, + skip); + + DBUG_ASSERT(err_code >= 0 && err_code <= 2); - error= modify_defaults_file(Options::config_file, option, - option_value, instance_name, skip); - DBUG_ASSERT(error >= 0 && error <= 2); + if (err_code) + { + log_error("Can not modify option (%s) in defaults file (%s). " + "Original error code: %d.", + (const char *) option->get_name(), + (const char *) Options::Main::config_file, + (int) err_code); + } - return mysys_to_im_error[error]; + return modify_defaults_to_im_error[err_code]; } /* - The method sets an option in the the default config file (/etc/my.cnf). - - SYNOPSYS - Set_option::do_command() - net The network connection to the client. - - RETURN - 0 - ok - 1 - error occured + Implementation of SET statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_INCOMPATIBLE_OPTION The specified option can not be set for + mysqld-compatible instance + ER_INSTANCE_IS_ACTIVE The specified instance is active + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Set_option::do_command(struct st_net *net) +int Abstract_option_cmd::execute(st_net *net, ulong connection_id) { - int error; + int err_code; - /* we must hold the instance_map mutex while changing config file */ instance_map->lock(); - error= correct_file(FALSE); + + err_code= execute_impl(net, connection_id); + instance_map->unlock(); - return error; + return err_code; +} + + +Instance_options_list * +Abstract_option_cmd::get_instance_options_list(const LEX_STRING *instance_name) +{ + Instance_options_list *lst= + (Instance_options_list *) hash_search(&instance_options_map, + (byte *) instance_name->str, + instance_name->length); + + if (!lst) + { + lst= new Instance_options_list(instance_name); + + if (!lst) + return NULL; + + if (lst->init() || my_hash_insert(&instance_options_map, (byte *) lst)) + { + delete lst; + return NULL; + } + } + + return lst; } -int Set_option::execute(struct st_net *net, ulong connection_id) +int Abstract_option_cmd::execute_impl(st_net *net, ulong connection_id) { - if (instance_name != NULL) + int err_code; + + /* Check that all the specified instances exist and are offline. */ + + for (uint i= 0; i < instance_options_map.records; ++i) { - int val; + Instance_options_list *lst= + (Instance_options_list *) hash_element(&instance_options_map, i); - val= do_command(net); + lst->instance= instance_map->find(lst->get_instance_name()); - if (val == 0) - net_send_ok(net, connection_id, NULL); + if (!lst->instance) + return ER_BAD_INSTANCE_NAME; - return val; + if (instance_map->guardian->is_active(lst->instance)) + return ER_INSTANCE_IS_ACTIVE; } - return ER_BAD_INSTANCE_NAME; + /* Perform command-specific (SET/UNSET) actions. */ + + for (uint i= 0; i < instance_options_map.records; ++i) + { + Instance_options_list *lst= + (Instance_options_list *) hash_element(&instance_options_map, i); + + for (int j= 0; j < lst->options.get_size(); ++j) + { + Named_value option= lst->options.get_element(j); + err_code= process_option(lst->instance, &option); + + if (err_code) + break; + } + + if (err_code) + break; + } + + if (err_code == 0) + net_send_ok(net, connection_id, NULL); + + return err_code; } -/* the only function from Unset_option we need to Implement */ +/************************************************************************** + Implementation of Set_option. +**************************************************************************/ -int Unset_option::do_command(struct st_net *net) +Set_option::Set_option(Instance_map *instance_map_arg) + :Abstract_option_cmd(instance_map_arg) { - return correct_file(TRUE); } -/* Implementation for Stop_instance: */ +/* + This operation parses SET options. + + SYNOPSYS + text [IN/OUT] a pointer to the text containing options. -Stop_instance::Stop_instance(Instance_map *instance_map_arg, - const char *name, uint len) - :Command(instance_map_arg) + RETURN + FALSE On success. + TRUE On syntax error. +*/ + +bool Set_option::parse_args(const char **text) { - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; + uint len; + + /* Check if we have something (and trim leading spaces). */ + + get_word(text, &len, NONSPACE); + + if (len == 0) + return TRUE; /* Syntax error: no option. */ + + /* Main parsing loop. */ + + while (TRUE) + { + LEX_STRING instance_name; + LEX_STRING option_name; + char *option_name_str; + char *option_value_str= NULL; + + /* Looking for instance name. */ + + get_word(text, &instance_name.length, ALPHANUM); + + if (instance_name.length == 0) + return TRUE; /* Syntax error: instance name expected. */ + + instance_name.str= (char *) *text; + *text+= instance_name.length; + + skip_spaces(text); + + /* Check the the delimiter is a dot. */ + + if (**text != '.') + return TRUE; /* Syntax error: dot expected. */ + + ++(*text); + + /* Looking for option name. */ + + get_word(text, &option_name.length, OPTION_NAME); + + if (option_name.length == 0) + return TRUE; /* Syntax error: option name expected. */ + + option_name.str= (char *) *text; + *text+= option_name.length; + + /* Looking for equal sign. */ + + skip_spaces(text); + + if (**text == '=') + { + ++(*text); /* Skip an equal sign. */ + + /* Looking for option value. */ + + skip_spaces(text); + + if (!**text) + return TRUE; /* Syntax error: EOS when option value expected. */ + + if (**text != '\'' && **text != '"') + { + /* Option value is a simple token. */ + + LEX_STRING option_value; + + get_word(text, &option_value.length, ALPHANUM); + + if (option_value.length == 0) + return TRUE; /* internal parser error. */ + + option_value.str= (char *) *text; + *text+= option_value.length; + + if (!(option_value_str= Named_value::alloc_str(&option_value))) + return TRUE; /* out of memory during parsing. */ + } + else + { + /* Option value is a string. */ + + if (parse_option_value(*text, &len, &option_value_str)) + return TRUE; /* Syntax error: invalid string specification. */ + + *text+= len; + } + } + + if (!option_value_str) + { + LEX_STRING empty_str= { C_STRING_WITH_SIZE("") }; + + if (!(option_value_str= Named_value::alloc_str(&empty_str))) + return TRUE; /* out of memory during parsing. */ + } + + if (!(option_name_str= Named_value::alloc_str(&option_name))) + { + Named_value::free_str(&option_name_str); + return TRUE; /* out of memory during parsing. */ + } + + { + Named_value option(option_name_str, option_value_str); + + if (add_option(&instance_name, &option)) + { + option.free(); + return TRUE; /* out of memory during parsing. */ + } + } + + skip_spaces(text); + + if (!**text) + return FALSE; /* OK: end of options. */ + + if (**text != ',') + return TRUE; /* Syntax error: comma expected. */ + + ++(*text); /* Skip a comma. */ + } } -int Stop_instance::execute(struct st_net *net, ulong connection_id) +int Set_option::process_option(Instance *instance, Named_value *option) { - uint err_code; + /* Check that the option is valid. */ + + if (instance->is_mysqld_compatible() && + Instance_options::is_option_im_specific(option->get_name())) + { + log_error("Error: IM-option (%s) can not be used " + "in the configuration of mysqld-compatible instance (%s).", + (const char *) option->get_name(), + (const char *) instance->get_name()->str); + return ER_INCOMPATIBLE_OPTION; + } - if (instance == 0) - return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ + /* Update the configuration file. */ - if (!(instance->options.nonguarded)) - instance_map->guardian->stop_guard(instance); + int err_code= correct_file(instance, option, FALSE); - if ((err_code= instance->stop())) + if (err_code) + return err_code; + + /* Update the internal cache. */ + + if (instance->options.set_option(option)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation of Unset_option. +**************************************************************************/ + +Unset_option::Unset_option(Instance_map *instance_map_arg) + :Abstract_option_cmd(instance_map_arg) +{ +} + + +/* + This operation parses UNSET options. + + SYNOPSYS + text [IN/OUT] a pointer to the text containing options. + + RETURN + FALSE On success. + TRUE On syntax error. +*/ + +bool Unset_option::parse_args(const char **text) +{ + uint len; + + /* Check if we have something (and trim leading spaces). */ + + get_word(text, &len, NONSPACE); + + if (len == 0) + return TRUE; /* Syntax error: no option. */ + + /* Main parsing loop. */ + + while (TRUE) + { + LEX_STRING instance_name; + LEX_STRING option_name; + char *option_name_str; + char *option_value_str; + + /* Looking for instance name. */ + + get_word(text, &instance_name.length, ALPHANUM); + + if (instance_name.length == 0) + return TRUE; /* Syntax error: instance name expected. */ + + instance_name.str= (char *) *text; + *text+= instance_name.length; + + skip_spaces(text); + + /* Check the the delimiter is a dot. */ + + if (**text != '.') + return TRUE; /* Syntax error: dot expected. */ + + ++(*text); /* Skip a dot. */ + + /* Looking for option name. */ + + get_word(text, &option_name.length, OPTION_NAME); + + if (option_name.length == 0) + return TRUE; /* Syntax error: option name expected. */ + + option_name.str= (char *) *text; + *text+= option_name.length; + + if (!(option_name_str= Named_value::alloc_str(&option_name))) + return TRUE; /* out of memory during parsing. */ + + { + LEX_STRING empty_str= { C_STRING_WITH_SIZE("") }; + + if (!(option_value_str= Named_value::alloc_str(&empty_str))) + { + Named_value::free_str(&option_name_str); + return TRUE; + } + } + + { + Named_value option(option_name_str, option_value_str); + + if (add_option(&instance_name, &option)) + { + option.free(); + return TRUE; /* out of memory during parsing. */ + } + } + + skip_spaces(text); + + if (!**text) + return FALSE; /* OK: end of options. */ + + if (**text != ',') + return TRUE; /* Syntax error: comma expected. */ + + ++(*text); /* Skip a comma. */ + } +} + + +/* + Implementation of UNSET statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance name specified is not valid + ER_INSTANCE_IS_ACTIVE The specified instance is active + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Unset_option::process_option(Instance *instance, Named_value *option) +{ + /* Update the configuration file. */ + + int err_code= correct_file(instance, option, TRUE); + + if (err_code) return err_code; - net_send_ok(net, connection_id, NULL); + /* Update the internal cache. */ + + instance->options.unset_option(option->get_name()); + return 0; } -int Syntax_error::execute(struct st_net *net, ulong connection_id) +/************************************************************************** + Implementation of Syntax_error. +**************************************************************************/ + +int Syntax_error::execute(st_net *net, ulong connection_id) { return ER_SYNTAX_ERROR; } diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index bfd38d34889..9a9911f2358 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -16,10 +16,20 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <my_global.h> +#include <my_sys.h> +#include <m_string.h> +#include <hash.h> + #include "command.h" #include "instance.h" #include "parse.h" +#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) +#pragma interface +#endif + + /* Print all instances of this instance manager. Grammar: SHOW ISTANCES @@ -31,12 +41,16 @@ public: Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg) {} - int execute(struct st_net *net, ulong connection_id); + int execute(st_net *net, ulong connection_id); + +private: + int write_header(st_net *net); + int write_data(st_net *net); }; /* - Reread configuration file and refresh instance map. + Reread configuration file and refresh internal cache. Grammar: FLUSH INSTANCES */ @@ -46,7 +60,43 @@ public: Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg) {} - int execute(struct st_net *net, ulong connection_id); + int execute(st_net *net, ulong connection_id); +}; + + +/* + Abstract class for Instance-specific commands. +*/ + +class Abstract_instance_cmd : public Command +{ +public: + Abstract_instance_cmd(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg); + +public: + virtual int execute(st_net *net, ulong connection_id); + +protected: + /* MT-NOTE: this operation is called under acquired Instance_map's lock. */ + virtual int execute_impl(st_net *net, Instance *instance) = 0; + + /* + This operation is invoked on successful return of execute_impl() and is + intended to send closing data. + + MT-NOTE: this operation is called under released Instance_map's lock. + */ + virtual int send_ok_response(st_net *net, ulong connection_id) = 0; + +protected: + inline const LEX_STRING *get_instance_name() const + { + return instance_name.get_str(); + } + +private: + Instance_name instance_name; }; @@ -55,31 +105,40 @@ public: Grammar: SHOW ISTANCE STATUS <instance_name> */ -class Show_instance_status : public Command +class Show_instance_status : public Abstract_instance_cmd { public: - Show_instance_status(Instance_map *instance_map_arg, - const char *name, uint len); - int execute(struct st_net *net, ulong connection_id); - const char *instance_name; + const LEX_STRING *instance_name_arg); + +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); + +private: + int write_header(st_net *net); + int write_data(st_net *net, Instance *instance); }; /* - Print options if chosen instance. + Print options of chosen instance. Grammar: SHOW INSTANCE OPTIONS <instance_name> */ -class Show_instance_options : public Command +class Show_instance_options : public Abstract_instance_cmd { public: - Show_instance_options(Instance_map *instance_map_arg, - const char *name, uint len); + const LEX_STRING *instance_name_arg); - int execute(struct st_net *net, ulong connection_id); - const char *instance_name; +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); + +private: + int write_header(st_net *net); + int write_data(st_net *net, Instance *instance); }; @@ -88,14 +147,15 @@ public: Grammar: START INSTANCE <instance_name> */ -class Start_instance : public Command +class Start_instance : public Abstract_instance_cmd { public: - Start_instance(Instance_map *instance_map_arg, const char *name, uint len); + Start_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg); - int execute(struct st_net *net, ulong connection_id); - const char *instance_name; - Instance *instance; +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); }; @@ -104,33 +164,95 @@ public: Grammar: STOP INSTANCE <instance_name> */ -class Stop_instance : public Command +class Stop_instance : public Abstract_instance_cmd { public: - Stop_instance(Instance_map *instance_map_arg, const char *name, uint len); + Stop_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg); - Instance *instance; - int execute(struct st_net *net, ulong connection_id); - const char *instance_name; +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); }; /* - Print requested part of the log + Create an instance. + Grammar: CREATE INSTANCE <instance_name> [<options>] +*/ + +class Create_instance : public Command +{ +public: + Create_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg); + +public: + bool init(const char **text); + +protected: + virtual int execute(st_net *net, ulong connection_id); + + inline const LEX_STRING *get_instance_name() const + { + return instance_name.get_str(); + } + +private: + bool parse_args(const char **text); + +private: + Instance_name instance_name; + + Named_value_arr options; +}; + + +/* + Drop an instance. + Grammar: DROP INSTANCE <instance_name> + + Operation is permitted only if the instance is stopped. On successful + completion the instance section is removed from config file and the instance + is removed from the instance map. +*/ + +class Drop_instance : public Abstract_instance_cmd +{ +public: + Drop_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg); + +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); +}; + + +/* + Print requested part of the log. Grammar: - SHOW <instance_name> log {ERROR | SLOW | GENERAL} size[, offset_from_end] + SHOW <instance_name> LOG {ERROR | SLOW | GENERAL} size[, offset_from_end] */ -class Show_instance_log : public Command +class Show_instance_log : public Abstract_instance_cmd { public: + Show_instance_log(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg, + Log_type log_type_arg, uint size_arg, uint offset_arg); + +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); + +private: + int check_params(Instance *instance); + int write_header(st_net *net); + int write_data(st_net *net, Instance *instance); - Show_instance_log(Instance_map *instance_map_arg, const char *name, - uint len, Log_type log_type_arg, const char *size_arg, - const char *offset_arg); - int execute(struct st_net *net, ulong connection_id); +private: Log_type log_type; - const char *instance_name; uint size; uint offset; }; @@ -141,75 +263,112 @@ public: Grammar: SHOW <instance_name> LOG FILES */ -class Show_instance_log_files : public Command +class Show_instance_log_files : public Abstract_instance_cmd { public: - Show_instance_log_files(Instance_map *instance_map_arg, - const char *name, uint len); - int execute(struct st_net *net, ulong connection_id); - const char *instance_name; - const char *option; + const LEX_STRING *instance_name_arg); + +protected: + virtual int execute_impl(st_net *net, Instance *instance); + virtual int send_ok_response(st_net *net, ulong connection_id); + +private: + int write_header(st_net *net); + int write_data(st_net *net, Instance *instance); }; /* - Syntax error command. This command is issued if parser reported a syntax - error. We need it to distinguish the parse error and the situation when - parser internal error occured. E.g. parsing failed because we hadn't had - enought memory. In the latter case parse_command() should return an error. + Abstract class for option-management commands. */ -class Syntax_error : public Command +class Instance_options_list; + +class Abstract_option_cmd : public Command { public: - int execute(struct st_net *net, ulong connection_id); + ~Abstract_option_cmd(); + +public: + bool add_option(const LEX_STRING *instance_name, Named_value *option); + +public: + bool init(const char **text); + + virtual int execute(st_net *net, ulong connection_id); + +protected: + Abstract_option_cmd(Instance_map *instance_map_arg); + + int correct_file(Instance *instance, Named_value *option, bool skip); + +protected: + virtual bool parse_args(const char **text) = 0; + virtual int process_option(Instance *instance, Named_value *option) = 0; + +private: + Instance_options_list * + get_instance_options_list(const LEX_STRING *instance_name); + + int execute_impl(st_net *net, ulong connection_id); + +private: + HASH instance_options_map; + bool initialized; }; + /* Set an option for the instance. - Grammar: SET instance_name.option=option_value + Grammar: SET instance_name.option[=option_value][, ...] */ -class Set_option : public Command +class Set_option : public Abstract_option_cmd { public: - Set_option(Instance_map *instance_map_arg, const char *name, uint len, - const char *option_arg, uint option_len, - const char *option_value_arg, uint option_value_len); - /* - the following function is virtual to let Unset_option to use - */ - virtual int do_command(struct st_net *net); - int execute(struct st_net *net, ulong connection_id); + Set_option(Instance_map *instance_map_arg); + protected: - int correct_file(int skip); -public: - const char *instance_name; - uint instance_name_len; - /* buffer for the option */ - enum { MAX_OPTION_LEN= 1024 }; - char option[MAX_OPTION_LEN]; - char option_value[MAX_OPTION_LEN]; + virtual bool parse_args(const char **text); + virtual int process_option(Instance *instance, Named_value *option); }; /* - Remove option of the instance from config file - Grammar: UNSET instance_name.option + Remove option of the instance. + Grammar: UNSET instance_name.option[, ...] */ -class Unset_option: public Set_option +class Unset_option: public Abstract_option_cmd { public: - Unset_option(Instance_map *instance_map_arg, const char *name, uint len, - const char *option_arg, uint option_len, - const char *option_value_arg, uint option_value_len): - Set_option(instance_map_arg, name, len, option_arg, option_len, - option_value_arg, option_value_len) - {} - int do_command(struct st_net *net); + Unset_option(Instance_map *instance_map_arg); + +protected: + virtual bool parse_args(const char **text); + virtual int process_option(Instance *instance, Named_value *option); }; +/* + Syntax error command. + + This command is issued if parser reported a syntax error. We need it to + distinguish between syntax error and internal parser error. E.g. parsing + failed because we hadn't had enought memory. In the latter case the parser + just returns NULL. +*/ + +class Syntax_error : public Command +{ +public: + /* This is just to avoid compiler warning. */ + Syntax_error() :Command(NULL) + {} + +public: + int execute(st_net *net, ulong connection_id); +}; + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */ diff --git a/server-tools/instance-manager/exit_codes.h b/server-tools/instance-manager/exit_codes.h new file mode 100644 index 00000000000..560ce30b7aa --- /dev/null +++ b/server-tools/instance-manager/exit_codes.h @@ -0,0 +1,41 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H + +/* + Copyright (C) 2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + +/* + This file contains a list of exit codes, which are used when Instance + Manager is working in user-management mode. +*/ + +const int ERR_OK = 0; + +const int ERR_OUT_OF_MEMORY = 1; +const int ERR_INVALID_USAGE = 2; +const int ERR_INTERNAL_ERROR = 3; +const int ERR_IO_ERROR = 4; +const int ERR_PASSWORD_FILE_CORRUPTED = 5; +const int ERR_PASSWORD_FILE_DOES_NOT_EXIST = 6; + +const int ERR_CAN_NOT_READ_USER_NAME = 10; +const int ERR_CAN_NOT_READ_PASSWORD = 11; +const int ERR_USER_ALREADY_EXISTS = 12; +const int ERR_USER_NOT_FOUND = 13; + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_EXIT_CODES_H diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 3be672cd71c..7a9ff3e8367 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -21,16 +21,14 @@ #include "guardian.h" -#include "instance_map.h" -#include "instance.h" -#include "mysql_manager_error.h" -#include "log.h" -#include "portability.h" - #include <string.h> #include <sys/types.h> #include <signal.h> +#include "instance.h" +#include "instance_map.h" +#include "log.h" +#include "mysql_manager_error.h" pthread_handler_t guardian(void *arg) @@ -40,6 +38,37 @@ pthread_handler_t guardian(void *arg) return 0; } + +const char * +Guardian_thread::get_instance_state_name(enum_instance_state state) +{ + switch (state) { + case NOT_STARTED: + return "offline"; + + case STARTING: + return "starting"; + + case STARTED: + return "online"; + + case JUST_CRASHED: + return "failed"; + + case CRASHED: + return "crashed"; + + case CRASHED_AND_ABANDONED: + return "abandoned"; + + case STOPPING: + return "stopping"; + } + + return NULL; /* just to ignore compiler warning. */ +} + + Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, Instance_map *instance_map_arg, uint monitoring_interval_arg) : @@ -89,10 +118,17 @@ void Guardian_thread::process_instance(Instance *instance, if (current_node->state == STOPPING) { /* this brach is executed during shutdown */ - if (instance->options.shutdown_delay_val) + if (instance->options.shutdown_delay) + { + /* + NOTE: it is important to check shutdown_delay here, but use + shutdown_delay_val. The idea is that if the option is unset, + shutdown_delay will be NULL, but shutdown_delay_val will not be reset. + */ waitchild= instance->options.shutdown_delay_val; + } - /* this returns true if and only if an instance was stopped for sure */ + /* this returns TRUE if and only if an instance was stopped for sure */ if (instance->is_crashed()) *guarded_instances= list_delete(*guarded_instances, node); else if ( (uint) (current_time - current_node->last_checked) > waitchild) @@ -159,7 +195,11 @@ void Guardian_thread::process_instance(Instance *instance, instance->options.instance_name); } else + { + log_info("guardian: cannot start instance %s. Abandoning attempts " + "to (re)start it", instance->options.instance_name); current_node->state= CRASHED_AND_ABANDONED; + } } break; case CRASHED_AND_ABANDONED: @@ -242,7 +282,9 @@ int Guardian_thread::is_stopped() SYNOPSYS Guardian_thread::init() - NOTE: One should always lock guardian before calling this routine. + NOTE: The operation should be invoked with the following locks acquired: + - Guardian_thread; + - Instance_map; RETURN 0 - ok @@ -261,12 +303,11 @@ int Guardian_thread::init() while ((instance= iterator.next())) { - if (!(instance->options.nonguarded)) - if (guard(instance, TRUE)) /* do not lock guardian */ - { - instance_map->unlock(); - return 1; - } + if (instance->options.nonguarded) + continue; + + if (guard(instance, TRUE)) /* do not lock guardian */ + return 1; } return 0; @@ -334,24 +375,14 @@ int Guardian_thread::stop_guard(Instance *instance) LIST *node; pthread_mutex_lock(&LOCK_guardian); - node= guarded_instances; - while (node != NULL) - { - /* - We compare only pointers, as we always use pointers from the - instance_map's MEM_ROOT. - */ - if (((GUARD_NODE *) node->data)->instance == instance) - { - guarded_instances= list_delete(guarded_instances, node); - pthread_mutex_unlock(&LOCK_guardian); - return 0; - } - else - node= node->next; - } + node= find_instance_node(instance); + + if (node != NULL) + guarded_instances= list_delete(guarded_instances, node); + pthread_mutex_unlock(&LOCK_guardian); + /* if there is nothing to delete it is also fine */ return 0; } @@ -420,7 +451,7 @@ int Guardian_thread::stop_instances(bool stop_instances_arg) void Guardian_thread::lock() { - pthread_mutex_lock(&LOCK_guardian); + pthread_mutex_lock(&LOCK_guardian); } @@ -428,3 +459,41 @@ void Guardian_thread::unlock() { pthread_mutex_unlock(&LOCK_guardian); } + + +LIST *Guardian_thread::find_instance_node(Instance *instance) +{ + LIST *node= guarded_instances; + + while (node != NULL) + { + /* + We compare only pointers, as we always use pointers from the + instance_map's MEM_ROOT. + */ + if (((GUARD_NODE *) node->data)->instance == instance) + return node; + + node= node->next; + } + + return NULL; +} + + +bool Guardian_thread::is_active(Instance *instance) +{ + bool guarded; + + lock(); + + guarded= find_instance_node(instance) != NULL; + + /* is_running() can take a long time, so let's unlock mutex first. */ + unlock(); + + if (guarded) + return true; + + return instance->is_running(); +} diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 16b4c373c91..6d3a2b222d7 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -17,11 +17,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <my_global.h> -#include "thread_registry.h" - #include <my_sys.h> #include <my_list.h> +#include "thread_registry.h" + #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface #endif @@ -79,6 +79,8 @@ public: time_t last_checked; }; + /* Return client state name. */ + static const char *get_instance_state_name(enum_instance_state state); Guardian_thread(Thread_registry &thread_registry_arg, Instance_map *instance_map_arg, @@ -94,11 +96,28 @@ public: int guard(Instance *instance, bool nolock= FALSE); /* Stop instance protection */ int stop_guard(Instance *instance); - /* Returns true if guardian thread is stopped */ + /* Returns TRUE if guardian thread is stopped */ int is_stopped(); void lock(); void unlock(); + /* + Return an internal list node for the given instance if the instance is + managed by Guardian. Otherwise, return NULL. + + MT-NOTE: must be called under acquired lock. + */ + LIST *find_instance_node(Instance *instance); + + /* The operation is used to check if the instance is active or not. */ + bool is_active(Instance *instance); + + /* + Return state of the given instance list node. The pointer must specify + a valid list node. + */ + inline enum_instance_state get_instance_state(LIST *instance_node); + public: pthread_cond_t COND_guardian; @@ -108,6 +127,7 @@ private: /* check instance state and act accordingly */ void process_instance(Instance *instance, GUARD_NODE *current_node, LIST **guarded_instances, LIST *elem); + int stopped; private: @@ -115,9 +135,15 @@ private: Thread_info thread_info; LIST *guarded_instances; MEM_ROOT alloc; - enum { MEM_ROOT_BLOCK_SIZE= 512 }; /* this variable is set to TRUE when we want to stop Guardian thread */ bool shutdown_requested; }; + +inline Guardian_thread::enum_instance_state +Guardian_thread::get_instance_state(LIST *instance_node) +{ + return ((GUARD_NODE *) instance_node->data)->state; +} + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 39381b457ab..456052bf7e5 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -20,18 +20,27 @@ #include "instance.h" -#include "mysql_manager_error.h" -#include "log.h" -#include "instance_map.h" -#include "priv.h" -#include "portability.h" +#include <my_global.h> +#include <mysql.h> + +#include <signal.h> #ifndef __WIN__ #include <sys/wait.h> #endif -#include <my_sys.h> -#include <signal.h> -#include <m_string.h> -#include <mysql.h> + +#include "guardian.h" +#include "instance_map.h" +#include "log.h" +#include "mysql_manager_error.h" +#include "portability.h" +#include "priv.h" + + +const LEX_STRING +Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_SIZE("mysqld") }; + +static const char * const INSTANCE_NAME_PREFIX= Instance::DFLT_INSTANCE_NAME.str; +static const int INSTANCE_NAME_PREFIX_LEN= Instance::DFLT_INSTANCE_NAME.length; static void start_and_monitor_instance(Instance_options *old_instance_options, @@ -152,7 +161,7 @@ static int start_process(Instance_options *instance_options, switch (*pi) { case 0: /* never happens on QNX */ - execv(instance_options->mysqld_path, instance_options->argv); + execv(instance_options->mysqld_path.str, instance_options->argv); /* exec never returns */ exit(1); case -1: @@ -180,7 +189,7 @@ static int start_process(Instance_options *instance_options, char *cmdline= new char[cmdlen]; if (cmdline == NULL) return 1; - + cmdline[0]= 0; for (int i= 0; instance_options->argv[i] != 0; i++) { @@ -232,9 +241,7 @@ static int start_process(Instance_options *instance_options, static void start_and_monitor_instance(Instance_options *old_instance_options, Instance_map *instance_map) { - enum { MAX_INSTANCE_NAME_LEN= 512 }; - char instance_name_buff[MAX_INSTANCE_NAME_LEN]; - uint instance_name_len; + Instance_name instance_name(&old_instance_options->instance_name); Instance *current_instance; My_process_info process_info; @@ -248,11 +255,8 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, Save the instance name in the case if Instance object we are using is destroyed. (E.g. by "FLUSH INSTANCES") */ - strmake(instance_name_buff, old_instance_options->instance_name, - MAX_INSTANCE_NAME_LEN - 1); - instance_name_len= old_instance_options->instance_name_len; - log_info("starting instance %s", instance_name_buff); + log_info("starting instance %s", (const char *) instance_name.get_c_str()); if (start_process(old_instance_options, &process_info)) { @@ -266,15 +270,36 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, /* don't check for return value */ wait_process(&process_info); - current_instance= instance_map->find(instance_name_buff, instance_name_len); + instance_map->lock(); + + current_instance= instance_map->find(instance_name.get_str()); if (current_instance) current_instance->set_crash_flag_n_wake_all(); + instance_map->unlock(); + return; } +bool Instance::is_name_valid(const LEX_STRING *name) +{ + const char *name_suffix= name->str + INSTANCE_NAME_PREFIX_LEN; + + if (strncmp(name->str, INSTANCE_NAME_PREFIX, INSTANCE_NAME_PREFIX_LEN) != 0) + return FALSE; + + return *name_suffix == 0 || my_isdigit(default_charset_info, *name_suffix); +} + + +bool Instance::is_mysqld_compatible_name(const LEX_STRING *name) +{ + return strcmp(name->str, INSTANCE_NAME_PREFIX) == 0; +} + + Instance_map *Instance::get_map() { return instance_map; @@ -309,11 +334,11 @@ int Instance::start() { /* clear crash flag */ pthread_mutex_lock(&LOCK_instance); - crashed= 0; + crashed= FALSE; pthread_mutex_unlock(&LOCK_instance); - if (!is_running()) + if (configured && !is_running()) { remove_pid(); @@ -339,8 +364,8 @@ int Instance::start() return 0; } - /* the instance is started already */ - return ER_INSTANCE_ALREADY_STARTED; + /* The instance is started already or misconfigured. */ + return configured ? ER_INSTANCE_ALREADY_STARTED : ER_INSTANCE_MISCONFIGURED; } /* @@ -363,7 +388,7 @@ void Instance::set_crash_flag_n_wake_all() { /* set instance state to crashed */ pthread_mutex_lock(&LOCK_instance); - crashed= 1; + crashed= TRUE; pthread_mutex_unlock(&LOCK_instance); /* @@ -378,7 +403,7 @@ void Instance::set_crash_flag_n_wake_all() -Instance::Instance(): crashed(0) +Instance::Instance(): crashed(FALSE), configured(FALSE) { pthread_mutex_init(&LOCK_instance, 0); pthread_cond_init(&COND_instance_stopped, 0); @@ -392,9 +417,9 @@ Instance::~Instance() } -int Instance::is_crashed() +bool Instance::is_crashed() { - int val; + bool val; pthread_mutex_lock(&LOCK_instance); val= crashed; pthread_mutex_unlock(&LOCK_instance); @@ -413,10 +438,17 @@ bool Instance::is_running() bool return_val; if (options.mysqld_port) + { + /* + NOTE: it is important to check mysqld_port here, but use + mysqld_port_val. The idea is that if the option is unset, mysqld_port + will be NULL, but mysqld_port_val will not be reset. + */ port= options.mysqld_port_val; + } if (options.mysqld_socket) - socket= strchr(options.mysqld_socket, '=') + 1; + socket= options.mysqld_socket; /* no port was specified => instance falled back to default value */ if (!options.mysqld_port && !options.mysqld_socket) @@ -469,8 +501,15 @@ int Instance::stop() struct timespec timeout; uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY; - if (options.shutdown_delay_val) + if (options.shutdown_delay) + { + /* + NOTE: it is important to check shutdown_delay here, but use + shutdown_delay_val. The idea is that if the option is unset, + shutdown_delay will be NULL, but shutdown_delay_val will not be reset. + */ waitchild= options.shutdown_delay_val; + } kill_instance(SIGTERM); /* sleep on condition to wait for SIGCHLD */ @@ -588,20 +627,33 @@ void Instance::kill_instance(int signum) } /* - We execute this function to initialize instance parameters. - Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY. + Initialize instance parameters. + + SYNOPSYS + Instance::init() + name_arg name of the instance + + RETURN: + 0 ok + !0 error */ -int Instance::init(const char *name_arg) +int Instance::init(const LEX_STRING *name_arg) { + mysqld_compatible= is_mysqld_compatible_name(name_arg); + return options.init(name_arg); } int Instance::complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path, - uint instance_type) + const char *mysqld_path) { instance_map= instance_map_arg; - return options.complete_initialization(mysqld_path, instance_type); + configured= !options.complete_initialization(mysqld_path); + return 0; + /* + TODO: return actual status (from + Instance_options::complete_initialization()) here. + */ } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index adb66991685..1f06cabebf7 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -17,7 +17,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <my_global.h> +#include <m_string.h> + #include "instance_options.h" +#include "priv.h" #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface @@ -25,31 +28,120 @@ class Instance_map; + +/* + Instance_name -- the class represents instance name -- a string of length + less than MAX_INSTANCE_NAME_SIZE. + + Generally, this is just a string with self-memory-management and should be + eliminated in the future. +*/ + +class Instance_name +{ +public: + Instance_name(const LEX_STRING *name); + +public: + inline const LEX_STRING *get_str() const + { + return &str; + } + + inline const char *get_c_str() const + { + return str.str; + } + + inline uint get_length() const + { + return str.length; + } + +private: + LEX_STRING str; + char str_buffer[MAX_INSTANCE_NAME_SIZE]; +}; + + class Instance { public: + /* + The following two constants defines name of the default mysqld-instance + ("mysqld"). + */ + static const LEX_STRING DFLT_INSTANCE_NAME; + +public: + /* + The operation is intended to check whether string is a well-formed + instance name or not. + */ + static bool is_name_valid(const LEX_STRING *name); + + /* + The operation is intended to check if the given instance name is + mysqld-compatible or not. + */ + static bool is_mysqld_compatible_name(const LEX_STRING *name); + +public: Instance(); ~Instance(); - int init(const char *name); + int init(const LEX_STRING *name_arg); int complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path, uint instance_type); + const char *mysqld_path); bool is_running(); int start(); int stop(); /* send a signal to the instance */ void kill_instance(int signo); - int is_crashed(); + bool is_crashed(); void set_crash_flag_n_wake_all(); Instance_map *get_map(); + /* + The operation is intended to check if the instance is mysqld-compatible + or not. + */ + inline bool is_mysqld_compatible() const; + + /* + The operation is intended to check if the instance is configured properly + or not. Misconfigured instances are not managed. + */ + inline bool is_configured() const; + + inline const LEX_STRING *get_name() const; + public: enum { DEFAULT_SHUTDOWN_DELAY= 35 }; Instance_options options; private: - int crashed; + /* This attributes is a flag, specifies if the instance has been crashed. */ + bool crashed; + + /* + This attribute specifies if the instance is configured properly or not. + Misconfigured instances are not managed. + */ + bool configured; + + /* + This attribute specifies whether the instance is mysqld-compatible or not. + Mysqld-compatible instances can contain only mysqld-specific options. + At the moment an instance is mysqld-compatible if its name is "mysqld". + + The idea is that [mysqld] section should contain only mysqld-specific + options (no Instance Manager-specific options) to be readable by mysqld + program. + */ + bool mysqld_compatible; + /* Mutex protecting the instance. Currently we use it to avoid the double start of the instance. This happens when the instance is starting @@ -66,4 +158,22 @@ private: void remove_pid(); }; + +inline bool Instance::is_mysqld_compatible() const +{ + return mysqld_compatible; +} + + +inline bool Instance::is_configured() const +{ + return configured; +} + + +inline const LEX_STRING *Instance::get_name() const +{ + return &options.instance_name; +} + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 543c9c31bfa..c9608fa7c14 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -20,14 +20,19 @@ #include "instance_map.h" +#include <my_global.h> +#include <m_ctype.h> +#include <mysql_com.h> + #include "buffer.h" +#include "guardian.h" #include "instance.h" #include "log.h" +#include "manager.h" +#include "mysqld_error.h" +#include "mysql_manager_error.h" #include "options.h" - -#include <m_ctype.h> -#include <mysql_com.h> -#include <m_string.h> +#include "priv.h" /* Note: As we are going to suppost different types of connections, @@ -45,8 +50,8 @@ static byte* get_instance_key(const byte* u, uint* len, my_bool __attribute__((unused)) t) { const Instance *instance= (const Instance *) u; - *len= instance->options.instance_name_len; - return (byte *) instance->options.instance_name; + *len= instance->options.instance_name.length; + return (byte *) instance->options.instance_name.str; } static void delete_instance(void *u) @@ -79,16 +84,60 @@ static void delete_instance(void *u) static int process_option(void *ctx, const char *group, const char *option) { - Instance_map *map= NULL; + Instance_map *map= (Instance_map*) ctx; + LEX_STRING group_str; - map = (Instance_map*) ctx; - return map->process_one_option(group, option); + group_str.str= (char *) group; + group_str.length= strlen(group); + + return map->process_one_option(&group_str, option); } C_MODE_END /* + Parse option string. + + SYNOPSIS + parse_option() + option_str [IN] option string (e.g. "--name=value") + option_name_buf [OUT] parsed name of the option. + Must be of (MAX_OPTION_LEN + 1) size. + option_value_buf [OUT] parsed value of the option. + Must be of (MAX_OPTION_LEN + 1) size. + + DESCRIPTION + This is an auxiliary function and should not be used externally. It is + intended to parse whole option string into option name and option value. +*/ + +static void parse_option(const char *option_str, + char *option_name_buf, + char *option_value_buf) +{ + const char *eq_pos; + const char *ptr= option_str; + + while (*ptr == '-') + ++ptr; + + strmake(option_name_buf, ptr, MAX_OPTION_LEN + 1); + + eq_pos= strchr(ptr, '='); + if (eq_pos) + { + option_name_buf[eq_pos - ptr]= 0; + strmake(option_value_buf, eq_pos + 1, MAX_OPTION_LEN + 1); + } + else + { + option_value_buf[0]= 0; + } +} + + +/* Process one option from the configuration file. SYNOPSIS @@ -103,34 +152,64 @@ C_MODE_END of the instance map object. */ -int Instance_map::process_one_option(const char *group, const char *option) +int Instance_map::process_one_option(const LEX_STRING *group, + const char *option) { Instance *instance= NULL; - static const char prefix[]= { 'm', 'y', 's', 'q', 'l', 'd' }; - if (strncmp(group, prefix, sizeof prefix) == 0 && - ((my_isdigit(default_charset_info, group[sizeof prefix])) - || group[sizeof(prefix)] == '\0')) + if (!Instance::is_name_valid(group)) + { + /* + Current section name is not a valid instance name. + We should skip it w/o error. + */ + return 0; + } + + if (!(instance= (Instance *) hash_search(&hash, (byte *) group->str, + group->length))) + { + if (!(instance= new Instance())) + return 1; + + if (instance->init(group) || add_instance(instance)) { - if (!(instance= (Instance *) hash_search(&hash, (byte *) group, - strlen(group)))) - { - if (!(instance= new Instance)) - goto err; - if (instance->init(group) || my_hash_insert(&hash, (byte *) instance)) - goto err_instance; - } - - if (instance->options.add_option(option)) - goto err; /* the instance'll be deleted when we destroy the map */ + delete instance; + return 1; } - return 0; + if (instance->is_mysqld_compatible()) + log_info("Warning: instance name '%s' is mysqld-compatible.", + (const char *) group->str); -err_instance: - delete instance; -err: - return 1; + log_info("mysqld instance '%s' has been added successfully.", + (const char *) group->str); + } + + if (option) + { + char option_name[MAX_OPTION_LEN + 1]; + char option_value[MAX_OPTION_LEN + 1]; + + parse_option(option, option_name, option_value); + + if (instance->is_mysqld_compatible() && + Instance_options::is_option_im_specific(option_name)) + { + log_info("Warning: configuration of mysqld-compatible instance '%s' " + "contains IM-specific option '%s'. " + "This breaks backward compatibility for the configuration file.", + (const char *) group->str, + (const char *) option_name); + } + + Named_value option(option_name, option_value); + + if (instance->options.set_option(&option)) + return 1; /* the instance'll be deleted when we destroy the map */ + } + + return 0; } @@ -181,7 +260,7 @@ void Instance_map::unlock() - pass on the new map to the guardian thread: it will start all instances that are marked `guarded' and not yet started. Note, as the check whether an instance is started is currently - very simple (returns true if there is a MySQL server running + very simple (returns TRUE if there is a MySQL server running at the given port), this function has some peculiar side-effects: * if the port number of a running instance was changed, the @@ -194,9 +273,9 @@ void Instance_map::unlock() In order to avoid such side effects one should never call FLUSH INSTANCES without prior stop of all running instances. - TODO - FLUSH INSTANCES should return an error if it's called - while there is a running instance. + NOTE: The operation should be invoked with the following locks acquired: + - Guardian_thread; + - Instance_map; */ int Instance_map::flush_instances() @@ -209,67 +288,169 @@ int Instance_map::flush_instances() guardian (2) reload the instance map (3) reinitialize the guardian with new instances. */ - guardian->lock(); - pthread_mutex_lock(&LOCK_instance_map); hash_free(&hash); hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0); + rc= load(); guardian->init(); - pthread_mutex_unlock(&LOCK_instance_map); - guardian->unlock(); return rc; } -Instance * -Instance_map::find(const char *name, uint name_len) +bool Instance_map::is_there_active_instance() { Instance *instance; - pthread_mutex_lock(&LOCK_instance_map); - instance= (Instance *) hash_search(&hash, (byte *) name, name_len); - pthread_mutex_unlock(&LOCK_instance_map); - return instance; + Iterator iterator(this); + + while ((instance= iterator.next())) + { + if (guardian->find_instance_node(instance) != NULL || + instance->is_running()) + { + return TRUE; + } + } + + return FALSE; } -int Instance_map::complete_initialization() +int Instance_map::add_instance(Instance *instance) { - Instance *instance; - uint i= 0; + return my_hash_insert(&hash, (byte *) instance); +} - if (hash.records == 0) /* no instances found */ - { - if ((instance= new Instance) == 0) - goto err; +int Instance_map::remove_instance(Instance *instance) +{ + return hash_delete(&hash, (byte *) instance); +} - if (instance->init("mysqld") || my_hash_insert(&hash, (byte *) instance)) - goto err_instance; - /* - After an instance have been added to the instance_map, - hash_free should handle it's deletion => goto err, not - err_instance. - */ - if (instance->complete_initialization(this, mysqld_path, - DEFAULT_SINGLE_INSTANCE)) - goto err; +int Instance_map::create_instance(const LEX_STRING *instance_name, + const Named_value_arr *options) +{ + Instance *instance= new Instance(); + + if (!instance) + { + log_error("Error: can not initialize (name: '%s').", + (const char *) instance_name->str); + return ER_OUT_OF_RESOURCES; } - else - while (i < hash.records) + + if (instance->init(instance_name)) + { + log_error("Error: can not initialize (name: '%s').", + (const char *) instance_name->str); + delete instance; + return ER_OUT_OF_RESOURCES; + } + + for (int i= 0; options && i < options->get_size(); ++i) + { + Named_value option= options->get_element(i); + + if (instance->is_mysqld_compatible() && + Instance_options::is_option_im_specific(option.get_name())) { - instance= (Instance *) hash_element(&hash, i); - if (instance->complete_initialization(this, mysqld_path, USUAL_INSTANCE)) - goto err; - i++; + log_error("Error: IM-option (%s) can not be used " + "in configuration of mysqld-compatible instance (%s).", + (const char *) option.get_name(), + (const char *) instance_name->str); + delete instance; + return ER_INCOMPATIBLE_OPTION; } + instance->options.set_option(&option); + } + + if (instance->is_mysqld_compatible()) + log_info("Warning: instance name '%s' is mysqld-compatible.", + (const char *) instance_name->str); + + if (instance->complete_initialization(this, mysqld_path)) + { + log_error("Error: can not complete initialization of instance (name: '%s').", + (const char *) instance_name->str); + delete instance; + return ER_OUT_OF_RESOURCES; + /* TODO: return more appropriate error code in this case. */ + } + + if (add_instance(instance)) + { + log_error("Error: can not register instance (name: '%s').", + (const char *) instance_name->str); + delete instance; + return ER_OUT_OF_RESOURCES; + } + return 0; -err_instance: - delete instance; -err: - return 1; +} + + +Instance * Instance_map::find(const LEX_STRING *name) +{ + return (Instance *) hash_search(&hash, (byte *) name->str, name->length); +} + + +bool Instance_map::complete_initialization() +{ + bool mysqld_found; + + /* Complete initialization of all registered instances. */ + + for (uint i= 0; i < hash.records; ++i) + { + Instance *instance= (Instance *) hash_element(&hash, i); + + if (instance->complete_initialization(this, mysqld_path)) + return TRUE; + } + + /* That's all if we are runnning in an ordinary mode. */ + + if (!Options::Main::mysqld_safe_compatible) + return FALSE; + + /* In mysqld-compatible mode we must ensure that there 'mysqld' instance. */ + + mysqld_found= find(&Instance::DFLT_INSTANCE_NAME) != NULL; + + if (mysqld_found) + return FALSE; + + if (create_instance(&Instance::DFLT_INSTANCE_NAME, NULL)) + { + log_error("Error: could not create default instance."); + return TRUE; + } + + switch (create_instance_in_file(&Instance::DFLT_INSTANCE_NAME, NULL)) + { + case 0: + case ER_CONF_FILE_DOES_NOT_EXIST: + /* + Continue if the instance has been added to the config file + successfully, or the config file just does not exist. + */ + break; + + default: + log_error("Error: could not add default instance to the config file."); + + Instance *instance= find(&Instance::DFLT_INSTANCE_NAME); + + if (instance) + remove_instance(instance); /* instance is deleted here. */ + + return TRUE; + } + + return FALSE; } @@ -297,10 +478,10 @@ int Instance_map::load() name and start looking for files named "my.cnf.cnf" in all default dirs. Which is not what we want. */ - if (Options::is_forced_default_file) + if (Options::Main::is_forced_default_file) { snprintf(defaults_file_arg, FN_REFLEN, "--defaults-file=%s", - Options::config_file); + Options::Main::config_file); argv_options[1]= defaults_file_arg; argv_options[2]= '\0'; @@ -314,15 +495,12 @@ int Instance_map::load() If the routine failed, we'll simply fallback to defaults in complete_initialization(). */ - if (my_search_option_files(Options::config_file, &argc, + if (my_search_option_files(Options::Main::config_file, &argc, (char ***) &argv, &args_used, process_option, (void*) this)) log_info("Falling back to compiled-in defaults"); - if (complete_initialization()) - return 1; - - return 0; + return complete_initialization(); } @@ -343,3 +521,105 @@ Instance *Instance_map::Iterator::next() return NULL; } + +const char *Instance_map::get_instance_state_name(Instance *instance) +{ + LIST *instance_node; + + if (!instance->is_configured()) + return "misconfigured"; + + if ((instance_node= guardian->find_instance_node(instance)) != NULL) + { + /* The instance is managed by Guardian: we can report precise state. */ + + return Guardian_thread::get_instance_state_name( + guardian->get_instance_state(instance_node)); + } + + /* The instance is not managed by Guardian: we can report status only. */ + + return instance->is_running() ? "online" : "offline"; +} + + +/* + Create a new configuration section for mysqld-instance in the config file. + + SYNOPSYS + create_instance_in_file() + instance_name mysqld-instance name + options options for the new mysqld-instance + + RETURN + 0 On success + ER_CONF_FILE_DOES_NOT_EXIST If config file does not exist + ER_ACCESS_OPTION_FILE If config file is not writable or some I/O + error ocurred during writing configuration +*/ + +int create_instance_in_file(const LEX_STRING *instance_name, + const Named_value_arr *options) +{ + File cnf_file; + + if (my_access(Options::Main::config_file, W_OK)) + { + log_error("Error: configuration file (%s) does not exist.", + (const char *) Options::Main::config_file); + return ER_CONF_FILE_DOES_NOT_EXIST; + } + + cnf_file= my_open(Options::Main::config_file, O_WRONLY | O_APPEND, MYF(0)); + + if (cnf_file <= 0) + { + log_error("Error: can not open configuration file (%s): %s.", + (const char *) Options::Main::config_file, + (const char *) strerror(errno)); + return ER_ACCESS_OPTION_FILE; + } + + if (my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP)) || + my_write(cnf_file, (byte*)"[", 1, MYF(MY_NABP)) || + my_write(cnf_file, (byte*)instance_name->str, instance_name->length, + MYF(MY_NABP)) || + my_write(cnf_file, (byte*)"]", 1, MYF(MY_NABP)) || + my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP))) + { + log_error("Error: can not write to configuration file (%s): %s.", + (const char *) Options::Main::config_file, + (const char *) strerror(errno)); + my_close(cnf_file, MYF(0)); + return ER_ACCESS_OPTION_FILE; + } + + for (int i= 0; options && i < options->get_size(); ++i) + { + char option_str[MAX_OPTION_STR_LEN]; + char *ptr; + int option_str_len; + Named_value option= options->get_element(i); + + ptr= strxnmov(option_str, MAX_OPTION_LEN + 1, option.get_name(), NullS); + + if (option.get_value()[0]) + ptr= strxnmov(ptr, MAX_OPTION_LEN + 2, "=", option.get_value(), NullS); + + option_str_len= ptr - option_str; + + if (my_write(cnf_file, (byte*)option_str, option_str_len, MYF(MY_NABP)) || + my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP))) + { + log_error("Error: can not write to configuration file (%s): %s.", + (const char *) Options::Main::config_file, + (const char *) strerror(errno)); + my_close(cnf_file, MYF(0)); + return ER_ACCESS_OPTION_FILE; + } + } + + my_close(cnf_file, MYF(0)); + + return 0; +} diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index d3de42f4d80..8e6d2360652 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -17,21 +17,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <my_global.h> - -#include "protocol.h" -#include "guardian.h" - #include <my_sys.h> +#include <m_string.h> #include <hash.h> #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface #endif +class Guardian_thread; class Instance; +class Named_value_arr; + extern int load_all_groups(char ***groups, const char *filename); extern void free_groups(char **groups); +extern int create_instance_in_file(const LEX_STRING *instance_name, + const Named_value_arr *options); + /* Instance_map - stores all existing instances @@ -56,22 +59,64 @@ public: }; friend class Iterator; public: - /* returns a pointer to the instance or NULL, if there is no such instance */ - Instance *find(const char *name, uint name_len); + /* + Return a pointer to the instance or NULL, if there is no such instance. + MT-NOTE: must be called under acquired lock. + */ + Instance *find(const LEX_STRING *name); + /* Clear the configuration cache and reload the configuration file. */ int flush_instances(); + + /* The operation is used to check if there is an active instance or not. */ + bool is_there_active_instance(); + void lock(); void unlock(); + int init(); + /* Process a given option and assign it to appropricate instance. This is required for the option handler, passed to my_search_option_files(). */ - int process_one_option(const char *group, const char *option); + int process_one_option(const LEX_STRING *group, const char *option); + + /* + Add an instance into the internal hash. + + MT-NOTE: the operation must be called under acquired lock. + */ + int add_instance(Instance *instance); + + /* + Remove instance from the internal hash. + + MT-NOTE: the operation must be called under acquired lock. + */ + int remove_instance(Instance *instance); + + /* + Create a new instance and register it in the internal hash. + + MT-NOTE: the operation must be called under acquired lock. + */ + int create_instance(const LEX_STRING *instance_name, + const Named_value_arr *options); Instance_map(const char *default_mysqld_path_arg); ~Instance_map(); + /* + Retrieve client state name of the given instance. + + MT-NOTE: the options must be called under acquired locks of the following + objects: + - Instance_map; + - Guardian_thread; + */ + const char *get_instance_state_name(Instance *instance); + public: const char *mysqld_path; Guardian_thread *guardian; @@ -80,7 +125,7 @@ private: /* loads options from config files */ int load(); /* inits instances argv's after all options have been loaded */ - int complete_initialization(); + bool complete_initialization(); private: enum { START_HASH_SIZE = 16 }; pthread_mutex_t LOCK_instance_map; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 8bbd362a15b..b05e40734b7 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -20,28 +20,24 @@ #include "instance_options.h" -#include "parse_output.h" -#include "buffer.h" -#include "log.h" - +#include <my_global.h> #include <my_sys.h> -#include <signal.h> #include <m_string.h> -#ifdef __WIN__ -#define NEWLINE_LEN 2 -#else -#define NEWLINE_LEN 1 -#endif +#include <signal.h> + +#include "buffer.h" +#include "instance.h" +#include "log.h" +#include "parse_output.h" +#include "priv.h" /* Create "mysqld ..." command in the buffer */ static inline int create_mysqld_command(Buffer *buf, - const char *mysqld_path_str, - uint mysqld_path_len, - const char *option, - uint option_len) + const LEX_STRING *mysqld_path, + const LEX_STRING *option) { int position= 0; @@ -50,13 +46,13 @@ static inline int create_mysqld_command(Buffer *buf, #ifdef __WIN__ buf->append(position++, "\"", 1); #endif - buf->append(position, mysqld_path_str, mysqld_path_len); - position+= mysqld_path_len; + buf->append(position, mysqld_path->str, mysqld_path->length); + position+= mysqld_path->length; #ifdef __WIN__ buf->append(position++, "\"", 1); #endif /* here the '\0' character is copied from the option string */ - buf->append(position, option, option_len); + buf->append(position, option->str, option->length + 1); return buf->is_error(); } @@ -64,6 +60,42 @@ static inline int create_mysqld_command(Buffer *buf, } +bool Instance_options::is_option_im_specific(const char *option_name) +{ + static const char *IM_SPECIFIC_OPTIONS[] = + { + "nonguarded", + "mysqld-path", + "shutdown-delay", + NULL + }; + + for (int i= 0; IM_SPECIFIC_OPTIONS[i]; ++i) + { + if (!strcmp(option_name, IM_SPECIFIC_OPTIONS[i])) + return TRUE; + } + + return FALSE; +} + + +Instance_options::Instance_options() + :mysqld_version(NULL), mysqld_socket(NULL), mysqld_datadir(NULL), + mysqld_pid_file(NULL), mysqld_port(NULL), mysqld_port_val(0), + nonguarded(NULL), shutdown_delay(NULL), shutdown_delay_val(0), + filled_default_options(0) +{ + mysqld_path.str= NULL; + mysqld_path.length= 0; + + mysqld_real_path.str= NULL; + mysqld_real_path.length= 0; + + memset(logs, 0, sizeof(logs)); +} + + /* Get compiled-in value of default_option @@ -87,13 +119,13 @@ int Instance_options::get_default_option(char *result, size_t result_len, const char *option_name) { int rc= 1; - char verbose_option[]= " --no-defaults --verbose --help"; + LEX_STRING verbose_option= + { C_STRING_WITH_SIZE(" --no-defaults --verbose --help") }; - /* reserve space fot the path + option + final '\0' */ - Buffer cmd(mysqld_path_len + sizeof(verbose_option)); + /* reserve space for the path + option + final '\0' */ + Buffer cmd(mysqld_path.length + verbose_option.length + 1); - if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len, - verbose_option, sizeof(verbose_option))) + if (create_mysqld_command(&cmd, &mysqld_path, &verbose_option)) goto err; /* +2 eats first "--" from the option string (E.g. "--datadir") */ @@ -121,21 +153,19 @@ err: int Instance_options::fill_instance_version() { - enum { MAX_VERSION_STRING_LENGTH= 160 }; - char result[MAX_VERSION_STRING_LENGTH]; - char version_option[]= " --no-defaults --version"; + char result[MAX_VERSION_LENGTH]; + LEX_STRING version_option= + { C_STRING_WITH_SIZE(" --no-defaults --version") }; int rc= 1; - Buffer cmd(mysqld_path_len + sizeof(version_option)); + Buffer cmd(mysqld_path.length + version_option.length + 1); - if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len, - version_option, sizeof(version_option))) + if (create_mysqld_command(&cmd, &mysqld_path, &version_option)) goto err; - bzero(result, MAX_VERSION_STRING_LENGTH); + bzero(result, MAX_VERSION_LENGTH); - rc= parse_output_and_get_value(cmd.buffer, "Ver", - result, MAX_VERSION_STRING_LENGTH, - GET_LINE); + rc= parse_output_and_get_value(cmd.buffer, "Ver", result, + MAX_VERSION_LENGTH, GET_LINE); if (*result != '\0') { @@ -146,6 +176,7 @@ int Instance_options::fill_instance_version() start= result; while (my_isspace(default_charset_info, *start)) ++start; + mysqld_version= strdup_root(&alloc, start); } err: @@ -178,12 +209,12 @@ err: int Instance_options::fill_mysqld_real_path() { char result[FN_REFLEN]; - char help_option[]= " --no-defaults --help"; + LEX_STRING help_option= + { C_STRING_WITH_SIZE(" --no-defaults --help") }; int rc= 1; - Buffer cmd(mysqld_path_len + sizeof(help_option)); + Buffer cmd(mysqld_path.length + help_option.length); - if (create_mysqld_command(&cmd, mysqld_path, mysqld_path_len, - help_option, sizeof(help_option))) + if (create_mysqld_command(&cmd, &mysqld_path, &help_option)) goto err; bzero(result, FN_REFLEN); @@ -198,7 +229,8 @@ int Instance_options::fill_mysqld_real_path() /* chop the path of at [OPTIONS] */ if ((options_str= strstr(result, "[OPTIONS]"))) *options_str= '\0'; - mysqld_real_path= strdup_root(&alloc, result); + mysqld_real_path.str= strdup_root(&alloc, result); + mysqld_real_path.length= strlen(mysqld_real_path.str); } err: if (rc) @@ -255,8 +287,7 @@ int Instance_options::fill_log_options() else { /* below is safe, as --datadir always has a value */ - strmake(datadir, - strchr(mysqld_datadir, '=') + 1, MAX_LOG_OPTION_LENGTH - 1); + strmake(datadir, mysqld_datadir, MAX_LOG_OPTION_LENGTH - 1); } if (gethostname(hostname,sizeof(hostname)-1) < 0) @@ -342,7 +373,6 @@ err: int Instance_options::get_pid_filename(char *result) { - const char *pid_file= mysqld_pid_file; char datadir[MAX_PATH_LEN]; if (mysqld_datadir == NULL) @@ -352,14 +382,10 @@ int Instance_options::get_pid_filename(char *result) return 1; } else - strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, - "/", NullS); - - DBUG_ASSERT(mysqld_pid_file); - pid_file= strchr(pid_file, '=') + 1; + strxnmov(datadir, MAX_PATH_LEN - 1, mysqld_datadir, "/", NullS); /* get the full path to the pidfile */ - my_load_path(result, pid_file, datadir); + my_load_path(result, mysqld_pid_file, datadir); return 0; } @@ -388,23 +414,23 @@ pid_t Instance_options::get_pid() } -int Instance_options::complete_initialization(const char *default_path, - uint instance_type) +int Instance_options::complete_initialization(const char *default_path) { + int arg_idx; const char *tmp; char *end; - if (!mysqld_path && !(mysqld_path= strdup_root(&alloc, default_path))) + if (!mysqld_path.str && !(mysqld_path.str= strdup_root(&alloc, default_path))) goto err; // it's safe to cast this to char* since this is a buffer we are allocating - end= convert_dirname((char*)mysqld_path, mysqld_path, NullS); + end= convert_dirname((char*)mysqld_path.str, mysqld_path.str, NullS); end[-1]= 0; - mysqld_path_len= strlen(mysqld_path); + mysqld_path.length= strlen(mysqld_path.str); if (mysqld_port) - mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1); + mysqld_port_val= atoi(mysqld_port); if (shutdown_delay) shutdown_delay_val= atoi(shutdown_delay); @@ -412,7 +438,7 @@ int Instance_options::complete_initialization(const char *default_path, if (!(tmp= strdup_root(&alloc, "--no-defaults"))) goto err; - if (!(mysqld_pid_file)) + if (!mysqld_pid_file) { char pidfilename[MAX_PATH_LEN]; char hostname[MAX_PATH_LEN]; @@ -421,26 +447,27 @@ int Instance_options::complete_initialization(const char *default_path, If we created only one istance [mysqld], because no config. files were found, we would like to model mysqld pid file values. */ + if (!gethostname(hostname, sizeof(hostname) - 1)) { - if (instance_type & DEFAULT_SINGLE_INSTANCE) - strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, - ".pid", NullS); + if (Instance::is_mysqld_compatible_name(&instance_name)) + strxnmov(pidfilename, MAX_PATH_LEN - 1, hostname, ".pid", NullS); else - strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, - "-", hostname, ".pid", NullS); + strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, "-", + hostname, ".pid", NullS); } else { - if (instance_type & DEFAULT_SINGLE_INSTANCE) - strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql", - ".pid", NullS); + if (Instance::is_mysqld_compatible_name(&instance_name)) + strxnmov(pidfilename, MAX_PATH_LEN - 1, "mysql", ".pid", NullS); else - strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, - ".pid", NullS); + strxnmov(pidfilename, MAX_PATH_LEN - 1, instance_name.str, ".pid", + NullS); } - add_option(pidfilename); + Named_value option((char *) "pid-file", pidfilename); + + set_option(&option); } if (get_pid_filename(pid_file_with_path)) @@ -448,20 +475,37 @@ int Instance_options::complete_initialization(const char *default_path, /* we need to reserve space for the final zero + possible default options */ if (!(argv= (char**) - alloc_root(&alloc, (options_array.elements + 1 + alloc_root(&alloc, (get_num_options() + 1 + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) goto err; + filled_default_options= 0; /* the path must be first in the argv */ - if (add_to_argv(mysqld_path)) + if (add_to_argv(mysqld_path.str)) goto err; if (add_to_argv(tmp)) goto err; - memcpy((gptr) (argv + filled_default_options), options_array.buffer, - options_array.elements*sizeof(char*)); - argv[filled_default_options + options_array.elements]= 0; + arg_idx= filled_default_options; + for (int opt_idx= 0; opt_idx < get_num_options(); ++opt_idx) + { + char option_str[MAX_OPTION_STR_LEN]; + Named_value option= get_option(opt_idx); + + if (is_option_im_specific(option.get_name())) + continue; + + char *ptr= strxnmov(option_str, MAX_OPTION_LEN + 3, "--", option.get_name(), + NullS); + + if (option.get_value()[0]) + strxnmov(ptr, MAX_OPTION_LEN + 2, "=", option.get_value(), NullS); + + argv[arg_idx++]= strdup_root(&alloc, option_str); + } + + argv[arg_idx]= 0; if (fill_log_options() || fill_mysqld_real_path() || fill_instance_version()) goto err; @@ -473,75 +517,91 @@ err: } -/* - Assigns given value to the appropriate option from the class. +bool Instance_options::set_option(Named_value *option) +{ + bool err_status; + int idx= find_option(option->get_name()); + char *option_name_str; + char *option_value_str; - SYNOPSYS - add_option() - option string with the option prefixed by -- + if (!(option_name_str= Named_value::alloc_str(option->get_name()))) + return TRUE; - DESCRIPTION + if (!(option_value_str= Named_value::alloc_str(option->get_value()))) + { + Named_value::free_str(&option_name_str); + return TRUE; + } - The method is called from the option handling routine. + Named_value option_copy(option_name_str, option_value_str); - RETURN - 0 - ok - 1 - error occured -*/ + if (idx < 0) + err_status= options.add_element(&option_copy); + else + err_status= options.replace_element(idx, &option_copy); + + if (!err_status) + update_var(option_copy.get_name(), option_copy.get_value()); + else + option_copy.free(); + + return err_status; +} -int Instance_options::add_option(const char* option) + +void Instance_options::unset_option(const char *option_name) { - char *tmp; - enum { SAVE_VALUE= 1, SAVE_WHOLE, SAVE_WHOLE_AND_ADD }; - struct selected_options_st + int idx= find_option(option_name); + + if (idx < 0) + return; /* the option has not been set. */ + + options.remove_element(idx); + + update_var(option_name, NULL); +} + + +void Instance_options::update_var(const char *option_name, + const char *option_value) +{ + struct options_st { const char *name; - uint length; - const char **value; - uint type; - } options[]= + uint name_len; + const char **var; + } options_def[]= { - {"--socket=", 9, &mysqld_socket, SAVE_WHOLE_AND_ADD}, - {"--port=", 7, &mysqld_port, SAVE_WHOLE_AND_ADD}, - {"--datadir=", 10, &mysqld_datadir, SAVE_WHOLE_AND_ADD}, - {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD}, - {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD}, - {"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE}, - {"--nonguarded", 9, &nonguarded, SAVE_WHOLE}, - {"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE}, - {NULL, 0, NULL, 0} + {"socket", 6, &mysqld_socket}, + {"port", 4, &mysqld_port}, + {"datadir", 7, &mysqld_datadir}, + {"pid-file", 8, &mysqld_pid_file}, + {"nonguarded", 10, &nonguarded}, + {"mysqld-path", 11, (const char **) &mysqld_path.str}, + {"shutdown-delay", 14, &shutdown_delay}, + {NULL, 0, NULL} }; - struct selected_options_st *selected_options; - if (!(tmp= strdup_root(&alloc, option))) - goto err; + for (options_st *opt= options_def; opt->name; ++opt) + { + if (!strncmp(opt->name, option_name, opt->name_len)) + { + *(opt->var)= option_value; + break; + } + } +} - for (selected_options= options; selected_options->name; selected_options++) - { - if (strncmp(tmp, selected_options->name, selected_options->length) == 0) - switch (selected_options->type) { - case SAVE_WHOLE_AND_ADD: - *(selected_options->value)= tmp; - insert_dynamic(&options_array,(gptr) &tmp); - return 0; - case SAVE_VALUE: - *(selected_options->value)= strchr(tmp, '=') + 1; - return 0; - case SAVE_WHOLE: - *(selected_options->value)= tmp; - return 0; - default: - break; - } - } - - /* if we haven't returned earlier we should just save the option */ - insert_dynamic(&options_array,(gptr) &tmp); - return 0; +int Instance_options::find_option(const char *option_name) +{ + for (int i= 0; i < get_num_options(); i++) + { + if (!strcmp(get_option(i).get_name(), option_name)) + return i; + } -err: - return 1; + return -1; } @@ -559,7 +619,10 @@ int Instance_options::add_to_argv(const char* option) void Instance_options::print_argv() { int i; - printf("printing out an instance %s argv:\n", instance_name); + + printf("printing out an instance %s argv:\n", + (const char *) instance_name.str); + for (i=0; argv[i] != NULL; i++) printf("argv: %s\n", argv[i]); } @@ -570,17 +633,17 @@ void Instance_options::print_argv() Return value: 0 - ok. 1 - unable to allocate memory. */ -int Instance_options::init(const char *instance_name_arg) +int Instance_options::init(const LEX_STRING *instance_name_arg) { - instance_name_len= strlen(instance_name_arg); + instance_name.length= instance_name_arg->length; init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); - if (my_init_dynamic_array(&options_array, sizeof(char*), 0, 32)) + if (options.init()) goto err; - if (!(instance_name= strmake_root(&alloc, (char*) instance_name_arg, - instance_name_len))) + if (!(instance_name.str= strmake_root(&alloc, instance_name_arg->str, + instance_name_arg->length))) goto err; return 0; @@ -593,6 +656,4 @@ err: Instance_options::~Instance_options() { free_root(&alloc, MYF(0)); - delete_dynamic(&options_array); } - diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index b316dbf00fc..c3b0a16a40d 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -18,8 +18,9 @@ #include <my_global.h> #include <my_sys.h> + #include "parse.h" -#include "portability.h" +#include "portability.h" /* for pid_t on Win32 */ #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface @@ -35,25 +36,26 @@ don't have to synchronize between threads. */ -#define USUAL_INSTANCE 0 -#define DEFAULT_SINGLE_INSTANCE 1 - class Instance_options { public: - Instance_options() : - mysqld_version(0), mysqld_socket(0), mysqld_datadir(0), - mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), - mysqld_port_val(0), mysqld_path(0), mysqld_real_path(0), - nonguarded(0), shutdown_delay(0), - shutdown_delay_val(0), filled_default_options(0) - {} + /* The operation is used to check if the option is IM-specific or not. */ + static bool is_option_im_specific(const char *option_name); + +public: + Instance_options(); ~Instance_options(); /* fills in argv */ - int complete_initialization(const char *default_path, uint instance_type); + int complete_initialization(const char *default_path); - int add_option(const char* option); - int init(const char *instance_name_arg); + bool set_option(Named_value *option); + void unset_option(const char *option_name); + + inline int get_num_options() const; + inline Named_value get_option(int idx) const; + +public: + int init(const LEX_STRING *instance_name_arg); pid_t get_pid(); int get_pid_filename(char *result); int unlink_pidfile(); @@ -66,7 +68,6 @@ public: */ enum { MAX_PATH_LEN= 512 }; enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 }; - enum { MEM_ROOT_BLOCK_SIZE= 512 }; char pid_file_with_path[MAX_PATH_LEN]; char **argv; /* @@ -77,23 +78,18 @@ public: /* We need the some options, so we store them as a separate pointers */ const char *mysqld_socket; const char *mysqld_datadir; - const char *mysqld_bind_address; const char *mysqld_pid_file; const char *mysqld_port; uint mysqld_port_val; - const char *instance_name; - uint instance_name_len; - const char *mysqld_path; - uint mysqld_path_len; - const char *mysqld_real_path; + LEX_STRING instance_name; + LEX_STRING mysqld_path; + LEX_STRING mysqld_real_path; const char *nonguarded; const char *shutdown_delay; uint shutdown_delay_val; /* log enums are defined in parse.h */ char *logs[3]; - /* this value is computed and cashed here */ - DYNAMIC_ARRAY options_array; private: int fill_log_options(); int fill_instance_version(); @@ -101,9 +97,27 @@ private: int add_to_argv(const char *option); int get_default_option(char *result, size_t result_len, const char *option_name); + + void update_var(const char *option_name, const char *option_value); + int find_option(const char *option_name); + private: uint filled_default_options; MEM_ROOT alloc; + + Named_value_arr options; }; + +inline int Instance_options::get_num_options() const +{ + return options.get_size(); +} + + +inline Named_value Instance_options::get_option(int idx) const +{ + return options.get_element(idx); +} + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 500b25bec03..da9338e28c5 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -19,21 +19,23 @@ #endif #include "listener.h" -#include "priv.h" -#include <m_string.h> + +#include <my_global.h> #include <mysql.h> #include <violite.h> + +#include <sys/stat.h> #ifndef __WIN__ #include <sys/un.h> #endif -#include <sys/stat.h> -#include "thread_registry.h" -#include "options.h" #include "instance_map.h" #include "log.h" #include "mysql_connection.h" +#include "options.h" #include "portability.h" +#include "priv.h" +#include "thread_registry.h" /* @@ -62,8 +64,7 @@ private: Listener_thread::Listener_thread(const Listener_thread_args &args) : - Listener_thread_args(args.thread_registry, args.options, args.user_map, - args.instance_map) + Listener_thread_args(args.thread_registry, args.user_map, args.instance_map) ,total_connection_count(0) ,thread_info(pthread_self()) ,num_sockets(0) @@ -234,14 +235,16 @@ int Listener_thread::create_tcp_socket() bzero(&ip_socket_address, sizeof(ip_socket_address)); ulong im_bind_addr; - if (options.bind_address != 0) + if (Options::Main::bind_address != 0) { - if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE) + im_bind_addr= (ulong) inet_addr(Options::Main::bind_address); + + if (im_bind_addr == INADDR_NONE) im_bind_addr= htonl(INADDR_ANY); } else im_bind_addr= htonl(INADDR_ANY); - uint im_port= options.port_number; + uint im_port= Options::Main::port_number; ip_socket_address.sin_family= AF_INET; ip_socket_address.sin_addr.s_addr= im_bind_addr; @@ -295,7 +298,7 @@ create_unix_socket(struct sockaddr_un &unix_socket_address) bzero(&unix_socket_address, sizeof(unix_socket_address)); unix_socket_address.sun_family= AF_UNIX; - strmake(unix_socket_address.sun_path, options.socket_file_name, + strmake(unix_socket_address.sun_path, Options::Main::socket_file_name, sizeof(unix_socket_address.sun_path)); unlink(unix_socket_address.sun_path); // in case we have stale socket file diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h index 28ccbf91731..c28ab0649d7 100644 --- a/server-tools/instance-manager/listener.h +++ b/server-tools/instance-manager/listener.h @@ -27,23 +27,19 @@ pthread_handler_t listener(void *arg); class Thread_registry; -struct Options; class User_map; class Instance_map; struct Listener_thread_args { Thread_registry &thread_registry; - const Options &options; const User_map &user_map; Instance_map &instance_map; Listener_thread_args(Thread_registry &thread_registry_arg, - const Options &options_arg, const User_map &user_map_arg, Instance_map &instance_map_arg) : thread_registry(thread_registry_arg) - ,options(options_arg) ,user_map(user_map_arg) ,instance_map(instance_map_arg) {} diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index 3f54bc0649a..a88344f0b91 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -14,14 +14,16 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> - #include "log.h" -#include "portability.h" -#include <stdarg.h> + +#include <my_global.h> #include <m_string.h> #include <my_sys.h> +#include <stdarg.h> + +#include "portability.h" /* for vsnprintf() on Windows. */ + /* TODO: - add flexible header support @@ -71,7 +73,7 @@ static inline void log(FILE *file, const char *format, va_list args) { int size= sizeof(buff_stack) * 2; buff_msg= (char*) my_malloc(size, MYF(0)); - while (true) + while (TRUE) { if (buff_msg == 0) { diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 95f9029f648..e2bb09cb9fa 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -14,39 +14,55 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> #include "manager.h" -#include "priv.h" -#include "thread_registry.h" -#include "listener.h" -#include "instance_map.h" -#include "options.h" -#include "user_map.h" -#include "log.h" -#include "guardian.h" - -#include <my_sys.h> +#include <my_global.h> #include <m_string.h> -#include <signal.h> +#include <my_sys.h> #include <thr_alarm.h> + +#include <signal.h> #ifndef __WIN__ #include <sys/wait.h> #endif +#include "exit_codes.h" +#include "guardian.h" +#include "instance_map.h" +#include "listener.h" +#include "log.h" +#include "options.h" +#include "priv.h" +#include "thread_registry.h" +#include "user_map.h" + -static int create_pid_file(const char *pid_file_name) +int create_pid_file(const char *pid_file_name, int pid) { - if (FILE *pid_file= my_fopen(pid_file_name, - O_WRONLY | O_CREAT | O_BINARY, MYF(0))) + FILE *pid_file; + + if (!(pid_file= my_fopen(pid_file_name, O_WRONLY | O_CREAT | O_BINARY, + MYF(0)))) { - fprintf(pid_file, "%d\n", (int) getpid()); - my_fclose(pid_file, MYF(0)); - return 0; + log_error("Error: can not create pid file '%s': %s (errno: %d)", + (const char *) pid_file_name, + (const char *) strerror(errno), + (int) errno); + return 1; } - log_error("can't create pid file %s: errno=%d, %s", - pid_file_name, errno, strerror(errno)); - return 1; + + if (fprintf(pid_file, "%d\n", (int) pid) <= 0) + { + log_error("Error: can not write to pid file '%s': %s (errno: %d)", + (const char *) pid_file_name, + (const char *) strerror(errno), + (int) errno); + return 1; + } + + my_fclose(pid_file, MYF(0)); + + return 0; } #ifndef __WIN__ @@ -82,14 +98,14 @@ bool have_signal; void onsignal(int signo) { - have_signal= true; + have_signal= TRUE; } void set_signals(sigset_t *set) { signal(SIGINT, onsignal); signal(SIGTERM, onsignal); - have_signal= false; + have_signal= FALSE; } int my_sigwait(const sigset_t *set, int *sig) @@ -109,10 +125,16 @@ int my_sigwait(const sigset_t *set, int *sig) listener thread, write pid file and enter into signal handling. See also comments in mysqlmanager.cc to picture general Instance Manager architecture. + + TODO: how about returning error status. */ -void manager(const Options &options) +void manager() { + int err_code; + const char *err_msg; + bool shutdown_complete= FALSE; + Thread_registry thread_registry; /* All objects created in the manager() function live as long as @@ -121,26 +143,60 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map(options.default_mysqld_path); + Instance_map instance_map(Options::Main::default_mysqld_path); Guardian_thread guardian_thread(thread_registry, &instance_map, - options.monitoring_interval); + Options::Main::monitoring_interval); - Listener_thread_args listener_args(thread_registry, options, user_map, - instance_map); + Listener_thread_args listener_args(thread_registry, user_map, instance_map); manager_pid= getpid(); instance_map.guardian= &guardian_thread; - if (instance_map.init() || user_map.init()) - return; + /* Initialize instance map. */ - if (user_map.load(options.password_file_name)) + if (instance_map.init()) + { + log_error("Error: can not initialize instance list: out of memory."); return; + } + + /* Initialize user map and load password file. */ - /* write pid file */ - if (create_pid_file(options.pid_file_name)) + if (user_map.init()) + { + log_error("Error: can not initialize user list: out of memory."); return; + } + + if ((err_code= user_map.load(Options::Main::password_file_name, &err_msg))) + { + if (err_code == ERR_PASSWORD_FILE_DOES_NOT_EXIST && + Options::Main::mysqld_safe_compatible) + { + /* + The password file does not exist, but we are running in + mysqld_safe-compatible mode. Continue, but complain in log. + */ + + log_error("Warning: password file does not exist, " + "nobody will be able to connect to Instance Manager."); + } + else + { + log_error("Error: %s.", (const char *) err_msg); + return; + } + } + + /* write Instance Manager pid file */ + + log_info("IM pid file: '%s'; PID: %d.", + (const char *) Options::Main::pid_file_name, + (int) manager_pid); + + if (create_pid_file(Options::Main::pid_file_name, manager_pid)) + return; /* necessary logging has been already done. */ sigset_t mask; set_signals(&mask); @@ -193,17 +249,23 @@ void manager(const Options &options) To work nicely with LinuxThreads, the signal thread is the first thread in the process. */ - int signo; - bool shutdown_complete; - - shutdown_complete= FALSE; - if (instance_map.flush_instances()) { - log_error("Cannot init instances repository. This might be caused by " - "the wrong config file options. For instance, missing mysqld " - "binary. Aborting."); - return; + instance_map.guardian->lock(); + instance_map.lock(); + + int flush_instances_status= instance_map.flush_instances(); + + instance_map.unlock(); + instance_map.guardian->unlock(); + + if (flush_instances_status) + { + log_error("Cannot init instances repository. This might be caused by " + "the wrong config file options. For instance, missing mysqld " + "binary. Aborting."); + return; + } } /* @@ -214,6 +276,7 @@ void manager(const Options &options) while (!shutdown_complete) { + int signo; int status= 0; if ((status= my_sigwait(&mask, &signo)) != 0) @@ -240,7 +303,7 @@ void manager(const Options &options) { if (!guardian_thread.is_stopped()) { - bool stop_instances= true; + bool stop_instances= TRUE; guardian_thread.request_shutdown(stop_instances); pthread_cond_signal(&guardian_thread.COND_guardian); } @@ -254,7 +317,7 @@ void manager(const Options &options) err: /* delete the pid file */ - my_delete(options.pid_file_name, MYF(0)); + my_delete(Options::Main::pid_file_name, MYF(0)); #ifndef __WIN__ /* free alarm structures */ @@ -262,4 +325,3 @@ err: /* don't pthread_exit to kill all threads who did not shut down in time */ #endif } - diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h index 12ed6b3b1ff..7aa4b3e1a96 100644 --- a/server-tools/instance-manager/manager.h +++ b/server-tools/instance-manager/manager.h @@ -16,8 +16,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -struct Options; +void manager(); -void manager(const Options &options); +int create_pid_file(const char *pid_file_name, int pid); #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc index a9b00b9e01f..af35af1e7b0 100644 --- a/server-tools/instance-manager/messages.cc +++ b/server-tools/instance-manager/messages.cc @@ -14,15 +14,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> #include "messages.h" +#include <my_global.h> +#include <mysql_com.h> + #include "mysqld_error.h" #include "mysql_manager_error.h" -#include <mysql_com.h> -#include <assert.h> - static const char *mysqld_error_message(unsigned sql_errno) { @@ -70,6 +69,23 @@ static const char *mysqld_error_message(unsigned sql_errno) "in the instance options"; case ER_ACCESS_OPTION_FILE: return "Cannot open the option file to edit. Check permissions"; + case ER_DROP_ACTIVE_INSTANCE: + return "Cannot drop an active instance. You should stop it first"; + case ER_CREATE_EXISTING_INSTANCE: + return "Instance already exists"; + case ER_INSTANCE_MISCONFIGURED: + return "Instance is misconfigured. Cannot start it"; + case ER_MALFORMED_INSTANCE_NAME: + return "Malformed instance name."; + case ER_INSTANCE_IS_ACTIVE: + return "The instance is active. Stop the instance first"; + case ER_THERE_IS_ACTIVE_INSTACE: + return "At least one instance is active. Stop all instances first"; + case ER_INCOMPATIBLE_OPTION: + return "Instance Manager-specific options are prohibited from being used " + "in the configuration of mysqld-compatible instances"; + case ER_CONF_FILE_DOES_NOT_EXIST: + return "Configuration file does not exist"; default: DBUG_ASSERT(0); return 0; diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index dcd1807701f..17cda3af704 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -20,22 +20,24 @@ #include "mysql_connection.h" -#include "priv.h" -#include "mysql_manager_error.h" -#include "mysqld_error.h" -#include "thread_registry.h" +#include <m_string.h> +#include <m_string.h> +#include <my_global.h> +#include <mysql_com.h> +#include <mysql.h> +#include <my_sys.h> +#include <violite.h> + +#include "command.h" #include "log.h" -#include "user_map.h" -#include "protocol.h" #include "messages.h" -#include "command.h" +#include "mysqld_error.h" +#include "mysql_manager_error.h" #include "parse.h" - -#include <mysql.h> -#include <violite.h> -#include <mysql_com.h> -#include <m_string.h> -#include <my_sys.h> +#include "priv.h" +#include "protocol.h" +#include "thread_registry.h" +#include "user_map.h" Mysql_connection_thread_args::Mysql_connection_thread_args( @@ -56,7 +58,7 @@ Mysql_connection_thread_args::Mysql_connection_thread_args( See also comments in mysqlmanager.cc to picture general Instance Manager architecture. We use conventional technique to work with classes without exceptions: - class acquires all vital resource in init(); Thus if init() succeed, + class acquires all vital resource in init(); Thus if init() succeed, a user must call cleanup(). All other methods are valid only between init() and cleanup(). */ @@ -190,8 +192,6 @@ void Mysql_connection_thread::run() int Mysql_connection_thread::check_connection() { ulong pkt_len=0; // to hold client reply length - /* maximum size of the version string */ - enum { MAX_VERSION_LENGTH= 80 }; /* buffer for the first packet */ /* packet contains: */ char buff[MAX_VERSION_LENGTH + 1 + // server version, 0-ended @@ -202,8 +202,8 @@ int Mysql_connection_thread::check_connection() char *pos= buff; ulong server_flags; - memcpy(pos, mysqlmanager_version, mysqlmanager_version_length + 1); - pos+= mysqlmanager_version_length + 1; + memcpy(pos, mysqlmanager_version.str, mysqlmanager_version.length + 1); + pos+= mysqlmanager_version.length + 1; int4store((uchar*) pos, connection_id); pos+= 4; @@ -271,12 +271,14 @@ int Mysql_connection_thread::check_connection() const char *user= pos; const char *password= strend(user)+1; ulong password_len= *password++; + LEX_STRING user_name= { (char *) user, password - user - 2 }; + if (password_len != SCRAMBLE_LENGTH) { net_send_error(&net, ER_ACCESS_DENIED_ERROR); return 1; } - if (user_map.authenticate(user, password-user-2, password, scramble)) + if (user_map.authenticate(&user_name, password, scramble)) { net_send_error(&net, ER_ACCESS_DENIED_ERROR); return 1; @@ -312,7 +314,7 @@ int Mysql_connection_thread::do_command() packet= (char*) net.read_pos; enum enum_server_command command= (enum enum_server_command) (uchar) *packet; - log_info("connection %d: packet_length=%d, command=%d", + log_info("connection %d: packet_length=%d, command=%d", connection_id, packet_length, command); return dispatch_command(command, packet + 1, packet_length - 1); } @@ -336,7 +338,7 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command, if (Command *command= parse_command(&instance_map, packet)) { int res= 0; - log_info("query for connection %d successefully parsed",connection_id); + log_info("query for connection %d successfully parsed",connection_id); res= command->execute(&net, connection_id); delete command; if (!res) @@ -380,5 +382,5 @@ pthread_handler_t mysql_connection(void *arg) } /* - vim: fdm=marker + vim: fdm=marker */ diff --git a/server-tools/instance-manager/mysql_manager_error.h b/server-tools/instance-manager/mysql_manager_error.h index ff782923a8e..373c7d798a0 100644 --- a/server-tools/instance-manager/mysql_manager_error.h +++ b/server-tools/instance-manager/mysql_manager_error.h @@ -29,5 +29,13 @@ #define ER_ACCESS_OPTION_FILE 3008 #define ER_OFFSET_ERROR 3009 #define ER_READ_FILE 3010 +#define ER_DROP_ACTIVE_INSTANCE 3011 +#define ER_CREATE_EXISTING_INSTANCE 3012 +#define ER_INSTANCE_MISCONFIGURED 3013 +#define ER_MALFORMED_INSTANCE_NAME 3014 +#define ER_INSTANCE_IS_ACTIVE 3015 +#define ER_THERE_IS_ACTIVE_INSTACE 3016 +#define ER_INCOMPATIBLE_OPTION 3017 +#define ER_CONF_FILE_DOES_NOT_EXIST 3018 #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */ diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index d0b2cf2666c..177b761b419 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -15,25 +15,30 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <my_global.h> -#include "manager.h" - -#include "options.h" -#include "log.h" - #include <my_sys.h> + #include <string.h> #include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> + #ifndef __WIN__ #include <pwd.h> #include <grp.h> #include <sys/wait.h> #endif -#include <sys/types.h> -#include <sys/stat.h> + +#include "log.h" +#include "manager.h" +#include "options.h" +#include "user_management_commands.h" + #ifdef __WIN__ -#include "windowsservice.h" +#include "IMService.h" +#include "WindowsService.h" #endif + /* Few notes about Instance Manager architecture: Instance Manager consisits of two processes: the angel process, and the @@ -59,13 +64,12 @@ */ static void init_environment(char *progname); + #ifndef __WIN__ static void daemonize(const char *log_file_name); -static void angel(const Options &options); +static void angel(); static struct passwd *check_user(const char *user); static int set_user(const char *user, struct passwd *user_info); -#else -int HandleServiceOptions(Options options); #endif @@ -81,41 +85,61 @@ int main(int argc, char *argv[]) { int return_value= 1; init_environment(argv[0]); - Options options; - if (options.load(argc, argv)) - goto err; + if ((return_value= Options::load(argc, argv))) + goto main_end; + + if (Options::User_management::cmd) + { + return_value= Options::User_management::cmd->execute(); + + goto main_end; + } #ifndef __WIN__ + struct passwd *user_info; - if ((user_info= check_user(options.user))) + if ((user_info= check_user(Options::Daemon::user))) { - if (set_user(options.user, user_info)) - goto err; + if (set_user(Options::Daemon::user, user_info)) + { + return_value= 1; + goto main_end; + } } - if (options.run_as_service) + if (Options::Daemon::run_as_service) { /* forks, and returns only in child */ - daemonize(options.log_file_name); + daemonize(Options::Daemon::log_file_name); /* forks again, and returns only in child: parent becomes angel */ - angel(options); + angel(); } + + manager(); + #else - if (!options.stand_alone) + + if (!Options::Service::stand_alone) { - if (HandleServiceOptions(options)) - goto err; + if (HandleServiceOptions()) + { + return_value= 1; + goto main_end; + } } else + { + manager(); + } + #endif - manager(options); return_value= 0; -err: - options.cleanup(); +main_end: + Options::cleanup(); my_end(0); return return_value; } @@ -200,7 +224,7 @@ static void init_environment(char *progname) MY_INIT(progname); log_init(); umask(0117); - srand(time(0)); + srand((unsigned int) time(0)); } @@ -298,7 +322,7 @@ void terminate(int signo) Angel process will exit silently if mysqlmanager exits normally. */ -static void angel(const Options &options) +static void angel() { /* install signal handlers */ sigset_t zeromask; // to sigsuspend in parent @@ -338,6 +362,14 @@ spawn: /* Here we return to main, and fall into manager */ break; default: // parent, success + pid= getpid(); /* Get our pid. */ + + log_info("Angel pid file: '%s'; PID: %d.", + (const char *) Options::Daemon::angel_pid_file_name, + (int) pid); + + create_pid_file(Options::Daemon::angel_pid_file_name, pid); + while (child_status == CHILD_OK && is_terminated == 0) sigsuspend(&zeromask); diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 85fea76c079..31ce5b00190 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -20,70 +20,128 @@ #include "options.h" -#include "priv.h" -#include "portability.h" +#include <my_global.h> #include <my_sys.h> #include <my_getopt.h> -#include <m_string.h> #include <mysql_com.h> +#include "exit_codes.h" +#include "log.h" +#include "portability.h" +#include "priv.h" +#include "user_management_commands.h" + #define QUOTE2(x) #x #define QUOTE(x) QUOTE2(x) #ifdef __WIN__ -char Options::install_as_service; -char Options::remove_service; -char Options::stand_alone; -char windows_config_file[FN_REFLEN]; -char default_password_file_name[FN_REFLEN]; -char default_log_file_name[FN_REFLEN]; -const char *Options::config_file= windows_config_file; -#else -char Options::run_as_service; -const char *Options::user= 0; /* No default value */ -const char *default_password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); -const char *default_log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME); -const char *Options::config_file= QUOTE(DEFAULT_CONFIG_FILE); + +/* Define holders for default values. */ + +static char win_dflt_config_file_name[FN_REFLEN]; +static char win_dflt_password_file_name[FN_REFLEN]; +static char win_dflt_pid_file_name[FN_REFLEN]; +static char win_dflt_socket_file_name[FN_REFLEN]; + +static char win_dflt_mysqld_path[FN_REFLEN]; + +/* Define and initialize Windows-specific options. */ + +my_bool Options::Service::install_as_service; +my_bool Options::Service::remove_service; +my_bool Options::Service::stand_alone; + +const char *Options::Main::config_file= win_dflt_config_file_name; +const char *Options::Main::password_file_name= win_dflt_password_file_name; +const char *Options::Main::pid_file_name= win_dflt_pid_file_name; +const char *Options::Main::socket_file_name= win_dflt_socket_file_name; + +const char *Options::Main::default_mysqld_path= win_dflt_mysqld_path; + +static int setup_windows_defaults(); + +#else /* UNIX */ + +/* Define and initialize UNIX-specific options. */ + +my_bool Options::Daemon::run_as_service= FALSE; +const char *Options::Daemon::log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME); +const char *Options::Daemon::user= NULL; /* No default value */ +const char *Options::Daemon::angel_pid_file_name= NULL; + +const char *Options::Main::config_file= QUOTE(DEFAULT_CONFIG_FILE); +const char * +Options::Main::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); +const char *Options::Main::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); +const char *Options::Main::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); + +const char *Options::Main::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); + #endif -const char *Options::log_file_name= default_log_file_name; -const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); -const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); -const char *Options::password_file_name= default_password_file_name; -const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); -const char *Options::bind_address= 0; /* No default value */ -uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; -uint Options::port_number= DEFAULT_PORT; -/* just to declare */ + +/* Remember if the config file was forced. */ + +bool Options::Main::is_forced_default_file= FALSE; + +/* Define and initialize common options. */ + +const char *Options::Main::bind_address= NULL; /* No default value */ +uint Options::Main::monitoring_interval= DEFAULT_MONITORING_INTERVAL; +uint Options::Main::port_number= DEFAULT_PORT; +my_bool Options::Main::mysqld_safe_compatible= FALSE; + +/* Options::User_management */ + +char *Options::User_management::user_name= NULL; +char *Options::User_management::password= NULL; + +User_management_cmd *Options::User_management::cmd= NULL; + +/* Private members. */ + char **Options::saved_argv= NULL; -/* Remember if the config file was forced */ -bool Options::is_forced_default_file= 0; + #ifndef DBUG_OFF -const char *Options::default_dbug_option= "d:t:i:O,im.trace"; +const char *Options::Debug::config_str= "d:t:i:O,im.trace"; #endif +static const char * const ANGEL_PID_FILE_SUFFIX= ".angel.pid"; +static const int ANGEL_PID_FILE_SUFFIX_LEN= strlen(ANGEL_PID_FILE_SUFFIX); + /* List of options, accepted by the instance manager. List must be closed with empty option. */ enum options { + OPT_PASSWD= 'P', + OPT_USERNAME= 'u', + OPT_PASSWORD= 'p', OPT_LOG= 256, OPT_PID_FILE, OPT_SOCKET, OPT_PASSWORD_FILE, OPT_MYSQLD_PATH, -#ifndef __WIN__ - OPT_RUN_AS_SERVICE, - OPT_USER, -#else +#ifdef __WIN__ OPT_INSTALL_SERVICE, OPT_REMOVE_SERVICE, OPT_STAND_ALONE, +#else + OPT_RUN_AS_SERVICE, + OPT_USER, + OPT_ANGEL_PID_FILE, #endif OPT_MONITORING_INTERVAL, OPT_PORT, OPT_WAIT_TIMEOUT, - OPT_BIND_ADDRESS + OPT_BIND_ADDRESS, + OPT_ADD_USER, + OPT_DROP_USER, + OPT_EDIT_USER, + OPT_CLEAN_PASSWORD_FILE, + OPT_CHECK_PASSWORD_FILE, + OPT_LIST_USERS, + OPT_MYSQLD_SAFE_COMPATIBLE }; static struct my_option my_long_options[] = @@ -91,94 +149,158 @@ static struct my_option my_long_options[] = { "help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "add-user", OPT_ADD_USER, + "Add a user to the password file", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + +#ifndef __WIN__ + { "angel-pid-file", OPT_ANGEL_PID_FILE, "Pid file for angel process.", + (gptr *) &Options::Daemon::angel_pid_file_name, + (gptr *) &Options::Daemon::angel_pid_file_name, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, +#endif + { "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.", - (gptr *) &Options::bind_address, (gptr *) &Options::bind_address, + (gptr *) &Options::Main::bind_address, + (gptr *) &Options::Main::bind_address, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "check-password-file", OPT_CHECK_PASSWORD_FILE, + "Check the password file for consistency", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + + { "clean-password-file", OPT_CLEAN_PASSWORD_FILE, + "Clean the password file", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + #ifndef DBUG_OFF {"debug", '#', "Debug log.", - (gptr*) &Options::default_dbug_option, (gptr*) &Options::default_dbug_option, + (gptr *) &Options::Debug::config_str, + (gptr *) &Options::Debug::config_str, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif { "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL" " Server binary.", - (gptr *) &Options::default_mysqld_path, - (gptr *) &Options::default_mysqld_path, + (gptr *) &Options::Main::default_mysqld_path, + (gptr *) &Options::Main::default_mysqld_path, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }, + + { "drop-user", OPT_DROP_USER, + "Drop existing user from the password file", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + + { "edit-user", OPT_EDIT_USER, + "Edit existing user in the password file", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + #ifdef __WIN__ { "install", OPT_INSTALL_SERVICE, "Install as system service.", - (gptr *) &Options::install_as_service, (gptr*) &Options::install_as_service, + (gptr *) &Options::Service::install_as_service, + (gptr *) &Options::Service::install_as_service, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, #endif + { "list-users", OPT_LIST_USERS, + "Print out a list of registered users", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + +#ifndef __WIN__ { "log", OPT_LOG, "Path to log file. Used only with --run-as-service.", - (gptr *) &Options::log_file_name, (gptr *) &Options::log_file_name, + (gptr *) &Options::Daemon::log_file_name, + (gptr *) &Options::Daemon::log_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, +#endif { "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor" " instances in seconds.", - (gptr *) &Options::monitoring_interval, - (gptr *) &Options::monitoring_interval, + (gptr *) &Options::Main::monitoring_interval, + (gptr *) &Options::Main::monitoring_interval, 0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL, 0, 0, 0, 0, 0 }, - { "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0, - GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "mysqld-safe-compatible", OPT_MYSQLD_SAFE_COMPATIBLE, + "Start Instance Manager in mysqld_safe compatible manner", + (gptr *) &Options::Main::mysqld_safe_compatible, + (gptr *) &Options::Main::mysqld_safe_compatible, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, - { "password-file", OPT_PASSWORD_FILE, "Look for Instance Manager users" - " and passwords here.", - (gptr *) &Options::password_file_name, - (gptr *) &Options::password_file_name, + { "passwd", OPT_PASSWD, + "Prepare an entry for the password file and exit.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + + { "password", OPT_PASSWORD, "Password to update the password file", + (gptr *) &Options::User_management::password, + (gptr *) &Options::User_management::password, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "password-file", OPT_PASSWORD_FILE, + "Look for Instance Manager users and passwords here.", + (gptr *) &Options::Main::password_file_name, + (gptr *) &Options::Main::password_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, { "pid-file", OPT_PID_FILE, "Pid file to use.", - (gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name, + (gptr *) &Options::Main::pid_file_name, + (gptr *) &Options::Main::pid_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, { "port", OPT_PORT, "Port number to use for connections", - (gptr *) &Options::port_number, (gptr *) &Options::port_number, + (gptr *) &Options::Main::port_number, + (gptr *) &Options::Main::port_number, 0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 }, #ifdef __WIN__ { "remove", OPT_REMOVE_SERVICE, "Remove system service.", - (gptr *)&Options::remove_service, (gptr*) &Options::remove_service, + (gptr *) &Options::Service::remove_service, + (gptr *) &Options::Service::remove_service, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0}, #else { "run-as-service", OPT_RUN_AS_SERVICE, - "Daemonize and start angel process.", (gptr *) &Options::run_as_service, + "Daemonize and start angel process.", + (gptr *) &Options::Daemon::run_as_service, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, #endif { "socket", OPT_SOCKET, "Socket file to use for connection.", - (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, + (gptr *) &Options::Main::socket_file_name, + (gptr *) &Options::Main::socket_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, #ifdef __WIN__ { "standalone", OPT_STAND_ALONE, "Run the application in stand alone mode.", - (gptr *)&Options::stand_alone, (gptr*) &Options::stand_alone, + (gptr *) &Options::Service::stand_alone, + (gptr *) &Options::Service::stand_alone, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0}, #else { "user", OPT_USER, "Username to start mysqlmanager", - (gptr *) &Options::user, - (gptr *) &Options::user, + (gptr *) &Options::Daemon::user, + (gptr *) &Options::Daemon::user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, #endif + { "username", OPT_USERNAME, + "Username to update the password file", + (gptr *) &Options::User_management::user_name, + (gptr *) &Options::User_management::user_name, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, { "wait-timeout", OPT_WAIT_TIMEOUT, "The number of seconds IM waits " "for activity on a connection before closing it.", - (gptr *) &net_read_timeout, (gptr *) &net_read_timeout, 0, GET_ULONG, - REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 }, + (gptr *) &net_read_timeout, + (gptr *) &net_read_timeout, + 0, GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 } }; static void version() { - printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version, + printf("%s Ver %s for %s on %s\n", my_progname, + (const char *) mysqlmanager_version.str, SYSTEM_TYPE, MACHINE_TYPE); } @@ -206,37 +328,6 @@ static void usage() } -static void passwd() -{ - char user[1024], *p; - const char *pw1, *pw2; - char pw1msg[]= "Enter password: "; - char pw2msg[]= "Re-type password: "; - char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1]; - - fprintf(stderr, "Creating record for new user.\n"); - fprintf(stderr, "Enter user name: "); - if (!fgets(user, sizeof(user), stdin)) - { - fprintf(stderr, "Unable to read user.\n"); - return; - } - if ((p= strchr(user, '\n'))) *p= 0; - - pw1= get_tty_password(pw1msg); - pw2= get_tty_password(pw2msg); - - if (strcmp(pw1, pw2)) - { - fprintf(stderr, "Sorry, passwords do not match.\n"); - return; - } - - make_scrambled_password(crypted_pw, pw1); - printf("%s:%s\n", user, crypted_pw); -} - - C_MODE_START static my_bool @@ -248,16 +339,52 @@ get_one_option(int optid, case 'V': version(); exit(0); - case 'P': - passwd(); - exit(0); + case OPT_PASSWD: + case OPT_ADD_USER: + case OPT_DROP_USER: + case OPT_EDIT_USER: + case OPT_CLEAN_PASSWORD_FILE: + case OPT_CHECK_PASSWORD_FILE: + case OPT_LIST_USERS: + if (Options::User_management::cmd) + { + fprintf(stderr, "Error: only one password-management command " + "can be specified at a time.\n"); + exit(ERR_INVALID_USAGE); + } + + switch (optid) { + case OPT_PASSWD: + Options::User_management::cmd= new Passwd_cmd(); + break; + case OPT_ADD_USER: + Options::User_management::cmd= new Add_user_cmd(); + break; + case OPT_DROP_USER: + Options::User_management::cmd= new Drop_user_cmd(); + break; + case OPT_EDIT_USER: + Options::User_management::cmd= new Edit_user_cmd(); + break; + case OPT_CLEAN_PASSWORD_FILE: + Options::User_management::cmd= new Clean_db_cmd(); + break; + case OPT_CHECK_PASSWORD_FILE: + Options::User_management::cmd= new Check_db_cmd(); + break; + case OPT_LIST_USERS: + Options::User_management::cmd= new List_users_cmd(); + break; + } + + break; case '?': usage(); exit(0); case '#': #ifndef DBUG_OFF - DBUG_SET(argument ? argument : Options::default_dbug_option); - DBUG_SET_INITIAL(argument ? argument : Options::default_dbug_option); + DBUG_SET(argument ? argument : Options::Debug::config_str); + DBUG_SET_INITIAL(argument ? argument : Options::Debug::config_str); #endif break; } @@ -283,8 +410,8 @@ int Options::load(int argc, char **argv) { if (is_prefix(argv[1], "--defaults-file=")) { - Options::config_file= strchr(argv[1], '=') + 1; - Options::is_forced_default_file= 1; + Main::config_file= strchr(argv[1], '=') + 1; + Main::is_forced_default_file= TRUE; } if (is_prefix(argv[1], "--defaults-extra-file=") || is_prefix(argv[1], "--no-defaults")) @@ -293,59 +420,139 @@ int Options::load(int argc, char **argv) fprintf(stderr, "The --defaults-extra-file and --no-defaults options" " are not supported by\n" "Instance Manager. Program aborted.\n"); - goto err; + return ERR_INVALID_USAGE; } } #ifdef __WIN__ if (setup_windows_defaults()) - goto err; + { + fprintf(stderr, "Internal error: could not setup default values.\n"); + return ERR_OUT_OF_MEMORY; + } #endif + /* load_defaults will reset saved_argv with a new allocated list */ saved_argv= argv; /* config-file options are prepended to command-line ones */ - load_defaults(config_file, default_groups, &argc, - &saved_argv); - if ((handle_options(&argc, &saved_argv, my_long_options, - get_one_option)) != 0) - goto err; + log_info("Loading config file '%s'...", + (const char *) Main::config_file); - return 0; + load_defaults(Main::config_file, default_groups, &argc, &saved_argv); + + if ((handle_options(&argc, &saved_argv, my_long_options, get_one_option))) + return ERR_INVALID_USAGE; + + if (!User_management::cmd && + (User_management::user_name || User_management::password)) + { + fprintf(stderr, + "--username and/or --password options have been specified, " + "but no password-management command has been given.\n"); + return ERR_INVALID_USAGE; + } + +#ifndef __WIN__ + if (Options::Daemon::run_as_service) + { + if (Options::Daemon::angel_pid_file_name == NULL) + { + /* + Calculate angel pid file on the IM pid file basis: replace the + extension (everything after the last dot) of the pid file basename to + '.angel.pid'. + */ + + char *angel_pid_file_name; + char *base_name_ptr; + char *ext_ptr; + + angel_pid_file_name= + (char *) malloc(strlen(Options::Main::pid_file_name) + + ANGEL_PID_FILE_SUFFIX_LEN); + + strcpy(angel_pid_file_name, Options::Main::pid_file_name); + + base_name_ptr= strrchr(angel_pid_file_name, '/'); + + if (!base_name_ptr) + base_name_ptr= angel_pid_file_name + 1; + + ext_ptr= strrchr(base_name_ptr, '.'); + if (ext_ptr) + *ext_ptr= 0; + + strcat(angel_pid_file_name, ANGEL_PID_FILE_SUFFIX); -err: - return 1; + Options::Daemon::angel_pid_file_name= angel_pid_file_name; + } + else + { + Options::Daemon::angel_pid_file_name= + strdup(Options::Daemon::angel_pid_file_name); + } + } +#endif + + return 0; } void Options::cleanup() { - /* free_defaults returns nothing */ - if (Options::saved_argv != NULL) - free_defaults(Options::saved_argv); + if (saved_argv) + free_defaults(saved_argv); + + delete User_management::cmd; + +#ifndef __WIN__ + if (Options::Daemon::run_as_service) + free((void *) Options::Daemon::angel_pid_file_name); +#endif } #ifdef __WIN__ -int Options::setup_windows_defaults() +static int setup_windows_defaults() { - if (!GetModuleFileName(NULL, default_password_file_name, - sizeof(default_password_file_name))) - return 1; - char *filename= strstr(default_password_file_name, ".exe"); - strcpy(filename, ".passwd"); - - if (!GetModuleFileName(NULL, default_log_file_name, - sizeof(default_log_file_name))) + char module_full_name[FN_REFLEN]; + char dir_name[FN_REFLEN]; + char base_name[FN_REFLEN]; + char im_name[FN_REFLEN]; + char *base_name_ptr; + char *ptr; + + /* Determine dirname and basename. */ + + if (!GetModuleFileName(NULL, module_full_name, sizeof (module_full_name)) || + !GetFullPathName(module_full_name, sizeof (dir_name), dir_name, + &base_name_ptr)) + { return 1; - filename= strstr(default_log_file_name, ".exe"); - strcpy(filename, ".log"); + } + + strmake(base_name, base_name_ptr, FN_REFLEN); + *base_name_ptr= 0; + + strmake(im_name, base_name, FN_REFLEN); + ptr= strrchr(im_name, '.'); + + if (!ptr) + return 1; + + *ptr= 0; + + /* Initialize the defaults. */ + + strxmov(win_dflt_config_file_name, dir_name, DFLT_CONFIG_FILE_NAME, NullS); + strxmov(win_dflt_mysqld_path, dir_name, DFLT_MYSQLD_PATH, NullS); + strxmov(win_dflt_password_file_name, dir_name, im_name, DFLT_PASSWD_FILE_EXT, + NullS); + strxmov(win_dflt_pid_file_name, dir_name, im_name, DFLT_PID_FILE_EXT, NullS); + strxmov(win_dflt_socket_file_name, dir_name, im_name, DFLT_SOCKET_FILE_EXT, + NullS); - if (!GetModuleFileName(NULL, windows_config_file, - sizeof(windows_config_file))) - return 1; - char *slash= strrchr(windows_config_file, '\\'); - strcpy(slash, "\\my.ini"); return 0; } diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index 06818288606..5c54ff201b2 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -17,49 +17,87 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* - Options - all possible options for the instance manager grouped in one - struct. + Options - all possible command-line options for the Instance Manager grouped + in one struct. */ + #include <my_global.h> #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface #endif +class User_management_cmd; + struct Options { -#ifdef __WIN__ - static char install_as_service; - static char remove_service; - static char stand_alone; -#else - static char run_as_service; /* handle_options doesn't support bool */ - static const char *user; -#endif - static bool is_forced_default_file; - static const char *log_file_name; - static const char *pid_file_name; - static const char *socket_file_name; - static const char *password_file_name; - static const char *default_mysqld_path; - /* the option which should be passed to process_default_option_files */ - static uint monitoring_interval; - static uint port_number; - static const char *bind_address; - static const char *config_file; + /* + NOTE: handle_options() expects value of my_bool type for GET_BOOL + accessor (i.e. bool must not be used). + */ - /* argv pointer returned by load_defaults() to be used by free_defaults() */ - static char **saved_argv; + struct User_management + { + static User_management_cmd *cmd; + + static char *user_name; + static char *password; + }; + + struct Main + { + /* this is not an option parsed by handle_options(). */ + static bool is_forced_default_file; + + static const char *pid_file_name; + static const char *socket_file_name; + static const char *password_file_name; + static const char *default_mysqld_path; + static uint monitoring_interval; + static uint port_number; + static const char *bind_address; + static const char *config_file; + static my_bool mysqld_safe_compatible; + }; #ifndef DBUG_OFF - static const char *default_dbug_option; + struct Debug + { + static const char *config_str; + }; #endif - int load(int argc, char **argv); - void cleanup(); -#ifdef __WIN__ - int setup_windows_defaults(); +#ifndef __WIN__ + + struct Daemon + { + static my_bool run_as_service; + static const char *log_file_name; + static const char *user; + static const char *angel_pid_file_name; + }; + +#else + + struct Service + { + static my_bool install_as_service; + static my_bool remove_service; + static my_bool stand_alone; + }; + #endif + +public: + static int load(int argc, char **argv); + static void cleanup(); + +private: + Options(); /* Deny instantiation of this class. */ + +private: + /* argv pointer returned by load_defaults() to be used by free_defaults() */ + static char **saved_argv; }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index 14b3db16b45..4e931488fce 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -17,12 +17,12 @@ #include "parse.h" #include "commands.h" -#include <string.h> - enum Token { - TOK_ERROR= 0, /* Encodes the "ERROR" word, it doesn't indicate error. */ + TOK_CREATE= 0, + TOK_DROP, + TOK_ERROR, /* Encodes the "ERROR" word, it doesn't indicate error. */ TOK_FILES, TOK_FLUSH, TOK_GENERAL, @@ -50,6 +50,8 @@ struct tokens_st static struct tokens_st tokens[]= { + {6, "CREATE"}, + {4, "DROP"}, {5, "ERROR"}, {5, "FILES"}, {5, "FLUSH"}, @@ -67,6 +69,37 @@ static struct tokens_st tokens[]= { {5, "UNSET"} }; +/************************************************************************/ + +Named_value_arr::Named_value_arr() : + initialized(FALSE) +{ +} + + +bool Named_value_arr::init() +{ + if (my_init_dynamic_array(&arr, sizeof(Named_value), 0, 32)) + return TRUE; + + initialized= TRUE; + + return FALSE; +} + + +Named_value_arr::~Named_value_arr() +{ + if (!initialized) + return; + + for (int i= 0; i < get_size(); ++i) + get_element(i).free(); + + delete_dynamic(&arr); +} + +/************************************************************************/ /* Returns token no if word corresponds to some token, otherwise returns @@ -104,53 +137,200 @@ Token shift_token(const char **text, uint *word_len) } -int get_text_id(const char **text, uint *word_len, const char **id) +int get_text_id(const char **text, LEX_STRING *token) { - get_word(text, word_len); - if (*word_len == 0) + get_word(text, &token->length); + if (token->length == 0) return 1; - *id= *text; + token->str= (char *) *text; return 0; } +static bool parse_long(const LEX_STRING *token, long *value) +{ + int err_code; + char *end_ptr= token->str + token->length; + + *value= my_strtoll10(token->str, &end_ptr, &err_code); + + return err_code != 0; +} + + +bool parse_option_value(const char *text, uint *text_len, char **value) +{ + char beginning_quote; + const char *text_start_ptr; + char *v; + bool escape_mode= FALSE; + + if (!*text || (*text != '\'' && *text != '"')) + return TRUE; /* syntax error: string expected. */ + + beginning_quote= *text; + + ++text; /* skip the beginning quote. */ + + text_start_ptr= text; + + if (!(v= Named_value::alloc_str(text))) + return TRUE; + + *value= v; + + while (TRUE) + { + if (!*text) + { + Named_value::free_str(value); + return TRUE; /* syntax error: missing terminating ' character. */ + } + + if (*text == '\n' || *text == '\r') + { + Named_value::free_str(value); + return TRUE; /* syntax error: option value should be a single line. */ + } + + if (!escape_mode && *text == beginning_quote) + break; + + if (escape_mode) + { + switch (*text) + { + case 'b': /* \b -- backspace */ + if (v > *value) + --v; + break; + + case 't': /* \t -- tab */ + *v= '\t'; + ++v; + break; + + case 'n': /* \n -- newline */ + *v= '\n'; + ++v; + break; + + case 'r': /* \r -- carriage return */ + *v= '\r'; + ++v; + break; + + case '\\': /* \\ -- back slash */ + *v= '\\'; + ++v; + break; + + case 's': /* \s -- space */ + *v= ' '; + ++v; + break; + + default: /* Unknown escape sequence. Treat as error. */ + Named_value::free_str(value); + return TRUE; + } + + escape_mode= FALSE; + } + else + { + if (*text == '\\') + { + escape_mode= TRUE; + } + else + { + *v= *text; + ++v; + } + } + + ++text; + } + + *v= 0; + + /* "2" below stands for beginning and ending quotes. */ + *text_len= text - text_start_ptr + 2; + + return FALSE; +} + + +void skip_spaces(const char **text) +{ + while (**text && my_isspace(default_charset_info, **text)) + ++(*text); +} + + Command *parse_command(Instance_map *map, const char *text) { uint word_len; - const char *instance_name; - uint instance_name_len; - const char *option; - uint option_len; - const char *option_value; - uint option_value_len; - const char *log_size; + LEX_STRING instance_name; Command *command; const char *saved_text= text; - bool skip= false; - const char *tmp; Token tok1= shift_token(&text, &word_len); switch (tok1) { case TOK_START: // fallthrough case TOK_STOP: + case TOK_CREATE: + case TOK_DROP: if (shift_token(&text, &word_len) != TOK_INSTANCE) goto syntax_error; get_word(&text, &word_len); if (word_len == 0) goto syntax_error; - instance_name= text; - instance_name_len= word_len; + instance_name.str= (char *) text; + instance_name.length= word_len; text+= word_len; - /* it should be the end of command */ - get_word(&text, &word_len, NONSPACE); - if (word_len) - goto syntax_error; - if (tok1 == TOK_START) - command= new Start_instance(map, instance_name, instance_name_len); + if (tok1 == TOK_CREATE) + { + Create_instance *cmd= new Create_instance(map, &instance_name); + + if (!cmd) + return NULL; /* Report ER_OUT_OF_RESOURCES. */ + + if (cmd->init(&text)) + { + delete cmd; + goto syntax_error; + } + + command= cmd; + } else - command= new Stop_instance(map, instance_name, instance_name_len); + { + /* it should be the end of command */ + get_word(&text, &word_len, NONSPACE); + if (word_len) + goto syntax_error; + } + + switch (tok1) { + case TOK_START: + command= new Start_instance(map, &instance_name); + break; + case TOK_STOP: + command= new Stop_instance(map, &instance_name); + break; + case TOK_CREATE: + ; /* command already initialized. */ + break; + case TOK_DROP: + command= new Drop_instance(map, &instance_name); + break; + default: /* this is impossible, but nevertheless... */ + DBUG_ASSERT(0); + } break; case TOK_FLUSH: if (shift_token(&text, &word_len) != TOK_INSTANCES) @@ -163,53 +343,28 @@ Command *parse_command(Instance_map *map, const char *text) command= new Flush_instances(map); break; case TOK_UNSET: - skip= true; case TOK_SET: + { + Abstract_option_cmd *cmd; - if (get_text_id(&text, &instance_name_len, &instance_name)) - goto syntax_error; - text+= instance_name_len; - - /* the next token should be a dot */ - get_word(&text, &word_len); - if (*text != '.') - goto syntax_error; - text++; + if (tok1 == TOK_SET) + cmd= new Set_option(map); + else + cmd= new Unset_option(map); - get_word(&text, &option_len, NONSPACE); - option= text; - if ((tmp= strchr(text, '=')) != NULL) - option_len= tmp - text; - text+= option_len; + if (!cmd) + return NULL; /* Report ER_OUT_OF_RESOURCES. */ - get_word(&text, &word_len); - if (*text == '=') - { - text++; /* skip '=' */ - get_word(&text, &option_value_len, NONSPACE); - option_value= text; - text+= option_value_len; - } - else - { - option_value= ""; - option_value_len= 0; - } + if (cmd->init(&text)) + { + delete cmd; + goto syntax_error; + } - /* should be empty */ - get_word(&text, &word_len, NONSPACE); - if (word_len) - goto syntax_error; + command= cmd; - if (skip) - command= new Unset_option(map, instance_name, instance_name_len, - option, option_len, option_value, - option_value_len); - else - command= new Set_option(map, instance_name, instance_name_len, - option, option_len, option_value, - option_value_len); - break; + break; + } case TOK_SHOW: switch (shift_token(&text, &word_len)) { case TOK_INSTANCES: @@ -222,30 +377,35 @@ Command *parse_command(Instance_map *map, const char *text) switch (Token tok2= shift_token(&text, &word_len)) { case TOK_OPTIONS: case TOK_STATUS: - if (get_text_id(&text, &instance_name_len, &instance_name)) + if (get_text_id(&text, &instance_name)) goto syntax_error; - text+= instance_name_len; + text+= instance_name.length; /* check that this is the end of the command */ get_word(&text, &word_len, NONSPACE); if (word_len) goto syntax_error; if (tok2 == TOK_STATUS) - command= new Show_instance_status(map, instance_name, - instance_name_len); + command= new Show_instance_status(map, &instance_name); else - command= new Show_instance_options(map, instance_name, - instance_name_len); + command= new Show_instance_options(map, &instance_name); break; default: goto syntax_error; } break; default: - instance_name= text - word_len; - instance_name_len= word_len; - if (instance_name_len) + instance_name.str= (char *) text - word_len; + instance_name.length= word_len; + if (instance_name.length) { Log_type log_type; + + long log_size; + LEX_STRING log_size_str; + + long log_offset= 0; + LEX_STRING log_offset_str= { NULL, 0 }; + switch (shift_token(&text, &word_len)) { case TOK_LOG: switch (Token tok3= shift_token(&text, &word_len)) { @@ -254,8 +414,7 @@ Command *parse_command(Instance_map *map, const char *text) /* check that this is the end of the command */ if (word_len) goto syntax_error; - command= new Show_instance_log_files(map, instance_name, - instance_name_len); + command= new Show_instance_log_files(map, &instance_name); break; case TOK_ERROR: case TOK_GENERAL: @@ -275,12 +434,14 @@ Command *parse_command(Instance_map *map, const char *text) goto syntax_error; } /* get the size of the log we want to retrieve */ - if (get_text_id(&text, &word_len, &log_size)) + if (get_text_id(&text, &log_size_str)) goto syntax_error; - text+= word_len; + text+= log_size_str.length; + /* this parameter is required */ - if (!word_len) + if (!log_size_str.length) goto syntax_error; + /* the next token should be comma, or nothing */ get_word(&text, &word_len); switch (*text) { @@ -290,23 +451,41 @@ Command *parse_command(Instance_map *map, const char *text) get_word(&text, &word_len); if (!word_len) goto syntax_error; + log_offset_str.str= (char *) text; + log_offset_str.length= word_len; text+= word_len; - command= new Show_instance_log(map, instance_name, - instance_name_len, log_type, - log_size, text); get_word(&text, &word_len, NONSPACE); /* check that this is the end of the command */ if (word_len) goto syntax_error; break; case '\0': - command= new Show_instance_log(map, instance_name, - instance_name_len, log_type, - log_size, NULL); break; /* this is ok */ default: + goto syntax_error; + } + + /* Parse size parameter. */ + + if (parse_long(&log_size_str, &log_size)) + goto syntax_error; + + if (log_size <= 0) goto syntax_error; + + /* Parse offset parameter (if specified). */ + + if (log_offset_str.length) + { + if (parse_long(&log_offset_str, &log_offset)) + goto syntax_error; + + if (log_offset <= 0) + goto syntax_error; } + + command= new Show_instance_log(map, &instance_name, + log_type, log_size, log_offset); break; default: goto syntax_error; diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h index 3da53e3a61e..ae29c7eb64a 100644 --- a/server-tools/instance-manager/parse.h +++ b/server-tools/instance-manager/parse.h @@ -18,6 +18,7 @@ #include <my_global.h> #include <my_sys.h> +#include <m_string.h> class Command; class Instance_map; @@ -29,10 +30,148 @@ enum Log_type IM_LOG_SLOW }; -Command *parse_command(Instance_map *instance_map, const char *text); +Command *parse_command(Instance_map *map, const char *text); + +bool parse_option_value(const char *text, uint *text_len, char **value); + +void skip_spaces(const char **text); /* define kinds of the word seek method */ -enum { ALPHANUM= 1, NONSPACE }; +enum enum_seek_method { ALPHANUM= 1, NONSPACE, OPTION_NAME }; + +/************************************************************************/ + +class Named_value +{ +public: + /* + The purpose of these methods is just to have one method for + allocating/deallocating memory for strings for Named_value. + */ + + static inline char *alloc_str(const LEX_STRING *str); + static inline char *alloc_str(const char *str); + static inline void free_str(char **str); + +public: + inline Named_value(); + inline Named_value(char *name_arg, char *value_arg); + + inline char *get_name(); + inline char *get_value(); + + inline void free(); + +private: + char *name; + char *value; +}; + +inline char *Named_value::alloc_str(const LEX_STRING *str) +{ + return my_strndup((const byte *) str->str, str->length, MYF(0)); +} + +inline char *Named_value::alloc_str(const char *str) +{ + return my_strdup(str, MYF(0)); +} + +inline void Named_value::free_str(char **str) +{ + my_free(*str, MYF(MY_ALLOW_ZERO_PTR)); + *str= NULL; +} + +inline Named_value::Named_value() + :name(NULL), value(NULL) +{ } + +inline Named_value::Named_value(char *name_arg, char *value_arg) + :name(name_arg), value(value_arg) +{ } + +inline char *Named_value::get_name() +{ + return name; +} + +inline char *Named_value::get_value() +{ + return value; +} + +void Named_value::free() +{ + free_str(&name); + free_str(&value); +} + +/************************************************************************/ + +class Named_value_arr +{ +public: + Named_value_arr(); + ~Named_value_arr(); + + bool init(); + + inline int get_size() const; + inline Named_value get_element(int idx) const; + inline void remove_element(int idx); + inline bool add_element(Named_value *option); + inline bool replace_element(int idx, Named_value *option); + +private: + bool initialized; + DYNAMIC_ARRAY arr; +}; + + +inline int Named_value_arr::get_size() const +{ + return arr.elements; +} + + +inline Named_value Named_value_arr::get_element(int idx) const +{ + DBUG_ASSERT(0 <= idx && (uint) idx < arr.elements); + + Named_value option; + get_dynamic((DYNAMIC_ARRAY *) &arr, (gptr) &option, idx); + + return option; +} + + +inline void Named_value_arr::remove_element(int idx) +{ + DBUG_ASSERT(0 <= idx && (uint) idx < arr.elements); + + get_element(idx).free(); + + delete_dynamic_element(&arr, idx); +} + + +inline bool Named_value_arr::add_element(Named_value *option) +{ + return insert_dynamic(&arr, (gptr) option); +} + + +inline bool Named_value_arr::replace_element(int idx, Named_value *option) +{ + DBUG_ASSERT(0 <= idx && (uint) idx < arr.elements); + + get_element(idx).free(); + + return set_dynamic(&arr, (gptr) option, idx); +} + +/************************************************************************/ /* tries to find next word in the text @@ -41,7 +180,7 @@ enum { ALPHANUM= 1, NONSPACE }; */ inline void get_word(const char **text, uint *word_len, - int seek_method= ALPHANUM) + enum_seek_method seek_method= ALPHANUM) { const char *word_end; @@ -51,13 +190,23 @@ inline void get_word(const char **text, uint *word_len, word_end= *text; - if (seek_method == ALPHANUM) + switch (seek_method) { + case ALPHANUM: while (my_isalnum(default_charset_info, *word_end)) ++word_end; - else + break; + case NONSPACE: while (!my_isspace(default_charset_info, *word_end) && (*word_end != '\0')) ++word_end; + break; + case OPTION_NAME: + while (my_isalnum(default_charset_info, *word_end) || + *word_end == '-' || + *word_end == '_') + ++word_end; + break; + } *word_len= word_end - *text; } diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 64bb6a6485f..643a50625a1 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -14,13 +14,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> -#include "parse.h" #include "parse_output.h" -#include <stdio.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> + +#include <stdio.h> + +#include "parse.h" #include "portability.h" diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h index 6a84fabbf17..b86363a4452 100644 --- a/server-tools/instance-manager/parse_output.h +++ b/server-tools/instance-manager/parse_output.h @@ -16,6 +16,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <my_global.h> + #define GET_VALUE 1 #define GET_LINE 2 diff --git a/server-tools/instance-manager/portability.h b/server-tools/instance-manager/portability.h index 1a3be5705e3..a76cff58893 100644 --- a/server-tools/instance-manager/portability.h +++ b/server-tools/instance-manager/portability.h @@ -16,11 +16,25 @@ /*TODO: fix this */ #define PROTOCOL_VERSION 10 +#define DFLT_CONFIG_FILE_NAME "my.ini" +#define DFLT_MYSQLD_PATH "mysqld" +#define DFLT_PASSWD_FILE_EXT ".passwd" +#define DFLT_PID_FILE_EXT ".pid" +#define DFLT_SOCKET_FILE_EXT ".sock" + typedef int pid_t; #undef popen #define popen(A,B) _popen(A,B) +#define NEWLINE "\r\n" +#define NEWLINE_LEN 2 + +#else /* ! __WIN__ */ + +#define NEWLINE "\n" +#define NEWLINE_LEN 1 + #endif /* __WIN__ */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PORTABILITY_H */ diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index d2d6a3f636c..d3cc52ec638 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -14,10 +14,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "priv.h" + #include <my_global.h> #include <mysql_com.h> -#include "priv.h" -#include "portability.h" #if defined(__ia64__) || defined(__ia64) /* @@ -43,9 +43,7 @@ bool linuxthreads; The following string must be less then 80 characters, as mysql_connection.cc relies on it */ -const char mysqlmanager_version[] = "0.2-alpha"; - -const int mysqlmanager_version_length= sizeof(mysqlmanager_version) - 1; +const LEX_STRING mysqlmanager_version= { C_STRING_WITH_SIZE("1.0-beta") }; const unsigned char protocol_version= PROTOCOL_VERSION; diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 52d7aa1d23d..0b393c17ac2 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -16,13 +16,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <my_global.h> +#include <m_string.h> +#include <my_pthread.h> + #include <sys/types.h> -#ifdef __WIN__ -#include "portability.h" -#else + +#ifndef __WIN__ #include <unistd.h> #endif -#include "my_pthread.h" + +#include "portability.h" /* IM-wide platform-independent defines */ #define SERVER_DEFAULT_PORT 3306 @@ -31,6 +35,21 @@ /* three-week timeout should be enough */ #define LONG_TIMEOUT ((ulong) 3600L*24L*21L) +const int MEM_ROOT_BLOCK_SIZE= 512; + +/* The maximal length of option name and option value. */ +const int MAX_OPTION_LEN= 1024; + +/* + The maximal length of whole option string: + --<option name>=<option value> +*/ +const int MAX_OPTION_STR_LEN= 2 + MAX_OPTION_LEN + 1 + MAX_OPTION_LEN + 1; + +const int MAX_VERSION_LENGTH= 160; + +const int MAX_INSTANCE_NAME_SIZE= FN_REFLEN; + /* the pid of the manager process (of the signal thread on the LinuxThreads) */ extern pid_t manager_pid; @@ -42,8 +61,7 @@ extern pid_t manager_pid; extern bool linuxthreads; #endif -extern const char mysqlmanager_version[]; -extern const int mysqlmanager_version_length; +extern const LEX_STRING mysqlmanager_version; /* MySQL client-server protocol version: substituted from configure */ extern const unsigned char protocol_version; diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 73e07f993ae..4a8c4d0b88d 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -163,7 +163,7 @@ int send_fields(struct st_net *net, LIST *fields) Buffer send_buff; char small_buff[4]; uint position= 0; - NAME_WITH_LENGTH *field; + LEX_STRING *field; /* send the number of fileds */ net_store_length(small_buff, (uint) list_length(fields)); @@ -173,7 +173,7 @@ int send_fields(struct st_net *net, LIST *fields) while (tmp) { position= 0; - field= (NAME_WITH_LENGTH *) tmp->data; + field= (LEX_STRING *) tmp->data; store_to_protocol_packet(&send_buff, (char*) "", &position); /* catalog name */ @@ -184,9 +184,9 @@ int send_fields(struct st_net *net, LIST *fields) store_to_protocol_packet(&send_buff, (char*) "", &position); /* table name alias */ store_to_protocol_packet(&send_buff, - field->name, &position); /* column name */ + field->str, &position); /* column name */ store_to_protocol_packet(&send_buff, - field->name, &position); /* column name alias */ + field->str, &position); /* column name alias */ send_buff.reserve(position, 12); if (send_buff.is_error()) goto err; diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h index f38eac6b079..2c84ad394b4 100644 --- a/server-tools/instance-manager/protocol.h +++ b/server-tools/instance-manager/protocol.h @@ -20,11 +20,6 @@ #include <my_list.h> -typedef struct field { - char *name; - uint length; -} NAME_WITH_LENGTH; - /* default field length to be used in various field-realted functions */ enum { DEFAULT_FIELD_LENGTH= 20 }; diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc index 0091d713a91..a424860548d 100644 --- a/server-tools/instance-manager/thread_registry.cc +++ b/server-tools/instance-manager/thread_registry.cc @@ -20,11 +20,12 @@ #include "thread_registry.h" -#include "log.h" +#include <my_global.h> +#include <thr_alarm.h> -#include <assert.h> #include <signal.h> -#include <thr_alarm.h> + +#include "log.h" #ifndef __WIN__ @@ -52,7 +53,7 @@ Thread_info::Thread_info(pthread_t thread_id_arg) : */ Thread_registry::Thread_registry() : - shutdown_in_progress(false) + shutdown_in_progress(FALSE) ,sigwait_thread_pid(pthread_self()) { pthread_mutex_init(&LOCK_thread_registry, 0); @@ -186,7 +187,7 @@ void Thread_registry::deliver_shutdown() set_timespec(shutdown_time, 1); pthread_mutex_lock(&LOCK_thread_registry); - shutdown_in_progress= true; + shutdown_in_progress= TRUE; #ifndef __WIN__ /* to stop reading from the network we need to flush alarm queue */ diff --git a/server-tools/instance-manager/user_management_commands.cc b/server-tools/instance-manager/user_management_commands.cc new file mode 100644 index 00000000000..03a3f9814e3 --- /dev/null +++ b/server-tools/instance-manager/user_management_commands.cc @@ -0,0 +1,406 @@ +#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION) +#pragma implementation +#endif + +#include "user_management_commands.h" + +#include "exit_codes.h" +#include "options.h" +#include "user_map.h" + +/************************************************************************* + Module-specific (internal) functions. +*************************************************************************/ + +/* + The function returns user name. The user name is retrieved from command-line + options (if specified) or from console. + + NOTE + This function must not be used in user-management command implementations. + Use get_user_name() instead. + + SYNOPSYS + get_user_name_impl() + + RETURN + NULL on error + valid pointer on success +*/ + +static char *get_user_name_impl() +{ + static char user_name_buf[1024]; + char *ptr; + + if (Options::User_management::user_name) + return Options::User_management::user_name; + + printf("Enter user name: "); + fflush(stdout); + + if (!fgets(user_name_buf, sizeof (user_name_buf), stdin)) + return NULL; + + if ((ptr= strchr(user_name_buf, '\n'))) + *ptr= 0; + + if ((ptr= strchr(user_name_buf, '\r'))) + *ptr= 0; + + return user_name_buf; +} + + +/* + The function is intended to provide user name for user-management + operations. It also checks that length of the specified user name is correct + (not empty, not exceeds USERNAME_LENGTH). Report to stderr if something is + wrong. + + SYNOPSYS + get_user_name() + user_name [OUT] on success contains user name + + RETURN + TRUE on error + FALSE on success +*/ + +static bool get_user_name(LEX_STRING *user_name) +{ + char *user_name_str= get_user_name_impl(); + + if (!user_name_str) + { + fprintf(stderr, "Error: unable to read user name from stdin.\n"); + return TRUE; + } + + user_name->str= user_name_str; + user_name->length= strlen(user_name->str); + + if (user_name->length == 0) + { + fprintf(stderr, "Error: user name can not be empty.\n"); + return TRUE; + } + + if (user_name->length > USERNAME_LENGTH) + { + fprintf(stderr, "Error: user name must not exceed %d characters.\n", + (int) USERNAME_LENGTH); + return TRUE; + } + + return FALSE; +} + + +/* + The function is intended to provide password for user-management operations. + The password is retrieved from command-line options (if specified) or from + console. + + SYNOPSYS + get_password() + + RETURN + NULL on error + valid pointer on success +*/ + +static const char *get_password() +{ + if (Options::User_management::password) + return Options::User_management::password; + + const char *passwd1= get_tty_password("Enter password: "); + const char *passwd2= get_tty_password("Re-type password: "); + + if (strcmp(passwd1, passwd2)) + { + fprintf(stderr, "Error: passwords do not match.\n"); + return 0; + } + + return passwd1; +} + + +/* + Load password file into user map. + + SYNOPSYS + load_password_file() + user_map target user map + + RETURN + See exit_codes.h for possible values. +*/ + +static int load_password_file(User_map *user_map) +{ + int err_code; + const char *err_msg; + + if (user_map->init()) + { + fprintf(stderr, "Error: can not initialize user map.\n"); + return ERR_OUT_OF_MEMORY; + } + + if ((err_code= user_map->load(Options::Main::password_file_name, &err_msg))) + fprintf(stderr, "Error: %s.\n", (const char *) err_msg); + + return err_code; +} + + +/* + Save user map into password file. + + SYNOPSYS + save_password_file() + user_map user map + + RETURN + See exit_codes.h for possible values. +*/ + +static int save_password_file(User_map *user_map) +{ + int err_code; + const char *err_msg; + + if ((err_code= user_map->save(Options::Main::password_file_name, &err_msg))) + fprintf(stderr, "Error: %s.\n", (const char *) err_msg); + + return err_code; +} + +/************************************************************************* + Passwd_cmd +*************************************************************************/ + +int Passwd_cmd::execute() +{ + LEX_STRING user_name; + const char *password; + + printf("Creating record for new user.\n"); + + if (get_user_name(&user_name)) + return ERR_CAN_NOT_READ_USER_NAME; + + if (!(password= get_password())) + return ERR_CAN_NOT_READ_PASSWORD; + + { + User user(&user_name, password); + + printf("%s:%s\n", + (const char *) user.user, + (const char *) user.scrambled_password); + } + + return ERR_OK; +} + + +/************************************************************************* + Add_user_cmd +*************************************************************************/ + +int Add_user_cmd::execute() +{ + LEX_STRING user_name; + const char *password; + + User_map user_map; + User *new_user; + + int err_code; + + if (get_user_name(&user_name)) + return ERR_CAN_NOT_READ_USER_NAME; + + /* Load the password file. */ + + if ((err_code= load_password_file(&user_map)) != ERR_OK) + return err_code; + + /* Check that the user does not exist. */ + + if (user_map.find_user(&user_name)) + { + fprintf(stderr, "Error: user '%s' already exists.\n", + (const char *) user_name.str); + return ERR_USER_ALREADY_EXISTS; + } + + /* Add the user. */ + + if (!(password= get_password())) + return ERR_CAN_NOT_READ_PASSWORD; + + if (!(new_user= new User(&user_name, password))) + return ERR_OUT_OF_MEMORY; + + if (user_map.add_user(new_user)) + { + delete new_user; + return ERR_OUT_OF_MEMORY; + } + + /* Save the password file. */ + + return save_password_file(&user_map); +} + + +/************************************************************************* + Drop_user_cmd +*************************************************************************/ + +int Drop_user_cmd::execute() +{ + LEX_STRING user_name; + + User_map user_map; + User *user; + + int err_code; + + if (get_user_name(&user_name)) + return ERR_CAN_NOT_READ_USER_NAME; + + /* Load the password file. */ + + if ((err_code= load_password_file(&user_map)) != ERR_OK) + return err_code; + + /* Find the user. */ + + user= user_map.find_user(&user_name); + + if (!user) + { + fprintf(stderr, "Error: user '%s' does not exist.\n", + (const char *) user_name.str); + return ERR_USER_NOT_FOUND; + } + + /* Remove the user (ignore possible errors). */ + + user_map.remove_user(user); + + /* Save the password file. */ + + return save_password_file(&user_map); +} + + +/************************************************************************* + Edit_user_cmd +*************************************************************************/ + +int Edit_user_cmd::execute() +{ + LEX_STRING user_name; + const char *password; + + User_map user_map; + User *user; + + int err_code; + + if (get_user_name(&user_name)) + return ERR_CAN_NOT_READ_USER_NAME; + + /* Load the password file. */ + + if ((err_code= load_password_file(&user_map)) != ERR_OK) + return err_code; + + /* Find the user. */ + + user= user_map.find_user(&user_name); + + if (!user) + { + fprintf(stderr, "Error: user '%s' does not exist.\n", + (const char *) user_name.str); + return ERR_USER_NOT_FOUND; + } + + /* Modify user's password. */ + + if (!(password= get_password())) + return ERR_CAN_NOT_READ_PASSWORD; + + user->set_password(password); + + /* Save the password file. */ + + return save_password_file(&user_map); +} + + +/************************************************************************* + Clean_db_cmd +*************************************************************************/ + +int Clean_db_cmd::execute() +{ + User_map user_map; + + if (user_map.init()) + { + fprintf(stderr, "Error: can not initialize user map.\n"); + return ERR_OUT_OF_MEMORY; + } + + return save_password_file(&user_map); +} + + +/************************************************************************* + Check_db_cmd +*************************************************************************/ + +int Check_db_cmd::execute() +{ + User_map user_map; + + return load_password_file(&user_map); +} + + +/************************************************************************* + List_users_cmd +*************************************************************************/ + +int List_users_cmd::execute() +{ + User_map user_map; + + int err_code; + + /* Load the password file. */ + + if ((err_code= load_password_file(&user_map))) + return err_code; + + /* Print out registered users. */ + + { + User_map::Iterator it(&user_map); + User *user; + + while ((user= it.next())) + fprintf(stderr, "%s\n", (const char *) user->user); + } + + return ERR_OK; +} diff --git a/server-tools/instance-manager/user_management_commands.h b/server-tools/instance-manager/user_management_commands.h new file mode 100644 index 00000000000..4bf3546f0a6 --- /dev/null +++ b/server-tools/instance-manager/user_management_commands.h @@ -0,0 +1,167 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H + +/* + Copyright (C) 2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + +/* + This header contains declarations of classes inteded to support + user-management commands (such as add user, get list of users, etc). + + The general idea is to have one interface (pure abstract class) for such a + command. Each concrete user-management command is implemented in concrete + class, derived from the common interface. +*/ + +#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) +#pragma interface +#endif + +/************************************************************************* + User_management_cmd -- base class for User-management commands. +*************************************************************************/ + +class User_management_cmd +{ +public: + User_management_cmd() + { } + + virtual ~User_management_cmd() + { } + +public: + /* + Executes user-management command. + + SYNOPSYS + execute() + + RETURN + See exit_codes.h for possible values. + */ + + virtual int execute() = 0; +}; + + +/************************************************************************* + Passwd_cmd: support for --passwd command-line option. +*************************************************************************/ + +class Passwd_cmd : public User_management_cmd +{ +public: + Passwd_cmd() + { } + +public: + virtual int execute(); +}; + + +/************************************************************************* + Add_user_cmd: support for --add-user command-line option. +*************************************************************************/ + +class Add_user_cmd : public User_management_cmd +{ +public: + Add_user_cmd() + { } + +public: + virtual int execute(); +}; + + +/************************************************************************* + Drop_user_cmd: support for --drop-user command-line option. +*************************************************************************/ + +class Drop_user_cmd : public User_management_cmd +{ +public: + Drop_user_cmd() + { } + +public: + virtual int execute(); +}; + + +/************************************************************************* + Edit_user_cmd: support for --edit-user command-line option. +*************************************************************************/ + +class Edit_user_cmd : public User_management_cmd +{ +public: + Edit_user_cmd() + { } + +public: + virtual int execute(); +}; + + +/************************************************************************* + Clean_db_cmd: support for --clean-db command-line option. +*************************************************************************/ + +class Clean_db_cmd : public User_management_cmd +{ +public: + Clean_db_cmd() + { } + +public: + virtual int execute(); +}; + + +/************************************************************************* + Check_db_cmd: support for --check-db command-line option. +*************************************************************************/ + +class Check_db_cmd : public User_management_cmd +{ +public: + Check_db_cmd() + { } + +public: + virtual int execute(); +}; + + +/************************************************************************* + List_users_cmd: support for --list-users command-line option. +*************************************************************************/ + +class List_users_cmd : public User_management_cmd +{ +public: + List_users_cmd() + { } + +public: + virtual int execute(); +}; + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MANAGEMENT_CMD_H diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 9cb15307131..e8128cf015b 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -19,32 +19,32 @@ #endif #include "user_map.h" - -#include <mysql_com.h> -#include <m_string.h> - +#include "exit_codes.h" #include "log.h" +#include "portability.h" -struct User +User::User(const LEX_STRING *user_name_arg, const char *password) { - char user[USERNAME_LENGTH + 1]; - uint8 user_length; - uint8 salt[SCRAMBLE_LENGTH]; - int init(const char *line); -}; + user_length= strmake(user, user_name_arg->str, USERNAME_LENGTH + 1) - user; + set_password(password); +} int User::init(const char *line) { const char *name_begin, *name_end, *password; - int line_ending_len= 1; + int password_length; if (line[0] == '\'' || line[0] == '"') { name_begin= line + 1; name_end= strchr(name_begin, line[0]); if (name_end == 0 || name_end[1] != ':') - goto err; + { + log_info("Error: invalid format (unmatched quote) of user line (%s).", + (const char *) line); + return 1; + } password= name_end + 2; } else @@ -52,33 +52,47 @@ int User::init(const char *line) name_begin= line; name_end= strchr(name_begin, ':'); if (name_end == 0) - goto err; + { + log_info("Error: invalid format (no delimiter) of user line (%s).", + (const char *) line); + return 1; + } password= name_end + 1; } + user_length= name_end - name_begin; if (user_length > USERNAME_LENGTH) - goto err; - - /* - assume that newline characater is present - we support reading password files that end in \n or \r\n on - either platform. - */ - if (password[strlen(password)-2] == '\r') - line_ending_len= 2; - if (strlen(password) != (uint) (SCRAMBLED_PASSWORD_CHAR_LENGTH + - line_ending_len)) - goto err; + { + log_info("Error: user name is too long (%d). Max length: %d. " + "User line: '%s'.", + (int) user_length, + (int) USERNAME_LENGTH, + (const char *) line); + return 1; + } + + password_length= strlen(password); + if (password_length > SCRAMBLED_PASSWORD_CHAR_LENGTH) + { + log_info("Error: password is too long (%d). Max length: %d. ", + "User line: '%s'.", + (int) password_length, + (int) SCRAMBLED_PASSWORD_CHAR_LENGTH, + (const char *) line); + return 1; + } memcpy(user, name_begin, user_length); user[user_length]= 0; + + memcpy(scrambled_password, password, password_length); + scrambled_password[password_length]= 0; + get_salt_from_password(salt, password); - log_info("loaded user %s", user); + + log_info("loaded user '%s'.", user); return 0; -err: - log_error("error parsing user and password at line %s", line); - return 1; } @@ -101,30 +115,70 @@ static void delete_user(void *u) C_MODE_END +void User_map::Iterator::reset() +{ + cur_idx= 0; +} + + +User *User_map::Iterator::next() +{ + if (cur_idx < user_map->hash.records) + return (User *) hash_element(&user_map->hash, cur_idx++); + + return NULL; +} + + int User_map::init() { enum { START_HASH_SIZE= 16 }; if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_user_key, delete_user, 0)) return 1; + + initialized= TRUE; + return 0; } +User_map::User_map() + :initialized(FALSE) +{ +} + + User_map::~User_map() { - hash_free(&hash); + if (initialized) + hash_free(&hash); } /* - Load all users from the password file. Must be called once right after - construction. - In case of failure, puts error message to the log file and returns 1 + Load password database. + + SYNOPSYS + load() + password_file_name [IN] password file path + err_msg [OUT] error message + + DESCRIPTION + Load all users from the password file. Must be called once right after + construction. In case of failure, puts error message to the log file and + returns specific error code. + + RETURN + 0 on success + !0 on error */ -int User_map::load(const char *password_file_name) +int User_map::load(const char *password_file_name, const char **err_msg) { + static const int ERR_MSG_BUF_SIZE = 255; + static char err_msg_buf[ERR_MSG_BUF_SIZE]; + FILE *file; char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH + 2 + /* for possible quotes */ @@ -134,33 +188,172 @@ int User_map::load(const char *password_file_name) User *user; int rc= 1; + if (my_access(password_file_name, F_OK) != 0) + { + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "password file (%s) does not exist", + (const char *) password_file_name); + *err_msg= err_msg_buf; + } + + return ERR_PASSWORD_FILE_DOES_NOT_EXIST; + } + if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0) { - /* Probably the password file wasn't specified. Try to leave without it */ - log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name, - errno, strerror(errno)); - return 0; + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "can not open password file (%s): %s", + (const char *) password_file_name, + (const char *) strerror(errno)); + *err_msg= err_msg_buf; + } + + return ERR_IO_ERROR; } + log_info("loading the password database..."); + while (fgets(line, sizeof(line), file)) { + char *user_line= line; + + /* + We need to skip EOL-symbols also from the beginning of the line, because + if the previous line was ended by \n\r sequence, we get \r in our line. + */ + + while (user_line[0] == '\r' || user_line[0] == '\n') + ++user_line; + + /* Skip EOL-symbols in the end of the line. */ + + { + char *ptr; + + if ((ptr= strchr(user_line, '\n'))) + *ptr= 0; + + if ((ptr= strchr(user_line, '\r'))) + *ptr= 0; + } + /* skip comments and empty lines */ - if (line[0] == '#' || line[0] == '\n' && - (line[1] == '\0' || line[1] == '\r')) + if (!user_line[0] || user_line[0] == '#') continue; + if ((user= new User) == 0) - goto done; - if (user->init(line) || my_hash_insert(&hash, (byte *) user)) - goto err_init_user; + { + my_fclose(file, MYF(0)); + + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "out of memory while parsing password file (%s)", + (const char *) password_file_name); + *err_msg= err_msg_buf; + } + + return ERR_OUT_OF_MEMORY; + } + + if (user->init(user_line)) + { + delete user; + my_fclose(file, MYF(0)); + + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "password file (%s) corrupted", + (const char *) password_file_name); + *err_msg= err_msg_buf; + } + + return ERR_PASSWORD_FILE_CORRUPTED; + } + + if (my_hash_insert(&hash, (byte *) user)) + { + delete user; + my_fclose(file, MYF(0)); + + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "out of memory while parsing password file (%s)", + (const char *) password_file_name); + *err_msg= err_msg_buf; + } + + return ERR_OUT_OF_MEMORY; + } } - if (feof(file)) - rc= 0; - goto done; -err_init_user: - delete user; -done: + + log_info("the password database loaded successfully."); + my_fclose(file, MYF(0)); - return rc; + + if (err_msg) + *err_msg= NULL; + + return ERR_OK; +} + + +int User_map::save(const char *password_file_name, const char **err_msg) +{ + static const int ERR_MSG_BUF_SIZE = 255; + static char err_msg_buf[ERR_MSG_BUF_SIZE]; + + FILE *file; + + if ((file= my_fopen(password_file_name, O_WRONLY | O_TRUNC | O_BINARY, + MYF(0))) == 0) + { + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "can not open password file (%s) for writing: %s", + (const char *) password_file_name, + (const char *) strerror(errno)); + *err_msg= err_msg_buf; + } + + return ERR_IO_ERROR; + } + + { + User_map::Iterator it(this); + User *user; + + while ((user= it.next())) + { + if (fprintf(file, "%s:%s\n", (const char *) user->user, + (const char *) user->scrambled_password) < 0) + { + if (err_msg) + { + snprintf(err_msg_buf, ERR_MSG_BUF_SIZE, + "can not write to password file (%s): %s", + (const char *) password_file_name, + (const char *) strerror(errno)); + *err_msg= err_msg_buf; + } + + my_fclose(file, MYF(0)); + + return ERR_IO_ERROR; + } + } + } + + my_fclose(file, MYF(0)); + + return ERR_OK; } @@ -172,13 +365,33 @@ done: 2 - user not found */ -int User_map::authenticate(const char *user_name, uint length, +int User_map::authenticate(const LEX_STRING *user_name, const char *scrambled_password, const char *scramble) const { - const User *user= (const User *) hash_search((HASH *) &hash, - (byte *) user_name, length); - if (user) - return check_scramble(scrambled_password, scramble, user->salt); - return 2; + const User *user= find_user(user_name); + return user ? check_scramble(scrambled_password, scramble, user->salt) : 2; +} + + +User *User_map::find_user(const LEX_STRING *user_name) +{ + return (User*) hash_search(&hash, (byte*) user_name->str, user_name->length); +} + +const User *User_map::find_user(const LEX_STRING *user_name) const +{ + return const_cast<User_map *> (this)->find_user(user_name); +} + + +bool User_map::add_user(User *user) +{ + return my_hash_insert(&hash, (byte*) user) == 0 ? FALSE : TRUE; +} + + +bool User_map::remove_user(User *user) +{ + return hash_delete(&hash, (byte*) user) == 0 ? FALSE : TRUE; } diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h index 4134017dd9b..de207c11e65 100644 --- a/server-tools/instance-manager/user_map.h +++ b/server-tools/instance-manager/user_map.h @@ -18,14 +18,35 @@ #include <my_global.h> - #include <my_sys.h> +#include <mysql_com.h> +#include <m_string.h> #include <hash.h> #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface #endif +struct User +{ + User() + {} + + User(const LEX_STRING *user_name_arg, const char *password); + + int init(const char *line); + + inline void set_password(const char *password) + { + make_scrambled_password(scrambled_password, password); + } + + char user[USERNAME_LENGTH + 1]; + char scrambled_password[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1]; + uint8 user_length; + uint8 salt[SCRAMBLE_LENGTH]; +}; + /* User_map -- all users and passwords */ @@ -33,15 +54,51 @@ class User_map { public: + /* User_map iterator */ + + class Iterator + { + public: + Iterator(User_map *user_map_arg) : + cur_idx(0), user_map(user_map_arg) + { } + + public: + void reset(); + + User *next(); + + private: + User_map *user_map; + uint cur_idx; + }; + +public: + User_map(); ~User_map(); int init(); - int load(const char *password_file_name); - int authenticate(const char *user_name, uint length, + int load(const char *password_file_name, const char **err_msg); + int save(const char *password_file_name, const char **err_msg); + int authenticate(const LEX_STRING *user_name, const char *scrambled_password, const char *scramble) const; + + const User *find_user(const LEX_STRING *user_name) const; + User *find_user(const LEX_STRING *user_name); + + bool add_user(User *user); + bool remove_user(User *user); + +private: + User_map(const User_map &); + User_map &operator =(const User_map &); + private: HASH hash; + bool initialized; + + friend class Iterator; }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H diff --git a/sql-common/my_time.c b/sql-common/my_time.c index c9d39260761..f75298e4f5d 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -689,7 +689,7 @@ long calc_daynr(uint year,uint month,uint day) if (year == 0 && month == 0 && day == 0) DBUG_RETURN(0); /* Skip errors */ - if (year < 200) + if (year < YY_MAGIC_BELOW) { if ((year=year+1900) < 1900+YY_PART_YEAR) year+=100; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 05b1efdbe51..2b44fbdcc79 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -51,7 +51,7 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc - rpl_tblmap.cc sql_binlog.cc event_executor.cc event_timed.cc + rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_timed.cc sql_tablespace.cc event.cc ../sql-common/my_user.c partition_info.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index deaf2427aeb..7f711a1e212 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -66,7 +66,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ parse_file.h sql_view.h sql_trigger.h \ sql_array.h sql_cursor.h event.h event_priv.h \ sql_plugin.h authors.h sql_partition.h \ - partition_info.h partition_element.h + partition_info.h partition_element.h event_scheduler.h \ + contributors.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -103,7 +104,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ tztime.cc my_time.c my_user.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ - event_executor.cc event.cc event_timed.cc \ + event_scheduler.cc event.cc event_timed.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc diff --git a/sql/contributors.h b/sql/contributors.h new file mode 100644 index 00000000000..dca232b9b69 --- /dev/null +++ b/sql/contributors.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult 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; either version 2 of the License, or + (at your option) any later version. + + 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 */ + +/* Structure of the name list */ + +struct show_table_contributors_st { + const char *name; + const char *location; + const char *comment; +}; + +/* + Output from "SHOW CONTRIBUTORS" + + Get permission before editing. + + IMPORTANT: Names should be left in historical order. + + Names should be encoded using UTF-8. +*/ + +struct show_table_contributors_st show_table_contributors[]= { + {"Ronald Bradford", "Brisbane, Australia", "EFF contribution for UC2006 Auction"}, + {"Sheeri Kritzer", "Boston, Mass. USA", "EFF contribution for UC2006 Auction"}, + {"Mark Shuttleworth", "London, UK.", "EFF contribution for UC2006 Auction"}, + {NULL, NULL, NULL} +}; diff --git a/sql/event.cc b/sql/event.cc index 4a3c6aad30c..7c3f17304aa 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2004-2005 MySQL AB +/* Copyright (C) 2004-2006 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 @@ -16,13 +16,12 @@ #include "event_priv.h" #include "event.h" +#include "event_scheduler.h" #include "sp.h" +#include "sp_head.h" /* TODO list : - - The default value of created/modified should not be 0000-00-00 because of - STRICT mode restricions. - - CREATE EVENT should not go into binary log! Does it now? The SQL statements issued by the EVENT are replicated. I have an idea how to solve the problem at failover. So the status field @@ -38,23 +37,8 @@ ENABLED to DISABLED status change and this is safe for replicating. As well an event may be deleted which is also safe for RBR. - - Maybe move all allocations during parsing to evex_mem_root thus saving - double parsing in evex_create_event! - - - If the server is killed (stopping) try to kill executing events? - - - What happens if one renames an event in the DB while it is in memory? - Or even deleting it? - - - Consider using conditional variable when doing shutdown instead of - waiting till all worker threads end. - - - Make Event_timed::get_show_create_event() work - - Add logging to file - - Move comparison code to class Event_timed - Warning: - For now parallel execution is not possible because the same sp_head cannot be executed few times!!! There is still no lock attached to particular event. @@ -62,12 +46,26 @@ Warning: */ -QUEUE EVEX_EQ_NAME; MEM_ROOT evex_mem_root; time_t mysql_event_last_create_time= 0L; -static TABLE_FIELD_W_TYPE event_table_fields[EVEX_FIELD_COUNT] = { +const char *event_scheduler_state_names[]= + { "OFF", "0", "ON", "1", "SUSPEND", "2", NullS }; + +TYPELIB Events::opt_typelib= +{ + array_elements(event_scheduler_state_names)-1, + "", + event_scheduler_state_names, + NULL +}; + + +ulong Events::opt_event_scheduler= 2; + +static +TABLE_FIELD_W_TYPE event_table_fields[Events::FIELD_COUNT] = { { {(char *) STRING_WITH_LEN("db")}, {(char *) STRING_WITH_LEN("char(64)")}, @@ -186,41 +184,17 @@ LEX_STRING interval_type_to_name[] = { }; - -/* - Inits the scheduler queue - prioritized queue from mysys/queue.c - - Synopsis - evex_queue_init() - - queue - pointer the the memory to be initialized as queue. has to be - allocated from the caller - - Notes - During initialization the queue is sized for 30 events, and when is full - will auto extent with 30. -*/ - -void -evex_queue_init(EVEX_QUEUE_TYPE *queue) -{ - if (init_queue_ex(queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/, - event_timed_compare_q, NULL, 30 /*auto_extent*/)) - sql_print_error("Insufficient memory to initialize executing queue."); -} - - /* Compares 2 LEX strings regarding case. - Synopsis + SYNOPSIS my_time_compare() s - first LEX_STRING t - second LEX_STRING cs - charset - RETURNS: + RETURN VALUE -1 - s < t 0 - s == t 1 - s > t @@ -239,32 +213,26 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) /* Compares 2 TIME structures - Synopsis + SYNOPSIS my_time_compare() a - first TIME b - second time - RETURNS: + RETURN VALUE -1 - a < b 0 - a == b 1 - a > b - Notes + NOTES TIME.second_part is not considered during comparison */ int my_time_compare(TIME *a, TIME *b) { - -#ifdef ENABLE_WHEN_WE_HAVE_MILLISECOND_IN_TIMESTAMPS - my_ulonglong a_t= TIME_to_ulonglong_datetime(a)*100L + a->second_part; - my_ulonglong b_t= TIME_to_ulonglong_datetime(b)*100L + b->second_part; -#else my_ulonglong a_t= TIME_to_ulonglong_datetime(a); my_ulonglong b_t= TIME_to_ulonglong_datetime(b); -#endif if (a_t > b_t) return 1; @@ -276,36 +244,11 @@ my_time_compare(TIME *a, TIME *b) /* - Compares the execute_at members of 2 Event_timed instances - - Synopsis - event_timed_compare() - - a - first Event_timed object - b - second Event_timed object - - RETURNS: - -1 - a->execute_at < b->execute_at - 0 - a->execute_at == b->execute_at - 1 - a->execute_at > b->execute_at - - Notes - execute_at.second_part is not considered during comparison -*/ - -int -event_timed_compare(Event_timed *a, Event_timed *b) -{ - return my_time_compare(&a->execute_at, &b->execute_at); -} - - -/* Compares the execute_at members of 2 Event_timed instances. Used as callback for the prioritized queue when shifting elements inside. - Synopsis + SYNOPSIS event_timed_compare() vptr - not used (set it to NULL) @@ -324,7 +267,8 @@ event_timed_compare(Event_timed *a, Event_timed *b) int event_timed_compare_q(void *vptr, byte* a, byte *b) { - return event_timed_compare((Event_timed *)a, (Event_timed *)b); + return my_time_compare(&((Event_timed *)a)->execute_at, + &((Event_timed *)b)->execute_at); } @@ -335,8 +279,8 @@ event_timed_compare_q(void *vptr, byte* a, byte *b) YEAR_MONTH - expression is in months DAY_MINUTE - expression is in minutes - Synopsis - event_reconstruct_interval_expression() + SYNOPSIS + Events::reconstruct_interval_expression() buf - preallocated String buffer to add the value to interval - the interval type (for instance YEAR_MONTH) expression - the value in the lowest entity @@ -347,9 +291,9 @@ event_timed_compare_q(void *vptr, byte* a, byte *b) */ int -event_reconstruct_interval_expression(String *buf, - interval_type interval, - longlong expression) +Events::reconstruct_interval_expression(String *buf, + interval_type interval, + longlong expression) { ulonglong expr= expression; char tmp_buff[128], *end; @@ -466,19 +410,20 @@ common_1_lev_code: Open mysql.event table for read SYNOPSIS - evex_open_event_table_for_read() + Events::open_event_table() thd Thread context lock_type How to lock the table table The table pointer - RETURN + RETURN VALUE 1 Cannot lock table 2 The table is corrupted - different number of fields 0 OK */ int -evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) +Events::open_event_table(THD *thd, enum thr_lock_type lock_type, + TABLE **table) { TABLE_LIST tables; DBUG_ENTER("open_proc_table"); @@ -491,7 +436,8 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) if (simple_open_n_lock_tables(thd, &tables)) DBUG_RETURN(1); - if (table_check_intact(tables.table, EVEX_FIELD_COUNT, event_table_fields, + if (table_check_intact(tables.table, Events::FIELD_COUNT, + event_table_fields, &mysql_event_last_create_time, ER_CANNOT_LOAD_FROM_TABLE)) { @@ -558,50 +504,56 @@ evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, 'db' and 'name' and the first key is the primary key over the same fields. */ - if (dbname.length > table->field[EVEX_FIELD_DB]->field_length || - ev_name.length > table->field[EVEX_FIELD_NAME]->field_length || - user_name.length > table->field[EVEX_FIELD_DEFINER]->field_length) + if (dbname.length > table->field[Events::FIELD_DB]->field_length || + ev_name.length > table->field[Events::FIELD_NAME]->field_length || + user_name.length > table->field[Events::FIELD_DEFINER]->field_length) DBUG_RETURN(EVEX_KEY_NOT_FOUND); - table->field[EVEX_FIELD_DB]->store(dbname.str, dbname.length, &my_charset_bin); - table->field[EVEX_FIELD_NAME]->store(ev_name.str, ev_name.length, - &my_charset_bin); - table->field[EVEX_FIELD_DEFINER]->store(user_name.str, user_name.length, - &my_charset_bin); + table->field[Events::FIELD_DB]->store(dbname.str, dbname.length, + &my_charset_bin); + table->field[Events::FIELD_NAME]->store(ev_name.str, ev_name.length, + &my_charset_bin); + table->field[Events::FIELD_DEFINER]->store(user_name.str, + user_name.length, + &my_charset_bin); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); if (table->file->index_read_idx(table->record[0], 0, key, table->key_info->key_length,HA_READ_KEY_EXACT)) + { + DBUG_PRINT("info", ("Row not fonud")); DBUG_RETURN(EVEX_KEY_NOT_FOUND); + } + DBUG_PRINT("info", ("Row found!")); DBUG_RETURN(0); } /* - Puts some data common to CREATE and ALTER EVENT into a row. + Puts some data common to CREATE and ALTER EVENT into a row. - SYNOPSIS - evex_fill_row() - thd THD - table the row to fill out - et Event's data + SYNOPSIS + evex_fill_row() + thd THD + table the row to fill out + et Event's data - Returns - 0 - ok - EVEX_GENERAL_ERROR - bad data - EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? + RETURN VALUE + 0 - OK + EVEX_GENERAL_ERROR - bad data + EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? - DESCRIPTION - Used both when an event is created and when it is altered. + DESCRIPTION + Used both when an event is created and when it is altered. */ static int evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) { - enum evex_table_field field_num; + enum Events::enum_table_field field_num; DBUG_ENTER("evex_fill_row"); @@ -609,19 +561,20 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) DBUG_PRINT("info", ("name =[%s]", et->name.str)); DBUG_PRINT("info", ("body =[%s]", et->body.str)); - if (table->field[field_num= EVEX_FIELD_DB]-> + if (table->field[field_num= Events::FIELD_DB]-> store(et->dbname.str, et->dbname.length, system_charset_info)) goto trunc_err; - if (table->field[field_num= EVEX_FIELD_NAME]-> + if (table->field[field_num= Events::FIELD_NAME]-> store(et->name.str, et->name.length, system_charset_info)) goto trunc_err; - /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull() */ - table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, - true); + /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ + table->field[Events::FIELD_ON_COMPLETION]-> + store((longlong)et->on_completion, true); - table->field[EVEX_FIELD_STATUS]->store((longlong)et->status, true); + table->field[Events::FIELD_STATUS]-> + store((longlong)et->status, true); /* Change the SQL_MODE only if body was present in an ALTER EVENT and of course @@ -629,53 +582,54 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) */ if (et->body.str) { - table->field[EVEX_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, - true); + table->field[Events::FIELD_SQL_MODE]-> + store((longlong)thd->variables.sql_mode, true); - if (table->field[field_num= EVEX_FIELD_BODY]-> + if (table->field[field_num= Events::FIELD_BODY]-> store(et->body.str, et->body.length, system_charset_info)) goto trunc_err; } if (et->expression) { - table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull(); - table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->expression,true); + table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull(); + table->field[Events::FIELD_INTERVAL_EXPR]-> + store((longlong)et->expression, true); - table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_notnull(); + table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull(); /* In the enum (C) intervals start from 0 but in mysql enum valid values start from 1. Thus +1 offset is needed! */ - table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, - true); + table->field[Events::FIELD_TRANSIENT_INTERVAL]-> + store((longlong)et->interval+1, true); - table->field[EVEX_FIELD_EXECUTE_AT]->set_null(); + table->field[Events::FIELD_EXECUTE_AT]->set_null(); if (!et->starts_null) { - table->field[EVEX_FIELD_STARTS]->set_notnull(); - table->field[EVEX_FIELD_STARTS]-> + table->field[Events::FIELD_STARTS]->set_notnull(); + table->field[Events::FIELD_STARTS]-> store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); } if (!et->ends_null) { - table->field[EVEX_FIELD_ENDS]->set_notnull(); - table->field[EVEX_FIELD_ENDS]-> + table->field[Events::FIELD_ENDS]->set_notnull(); + table->field[Events::FIELD_ENDS]-> store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); } } else if (et->execute_at.year) { - table->field[EVEX_FIELD_INTERVAL_EXPR]->set_null(); - table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null(); - table->field[EVEX_FIELD_STARTS]->set_null(); - table->field[EVEX_FIELD_ENDS]->set_null(); + table->field[Events::FIELD_INTERVAL_EXPR]->set_null(); + table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null(); + table->field[Events::FIELD_STARTS]->set_null(); + table->field[Events::FIELD_ENDS]->set_null(); - table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull(); - table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at, - MYSQL_TIMESTAMP_DATETIME); + table->field[Events::FIELD_EXECUTE_AT]->set_notnull(); + table->field[Events::FIELD_EXECUTE_AT]-> + store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); } else { @@ -686,13 +640,12 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) */ } - ((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time(); + ((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time(); if (et->comment.str) { - if (table->field[field_num= EVEX_FIELD_COMMENT]->store(et->comment.str, - et->comment.length, - system_charset_info)) + if (table->field[field_num= Events::FIELD_COMMENT]-> + store(et->comment.str, et->comment.length, system_charset_info)) goto trunc_err; } @@ -704,28 +657,30 @@ trunc_err: /* - Creates an event in mysql.event - - SYNOPSIS - db_create_event() - thd THD - et Event_timed object containing information for the event - create_if_not - if an warning should be generated in case event exists - rows_affected - how many rows were affected - - Return value - 0 - OK - EVEX_GENERAL_ERROR - Failure - DESCRIPTION - Creates an event. Relies on evex_fill_row which is shared with - db_update_event. The name of the event is inside "et". + Creates an event in mysql.event + + SYNOPSIS + db_create_event() + thd THD + et Event_timed object containing information for the event + create_if_not If an warning should be generated in case event exists + rows_affected How many rows were affected + + RETURN VALUE + 0 - OK + EVEX_GENERAL_ERROR - Failure + + DESCRIPTION + Creates an event. Relies on evex_fill_row which is shared with + db_update_event. The name of the event is inside "et". */ -static int +int db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, uint *rows_affected) { int ret= 0; + CHARSET_INFO *scs= system_charset_info; TABLE *table; char olddb[128]; bool dbchanged= false; @@ -734,7 +689,7 @@ db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, *rows_affected= 0; DBUG_PRINT("info", ("open mysql.event for update")); - if (evex_open_event_table(thd, TL_WRITE, &table)) + if (Events::open_event_table(thd, TL_WRITE, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; @@ -778,7 +733,7 @@ db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, goto err; } - if (et->body.length > table->field[EVEX_FIELD_BODY]->field_length) + if (et->body.length > table->field[Events::FIELD_BODY]->field_length) { my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); goto err; @@ -791,15 +746,14 @@ db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, goto err; } - if ((ret=table->field[EVEX_FIELD_DEFINER]->store(et->definer.str, - et->definer.length, - system_charset_info))) + if ((ret=table->field[Events::FIELD_DEFINER]-> + store(et->definer.str, et->definer.length, scs))) { my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); goto err; } - ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); + ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time(); /* evex_fill_row() calls my_error() in case of error so no need to @@ -819,8 +773,8 @@ db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, { thd->clear_error(); /* Such a statement can always go directly to binlog, no trans cache */ - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, + FALSE, FALSE); } #endif @@ -842,17 +796,21 @@ err: /* - Used to execute ALTER EVENT. Pendant to evex_update_event(). + Used to execute ALTER EVENT. Pendant to Events::update_event(). - SYNOPSIS - db_update_event() - thd THD - sp_name the name of the event to alter - et event's data + SYNOPSIS + db_update_event() + thd THD + sp_name the name of the event to alter + et event's data + + RETURN VALUE + 0 OK + EVEX_GENERAL_ERROR Error occured (my_error() called) - NOTES - sp_name is passed since this is the name of the event to - alter in case of RENAME TO. + NOTES + sp_name is passed since this is the name of the event to + alter in case of RENAME TO. */ static int @@ -863,12 +821,12 @@ db_update_event(THD *thd, Event_timed *et, sp_name *new_name) DBUG_ENTER("db_update_event"); DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str)); DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); - DBUG_PRINT("enter", ("user: %.*s", et->name.length, et->name.str)); + DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str)); if (new_name) DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, new_name->m_name.str)); - if (evex_open_event_table(thd, TL_WRITE, &table)) + if (Events::open_event_table(thd, TL_WRITE, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; @@ -914,9 +872,9 @@ db_update_event(THD *thd, Event_timed *et, sp_name *new_name) if (new_name) { - table->field[EVEX_FIELD_DB]-> + table->field[Events::FIELD_DB]-> store(new_name->m_db.str, new_name->m_db.length, system_charset_info); - table->field[EVEX_FIELD_NAME]-> + table->field[Events::FIELD_NAME]-> store(new_name->m_name.str, new_name->m_name.length, system_charset_info); } @@ -938,33 +896,33 @@ err: /* - Looks for a named event in mysql.event and in case of success returns - an object will data loaded from the table. - - SYNOPSIS - db_find_event() - thd THD - name the name of the event to find - definer who owns the event - ett event's data if event is found - tbl TABLE object to use when not NULL - - NOTES - 1) Use sp_name for look up, return in **ett if found - 2) tbl is not closed at exit - - RETURN - 0 ok In this case *ett is set to the event - # error *ett == 0 + Looks for a named event in mysql.event and in case of success returns + an object will data loaded from the table. + + SYNOPSIS + db_find_event() + thd THD + name the name of the event to find + definer who owns the event + ett event's data if event is found + tbl TABLE object to use when not NULL + + NOTES + 1) Use sp_name for look up, return in **ett if found + 2) tbl is not closed at exit + + RETURN VALUE + 0 ok In this case *ett is set to the event + # error *ett == 0 */ -static int +int db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, TABLE *tbl, MEM_ROOT *root) { TABLE *table; int ret; - Event_timed *et= 0; + Event_timed *et=NULL; DBUG_ENTER("db_find_event"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); @@ -973,7 +931,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, if (tbl) table= tbl; - else if (evex_open_event_table(thd, TL_READ, &table)) + else if (Events::open_event_table(thd, TL_READ, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); ret= EVEX_GENERAL_ERROR; @@ -1001,7 +959,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, } done: - if (ret) + if (ret && et) { delete et; et= 0; @@ -1015,179 +973,43 @@ done: /* - Looks for a named event in mysql.event and then loads it from - the table, compiles it and insert it into the cache. - - SYNOPSIS - evex_load_and_compile_event() - thd THD - spn the name of the event to alter - definer who is the owner - use_lock whether to obtain a lock on LOCK_event_arrays or not - - RETURN VALUE - 0 - OK - < 0 - error (in this case underlying functions call my_error()). -*/ - -static int -evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer, - bool use_lock) -{ - int ret= 0; - MEM_ROOT *tmp_mem_root; - Event_timed *ett; - Open_tables_state backup; - - DBUG_ENTER("db_load_and_compile_event"); - DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); - - tmp_mem_root= thd->mem_root; - thd->mem_root= &evex_mem_root; - - thd->reset_n_backup_open_tables_state(&backup); - /* no need to use my_error() here because db_find_event() has done it */ - ret= db_find_event(thd, spn, &definer, &ett, NULL, NULL); - thd->restore_backup_open_tables_state(&backup); - if (ret) - goto done; - - ett->compute_next_execution_time(); - if (use_lock) - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - - evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) ett); - - /* - There is a copy in the array which we don't need. sphead won't be - destroyed. - */ - - if (use_lock) - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - -done: - if (thd->mem_root != tmp_mem_root) - thd->mem_root= tmp_mem_root; - - DBUG_RETURN(ret); -} - - -/* - Removes from queue in memory the event which is identified by the tupple - (db, name). - - SYNOPSIS - evex_remove_from_cache() - - db - db name - name - event name - use_lock - whether to lock the mutex LOCK_event_arrays or not in case it - has been already locked outside - is_drop - if an event is currently being executed then we can also delete - the Event_timed instance, so we alarm the event that it should - drop itself if this parameter is set to TRUE. It's false on - ALTER EVENT. - - RETURNS - 0 OK (always) -*/ - -static int -evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock, - bool is_drop) -{ - //ToDo : Add definer to the tuple (db, name) to become triple - uint i; - int ret= 0; - - DBUG_ENTER("evex_remove_from_cache"); - /* - It is possible that 2 (or 1) pass(es) won't find the event in memory. - The reason is that DISABLED events are not cached. - */ - - if (use_lock) - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - - for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) - { - Event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, Event_timed*); - DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, et->dbname.str, - et->name.str)); - if (!sortcmp_lex_string(*name, et->name, system_charset_info) && - !sortcmp_lex_string(*db, et->dbname, system_charset_info)) - { - if (et->can_spawn_now()) - { - DBUG_PRINT("evex_remove_from_cache", ("not running - free and delete")); - et->free_sp(); - delete et; - } - else - { - DBUG_PRINT("evex_remove_from_cache", - ("running.defer mem free. is_drop=%d", is_drop)); - et->flags|= EVENT_EXEC_NO_MORE; - et->dropped= is_drop; - } - DBUG_PRINT("evex_remove_from_cache", ("delete from queue")); - evex_queue_delete_element(&EVEX_EQ_NAME, i); - /* ok, we have cleaned */ - ret= 0; - goto done; - } - } - -done: - if (use_lock) - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + The function exported to the world for creating of events. - DBUG_RETURN(ret); -} + SYNOPSIS + Events::create_event() + thd THD + et event's data + create_options Options specified when in the query. We are + interested whether there is IF NOT EXISTS + rows_affected How many rows were affected + RETURN VALUE + 0 OK + !0 Error -/* - The function exported to the world for creating of events. - - SYNOPSIS - evex_create_event() - thd THD - et event's data - create_options Options specified when in the query. We are - interested whether there is IF NOT EXISTS - rows_affected How many rows were affected - - NOTES - - in case there is an event with the same name (db) and - IF NOT EXISTS is specified, an warning is put into the W stack. + NOTES + - in case there is an event with the same name (db) and + IF NOT EXISTS is specified, an warning is put into the W stack. */ int -evex_create_event(THD *thd, Event_timed *et, uint create_options, - uint *rows_affected) +Events::create_event(THD *thd, Event_timed *et, uint create_options, + uint *rows_affected) { - int ret = 0; + int ret; - DBUG_ENTER("evex_create_event"); + DBUG_ENTER("Events::create_event"); DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length, et->name.str, create_options)); - if ((ret = db_create_event(thd, et, + if (!(ret = db_create_event(thd, et, create_options & HA_LEX_CREATE_IF_NOT_EXISTS, rows_affected))) - goto done; - - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (evex_is_running && et->status == MYSQL_EVENT_ENABLED) { - sp_name spn(et->dbname, et->name); - ret= evex_load_and_compile_event(thd, &spn, et->definer, true); + Event_scheduler *scheduler= Event_scheduler::get_instance(); + if (scheduler->initialized() && (ret= scheduler->add_event(thd, et, true))) + my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); } - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - -done: /* No need to close the table, it will be closed in sql_parse::do_command */ DBUG_RETURN(ret); @@ -1195,73 +1017,63 @@ done: /* - The function exported to the world for alteration of events. - - SYNOPSIS - evex_update_event() - thd THD - et event's data - new_name set in case of RENAME TO. - - NOTES - et contains data about dbname and event name. - new_name is the new name of the event, if not null (this means - that RENAME TO was specified in the query) + The function exported to the world for alteration of events. + + SYNOPSIS + Events::update_event() + thd THD + et event's data + new_name set in case of RENAME TO. + + RETURN VALUE + 0 OK + !0 Error + + NOTES + et contains data about dbname and event name. + new_name is the new name of the event, if not null (this means + that RENAME TO was specified in the query) */ int -evex_update_event(THD *thd, Event_timed *et, sp_name *new_name, - uint *rows_affected) +Events::update_event(THD *thd, Event_timed *et, sp_name *new_name, + uint *rows_affected) { int ret; - bool need_second_pass= true; - DBUG_ENTER("evex_update_event"); + DBUG_ENTER("Events::update_event"); DBUG_PRINT("enter", ("name: %*s", et->name.length, et->name.str)); - /* db_update_event() opens & closes the table to prevent crash later in the code when loading and compiling the new definition. Also on error conditions my_error() is called so no need to handle here */ - if ((ret= db_update_event(thd, et, new_name))) - goto done; - - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (!evex_is_running) - UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_evex_running, done); - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - evex_remove_from_cache(&et->dbname, &et->name, false, false); - if (et->status == MYSQL_EVENT_ENABLED) + if (!(ret= db_update_event(thd, et, new_name))) { - if (new_name) - ret= evex_load_and_compile_event(thd, new_name, et->definer, false); - else - { - sp_name spn(et->dbname, et->name); - ret= evex_load_and_compile_event(thd, &spn, et->definer, false); - } - if (ret == EVEX_COMPILE_ERROR) - my_error(ER_EVENT_COMPILE_ERROR, MYF(0)); + Event_scheduler *scheduler= Event_scheduler::get_instance(); + if (scheduler->initialized() && + (ret= scheduler->replace_event(thd, et, + new_name? &new_name->m_db: NULL, + new_name? &new_name->m_name: NULL))) + my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); } - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - -done: DBUG_RETURN(ret); } /* - Drops an event - - SYNOPSIS - db_drop_event() - thd THD - et event's name - drop_if_exists if set and the event not existing => warning onto the stack - rows_affected affected number of rows is returned heres + Drops an event + + SYNOPSIS + db_drop_event() + thd THD + et event's name + drop_if_exists if set and the event not existing => warning onto the stack + rows_affected affected number of rows is returned heres + + RETURN VALUE + 0 OK + !0 Error (my_error() called) */ int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, @@ -1275,7 +1087,7 @@ int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, ret= EVEX_OPEN_TABLE_FAILED; thd->reset_n_backup_open_tables_state(&backup); - if (evex_open_event_table(thd, TL_WRITE, &table)) + if (Events::open_event_table(thd, TL_WRITE, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto done; @@ -1315,58 +1127,54 @@ done: /* - Drops an event - - SYNOPSIS - evex_drop_event() - thd THD - et event's name - drop_if_exists if set and the event not existing => warning onto the stack - rows_affected affected number of rows is returned heres - + Drops an event + + SYNOPSIS + Events::drop_event() + thd THD + et event's name + drop_if_exists if set and the event not existing => warning onto the stack + rows_affected affected number of rows is returned heres + + RETURN VALUE + 0 OK + !0 Error (reported) */ int -evex_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) +Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, + uint *rows_affected) { - int ret= 0; - - DBUG_ENTER("evex_drop_event"); - - - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (evex_is_running) - ret= evex_remove_from_cache(&et->dbname, &et->name, true, true); - VOID(pthread_mutex_unlock(&LOCK_evex_running)); + int ret; - if (ret == 1) - ret= 0; - else if (ret == 0) - ret= db_drop_event(thd, et, drop_if_exists, rows_affected); - else - my_error(ER_UNKNOWN_ERROR, MYF(0)); + DBUG_ENTER("Events::drop_event"); + if (!(ret= db_drop_event(thd, et, drop_if_exists, rows_affected))) + { + Event_scheduler *scheduler= Event_scheduler::get_instance(); + if (scheduler->initialized() && (ret= scheduler->drop_event(thd, et))) + my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); + } DBUG_RETURN(ret); } /* - SHOW CREATE EVENT + SHOW CREATE EVENT - SYNOPSIS - evex_show_create_event() - thd THD - spn the name of the event (db, name) - definer the definer of the event + SYNOPSIS + Events::show_create_event() + thd THD + spn the name of the event (db, name) + definer the definer of the event - RETURNS - 0 - OK - 1 - Error during writing to the wire + RETURN VALUE + 0 OK + 1 Error during writing to the wire */ int -evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) +Events::show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) { int ret; Event_timed *et= NULL; @@ -1379,7 +1187,7 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) ret= db_find_event(thd, spn, &definer, &et, NULL, thd->mem_root); thd->restore_backup_open_tables_state(&backup); - if (et) + if (!ret) { Protocol *protocol= thd->protocol; char show_str_buf[768]; @@ -1389,12 +1197,10 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) ulong sql_mode_len=0; show_str.length(0); + show_str.set_charset(system_charset_info); if (et->get_create_event(thd, &show_str)) - { - delete et; - DBUG_RETURN(1); - } + goto err; field_list.push_back(new Item_empty_string("Event", NAME_LEN)); @@ -1408,201 +1214,216 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) show_str.length())); if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - { - delete et; - DBUG_RETURN(1); - } + goto err; + protocol->prepare_for_resend(); protocol->store(et->name.str, et->name.length, system_charset_info); protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); - protocol->store(show_str.ptr(), show_str.length(), system_charset_info); + protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info); ret= protocol->write(); send_eof(thd); - delete et; } - + delete et; DBUG_RETURN(ret); +err: + delete et; + DBUG_RETURN(1); } /* - evex_drop_db_events - Drops all events in the selected database + Drops all events from a schema - thd - Thread - db - ASCIIZ the name of the database - - Returns: - 0 - OK - 1 - Failed to delete a specific row - 2 - Got NULL while reading db name from a row - - Note: - The algo is the following - 1. Go through the in-memory cache, if the scheduler is working - and for every event whose dbname matches the database we drop - check whether is currently in execution: - - Event_timed::can_spawn() returns true -> the event is not - being executed in a child thread. The reason not to use - Event_timed::is_running() is that the latter shows only if - it is being executed, which is 99% of the time in the thread - but there are some initiliazations before and after the - anonymous SP is being called. So if we delete in this moment - -=> *boom*, so we have to check whether the thread has been - spawned and can_spawn() is the right method. - - Event_timed::can_spawn() returns false -> being runned ATM - just set the flags so it should drop itself. + SYNOPSIS + Events::drop_schema_events() + thd Thread + db ASCIIZ schema name + + RETURN VALUE + 0 OK + !0 Error */ int -evex_drop_db_events(THD *thd, char *db) +Events::drop_schema_events(THD *thd, char *db) { - TABLE *table; - READ_RECORD read_record_info; int ret= 0; - uint i; LEX_STRING db_lex= {db, strlen(db)}; DBUG_ENTER("evex_drop_db_events"); - DBUG_PRINT("info",("dropping events from %s", db)); + DBUG_PRINT("enter", ("dropping events from %s", db)); - VOID(pthread_mutex_lock(&LOCK_event_arrays)); + Event_scheduler *scheduler= Event_scheduler::get_instance(); + if (scheduler->initialized()) + ret= scheduler->drop_schema_events(thd, &db_lex); + else + ret= db_drop_events_from_table(thd, &db_lex); - if ((ret= evex_open_event_table(thd, TL_WRITE, &table))) - { - sql_print_error("Table mysql.event is damaged."); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - } + DBUG_RETURN(ret); +} - DBUG_PRINT("info",("%d elements in the queue", - evex_queue_num_elements(EVEX_EQ_NAME))); - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (!evex_is_running) - goto skip_memory; - for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) - { - Event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, Event_timed*); - if (sortcmp_lex_string(et->dbname, db_lex, system_charset_info)) - continue; +/* + Drops all events in the selected database, from mysql.event. - if (et->can_spawn_now_n_lock(thd)) - { - DBUG_PRINT("info",("event %s not running - direct delete", et->name.str)); - if (!(ret= evex_db_find_event_aux(thd, et, table))) - { - DBUG_PRINT("info",("event %s found on disk", et->name.str)); - if ((ret= table->file->ha_delete_row(table->record[0]))) - { - sql_print_error("Error while deleting a row - dropping " - "a database. Skipping the rest."); - my_error(ER_EVENT_DROP_FAILED, MYF(0), et->name.str); - goto end; - } - DBUG_PRINT("info",("deleted event [%s] num [%d]. Time to free mem", - et->name.str, i)); - } - else if (ret == EVEX_KEY_NOT_FOUND) - { - sql_print_error("Expected to find event %s.%s of %s on disk-not there.", - et->dbname.str, et->name.str, et->definer.str); - } - et->free_sp(); - delete et; - et= 0; - /* no need to call et->spawn_unlock because we already cleaned et */ - } - else - { - DBUG_PRINT("info",("event %s is running. setting exec_no_more and dropped", - et->name.str)); - et->flags|= EVENT_EXEC_NO_MORE; - et->dropped= TRUE; - } - DBUG_PRINT("info",("%d elements in the queue", - evex_queue_num_elements(EVEX_EQ_NAME))); - evex_queue_delete_element(&EVEX_EQ_NAME, i);// 0 is top - DBUG_PRINT("info",("%d elements in the queue", - evex_queue_num_elements(EVEX_EQ_NAME))); - /* - decrease so we start at the same position, there will be - less elements in the queue, it will still be ordered so on - next iteration it will be again i the current element or if - no more we finish. - */ - --i; - } + SYNOPSIS + evex_drop_db_events_from_table() + thd Thread + db Schema name -skip_memory: - /* - The reasoning behind having two loops is the following: - If there was only one loop, the table-scan, then for every element which - matches, the queue in memory has to be searched to remove the element. - While if we go first over the queue and remove what's in there we have only - one pass over it and after finishing it, moving to table-scan for the disabled - events. This needs quite less time and means quite less locking on - LOCK_event_arrays. - */ - DBUG_PRINT("info",("Mem-cache checked, now going to db for disabled events")); + RETURN VALUE + 0 OK + !0 Error from ha_delete_row +*/ + +int +db_drop_events_from_table(THD *thd, LEX_STRING *db) +{ + int ret; + TABLE *table; + READ_RECORD read_record_info; + DBUG_ENTER("db_drop_events_from_table"); + DBUG_PRINT("info", ("dropping events from %s", db->str)); + + if ((ret= Events::open_event_table(thd, TL_WRITE, &table))) + { + sql_print_error("Table mysql.event is damaged."); + DBUG_RETURN(ret); + } /* only enabled events are in memory, so we go now and delete the rest */ - init_read_record(&read_record_info, thd, table ,NULL,1,0); + init_read_record(&read_record_info, thd, table, NULL, 1, 0); while (!(read_record_info.read_record(&read_record_info)) && !ret) { - char *et_db; + char *et_db= get_field(thd->mem_root, + table->field[Events::FIELD_DB]); - if ((et_db= get_field(thd->mem_root, table->field[EVEX_FIELD_DB])) == NULL) - { - ret= 2; - break; - } - LEX_STRING et_db_lex= {et_db, strlen(et_db)}; - if (!sortcmp_lex_string(et_db_lex, db_lex, system_charset_info)) + DBUG_PRINT("info", ("Current event %s.%s", et_db, + get_field(thd->mem_root, + table->field[Events::FIELD_NAME]))); + + if (!sortcmp_lex_string(et_db_lex, *db, system_charset_info)) { - Event_timed ett; - char *ptr; - - if ((ptr= get_field(thd->mem_root, table->field[EVEX_FIELD_STATUS])) - == NullS) - { - sql_print_error("Error while loading from mysql.event. " - "Table probably corrupted"); - goto end; - } - /* - When not running nothing is in memory so we have to clean - everything. - We don't delete EVENT_ENABLED events when the scheduler is running - because maybe this is an event which we asked to drop itself when - it is finished and it hasn't finished yet, so we don't touch it. - It will drop itself. The not running ENABLED events has been already - deleted from ha_delete_row() above in the loop over the QUEUE - (in case the executor is running). - 'D' stands for DISABLED, 'E' for ENABLED - it's an enum - */ - if ((evex_is_running && ptr[0] == 'D') || !evex_is_running) - { - DBUG_PRINT("info", ("Dropping %s.%s", et_db, ett.name.str)); - if ((ret= table->file->ha_delete_row(table->record[0]))) - { - my_error(ER_EVENT_DROP_FAILED, MYF(0), ett.name.str); - goto end; - } - } + DBUG_PRINT("info", ("Dropping")); + if ((ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_DROP_FAILED, MYF(0), + get_field(thd->mem_root, + table->field[Events::FIELD_NAME])); } } - DBUG_PRINT("info",("Disk checked for disabled events. Finishing.")); - -end: - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); end_read_record(&read_record_info); - thd->version--; /* Force close to free memory */ close_thread_tables(thd); DBUG_RETURN(ret); } + + + +/* + Inits the scheduler's structures. + + SYNOPSIS + Events::init() + + NOTES + This function is not synchronized. + + RETURN VALUE + 0 OK + 1 Error +*/ + +int +Events::init() +{ + int ret= 0; + DBUG_ENTER("Events::init"); + + /* it should be an assignment! */ + if (opt_event_scheduler) + { + Event_scheduler *scheduler= Event_scheduler::get_instance(); + DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2); + DBUG_RETURN(scheduler->init() || + (opt_event_scheduler == 1? scheduler->start(): + scheduler->start_suspended())); + } + DBUG_RETURN(0); +} + + +/* + Cleans up scheduler's resources. Called at server shutdown. + + SYNOPSIS + Events::shutdown() + + NOTES + This function is not synchronized. +*/ + +void +Events::shutdown() +{ + DBUG_ENTER("Events::shutdown"); + Event_scheduler *scheduler= Event_scheduler::get_instance(); + if (scheduler->initialized()) + { + scheduler->stop(); + scheduler->destroy(); + } + + DBUG_VOID_RETURN; +} + + +/* + Proxy for Event_scheduler::dump_internal_status + + SYNOPSIS + Events::dump_internal_status() + thd Thread + + RETURN VALUE + 0 OK + !0 Error +*/ + +int +Events::dump_internal_status(THD *thd) +{ + return Event_scheduler::dump_internal_status(thd); +} + + +/* + Inits Events mutexes + + SYNOPSIS + Events::init_mutexes() + thd Thread +*/ + +void +Events::init_mutexes() +{ + Event_scheduler::init_mutexes(); +} + + +/* + Destroys Events mutexes + + SYNOPSIS + Events::destroy_mutexes() +*/ + +void +Events::destroy_mutexes() +{ + Event_scheduler::destroy_mutexes(); +} diff --git a/sql/event.h b/sql/event.h index 27de8b46e32..40ede7b0c5f 100644 --- a/sql/event.h +++ b/sql/event.h @@ -1,4 +1,6 @@ -/* Copyright (C) 2004-2005 MySQL AB +#ifndef _EVENT_H_ +#define _EVENT_H_ +/* Copyright (C) 2004-2006 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 @@ -14,66 +16,109 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _EVENT_H_ -#define _EVENT_H_ -#include "sp.h" -#include "sp_head.h" - -#define EVEX_OK SP_OK -#define EVEX_KEY_NOT_FOUND SP_KEY_NOT_FOUND -#define EVEX_OPEN_TABLE_FAILED SP_OPEN_TABLE_FAILED -#define EVEX_WRITE_ROW_FAILED SP_WRITE_ROW_FAILED -#define EVEX_DELETE_ROW_FAILED SP_DELETE_ROW_FAILED -#define EVEX_GET_FIELD_FAILED SP_GET_FIELD_FAILED -#define EVEX_PARSE_ERROR SP_PARSE_ERROR -#define EVEX_INTERNAL_ERROR SP_INTERNAL_ERROR -#define EVEX_NO_DB_ERROR SP_NO_DB_ERROR + +#define EVEX_OK 0 +#define EVEX_KEY_NOT_FOUND -1 +#define EVEX_OPEN_TABLE_FAILED -2 +#define EVEX_WRITE_ROW_FAILED -3 +#define EVEX_DELETE_ROW_FAILED -4 +#define EVEX_GET_FIELD_FAILED -5 +#define EVEX_PARSE_ERROR -6 +#define EVEX_INTERNAL_ERROR -7 +#define EVEX_NO_DB_ERROR -8 #define EVEX_COMPILE_ERROR -19 #define EVEX_GENERAL_ERROR -20 -#define EVEX_BAD_IDENTIFIER SP_BAD_IDENTIFIER -#define EVEX_BODY_TOO_LONG SP_BODY_TOO_LONG -#define EVEX_BAD_PARAMS -21 -#define EVEX_NOT_RUNNING -22 -#define EVEX_MICROSECOND_UNSUP -23 +#define EVEX_BAD_IDENTIFIER -21 +#define EVEX_BODY_TOO_LONG -22 +#define EVEX_BAD_PARAMS -23 +#define EVEX_NOT_RUNNING -24 +#define EVEX_MICROSECOND_UNSUP -25 +#define EVEX_CANT_KILL -26 #define EVENT_EXEC_NO_MORE (1L << 0) #define EVENT_NOT_USED (1L << 1) +#define EVENT_FREE_WHEN_FINISHED (1L << 2) -extern ulong opt_event_executor; +class Event_timed; -enum enum_event_on_completion +class Events { - MYSQL_EVENT_ON_COMPLETION_DROP = 1, - MYSQL_EVENT_ON_COMPLETION_PRESERVE -}; +public: + static ulong opt_event_scheduler; + static TYPELIB opt_typelib; -enum enum_event_status -{ - MYSQL_EVENT_ENABLED = 1, - MYSQL_EVENT_DISABLED + enum enum_table_field + { + FIELD_DB = 0, + FIELD_NAME, + FIELD_BODY, + FIELD_DEFINER, + FIELD_EXECUTE_AT, + FIELD_INTERVAL_EXPR, + FIELD_TRANSIENT_INTERVAL, + FIELD_CREATED, + FIELD_MODIFIED, + FIELD_LAST_EXECUTED, + FIELD_STARTS, + FIELD_ENDS, + FIELD_STATUS, + FIELD_ON_COMPLETION, + FIELD_SQL_MODE, + FIELD_COMMENT, + FIELD_COUNT /* a cool trick to count the number of fields :) */ + }; + + static int + create_event(THD *thd, Event_timed *et, uint create_options, + uint *rows_affected); + + static int + update_event(THD *thd, Event_timed *et, sp_name *new_name, + uint *rows_affected); + + static int + drop_event(THD *thd, Event_timed *et, bool drop_if_exists, + uint *rows_affected); + + static int + open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + + static int + show_create_event(THD *thd, sp_name *spn, LEX_STRING definer); + + static int + reconstruct_interval_expression(String *buf, interval_type interval, + longlong expression); + + static int + drop_schema_events(THD *thd, char *db); + + static int + dump_internal_status(THD *thd); + + static int + init(); + + static void + shutdown(); + + static void + init_mutexes(); + + static void + destroy_mutexes(); + + +private: + /* Prevent use of these */ + Events(const Events &); + void operator=(Events &); }; -enum evex_table_field -{ - EVEX_FIELD_DB = 0, - EVEX_FIELD_NAME, - EVEX_FIELD_BODY, - EVEX_FIELD_DEFINER, - EVEX_FIELD_EXECUTE_AT, - EVEX_FIELD_INTERVAL_EXPR, - EVEX_FIELD_TRANSIENT_INTERVAL, - EVEX_FIELD_CREATED, - EVEX_FIELD_MODIFIED, - EVEX_FIELD_LAST_EXECUTED, - EVEX_FIELD_STARTS, - EVEX_FIELD_ENDS, - EVEX_FIELD_STATUS, - EVEX_FIELD_ON_COMPLETION, - EVEX_FIELD_SQL_MODE, - EVEX_FIELD_COMMENT, - EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ -} ; + + +class sp_head; class Event_timed { @@ -82,12 +127,26 @@ class Event_timed my_bool in_spawned_thread; ulong locked_by_thread_id; my_bool running; + ulong thread_id; pthread_mutex_t LOCK_running; + pthread_cond_t COND_finished; bool status_changed; bool last_executed_changed; public: + enum enum_status + { + ENABLED = 1, + DISABLED + }; + + enum enum_on_completion + { + ON_COMPLETION_DROP = 1, + ON_COMPLETION_PRESERVE + }; + TIME last_executed; LEX_STRING dbname; @@ -111,8 +170,8 @@ public: ulonglong created; ulonglong modified; - enum enum_event_on_completion on_completion; - enum enum_event_status status; + enum enum_on_completion on_completion; + enum enum_status status; sp_head *sphead; ulong sql_mode; const uchar *body_begin; @@ -153,36 +212,15 @@ public: DBUG_ASSERT(0); } + Event_timed(); - Event_timed():in_spawned_thread(0),locked_by_thread_id(0), - running(0), status_changed(false), - last_executed_changed(false), expression(0), created(0), - modified(0), on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), - status(MYSQL_EVENT_ENABLED), sphead(0), sql_mode(0), - body_begin(0), dropped(false), - free_sphead_on_delete(true), flags(0) - - { - pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST); - init(); - } - - ~Event_timed() - { - deinit_mutexes(); - - if (free_sphead_on_delete) - free_sp(); - } + ~Event_timed(); void init(); - + void - deinit_mutexes() - { - pthread_mutex_destroy(&this->LOCK_running); - } + deinit_mutexes(); int init_definer(THD *thd); @@ -214,12 +252,12 @@ public: bool compute_next_execution_time(); - void - mark_last_executed(THD *thd); - int drop(THD *thd); + void + mark_last_executed(THD *thd); + bool update_fields(THD *thd); @@ -227,142 +265,32 @@ public: get_create_event(THD *thd, String *buf); int - execute(THD *thd, MEM_ROOT *mem_root= NULL); + execute(THD *thd, MEM_ROOT *mem_root); int - compile(THD *thd, MEM_ROOT *mem_root= NULL); - - my_bool - is_running() - { - my_bool ret; - - VOID(pthread_mutex_lock(&this->LOCK_running)); - ret= running; - VOID(pthread_mutex_unlock(&this->LOCK_running)); - - return ret; - } - - /* - Checks whether the object is being used in a spawned thread. - This method is for very basic checking. Use ::can_spawn_now_n_lock() - for most of the cases. - */ - - my_bool - can_spawn_now() - { - my_bool ret; - VOID(pthread_mutex_lock(&this->LOCK_running)); - ret= !in_spawned_thread; - VOID(pthread_mutex_unlock(&this->LOCK_running)); - return ret; - } + compile(THD *thd, MEM_ROOT *mem_root); - /* - Checks whether this thread can lock the object for modification -> - preventing being spawned for execution, and locks if possible. - use ::can_spawn_now() only for basic checking because a race - condition may occur between the check and eventual modification (deletion) - of the object. - */ - - my_bool - can_spawn_now_n_lock(THD *thd); - - int - spawn_unlock(THD *thd); + bool + is_running(); int - spawn_now(void * (*thread_func)(void*)); + spawn_now(void * (*thread_func)(void*), void *arg); - void + bool spawn_thread_finish(THD *thd); void - free_sp() - { - delete sphead; - sphead= 0; - } -protected: + free_sp(); + bool - change_security_context(THD *thd, Security_context *s_ctx, - Security_context **backup); + has_equal_db(Event_timed *etn); + + int + kill_thread(THD *thd); void - restore_security_context(THD *thd, Security_context *backup); + set_thread_id(ulong tid) { thread_id= tid; } }; -int -evex_create_event(THD *thd, Event_timed *et, uint create_options, - uint *rows_affected); - -int -evex_update_event(THD *thd, Event_timed *et, sp_name *new_name, - uint *rows_affected); - -int -evex_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); - -int -evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - -int -evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer); - -int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); - -int -event_reconstruct_interval_expression(String *buf, - interval_type interval, - longlong expression); - -int -evex_drop_db_events(THD *thd, char *db); - - -int -init_events(); - -void -shutdown_events(); - - -// auxiliary -int -event_timed_compare(Event_timed **a, Event_timed **b); - - - -/* -CREATE TABLE event ( - db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - body longblob NOT NULL, - definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - execute_at DATETIME default NULL, - interval_value int(11) default NULL, - interval_field ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK', - 'SECOND','MICROSECOND', 'YEAR_MONTH','DAY_HOUR', - 'DAY_MINUTE','DAY_SECOND', - 'HOUR_MINUTE','HOUR_SECOND', - 'MINUTE_SECOND','DAY_MICROSECOND', - 'HOUR_MICROSECOND','MINUTE_MICROSECOND', - 'SECOND_MICROSECOND') default NULL, - created TIMESTAMP NOT NULL, - modified TIMESTAMP NOT NULL, - last_executed DATETIME default NULL, - starts DATETIME default NULL, - ends DATETIME default NULL, - status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', - on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', - comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY (definer,db,name) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; -*/ - #endif /* _EVENT_H_ */ diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 21464dd777b..f236fb47771 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -13,998 +13,3 @@ 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 "event_priv.h" -#include "event.h" -#include "sp.h" - -#define WAIT_STATUS_READY 0 -#define WAIT_STATUS_EMPTY_QUEUE 1 -#define WAIT_STATUS_NEW_TOP_EVENT 2 -#define WAIT_STATUS_STOP_EXECUTOR 3 - - -/* - Make this define DBUG_FAULTY_THR to be able to put breakpoints inside - code used by the scheduler's thread(s). In this case user connections - are not possible because the scheduler thread code is ran inside the - main thread (no spawning takes place. If you want to debug client - connection then start with --one-thread and make the define - DBUG_FAULTY_THR ! -*/ -#define DBUG_FAULTY_THR2 - -extern ulong thread_created; -extern const char *my_localhost; -extern pthread_attr_t connection_attrib; - -pthread_mutex_t LOCK_event_arrays, // mutex for when working with the queue - LOCK_workers_count, // mutex for when inc/dec uint workers_count - LOCK_evex_running; // mutes for managing bool evex_is_running - -static pthread_mutex_t LOCK_evex_main_thread; // mutex for when working with the queue -bool scheduler_main_thread_running= false; - -bool evex_is_running= false; - -ulonglong evex_main_thread_id= 0; -ulong opt_event_executor; -my_bool event_executor_running_global_var; -static my_bool evex_mutexes_initted= FALSE; -static uint workers_count; - -static int -evex_load_events_from_db(THD *thd); - -bool -evex_print_warnings(THD *thd, Event_timed *et); - -/* - TODO Andrey: Check for command line option whether to start - the main thread or not. -*/ - -pthread_handler_t -event_executor_worker(void *arg); - -pthread_handler_t -event_executor_main(void *arg); - - -/* - Returns the seconds difference of 2 TIME structs - - SYNOPSIS - evex_time_diff() - a - TIME struct 1 - b - TIME struct 2 - - Returns: - the seconds difference -*/ - -static int -evex_time_diff(TIME *a, TIME *b) -{ - return sec_since_epoch_TIME(a) - sec_since_epoch_TIME(b); -} - - -/* - Inits the mutexes used by the scheduler module - - SYNOPSIS - evex_init_mutexes() - - NOTES - The mutexes are : - LOCK_event_arrays - LOCK_workers_count - LOCK_evex_running -*/ - -static void -evex_init_mutexes() -{ - if (evex_mutexes_initted) - return; - - evex_mutexes_initted= TRUE; - pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_evex_main_thread, MY_MUTEX_INIT_FAST); - - event_executor_running_global_var= opt_event_executor; -} - -extern TABLE_FIELD_W_TYPE mysql_db_table_fields[]; -extern time_t mysql_db_table_last_check; - -/* - Opens mysql.db and mysql.user and checks whether - 1. mysql.db has column Event_priv at column 20 (0 based); - 2. mysql.user has column Event_priv at column 29 (0 based); - - Synopsis - evex_check_system_tables() -*/ - -void -evex_check_system_tables() -{ - THD *thd= current_thd; - TABLE_LIST tables; - Open_tables_state backup; - - /* thd is 0x0 during boot of the server. Later it's !=0x0 */ - if (!thd) - return; - - thd->reset_n_backup_open_tables_state(&backup); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "db"; - tables.lock_type= TL_READ; - - if (simple_open_n_lock_tables(thd, &tables)) - sql_print_error("Cannot open mysql.db"); - else - { - table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, - &mysql_db_table_last_check,ER_CANNOT_LOAD_FROM_TABLE); - close_thread_tables(thd); - } - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "user"; - tables.lock_type= TL_READ; - - if (simple_open_n_lock_tables(thd, &tables)) - sql_print_error("Cannot open mysql.db"); - else - { - if (tables.table->s->fields < 29 || - strncmp(tables.table->field[29]->field_name, - STRING_WITH_LEN("Event_priv"))) - sql_print_error("mysql.user has no `Event_priv` column at position 29"); - - close_thread_tables(thd); - } - - thd->restore_backup_open_tables_state(&backup); -} - - -/* - Inits the scheduler. Called on server start and every time the scheduler - is started with switching the event_scheduler global variable to TRUE - - SYNOPSIS - init_events() - - NOTES - Inits the mutexes used by the scheduler. Done at server start. -*/ - -int -init_events() -{ - pthread_t th; - DBUG_ENTER("init_events"); - - DBUG_PRINT("info",("Starting events main thread")); - - evex_check_system_tables(); - - evex_init_mutexes(); - - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - if (event_executor_running_global_var) - { -#ifndef DBUG_FAULTY_THR - /* TODO Andrey: Change the error code returned! */ - if (pthread_create(&th, &connection_attrib, event_executor_main,(void*)NULL)) - DBUG_RETURN(ER_SLAVE_THREAD); -#else - event_executor_main(NULL); -#endif - } - - DBUG_RETURN(0); -} - - -/* - Cleans up scheduler memory. Called on server shutdown. - - SYNOPSIS - shutdown_events() - - NOTES - Destroys the mutexes. -*/ - -void -shutdown_events() -{ - DBUG_ENTER("shutdown_events"); - - if (evex_mutexes_initted) - { - evex_mutexes_initted= FALSE; - VOID(pthread_mutex_lock(&LOCK_evex_running)); - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - pthread_mutex_destroy(&LOCK_event_arrays); - pthread_mutex_destroy(&LOCK_workers_count); - pthread_mutex_destroy(&LOCK_evex_running); - pthread_mutex_destroy(&LOCK_evex_main_thread); - } - DBUG_VOID_RETURN; -} - - -/* - Inits an scheduler thread handler, both the main and a worker - - SYNOPSIS - init_event_thread() - thd - the THD of the thread. Has to be allocated by the caller. - - NOTES - 1. The host of the thead is my_localhost - 2. thd->net is initted with NULL - no communication. - - Returns - 0 - OK - -1 - Error -*/ - -static int -init_event_thread(THD* thd) -{ - DBUG_ENTER("init_event_thread"); - thd->client_capabilities= 0; - thd->security_ctx->master_access= 0; - thd->security_ctx->db_access= 0; - thd->security_ctx->host_or_ip= (char*)my_localhost; - my_net_init(&thd->net, 0); - thd->net.read_timeout = slave_net_timeout; - thd->slave_thread= 0; - thd->options|= OPTION_AUTO_IS_NULL; - thd->client_capabilities= CLIENT_LOCAL_FILES; - thd->real_id=pthread_self(); - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->thread_id= thread_id++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - if (init_thr_lock() || thd->store_globals()) - { - thd->cleanup(); - delete thd; - DBUG_RETURN(-1); - } - -#if !defined(__WIN__) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif - - thd->proc_info= "Initialized"; - thd->version= refresh_version; - thd->set_time(); - DBUG_RETURN(0); -} - - -/* - This function waits till the time next event in the queue should be - executed. - - Returns - WAIT_STATUS_READY There is an event to be executed right now - WAIT_STATUS_EMPTY_QUEUE No events or the last event was dropped. - WAIT_STATUS_NEW_TOP_EVENT New event has entered the queue and scheduled - on top. Restart ticking. - WAIT_STATUS_STOP_EXECUTOR The thread was killed or SET global event_scheduler=0; -*/ - -static int -executor_wait_till_next_event_exec(THD *thd) -{ - Event_timed *et; - TIME time_now; - int t2sleep; - - DBUG_ENTER("executor_wait_till_next_event_exec"); - /* - now let's see how much time to sleep, we know there is at least 1 - element in the queue. - */ - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - if (!evex_queue_num_elements(EVEX_EQ_NAME)) - { - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - DBUG_RETURN(WAIT_STATUS_EMPTY_QUEUE); - } - et= evex_queue_first_element(&EVEX_EQ_NAME, Event_timed*); - DBUG_ASSERT(et); - if (et->status == MYSQL_EVENT_DISABLED) - { - DBUG_PRINT("evex main thread",("Now it is disabled-exec no more")); - if (et->dropped) - et->drop(thd); - delete et; - evex_queue_delete_element(&EVEX_EQ_NAME, 0);// 0 is top, internally 1 - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - sql_print_information("Event found disabled, dropping."); - DBUG_RETURN(1); - } - - DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); - /* set the internal clock of thd */ - thd->end_time(); - my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); - t2sleep= evex_time_diff(&et->execute_at, &time_now); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - - t2sleep*=20; - DBUG_PRINT("evex main thread",("unlocked LOCK_event_arrays")); - if (t2sleep > 0) - { - ulonglong modified= et->modified; - /* - We sleep t2sleep seconds but we check every second whether this thread - has been killed, or there is a new candidate - */ - while (t2sleep-- && !thd->killed && event_executor_running_global_var && - evex_queue_num_elements(EVEX_EQ_NAME) && - (evex_queue_first_element(&EVEX_EQ_NAME, Event_timed*) == et && - evex_queue_first_element(&EVEX_EQ_NAME, Event_timed*)->modified == - modified)) - { - DBUG_PRINT("evex main thread",("will sleep a bit more.")); - my_sleep(50000); - } - DBUG_PRINT("info",("saved_modified=%llu current=%llu", modified, - evex_queue_num_elements(EVEX_EQ_NAME)? - evex_queue_first_element(&EVEX_EQ_NAME, Event_timed*)->modified: - (ulonglong)~0)); - } - - int ret= WAIT_STATUS_READY; - if (!evex_queue_num_elements(EVEX_EQ_NAME)) - ret= WAIT_STATUS_EMPTY_QUEUE; - else if (evex_queue_first_element(&EVEX_EQ_NAME, Event_timed*) != et) - ret= WAIT_STATUS_NEW_TOP_EVENT; - if (thd->killed && event_executor_running_global_var) - ret= WAIT_STATUS_STOP_EXECUTOR; - - DBUG_RETURN(ret); -} - - -/* - The main scheduler thread. Inits the priority queue on start and - destroys it on thread shutdown. Forks child threads for every event - execution. Sleeps between thread forking and does not do a busy wait. - - SYNOPSIS - event_executor_main() - arg unused - - NOTES - 1. The host of the thead is my_localhost - 2. thd->net is initted with NULL - no communication. - -*/ - -pthread_handler_t -event_executor_main(void *arg) -{ - THD *thd; /* needs to be first for thread_stack */ - uint i=0, j=0; - my_ulonglong cnt= 0; - - DBUG_ENTER("event_executor_main"); - DBUG_PRINT("event_executor_main", ("EVEX thread started")); - - pthread_mutex_lock(&LOCK_evex_main_thread); - if (!scheduler_main_thread_running) - scheduler_main_thread_running= true; - else - { - DBUG_PRINT("event_executor_main", ("already running. thd_id=%d", - evex_main_thread_id)); - pthread_mutex_unlock(&LOCK_evex_main_thread); - my_thread_end(); - pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here - } - pthread_mutex_unlock(&LOCK_evex_main_thread); - - /* init memory root */ - init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - - /* needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff*/ - my_thread_init(); - - if (sizeof(my_time_t) != sizeof(time_t)) - { - sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." - "The scheduler will not work correctly. Stopping."); - DBUG_ASSERT(0); - goto err_no_thd; - } - - /* note that contructor of THD uses DBUG_ ! */ - if (!(thd = new THD)) - { - sql_print_error("SCHEDULER: Cannot create THD for the main thread."); - goto err_no_thd; - } - thd->thread_stack = (char*)&thd; // remember where our stack is - - pthread_detach_this_thread(); - - if (init_event_thread(thd)) - goto finish; - - /* - make this thread visible it has no vio -> show processlist won't see it - unless it's marked as system thread - */ - thd->system_thread= 1; - - VOID(pthread_mutex_lock(&LOCK_thread_count)); - threads.append(thd); - thread_count++; - thread_running++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - DBUG_PRINT("EVEX main thread", ("Initing events_queue")); - - /* - eventually manifest that we are running, not to crashe because of - usage of non-initialized memory structures. - */ - VOID(pthread_mutex_lock(&LOCK_evex_running)); - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - evex_queue_init(&EVEX_EQ_NAME); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - evex_is_running= true; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - thd->security_ctx->user= my_strdup("event_scheduler", MYF(0)); - - if (evex_load_events_from_db(thd)) - goto finish; - - evex_main_thread_id= thd->thread_id; - - sql_print_information("SCHEDULER: Main thread started"); - while (!thd->killed) - { - TIME time_now; - Event_timed *et; - - cnt++; - DBUG_PRINT("info", ("EVEX External Loop %d thd->k", cnt)); - - thd->proc_info = "Sleeping"; - if (!event_executor_running_global_var) - { - sql_print_information("SCHEDULER: Asked to stop."); - break; - } - - if (!evex_queue_num_elements(EVEX_EQ_NAME)) - { - my_sleep(100000);// sleep 0.1s - continue; - } - -restart_ticking: - switch (executor_wait_till_next_event_exec(thd)) { - case WAIT_STATUS_READY: // time to execute the event on top - DBUG_PRINT("evex main thread",("time to execute an event")); - break; - case WAIT_STATUS_EMPTY_QUEUE: // no more events - DBUG_PRINT("evex main thread",("no more events")); - continue; - break; - case WAIT_STATUS_NEW_TOP_EVENT: // new event on top in the queue - DBUG_PRINT("evex main thread",("restart ticking")); - goto restart_ticking; - case WAIT_STATUS_STOP_EXECUTOR: - sql_print_information("SCHEDULER: Asked to stop."); - goto finish; - break; - default: - DBUG_ASSERT(0); - } - - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - thd->end_time(); - my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); - - if (!evex_queue_num_elements(EVEX_EQ_NAME)) - { - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - DBUG_PRINT("evex main thread",("empty queue")); - continue; - } - et= evex_queue_first_element(&EVEX_EQ_NAME, Event_timed*); - DBUG_PRINT("evex main thread",("got event from the queue")); - - if (!et->execute_at_null && my_time_compare(&time_now,&et->execute_at) == -1) - { - DBUG_PRINT("evex main thread",("still not the time for execution")); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - continue; - } - - DBUG_PRINT("evex main thread",("it's right time")); - if (et->status == MYSQL_EVENT_ENABLED) - { - int fork_ret_code; - - DBUG_PRINT("evex main thread", ("[%10s] this exec at [%llu]", et->name.str, - TIME_to_ulonglong_datetime(&et->execute_at))); - et->mark_last_executed(thd); - if (et->compute_next_execution_time()) - { - sql_print_error("SCHEDULER: Error while computing time of %s.%s . " - "Disabling after execution.", - et->dbname.str, et->name.str); - et->status= MYSQL_EVENT_DISABLED; - } - DBUG_PRINT("evex main thread", ("[%10s] next exec at [%llu]", et->name.str, - TIME_to_ulonglong_datetime(&et->execute_at))); - - et->update_fields(thd); -#ifndef DBUG_FAULTY_THR - thread_safe_increment(workers_count, &LOCK_workers_count); - switch ((fork_ret_code= et->spawn_now(event_executor_worker))) { - case EVENT_EXEC_CANT_FORK: - thread_safe_decrement(workers_count, &LOCK_workers_count); - sql_print_error("SCHEDULER: Problem while trying to create a thread"); - UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, finish); - case EVENT_EXEC_ALREADY_EXEC: - thread_safe_decrement(workers_count, &LOCK_workers_count); - sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.", - et->dbname.str, et->name.str); - break; - default: - DBUG_ASSERT(!fork_ret_code); - if (fork_ret_code) - thread_safe_decrement(workers_count, &LOCK_workers_count); - break; - } -#else - event_executor_worker((void *) et); -#endif - /* - 1. For one-time event : year is > 0 and expression is 0 - 2. For recurring, expression is != -=> check execute_at_null in this case - */ - if ((et->execute_at.year && !et->expression) || et->execute_at_null) - et->flags |= EVENT_EXEC_NO_MORE; - - if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) - evex_queue_delete_element(&EVEX_EQ_NAME, 0);// 0 is top, internally 1 - else - evex_queue_first_updated(&EVEX_EQ_NAME); - } - DBUG_PRINT("evex main thread",("unlocking")); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - }/* while */ -finish: - - /* First manifest that this thread does not work and then destroy */ - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; - evex_main_thread_id= 0; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - - /* - TODO: A better will be with a conditional variable - */ - /* - Read workers_count without lock, no need for locking. - In the worst case we have to wait 1sec more. - */ - sql_print_information("SCHEDULER: Stopping. Waiting for worker threads to finish."); - while (1) - { - VOID(pthread_mutex_lock(&LOCK_workers_count)); - if (!workers_count) - { - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - break; - } - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - my_sleep(1000000);// 1s - } - - /* - First we free all objects ... - Lock because a DROP DATABASE could be running in parallel and it locks on these - */ - sql_print_information("SCHEDULER: Emptying the queue."); - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) - { - Event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, Event_timed*); - et->free_sp(); - delete et; - } - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - /* ... then we can thrash the whole queue at once */ - evex_queue_destroy(&EVEX_EQ_NAME); - - thd->proc_info = "Clearing"; - DBUG_ASSERT(thd->net.buff != 0); - net_end(&thd->net); // destructor will not free it, because we are weird - THD_CHECK_SENTRY(thd); - - pthread_mutex_lock(&LOCK_thread_count); - thread_count--; - thread_running--; -#ifndef DBUG_FAULTY_THR - THD_CHECK_SENTRY(thd); - delete thd; -#endif - pthread_mutex_unlock(&LOCK_thread_count); - - -err_no_thd: - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; - event_executor_running_global_var= false; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - free_root(&evex_mem_root, MYF(0)); - sql_print_information("SCHEDULER: Stopped."); - -#ifndef DBUG_FAULTY_THR - pthread_mutex_lock(&LOCK_evex_main_thread); - scheduler_main_thread_running= false; - pthread_mutex_unlock(&LOCK_evex_main_thread); - - my_thread_end(); - pthread_exit(0); -#endif - DBUG_RETURN(0); // Can't return anything here -} - - -/* - Function that executes an event in a child thread. Setups the - environment for the event execution and cleans after that. - - SYNOPSIS - event_executor_worker() - arg The Event_timed object to be processed -*/ - -pthread_handler_t -event_executor_worker(void *event_void) -{ - THD *thd; /* needs to be first for thread_stack */ - Event_timed *event = (Event_timed *) event_void; - MEM_ROOT worker_mem_root; - - DBUG_ENTER("event_executor_worker"); - - init_alloc_root(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - -#ifndef DBUG_FAULTY_THR - my_thread_init(); - - if (!(thd = new THD)) /* note that contructor of THD uses DBUG_ ! */ - { - sql_print_error("SCHEDULER: Cannot create a THD structure in an worker."); - goto err_no_thd; - } - thd->thread_stack = (char*)&thd; // remember where our stack is - thd->mem_root= &worker_mem_root; - - pthread_detach_this_thread(); - - if (init_event_thread(thd)) - goto err; - - thd->init_for_queries(); - - /* make this thread visible it has no vio -> show processlist needs this flag */ - thd->system_thread= 1; - - VOID(pthread_mutex_lock(&LOCK_thread_count)); - threads.append(thd); - thread_count++; - thread_running++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); -#else - thd= current_thd; -#endif - - thd->enable_slow_log= TRUE; - { - int ret; - sql_print_information("SCHEDULER: Executing event %s.%s of %s [EXPR:%d]", - event->dbname.str, event->name.str, - event->definer.str, (int) event->expression); - - ret= event->execute(thd, &worker_mem_root); - - evex_print_warnings(thd, event); - sql_print_information("SCHEDULER: Executed event %s.%s of %s [EXPR:%d]. " - "RetCode=%d", event->dbname.str, event->name.str, - event->definer.str, (int) event->expression, ret); - if (ret == EVEX_COMPILE_ERROR) - sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of", - event->dbname.str, event->name.str, - event->definer.str); - else if (ret == EVEX_MICROSECOND_UNSUP) - sql_print_information("SCHEDULER: MICROSECOND is not supported"); - } - event->spawn_thread_finish(thd); - - -err: - VOID(pthread_mutex_lock(&LOCK_thread_count)); -#ifndef DBUG_FAULTY_THR - thread_count--; - thread_running--; - /* - Some extra safety, which should not been needed (normally, event deletion - should already have done these assignments (each event which sets these - variables is supposed to set them to 0 before terminating)). - */ - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - thd->proc_info = "Clearing"; - DBUG_ASSERT(thd->net.buff != 0); - net_end(&thd->net); // destructor will not free it, because we are weird - THD_CHECK_SENTRY(thd); - - VOID(pthread_mutex_lock(&LOCK_thread_count)); - THD_CHECK_SENTRY(thd); - delete thd; -#endif - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - -err_no_thd: - - free_root(&worker_mem_root, MYF(0)); - thread_safe_decrement(workers_count, &LOCK_workers_count); - -#ifndef DBUG_FAULTY_THR - my_thread_end(); - pthread_exit(0); -#endif - DBUG_RETURN(0); // Can't return anything here -} - - -/* - Loads all ENABLED events from mysql.event into the prioritized - queue. Called during scheduler main thread initialization. Compiles - the events. Creates Event_timed instances for every ENABLED event - from mysql.event. - - SYNOPSIS - evex_load_events_from_db() - thd - Thread context. Used for memory allocation in some cases. - - RETURNS - 0 OK - !0 Error - - NOTES - Reports the error to the console -*/ - -static int -evex_load_events_from_db(THD *thd) -{ - TABLE *table; - READ_RECORD read_record_info; - int ret= -1; - uint count= 0; - - DBUG_ENTER("evex_load_events_from_db"); - - if ((ret= evex_open_event_table(thd, TL_READ, &table))) - { - sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - } - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - - init_read_record(&read_record_info, thd, table ,NULL,1,0); - while (!(read_record_info.read_record(&read_record_info))) - { - Event_timed *et; - if (!(et= new Event_timed)) - { - DBUG_PRINT("evex_load_events_from_db", ("Out of memory")); - ret= -1; - goto end; - } - DBUG_PRINT("evex_load_events_from_db", ("Loading event from row.")); - - if ((ret= et->load_from_row(&evex_mem_root, table))) - { - sql_print_error("SCHEDULER: Error while loading from mysql.event. " - "Table probably corrupted"); - goto end; - } - if (et->status != MYSQL_EVENT_ENABLED) - { - DBUG_PRINT("evex_load_events_from_db",("%s is disabled",et->name.str)); - delete et; - continue; - } - - DBUG_PRINT("evex_load_events_from_db", - ("Event %s loaded from row. Time to compile", et->name.str)); - - switch (ret= et->compile(thd, &evex_mem_root)) { - case EVEX_MICROSECOND_UNSUP: - sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not " - "supported but found in mysql.event"); - goto end; - case EVEX_COMPILE_ERROR: - sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.", - et->dbname.str, et->name.str); - goto end; - default: - break; - } - - /* let's find when to be executed */ - if (et->compute_next_execution_time()) - { - sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." - " Skipping", et->dbname.str, et->name.str); - continue; - } - - DBUG_PRINT("evex_load_events_from_db", ("Adding to the exec list.")); - - evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) et); - DBUG_PRINT("evex_load_events_from_db", ("%p %*s", - et, et->name.length,et->name.str)); - count++; - } - - ret= 0; - -end: - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - end_read_record(&read_record_info); - - /* Force close to free memory */ - thd->version--; - - close_thread_tables(thd); - if (!ret) - sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s"); - DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); - - DBUG_RETURN(ret); -} - - -/* - The update method of the global variable event_scheduler. - If event_scheduler is switched from 0 to 1 then the scheduler main - thread is started. - - SYNOPSIS - event_executor_worker() - thd - Thread context (unused) - car - the new value - - Returns - 0 OK (always) -*/ - -bool -sys_var_event_executor::update(THD *thd, set_var *var) -{ - /* here start the thread if not running. */ - DBUG_ENTER("sys_var_event_executor::update"); - VOID(pthread_mutex_lock(&LOCK_evex_running)); - *value= var->save_result.ulong_value; - - DBUG_PRINT("new_value", ("%d", *value)); - if ((my_bool) *value && !evex_is_running) - { - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - init_events(); - } else - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - DBUG_RETURN(0); -} - - -extern LEX_STRING warning_level_names[]; - -typedef void (*sql_print_xxx_func)(const char *format, ...); -static sql_print_xxx_func sql_print_xxx_handlers[3] = -{ - sql_print_information, - sql_print_warning, - sql_print_error -}; - - -/* - Prints the stack of infos, warnings, errors from thd to - the console so it can be fetched by the logs-into-tables and - checked later. - - Synopsis - evex_print_warnings - thd - thread used during the execution of the event - et - the event itself - - Returns - 0 - OK (always) - -*/ - -bool -evex_print_warnings(THD *thd, Event_timed *et) -{ - MYSQL_ERROR *err; - DBUG_ENTER("evex_show_warnings"); - char msg_buf[1024]; - char prefix_buf[512]; - String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); - prefix.length(0); - - List_iterator_fast<MYSQL_ERROR> it(thd->warn_list); - while ((err= it++)) - { - String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); - /* set it to 0 or we start adding at the end. That's the trick ;) */ - err_msg.length(0); - if (!prefix.length()) - { - prefix.append("SCHEDULER: ["); - - append_identifier(thd,&prefix,et->definer_user.str,et->definer_user.length); - prefix.append('@'); - append_identifier(thd,&prefix,et->definer_host.str,et->definer_host.length); - prefix.append("][", 2); - append_identifier(thd,&prefix, et->dbname.str, et->dbname.length); - prefix.append('.'); - append_identifier(thd,&prefix, et->name.str, et->name.length); - prefix.append("] ", 2); - } - - err_msg.append(prefix); - err_msg.append(err->msg, strlen(err->msg), system_charset_info); - err_msg.append("]"); - DBUG_ASSERT(err->level < 3); - (sql_print_xxx_handlers[err->level])("%*s", err_msg.length(), err_msg.c_ptr()); - } - - - DBUG_RETURN(FALSE); -} diff --git a/sql/event_priv.h b/sql/event_priv.h index 6b23136847e..b0a18377ace 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -1,4 +1,6 @@ -/* Copyright (C) 2004-2005 MySQL AB +#ifndef _EVENT_PRIV_H_ +#define _EVENT_PRIV_H_ +/* Copyright (C) 2004-2006 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 @@ -14,8 +16,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _EVENT_PRIV_H_ -#define _EVENT_PRIV_H_ #include "mysql_priv.h" @@ -23,11 +23,6 @@ #define EVENT_EXEC_ALREADY_EXEC 1 #define EVENT_EXEC_CANT_FORK 2 -#define EVEX_USE_QUEUE - -#define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ - { VOID(pthread_mutex_unlock(&__mutex)); goto __label; } - #define EVEX_DB_FIELD_LEN 64 #define EVEX_NAME_FIELD_LEN 64 #define EVEX_MAX_INTERVAL_VALUE 2147483647L @@ -44,39 +39,49 @@ evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, int event_timed_compare_q(void *vptr, byte* a, byte *b); -int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); - +int +db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, + uint *rows_affected); +int +db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, + TABLE *tbl, MEM_ROOT *root); -#define EXEC_QUEUE_QUEUE_NAME executing_queue -#define EXEC_QUEUE_DARR_NAME evex_executing_queue +int +db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, + uint *rows_affected); +int +db_drop_events_from_table(THD *thd, LEX_STRING *db); -#define EVEX_QUEUE_TYPE QUEUE -#define EVEX_PTOQEL byte * +int +sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); -#define EVEX_EQ_NAME executing_queue -#define evex_queue_first_element(queue, __cast) ((__cast)queue_top(queue)) -#define evex_queue_element(queue, idx, __cast) ((__cast)queue_element(queue, idx)) -#define evex_queue_delete_element(queue, idx) queue_remove(queue, idx) -#define evex_queue_destroy(queue) delete_queue(queue) -#define evex_queue_first_updated(queue) queue_replaced(queue) -#define evex_queue_insert(queue, element) queue_insert_safe(queue, element); +/* Compares only the name part of the identifier */ +bool +event_timed_name_equal(Event_timed *et, LEX_STRING *name); +/* Compares only the schema part of the identifier */ +bool +event_timed_db_equal(Event_timed *et, LEX_STRING *db); +/* + Compares only the definer part of the identifier. Use during DROP USER + to drop user's events. (Still not implemented) +*/ +bool +event_timed_definer_equal(Event_timed *et, LEX_STRING *definer); -void -evex_queue_init(EVEX_QUEUE_TYPE *queue); +/* Compares the whole identifier*/ +bool +event_timed_identifier_equal(Event_timed *a, Event_timed *b); -#define evex_queue_num_elements(queue) queue.elements +bool +change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *s_ctx, + Security_context **backup); -extern bool evex_is_running; -extern MEM_ROOT evex_mem_root; -extern pthread_mutex_t LOCK_event_arrays, - LOCK_workers_count, - LOCK_evex_running; -extern ulonglong evex_main_thread_id; -extern QUEUE EVEX_EQ_NAME; +void +restore_security_context(THD *thd, Security_context *backup); #endif /* _EVENT_PRIV_H_ */ diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc new file mode 100644 index 00000000000..0644f8ec534 --- /dev/null +++ b/sql/event_scheduler.cc @@ -0,0 +1,2418 @@ +/* Copyright (C) 2004-2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 "event_priv.h" +#include "event.h" +#include "event_scheduler.h" +#include "sp_head.h" + +/* + ToDo: + 1. Talk to Alik to get a check for configure.in for my_time_t and time_t + 2. Look at guardian.h|cc to see its life cycle, has similarities. +*/ + + +/* + The scheduler is implemented as class Event_scheduler. Only one instance is + kept during the runtime of the server, by implementing the Singleton DP. + Object instance is always there because the memory is allocated statically + and initialized when the OS loader loads mysqld. This initialization is + bare. Extended initialization is done during the call to + Event_scheduler::init() in Events::init(). The reason for that late initialization + is that some subsystems needed to boot the Scheduler are not available at + earlier stages of the mysqld boot procedure. Events::init() is called in + mysqld.cc . If the mysqld is started with --event-scheduler=0 then + no initialization takes place and the scheduler is unavailable during this + server run. The server should be started with --event-scheduler=1 to have + the scheduler initialized and able to execute jobs. This starting alwa + s implies that the jobs execution will start immediately. If the server + is started with --event-scheduler=2 then the scheduler is started in suspended + state. Default state, if --event-scheduler is not specified is 2. + + The scheduler only manages execution of the events. Their creation, + alteration and deletion is delegated to other routines found in event.cc . + These routines interact with the scheduler : + - CREATE EVENT -> Event_scheduler::add_event() + - ALTER EVENT -> Event_scheduler::replace_event() + - DROP EVENT -> Event_scheduler::drop_event() + + There is one mutex in the single Event_scheduler object which controls + the simultaneous access to the objects invariants. Using one lock makes + it easy to follow the workflow. This mutex is LOCK_scheduler_data. It is + initialized in Event_scheduler::init(). Which in turn is called by the + Facade class Events in event.cc, coming from init_thread_environment() from + mysqld.cc -> no concurrency at this point. It's destroyed in + Events::destroy_mutexes() called from clean_up_mutexes() in mysqld.cc . + + The full initialization is done in Event_scheduler::init() called from + Events::init(). It's done before any requests coming in, so this is a + guarantee for not having concurrency. + + The scheduler is started with Event_scheduler::start() and stopped with + Event_scheduler::stop(). When the scheduler starts it loads all events + from mysql.event table. Unfortunately, there is a race condition between + the event disk management functions and the scheduler ones + (add/replace/drop_event & load_events_from_db()), because the operations + do not happen under one global lock but the disk operations are guarded + by the MYISAM lock on mysql.event. In the same time, the queue operations + are guarded by LOCK_scheduler_data. If the scheduler is start()-ed during + server startup and stopped()-ed during server shutdown (in Events::shutdown() + called by kill_server() in mysqld.cc) these races does not exist. + + Since the user may want to temporarily inhibit execution of events the + scheduler can be suspended and then it can be forced to resume its + operations. The API call to perform these is + Event_scheduler::suspend_or_resume(enum enum_suspend_or_resume) . + When the scheduler is suspended the main scheduler thread, which ATM + happens to have thread_id 1, locks on a condition COND_suspend_or_resume. + When this is signal is sent for the reverse operation the main scheduler + loops continues to roll and execute events. + + When the scheduler is suspended all add/replace/drop_event() operations + work as expected and the modify the queue but no events execution takes + place. + + In contrast to the previous scheduler implementation, found in + event_executor.cc, the start, shutdown, suspend and resume are synchronous + operations. As a whole all operations are synchronized and no busy waits + are used except in stop_all_running_events(), which waits until all + running event worker threads have finished. It would have been nice to + use a conditional on which this method will wait and the last thread to + finish would signal it but this implies subclassing THD. + + The scheduler does not keep a counter of how many event worker threads are + running, at any specific moment, because this will copy functionality + already existing in the server. Namely, all THDs are registered in the + global `threads` array. THD has member variable system_thread which + identifies the type of thread. Connection threads being NON_SYSTEM_THREAD, + all other have their enum value. Important for the scheduler are + SYSTEM_THREAD_EVENT_SCHEDULER and SYSTEM_THREAD_EVENT_WORKER. + + Class THD subclasses class ilink, which is the linked list of all threads. + When a THD instance is destroyed it's being removed from threads, thus + no manual intervention is needed. On the contrary registering is manual + with threads.append() . Traversing the threads array every time a subclass + of THD, for instance if we would have had THD_scheduler_worker to see + how many events we have and whether the scheduler is shutting down will + take much time and lead to a deadlock. stop_all_running_events() is called + under LOCK_scheduler_data. If the THD_scheduler_worker was aware of + the single Event_scheduler instance it will try to check + Event_scheduler::state but for this it would need to acquire + LOCK_scheduler_data => deadlock. Thus stop_all_running_events() uses a + busy wait. + + DROP DATABASE DDL should drop all events defined in a specific schema. + DROP USER also should drop all events who has as definer the user being + dropped (this one is not addressed at the moment but a hook exists). For + this specific needs Event_scheduler::drop_matching_events() is + implemented. Which expects a callback to be applied on every object in + the queue. Thus events that match specific schema or user, will be + removed from the queue. The exposed interface is : + - Event_scheduler::drop_schema_events() + - Event_scheduler::drop_user_events() + + This bulk dropping happens under LOCK_scheduler_data, thus no two or + more threads can execute it in parallel. However, DROP DATABASE is also + synchronized, currently, in the server thus this does not impact the + overall performance. In addition, DROP DATABASE is not that often + executed DDL. + + Though the interface to the scheduler is only through the public methods + of class Event_scheduler, there are currently few functions which are + used during its operations. Namely : + - static evex_print_warnings() + After every event execution all errors/warnings are dumped, so the user + can see in case of a problem what the problem was. + + - static init_event_thread() + This function is both used by event_scheduler_thread() and + event_worker_thread(). It initializes the THD structure. The + initialization looks pretty similar to the one in slave.cc done for the + replication threads. However, though the similarities it cannot be + factored out to have one routine. + + - static event_scheduler_thread() + Because our way to register functions to be used by the threading library + does not allow usage of static methods this function is used to start the + scheduler in it. It does THD initialization and then calls + Event_scheduler::run(). + + - static event_worker_thread() + With already stated the reason for not being able to use methods, this + function executes the worker threads. + + The execution of events is, to some extent, synchronized to inhibit race + conditions when Event_timed::thread_id is being updated with the thread_id of + the THD in which the event is being executed. The thread_id is in the + Event_timed object because we need to be able to kill quickly a specific + event during ALTER/DROP EVENT without traversing the global `threads` array. + However, this makes the scheduler's code more complicated. The event worker + thread is started by Event_timed::spawn_now(), which in turn calls + pthread_create(). The thread_id which will be associated in init_event_thread + is not known in advance thus the registering takes place in + event_worker_thread(). This registering has to be synchronized under + LOCK_scheduler_data, so no kill_event() on a object in + replace_event/drop_event/drop_matching_events() could take place. + + This synchronization is done through class Worker_thread_param that is + local to this file. Event_scheduler::execute_top() is called under + LOCK_scheduler_data. This method : + 1. Creates an instance of Worker_thread_param on the stack + 2. Locks Worker_thread_param::LOCK_started + 3. Calls Event_timed::spawn_now() which in turn creates a new thread. + 4. Locks on Worker_thread_param::COND_started_or_stopped and waits till the + worker thread send signal. The code is spurious wake-up safe because + Worker_thread_param::started is checked. + 5. The worker thread initializes its THD, then sets Event_timed::thread_id, + sets Worker_thread_param::started to TRUE and sends back + Worker_thread_param::COND_started. From this moment on, the event + is being executed and could be killed by using Event_timed::thread_id. + When Event_timed::spawn_thread_finish() is called in the worker thread, + it sets thread_id to 0. From this moment on, the worker thread should not + touch the Event_timed instance. + + + The life-cycle of the server is a FSA. + enum enum_state Event_scheduler::state keeps the state of the scheduler. + + The states are: + + |---UNINITIALIZED + | + | |------------------> IN_SHUTDOWN + --> INITIALIZED -> COMMENCING ---> RUNNING ----------| + ^ ^ | | ^ | + | |- CANTSTART <--| | |- SUSPENDED <-| + |______________________________| + + - UNINITIALIZED :The object is created and only the mutex is initialized + - INITIALIZED :All member variables are initialized + - COMMENCING :The scheduler is starting, no other attempt to start + should succeed before the state is back to INITIALIZED. + - CANTSTART :Set by the ::run() method in case it can't start for some + reason. In this case the connection thread that tries to + start the scheduler sees that some error has occurred and + returns an error to the user. Finally, the connection + thread sets the state to INITIALIZED, so further attempts + to start the scheduler could be made. + - RUNNING :The scheduler is running. New events could be added, + dropped, altered. The scheduler could be stopped. + - SUSPENDED :Like RUNNING but execution of events does not take place. + Operations on the memory queue are possible. + - IN_SHUTDOWN :The scheduler is shutting down, due to request by setting + the global event_scheduler to 0/FALSE, or because of a + KILL command sent by a user to the master thread. + + In every method the macros LOCK_SCHEDULER_DATA() and UNLOCK_SCHEDULER_DATA() + are used for (un)locking purposes. They are used to save the programmer + from typing everytime + lock_data(__FUNCTION__, __LINE__); + All locking goes through Event_scheduler::lock_data() and ::unlock_data(). + These two functions then record in variables where for last time + LOCK_scheduler_data was locked and unlocked (two different variables). In + multithreaded environment, in some cases they make no sense but are useful for + inspecting deadlocks without having the server debug log turned on and the + server is still running. + + The same strategy is used for conditional variables. + Event_scheduler::cond_wait() is invoked from all places with parameter + an enum enum_cond_vars. In this manner, it's possible to inspect the last + on which condition the last call to cond_wait() was waiting. If the server + was started with debug trace switched on, the trace file also holds information + about conditional variables used. +*/ + +#ifdef __GNUC__ +#if __GNUC__ >= 2 +#define SCHED_FUNC __FUNCTION__ +#endif +#else +#define SCHED_FUNC "<unknown>" +#endif + +#define LOCK_SCHEDULER_DATA() lock_data(SCHED_FUNC, __LINE__) +#define UNLOCK_SCHEDULER_DATA() unlock_data(SCHED_FUNC, __LINE__) + + +#ifndef DBUG_OFF +static +LEX_STRING states_names[] = +{ + {(char*) STRING_WITH_LEN("UNINITIALIZED")}, + {(char*) STRING_WITH_LEN("INITIALIZED")}, + {(char*) STRING_WITH_LEN("COMMENCING")}, + {(char*) STRING_WITH_LEN("CANTSTART")}, + {(char*) STRING_WITH_LEN("RUNNING")}, + {(char*) STRING_WITH_LEN("SUSPENDED")}, + {(char*) STRING_WITH_LEN("IN_SHUTDOWN")} +}; +#endif + + +Event_scheduler +Event_scheduler::singleton; + + +const char * const +Event_scheduler::cond_vars_names[Event_scheduler::COND_LAST] = +{ + "new work", + "started or stopped", + "suspend or resume" +}; + + +class Worker_thread_param +{ +public: + Event_timed *et; + pthread_mutex_t LOCK_started; + pthread_cond_t COND_started; + bool started; + + Worker_thread_param(Event_timed *etn):et(etn), started(FALSE) + { + pthread_mutex_init(&LOCK_started, MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_started, NULL); + } + + ~Worker_thread_param() + { + pthread_mutex_destroy(&LOCK_started); + pthread_cond_destroy(&COND_started); + } +}; + + +/* + Prints the stack of infos, warnings, errors from thd to + the console so it can be fetched by the logs-into-tables and + checked later. + + SYNOPSIS + evex_print_warnings + thd - thread used during the execution of the event + et - the event itself +*/ + +static void +evex_print_warnings(THD *thd, Event_timed *et) +{ + MYSQL_ERROR *err; + DBUG_ENTER("evex_print_warnings"); + if (!thd->warn_list.elements) + DBUG_VOID_RETURN; + + char msg_buf[10 * STRING_BUFFER_USUAL_SIZE]; + char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE]; + String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); + prefix.length(0); + prefix.append("SCHEDULER: ["); + + append_identifier(thd, &prefix, et->definer_user.str, et->definer_user.length); + prefix.append('@'); + append_identifier(thd, &prefix, et->definer_host.str, et->definer_host.length); + prefix.append("][", 2); + append_identifier(thd,&prefix, et->dbname.str, et->dbname.length); + prefix.append('.'); + append_identifier(thd,&prefix, et->name.str, et->name.length); + prefix.append("] ", 2); + + List_iterator_fast<MYSQL_ERROR> it(thd->warn_list); + while ((err= it++)) + { + String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); + /* set it to 0 or we start adding at the end. That's the trick ;) */ + err_msg.length(0); + err_msg.append(prefix); + err_msg.append(err->msg, strlen(err->msg), system_charset_info); + err_msg.append("]"); + DBUG_ASSERT(err->level < 3); + (sql_print_message_handlers[err->level])("%*s", err_msg.length(), + err_msg.c_ptr()); + } + DBUG_VOID_RETURN; +} + + +/* + Inits an scheduler thread handler, both the main and a worker + + SYNOPSIS + init_event_thread() + thd - the THD of the thread. Has to be allocated by the caller. + + NOTES + 1. The host of the thead is my_localhost + 2. thd->net is initted with NULL - no communication. + + RETURN VALUE + 0 OK + -1 Error +*/ + +static int +init_event_thread(THD** t, enum enum_thread_type thread_type) +{ + THD *thd= *t; + thd->thread_stack= (char*)t; // remember where our stack is + DBUG_ENTER("init_event_thread"); + thd->client_capabilities= 0; + thd->security_ctx->master_access= 0; + thd->security_ctx->db_access= 0; + thd->security_ctx->host_or_ip= (char*)my_localhost; + my_net_init(&thd->net, 0); + thd->net.read_timeout= slave_net_timeout; + thd->slave_thread= 0; + thd->options|= OPTION_AUTO_IS_NULL; + thd->client_capabilities|= CLIENT_MULTI_RESULTS; + thd->real_id=pthread_self(); + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->thread_id= thread_id++; + threads.append(thd); + thread_count++; + thread_running++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + if (init_thr_lock() || thd->store_globals()) + { + thd->cleanup(); + DBUG_RETURN(-1); + } + +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + + /* + Guarantees that we will see the thread in SHOW PROCESSLIST though its + vio is NULL. + */ + thd->system_thread= thread_type; + + thd->proc_info= "Initialized"; + thd->version= refresh_version; + thd->set_time(); + + DBUG_RETURN(0); +} + + +/* + Inits the main scheduler thread and then calls Event_scheduler::run() + of arg. + + SYNOPSIS + event_scheduler_thread() + arg void* ptr to Event_scheduler + + NOTES + 1. The host of the thead is my_localhost + 2. thd->net is initted with NULL - no communication. + 3. The reason to have a proxy function is that it's not possible to + use a method as function to be executed in a spawned thread: + - our pthread_hander_t macro uses extern "C" + - separating thread setup from the real execution loop is also to be + considered good. + + RETURN VALUE + 0 OK +*/ + +pthread_handler_t +event_scheduler_thread(void *arg) +{ + /* needs to be first for thread_stack */ + THD *thd= NULL; + Event_scheduler *scheduler= (Event_scheduler *) arg; + + DBUG_ENTER("event_scheduler_thread"); + + my_thread_init(); + pthread_detach_this_thread(); + + /* note that constructor of THD uses DBUG_ ! */ + if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_SCHEDULER)) + { + sql_print_error("SCHEDULER: Cannot init manager event thread."); + scheduler->report_error_during_start(); + } + else + { + thd->security_ctx->set_user((char*)"event_scheduler"); + + sql_print_information("SCHEDULER: Manager thread booting"); + if (Event_scheduler::check_system_tables(thd)) + scheduler->report_error_during_start(); + else + scheduler->run(thd); + + /* + NOTE: Don't touch `scheduler` after this point because we have notified + the + thread which shuts us down that we have finished cleaning. In this + very moment a new scheduler thread could be started and a crash is + not welcome. + */ + } + + /* + If we cannot create THD then don't decrease because we haven't touched + thread_count and thread_running in init_event_thread() which was never + called. In init_event_thread() thread_count and thread_running are + always increased even in the case the method returns an error. + */ + if (thd) + { + thd->proc_info= "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + thread_running--; + delete thd; + pthread_mutex_unlock(&LOCK_thread_count); + } + my_thread_end(); + DBUG_RETURN(0); // Can't return anything here +} + + +/* + Function that executes an event in a child thread. Setups the + environment for the event execution and cleans after that. + + SYNOPSIS + event_worker_thread() + arg The Event_timed object to be processed + + RETURN VALUE + 0 OK +*/ + +pthread_handler_t +event_worker_thread(void *arg) +{ + THD *thd; /* needs to be first for thread_stack */ + Worker_thread_param *param= (Worker_thread_param *) arg; + Event_timed *event= param->et; + int ret; + bool startup_error= FALSE; + Security_context *save_ctx; + /* this one is local and not needed after exec */ + Security_context security_ctx; + + DBUG_ENTER("event_worker_thread"); + DBUG_PRINT("enter", ("event=[%s.%s]", event->dbname.str, event->name.str)); + + my_thread_init(); + pthread_detach_this_thread(); + + if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_WORKER)) + { + sql_print_error("SCHEDULER: Startup failure."); + startup_error= TRUE; + event->spawn_thread_finish(thd); + } + else + event->set_thread_id(thd->thread_id); + + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + /* + If we don't change it before we send the signal back, then an intermittent + DROP EVENT will take LOCK_scheduler_data and try to kill this thread, because + event->thread_id is already real. However, because thd->security_ctx->user + is not initialized then a crash occurs in kill_one_thread(). Thus, we have + to change the context before sending the signal. We are under + LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top(). + */ + change_security_context(thd, event->definer_user, event->definer_host, + event->dbname, &security_ctx, &save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + + /* Signal the scheduler thread that we have started successfully */ + pthread_mutex_lock(¶m->LOCK_started); + param->started= TRUE; + pthread_cond_signal(¶m->COND_started); + pthread_mutex_unlock(¶m->LOCK_started); + + if (!startup_error) + { + thd->init_for_queries(); + thd->enable_slow_log= TRUE; + + event->set_thread_id(thd->thread_id); + sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu", + event->dbname.str, event->name.str, + event->definer.str, thd->thread_id); + + ret= event->execute(thd, thd->mem_root); + evex_print_warnings(thd, event); + sql_print_information("SCHEDULER: [%s.%s of %s] executed. RetCode=%d", + event->dbname.str, event->name.str, + event->definer.str, ret); + if (ret == EVEX_COMPILE_ERROR) + sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s", + event->dbname.str, event->name.str, + event->definer.str); + else if (ret == EVEX_MICROSECOND_UNSUP) + sql_print_information("SCHEDULER: MICROSECOND is not supported"); + + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + + /* If true is returned, we are expected to free it */ + if (event->spawn_thread_finish(thd)) + { + DBUG_PRINT("info", ("Freeing object pointer")); + delete event; + } + } + + if (thd) + { + thd->proc_info= "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + /* + Free it here because net.vio is NULL for us => THD::~THD will check it + and won't call net_end(&net); See also replication code. + */ + net_end(&thd->net); + DBUG_PRINT("info", ("Worker thread %lu exiting", thd->thread_id)); + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thread_count--; + thread_running--; + delete thd; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + } + + my_thread_end(); + DBUG_RETURN(0); // Can't return anything here +} + + +/* + Constructor of class Event_scheduler. + + SYNOPSIS + Event_scheduler::Event_scheduler() +*/ + +Event_scheduler::Event_scheduler() + :state(UNINITIALIZED), start_scheduler_suspended(FALSE), + thread_id(0), mutex_last_locked_at_line(0), + mutex_last_unlocked_at_line(0), mutex_last_locked_in_func(""), + mutex_last_unlocked_in_func(""), cond_waiting_on(COND_NONE), + mutex_scheduler_data_locked(FALSE) +{ +} + + +/* + Returns the singleton instance of the class. + + SYNOPSIS + Event_scheduler::get_instance() + + RETURN VALUE + address +*/ + +Event_scheduler* +Event_scheduler::get_instance() +{ + DBUG_ENTER("Event_scheduler::get_instance"); + DBUG_RETURN(&singleton); +} + + +/* + The implementation of full-fledged initialization. + + SYNOPSIS + Event_scheduler::init() + + RETURN VALUE + FALSE OK + TRUE Error +*/ + +bool +Event_scheduler::init() +{ + int i= 0; + bool ret= FALSE; + DBUG_ENTER("Event_scheduler::init"); + DBUG_PRINT("enter", ("this=%p", this)); + + LOCK_SCHEDULER_DATA(); + for (;i < COND_LAST; i++) + if (pthread_cond_init(&cond_vars[i], NULL)) + { + sql_print_error("SCHEDULER: Unable to initalize conditions"); + ret= TRUE; + goto end; + } + + /* init memory root */ + init_alloc_root(&scheduler_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + + if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/, + event_timed_compare_q, NULL, 30 /*auto_extent*/)) + { + sql_print_error("SCHEDULER: Can't initialize the execution queue"); + ret= TRUE; + goto end; + } + + if (sizeof(my_time_t) != sizeof(time_t)) + { + sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." + "The scheduler may not work correctly. Stopping."); + DBUG_ASSERT(0); + ret= TRUE; + goto end; + } + + state= INITIALIZED; +end: + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(ret); +} + + +/* + Frees all memory allocated by the scheduler object. + + SYNOPSIS + Event_scheduler::destroy() + + RETURN VALUE + FALSE OK + TRUE Error +*/ + +void +Event_scheduler::destroy() +{ + DBUG_ENTER("Event_scheduler"); + + LOCK_SCHEDULER_DATA(); + switch (state) { + case UNINITIALIZED: + break; + case INITIALIZED: + delete_queue(&queue); + free_root(&scheduler_root, MYF(0)); + int i; + for (i= 0; i < COND_LAST; i++) + pthread_cond_destroy(&cond_vars[i]); + state= UNINITIALIZED; + break; + default: + sql_print_error("SCHEDULER: Destroying while state is %d", state); + /* I trust my code but ::safe() > ::sorry() */ + DBUG_ASSERT(0); + break; + } + UNLOCK_SCHEDULER_DATA(); + + DBUG_VOID_RETURN; +} + + +/* + Adds an event to the scheduler queue + + SYNOPSIS + Event_scheduler::add_event() + et The event to add + check_existence Whether to check if already loaded. + + RETURN VALUE + OP_OK OK or scheduler not working + OP_LOAD_ERROR Error during loading from disk +*/ + +enum Event_scheduler::enum_error_code +Event_scheduler::add_event(THD *thd, Event_timed *et, bool check_existence) +{ + enum enum_error_code res; + Event_timed *et_new; + DBUG_ENTER("Event_scheduler::add_event"); + DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); + + LOCK_SCHEDULER_DATA(); + if (!is_running_or_suspended()) + { + DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state)); + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(OP_OK); + } + if (check_existence && find_event(et, FALSE)) + { + res= OP_ALREADY_EXISTS; + goto end; + } + + /* We need to load the event on scheduler_root */ + if (!(res= load_named_event(thd, et, &et_new))) + { + queue_insert_safe(&queue, (byte *) et_new); + DBUG_PRINT("info", ("Sending COND_new_work")); + pthread_cond_signal(&cond_vars[COND_new_work]); + } + else if (res == OP_DISABLED_EVENT) + res= OP_OK; +end: + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(res); +} + + +/* + Drops an event from the scheduler queue + + SYNOPSIS + Event_scheduler::drop_event() + etn The event to drop + state Wait the event or kill&drop + + RETURN VALUE + FALSE OK (replaced or scheduler not working) + TRUE Failure +*/ + +bool +Event_scheduler::drop_event(THD *thd, Event_timed *et) +{ + int res; + Event_timed *et_old; + DBUG_ENTER("Event_scheduler::drop_event"); + DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); + + LOCK_SCHEDULER_DATA(); + if (!is_running_or_suspended()) + { + DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state)); + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(OP_OK); + } + + if (!(et_old= find_event(et, TRUE))) + DBUG_PRINT("info", ("No such event found, probably DISABLED")); + + UNLOCK_SCHEDULER_DATA(); + + /* See comments in ::replace_event() why this is split in two parts. */ + if (et_old) + { + switch ((res= et_old->kill_thread(thd))) { + case EVEX_CANT_KILL: + /* Don't delete but continue */ + et_old->flags |= EVENT_FREE_WHEN_FINISHED; + break; + case 0: + /* + kill_thread() waits till the spawned thread finishes after it's + killed. Hence, we delete here memory which is no more referenced from + a running thread. + */ + delete et_old; + /* + We don't signal COND_new_work here because: + 1. Even if the dropped event is on top of the queue this will not + move another one to be executed before the time the one on the + top (but could be at the same second as the dropped one) + 2. If this was the last event on the queue, then pthread_cond_timedwait + in ::run() will finish and then see that the queue is empty and + call cond_wait(). Hence, no need to interrupt the blocked + ::run() thread. + */ + break; + default: + sql_print_error("SCHEDULER: Got unexpected error %d", res); + DBUG_ASSERT(0); + } + } + + DBUG_RETURN(FALSE); +} + + +/* + Replaces an event in the scheduler queue + + SYNOPSIS + Event_scheduler::replace_event() + et The event to replace(add) into the queue + state Async or sync stopping + + RETURN VALUE + OP_OK OK or scheduler not working + OP_LOAD_ERROR Error during loading from disk + OP_ALREADY_EXISTS Event already in the queue +*/ + +enum Event_scheduler::enum_error_code +Event_scheduler::replace_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, + LEX_STRING *new_name) +{ + enum enum_error_code res; + Event_timed *et_old, *et_new= NULL; + LEX_STRING old_schema, old_name; + + DBUG_ENTER("Event_scheduler::replace_event"); + DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p", + thd, et, et->dbname.str, et->name.str, &LOCK_scheduler_data)); + + LOCK_SCHEDULER_DATA(); + if (!is_running_or_suspended()) + { + DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state)); + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(OP_OK); + } + + if (!(et_old= find_event(et, TRUE))) + DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED", + et->dbname.str, et->name.str)); + + if (new_schema && new_name) + { + old_schema= et->dbname; + old_name= et->name; + et->dbname= *new_schema; + et->name= *new_name; + } + /* + We need to load the event (it's strings but on the object itself) + on scheduler_root. et_new could be NULL : + 1. Error occured + 2. If the replace is DISABLED, we don't load it into the queue. + */ + if (!(res= load_named_event(thd, et, &et_new))) + { + queue_insert_safe(&queue, (byte *) et_new); + DBUG_PRINT("info", ("Sending COND_new_work")); + pthread_cond_signal(&cond_vars[COND_new_work]); + } + else if (res == OP_DISABLED_EVENT) + res= OP_OK; + + if (new_schema && new_name) + { + et->dbname= old_schema; + et->name= old_name; + } + + UNLOCK_SCHEDULER_DATA(); + /* + Andrey: Is this comment still truthful ??? + + We don't move this code above because a potential kill_thread will call + THD::awake(). Which in turn will try to acqure mysys_var->current_mutex, + which is LOCK_scheduler_data on which the COND_new_work in ::run() locks. + Hence, we try to acquire a lock which we have already acquired and we run + into an assert. Holding LOCK_scheduler_data however is not needed because + we don't touch any invariant of the scheduler anymore. ::drop_event() does + the same. + */ + if (et_old) + { + switch (et_old->kill_thread(thd)) { + case EVEX_CANT_KILL: + /* Don't delete but continue */ + et_old->flags |= EVENT_FREE_WHEN_FINISHED; + break; + case 0: + /* + kill_thread() waits till the spawned thread finishes after it's + killed. Hence, we delete here memory which is no more referenced from + a running thread. + */ + delete et_old; + /* + We don't signal COND_new_work here because: + 1. Even if the dropped event is on top of the queue this will not + move another one to be executed before the time the one on the + top (but could be at the same second as the dropped one) + 2. If this was the last event on the queue, then pthread_cond_timedwait + in ::run() will finish and then see that the queue is empty and + call cond_wait(). Hence, no need to interrupt the blocked + ::run() thread. + */ + break; + default: + DBUG_ASSERT(0); + } + } + + DBUG_RETURN(res); +} + + +/* + Searches for an event in the scheduler queue + + SYNOPSIS + Event_scheduler::find_event() + etn The event to find + comparator The function to use for comparing + remove_from_q If found whether to remove from the Q + + RETURN VALUE + NULL Not found + otherwise Address + + NOTE + The caller should do the locking also the caller is responsible for + actual signalling in case an event is removed from the queue + (signalling COND_new_work for instance). +*/ + +Event_timed * +Event_scheduler::find_event(Event_timed *etn, bool remove_from_q) +{ + uint i; + DBUG_ENTER("Event_scheduler::find_event"); + + for (i= 0; i < queue.elements; ++i) + { + Event_timed *et= (Event_timed *) queue_element(&queue, i); + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", etn->dbname.str, etn->name.str, + et->dbname.str, et->name.str)); + if (event_timed_identifier_equal(etn, et)) + { + if (remove_from_q) + queue_remove(&queue, i); + DBUG_RETURN(et); + } + } + + DBUG_RETURN(NULL); +} + + +/* + Drops all events from the in-memory queue and disk that match + certain pattern evaluated by a comparator function + + SYNOPSIS + Event_scheduler::drop_matching_events() + thd THD + pattern A pattern string + comparator The function to use for comparing + + RETURN VALUE + -1 Scheduler not working + >=0 Number of dropped events + + NOTE + Expected is the caller to acquire lock on LOCK_scheduler_data +*/ + +void +Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, + bool (*comparator)(Event_timed *,LEX_STRING *)) +{ + DBUG_ENTER("Event_scheduler::drop_matching_events"); + DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str, + state)); + if (is_running_or_suspended()) + { + uint i= 0, dropped= 0; + while (i < queue.elements) + { + Event_timed *et= (Event_timed *) queue_element(&queue, i); + DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str)); + if (comparator(et, pattern)) + { + /* + The queue is ordered. If we remove an element, then all elements after + it will shift one position to the left, if we imagine it as an array + from left to the right. In this case we should not increment the + counter and the (i < queue.elements) condition is ok. + */ + queue_remove(&queue, i); + + /* See replace_event() */ + switch (et->kill_thread(thd)) { + case EVEX_CANT_KILL: + /* Don't delete but continue */ + et->flags |= EVENT_FREE_WHEN_FINISHED; + ++dropped; + break; + case 0: + delete et; + ++dropped; + break; + default: + DBUG_ASSERT(0); + } + } + else + i++; + } + DBUG_PRINT("info", ("Dropped %lu", dropped)); + } + /* + Don't send COND_new_work because no need to wake up the scheduler thread. + When it wakes next time up it will recalculate how much more it should + sleep if the top of the queue has been changed by this method. + */ + + DBUG_VOID_RETURN; +} + + +/* + Drops all events from the in-memory queue and disk that are from + certain schema. + + SYNOPSIS + Event_scheduler::drop_schema_events() + thd THD + db The schema name + + RETURN VALUE + -1 Scheduler not working + >=0 Number of dropped events +*/ + +int +Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema) +{ + int ret; + DBUG_ENTER("Event_scheduler::drop_schema_events"); + LOCK_SCHEDULER_DATA(); + if (is_running_or_suspended()) + drop_matching_events(thd, schema, event_timed_db_equal); + + ret= db_drop_events_from_table(thd, schema); + UNLOCK_SCHEDULER_DATA(); + + DBUG_RETURN(ret); +} + + +extern pthread_attr_t connection_attrib; + + +/* + Starts the event scheduler + + SYNOPSIS + Event_scheduler::start() + + RETURN VALUE + FALSE OK + TRUE Error +*/ + +bool +Event_scheduler::start() +{ + bool ret= FALSE; + pthread_t th; + DBUG_ENTER("Event_scheduler::start"); + + LOCK_SCHEDULER_DATA(); + /* If already working or starting don't make another attempt */ + DBUG_ASSERT(state == INITIALIZED); + if (state > INITIALIZED) + { + DBUG_PRINT("info", ("scheduler is already running or starting")); + ret= TRUE; + goto end; + } + + /* + Now if another thread calls start it will bail-out because the branch + above will be executed. Thus no two or more child threads will be forked. + If the child thread cannot start for some reason then `state` is set + to CANTSTART and COND_started is also signaled. In this case we + set `state` back to INITIALIZED so another attempt to start the scheduler + can be made. + */ + state= COMMENCING; + /* Fork */ + if (pthread_create(&th, &connection_attrib, event_scheduler_thread, + (void*)this)) + { + DBUG_PRINT("error", ("cannot create a new thread")); + state= INITIALIZED; + ret= TRUE; + goto end; + } + + /* Wait till the child thread has booted (w/ or wo success) */ + while (!is_running_or_suspended() && state != CANTSTART) + cond_wait(COND_started_or_stopped, &LOCK_scheduler_data); + + /* + If we cannot start for some reason then don't prohibit further attempts. + Set back to INITIALIZED. + */ + if (state == CANTSTART) + { + state= INITIALIZED; + ret= TRUE; + goto end; + } + +end: + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(ret); +} + + +/* + Starts the event scheduler in suspended mode. + + SYNOPSIS + Event_scheduler::start_suspended() + + RETURN VALUE + TRUE OK + FALSE Error +*/ + +bool +Event_scheduler::start_suspended() +{ + DBUG_ENTER("Event_scheduler::start_suspended"); + start_scheduler_suspended= TRUE; + DBUG_RETURN(start()); +} + + + +/* + Report back that we cannot start. Used for ocasions where + we can't go into ::run() and have to report externally. + + SYNOPSIS + Event_scheduler::report_error_during_start() +*/ + +inline void +Event_scheduler::report_error_during_start() +{ + DBUG_ENTER("Event_scheduler::report_error_during_start"); + + LOCK_SCHEDULER_DATA(); + state= CANTSTART; + DBUG_PRINT("info", ("Sending back COND_started_or_stopped")); + pthread_cond_signal(&cond_vars[COND_started_or_stopped]); + UNLOCK_SCHEDULER_DATA(); + + DBUG_VOID_RETURN; +} + + +/* + The internal loop of the event scheduler + + SYNOPSIS + Event_scheduler::run() + thd Thread + + RETURN VALUE + FALSE OK + TRUE Failure +*/ + +bool +Event_scheduler::run(THD *thd) +{ + int ret; + struct timespec abstime; + DBUG_ENTER("Event_scheduler::run"); + DBUG_PRINT("enter", ("thd=%p", thd)); + + LOCK_SCHEDULER_DATA(); + ret= load_events_from_db(thd); + + if (!ret) + { + thread_id= thd->thread_id; + state= start_scheduler_suspended? SUSPENDED:RUNNING; + start_scheduler_suspended= FALSE; + } + else + state= CANTSTART; + + DBUG_PRINT("info", ("Sending back COND_started_or_stopped")); + pthread_cond_signal(&cond_vars[COND_started_or_stopped]); + if (ret) + { + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(TRUE); + } + if (!check_n_suspend_if_needed(thd)) + UNLOCK_SCHEDULER_DATA(); + + sql_print_information("SCHEDULER: Manager thread started with id %lu", + thd->thread_id); + abstime.tv_nsec= 0; + while (is_running_or_suspended()) + { + Event_timed *et; + + LOCK_SCHEDULER_DATA(); + if (check_n_wait_for_non_empty_queue(thd)) + continue; + + /* On TRUE data is unlocked, go back to the beginning */ + if (check_n_suspend_if_needed(thd)) + continue; + + /* Guaranteed locked here */ + if (state == IN_SHUTDOWN || shutdown_in_progress) + { + UNLOCK_SCHEDULER_DATA(); + break; + } + DBUG_ASSERT(state == RUNNING); + + et= (Event_timed *)queue_top(&queue); + + /* Skip disabled events */ + if (et->status != Event_timed::ENABLED) + { + sql_print_error("SCHEDULER: Found a disabled event %*s.%*s in the queue", + et->dbname.length, et->dbname.str, et->name.length, + et->name.str); + queue_remove(&queue, 0); + /* ToDo: check this again */ + delete et; + UNLOCK_SCHEDULER_DATA(); + continue; + } + thd->proc_info= (char *)"Computing"; + DBUG_PRINT("evex manager",("computing time to sleep till next exec")); + /* Timestamp is in UTC */ + abstime.tv_sec= sec_since_epoch_TIME(&et->execute_at); + + thd->end_time(); + if (abstime.tv_sec > thd->query_start()) + { + /* Event trigger time is in the future */ + thd->proc_info= (char *)"Sleep"; + DBUG_PRINT("info", ("Going to sleep. Should wakeup after approx %d secs", + abstime.tv_sec - thd->query_start())); + DBUG_PRINT("info", ("Entering condition because waiting for activation")); + /* + Use THD::enter_cond()/exit_cond() or we won't be able to kill a + sleeping thread. Though ::stop() can do it by sending COND_new_work + an user can't by just issuing 'KILL x'; . In the latter case + pthread_cond_timedwait() will wait till `abstime`. + "Sleeping until next time" + */ + thd->enter_cond(&cond_vars[COND_new_work],&LOCK_scheduler_data,"Sleeping"); + + pthread_cond_timedwait(&cond_vars[COND_new_work], &LOCK_scheduler_data, + &abstime); + + DBUG_PRINT("info", ("Manager woke up. state is %d", state)); + /* + If we get signal we should recalculate the whether it's the right time + because there could be : + 1. Spurious wake-up + 2. The top of the queue was changed (new one becase of add/drop/replace) + */ + /* This will do implicit UNLOCK_SCHEDULER_DATA() */ + thd->exit_cond(""); + } + else + { + thd->proc_info= (char *)"Executing"; + /* + Execute the event. An error may occur if a thread cannot be forked. + In this case stop the manager. + We should enter ::execute_top() with locked LOCK_scheduler_data. + */ + int ret= execute_top(thd); + UNLOCK_SCHEDULER_DATA(); + if (ret) + break; + } + } + + thd->proc_info= (char *)"Cleaning"; + + LOCK_SCHEDULER_DATA(); + /* + It's possible that a user has used (SQL)COM_KILL. Hence set the appropriate + state because it is only set by ::stop(). + */ + if (state != IN_SHUTDOWN) + { + DBUG_PRINT("info", ("We got KILL but the but not from ::stop()")); + state= IN_SHUTDOWN; + } + UNLOCK_SCHEDULER_DATA(); + + sql_print_information("SCHEDULER: Shutting down"); + + thd->proc_info= (char *)"Cleaning queue"; + clean_queue(thd); + THD_CHECK_SENTRY(thd); + + /* free mamager_root memory but don't destroy the root */ + thd->proc_info= (char *)"Cleaning memory root"; + free_root(&scheduler_root, MYF(0)); + THD_CHECK_SENTRY(thd); + + /* + We notify the waiting thread which shutdowns us that we have cleaned. + There are few more instructions to be executed in this pthread but + they don't affect manager structures thus it's safe to signal already + at this point. + */ + LOCK_SCHEDULER_DATA(); + thd->proc_info= (char *)"Sending shutdown signal"; + DBUG_PRINT("info", ("Sending COND_started_or_stopped")); + if (state == IN_SHUTDOWN) + pthread_cond_signal(&cond_vars[COND_started_or_stopped]); + + state= INITIALIZED; + /* + We set it here because ::run() can stop not only because of ::stop() + call but also because of `KILL x` + */ + thread_id= 0; + sql_print_information("SCHEDULER: Stopped"); + UNLOCK_SCHEDULER_DATA(); + + /* We have modified, we set back */ + thd->query= NULL; + thd->query_length= 0; + + DBUG_RETURN(FALSE); +} + + +/* + Executes the top element of the queue. Auxiliary method for ::run(). + + SYNOPSIS + Event_scheduler::execute_top() + + RETURN VALUE + FALSE OK + TRUE Failure + + NOTE + NO locking is done. EXPECTED is that the caller should have locked + the queue (w/ LOCK_scheduler_data). +*/ + +bool +Event_scheduler::execute_top(THD *thd) +{ + int spawn_ret_code; + bool ret= FALSE; + DBUG_ENTER("Event_scheduler::execute_top"); + DBUG_PRINT("enter", ("thd=%p", thd)); + + Event_timed *et= (Event_timed *)queue_top(&queue); + + /* Is it good idea to pass a stack address ?*/ + Worker_thread_param param(et); + + pthread_mutex_lock(¶m.LOCK_started); + /* + We don't lock LOCK_scheduler_data fpr workers_increment() because it's a + pre-requisite for calling the current_method. + */ + switch ((spawn_ret_code= et->spawn_now(event_worker_thread, ¶m))) { + case EVENT_EXEC_CANT_FORK: + /* + We don't lock LOCK_scheduler_data here because it's a pre-requisite + for calling the current_method. + */ + sql_print_error("SCHEDULER: Problem while trying to create a thread"); + ret= TRUE; + break; + case EVENT_EXEC_ALREADY_EXEC: + /* + We don't lock LOCK_scheduler_data here because it's a pre-requisite + for calling the current_method. + */ + sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.", + et->dbname.str, et->name.str); + if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED) + queue_remove(&queue, 0);// 0 is top, internally 1 + else + queue_replaced(&queue); + break; + default: + DBUG_ASSERT(!spawn_ret_code); + if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED) + queue_remove(&queue, 0);// 0 is top, internally 1 + else + queue_replaced(&queue); + /* + We don't lock LOCK_scheduler_data here because it's a pre-requisite + for calling the current_method. + */ + if (likely(!spawn_ret_code)) + { + /* Wait the forked thread to start */ + do { + pthread_cond_wait(¶m.COND_started, ¶m.LOCK_started); + } while (!param.started); + } + /* + param was allocated on the stack so no explicit delete as well as + in this moment it's no more used in the spawned thread so it's safe + to be deleted. + */ + break; + } + pthread_mutex_unlock(¶m.LOCK_started); + /* `param` is on the stack and will be destructed by the compiler */ + + DBUG_RETURN(ret); +} + + +/* + Cleans the scheduler's queue. Auxiliary method for ::run(). + + SYNOPSIS + Event_scheduler::clean_queue() + thd Thread +*/ + +void +Event_scheduler::clean_queue(THD *thd) +{ + CHARSET_INFO *scs= system_charset_info; + uint i; + DBUG_ENTER("Event_scheduler::clean_queue"); + DBUG_PRINT("enter", ("thd=%p", thd)); + + LOCK_SCHEDULER_DATA(); + stop_all_running_events(thd); + UNLOCK_SCHEDULER_DATA(); + + sql_print_information("SCHEDULER: Emptying the queue"); + + /* empty the queue */ + for (i= 0; i < queue.elements; ++i) + { + Event_timed *et= (Event_timed *) queue_element(&queue, i); + et->free_sp(); + delete et; + } + resize_queue(&queue, 0); + + DBUG_VOID_RETURN; +} + + +/* + Stops all running events + + SYNOPSIS + Event_scheduler::stop_all_running_events() + thd Thread + + NOTE + LOCK_scheduler data must be acquired prior to call to this method +*/ + +void +Event_scheduler::stop_all_running_events(THD *thd) +{ + CHARSET_INFO *scs= system_charset_info; + uint i; + DYNAMIC_ARRAY running_threads; + THD *tmp; + DBUG_ENTER("Event_scheduler::stop_all_running_events"); + DBUG_PRINT("enter", ("workers_count=%d", workers_count())); + + my_init_dynamic_array(&running_threads, sizeof(ulong), 10, 10); + + bool had_super= FALSE; + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + I_List_iterator<THD> it(threads); + while ((tmp=it++)) + { + if (tmp->command == COM_DAEMON) + continue; + if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER) + push_dynamic(&running_threads, (gptr) &tmp->thread_id); + } + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + /* We need temporarily SUPER_ACL to be able to kill our offsprings */ + if (!(thd->security_ctx->master_access & SUPER_ACL)) + thd->security_ctx->master_access|= SUPER_ACL; + else + had_super= TRUE; + + char tmp_buff[10*STRING_BUFFER_USUAL_SIZE]; + char int_buff[STRING_BUFFER_USUAL_SIZE]; + String tmp_string(tmp_buff, sizeof(tmp_buff), scs); + String int_string(int_buff, sizeof(int_buff), scs); + tmp_string.length(0); + + for (i= 0; i < running_threads.elements; ++i) + { + int ret; + ulong thd_id= *dynamic_element(&running_threads, i, ulong*); + + int_string.set((longlong) thd_id,scs); + tmp_string.append(int_string); + if (i < running_threads.elements - 1) + tmp_string.append(' '); + + if ((ret= kill_one_thread(thd, thd_id, FALSE))) + { + sql_print_error("SCHEDULER: Error killing %lu code=%d", thd_id, ret); + break; + } + } + if (running_threads.elements) + sql_print_information("SCHEDULER: Killing workers :%s", tmp_string.c_ptr()); + + if (!had_super) + thd->security_ctx->master_access &= ~SUPER_ACL; + + delete_dynamic(&running_threads); + + sql_print_information("SCHEDULER: Waiting for worker threads to finish"); + + while (workers_count()) + my_sleep(100000); + + DBUG_VOID_RETURN; +} + + +/* + Stops the event scheduler + + SYNOPSIS + Event_scheduler::stop() + + RETURN VALUE + OP_OK OK + OP_CANT_KILL Error during stopping of manager thread + OP_NOT_RUNNING Manager not working + + NOTE + The caller must have acquited LOCK_scheduler_data. +*/ + +enum Event_scheduler::enum_error_code +Event_scheduler::stop() +{ + THD *thd= current_thd; + DBUG_ENTER("Event_scheduler::stop"); + DBUG_PRINT("enter", ("thd=%p", current_thd)); + + LOCK_SCHEDULER_DATA(); + if (!is_running_or_suspended()) + { + /* + One situation to be here is if there was a start that forked a new + thread but the new thread did not acquire yet LOCK_scheduler_data. + Hence, in this case return an error. + */ + DBUG_PRINT("info", ("manager not running but %d. doing nothing", state)); + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(OP_NOT_RUNNING); + } + state= IN_SHUTDOWN; + + DBUG_PRINT("info", ("Manager thread has id %d", thread_id)); + sql_print_information("SCHEDULER: Killing manager thread %lu", thread_id); + + /* + Sending the COND_new_work to ::run() is a way to get this working without + race conditions. If we use kill_one_thread() it will call THD::awake() and + because in ::run() both THD::enter_cond()/::exit_cond() are used, + THD::awake() will try to lock LOCK_scheduler_data. If we UNLOCK it before, + then the pthread_cond_signal(COND_started_or_stopped) could be signaled in + ::run() and we can miss the signal before we relock. A way is to use + another mutex for this shutdown procedure but better not. + */ + pthread_cond_signal(&cond_vars[COND_new_work]); + /* Or we are suspended - then we should wake up */ + pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); + + /* Guarantee we don't catch spurious signals */ + sql_print_information("SCHEDULER: Waiting the manager thread to reply"); + while (state != INITIALIZED) + { + DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from the manager " + "thread. Current value of state is %d . " + "workers count=%d", state, workers_count())); + cond_wait(COND_started_or_stopped, &LOCK_scheduler_data); + } + DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT")); + UNLOCK_SCHEDULER_DATA(); + + DBUG_RETURN(OP_OK); +} + + +/* + Suspends or resumes the scheduler. + SUSPEND - it won't execute any event till resumed. + RESUME - it will resume if suspended. + + SYNOPSIS + Event_scheduler::suspend_or_resume() + + RETURN VALUE + OP_OK OK +*/ + +enum Event_scheduler::enum_error_code +Event_scheduler::suspend_or_resume( + enum Event_scheduler::enum_suspend_or_resume action) +{ + DBUG_ENTER("Event_scheduler::suspend_or_resume"); + DBUG_PRINT("enter", ("action=%d", action)); + + LOCK_SCHEDULER_DATA(); + + if ((action == SUSPEND && state == SUSPENDED) || + (action == RESUME && state == RUNNING)) + { + DBUG_PRINT("info", ("Either trying to suspend suspended or resume " + "running scheduler. Doing nothing.")); + } + else + { + /* Wake the main thread up if he is asleep */ + DBUG_PRINT("info", ("Sending signal")); + if (action==SUSPEND) + { + state= SUSPENDED; + pthread_cond_signal(&cond_vars[COND_new_work]); + } + else + { + state= RUNNING; + pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); + } + DBUG_PRINT("info", ("Waiting on COND_suspend_or_resume")); + cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data); + DBUG_PRINT("info", ("Got response")); + } + UNLOCK_SCHEDULER_DATA(); + DBUG_RETURN(OP_OK); +} + + +/* + Returns the number of executing events. + + SYNOPSIS + Event_scheduler::workers_count() +*/ + +uint +Event_scheduler::workers_count() +{ + THD *tmp; + uint count= 0; + + DBUG_ENTER("Event_scheduler::workers_count"); + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + I_List_iterator<THD> it(threads); + while ((tmp=it++)) + { + if (tmp->command == COM_DAEMON) + continue; + if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER) + ++count; + } + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + DBUG_PRINT("exit", ("%d", count)); + DBUG_RETURN(count); +} + + +/* + Checks and suspends if needed + + SYNOPSIS + Event_scheduler::check_n_suspend_if_needed() + thd Thread + + RETURN VALUE + FALSE Not suspended, we haven't slept + TRUE We were suspended. LOCK_scheduler_data is unlocked. + + NOTE + The caller should have locked LOCK_scheduler_data! + The mutex will be unlocked in case this function returns TRUE +*/ + +bool +Event_scheduler::check_n_suspend_if_needed(THD *thd) +{ + bool was_suspended= FALSE; + DBUG_ENTER("Event_scheduler::check_n_suspend_if_needed"); + if (thd->killed && !shutdown_in_progress) + { + state= SUSPENDED; + thd->killed= THD::NOT_KILLED; + } + if (state == SUSPENDED) + { + thd->enter_cond(&cond_vars[COND_suspend_or_resume], &LOCK_scheduler_data, + "Suspended"); + /* Send back signal to the thread that asked us to suspend operations */ + pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); + sql_print_information("SCHEDULER: Suspending operations"); + was_suspended= TRUE; + } + while (state == SUSPENDED) + { + cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data); + DBUG_PRINT("info", ("Woke up after waiting on COND_suspend_or_resume")); + if (state != SUSPENDED) + { + pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); + sql_print_information("SCHEDULER: Resuming operations"); + } + } + if (was_suspended) + { + if (queue.elements) + { + uint i; + DBUG_PRINT("info", ("We have to recompute the execution times")); + + for (i= 0; i < queue.elements; i++) + ((Event_timed*)queue_element(&queue, i))->compute_next_execution_time(); + queue_fix(&queue); + } + /* This will implicitly unlock LOCK_scheduler_data */ + thd->exit_cond(""); + } + DBUG_RETURN(was_suspended); +} + + +/* + Checks for empty queue and waits till new element gets in + + SYNOPSIS + Event_scheduler::check_n_wait_for_non_empty_queue() + thd Thread + + RETURN VALUE + FALSE Did not wait - LOCK_scheduler_data still locked. + TRUE Waited - LOCK_scheduler_data unlocked. + + NOTE + The caller should have locked LOCK_scheduler_data! +*/ + +bool +Event_scheduler::check_n_wait_for_non_empty_queue(THD *thd) +{ + bool slept= FALSE; + DBUG_ENTER("Event_scheduler::check_n_wait_for_non_empty_queue"); + DBUG_PRINT("enter", ("q.elements=%lu state=%s", + queue.elements, states_names[state])); + + if (!queue.elements) + thd->enter_cond(&cond_vars[COND_new_work], &LOCK_scheduler_data, + "Empty queue, sleeping"); + + /* Wait in a loop protecting against catching spurious signals */ + while (!queue.elements && state == RUNNING) + { + slept= TRUE; + DBUG_PRINT("info", ("Entering condition because of empty queue")); + cond_wait(COND_new_work, &LOCK_scheduler_data); + DBUG_PRINT("info", ("Manager woke up. Hope we have events now. state=%d", + state)); + /* + exit_cond does implicit mutex_UNLOCK, we needed it locked if + 1. we loop again + 2. end the current loop and start doing calculations + */ + } + if (slept) + thd->exit_cond(""); + + DBUG_PRINT("exit", ("q.elements=%lu state=%s thd->killed=%d", + queue.elements, states_names[state], thd->killed)); + + DBUG_RETURN(slept); +} + + +/* + Wrapper for pthread_mutex_lock + + SYNOPSIS + Event_scheduler::lock_data() + mutex Mutex to lock + line The line number on which the lock is done + + RETURN VALUE + Error code of pthread_mutex_lock() +*/ + +inline void +Event_scheduler::lock_data(const char *func, uint line) +{ + DBUG_ENTER("Event_scheduler::lock_mutex"); + DBUG_PRINT("enter", ("mutex_lock=%p func=%s line=%u", + &LOCK_scheduler_data, func, line)); + pthread_mutex_lock(&LOCK_scheduler_data); + mutex_last_locked_in_func= func; + mutex_last_locked_at_line= line; + mutex_scheduler_data_locked= TRUE; + DBUG_VOID_RETURN; +} + + +/* + Wrapper for pthread_mutex_unlock + + SYNOPSIS + Event_scheduler::unlock_data() + mutex Mutex to unlock + line The line number on which the unlock is done +*/ + +inline void +Event_scheduler::unlock_data(const char *func, uint line) +{ + DBUG_ENTER("Event_scheduler::UNLOCK_mutex"); + DBUG_PRINT("enter", ("mutex_unlock=%p func=%s line=%u", + &LOCK_scheduler_data, func, line)); + mutex_last_unlocked_at_line= line; + mutex_scheduler_data_locked= FALSE; + mutex_last_unlocked_in_func= func; + pthread_mutex_unlock(&LOCK_scheduler_data); + DBUG_VOID_RETURN; +} + + +/* + Wrapper for pthread_cond_wait + + SYNOPSIS + Event_scheduler::cond_wait() + cond Conditional to wait for + mutex Mutex of the conditional + + RETURN VALUE + Error code of pthread_cond_wait() +*/ + +inline int +Event_scheduler::cond_wait(enum Event_scheduler::enum_cond_vars cond, + pthread_mutex_t *mutex) +{ + int ret; + DBUG_ENTER("Event_scheduler::cond_wait"); + DBUG_PRINT("enter", ("cond=%s mutex=%p", cond_vars_names[cond], mutex)); + ret= pthread_cond_wait(&cond_vars[cond_waiting_on=cond], mutex); + cond_waiting_on= COND_NONE; + DBUG_RETURN(ret); +} + + +/* + Checks whether the scheduler is in a running or suspended state. + + SYNOPSIS + Event_scheduler::is_running_or_suspended() + + RETURN VALUE + TRUE Either running or suspended + FALSE IN_SHUTDOWN, not started, etc. +*/ + +inline bool +Event_scheduler::is_running_or_suspended() +{ + return (state == SUSPENDED || state == RUNNING); +} + + +/* + Returns the current state of the scheduler + + SYNOPSIS + Event_scheduler::get_state() +*/ + +enum Event_scheduler::enum_state +Event_scheduler::get_state() +{ + enum Event_scheduler::enum_state ret; + DBUG_ENTER("Event_scheduler::get_state"); + /* lock_data & unlock_data are not static */ + pthread_mutex_lock(&singleton.LOCK_scheduler_data); + ret= singleton.state; + pthread_mutex_unlock(&singleton.LOCK_scheduler_data); + DBUG_RETURN(ret); +} + + +/* + Returns whether the scheduler was initialized. + + SYNOPSIS + Event_scheduler::initialized() + + RETURN VALUE + FALSE Was not initialized so far + TRUE Was initialized +*/ + +bool +Event_scheduler::initialized() +{ + DBUG_ENTER("Event_scheduler::initialized"); + DBUG_RETURN(Event_scheduler::get_state() != UNINITIALIZED); +} + + +/* + Returns the number of elements in the queue + + SYNOPSIS + Event_scheduler::events_count() + + RETURN VALUE + 0 Number of Event_timed objects in the queue +*/ + +uint +Event_scheduler::events_count() +{ + uint n; + DBUG_ENTER("Event_scheduler::events_count"); + LOCK_SCHEDULER_DATA(); + n= queue.elements; + UNLOCK_SCHEDULER_DATA(); + + DBUG_RETURN(n); +} + + +/* + Looks for a named event in mysql.event and then loads it from + the table, compiles and inserts it into the cache. + + SYNOPSIS + Event_scheduler::load_named_event() + thd THD + etn The name of the event to load and compile on scheduler's root + etn_new The loaded event + + RETURN VALUE + NULL Error during compile or the event is non-enabled. + otherwise Address +*/ + +enum Event_scheduler::enum_error_code +Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new) +{ + int ret= 0; + MEM_ROOT *tmp_mem_root; + Event_timed *et_loaded= NULL; + Open_tables_state backup; + + DBUG_ENTER("Event_scheduler::load_and_compile_event"); + DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str)); + + thd->reset_n_backup_open_tables_state(&backup); + /* No need to use my_error() here because db_find_event() has done it */ + { + sp_name spn(etn->dbname, etn->name); + ret= db_find_event(thd, &spn, &etn->definer, &et_loaded, NULL, + &scheduler_root); + } + thd->restore_backup_open_tables_state(&backup); + /* In this case no memory was allocated so we don't need to clean */ + if (ret) + DBUG_RETURN(OP_LOAD_ERROR); + + if (et_loaded->status != Event_timed::ENABLED) + { + /* + We don't load non-enabled events. + In db_find_event() `et_new` was allocated on the heap and not on + scheduler_root therefore we delete it here. + */ + delete et_loaded; + DBUG_RETURN(OP_DISABLED_EVENT); + } + + et_loaded->compute_next_execution_time(); + *etn_new= et_loaded; + + DBUG_RETURN(OP_OK); +} + + +/* + Loads all ENABLED events from mysql.event into the prioritized + queue. Called during scheduler main thread initialization. Compiles + the events. Creates Event_timed instances for every ENABLED event + from mysql.event. + + SYNOPSIS + Event_scheduler::load_events_from_db() + thd - Thread context. Used for memory allocation in some cases. + + RETURN VALUE + 0 OK + !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP, + EVEX_COMPILE_ERROR) - in all these cases mysql.event was + tampered. + + NOTES + Reports the error to the console +*/ + +int +Event_scheduler::load_events_from_db(THD *thd) +{ + TABLE *table; + READ_RECORD read_record_info; + int ret= -1; + uint count= 0; + bool clean_the_queue= FALSE; + /* Compile the events on this root but only for syntax check, then discard */ + MEM_ROOT boot_root; + + DBUG_ENTER("Event_scheduler::load_events_from_db"); + DBUG_PRINT("enter", ("thd=%p", thd)); + + if (state > COMMENCING) + { + DBUG_ASSERT(0); + sql_print_error("SCHEDULER: Trying to load events while already running."); + DBUG_RETURN(EVEX_GENERAL_ERROR); + } + + if ((ret= Events::open_event_table(thd, TL_READ, &table))) + { + sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); + DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); + } + + init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + Event_timed *et; + if (!(et= new Event_timed)) + { + DBUG_PRINT("info", ("Out of memory")); + clean_the_queue= TRUE; + break; + } + DBUG_PRINT("info", ("Loading event from row.")); + + if ((ret= et->load_from_row(&scheduler_root, table))) + { + clean_the_queue= TRUE; + sql_print_error("SCHEDULER: Error while loading from mysql.event. " + "Table probably corrupted"); + break; + } + if (et->status != Event_timed::ENABLED) + { + DBUG_PRINT("info",("%s is disabled",et->name.str)); + delete et; + continue; + } + + DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str)); + + /* We load only on scheduler root just to check whether the body compiles */ + switch (ret= et->compile(thd, &boot_root)) { + case EVEX_MICROSECOND_UNSUP: + et->free_sp(); + sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not " + "supported but found in mysql.event"); + goto end; + case EVEX_COMPILE_ERROR: + sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.", + et->dbname.str, et->name.str); + goto end; + default: + /* Free it, it will be compiled again on the worker thread */ + et->free_sp(); + break; + } + + /* let's find when to be executed */ + if (et->compute_next_execution_time()) + { + sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." + " Skipping", et->dbname.str, et->name.str); + continue; + } + + DBUG_PRINT("load_events_from_db", ("Adding %p to the exec list.")); + queue_insert_safe(&queue, (byte *) et); + count++; + } +end: + end_read_record(&read_record_info); + free_root(&boot_root, MYF(0)); + + if (clean_the_queue) + { + for (count= 0; count < queue.elements; ++count) + queue_remove(&queue, 0); + ret= -1; + } + else + { + ret= 0; + sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s"); + } + + /* Force close to free memory */ + thd->version--; + + close_thread_tables(thd); + + DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); + DBUG_RETURN(ret); +} + + +/* + Opens mysql.db and mysql.user and checks whether: + 1. mysql.db has column Event_priv at column 20 (0 based); + 2. mysql.user has column Event_priv at column 29 (0 based); + + SYNOPSIS + Event_scheduler::check_system_tables() +*/ + +bool +Event_scheduler::check_system_tables(THD *thd) +{ + TABLE_LIST tables; + bool not_used; + Open_tables_state backup; + bool ret; + + DBUG_ENTER("Event_scheduler::check_system_tables"); + DBUG_PRINT("enter", ("thd=%p", thd)); + + thd->reset_n_backup_open_tables_state(&backup); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "db"; + tables.lock_type= TL_READ; + + if ((ret= simple_open_n_lock_tables(thd, &tables))) + sql_print_error("Cannot open mysql.db"); + else + { + ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, + mysql_db_table_fields, &mysql_db_table_last_check, + ER_CANNOT_LOAD_FROM_TABLE); + close_thread_tables(thd); + } + if (ret) + DBUG_RETURN(TRUE); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "user"; + tables.lock_type= TL_READ; + + if ((ret= simple_open_n_lock_tables(thd, &tables))) + sql_print_error("Cannot open mysql.db"); + else + { + if (tables.table->s->fields < 29 || + strncmp(tables.table->field[29]->field_name, + STRING_WITH_LEN("Event_priv"))) + { + sql_print_error("mysql.user has no `Event_priv` column at position 29"); + ret= TRUE; + } + close_thread_tables(thd); + } + + thd->restore_backup_open_tables_state(&backup); + + DBUG_RETURN(ret); +} + + +/* + Inits mutexes. + + SYNOPSIS + Event_scheduler::init_mutexes() +*/ + +void +Event_scheduler::init_mutexes() +{ + pthread_mutex_init(&singleton.LOCK_scheduler_data, MY_MUTEX_INIT_FAST); +} + + +/* + Destroys mutexes. + + SYNOPSIS + Event_scheduler::destroy_mutexes() +*/ + +void +Event_scheduler::destroy_mutexes() +{ + pthread_mutex_destroy(&singleton.LOCK_scheduler_data); +} + + +/* + Dumps some data about the internal status of the scheduler. + + SYNOPSIS + Event_scheduler::dump_internal_status() + thd THD + + RETURN VALUE + 0 OK + 1 Error +*/ + +int +Event_scheduler::dump_internal_status(THD *thd) +{ + DBUG_ENTER("dump_internal_status"); +#ifndef DBUG_OFF + CHARSET_INFO *scs= system_charset_info; + Protocol *protocol= thd->protocol; + List<Item> field_list; + int ret; + char tmp_buff[5*STRING_BUFFER_USUAL_SIZE]; + char int_buff[STRING_BUFFER_USUAL_SIZE]; + String tmp_string(tmp_buff, sizeof(tmp_buff), scs); + String int_string(int_buff, sizeof(int_buff), scs); + tmp_string.length(0); + int_string.length(0); + + field_list.push_back(new Item_empty_string("Name", 20)); + field_list.push_back(new Item_empty_string("Value",20)); + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) + DBUG_RETURN(1); + + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("state"), scs); + protocol->store(states_names[singleton.state].str, + states_names[singleton.state].length, + scs); + + ret= protocol->write(); + /* + If not initialized - don't show anything else. get_instance() + will otherwise implicitly initialize it. We don't want that. + */ + if (singleton.state >= INITIALIZED) + { + /* last locked at*/ + /* + The first thing to do, or get_instance() will overwrite the values. + mutex_last_locked_at_line / mutex_last_unlocked_at_line + */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("last locked at"), scs); + tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), + tmp_string.alloced_length(), "%s::%d", + singleton.mutex_last_locked_in_func, + singleton.mutex_last_locked_at_line)); + protocol->store(&tmp_string); + ret= protocol->write(); + + /* last unlocked at*/ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("last unlocked at"), scs); + tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), + tmp_string.alloced_length(), "%s::%d", + singleton.mutex_last_unlocked_in_func, + singleton.mutex_last_unlocked_at_line)); + protocol->store(&tmp_string); + ret= protocol->write(); + + /* waiting on */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("waiting on condition"), scs); + tmp_string.length(scs->cset-> + snprintf(scs, (char*) tmp_string.ptr(), + tmp_string.alloced_length(), "%s", + (singleton.cond_waiting_on != COND_NONE) ? + cond_vars_names[singleton.cond_waiting_on]: + "NONE")); + protocol->store(&tmp_string); + ret= protocol->write(); + + Event_scheduler *scheduler= get_instance(); + + /* workers_count */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("workers_count"), scs); + int_string.set((longlong) scheduler->workers_count(), scs); + protocol->store(&int_string); + ret= protocol->write(); + + /* queue.elements */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue.elements"), scs); + int_string.set((longlong) scheduler->queue.elements, scs); + protocol->store(&int_string); + ret= protocol->write(); + + /* scheduler_data_locked */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("scheduler data locked"), scs); + int_string.set((longlong) scheduler->mutex_scheduler_data_locked, scs); + protocol->store(&int_string); + ret= protocol->write(); + } + send_eof(thd); +#endif + DBUG_RETURN(0); +} diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h new file mode 100644 index 00000000000..dffd47539fa --- /dev/null +++ b/sql/event_scheduler.h @@ -0,0 +1,254 @@ +#ifndef _EVENT_SCHEDULER_H_ +#define _EVENT_SCHEDULER_H_ +/* Copyright (C) 2004-2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 */ + + +class THD; +typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*); + +int +events_init(); + +void +events_shutdown(); + + +class Event_scheduler +{ +public: + /* Return codes */ + enum enum_error_code + { + OP_OK= 0, + OP_NOT_RUNNING, + OP_CANT_KILL, + OP_CANT_INIT, + OP_DISABLED_EVENT, + OP_LOAD_ERROR, + OP_ALREADY_EXISTS + }; + + enum enum_state + { + UNINITIALIZED= 0, + INITIALIZED, + COMMENCING, + CANTSTART, + RUNNING, + SUSPENDED, + IN_SHUTDOWN + }; + + enum enum_suspend_or_resume + { + SUSPEND= 1, + RESUME= 2 + }; + + /* Singleton access */ + static Event_scheduler* + get_instance(); + + /* Methods for queue management follow */ + + enum enum_error_code + add_event(THD *thd, Event_timed *et, bool check_existence); + + bool + drop_event(THD *thd, Event_timed *et); + + enum enum_error_code + replace_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, + LEX_STRING *new_name); + + int + drop_schema_events(THD *thd, LEX_STRING *schema); + + int + drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num) + { DBUG_ASSERT(0); return 0;} + + uint + events_count(); + + /* State changing methods follow */ + + bool + start(); + + enum enum_error_code + stop(); + + bool + start_suspended(); + + bool + run(THD *thd); + + enum enum_error_code + suspend_or_resume(enum enum_suspend_or_resume action); + + bool + init(); + + void + destroy(); + + static void + init_mutexes(); + + static void + destroy_mutexes(); + + void + report_error_during_start(); + + /* Information retrieving methods follow */ + + enum enum_state + get_state(); + + bool + initialized(); + + static int + dump_internal_status(THD *thd); + + static bool + check_system_tables(THD *thd); + +private: + Event_timed * + find_event(Event_timed *etn, bool remove_from_q); + + uint + workers_count(); + + bool + is_running_or_suspended(); + + /* helper functions */ + bool + execute_top(THD *thd); + + void + clean_queue(THD *thd); + + void + stop_all_running_events(THD *thd); + + enum enum_error_code + load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new); + + int + load_events_from_db(THD *thd); + + void + drop_matching_events(THD *thd, LEX_STRING *pattern, + bool (*)(Event_timed *,LEX_STRING *)); + + bool + check_n_suspend_if_needed(THD *thd); + + bool + check_n_wait_for_non_empty_queue(THD *thd); + + /* Singleton DP is used */ + Event_scheduler(); + + enum enum_cond_vars + { + COND_NONE= -1, + /* + COND_new_work is a conditional used to signal that there is a change + of the queue that should inform the executor thread that new event should + be executed sooner than previously expected, because of add/replace event. + */ + COND_new_work= 0, + /* + COND_started is a conditional used to synchronize the thread in which + ::start() was called and the spawned thread. ::start() spawns a new thread + and then waits on COND_started but also checks when awaken that `state` is + either RUNNING or CANTSTART. Then it returns back. + */ + COND_started_or_stopped, + /* + Conditional used for signalling from the scheduler thread back to the + thread that calls ::suspend() or ::resume. Synchronizing the calls. + */ + COND_suspend_or_resume, + /* Must be always last */ + COND_LAST + }; + + /* Singleton instance */ + static Event_scheduler singleton; + + /* This is the current status of the life-cycle of the manager. */ + enum enum_state state; + + /* Set to start the scheduler in suspended state */ + bool start_scheduler_suspended; + + /* + LOCK_scheduler_data is the mutex which protects the access to the + manager's queue as well as used when signalling COND_new_work, + COND_started and COND_shutdown. + */ + pthread_mutex_t LOCK_scheduler_data; + + /* + Holds the thread id of the executor thread or 0 if the executor is not + running. It is used by ::shutdown() to know which thread to kill with + kill_one_thread(). The latter wake ups a thread if it is waiting on a + conditional variable and sets thd->killed to non-zero. + */ + ulong thread_id; + + pthread_cond_t cond_vars[COND_LAST]; + static const char * const cond_vars_names[COND_LAST]; + + /* The MEM_ROOT of the object */ + MEM_ROOT scheduler_root; + + /* The sorted queue with the Event_timed objects */ + QUEUE queue; + + uint mutex_last_locked_at_line; + uint mutex_last_unlocked_at_line; + const char* mutex_last_locked_in_func; + const char* mutex_last_unlocked_in_func; + enum enum_cond_vars cond_waiting_on; + bool mutex_scheduler_data_locked; + + /* helper functions for working with mutexes & conditionals */ + void + lock_data(const char *func, uint line); + + void + unlock_data(const char *func, uint line); + + int + cond_wait(enum enum_cond_vars, pthread_mutex_t *mutex); + +private: + /* Prevent use of these */ + Event_scheduler(const Event_scheduler &); + void operator=(Event_scheduler &); +}; + +#endif /* _EVENT_SCHEDULER_H_ */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 879f4d6a3c9..fd85f5ebecc 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2004-2005 MySQL AB +/* Copyright (C) 2004-2006 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 @@ -17,7 +17,82 @@ #define MYSQL_LEX 1 #include "event_priv.h" #include "event.h" -#include "sp.h" +#include "sp_head.h" + + +/* + Constructor + + SYNOPSIS + Event_timed::Event_timed() +*/ + +Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0), + running(0), thread_id(0), status_changed(false), + last_executed_changed(false), expression(0), + created(0), modified(0), + on_completion(Event_timed::ON_COMPLETION_DROP), + status(Event_timed::ENABLED), sphead(0), + sql_mode(0), body_begin(0), dropped(false), + free_sphead_on_delete(true), flags(0) + +{ + pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST); + pthread_cond_init(&this->COND_finished, NULL); + init(); +} + + +/* + Destructor + + SYNOPSIS + Event_timed::~Event_timed() +*/ + +Event_timed::~Event_timed() +{ + deinit_mutexes(); + + if (free_sphead_on_delete) + free_sp(); +} + + +/* + Destructor + + SYNOPSIS + Event_timed::~deinit_mutexes() +*/ + +void +Event_timed::deinit_mutexes() +{ + pthread_mutex_destroy(&this->LOCK_running); + pthread_cond_destroy(&this->COND_finished); +} + + +/* + Checks whether the event is running + + SYNOPSIS + Event_timed::is_running() +*/ + +bool +Event_timed::is_running() +{ + bool ret; + + VOID(pthread_mutex_lock(&this->LOCK_running)); + ret= running; + VOID(pthread_mutex_unlock(&this->LOCK_running)); + + return ret; +} + /* Init all member variables @@ -238,7 +313,7 @@ Event_timed::init_execute_at(THD *thd, Item *expr) expr how much? new_interval what is the interval - RETURNS + RETURN VALUE 0 OK EVEX_PARSE_ERROR fix_fields failed EVEX_BAD_PARAMS Interval is not positive @@ -342,7 +417,7 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at same time. - RETURNS + RETURN VALUE 0 OK EVEX_PARSE_ERROR fix_fields failed EVEX_BAD_PARAMS starts before now @@ -408,7 +483,7 @@ Event_timed::init_starts(THD *thd, Item *new_starts) DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at same time. - RETURNS + RETURN VALUE 0 OK EVEX_PARSE_ERROR fix_fields failed ER_WRONG_VALUE starts distant date (after year 2037) @@ -492,6 +567,9 @@ Event_timed::init_comment(THD *thd, LEX_STRING *set_comment) SYNOPSIS Event_timed::init_definer() + + RETURN VALUE + 0 OK */ int @@ -534,6 +612,10 @@ Event_timed::init_definer(THD *thd) SYNOPSIS Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) + RETURN VALUE + 0 OK + EVEX_GET_FIELD_FAILED Error + NOTES This method is silent on errors and should behave like that. Callers should handle throwing of error messages. The reason is that the class @@ -555,29 +637,29 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et= this; - if (table->s->fields != EVEX_FIELD_COUNT) + if (table->s->fields != Events::FIELD_COUNT) goto error; if ((et->dbname.str= get_field(mem_root, - table->field[EVEX_FIELD_DB])) == NULL) + table->field[Events::FIELD_DB])) == NULL) goto error; et->dbname.length= strlen(et->dbname.str); if ((et->name.str= get_field(mem_root, - table->field[EVEX_FIELD_NAME])) == NULL) + table->field[Events::FIELD_NAME])) == NULL) goto error; et->name.length= strlen(et->name.str); if ((et->body.str= get_field(mem_root, - table->field[EVEX_FIELD_BODY])) == NULL) + table->field[Events::FIELD_BODY])) == NULL) goto error; et->body.length= strlen(et->body.str); if ((et->definer.str= get_field(mem_root, - table->field[EVEX_FIELD_DEFINER])) == NullS) + table->field[Events::FIELD_DEFINER])) == NullS) goto error; et->definer.length= strlen(et->definer.str); @@ -594,69 +676,71 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/ et->definer_host.length= len; - et->starts_null= table->field[EVEX_FIELD_STARTS]->is_null(); - res1= table->field[EVEX_FIELD_STARTS]->get_date(&et->starts,TIME_NO_ZERO_DATE); + et->starts_null= table->field[Events::FIELD_STARTS]->is_null(); + res1= table->field[Events::FIELD_STARTS]-> + get_date(&et->starts,TIME_NO_ZERO_DATE); - et->ends_null= table->field[EVEX_FIELD_ENDS]->is_null(); - res2= table->field[EVEX_FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); + et->ends_null= table->field[Events::FIELD_ENDS]->is_null(); + res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); - if (!table->field[EVEX_FIELD_INTERVAL_EXPR]->is_null()) - et->expression= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int(); + if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null()) + et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int(); else et->expression= 0; /* If res1 and res2 are true then both fields are empty. - Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error. + Hence if Events::FIELD_EXECUTE_AT is empty there is an error. */ - et->execute_at_null= table->field[EVEX_FIELD_EXECUTE_AT]->is_null(); + et->execute_at_null= + table->field[Events::FIELD_EXECUTE_AT]->is_null(); DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression && et->execute_at_null)); if (!et->expression && - table->field[EVEX_FIELD_EXECUTE_AT]->get_date(&et->execute_at, - TIME_NO_ZERO_DATE)) + table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at, + TIME_NO_ZERO_DATE)) goto error; /* In DB the values start from 1 but enum interval_type starts from 0 */ - if (!table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->is_null()) - et->interval= (interval_type) - ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); + if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null()) + et->interval= (interval_type) ((ulonglong) + table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1); else et->interval= (interval_type) 0; - et->created= table->field[EVEX_FIELD_CREATED]->val_int(); - et->modified= table->field[EVEX_FIELD_MODIFIED]->val_int(); + et->created= table->field[Events::FIELD_CREATED]->val_int(); + et->modified= table->field[Events::FIELD_MODIFIED]->val_int(); - table->field[EVEX_FIELD_LAST_EXECUTED]-> + table->field[Events::FIELD_LAST_EXECUTED]-> get_date(&et->last_executed, TIME_NO_ZERO_DATE); last_executed_changed= false; /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ - if ((ptr= get_field(mem_root, table->field[EVEX_FIELD_STATUS])) == NullS) + if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS) goto error; DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr)); - et->status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED:MYSQL_EVENT_DISABLED); + et->status= (ptr[0]=='E'? Event_timed::ENABLED:Event_timed::DISABLED); /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ if ((ptr= get_field(mem_root, - table->field[EVEX_FIELD_ON_COMPLETION])) == NullS) + table->field[Events::FIELD_ON_COMPLETION])) == NullS) goto error; - et->on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP: - MYSQL_EVENT_ON_COMPLETION_PRESERVE); + et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP: + Event_timed::ON_COMPLETION_PRESERVE); - et->comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]); + et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]); if (et->comment.str != NullS) et->comment.length= strlen(et->comment.str); else et->comment.length= 0; - et->sql_mode= (ulong) table->field[EVEX_FIELD_SQL_MODE]->val_int(); + et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int(); DBUG_RETURN(0); error: @@ -676,7 +760,7 @@ error: i_value quantity of time type interval to add i_type type of interval to add (SECOND, MINUTE, HOUR, WEEK ...) - RETURNS + RETURN VALUE 0 OK 1 Error @@ -834,6 +918,10 @@ done: SYNOPSIS Event_timed::compute_next_execution_time() + RETURN VALUE + FALSE OK + TRUE Error + NOTES The time is set in execute_at, if no more executions the latter is set to 0000-00-00. @@ -843,7 +931,6 @@ bool Event_timed::compute_next_execution_time() { TIME time_now; - my_time_t now; int tmp; DBUG_ENTER("Event_timed::compute_next_execution_time"); @@ -852,7 +939,7 @@ Event_timed::compute_next_execution_time() TIME_to_ulonglong_datetime(&ends), TIME_to_ulonglong_datetime(&last_executed))); - if (status == MYSQL_EVENT_DISABLED) + if (status == Event_timed::DISABLED) { DBUG_PRINT("compute_next_execution_time", ("Event %s is DISABLED", name.str)); @@ -866,14 +953,15 @@ Event_timed::compute_next_execution_time() { DBUG_PRINT("info",("One-time event %s.%s of was already executed", dbname.str, name.str, definer.str)); - dropped= (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP); + dropped= (on_completion == Event_timed::ON_COMPLETION_DROP); DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped)); - status= MYSQL_EVENT_DISABLED; + status= Event_timed::DISABLED; status_changed= true; } goto ret; } + current_thd->end_time(); my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start()); DBUG_PRINT("info",("NOW=[%llu]", TIME_to_ulonglong_datetime(&time_now))); @@ -885,9 +973,9 @@ Event_timed::compute_next_execution_time() /* time_now is after ends. don't execute anymore */ set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); execute_at_null= TRUE; - if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + if (on_completion == Event_timed::ON_COMPLETION_DROP) dropped= true; - status= MYSQL_EVENT_DISABLED; + status= Event_timed::DISABLED; status_changed= true; goto ret; @@ -937,7 +1025,6 @@ Event_timed::compute_next_execution_time() { TIME next_exec; - DBUG_PRINT("info", ("Executed at least once")); if (get_next_time(&next_exec, &starts, &time_now, last_executed.year? &last_executed:&starts, expression, interval)) @@ -946,12 +1033,15 @@ Event_timed::compute_next_execution_time() /* There was previous execution */ if (my_time_compare(&ends, &next_exec) == -1) { - DBUG_PRINT("info", ("Next execution after ENDS. Stop executing.")); + DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.", + name.str)); /* Next execution after ends. No more executions */ set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); execute_at_null= TRUE; - if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + if (on_completion == Event_timed::ON_COMPLETION_DROP) dropped= true; + status= Event_timed::DISABLED; + status_changed= true; } else { @@ -1006,7 +1096,6 @@ Event_timed::compute_next_execution_time() { TIME next_exec; - DBUG_PRINT("info", ("Executed at least once.")); if (get_next_time(&next_exec, &starts, &time_now, last_executed.year? &last_executed:&starts, expression, interval)) @@ -1042,7 +1131,9 @@ Event_timed::compute_next_execution_time() DBUG_PRINT("info", ("Next execution after ENDS. Stop executing.")); set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); execute_at_null= TRUE; - if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + status= Event_timed::DISABLED; + status_changed= true; + if (on_completion == Event_timed::ON_COMPLETION_DROP) dropped= true; } else @@ -1083,9 +1174,6 @@ Event_timed::mark_last_executed(THD *thd) my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start()); last_executed= time_now; /* was execute_at */ -#ifdef ANDREY_0 - last_executed= execute_at; -#endif last_executed_changed= true; } @@ -1125,7 +1213,7 @@ Event_timed::drop(THD *thd) RETURN VALUE 0 OK - SP_OPEN_TABLE_FAILED Error while opening mysql.event for writing + EVEX_OPEN_TABLE_FAILED Error while opening mysql.event for writing EVEX_WRITE_ROW_FAILED On error to write to disk others return code from SE in case deletion of the event @@ -1149,9 +1237,9 @@ Event_timed::update_fields(THD *thd) thd->reset_n_backup_open_tables_state(&backup); - if (evex_open_event_table(thd, TL_WRITE, &table)) + if (Events::open_event_table(thd, TL_WRITE, &table)) { - ret= SP_OPEN_TABLE_FAILED; + ret= EVEX_OPEN_TABLE_FAILED; goto done; } @@ -1165,15 +1253,15 @@ Event_timed::update_fields(THD *thd) if (last_executed_changed) { - table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull(); - table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&last_executed, - MYSQL_TIMESTAMP_DATETIME); + table->field[Events::FIELD_LAST_EXECUTED]->set_notnull(); + table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed, + MYSQL_TIMESTAMP_DATETIME); last_executed_changed= false; } if (status_changed) { - table->field[EVEX_FIELD_STATUS]->set_notnull(); - table->field[EVEX_FIELD_STATUS]->store((longlong)status, true); + table->field[Events::FIELD_STATUS]->set_notnull(); + table->field[Events::FIELD_STATUS]->store((longlong)status, true); status_changed= false; } @@ -1215,8 +1303,8 @@ Event_timed::get_create_event(THD *thd, String *buf) DBUG_ENTER("get_create_event"); DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]", body.length, body.str)); - if (expression && - event_reconstruct_interval_expression(&expr_buf, interval, expression)) + if (expression && Events::reconstruct_interval_expression(&expr_buf, interval, + expression)) DBUG_RETURN(EVEX_MICROSECOND_UNSUP); buf->append(STRING_WITH_LEN("CREATE EVENT ")); @@ -1243,12 +1331,12 @@ Event_timed::get_create_event(THD *thd, String *buf) buf->append(STRING_WITH_LEN("'")); } - if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + if (on_completion == Event_timed::ON_COMPLETION_DROP) buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE ")); else buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE ")); - if (status == MYSQL_EVENT_ENABLED) + if (status == Event_timed::ENABLED) buf->append(STRING_WITH_LEN("ENABLE")); else buf->append(STRING_WITH_LEN("DISABLE")); @@ -1273,7 +1361,7 @@ Event_timed::get_create_event(THD *thd, String *buf) thd THD mem_root If != NULL use it to compile the event on it - RETURNS + RETURN VALUE 0 success -99 No rights on this.dbname.str -100 event in execution (parallel execution is impossible) @@ -1283,7 +1371,6 @@ Event_timed::get_create_event(THD *thd, String *buf) int Event_timed::execute(THD *thd, MEM_ROOT *mem_root) { - Security_context *save_ctx; /* this one is local and not needed after exec */ Security_context security_ctx; int ret= 0; @@ -1301,16 +1388,8 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root) running= true; VOID(pthread_mutex_unlock(&this->LOCK_running)); - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - change_security_context(thd, &security_ctx, &save_ctx); - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - if (!sphead && (ret= compile(thd, mem_root))) goto done; - /* Now we are sure we have valid this->sphead so we can copy the context */ - sphead->m_security_ctx= security_ctx; /* THD::~THD will clean this or if there is DROP DATABASE in the SP then it will be free there. It should not point to our buffer which is allocated @@ -1334,12 +1413,11 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root) definer_host.str, dbname.str)); ret= -99; } - restore_security_context(thd, save_ctx); - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); VOID(pthread_mutex_lock(&this->LOCK_running)); running= false; + /* Will compile every time a new sp_head on different root */ + free_sp(); VOID(pthread_mutex_unlock(&this->LOCK_running)); done: @@ -1361,55 +1439,16 @@ done: /* - Switches the security context - Synopsis - Event_timed::change_security_context() - thd - thread - backup - where to store the old context - - RETURN - 0 - OK - 1 - Error (generates error too) -*/ -bool -Event_timed::change_security_context(THD *thd, Security_context *s_ctx, - Security_context **backup) -{ - DBUG_ENTER("Event_timed::change_security_context"); - DBUG_PRINT("info",("%s@%s@%s",definer_user.str,definer_host.str, dbname.str)); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - s_ctx->init(); - *backup= 0; - if (acl_getroot_no_password(s_ctx, definer_user.str, definer_host.str, - definer_host.str, dbname.str)) - { - my_error(ER_NO_SUCH_USER, MYF(0), definer_user.str, definer_host.str); - DBUG_RETURN(true); - } - *backup= thd->security_ctx; - thd->security_ctx= s_ctx; -#endif - DBUG_RETURN(false); -} - - -/* - Restores the security context - Synopsis - Event_timed::restore_security_context() - thd - thread - backup - switch to this context + Frees the memory of the sp_head object we hold + SYNOPSIS + Event_timed::free_sp() */ void -Event_timed::restore_security_context(THD *thd, Security_context *backup) +Event_timed::free_sp() { - DBUG_ENTER("Event_timed::restore_security_context"); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (backup) - thd->security_ctx= backup; -#endif - DBUG_VOID_RETURN; + delete sphead; + sphead= 0; } @@ -1445,6 +1484,9 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) CHARSET_INFO *old_character_set_client, *old_collation_connection, *old_character_set_results; + Security_context *save_ctx; + /* this one is local and not needed after exec */ + Security_context security_ctx; DBUG_ENTER("Event_timed::compile"); @@ -1488,8 +1530,10 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) thd->query= show_create.c_ptr(); thd->query_length= show_create.length(); - DBUG_PRINT("Event_timed::compile", ("query:%s",thd->query)); + DBUG_PRINT("info", ("query:%s",thd->query)); + change_security_context(thd, definer_user, definer_host, dbname, + &security_ctx, &save_ctx); thd->lex= &lex; lex_start(thd, (uchar*)thd->query, thd->query_length); lex.et_compile_phase= TRUE; @@ -1527,6 +1571,7 @@ done: lex.et->deinit_mutexes(); lex_end(&lex); + restore_security_context(thd, save_ctx); DBUG_PRINT("note", ("return old data on its place. set back NAMES")); thd->lex= old_lex; @@ -1548,72 +1593,63 @@ done: } -/* - Checks whether this thread can lock the object for modification -> - preventing being spawned for execution, and locks if possible. - use ::can_spawn_now() only for basic checking because a race - condition may occur between the check and eventual modification (deletion) - of the object. - - Returns - true - locked - false - cannot lock -*/ - -my_bool -Event_timed::can_spawn_now_n_lock(THD *thd) -{ - my_bool ret= FALSE; - VOID(pthread_mutex_lock(&this->LOCK_running)); - if (!in_spawned_thread) - { - in_spawned_thread= TRUE; - ret= TRUE; - locked_by_thread_id= thd->thread_id; - } - VOID(pthread_mutex_unlock(&this->LOCK_running)); - return ret; -} - - extern pthread_attr_t connection_attrib; /* Checks whether is possible and forks a thread. Passes self as argument. - Returns - EVENT_EXEC_STARTED - OK - EVENT_EXEC_ALREADY_EXEC - Thread not forked, already working - EVENT_EXEC_CANT_FORK - Unable to spawn thread (error) + RETURN VALUE + EVENT_EXEC_STARTED OK + EVENT_EXEC_ALREADY_EXEC Thread not forked, already working + EVENT_EXEC_CANT_FORK Unable to spawn thread (error) */ int -Event_timed::spawn_now(void * (*thread_func)(void*)) +Event_timed::spawn_now(void * (*thread_func)(void*), void *arg) { + THD *thd= current_thd; int ret= EVENT_EXEC_STARTED; - static uint exec_num= 0; DBUG_ENTER("Event_timed::spawn_now"); - DBUG_PRINT("info", ("this=0x%lx", this)); DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str)); VOID(pthread_mutex_lock(&this->LOCK_running)); + + DBUG_PRINT("info", ("SCHEDULER: execute_at of %s is %lld", name.str, + TIME_to_ulonglong_datetime(&execute_at))); + mark_last_executed(thd); + if (compute_next_execution_time()) + { + sql_print_error("SCHEDULER: Error while computing time of %s.%s . " + "Disabling after execution.", dbname.str, name.str); + status= DISABLED; + } + DBUG_PRINT("evex manager", ("[%10s] next exec at [%llu]", name.str, + TIME_to_ulonglong_datetime(&execute_at))); + /* + 1. For one-time event : year is > 0 and expression is 0 + 2. For recurring, expression is != -=> check execute_at_null in this case + */ + if ((execute_at.year && !expression) || execute_at_null) + { + sql_print_information("SCHEDULER: [%s.%s of %s] no more executions " + "after this one", dbname.str, name.str, + definer.str); + flags |= EVENT_EXEC_NO_MORE | EVENT_FREE_WHEN_FINISHED; + } + + update_fields(thd); + if (!in_spawned_thread) { pthread_t th; in_spawned_thread= true; - if (pthread_create(&th, &connection_attrib, thread_func, (void*)this)) + + if (pthread_create(&th, &connection_attrib, thread_func, arg)) { DBUG_PRINT("info", ("problem while spawning thread")); ret= EVENT_EXEC_CANT_FORK; in_spawned_thread= false; } -#ifndef DBUG_OFF - else - { - sql_print_information("SCHEDULER: Started thread %d", ++exec_num); - DBUG_PRINT("info", ("thread spawned")); - } -#endif } else { @@ -1626,55 +1662,207 @@ Event_timed::spawn_now(void * (*thread_func)(void*)) } -void +bool Event_timed::spawn_thread_finish(THD *thd) { + bool should_free; DBUG_ENTER("Event_timed::spawn_thread_finish"); - VOID(pthread_mutex_lock(&this->LOCK_running)); + VOID(pthread_mutex_lock(&LOCK_running)); in_spawned_thread= false; - if ((flags & EVENT_EXEC_NO_MORE) || status == MYSQL_EVENT_DISABLED) + DBUG_PRINT("info", ("Sending COND_finished for thread %d", thread_id)); + thread_id= 0; + if (dropped) + drop(thd); + pthread_cond_broadcast(&COND_finished); + should_free= flags & EVENT_FREE_WHEN_FINISHED; + VOID(pthread_mutex_unlock(&LOCK_running)); + DBUG_RETURN(should_free); +} + + +/* + Kills a running event + SYNOPSIS + Event_timed::kill_thread() + + RETURN VALUE + 0 OK + -1 EVEX_CANT_KILL + !0 Error +*/ + +int +Event_timed::kill_thread(THD *thd) +{ + int ret= 0; + DBUG_ENTER("Event_timed::kill_thread"); + pthread_mutex_lock(&LOCK_running); + DBUG_PRINT("info", ("thread_id=%lu", thread_id)); + + if (thread_id == thd->thread_id) { - DBUG_PRINT("info", ("%s exec no more. to drop=%d", name.str, dropped)); - if (dropped) - drop(thd); - VOID(pthread_mutex_unlock(&this->LOCK_running)); - delete this; - DBUG_VOID_RETURN; + /* + We don't kill ourselves in cases like : + alter event e_43 do alter event e_43 do set @a = 4 because + we will never receive COND_finished. + */ + DBUG_PRINT("info", ("It's not safe to kill ourselves in self altering queries")); + ret= EVEX_CANT_KILL; } - VOID(pthread_mutex_unlock(&this->LOCK_running)); - DBUG_VOID_RETURN; + else if (thread_id && !(ret= kill_one_thread(thd, thread_id, false))) + { + thd->enter_cond(&COND_finished, &LOCK_running, "Waiting for finished"); + DBUG_PRINT("info", ("Waiting for COND_finished from thread %d", thread_id)); + while (thread_id) + pthread_cond_wait(&COND_finished, &LOCK_running); + + DBUG_PRINT("info", ("Got COND_finished")); + /* This will implicitly unlock LOCK_running. Hence we return before that */ + thd->exit_cond(""); + + DBUG_RETURN(0); + } + else if (!thread_id && in_spawned_thread) + { + /* + Because the manager thread waits for the forked thread to update thread_id + this situation is impossible. + */ + DBUG_ASSERT(0); + } + pthread_mutex_unlock(&LOCK_running); + DBUG_PRINT("exit", ("%d", ret)); + DBUG_RETURN(ret); +} + + +/* + Checks whether two events have the same name + + SYNOPSIS + event_timed_name_equal() + + RETURN VALUE + TRUE names are equal + FALSE names are not equal +*/ + +bool +event_timed_name_equal(Event_timed *et, LEX_STRING *name) +{ + return !sortcmp_lex_string(et->name, *name, system_charset_info); } /* - Unlocks the object after it has been locked with ::can_spawn_now_n_lock() + Checks whether two events are in the same schema + + SYNOPSIS + event_timed_db_equal() + + RETURN VALUE + TRUE schemas are equal + FALSE schemas are not equal +*/ + +bool +event_timed_db_equal(Event_timed *et, LEX_STRING *db) +{ + return !sortcmp_lex_string(et->dbname, *db, system_charset_info); +} + + +/* + Checks whether two events have the same definer + + SYNOPSIS + event_timed_definer_equal() Returns - 0 - ok - 1 - not locked by this thread + TRUE definers are equal + FALSE definers are not equal */ -int -Event_timed::spawn_unlock(THD *thd) +bool +event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) { - int ret= 0; - VOID(pthread_mutex_lock(&this->LOCK_running)); - if (!in_spawned_thread) + return !sortcmp_lex_string(et->definer, *definer, system_charset_info); +} + + +/* + Checks whether two events are equal by identifiers + + SYNOPSIS + event_timed_identifier_equal() + + RETURN VALUE + TRUE equal + FALSE not equal +*/ + +bool +event_timed_identifier_equal(Event_timed *a, Event_timed *b) +{ + return event_timed_name_equal(a, &b->name) && + event_timed_db_equal(a, &b->dbname) && + event_timed_definer_equal(a, &b->definer); +} + + +/* + Switches the security context + SYNOPSIS + change_security_context() + thd Thread + user The user + host The host of the user + db The schema for which the security_ctx will be loaded + s_ctx Security context to load state into + backup Where to store the old context + + RETURN VALUE + 0 - OK + 1 - Error (generates error too) +*/ + +bool +change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *s_ctx, + Security_context **backup) +{ + DBUG_ENTER("change_security_context"); + DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + s_ctx->init(); + *backup= 0; + if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str)) { - if (locked_by_thread_id == thd->thread_id) - { - in_spawned_thread= FALSE; - locked_by_thread_id= 0; - } - else - { - sql_print_error("A thread tries to unlock when he hasn't locked. " - "thread_id=%ld locked by %ld", - thd->thread_id, locked_by_thread_id); - DBUG_ASSERT(0); - ret= 1; - } + my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str); + DBUG_RETURN(TRUE); } - VOID(pthread_mutex_unlock(&this->LOCK_running)); - return ret; + *backup= thd->security_ctx; + thd->security_ctx= s_ctx; +#endif + DBUG_RETURN(FALSE); +} + + +/* + Restores the security context + SYNOPSIS + restore_security_context() + thd - thread + backup - switch to this context +*/ + +void +restore_security_context(THD *thd, Security_context *backup) +{ + DBUG_ENTER("restore_security_context"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (backup) + thd->security_ctx= backup; +#endif + DBUG_VOID_RETURN; } diff --git a/sql/field.cc b/sql/field.cc index 9c504f186b3..e1d7aaf6343 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1223,7 +1223,7 @@ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, field_name(field_name_arg), query_id(0), key_start(0), part_of_key(0), part_of_sortkey(0), unireg_check(unireg_check_arg), - field_length(length_arg),null_bit(null_bit_arg) + field_length(length_arg), null_bit(null_bit_arg), dflt_field(0) { flags=null_ptr ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; diff --git a/sql/field.h b/sql/field.h index b473100eaab..602b4ea779d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -55,6 +55,12 @@ public: char *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is /* + dflt_field is used only for the fields of temporary tables. + It points to the default value of the field in another table + from which this field has been created. + */ + Field *dflt_field; // Field to copy default value from + /* Note that you can use table->in_use as replacement for current_thd member only inside of val_*() and store() members (e.g. you can't use it in cons) */ diff --git a/sql/field_conv.cc b/sql/field_conv.cc index f718a3d778c..3eab782d167 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -641,7 +641,8 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) void field_conv(Field *to,Field *from) { - if (to->real_type() == from->real_type()) + if (to->real_type() == from->real_type() && + !(to->type() == FIELD_TYPE_BLOB && to->table->copy_blobs)) { if (to->pack_length() == from->pack_length() && !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 786d45a4966..40081c975c8 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -1786,7 +1786,8 @@ bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info, if (info->auto_increment_value != auto_increment_value || info->data_file_name != data_file_name || info->index_file_name != index_file_name || - table_changes == IS_EQUAL_NO) + table_changes == IS_EQUAL_NO || + table_changes & IS_EQUAL_PACK_LENGTH) // Not implemented yet return COMPATIBLE_DATA_NO; if ((options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM | diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 1cde37644bc..f4a052cea8a 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -74,7 +74,7 @@ handlerton myisammrg_hton= { NULL, /* Alter table flags */ NULL, /* Alter Tablespace */ NULL, /* Fill Files Table */ - HTON_CAN_RECREATE, + HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE, NULL, /* binlog_func */ NULL, /* binlog_log_query */ NULL /* release_temporary_latches */ diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 89b9ed93d69..3425c638005 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -106,7 +106,6 @@ static uint ndbcluster_alter_table_flags(uint flags) } -#define NDB_FAILED_AUTO_INCREMENT ~(Uint64)0 #define NDB_AUTO_INCREMENT_RETRIES 10 #define ERR_PRINT(err) \ @@ -182,6 +181,8 @@ static const char * ndb_connected_host= 0; static long ndb_connected_port= 0; static long ndb_number_of_replicas= 0; long ndb_number_of_storage_nodes= 0; +long ndb_number_of_ready_storage_nodes= 0; +long ndb_connect_count= 0; static int update_status_variables(Ndb_cluster_connection *c) { @@ -190,6 +191,8 @@ static int update_status_variables(Ndb_cluster_connection *c) ndb_connected_host= c->get_connected_host(); ndb_number_of_replicas= 0; ndb_number_of_storage_nodes= c->no_db_nodes(); + ndb_number_of_ready_storage_nodes= c->get_no_ready(); + ndb_connect_count= c->get_connect_count(); return 0; } @@ -2461,14 +2464,16 @@ int ha_ndbcluster::write_row(byte *record) { // Table has hidden primary key Ndb *ndb= get_ndb(); - Uint64 auto_value= NDB_FAILED_AUTO_INCREMENT; + int ret; + Uint64 auto_value; uint retries= NDB_AUTO_INCREMENT_RETRIES; do { - auto_value= ndb->getAutoIncrementValue(m_table); - } while (auto_value == NDB_FAILED_AUTO_INCREMENT && + Ndb_tuple_id_range_guard g(m_share); + ret= ndb->getAutoIncrementValue(m_table, g.range, auto_value, 1); + } while (ret == -1 && --retries && ndb->getNdbError().status == NdbError::TemporaryError); - if (auto_value == NDB_FAILED_AUTO_INCREMENT) + if (ret == -1) ERR_RETURN(ndb->getNdbError()); if (set_hidden_key(op, table_share->fields, (const byte*)&auto_value)) ERR_RETURN(op->getNdbError()); @@ -2563,11 +2568,12 @@ int ha_ndbcluster::write_row(byte *record) Ndb *ndb= get_ndb(); Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1; DBUG_PRINT("info", - ("Trying to set next auto increment value to %lu", - (ulong) next_val)); - if (ndb->setAutoIncrementValue(m_table, next_val, TRUE)) - DBUG_PRINT("info", - ("Setting next auto increment value to %u", next_val)); + ("Trying to set next auto increment value to %llu", + (ulonglong) next_val)); + Ndb_tuple_id_range_guard g(m_share); + if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE) + == -1) + ERR_RETURN(ndb->getNdbError()); } m_skip_auto_increment= TRUE; @@ -2875,7 +2881,7 @@ void ndb_unpack_record(TABLE *table, NdbValue *value, MY_BITMAP *defined, byte *buf) { Field **p_field= table->field, *field= *p_field; - uint row_offset= (uint) (buf - table->record[0]); + my_ptrdiff_t row_offset= buf - table->record[0]; DBUG_ENTER("ndb_unpack_record"); // Set null flag(s) @@ -2906,24 +2912,34 @@ void ndb_unpack_record(TABLE *table, NdbValue *value, } else if (field->type() == MYSQL_TYPE_BIT) { - byte *save_field_ptr= field->ptr; - field->ptr= save_field_ptr + row_offset; + Field_bit *field_bit= static_cast<Field_bit*>(field); + + /* + Move internal field pointer to point to 'buf'. Calling + the correct member function directly since we know the + type of the object. + */ + field_bit->Field_bit::move_field_offset(row_offset); if (field->pack_length() < 5) { DBUG_PRINT("info", ("bit field H'%.8X", (*value).rec->u_32_value())); - ((Field_bit*) field)->store((longlong) - (*value).rec->u_32_value(), FALSE); + field_bit->Field_bit::store((longlong) (*value).rec->u_32_value(), + FALSE); } else { DBUG_PRINT("info", ("bit field H'%.8X%.8X", *(Uint32*) (*value).rec->aRef(), *((Uint32*) (*value).rec->aRef()+1))); - ((Field_bit*) field)->store((longlong) - (*value).rec->u_64_value(),TRUE); + field_bit->Field_bit::store((longlong) (*value).rec->u_64_value(), + TRUE); } - field->ptr= save_field_ptr; + /* + Move back internal field pointer to point to original + value (usually record[0]). + */ + field_bit->Field_bit::move_field_offset(-row_offset); DBUG_PRINT("info",("[%u] SET", (*value).rec->getColumn()->getColumnNo())); DBUG_DUMP("info", (const char*) field->ptr, field->field_length); @@ -3528,9 +3544,19 @@ void ha_ndbcluster::info(uint flag) if (m_table) { Ndb *ndb= get_ndb(); + Ndb_tuple_id_range_guard g(m_share); - auto_increment_value= - ndb->readAutoIncrementValue(m_table); + Uint64 auto_increment_value64; + if (ndb->readAutoIncrementValue(m_table, g.range, + auto_increment_value64) == -1) + { + const NdbError err= ndb->getNdbError(); + sql_print_error("Error %lu in readAutoIncrementValue(): %s", + (ulong) err.code, err.message); + auto_increment_value= ~(Uint64)0; + } + else + auto_increment_value= (ulonglong)auto_increment_value64; } } DBUG_VOID_RETURN; @@ -5222,17 +5248,18 @@ ulonglong ha_ndbcluster::get_auto_increment() m_rows_to_insert - m_rows_inserted : ((m_rows_to_insert > m_autoincrement_prefetch) ? m_rows_to_insert : m_autoincrement_prefetch)); - auto_value= NDB_FAILED_AUTO_INCREMENT; + int ret; uint retries= NDB_AUTO_INCREMENT_RETRIES; do { - auto_value= - (m_skip_auto_increment) ? - ndb->readAutoIncrementValue(m_table) - : ndb->getAutoIncrementValue(m_table, cache_size); - } while (auto_value == NDB_FAILED_AUTO_INCREMENT && + Ndb_tuple_id_range_guard g(m_share); + ret= + m_skip_auto_increment ? + ndb->readAutoIncrementValue(m_table, g.range, auto_value) : + ndb->getAutoIncrementValue(m_table, g.range, auto_value, cache_size); + } while (ret == -1 && --retries && ndb->getNdbError().status == NdbError::TemporaryError); - if (auto_value == NDB_FAILED_AUTO_INCREMENT) + if (ret == -1) { const NdbError err= ndb->getNdbError(); sql_print_error("Error %lu in ::get_auto_increment(): %s", @@ -7118,10 +7145,6 @@ void ndbcluster_real_free_share(NDB_SHARE **share) #ifndef DBUG_OFF bzero((gptr)(*share)->table_share, sizeof(*(*share)->table_share)); bzero((gptr)(*share)->table, sizeof(*(*share)->table)); -#endif - my_free((gptr) (*share)->table_share, MYF(0)); - my_free((gptr) (*share)->table, MYF(0)); -#ifndef DBUG_OFF (*share)->table_share= 0; (*share)->table= 0; #endif @@ -9351,11 +9374,15 @@ ndbcluster_show_status(THD* thd, stat_print_fn *stat_print, "cluster_node_id=%u, " "connected_host=%s, " "connected_port=%u, " - "number_of_storage_nodes=%u", + "number_of_storage_nodes=%u, " + "number_of_ready_storage_nodes=%u, " + "connect_count=%u", ndb_cluster_node_id, ndb_connected_host, ndb_connected_port, - ndb_number_of_storage_nodes); + ndb_number_of_storage_nodes, + ndb_number_of_ready_storage_nodes, + ndb_connect_count); if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name), "connection", strlen("connection"), buf, buflen)) diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index f407cb0090f..badca69941a 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -106,6 +106,7 @@ typedef struct st_ndbcluster_share { ulonglong commit_count; char *db; char *table_name; + Ndb::TupleIdRange tuple_id_range; #ifdef HAVE_NDB_BINLOG uint32 flags; NdbEventOperation *op; @@ -113,6 +114,7 @@ typedef struct st_ndbcluster_share { char *old_names; // for rename table TABLE_SHARE *table_share; TABLE *table; + byte *record[2]; // pointer to allocated records for receiving data NdbValue *ndb_value[2]; MY_BITMAP *subscriber_bitmap; #endif @@ -138,6 +140,19 @@ set_ndb_share_state(NDB_SHARE *share, NDB_SHARE_STATE state) pthread_mutex_unlock(&share->mutex); } +struct Ndb_tuple_id_range_guard { + Ndb_tuple_id_range_guard(NDB_SHARE* _share) : + share(_share), + range(share->tuple_id_range) { + pthread_mutex_lock(&share->mutex); + } + ~Ndb_tuple_id_range_guard() { + pthread_mutex_unlock(&share->mutex); + } + NDB_SHARE* share; + Ndb::TupleIdRange& range; +}; + #ifdef HAVE_NDB_BINLOG /* NDB_SHARE.flags */ #define NSF_HIDDEN_PK 1 /* table has hidden primary key */ @@ -725,7 +740,6 @@ private: int drop_indexes(Ndb *ndb, TABLE *tab); int add_index_handle(THD *thd, NdbDictionary::Dictionary *dict, KEY *key_info, const char *index_name, uint index_no); - int initialize_autoincrement(const void *table); int get_metadata(const char* path); void release_metadata(THD *thd, Ndb *ndb); NDB_INDEX_TYPE get_index_type(uint idx_no) const; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index a125fd5e722..144c073d565 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -25,6 +25,7 @@ #include "slave.h" #include "ha_ndbcluster_binlog.h" #include "NdbDictionary.hpp" +#include <util/NdbAutoPtr.hpp> #ifdef ndb_dynamite #undef assert @@ -265,7 +266,8 @@ ndbcluster_binlog_close_table(THD *thd, NDB_SHARE *share) static int ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, - TABLE_SHARE *table_share, TABLE *table) + TABLE_SHARE *table_share, TABLE *table, + int reopen) { int error; DBUG_ENTER("ndbcluster_binlog_open_table"); @@ -278,27 +280,34 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, share->key, error); DBUG_PRINT("error", ("open_table_def failed %d", error)); free_table_share(table_share); - my_free((gptr) table_share, MYF(0)); - my_free((gptr) table, MYF(0)); DBUG_RETURN(error); } - if ((error= open_table_from_share(thd, table_share, "", 0, + if ((error= open_table_from_share(thd, table_share, "", 0 /* fon't allocate buffers */, (uint) READ_ALL, 0, table, FALSE))) { sql_print_error("Unable to open table for %s, error=%d(%d)", share->key, error, my_errno); DBUG_PRINT("error", ("open_table_from_share failed %d", error)); free_table_share(table_share); - my_free((gptr) table_share, MYF(0)); - my_free((gptr) table, MYF(0)); DBUG_RETURN(error); } assign_new_table_id(table_share); - if (!table->record[1] || table->record[1] == table->record[0]) + + if (!reopen) + { + // allocate memory on ndb share so it can be reused after online alter table + share->record[0]= (byte*) alloc_root(&share->mem_root, table->s->rec_buff_length); + share->record[1]= (byte*) alloc_root(&share->mem_root, table->s->rec_buff_length); + } { - table->record[1]= alloc_root(&table->mem_root, - table->s->rec_buff_length); + my_ptrdiff_t row_offset= share->record[0] - table->record[0]; + Field **p_field; + for (p_field= table->field; *p_field; p_field++) + (*p_field)->move_field_offset(row_offset); + table->record[0]= share->record[0]; + table->record[1]= share->record[1]; } + table->in_use= injector_thd; table->s->db.str= share->db; @@ -366,10 +375,9 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) while (1) { int error; - TABLE_SHARE *table_share= - (TABLE_SHARE *) my_malloc(sizeof(*table_share), MYF(MY_WME)); - TABLE *table= (TABLE*) my_malloc(sizeof(*table), MYF(MY_WME)); - if ((error= ndbcluster_binlog_open_table(thd, share, table_share, table))) + TABLE_SHARE *table_share= (TABLE_SHARE *) alloc_root(mem_root, sizeof(*table_share)); + TABLE *table= (TABLE*) alloc_root(mem_root, sizeof(*table)); + if ((error= ndbcluster_binlog_open_table(thd, share, table_share, table, 0))) break; /* ! do not touch the contents of the table @@ -1535,6 +1543,10 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, sql_print_information("NDB: Failed write frm for %s.%s, error %d", dbname, tabname, error); } + + // copy names as memory will be freed + NdbAutoPtr<char> a1((char *)(dbname= strdup(dbname))); + NdbAutoPtr<char> a2((char *)(tabname= strdup(tabname))); ndbcluster_binlog_close_table(thd, share); TABLE_LIST table_list; @@ -1543,10 +1555,16 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, table_list.alias= table_list.table_name= (char *)tabname; close_cached_tables(thd, 0, &table_list, TRUE); - if ((error= ndbcluster_binlog_open_table(thd, share, - table_share, table))) + if ((error= ndbcluster_binlog_open_table(thd, share, + table_share, table, 1))) sql_print_information("NDB: Failed to re-open table %s.%s", dbname, tabname); + + table= share->table; + table_share= share->table_share; + dbname= table_share->db.str; + tabname= table_share->table_name.str; + pthread_mutex_unlock(&LOCK_open); } my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR)); @@ -1689,10 +1707,15 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, pthread_mutex_lock(&LOCK_open); if (ndb_create_table_from_engine(thd, schema->db, schema->name)) { - sql_print_error("Could not discover table '%s.%s' from " - "binlog schema event '%s' from node %d", + sql_print_error("NDB binlog: Could not discover table '%s.%s' from " + "binlog schema event '%s' from node %d. " + "my_errno: %d", schema->db, schema->name, schema->query, - schema->node_id); + schema->node_id, my_errno); + List_iterator_fast<MYSQL_ERROR> it(thd->warn_list); + MYSQL_ERROR *err; + while ((err= it++)) + sql_print_warning("NDB binlog: (%d)%s", err->code, err->msg); } pthread_mutex_unlock(&LOCK_open); log_query= 1; @@ -1770,6 +1793,9 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, // skip break; case NDBEVENT::TE_CLUSTER_FAILURE: + if (ndb_extra_logging) + sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.", + schema_share->key, (unsigned) pOp->getGCI()); // fall through case NDBEVENT::TE_DROP: if (ndb_extra_logging && @@ -1778,7 +1804,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, "read only on reconnect."); free_share(&schema_share); schema_share= 0; - ndb_binlog_tables_inited= FALSE; + close_cached_tables((THD*) 0, 0, (TABLE_LIST*) 0, FALSE); // fall through case NDBEVENT::TE_ALTER: ndb_handle_schema_change(thd, ndb, pOp, tmp_share); @@ -1916,7 +1942,7 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, // fall through case SOT_RENAME_TABLE_NEW: log_query= 1; - if (ndb_binlog_running) + if (ndb_binlog_running && (!share || !share->op)) { /* we need to free any share here as command below @@ -1930,10 +1956,14 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, pthread_mutex_lock(&LOCK_open); if (ndb_create_table_from_engine(thd, schema->db, schema->name)) { - sql_print_error("Could not discover table '%s.%s' from " - "binlog schema event '%s' from node %d", + sql_print_error("NDB binlog: Could not discover table '%s.%s' from " + "binlog schema event '%s' from node %d. my_errno: %d", schema->db, schema->name, schema->query, - schema->node_id); + schema->node_id, my_errno); + List_iterator_fast<MYSQL_ERROR> it(thd->warn_list); + MYSQL_ERROR *err; + while ((err= it++)) + sql_print_warning("NDB binlog: (%d)%s", err->code, err->msg); } pthread_mutex_unlock(&LOCK_open); } @@ -2092,7 +2122,14 @@ add_binlog_index_err: Functions for start, stop, wait for ndbcluster binlog thread *********************************************************************/ -static int do_ndbcluster_binlog_close_connection= 0; +enum Binlog_thread_state +{ + BCCC_running= 0, + BCCC_exit= 1, + BCCC_restart= 2 +}; + +static enum Binlog_thread_state do_ndbcluster_binlog_close_connection= BCCC_restart; int ndbcluster_binlog_start() { @@ -2130,7 +2167,7 @@ static void ndbcluster_binlog_close_connection(THD *thd) DBUG_ENTER("ndbcluster_binlog_close_connection"); const char *save_info= thd->proc_info; thd->proc_info= "ndbcluster_binlog_close_connection"; - do_ndbcluster_binlog_close_connection= 1; + do_ndbcluster_binlog_close_connection= BCCC_exit; while (ndb_binlog_thread_running > 0) sleep(1); thd->proc_info= save_info; @@ -2810,7 +2847,8 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, { case NDBEVENT::TE_CLUSTER_FAILURE: if (ndb_extra_logging) - sql_print_information("NDB Binlog: cluster failure for %s.", share->key); + sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.", + share->key, (unsigned) pOp->getGCI()); if (apply_status_share == share) { if (ndb_extra_logging && @@ -2819,7 +2857,6 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, "read only on reconnect."); free_share(&apply_status_share); apply_status_share= 0; - ndb_binlog_tables_inited= FALSE; } DBUG_PRINT("info", ("CLUSTER FAILURE EVENT: " "%s received share: 0x%lx op: %lx share op: %lx " @@ -2835,7 +2872,6 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, "read only on reconnect."); free_share(&apply_status_share); apply_status_share= 0; - ndb_binlog_tables_inited= FALSE; } /* ToDo: remove printout */ if (ndb_extra_logging) @@ -3248,37 +3284,73 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) pthread_mutex_unlock(&injector_mutex); pthread_cond_signal(&injector_cond); - thd->proc_info= "Waiting for ndbcluster to start"; - - pthread_mutex_lock(&injector_mutex); - while (!schema_share || - (ndb_binlog_running && !apply_status_share)) +restart: + /* + Main NDB Injector loop + */ { - /* ndb not connected yet */ - struct timespec abstime; - set_timespec(abstime, 1); - pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime); - if (abort_loop) + thd->proc_info= "Waiting for ndbcluster to start"; + + pthread_mutex_lock(&injector_mutex); + while (!schema_share || + (ndb_binlog_running && !apply_status_share)) { - pthread_mutex_unlock(&injector_mutex); - goto err; + /* ndb not connected yet */ + struct timespec abstime; + set_timespec(abstime, 1); + pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime); + if (abort_loop) + { + pthread_mutex_unlock(&injector_mutex); + goto err; + } } - } - pthread_mutex_unlock(&injector_mutex); + pthread_mutex_unlock(&injector_mutex); - /* - Main NDB Injector loop - */ + if (thd_ndb == NULL) + { + DBUG_ASSERT(ndbcluster_hton.slot != ~(uint)0); + if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) + { + sql_print_error("Could not allocate Thd_ndb object"); + goto err; + } + set_thd_ndb(thd, thd_ndb); + thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; + thd->query_id= 0; // to keep valgrind quiet + } + } - DBUG_ASSERT(ndbcluster_hton.slot != ~(uint)0); - if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) { - sql_print_error("Could not allocate Thd_ndb object"); - goto err; + // wait for the first event + thd->proc_info= "Waiting for first event from ndbcluster"; + DBUG_PRINT("info", ("Waiting for the first event")); + int schema_res= 0; + Uint64 schema_gci= 0; + while (schema_res == 0 && !abort_loop) + { + schema_res= s_ndb->pollEvents(100, &schema_gci); + } + // now check that we have epochs consistant with what we had before the restart + DBUG_PRINT("info", ("schema_res: %d schema_gci: %d", schema_res, schema_gci)); + if (schema_res > 0) + { + i_ndb->pollEvents(0); + i_ndb->flushIncompleteEvents(schema_gci); + s_ndb->flushIncompleteEvents(schema_gci); + if (schema_gci < ndb_latest_handled_binlog_epoch) + { + sql_print_error("NDB Binlog: cluster has been restarted --initial or with older filesystem. " + "ndb_latest_handled_binlog_epoch: %u, while current epoch: %u. " + "RESET MASTER should be issued. Resetting ndb_latest_handled_binlog_epoch.", + (unsigned) ndb_latest_handled_binlog_epoch, (unsigned) schema_gci); + g_latest_trans_gci= 0; + ndb_latest_handled_binlog_epoch= 0; + ndb_latest_applied_binlog_epoch= 0; + ndb_latest_received_binlog_epoch= 0; + } + } } - set_thd_ndb(thd, thd_ndb); - thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; - thd->query_id= 0; // to keep valgrind quiet { static char db[]= ""; thd->db= db; @@ -3286,11 +3358,20 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) open_binlog_index(thd, &binlog_tables, &binlog_index); thd->db= db; } - + do_ndbcluster_binlog_close_connection= BCCC_running; for ( ; !((abort_loop || do_ndbcluster_binlog_close_connection) && - ndb_latest_handled_binlog_epoch >= g_latest_trans_gci); ) + ndb_latest_handled_binlog_epoch >= g_latest_trans_gci) && + do_ndbcluster_binlog_close_connection != BCCC_restart; ) { - +#ifndef DBUG_OFF + if (do_ndbcluster_binlog_close_connection) + { + DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection: %d, " + "ndb_latest_handled_binlog_epoch: %llu, " + "g_latest_trans_gci: %llu", do_ndbcluster_binlog_close_connection, + ndb_latest_handled_binlog_epoch, g_latest_trans_gci)); + } +#endif #ifdef RUN_NDB_BINLOG_TIMER main_timer.stop(); sql_print_information("main_timer %ld ms", main_timer.elapsed_ms()); @@ -3315,7 +3396,13 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) ndb_latest_received_binlog_epoch= gci; while (gci > schema_gci && schema_res >= 0) + { + static char buf[64]; + thd->proc_info= "Waiting for schema epoch"; + my_snprintf(buf, sizeof(buf), "%s %u(%u)", thd->proc_info, (unsigned) schema_gci, (unsigned) gci); + thd->proc_info= buf; schema_res= s_ndb->pollEvents(10, &schema_gci); + } if ((abort_loop || do_ndbcluster_binlog_close_connection) && (ndb_latest_handled_binlog_epoch >= g_latest_trans_gci || @@ -3351,10 +3438,31 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) while (pOp != NULL) { if (!pOp->hasError()) + { ndb_binlog_thread_handle_schema_event(thd, s_ndb, pOp, &post_epoch_log_list, &post_epoch_unlock_list, &mem_root); + DBUG_PRINT("info", ("s_ndb first: %s", s_ndb->getEventOperation() ? + s_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + DBUG_PRINT("info", ("i_ndb first: %s", i_ndb->getEventOperation() ? + i_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + if (i_ndb->getEventOperation() == NULL && + s_ndb->getEventOperation() == NULL && + do_ndbcluster_binlog_close_connection == BCCC_running) + { + DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection= BCCC_restart")); + do_ndbcluster_binlog_close_connection= BCCC_restart; + if (ndb_latest_received_binlog_epoch < g_latest_trans_gci && ndb_binlog_running) + { + sql_print_error("NDB Binlog: latest transaction in epoch %lld not in binlog " + "as latest received epoch is %lld", + g_latest_trans_gci, ndb_latest_received_binlog_epoch); + } + } + } else sql_print_error("NDB: error %lu (%s) on handling " "binlog schema event", @@ -3523,6 +3631,25 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) ndb_binlog_thread_handle_non_data_event(thd, i_ndb, pOp, row); // reset to catch errors i_ndb->setDatabaseName(""); + DBUG_PRINT("info", ("s_ndb first: %s", s_ndb->getEventOperation() ? + s_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + DBUG_PRINT("info", ("i_ndb first: %s", i_ndb->getEventOperation() ? + i_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + if (i_ndb->getEventOperation() == NULL && + s_ndb->getEventOperation() == NULL && + do_ndbcluster_binlog_close_connection == BCCC_running) + { + DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection= BCCC_restart")); + do_ndbcluster_binlog_close_connection= BCCC_restart; + if (ndb_latest_received_binlog_epoch < g_latest_trans_gci && ndb_binlog_running) + { + sql_print_error("NDB Binlog: latest transaction in epoch %lld not in binlog " + "as latest received epoch is %lld", + g_latest_trans_gci, ndb_latest_received_binlog_epoch); + } + } } pOp= i_ndb->nextEvent(); @@ -3578,6 +3705,13 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) *root_ptr= old_root; ndb_latest_handled_binlog_epoch= ndb_latest_received_binlog_epoch; } + if (do_ndbcluster_binlog_close_connection == BCCC_restart) + { + ndb_binlog_tables_inited= FALSE; + close_thread_tables(thd); + binlog_index= 0; + goto restart; + } err: DBUG_PRINT("info",("Shutting down cluster binlog thread")); thd->proc_info= "Shutting down"; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b1a5a447b6f..af0556f1e6f 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1108,8 +1108,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, part)); if ((error= handle_opt_part(thd, check_opt, m_file[part], flag))) { - my_error(ER_GET_ERRNO, MYF(0), error); - DBUG_RETURN(TRUE); + DBUG_RETURN(error); } } while (++j < no_subparts); } @@ -1118,8 +1117,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, DBUG_PRINT("info", ("Optimize partition %u", i)); if ((error= handle_opt_part(thd, check_opt, m_file[i], flag))) { - my_error(ER_GET_ERRNO, MYF(0), error); - DBUG_RETURN(TRUE); + DBUG_RETURN(error); } } } @@ -4175,6 +4173,8 @@ void ha_partition::info(uint flag) index_file_length: Length of index file, in principle bytes in indexes in the table We report sum + delete_length: Length of free space easily used by new records in table + We report sum mean_record_length:Mean record length in the table We calculate this check_time: Time of last check (only applicable to MyISAM) @@ -4184,6 +4184,7 @@ void ha_partition::info(uint flag) deleted= 0; data_file_length= 0; index_file_length= 0; + delete_length= 0; check_time= 0; file_array= m_file; do @@ -4196,6 +4197,7 @@ void ha_partition::info(uint flag) deleted+= file->deleted; data_file_length+= file->data_file_length; index_file_length+= file->index_file_length; + delete_length+= file->delete_length; if (file->check_time > check_time) check_time= file->check_time; } @@ -5325,6 +5327,82 @@ void ha_partition::init_table_handle_for_HANDLER() /**************************************************************************** + MODULE enable/disable indexes +****************************************************************************/ + +/* + Disable indexes for a while + SYNOPSIS + disable_indexes() + mode Mode + RETURN VALUES + 0 Success + != 0 Error +*/ + +int ha_partition::disable_indexes(uint mode) +{ + handler **file; + int error= 0; + + for (file= m_file; *file; file++) + { + if ((error= (*file)->disable_indexes(mode))) + break; + } + return error; +} + + +/* + Enable indexes again + SYNOPSIS + enable_indexes() + mode Mode + RETURN VALUES + 0 Success + != 0 Error +*/ + +int ha_partition::enable_indexes(uint mode) +{ + handler **file; + int error= 0; + + for (file= m_file; *file; file++) + { + if ((error= (*file)->enable_indexes(mode))) + break; + } + return error; +} + + +/* + Check if indexes are disabled + SYNOPSIS + indexes_are_disabled() + + RETURN VALUES + 0 Indexes are enabled + != 0 Indexes are disabled +*/ + +int ha_partition::indexes_are_disabled(void) +{ + handler **file; + int error= 0; + + for (file= m_file; *file; file++) + { + if ((error= (*file)->indexes_are_disabled())) + break; + } + return error; +} + + +/**************************************************************************** MODULE Partition Share ****************************************************************************/ /* diff --git a/sql/ha_partition.h b/sql/ha_partition.h index b31b9af28a3..1443a20133c 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -938,17 +938,18 @@ public: virtual uint checksum() const; virtual bool is_crashed() const; virtual bool auto_repair() const; + */ + /* ------------------------------------------------------------------------- MODULE enable/disable indexes ------------------------------------------------------------------------- - Enable/Disable Indexes are not supported currently (Heap, MyISAM) - This means that the following methods are not implemented: + Enable/Disable Indexes are only supported by HEAP and MyISAM. ------------------------------------------------------------------------- + */ virtual int disable_indexes(uint mode); virtual int enable_indexes(uint mode); virtual int indexes_are_disabled(void); - */ /* ------------------------------------------------------------------------- diff --git a/sql/handler.cc b/sql/handler.cc index b9ef05a33c2..f51e91f1882 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1818,6 +1818,24 @@ ulonglong handler::get_auto_increment() } +void handler::print_keydupp_error(uint key_nr, const char *msg) +{ + /* Write the duplicated key in the error message */ + char key[MAX_KEY_LENGTH]; + String str(key,sizeof(key),system_charset_info); + /* Table is opened and defined at this point */ + key_unpack(&str,table,(uint) key_nr); + uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg); + if (str.length() >= max_length) + { + str.length(max_length-4); + str.append(STRING_WITH_LEN("...")); + } + my_printf_error(ER_DUP_ENTRY, msg, + MYF(0), str.c_ptr(), table->key_info[key_nr].name); +} + + /* Print error that we got from handler function @@ -1857,18 +1875,7 @@ void handler::print_error(int error, myf errflag) uint key_nr=get_dup_key(error); if ((int) key_nr >= 0) { - /* Write the duplicated key in the error message */ - char key[MAX_KEY_LENGTH]; - String str(key,sizeof(key),system_charset_info); - /* Table is opened and defined at this point */ - key_unpack(&str,table,(uint) key_nr); - uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_DUP_ENTRY)); - if (str.length() >= max_length) - { - str.length(max_length-4); - str.append(STRING_WITH_LEN("...")); - } - my_error(ER_DUP_ENTRY, MYF(0), str.c_ptr(), table->key_info[key_nr].name); + print_keydupp_error(key_nr, ER(ER_DUP_ENTRY)); DBUG_VOID_RETURN; } textno=ER_DUP_KEY; diff --git a/sql/handler.h b/sql/handler.h index d988e46b236..9a5a3b04823 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -603,6 +603,7 @@ struct show_table_alias_st { #define HTON_FLUSH_AFTER_RENAME (1 << 4) #define HTON_NOT_USER_SELECTABLE (1 << 5) #define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported +#define HTON_ALTER_CANNOT_CREATE (1 << 7) //Cannot use alter to create typedef struct st_thd_trans { @@ -864,6 +865,7 @@ public: virtual int ha_initialise(); int ha_open(TABLE *table, const char *name, int mode, int test_if_locked); bool update_auto_increment(); + void print_keydupp_error(uint key_nr, const char *msg); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint get_dup_key(int error); diff --git a/sql/item.cc b/sql/item.cc index f778f0cb38e..d596699dd30 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -304,6 +304,7 @@ Item::Item(): marker= 0; maybe_null=null_value=with_sum_func=unsigned_flag=0; decimals= 0; max_length= 0; + with_subselect= 0; /* Put item in free list so that we can free all items at end */ THD *thd= current_thd; @@ -961,6 +962,12 @@ void Item_splocal::print(String *str) } +bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) +{ + return ctx->set_variable(thd, get_var_idx(), it); +} + + /***************************************************************************** Item_case_expr methods *****************************************************************************/ @@ -1890,7 +1897,6 @@ Item_decimal::Item_decimal(const char *str_arg, uint length, name= (char*) str_arg; decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1900,7 +1906,6 @@ Item_decimal::Item_decimal(longlong val, bool unsig) int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1911,7 +1916,6 @@ Item_decimal::Item_decimal(double val, int precision, int scale) double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1924,7 +1928,6 @@ Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, name= (char*) str; decimals= (uint8) decimal_par; max_length= length; - unsigned_flag= !decimal_value.sign(); fixed= 1; } @@ -1934,7 +1937,6 @@ Item_decimal::Item_decimal(my_decimal *value_par) my_decimal2decimal(value_par, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1946,7 +1948,6 @@ Item_decimal::Item_decimal(const char *bin, int precision, int scale) &decimal_value, precision, scale); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(precision, decimals, unsigned_flag); } @@ -4881,7 +4882,16 @@ void Item_ref::cleanup() void Item_ref::print(String *str) { if (ref) - (*ref)->print(str); + { + if ((*ref)->type() != Item::CACHE_ITEM && ref_type() != VIEW_REF && + name && alias_name_used) + { + THD *thd= current_thd; + append_identifier(thd, str, name, (uint) strlen(name)); + } + else + (*ref)->print(str); + } else Item_ident::print(str); } @@ -5413,6 +5423,25 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const } +void Item_trigger_field::set_required_privilege(bool rw) +{ + /* + Require SELECT and UPDATE privilege if this field will be read and + set, and only UPDATE privilege for setting the field. + */ + want_privilege= (rw ? SELECT_ACL | UPDATE_ACL : UPDATE_ACL); +} + + +bool Item_trigger_field::set_value(THD *thd, sp_rcontext */*ctx*/, Item **it) +{ + Item *item= sp_prepare_func_item(thd, it); + + return (!item || (!fixed && fix_fields(thd, 0)) || + (item->save_in_field(field, 0) < 0)); +} + + bool Item_trigger_field::fix_fields(THD *thd, Item **items) { /* @@ -5435,8 +5464,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items) if (table_grants) { - table_grants->want_privilege= - access_type == AT_READ ? SELECT_ACL : UPDATE_ACL; + table_grants->want_privilege= want_privilege; if (check_grant_column(thd, table_grants, triggers->table->s->db.str, triggers->table->s->table_name.str, field_name, @@ -5468,6 +5496,7 @@ void Item_trigger_field::print(String *str) void Item_trigger_field::cleanup() { + want_privilege= original_privilege; /* Since special nature of Item_trigger_field we should not do most of things from Item_field::cleanup() or Item_ident::cleanup() here. diff --git a/sql/item.h b/sql/item.h index f73017563dd..2f99034130a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -401,6 +401,42 @@ typedef enum monotonicity_info /*************************************************************************/ +class sp_rcontext; + + +class Settable_routine_parameter +{ +public: + /* + Set required privileges for accessing the parameter. + + SYNOPSIS + set_required_privilege() + rw if 'rw' is true then we are going to read and set the + parameter, so SELECT and UPDATE privileges might be + required, otherwise we only reading it and SELECT + privilege might be required. + */ + virtual void set_required_privilege(bool rw) {}; + + /* + Set parameter value. + + SYNOPSIS + set_value() + thd thread handle + ctx context to which parameter belongs (if it is local + variable). + it item which represents new value + + RETURN + FALSE if parameter value has been set, + TRUE if error has occured. + */ + virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0; +}; + + typedef bool (Item::*Item_processor)(byte *arg); typedef Item* (Item::*Item_transformer) (byte *arg); typedef void (*Cond_traverser) (const Item *item, void *arg); @@ -454,6 +490,9 @@ public: my_bool is_autogenerated_name; /* indicate was name of this Item autogenerated or set by user */ DTCollation collation; + my_bool with_subselect; /* If this item is a subselect or some + of its arguments is or contains a + subselect */ // alloc & destruct is done as start of select using sql_alloc Item(); @@ -784,6 +823,15 @@ public: } virtual bool is_splocal() { return 0; } /* Needed for error checking */ + + /* + Return Settable_routine_parameter interface of the Item. Return 0 + if this Item is not Settable_routine_parameter. + */ + virtual Settable_routine_parameter *get_settable_routine_parameter() + { + return 0; + } }; @@ -882,7 +930,8 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str) runtime. *****************************************************************************/ -class Item_splocal :public Item_sp_variable +class Item_splocal :public Item_sp_variable, + private Settable_routine_parameter { uint m_var_idx; @@ -920,6 +969,15 @@ public: inline enum Type type() const; inline Item_result result_type() const; + +private: + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + +public: + Settable_routine_parameter *get_settable_routine_parameter() + { + return this; + } }; /***************************************************************************** @@ -2146,14 +2204,13 @@ class Table_triggers_list; two Field instances representing either OLD or NEW version of this field. */ -class Item_trigger_field : public Item_field +class Item_trigger_field : public Item_field, + private Settable_routine_parameter { public: /* Is this item represents row from NEW or OLD row ? */ enum row_version_type {OLD_ROW, NEW_ROW}; row_version_type row_version; - /* Is this item used for reading or updating the value? */ - enum access_types { AT_READ = 0x1, AT_UPDATE = 0x2 }; /* Next in list of all Item_trigger_field's in trigger */ Item_trigger_field *next_trg_field; /* Index of the field in the TABLE::field array */ @@ -2164,11 +2221,11 @@ public: Item_trigger_field(Name_resolution_context *context_arg, row_version_type row_ver_arg, const char *field_name_arg, - access_types access_type_arg) + ulong priv, const bool ro) :Item_field(context_arg, (const char *)NULL, (const char *)NULL, field_name_arg), - row_version(row_ver_arg), field_idx((uint)-1), - access_type(access_type_arg), table_grants(NULL) + row_version(row_ver_arg), field_idx((uint)-1), original_privilege(priv), + want_privilege(priv), table_grants(NULL), read_only (ro) {} void setup_field(THD *thd, TABLE *table, GRANT_INFO *table_grant_info); enum Type type() const { return TRIGGER_FIELD_ITEM; } @@ -2179,8 +2236,39 @@ public: void cleanup(); private: - access_types access_type; + void set_required_privilege(bool rw); + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + +public: + Settable_routine_parameter *get_settable_routine_parameter() + { + return (read_only ? 0 : this); + } + + bool set_value(THD *thd, Item **it) + { + return set_value(thd, NULL, it); + } + +private: + /* + 'want_privilege' holds privileges required to perform operation on + this trigger field (SELECT_ACL if we are going to read it and + UPDATE_ACL if we are going to update it). It is initialized at + parse time but can be updated later if this trigger field is used + as OUT or INOUT parameter of stored routine (in this case + set_required_privilege() is called to appropriately update + want_privilege and cleanup() is responsible for restoring of + original want_privilege once parameter's value is updated). + */ + ulong original_privilege; + ulong want_privilege; GRANT_INFO *table_grants; + /* + Trigger field is read-only unless it belongs to the NEW row in a + BEFORE INSERT of BEFORE UPDATE trigger. + */ + bool read_only; }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index eb26f7ff960..0552e8b6336 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -204,10 +204,28 @@ longlong Item_func_nop_all::val_int() /* - Convert a constant expression or string to an integer. - This is done when comparing DATE's of different formats and - also when comparing bigint to strings (in which case the string - is converted once to a bigint). + Convert a constant item to an int and replace the original item + + SYNOPSIS + convert_constant_item() + thd thread handle + field item will be converted using the type of this field + item [in/out] reference to the item to convert + + DESCRIPTION + The function converts a constant expression or string to an integer. + On successful conversion the original item is substituted for the + result of the item evaluation. + This is done when comparing DATE/TIME of different formats and + also when comparing bigint to strings (in which case strings + are converted to bigints). + + NOTES + This function is called only at prepare stage. + As all derived tables are filled only after all derived tables + are prepared we do not evaluate items with subselects here because + they can contain derived tables and thus we may attempt to use a + table that has not been populated yet. RESULT VALUES 0 Can't convert item @@ -216,7 +234,7 @@ longlong Item_func_nop_all::val_int() static bool convert_constant_item(THD *thd, Field *field, Item **item) { - if ((*item)->const_item()) + if (!(*item)->with_subselect && (*item)->const_item()) { /* For comparison purposes allow invalid dates like 2000-01-32 */ ulong orig_sql_mode= field->table->in_use->variables.sql_mode; @@ -2570,7 +2588,9 @@ Item_cond::fix_fields(THD *thd, Item **ref) (item= *li.ref())->check_cols(1)) return TRUE; /* purecov: inspected */ used_tables_cache|= item->used_tables(); - if (!item->const_item()) + if (item->const_item()) + and_tables_cache= (table_map) 0; + else { tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; @@ -2578,6 +2598,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) const_item_cache= FALSE; } with_sum_func= with_sum_func || item->with_sum_func; + with_subselect|= item->with_subselect; if (item->maybe_null) maybe_null=1; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 82e07ebdefe..acdaa4b246d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -184,6 +184,7 @@ Item_func::fix_fields(THD *thd, Item **ref) used_tables_cache|= item->used_tables(); not_null_tables_cache|= item->not_null_tables(); const_item_cache&= item->const_item(); + with_subselect|= item->with_subselect; } } fix_length_and_dec(); @@ -4122,6 +4123,18 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const } +bool Item_func_get_user_var::set_value(THD *thd, + sp_rcontext */*ctx*/, Item **it) +{ + Item_func_set_user_var *suv= new Item_func_set_user_var(get_name(), *it); + /* + Item_func_set_user_var is not fixed after construction, call + fix_fields(). + */ + return (!suv || suv->fix_fields(thd, it) || suv->check() || suv->update()); +} + + bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); @@ -4754,6 +4767,7 @@ Item_func_sp::sp_result_field(void) const dummy_table->alias= empty_name; dummy_table->maybe_null= maybe_null; dummy_table->in_use= current_thd; + dummy_table->copy_blobs= TRUE; dummy_table->s->table_cache_key.str = empty_name; dummy_table->s->table_name.str= empty_name; dummy_table->s->db.str= empty_name; diff --git a/sql/item_func.h b/sql/item_func.h index a91d93be8c6..1d8a1bd5e22 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1179,7 +1179,8 @@ public: }; -class Item_func_get_user_var :public Item_func +class Item_func_get_user_var :public Item_func, + private Settable_routine_parameter { user_var_entry *var_entry; @@ -1206,6 +1207,15 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; + +private: + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + +public: + Settable_routine_parameter *get_settable_routine_parameter() + { + return this; + } }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 1e483f32b02..eb1fbf4855d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2579,7 +2579,7 @@ String *Item_load_file::val_str(String *str) (void) fn_format(path, file_name->c_ptr(), mysql_real_data_home, "", MY_RELATIVE_PATH | MY_UNPACK_FILENAME); - if (!my_stat(path, &stat_info, MYF(MY_WME))) + if (!my_stat(path, &stat_info, MYF(0))) goto err; if (!(stat_info.st_mode & S_IROTH)) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 7d7b62df0dc..90d421a2c68 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -542,7 +542,7 @@ public: void fix_length_and_dec() { collation.set(default_charset()); - decimals=0; max_length=64; + max_length= 64; } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 6c2ff19825f..5280dbf6813 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -39,6 +39,7 @@ Item_subselect::Item_subselect(): engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1), engine_changed(0), changed(0) { + with_subselect= 1; reset(); /* item value is NULL if select_subselect not changed this value @@ -1354,6 +1355,17 @@ void Item_in_subselect::print(String *str) } +bool Item_in_subselect::fix_fields(THD *thd, Item **ref) +{ + bool result = 0; + + if(thd->lex->view_prepare_mode && left_expr && !left_expr->fixed) + result = left_expr->fix_fields(thd, &left_expr); + + return result || Item_subselect::fix_fields(thd, ref); +} + + Item_subselect::trans_res Item_allany_subselect::select_transformer(JOIN *join) { diff --git a/sql/item_subselect.h b/sql/item_subselect.h index a4dac5bda87..293408dc09e 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -258,6 +258,7 @@ public: void top_level_item() { abort_on_null=1; } bool test_limit(st_select_lex_unit *unit); void print(String *str); + bool fix_fields(THD *thd, Item **ref); friend class Item_ref_null_helper; friend class Item_is_not_null_test; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index f5d57d43350..9d98e446c84 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -870,9 +870,9 @@ String* Item_func_monthname::val_str(String* str) { DBUG_ASSERT(fixed == 1); const char *month_name; - uint month=(uint) Item_func_month::val_int(); + uint month= (uint) val_int(); - if (!month) // This is also true for NULL + if (null_value || !month) { null_value=1; return (String*) 0; @@ -1963,6 +1963,9 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) if (date_sub_interval) interval.neg = !interval.neg; + if (ltime->year < YY_MAGIC_BELOW) + return (null_value=1); + return (null_value= date_add_interval(ltime, int_type, interval)); } @@ -2021,8 +2024,13 @@ bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const Item_date_add_interval *other= (Item_date_add_interval*) item; if ((int_type != other->int_type) || - (!args[0]->eq(other->args[0], binary_cmp)) || - (get_interval_value(args[1], int_type, &val, &interval))) + (!args[0]->eq(other->args[0], binary_cmp))) + return FALSE; + + if (!args[1]->const_item() || !other->args[1]->const_item()) + return (args[1]->eq(other->args[1], binary_cmp)); + + if (get_interval_value(args[1], int_type, &val, &interval)) return FALSE; val= other->value; diff --git a/sql/lex.h b/sql/lex.h index 555a68dc388..67daf4566f8 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -133,6 +133,7 @@ static SYMBOL symbols[] = { { "CONSTRAINT", SYM(CONSTRAINT)}, { "CONTAINS", SYM(CONTAINS_SYM)}, { "CONTINUE", SYM(CONTINUE_SYM)}, + { "CONTRIBUTORS", SYM(CONTRIBUTORS_SYM)}, { "CONVERT", SYM(CONVERT_SYM)}, { "CREATE", SYM(CREATE)}, { "CROSS", SYM(CROSS)}, @@ -453,6 +454,7 @@ static SYMBOL symbols[] = { { "RTREE", SYM(RTREE_SYM)}, { "SAVEPOINT", SYM(SAVEPOINT_SYM)}, { "SCHEDULE", SYM(SCHEDULE_SYM)}, + { "SCHEDULER", SYM(SCHEDULER_SYM)}, { "SCHEMA", SYM(DATABASE)}, { "SCHEMAS", SYM(DATABASES)}, { "SECOND", SYM(SECOND_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index 7c8f314bc08..31133a71757 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -55,6 +55,14 @@ static int binlog_commit(THD *thd, bool all); static int binlog_rollback(THD *thd, bool all); static int binlog_prepare(THD *thd, bool all); +sql_print_message_func sql_print_message_handlers[3] = +{ + sql_print_information, + sql_print_warning, + sql_print_error +}; + + /* This is a POD. Please keep it that way! @@ -1090,12 +1098,16 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, Log_event *end_ev) were, we would have to ensure that we're not ending a statement inside a stored function. */ +#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(TRUE); +#endif error= mysql_bin_log.write(thd, trans_log, end_ev); } else { +#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_delete_pending_rows_event(); +#endif } /* @@ -2620,6 +2632,7 @@ bool MYSQL_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) } +#ifdef HAVE_ROW_BASED_REPLICATION /* These functions are placed in this file since they need access to binlog_hton, which has internal linkage. @@ -2797,6 +2810,7 @@ int MYSQL_LOG::flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) DBUG_RETURN(error); } +#endif /*HAVE_ROW_BASED_REPLICATION*/ /* Write an event to the binary log @@ -2831,7 +2845,9 @@ bool MYSQL_LOG::write(Log_event *event_info) */ bool const end_stmt= thd->prelocked_mode && thd->lex->requires_prelocking(); +#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(end_stmt); +#endif /*HAVE_ROW_BASED_REPLICATION*/ pthread_mutex_lock(&LOCK_log); @@ -2873,8 +2889,10 @@ bool MYSQL_LOG::write(Log_event *event_info) */ if (opt_using_transactions && thd) { +#ifdef HAVE_ROW_BASED_REPLICATION if (thd->binlog_setup_trx_data()) goto err; +#endif /*HAVE_ROW_BASED_REPLICATION*/ binlog_trx_data *const trx_data= (binlog_trx_data*) thd->ha_data[binlog_hton.slot]; diff --git a/sql/log_event.cc b/sql/log_event.cc index d51a0ef4c9f..ab9fa2975a1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5350,6 +5350,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) slave_print_msg(ERROR_LEVEL, rli, error, "Error in %s event: when locking tables", get_type_str()); + rli->clear_tables_to_lock(); DBUG_RETURN(error); } @@ -5385,6 +5386,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) "unexpected success or fatal error")); thd->query_error= 1; } + rli->clear_tables_to_lock(); DBUG_RETURN(error); } } @@ -5393,19 +5395,17 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) the table map and remove them from tables to lock. */ - TABLE_LIST *ptr= rli->tables_to_lock; - while (ptr) + TABLE_LIST *ptr; + for (ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) { rli->m_table_map.set_table(ptr->table_id, ptr->table); rli->touching_table(ptr->db, ptr->table_name, ptr->table_id); - char *to_free= reinterpret_cast<char*>(ptr); - ptr= ptr->next_global; - my_free(to_free, MYF(MY_WME)); } - rli->tables_to_lock= 0; - rli->tables_to_lock_count= 0; + rli->clear_tables_to_lock(); } + DBUG_ASSERT(rli->tables_to_lock == NULL && rli->tables_to_lock_count == 0); + TABLE* table= rli->m_table_map.get_table(m_table_id); if (table) @@ -5816,12 +5816,8 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) &tname_mem, NAME_LEN + 1, NULL); - /* - If memory is allocated, it the pointer to it should be stored in - table_list. If this is not true, the memory will not be correctly - free:ed later. - */ - DBUG_ASSERT(memory == NULL || memory == table_list); + if (memory == NULL) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); uint32 dummy_len; bzero(table_list, sizeof(*table_list)); @@ -5836,8 +5832,12 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) int error= 0; - if (rpl_filter->db_ok(table_list->db) && - (!rpl_filter->is_on() || rpl_filter->tables_ok("", table_list))) + if (!rpl_filter->db_ok(table_list->db) || + (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list))) + { + my_free((gptr) memory, MYF(MY_WME)); + } + else { /* Check if the slave is set to use SBR. If so, it should switch @@ -6416,12 +6416,17 @@ static int find_and_fetch_row(TABLE *table, byte *key) if (table->s->keys > 0) { int error; - /* - We need to set the null bytes to ensure that the filler bit - are all set when returning. There are storage engines that - just set the necessary bits on the bytes and don't set the - filler bits correctly. - */ + /* We have a key: search the table using the index */ + if (!table->file->inited) + if ((error= table->file->ha_index_init(0, FALSE))) + return error; + + /* + We need to set the null bytes to ensure that the filler bit are + all set when returning. There are storage engines that just set + the necessary bits on the bytes and don't set the filler bits + correctly. + */ my_ptrdiff_t const pos= table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; table->record[1][pos]= 0xFF; @@ -6430,6 +6435,7 @@ static int find_and_fetch_row(TABLE *table, byte *key) HA_READ_KEY_EXACT))) { table->file->print_error(error, MYF(0)); + table->file->ha_index_end(); DBUG_RETURN(error); } @@ -6448,7 +6454,10 @@ static int find_and_fetch_row(TABLE *table, byte *key) chose the row to change only using a PK or an UNNI. */ if (table->key_info->flags & HA_NOSAME) + { + table->file->ha_index_end(); DBUG_RETURN(0); + } while (record_compare(table)) { @@ -6465,15 +6474,26 @@ static int find_and_fetch_row(TABLE *table, byte *key) if ((error= table->file->index_next(table->record[1]))) { table->file->print_error(error, MYF(0)); + table->file->ha_index_end(); DBUG_RETURN(error); } } + + /* + Have to restart the scan to be able to fetch the next row. + */ + table->file->ha_index_end(); } else { - /* Continue until we find the right record or have made a full loop */ int restart_count= 0; // Number of times scanning has restarted from top - int error= 0; + int error; + + /* We don't have a key: search the table using rnd_next() */ + if ((error= table->file->ha_rnd_init(1))) + return error; + + /* Continue until we find the right record or have made a full loop */ do { /* @@ -6499,11 +6519,17 @@ static int find_and_fetch_row(TABLE *table, byte *key) default: table->file->print_error(error, MYF(0)); + table->file->ha_rnd_end(); DBUG_RETURN(error); } } while (restart_count < 2 && record_compare(table)); + /* + Have to restart the scan to be able to fetch the next row. + */ + table->file->ha_rnd_end(); + DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0); DBUG_RETURN(error); } @@ -6626,20 +6652,6 @@ int Delete_rows_log_event::do_exec_row(TABLE *table) { DBUG_ASSERT(table != NULL); - if (table->s->keys > 0) - { - /* We have a key: search the table using the index */ - if (!table->file->inited) - if (int error= table->file->ha_index_init(0, FALSE)) - return error; - } - else - { - /* We doesn't have a key: search the table using rnd_next() */ - if (int error= table->file->ha_rnd_init(1)) - return error; - } - int error= find_and_fetch_row(table, m_key); if (error) return error; @@ -6651,11 +6663,6 @@ int Delete_rows_log_event::do_exec_row(TABLE *table) */ error= table->file->ha_delete_row(table->record[0]); - /* - Have to restart the scan to be able to fetch the next row. - */ - table->file->ha_index_or_rnd_end(); - return error; } @@ -6736,17 +6743,6 @@ int Update_rows_log_event::do_before_row_operations(TABLE *table) if (!m_memory) return HA_ERR_OUT_OF_MEM; - if (table->s->keys > 0) - { - /* We have a key: search the table using the index */ - if (!table->file->inited) - error= table->file->ha_index_init(0, FALSE); - } - else - { - /* We doesn't have a key: search the table using rnd_next() */ - error= table->file->ha_rnd_init(1); - } table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; return error; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index d00addb9f6d..443066c5f0c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -79,7 +79,8 @@ char *sql_strmake_with_convert(const char *str, uint32 arg_length, CHARSET_INFO *from_cs, uint32 max_res_length, CHARSET_INFO *to_cs, uint32 *result_length); -void kill_one_thread(THD *thd, ulong id, bool only_kill_query); +uint kill_one_thread(THD *thd, ulong id, bool only_kill_query); +void sql_kill(THD *thd, ulong id, bool only_kill_query); bool net_request_file(NET* net, const char* fname); char* query_table_status(THD *thd,const char *db,const char *table_name); @@ -488,6 +489,7 @@ inline THD *_current_thd(void) my_bool thd_in_lock_tables(const THD *thd); my_bool thd_tablespace_op(const THD *thd); const char *thd_proc_info(THD *thd, const char *info); +void **thd_ha_data(const THD *thd, const struct handlerton *hton); /* External variables @@ -861,13 +863,6 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, List<create_field> &fields, List<Key> &keys, bool tmp_table, uint select_field_count); -TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, - TABLE_LIST *create_table, - List<create_field> *extra_fields, - List<Key> *keys, - List<Item> *items, - MYSQL_LOCK **lock, - TABLEOP_HOOKS *hooks); bool mysql_alter_table(THD *thd, char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, @@ -1005,6 +1000,7 @@ int mysql_find_files(THD *thd,List<char> *files, const char *db, const char *path, const char *wild, bool dir); bool mysqld_show_storage_engines(THD *thd); bool mysqld_show_authors(THD *thd); +bool mysqld_show_contributors(THD *thd); bool mysqld_show_privileges(THD *thd); bool mysqld_show_column_types(THD *thd); bool mysqld_help (THD *thd, const char *text); @@ -1386,10 +1382,13 @@ bool init_errmessage(void); #endif /* MYSQL_SERVER */ void sql_perror(const char *message); + int vprint_msg_to_log(enum loglevel level, const char *format, va_list args); void sql_print_error(const char *format, ...); void sql_print_warning(const char *format, ...); void sql_print_information(const char *format, ...); +typedef void (*sql_print_message_func)(const char *format, ...); +extern sql_print_message_func sql_print_message_handlers[]; /* type of the log table */ #define QUERY_LOG_SLOW 1 @@ -1506,6 +1505,7 @@ extern my_bool locked_in_memory; extern bool opt_using_transactions, mysqld_embedded; extern bool using_update_log, opt_large_files, server_id_supplied; extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log; +extern my_bool opt_log_queries_not_using_indexes; extern bool opt_disable_networking, opt_skip_show_db; extern my_bool opt_character_set_client_handshake; extern bool volatile abort_loop, shutdown_in_progress, grant_option; @@ -1650,10 +1650,11 @@ extern struct st_VioSSLFd * ssl_acceptor_fd; MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags, bool *need_reopen); -/* mysql_lock_tables() flags bits */ +/* 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_IGNORE_LOCKED_TABLES 0x0008 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); @@ -1943,7 +1944,6 @@ inline int hexchar_to_int(char c) return -1; } - /* Some functions that are different in the embedded library and the normal server diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 0c02971a2bc..2a8fe21f656 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -318,7 +318,6 @@ static bool volatile ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; static my_bool opt_bdb, opt_isam, opt_ndbcluster; static my_bool opt_short_log_format= 0; -static my_bool opt_log_queries_not_using_indexes= 0; static uint kill_cached_threads, wake_thread; static ulong killed_threads, thread_created; static ulong max_used_connections; @@ -344,6 +343,7 @@ static my_bool opt_sync_bdb_logs; /* Global variables */ bool opt_log, opt_update_log, opt_bin_log, opt_slow_log; +my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; my_bool opt_character_set_client_handshake= 1; @@ -693,6 +693,7 @@ my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif +#define SSL_VARS_NOT_STATIC #include "sslopt-vars.h" #ifdef HAVE_OPENSSL #include <openssl/crypto.h> @@ -863,8 +864,8 @@ static void close_connections(void) { DBUG_PRINT("quit",("Informing thread %ld that it's time to die", tmp->thread_id)); - /* We skip slave threads on this first loop through. */ - if (tmp->slave_thread) + /* We skip slave threads & scheduler on this first loop through. */ + if (tmp->slave_thread || tmp->system_thread == SYSTEM_THREAD_EVENT_SCHEDULER) continue; tmp->killed= THD::KILL_CONNECTION; @@ -883,6 +884,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list + Events::shutdown(); end_slave(); if (thread_count) @@ -1043,7 +1045,8 @@ static void __cdecl kill_server(int sig_ptr) RETURN_FROM_KILL_SERVER; kill_in_progress=TRUE; abort_loop=1; // This should be set - my_sigset(sig,SIG_IGN); + if (sig != 0) // 0 is not a valid signal number + my_sigset(sig,SIG_IGN); if (sig == MYSQL_KILL_SIGNAL || sig == 0) sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname); else @@ -1075,6 +1078,10 @@ static void __cdecl kill_server(int sig_ptr) pthread_join(select_thread, NULL); // wait for main thread #endif /* __NETWARE__ */ +#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2)) + my_thread_end(); +#endif + pthread_exit(0); /* purecov: deadcode */ #endif /* EMBEDDED_LIBRARY */ @@ -1293,6 +1300,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_sent); (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); + Events::destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); #ifndef HAVE_YASSL @@ -2848,6 +2856,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST); (void) pthread_cond_init(&COND_server_started,NULL); sp_cache_init(); + Events::init_mutexes(); /* Parameter for threads created for connections */ (void) pthread_attr_init(&connection_attrib); (void) pthread_attr_setdetachstate(&connection_attrib, @@ -2993,7 +3002,6 @@ static int init_server_components() #ifdef HAVE_REPLICATION init_slave_list(); #endif - init_events(); /* Setup logs */ @@ -3091,7 +3099,7 @@ with --log-bin instead."); } if (global_system_variables.binlog_format == BINLOG_FORMAT_UNSPEC) { -#ifdef HAVE_NDB_BINLOG +#if defined(HAVE_NDB_BINLOG) && defined(HAVE_ROW_BASED_REPLICATION) if (opt_bin_log && have_ndbcluster == SHOW_OPTION_YES) global_system_variables.binlog_format= BINLOG_FORMAT_ROW; else @@ -3617,6 +3625,10 @@ we force server id to 2, but this MySQL server will not act as a slave."); mysqld_server_started= 1; pthread_cond_signal(&COND_server_started); + if (!opt_noacl) + { + Events::init(); + } #if defined(__NT__) || defined(HAVE_SMEM) handle_connections_methods(); #else @@ -3668,7 +3680,6 @@ we force server id to 2, but this MySQL server will not act as a slave."); clean_up(1); wait_for_signal_thread_to_end(); clean_up_mutexes(); - shutdown_events(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(0); @@ -4659,7 +4670,7 @@ enum options_mysqld OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, OPT_SAFE_USER_CREATE, OPT_SQL_MODE, OPT_HAVE_NAMED_PIPE, - OPT_DO_PSTACK, OPT_EVENT_EXECUTOR, OPT_REPORT_HOST, + OPT_DO_PSTACK, OPT_EVENT_SCHEDULER, OPT_REPORT_HOST, OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT, OPT_SHOW_SLAVE_AUTH_INFO, OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE, @@ -4992,9 +5003,9 @@ Disable with --skip-bdb (will save memory).", (gptr*) &global_system_variables.engine_condition_pushdown, (gptr*) &global_system_variables.engine_condition_pushdown, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"event-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.", - (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG, - 0/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0}, + {"event-scheduler", OPT_EVENT_SCHEDULER, "Enable/disable the event scheduler.", + (gptr*) &Events::opt_event_scheduler, (gptr*) &Events::opt_event_scheduler, 0, GET_ULONG, + REQUIRED_ARG, 2/*default*/, 0/*min-value*/, 2/*max-value*/, 0, 0, 0}, {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.", @@ -7321,6 +7332,24 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; } #endif + case OPT_EVENT_SCHEDULER: + if (!argument) + Events::opt_event_scheduler= 2; + else + { + int type; + if ((type=find_type(argument, &Events::opt_typelib, 1)) <= 0) + { + fprintf(stderr,"Unknown option to event-scheduler: %s\n",argument); + exit(1); + } + /* + type= 1 2 3 4 5 6 + (OFF | 0) - (ON | 1) - (2 | SUSPEND) + */ + Events::opt_event_scheduler= (type-1) / 2; + } + break; case (int) OPT_SKIP_NEW: opt_specialflag|= SPECIAL_NO_NEW_FUNC; delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 9713e4bed44..03164827e8f 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -37,9 +37,6 @@ HFTODO this must be hidden if we don't want client capabilities in embedded library */ -#ifdef __WIN__ -#include <winsock.h> -#endif #include <my_global.h> #include <mysql.h> #include <mysql_embed.h> @@ -51,6 +48,12 @@ #include <violite.h> #include <signal.h> #include <errno.h> +#ifdef __WIN__ +#include <winsock.h> +#endif +#ifdef __NETWARE__ +#include <sys/select.h> +#endif #ifdef EMBEDDED_LIBRARY #undef MYSQL_SERVER diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 9ab1063d6b2..ffaf3fad6c8 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -450,6 +450,8 @@ public: /* TRUE if last checked tree->key can be used for ROR-scan */ bool is_ror_scan; + /* Number of ranges in the last checked tree->key */ + uint n_ranges; }; class TABLE_READ_PLAN; @@ -4696,17 +4698,46 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, if (inv) { - /* - We get here for conditions like "t.keypart NOT IN (....)". - - If the IN-list contains only constants (and func->array is an ordered - array of them), we construct the appropriate SEL_ARG tree manually, - because constructing it using the range analyzer (as - AND_i( t.keypart != c_i)) will cause lots of memory to be consumed - (see BUG#15872). - */ if (func->array && func->cmp_type != ROW_RESULT) { + /* + We get here for conditions in form "t.key NOT IN (c1, c2, ...)" + (where c{i} are constants). + Our goal is to produce a SEL_ARG graph that represents intervals: + + ($MIN<t.key<c1) OR (c1<t.key<c2) OR (c2<t.key<c3) OR ... (*) + + where $MIN is either "-inf" or NULL. + + The most straightforward way to handle NOT IN would be to convert + it to "(t.key != c1) AND (t.key != c2) AND ..." and let the range + optimizer to build SEL_ARG graph from that. However that will cause + the range optimizer to use O(N^2) memory (it's a bug, not filed), + and people do use big NOT IN lists (see BUG#15872). Also, for big + NOT IN lists constructing/using graph (*) does not make the query + faster. + + So, we will handle NOT IN manually in the following way: + * if the number of entries in the NOT IN list is less then + NOT_IN_IGNORE_THRESHOLD, we will construct SEL_ARG graph (*) + manually. + * Otherwise, we will construct a smaller graph: for + "t.key NOT IN (c1,...cN)" we construct a graph representing + ($MIN < t.key) OR (cN < t.key) // here sequence of c_i is + // ordered. + + A note about partially-covering indexes: for those (e.g. for + "a CHAR(10), KEY(a(5))") the handling is correct (albeit not very + efficient): + Instead of "t.key < c1" we get "t.key <= prefix-val(c1)". + Combining the intervals in (*) together, we get: + (-inf<=t.key<=c1) OR (c1<=t.key<=c2) OR (c2<=t.key<=c3) OR ... + i.e. actually we get intervals combined into one interval: + (-inf<=t.key<=+inf). This doesn't make much sense but it doesn't + cause any problems. + */ + MEM_ROOT *tmp_root= param->mem_root; + param->thd->mem_root= param->old_root; /* Create one Item_type constant object. We'll need it as get_mm_parts only accepts constant values wrapped in Item_Type @@ -4715,25 +4746,35 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, per-statement mem_root (while thd->mem_root is currently pointing to mem_root local to range optimizer). */ - MEM_ROOT *tmp_root= param->mem_root; - param->thd->mem_root= param->old_root; Item *value_item= func->array->create_item(); param->thd->mem_root= tmp_root; if (!value_item) break; - /* Get a SEL_TREE for "-inf < X < c_0" interval */ - func->array->value_to_item(0, value_item); - tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, - value_item, cmp_type); - if (!tree) + /* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */ + uint i=0; + do + { + func->array->value_to_item(i, value_item); + tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, + value_item, cmp_type); + if (!tree) + break; + i++; + } while (i < func->array->count && tree->type == SEL_TREE::IMPOSSIBLE); + + if (!tree || tree->type == SEL_TREE::IMPOSSIBLE) + { + /* We get here in cases like "t.unsigned NOT IN (-1,-2,-3) */ + tree= NULL; break; + } #define NOT_IN_IGNORE_THRESHOLD 1000 SEL_TREE *tree2; if (func->array->count < NOT_IN_IGNORE_THRESHOLD) { - for (uint i=1; i < func->array->count; i++) + for (; i < func->array->count; i++) { if (func->array->compare_elems(i, i-1)) { @@ -4741,32 +4782,44 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, func->array->value_to_item(i, value_item); tree2= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, value_item, cmp_type); - + if (!tree2) + { + tree= NULL; + break; + } + /* Change all intervals to be "c_{i-1} < X < c_i" */ for (uint idx= 0; idx < param->keys; idx++) { - SEL_ARG *new_interval; - if ((new_interval= tree2->keys[idx])) + SEL_ARG *new_interval, *last_val; + if (((new_interval= tree2->keys[idx])) && + ((last_val= tree->keys[idx]->last()))) { - SEL_ARG *last_val= tree->keys[idx]->last(); new_interval->min_value= last_val->max_value; new_interval->min_flag= NEAR_MIN; } } + /* + The following doesn't try to allocate memory so no need to + check for NULL. + */ tree= tree_or(param, tree, tree2); } } } else func->array->value_to_item(func->array->count - 1, value_item); - - /* - Get the SEL_TREE for the last "c_last < X < +inf" interval - (value_item cotains c_last already) - */ - tree2= get_mm_parts(param, cond_func, field, Item_func::GT_FUNC, - value_item, cmp_type); - tree= tree_or(param, tree, tree2); + + if (tree && tree->type != SEL_TREE::IMPOSSIBLE) + { + /* + Get the SEL_TREE for the last "c_last < X < +inf" interval + (value_item cotains c_last already) + */ + tree2= get_mm_parts(param, cond_func, field, Item_func::GT_FUNC, + value_item, cmp_type); + tree= tree_or(param, tree, tree2); + } } else { @@ -6582,6 +6635,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) param->table->file->primary_key_is_clustered()); param->is_ror_scan= !cpk_scan; } + param->n_ranges= 0; records=check_quick_keys(param,idx,tree,param->min_key,0,param->max_key,0); if (records != HA_POS_ERROR) @@ -6589,7 +6643,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) param->table->quick_keys.set_bit(key); param->table->quick_rows[key]=records; param->table->quick_key_parts[key]=param->max_key_part+1; - + param->table->quick_n_ranges[key]= param->n_ranges; if (cpk_scan) param->is_ror_scan= TRUE; } @@ -6725,7 +6779,10 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, HA_NOSAME && min_key_length == max_key_length && !memcmp(param->min_key,param->max_key,min_key_length)) + { tmp=1; // Max one record + param->n_ranges++; + } else { if (param->is_ror_scan) @@ -6745,6 +6802,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, is_key_scan_ror(param, keynr, key_tree->part + 1))) param->is_ror_scan= FALSE; } + param->n_ranges++; if (tmp_min_flag & GEOM_FLAG) { diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 6761b28331e..0924a8adf6e 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -432,18 +432,22 @@ char *partition_info::has_unique_names() bool partition_info::check_engine_mix(handlerton **engine_array, uint no_parts) { uint i= 0; - bool result= FALSE; DBUG_ENTER("partition_info::check_engine_mix"); do { if (engine_array[i] != engine_array[0]) { - result= TRUE; - break; + my_error(ER_MIX_HANDLER_ERROR, MYF(0)); + DBUG_RETURN(TRUE); } } while (++i < no_parts); - DBUG_RETURN(result); + if (engine_array[0] == &myisammrg_hton) + { + my_error(ER_PARTITION_MERGE_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); } @@ -757,10 +761,7 @@ bool partition_info::check_partition_info(handlerton **eng_type, } while (++i < no_parts); } if (unlikely(partition_info::check_engine_mix(engine_array, part_count))) - { - my_error(ER_MIX_HANDLER_ERROR, MYF(0)); goto end; - } if (eng_type) *eng_type= (handlerton*)engine_array[0]; diff --git a/sql/partition_info.h b/sql/partition_info.h index 3a1e6be4050..af43f7b7933 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -184,6 +184,7 @@ public: bool list_of_subpart_fields; bool linear_hash_ind; bool fixed; + bool is_auto_partitioned; bool from_openfrm; bool has_null_value; uint has_null_part_id; @@ -219,6 +220,7 @@ public: list_of_part_fields(FALSE), list_of_subpart_fields(FALSE), linear_hash_ind(FALSE), fixed(FALSE), + is_auto_partitioned(FALSE), from_openfrm(FALSE), has_null_value(FALSE), has_null_part_id(0) diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 9cabe1a3df0..66e2aa1c31c 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -61,12 +61,13 @@ static Slave_log_event* find_slave_event(IO_CACHE* log, static int init_failsafe_rpl_thread(THD* thd) { DBUG_ENTER("init_failsafe_rpl_thread"); + thd->system_thread = SYSTEM_THREAD_DELAYED_INSERT; /* thd->bootstrap is to report errors barely to stderr; if this code is enable again one day, one should check if bootstrap is still needed (maybe this thread has no other error reporting method). */ - thd->system_thread = thd->bootstrap = 1; + thd->bootstrap = 1; thd->security_ctx->skip_grants(); my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index cacae1aa4c2..99606353080 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -309,15 +309,14 @@ typedef struct st_relay_log_info void cleanup_context(THD *, bool); void clear_tables_to_lock() { - TABLE_LIST *ptr= tables_to_lock; - while (ptr) + while (tables_to_lock) { - char *to_free= reinterpret_cast<char*>(ptr); - ptr= ptr->next_global; + char *to_free= reinterpret_cast<gptr>(tables_to_lock); + tables_to_lock= tables_to_lock->next_global; + tables_to_lock_count--; my_free(to_free, MYF(MY_WME)); } - tables_to_lock= 0; - tables_to_lock_count= 0; + DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); } time_t unsafe_to_stop_at; diff --git a/sql/set_var.cc b/sql/set_var.cc index 57eeeed46e1..242fe0f6068 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -56,6 +56,8 @@ #include <thr_alarm.h> #include <myisam.h> +#include "event_scheduler.h" + /* WITH_BERKELEY_STORAGE_ENGINE */ extern bool berkeley_shared_data; extern ulong berkeley_max_lock, berkeley_log_buffer_size; @@ -106,7 +108,6 @@ extern ulong ndb_report_thresh_binlog_mem_usage; -extern my_bool event_executor_running_global_var; static HASH system_variable_hash; const char *bool_type_names[]= { "OFF", "ON", NullS }; @@ -162,6 +163,7 @@ void fix_sql_mode_var(THD *thd, enum_var_type type); static byte *get_error_count(THD *thd); static byte *get_warning_count(THD *thd); static byte *get_prepared_stmt_count(THD *thd); +static byte *get_tmpdir(THD *thd); /* Variable definition list @@ -184,6 +186,7 @@ sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset", sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges", &sp_automatic_privileges); +sys_var_const_str sys_basedir("basedir", mysql_home); sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", &binlog_cache_size); sys_var_thd_binlog_format sys_binlog_format("binlog_format", @@ -209,6 +212,7 @@ sys_var_long_ptr sys_concurrent_insert("concurrent_insert", &myisam_concurrent_insert); sys_var_long_ptr sys_connect_timeout("connect_timeout", &connect_timeout); +sys_var_const_str sys_datadir("datadir", mysql_real_data_home); #ifndef DBUG_OFF sys_var_thd_dbug sys_dbug("debug"); #endif @@ -222,9 +226,8 @@ sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout", &delayed_insert_timeout); sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size", &delayed_queue_size); -sys_var_event_executor sys_event_executor("event_scheduler", - (my_bool *) - &event_executor_running_global_var); + +sys_var_event_scheduler sys_event_scheduler("event_scheduler"); sys_var_long_ptr sys_expire_logs_days("expire_logs_days", &expire_logs_days); sys_var_bool_ptr sys_flush("flush", &myisam_flush); @@ -262,6 +265,9 @@ sys_trust_routine_creators("log_bin_trust_routine_creators", sys_var_bool_ptr sys_trust_function_creators("log_bin_trust_function_creators", &trust_function_creators); +sys_var_bool_ptr + sys_log_queries_not_using_indexes("log_queries_not_using_indexes", + &opt_log_queries_not_using_indexes); sys_var_thd_ulong sys_log_warnings("log_warnings", &SV::log_warnings); sys_var_thd_ulong sys_long_query_time("long_query_time", &SV::long_query_time); @@ -389,6 +395,7 @@ sys_var_thd_ulong sys_query_alloc_block_size("query_alloc_block_size", sys_var_thd_ulong sys_query_prealloc_size("query_prealloc_size", &SV::query_prealloc_size, 0, fix_thd_mem_root); +sys_var_readonly sys_tmpdir("tmpdir", OPT_GLOBAL, SHOW_CHAR, get_tmpdir); sys_var_thd_ulong sys_trans_alloc_block_size("transaction_alloc_block_size", &SV::trans_alloc_block_size, 0, fix_trans_mem_root); @@ -425,6 +432,21 @@ sys_var_thd_ulong sys_sort_buffer("sort_buffer_size", &SV::sortbuff_size); sys_var_thd_sql_mode sys_sql_mode("sql_mode", &SV::sql_mode); +#ifdef HAVE_OPENSSL +extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, + *opt_ssl_key; +sys_var_const_str_ptr sys_ssl_ca("ssl_ca", &opt_ssl_ca); +sys_var_const_str_ptr sys_ssl_capath("ssl_capath", &opt_ssl_capath); +sys_var_const_str_ptr sys_ssl_cert("ssl_cert", &opt_ssl_cert); +sys_var_const_str_ptr sys_ssl_cipher("ssl_cipher", &opt_ssl_cipher); +sys_var_const_str_ptr sys_ssl_key("ssl_key", &opt_ssl_key); +#else +sys_var_const_str sys_ssl_ca("ssl_ca", NULL); +sys_var_const_str sys_ssl_capath("ssl_capath", NULL); +sys_var_const_str sys_ssl_cert("ssl_cert", NULL); +sys_var_const_str sys_ssl_cipher("ssl_cipher", NULL); +sys_var_const_str sys_ssl_key("ssl_key", NULL); +#endif sys_var_thd_enum sys_updatable_views_with_limit("updatable_views_with_limit", &SV::updatable_views_with_limit, @@ -696,7 +718,6 @@ static int show_slave_skip_errors(THD *thd, SHOW_VAR *var, char *buff) } #endif /* HAVE_REPLICATION */ - /* Variables shown by SHOW variables in alphabetical order */ @@ -706,7 +727,7 @@ SHOW_VAR init_vars[]= { {"auto_increment_offset", (char*) &sys_auto_increment_offset, SHOW_SYS}, {sys_automatic_sp_privileges.name,(char*) &sys_automatic_sp_privileges, SHOW_SYS}, {"back_log", (char*) &back_log, SHOW_LONG}, - {"basedir", mysql_home, SHOW_CHAR}, + {sys_basedir.name, (char*) &sys_basedir, SHOW_SYS}, {"bdb_cache_parts", (char*) &berkeley_cache_parts, SHOW_LONG}, {"bdb_cache_size", (char*) &berkeley_cache_size, SHOW_LONGLONG}, {"bdb_home", (char*) &berkeley_home, SHOW_CHAR_PTR}, @@ -733,7 +754,7 @@ SHOW_VAR init_vars[]= { {sys_completion_type.name, (char*) &sys_completion_type, SHOW_SYS}, {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, - {"datadir", mysql_real_data_home, SHOW_CHAR}, + {sys_datadir.name, (char*) &sys_datadir, SHOW_SYS}, {sys_date_format.name, (char*) &sys_date_format, SHOW_SYS}, {sys_datetime_format.name, (char*) &sys_datetime_format, SHOW_SYS}, #ifndef DBUG_OFF @@ -747,7 +768,7 @@ SHOW_VAR init_vars[]= { {sys_div_precincrement.name,(char*) &sys_div_precincrement,SHOW_SYS}, {sys_engine_condition_pushdown.name, (char*) &sys_engine_condition_pushdown, SHOW_SYS}, - {sys_event_executor.name, (char*) &sys_event_executor, SHOW_SYS}, + {sys_event_scheduler.name, (char*) &sys_event_scheduler, SHOW_SYS}, {sys_expire_logs_days.name, (char*) &sys_expire_logs_days, SHOW_SYS}, {sys_flush.name, (char*) &sys_flush, SHOW_SYS}, {sys_flush_time.name, (char*) &sys_flush_time, SHOW_SYS}, @@ -833,6 +854,8 @@ SHOW_VAR init_vars[]= { {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, {sys_trust_function_creators.name,(char*) &sys_trust_function_creators, SHOW_SYS}, {"log_error", (char*) log_error_file, SHOW_CHAR}, + {sys_log_queries_not_using_indexes.name, + (char*) &sys_log_queries_not_using_indexes, SHOW_SYS}, #ifdef HAVE_REPLICATION {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_MY_BOOL}, #endif @@ -962,6 +985,11 @@ SHOW_VAR init_vars[]= { {sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS}, {"sql_notes", (char*) &sys_sql_notes, SHOW_SYS}, {"sql_warnings", (char*) &sys_sql_warnings, SHOW_SYS}, + {sys_ssl_ca.name, (char*) &sys_ssl_ca, SHOW_SYS}, + {sys_ssl_capath.name, (char*) &sys_ssl_capath, SHOW_SYS}, + {sys_ssl_cert.name, (char*) &sys_ssl_cert, SHOW_SYS}, + {sys_ssl_cipher.name, (char*) &sys_ssl_cipher, SHOW_SYS}, + {sys_ssl_key.name, (char*) &sys_ssl_key, SHOW_SYS}, {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS}, #ifdef HAVE_REPLICATION {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS}, @@ -983,7 +1011,7 @@ SHOW_VAR init_vars[]= { {"time_zone", (char*) &sys_time_zone, SHOW_SYS}, {sys_timed_mutexes.name, (char*) &sys_timed_mutexes, SHOW_SYS}, {sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS}, - {"tmpdir", (char*) &opt_mysql_tmpdir, SHOW_CHAR_PTR}, + {sys_tmpdir.name, (char*) &sys_tmpdir, SHOW_SYS}, {sys_trans_alloc_block_size.name, (char*) &sys_trans_alloc_block_size, SHOW_SYS}, {sys_trans_prealloc_size.name, (char*) &sys_trans_prealloc_size, SHOW_SYS}, @@ -1270,12 +1298,14 @@ bool sys_var_thd_binlog_format::is_readonly() const And this test will also prevent switching from RBR to RBR (a no-op which should not happen too often). */ +#ifdef HAVE_ROW_BASED_REPLICATION 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; } +#endif /*HAVE_ROW_BASED_REPLICATION*/ /* if in a stored function, it's too late to change mode */ @@ -1299,7 +1329,9 @@ bool sys_var_thd_binlog_format::is_readonly() const void fix_binlog_format_after_update(THD *thd, enum_var_type type) { +#ifdef HAVE_ROW_BASED_REPLICATION thd->reset_current_stmt_binlog_row_based(); +#endif /*HAVE_ROW_BASED_REPLICATION*/ } static void fix_max_binlog_size(THD *thd, enum_var_type type) @@ -2851,6 +2883,31 @@ static byte *get_prepared_stmt_count(THD *thd) return (byte*) &thd->sys_var_tmp.ulong_value; } + +/* + Get the tmpdir that was specified or chosen by default + + SYNOPSIS + get_tmpdir() + thd thread handle + + DESCRIPTION + This is necessary because if the user does not specify a temporary + directory via the command line, one is chosen based on the environment + or system defaults. But we can't just always use mysql_tmpdir, because + that is actually a call to my_tmpdir() which cycles among possible + temporary directories. + + RETURN VALUES + ptr pointer to NUL-terminated string + */ +static byte *get_tmpdir(THD *thd) +{ + if (opt_mysql_tmpdir) + return (byte *)opt_mysql_tmpdir; + return (byte*)mysql_tmpdir; +} + /**************************************************************************** Main handling of variables: - Initialisation @@ -3575,6 +3632,69 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) return (byte*) thd->strdup(buf); } + +/* + The update method of the global variable event_scheduler. + If event_scheduler is switched from 0 to 1 then the scheduler main + thread is resumed and if from 1 to 0 the scheduler thread is suspended + + SYNOPSIS + sys_var_event_scheduler::update() + thd Thread context (unused) + var The new value + + Returns + FALSE OK + TRUE Error +*/ + +bool +sys_var_event_scheduler::update(THD *thd, set_var *var) +{ + enum Event_scheduler::enum_error_code res; + Event_scheduler *scheduler= Event_scheduler::get_instance(); + /* here start the thread if not running. */ + DBUG_ENTER("sys_var_event_scheduler::update"); + + DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value)); + if (!scheduler->initialized()) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=0"); + DBUG_RETURN(true); + } + + if (var->save_result.ulonglong_value < 1 || + var->save_result.ulonglong_value > 2) + { + char buf[64]; + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "event_scheduler", + llstr(var->save_result.ulonglong_value, buf)); + DBUG_RETURN(true); + } + if ((res= scheduler->suspend_or_resume(var->save_result.ulonglong_value == 1? + Event_scheduler::RESUME : + Event_scheduler::SUSPEND))) + my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), (uint) res); + DBUG_RETURN((bool) res); +} + + +byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + Event_scheduler *scheduler= Event_scheduler::get_instance(); + + if (!scheduler->initialized()) + thd->sys_var_tmp.long_value= 0; + else if (scheduler->get_state() == Event_scheduler::RUNNING) + thd->sys_var_tmp.long_value= 1; + else + thd->sys_var_tmp.long_value= 2; + + return (byte*) &thd->sys_var_tmp; +} + + /**************************************************************************** Used templates ****************************************************************************/ diff --git a/sql/set_var.h b/sql/set_var.h index 8076f10bb0a..1049b154d47 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -231,6 +231,35 @@ public: }; +class sys_var_const_str_ptr :public sys_var +{ +public: + char **value; // Pointer to const value + sys_var_const_str_ptr(const char *name_arg, char **value_arg) + :sys_var(name_arg),value(value_arg) + {} + bool check(THD *thd, set_var *var) + { + return 1; + } + bool update(THD *thd, set_var *var) + { + return 1; + } + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) + { + return (byte*) *value; + } + bool check_update_type(Item_result type) + { + return 1; + } + bool check_default(enum_var_type type) { return 1; } + bool is_readonly() const { return 1; } +}; + + class sys_var_enum :public sys_var { uint *value; @@ -842,13 +871,14 @@ public: }; -class sys_var_event_executor :public sys_var_bool_ptr +class sys_var_event_scheduler :public sys_var_long_ptr { /* We need a derived class only to have a warn_deprecated() */ public: - sys_var_event_executor(const char *name_arg, my_bool *value_arg) : - sys_var_bool_ptr(name_arg, value_arg) {}; + sys_var_event_scheduler(const char *name_arg) : + sys_var_long_ptr(name_arg, NULL, NULL) {}; bool update(THD *thd, set_var *var); + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; extern void fix_binlog_format_after_update(THD *thd, enum_var_type type); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index a2bc77714bb..5aab951b2ca 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5042,7 +5042,7 @@ ER_OPTION_PREVENTS_STATEMENT ger "Der MySQL-Server läuft mit der Option %s und kann diese Anweisung deswegen nicht ausführen" por "O servidor MySQL está rodando com a opção %s razão pela qual não pode executar esse commando" spa "El servidor MySQL está rodando con la opción %s tal que no puede ejecutar este comando" - swe "MySQL är startad med --skip-grant-tables. Pga av detta kan du inte använda detta kommando" + swe "MySQL är startad med %s. Pga av detta kan du inte använda detta kommando" ER_DUPLICATED_VALUE_IN_TYPE eng "Column '%-.100s' has duplicated value '%-.64s' in %s" ger "Feld '%-.100s' hat doppelten Wert '%-.64s' in %s" @@ -5472,7 +5472,7 @@ ER_SP_DUP_HANDLER 42000 eng "Duplicate handler declared in the same block" ger "Doppelter Handler im selben Block deklariert" ER_SP_NOT_VAR_ARG 42000 - eng "OUT or INOUT argument %d for routine %s is not a variable" + eng "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger" ger "OUT- oder INOUT-Argument %d für Routine %s ist keine Variable" ER_SP_NO_RETSET 0A000 eng "Not allowed to return a result set from a %s" @@ -5842,3 +5842,12 @@ ER_WRONG_PARTITION_NAME swe "Felaktigt partitionsnamn" ER_CANT_CHANGE_TX_ISOLATION 25001 eng "Transaction isolation level can't be changed while a transaction is in progress" +ER_DUP_ENTRY_AUTOINCREMENT_CASE + eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.64s' for key '%-.64s'" +ER_EVENT_MODIFY_QUEUE_ERROR + eng "Internal scheduler error %d" +ER_EVENT_SET_VAR_ERROR + eng "Error during starting/stopping of the scheduler. Error code %u" +ER_PARTITION_MERGE_ERROR + eng "MyISAM Merge handler cannot be used in partitioned tables" + swe "MyISAM Merge kan inte anändas i en partitionerad tabell" diff --git a/sql/sp.cc b/sql/sp.cc index 6f074fd7dce..0e81e627f71 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -408,15 +408,13 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; - + char definer_user_name_holder[USERNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, - USERNAME_LENGTH); + LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH }; char definer_host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, - HOSTNAME_LENGTH); - + LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH }; + int ret; thd->variables.sql_mode= sql_mode; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8a64799e5f9..172cfbff532 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -202,6 +202,7 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_PROC_CODE: case SQLCOM_SHOW_FUNC_CODE: case SQLCOM_SHOW_AUTHORS: + case SQLCOM_SHOW_CONTRIBUTORS: case SQLCOM_REPAIR: case SQLCOM_BACKUP_TABLE: case SQLCOM_RESTORE_TABLE: @@ -315,14 +316,16 @@ sp_prepare_func_item(THD* thd, Item **it_addr) */ bool -sp_eval_expr(THD *thd, Field *result_field, Item *expr_item) +sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) { + Item *expr_item; + DBUG_ENTER("sp_eval_expr"); - if (!expr_item) + if (!*expr_item_ptr) DBUG_RETURN(TRUE); - if (!(expr_item= sp_prepare_func_item(thd, &expr_item))) + if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr))) DBUG_RETURN(TRUE); bool err_status= FALSE; @@ -951,6 +954,7 @@ sp_head::execute(THD *thd) bool err_status= FALSE; uint ip= 0; ulong save_sql_mode; + bool save_abort_on_warning; Query_arena *old_arena; /* per-instruction arena */ MEM_ROOT execute_mem_root; @@ -1011,6 +1015,10 @@ sp_head::execute(THD *thd) thd->derived_tables= 0; save_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; + save_abort_on_warning= thd->abort_on_warning; + thd->abort_on_warning= + (m_sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); + /* It is also more efficient to save/restore current thd->lex once when do it in each instruction @@ -1143,6 +1151,7 @@ sp_head::execute(THD *thd) DBUG_ASSERT(!thd->derived_tables); thd->derived_tables= old_derived_tables; thd->variables.sql_mode= save_sql_mode; + thd->abort_on_warning= save_abort_on_warning; thd->stmt_arena= old_arena; state= EXECUTED; @@ -1219,18 +1228,22 @@ bool sp_head::execute_function(THD *thd, Item **argp, uint argcount, Field *return_value_fld) { - Item_cache **param_values; ulonglong binlog_save_options; bool need_binlog_call; - uint params; + uint arg_no; sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; + char buf[STRING_BUFFER_USUAL_SIZE]; + String binlog_buf(buf, sizeof(buf), &my_charset_bin); bool err_status= FALSE; + MEM_ROOT call_mem_root; + Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP); + Query_arena backup_arena; + DBUG_ENTER("sp_head::execute_function"); DBUG_PRINT("info", ("function %s", m_name.str)); LINT_INIT(binlog_save_options); - params= m_pcont->context_var_count(); /* Check that the function is called with all specified arguments. @@ -1238,74 +1251,97 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, If it is not, use my_error() to report an error, or it will not terminate the invoking query properly. */ - - if (argcount != params) + if (argcount != m_pcont->context_var_count()) { /* Need to use my_error here, or it will not terminate the invoking query properly. */ my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), - "FUNCTION", m_qname.str, params, argcount); + "FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount); DBUG_RETURN(TRUE); } - - /* Allocate param_values to be used for dumping the call into binlog. */ - - if (!(param_values= (Item_cache**)thd->alloc(sizeof(Item_cache*)*argcount))) - DBUG_RETURN(TRUE); - - // QQ Should have some error checking here? (types, etc...) + /* + Prepare arena and memroot for objects which lifetime is whole + duration of function call (sp_rcontext, it's tables and items, + sp_cursor and Item_cache holders for case expressions). + We can't use caller's arena/memroot for those objects because + in this case some fixed amount of memory will be consumed for + each function/trigger invocation and so statements which involve + lot of them will hog memory. + TODO: we should create sp_rcontext once per command and reuse + it on subsequent executions of a function/trigger. + */ + init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0); + thd->set_n_backup_active_arena(&call_arena, &backup_arena); if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) || nctx->init(thd)) { - delete nctx; /* Delete nctx if it was init() that failed. */ - DBUG_RETURN(TRUE); + thd->restore_active_arena(&call_arena, &backup_arena); + err_status= TRUE; + goto err_with_cleanup; } + /* + We have to switch temporarily back to callers arena/memroot. + Function arguments belong to the caller and so the may reference + memory which they will allocate during calculation long after + this function call will be finished (e.g. in Item::cleanup()). + */ + thd->restore_active_arena(&call_arena, &backup_arena); + #ifndef DBUG_OFF nctx->sp= this; #endif /* Pass arguments. */ - + for (arg_no= 0; arg_no < argcount; arg_no++) { - uint i; - - for (i= 0 ; i < argcount ; i++) - { - if (!argp[i]->fixed && argp[i]->fix_fields(thd, &argp[i])) - { - err_status= TRUE; - break; - } - - param_values[i]= Item_cache::get_cache(argp[i]->result_type()); - param_values[i]->store(argp[i]); - - if (nctx->set_variable(thd, i, param_values[i])) - { - err_status= TRUE; - break; - } - } - } + /* Arguments must be fixed in Item_func_sp::fix_fields */ + DBUG_ASSERT(argp[arg_no]->fixed); - if (err_status) - { - delete nctx; - DBUG_RETURN(TRUE); + if ((err_status= nctx->set_variable(thd, arg_no, &(argp[arg_no])))) + goto err_with_cleanup; } - thd->spcont= nctx; - /* If row-based binlogging, we don't need to binlog the function's call, let each substatement be binlogged its way. */ need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG) && !thd->current_stmt_binlog_row_based; + + /* + Remember the original arguments for unrolled replication of functions + before they are changed by execution. + */ + if (need_binlog_call) + { + binlog_buf.length(0); + binlog_buf.append(STRING_WITH_LEN("SELECT ")); + append_identifier(thd, &binlog_buf, m_name.str, m_name.length); + binlog_buf.append('('); + for (arg_no= 0; arg_no < argcount; arg_no++) + { + String str_value_holder; + String *str_value; + + if (arg_no) + binlog_buf.append(','); + + str_value= sp_get_item_value(nctx->get_item(arg_no), + &str_value_holder); + + if (str_value) + binlog_buf.append(*str_value); + else + binlog_buf.append(STRING_WITH_LEN("NULL")); + } + binlog_buf.append(')'); + } + thd->spcont= nctx; + if (need_binlog_call) { reset_dynamic(&thd->user_var_events); @@ -1314,38 +1350,27 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, thd->options&= ~OPTION_BIN_LOG; } + /* + Switch to call arena/mem_root so objects like sp_cursor or + Item_cache holders for case expressions can be allocated on it. + + TODO: In future we should associate call arena/mem_root with + sp_rcontext and allocate all these objects (and sp_rcontext + itself) on it directly rather than juggle with arenas. + */ + thd->set_n_backup_active_arena(&call_arena, &backup_arena); + err_status= execute(thd); + thd->restore_active_arena(&call_arena, &backup_arena); + if (need_binlog_call) { mysql_bin_log.stop_union_events(thd); thd->options= binlog_save_options; if (thd->binlog_evt_union.unioned_events) { - char buf[256]; - String bufstr(buf, sizeof(buf), &my_charset_bin); - bufstr.length(0); - bufstr.append(STRING_WITH_LEN("SELECT ")); - append_identifier(thd, &bufstr, m_name.str, m_name.length); - bufstr.append('('); - for (uint i=0; i < argcount; i++) - { - String str_value_holder; - String *str_value; - - if (i) - bufstr.append(','); - - str_value= sp_get_item_value(param_values[i], &str_value_holder); - - if (str_value) - bufstr.append(*str_value); - else - bufstr.append(STRING_WITH_LEN("NULL")); - } - bufstr.append(')'); - - Query_log_event qinfo(thd, bufstr.ptr(), bufstr.length(), + Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(), thd->binlog_evt_union.unioned_events_trans, FALSE); if (mysql_bin_log.write(&qinfo) && thd->binlog_evt_union.unioned_events_trans) @@ -1369,27 +1394,19 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } } + nctx->pop_all_cursors(); // To avoid memory leaks after an error + +err_with_cleanup: delete nctx; + call_arena.free_items(); + free_root(&call_mem_root, MYF(0)); thd->spcont= octx; DBUG_RETURN(err_status); } -static Item_func_get_user_var *item_is_user_var(Item *it) -{ - if (it->type() == Item::FUNC_ITEM) - { - Item_func *fi= static_cast<Item_func*>(it); - - if (fi->functype() == Item_func::GUSERVAR_FUNC) - return static_cast<Item_func_get_user_var*>(fi); - } - return NULL; -} - - /* Execute a procedure. SYNOPSIS @@ -1467,22 +1484,28 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) for (uint i= 0 ; i < params ; i++) { Item *arg_item= it_args++; - sp_variable_t *spvar= m_pcont->find_variable(i); if (!arg_item) break; + sp_variable_t *spvar= m_pcont->find_variable(i); + if (!spvar) continue; if (spvar->mode != sp_param_in) { - if (!arg_item->is_splocal() && !item_is_user_var(arg_item)) + Settable_routine_parameter *srp= + arg_item->get_settable_routine_parameter(); + + if (!srp) { my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str); err_status= TRUE; break; } + + srp->set_required_privilege(spvar->mode == sp_param_inout); } if (spvar->mode == sp_param_out) @@ -1490,7 +1513,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) Item_null *null_item= new Item_null(); if (!null_item || - nctx->set_variable(thd, i, null_item)) + nctx->set_variable(thd, i, (struct Item **)&null_item)) { err_status= TRUE; break; @@ -1498,7 +1521,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } else { - if (nctx->set_variable(thd, i, *it_args.ref())) + if (nctx->set_variable(thd, i, it_args.ref())) { err_status= TRUE; break; @@ -1566,36 +1589,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (spvar->mode == sp_param_in) continue; - if (arg_item->is_splocal()) - { - if (octx->set_variable(thd, - ((Item_splocal*) arg_item)->get_var_idx(), - nctx->get_item(i))) - { - err_status= TRUE; - break; - } - } - else + Settable_routine_parameter *srp= + arg_item->get_settable_routine_parameter(); + + DBUG_ASSERT(srp); + + if (srp->set_value(thd, octx, nctx->get_item_addr(i))) { - Item_func_get_user_var *guv= item_is_user_var(arg_item); - - if (guv) - { - Item *item= nctx->get_item(i); - Item_func_set_user_var *suv; - - suv= new Item_func_set_user_var(guv->get_name(), item); - /* - Item_func_set_user_var is not fixed after construction, - call fix_fields(). - */ - if ((err_status= test(!suv || suv->fix_fields(thd, &item) || - suv->check() || suv->update()))) - break; - } + err_status= TRUE; + break; } - } } @@ -1821,10 +1824,10 @@ void sp_head::set_definer(const char *definer, uint definerlen) { char user_name_holder[USERNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH); + LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH }; char host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH); + LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH }; parse_user(definer, definerlen, user_name.str, &user_name.length, host_name.str, &host_name.length); @@ -2372,7 +2375,7 @@ sp_instr_set::execute(THD *thd, uint *nextp) int sp_instr_set::exec_core(THD *thd, uint *nextp) { - int res= thd->spcont->set_variable(thd, m_offset, m_value); + int res= thd->spcont->set_variable(thd, m_offset, &m_value); if (res && thd->spcont->found_handler_here()) { @@ -2437,12 +2440,7 @@ sp_instr_set_trigger_field::execute(THD *thd, uint *nextp) int sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp) { - int res= 0; - Item *it= sp_prepare_func_item(thd, &value); - if (!it || - !trigger_field->fixed && trigger_field->fix_fields(thd, 0) || - (it->save_in_field(trigger_field->field, 0) < 0)) - res= -1; + const int res= (trigger_field->set_value(thd, &value) ? -1 : 0); *nextp = m_ip+1; return res; } @@ -2647,7 +2645,7 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp) do it in scope of execution the current context/block. */ - return thd->spcont->set_return_value(thd, m_value); + return thd->spcont->set_return_value(thd, &m_value); } void @@ -3091,7 +3089,7 @@ sp_instr_set_case_expr::execute(THD *thd, uint *nextp) int sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) { - int res= thd->spcont->set_case_expr(thd, m_case_expr_id, m_case_expr); + int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr); if (res && !thd->spcont->get_case_expr(m_case_expr_id) && @@ -3105,7 +3103,7 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) Item *null_item= new Item_null(); if (!null_item || - thd->spcont->set_case_expr(thd, m_case_expr_id, null_item)) + thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item)) { /* If this also failed, we have to abort. */ diff --git a/sql/sp_head.h b/sql/sp_head.h index fbc277b4de8..791343f0061 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -1170,6 +1170,6 @@ Item * sp_prepare_func_item(THD* thd, Item **it_addr); bool -sp_eval_expr(THD *thd, Field *result_field, Item *expr_item); +sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr); #endif /* _SP_HEAD_H_ */ diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 38b6de0e75a..3bc27a029d0 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -150,7 +150,7 @@ sp_rcontext::init_var_items() bool -sp_rcontext::set_return_value(THD *thd, Item *return_value_item) +sp_rcontext::set_return_value(THD *thd, Item **return_value_item) { DBUG_ASSERT(m_return_value_fld); @@ -279,14 +279,14 @@ sp_rcontext::pop_cursors(uint count) int -sp_rcontext::set_variable(THD *thd, uint var_idx, Item *value) +sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value) { return set_variable(thd, m_var_table->field[var_idx], value); } int -sp_rcontext::set_variable(THD *thd, Field *field, Item *value) +sp_rcontext::set_variable(THD *thd, Field *field, Item **value) { if (!value) { @@ -478,9 +478,10 @@ sp_rcontext::create_case_expr_holder(THD *thd, Item_result result_type) */ int -sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item *case_expr_item) +sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr) { - if (!(case_expr_item= sp_prepare_func_item(thd, &case_expr_item))) + Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr); + if (!case_expr_item) return TRUE; if (!m_case_expr_holders[case_expr_id] || @@ -542,7 +543,7 @@ bool Select_fetch_into_spvars::send_data(List<Item> &items) */ for (; spvar= spvar_iter++, item= item_iter++; ) { - if (thd->spcont->set_variable(thd, spvar->offset, item)) + if (thd->spcont->set_variable(thd, spvar->offset, &item)) return TRUE; } return FALSE; diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 20aaea3b7c1..30521f6da84 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -91,7 +91,7 @@ class sp_rcontext : public Sql_alloc ~sp_rcontext(); int - set_variable(THD *thd, uint var_idx, Item *value); + set_variable(THD *thd, uint var_idx, Item **value); Item * get_item(uint var_idx); @@ -100,7 +100,7 @@ class sp_rcontext : public Sql_alloc get_item_addr(uint var_idx); bool - set_return_value(THD *thd, Item *return_value_item); + set_return_value(THD *thd, Item **return_value_item); inline bool is_return_value_set() const @@ -200,7 +200,7 @@ class sp_rcontext : public Sql_alloc */ int - set_case_expr(THD *thd, int case_expr_id, Item *case_expr_item); + set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr); Item * get_case_expr(int case_expr_id); @@ -254,7 +254,7 @@ private: Item_cache *create_case_expr_holder(THD *thd, Item_result result_type); - int set_variable(THD *thd, Field *field, Item *value); + int set_variable(THD *thd, Field *field, Item **value); }; // class sp_rcontext : public Sql_alloc diff --git a/sql/spatial.cc b/sql/spatial.cc index e91653f79d5..9f1f05aa18f 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -34,8 +34,11 @@ static Geometry::Class_info **ci_collection_end= Geometry::Class_info::Class_info(const char *name, int type_id, void(*create_func)(void *)): - m_name(name, strlen(name)), m_type_id(type_id), m_create_func(create_func) + m_type_id(type_id), m_create_func(create_func) { + m_name.str= (char *) name; + m_name.length= strlen(name); + ci_collection[type_id]= this; } diff --git a/sql/spatial.h b/sql/spatial.h index a6f74a1ada0..36949ff5014 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -200,7 +200,7 @@ public: class Class_info { public: - LEX_STRING_WITH_INIT m_name; + LEX_STRING m_name; int m_type_id; void (*m_create_func)(void *); Class_info(const char *name, int type_id, void(*create_func)(void *)); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index bc377a0ec16..d2501d08c4d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6154,20 +6154,21 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, } /* table privileges */ + rw_rdlock(&LOCK_grant); if (grant->version != grant_version) { - rw_rdlock(&LOCK_grant); grant->grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, table, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ - rw_unlock(&LOCK_grant); } if (grant->grant_table != 0) { grant->privilege|= grant->grant_table->privs; } + rw_unlock(&LOCK_grant); + DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); DBUG_VOID_RETURN; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 37060a315af..c39cb91198e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1069,7 +1069,9 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) handled either before writing a query log event (inside binlog_query()) or when preparing a pending event. */ +#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(TRUE); +#endif /*HAVE_ROW_BASED_REPLICATION*/ mysql_unlock_tables(thd, thd->lock); thd->lock=0; } @@ -1287,7 +1289,7 @@ void close_temporary_tables(THD *thd) table= next) { end_cur= strxmov(end_cur, "`", table->s->db.str, "`.`", - table->s->table_name.str, "`,", NullS); + table->s->table_name.str, "`,", NullS); next= table->next; close_temporary(table, 1, 1); } @@ -1315,7 +1317,6 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } - /* Find table in list. @@ -1791,6 +1792,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) MYSQL_LOCK_IGNORE_FLUSH - Open table even if someone has done a flush or namelock on it. No version number checking is done. + MYSQL_OPEN_IGNORE_LOCKED_TABLES - Open table + ignoring set of locked tables and prelocked mode. IMPLEMENTATION Uses a cache of open tables to find a table not in use. @@ -1850,7 +1853,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } } - if (thd->locked_tables || thd->prelocked_mode) + if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) && + (thd->locked_tables || thd->prelocked_mode)) { // Using table locks TABLE *best_table= 0; int best_distance= INT_MIN; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6166771e5d1..b4a7f9abc1c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -186,6 +186,11 @@ const char *thd_proc_info(THD *thd, const char *info) return old_info; } +void **thd_ha_data(const THD *thd, const struct handlerton *hton) +{ + return (void **) thd->ha_data + hton->slot; +} + /* Pass nominal parameters to Statement constructor only to ensure that @@ -197,7 +202,10 @@ THD::THD() :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0), Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), - user_time(0), in_sub_stmt(0), binlog_table_maps(0), + user_time(0), in_sub_stmt(0), +#ifdef HAVE_ROW_BASED_REPLICATION + binlog_table_maps(0), +#endif /*HAVE_ROW_BASED_REPLICATION*/ global_read_lock(0), is_fatal_error(0), rand_used(0), time_zone_used(0), last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0), @@ -250,7 +258,8 @@ THD::THD() net.last_error[0]=0; // If error on boot net.query_cache_query=0; // If error on boot ull=0; - system_thread= cleanup_done= abort_on_warning= no_warnings_for_error= 0; + system_thread= NON_SYSTEM_THREAD; + cleanup_done= abort_on_warning= no_warnings_for_error= 0; peer_port= 0; // For SHOW PROCESSLIST #ifdef HAVE_ROW_BASED_REPLICATION transaction.m_pending_rows_event= 0; @@ -333,7 +342,9 @@ void THD::init(void) bzero((char*) warn_count, sizeof(warn_count)); total_warn_count= 0; update_charset(); +#ifdef HAVE_ROW_BASED_REPLICATION reset_current_stmt_binlog_row_based(); +#endif /*HAVE_ROW_BASED_REPLICATION*/ bzero((char *) &status_var, sizeof(status_var)); } @@ -507,6 +518,8 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) void THD::awake(THD::killed_state state_to_set) { + DBUG_ENTER("THD::awake"); + DBUG_PRINT("enter", ("this=0x%lx", this)); THD_CHECK_SENTRY(this); safe_mutex_assert_owner(&LOCK_delete); @@ -550,6 +563,7 @@ void THD::awake(THD::killed_state state_to_set) } pthread_mutex_unlock(&mysys_var->mutex); } + DBUG_VOID_RETURN; } /* @@ -1925,7 +1939,7 @@ bool select_dumpvar::send_data(List<Item> &items) if ((yy=var_li++)) { if (thd->spcont->set_variable(current_thd, yy->get_var_idx(), - *it.ref())) + it.ref())) DBUG_RETURN(1); } } @@ -2026,6 +2040,13 @@ void Security_context::skip_grants() } +bool Security_context::set_user(char *user_arg) +{ + safeFree(user); + user= my_strdup(user_arg, MYF(0)); + return user == 0; +} + /**************************************************************************** Handling of open and locked tables states. @@ -2713,8 +2734,10 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, the flushing will be done inside the top-most close_thread_tables(). */ +#ifdef HAVE_ROW_BASED_REPLICATION if (this->lock) DBUG_RETURN(binlog_flush_pending_rows_event(TRUE)); +#endif /*HAVE_ROW_BASED_REPLICATION*/ DBUG_RETURN(0); } /* Otherwise, we fall through */ @@ -2733,7 +2756,9 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, table maps were written. */ int error= mysql_bin_log.write(&qinfo); +#ifdef HAVE_ROW_BASED_REPLICATION binlog_table_maps= 0; +#endif /*HAVE_ROW_BASED_REPLICATION*/ DBUG_RETURN(error); } break; diff --git a/sql/sql_class.h b/sql/sql_class.h index 54f256997d0..a0971b22d3d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -629,6 +629,8 @@ public: { return (*priv_host ? priv_host : (char *)"%"); } + + bool set_user(char *user_arg); }; @@ -770,6 +772,19 @@ public: }; +/* Flags for the THD::system_thread variable */ +enum enum_thread_type +{ + NON_SYSTEM_THREAD= 0, + SYSTEM_THREAD_DELAYED_INSERT= 1, + SYSTEM_THREAD_SLAVE_IO= 2, + SYSTEM_THREAD_SLAVE_SQL= 4, + SYSTEM_THREAD_NDBCLUSTER_BINLOG= 8, + SYSTEM_THREAD_EVENT_SCHEDULER= 16, + SYSTEM_THREAD_EVENT_WORKER= 32 +}; + + /* For each client connection we create a separate thread with THD serving as a thread/connection descriptor @@ -1103,7 +1118,8 @@ public: long dbug_thread_id; pthread_t real_id; uint tmp_table, global_read_lock; - uint server_status,open_options,system_thread; + uint server_status,open_options; + enum enum_thread_type system_thread; uint32 db_length; uint select_number; //number of select (used for EXPLAIN) /* variables.transaction_isolation is reset to this after each commit */ @@ -1380,6 +1396,7 @@ public: void restore_sub_statement_state(Sub_statement_state *backup); void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); void restore_active_arena(Query_arena *set, Query_arena *backup); +#ifdef HAVE_ROW_BASED_REPLICATION inline void set_current_stmt_binlog_row_based_if_mixed() { if (variables.binlog_format == BINLOG_FORMAT_MIXED) @@ -1393,6 +1410,7 @@ public: { current_stmt_binlog_row_based= test(variables.binlog_format == BINLOG_FORMAT_ROW); } +#endif /*HAVE_ROW_BASED_REPLICATION*/ }; @@ -1402,11 +1420,6 @@ public: #define reenable_binlog(A) (A)->options= tmp_disable_binlog__save_options;} -/* Flags for the THD::system_thread (bitmap) variable */ -#define SYSTEM_THREAD_DELAYED_INSERT 1 -#define SYSTEM_THREAD_SLAVE_IO 2 -#define SYSTEM_THREAD_SLAVE_SQL 4 -#define SYSTEM_THREAD_NDBCLUSTER_BINLOG 8 /* Used to hold information about file and file structure in exchainge diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 3d035359b6f..6d5362c2554 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -871,7 +871,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ - (void)evex_drop_db_events(thd, db); /* QQ Ignore errors for now */ + error= Events::drop_schema_events(thd, db); start_waiting_global_read_lock(thd); /* If this database was the client's selected database, we silently change the diff --git a/sql/sql_error.h b/sql/sql_error.h index 223b50be744..b5cac24d894 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -40,3 +40,5 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, const char *format, ...); void mysql_reset_errors(THD *thd, bool force); bool mysqld_show_warnings(THD *thd, ulong levels_to_show); + +extern LEX_STRING warning_level_names[]; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 946c0536897..84760e93d8e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2518,23 +2518,179 @@ bool select_insert::send_eof() CREATE TABLE (SELECT) ... ***************************************************************************/ -int -select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) -{ - DBUG_ENTER("select_create::prepare"); +/* + Create table from lists of fields and items (or open existing table + with same name). + + 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 + extra_fields in/out Initial list of fields for table to be created + keys in List of keys for 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 'extra_fields' list) + lock out Pointer to the MYSQL_LOCK object for table created + (open) will be returned in this parameter. Since + this table is not included in THD::lock caller is + responsible for explicitly unlocking this table. + hooks + + NOTES + If 'create_info->options' bitmask has HA_LEX_CREATE_IF_NOT_EXISTS + flag and table with name provided already exists then this function will + simply open existing table. + Also note that create, open and lock sequence in this function is not + atomic and thus contains gap for deadlock and can cause other troubles. + 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 +*/ - class MY_HOOKS : public TABLEOP_HOOKS { - public: - MY_HOOKS(select_create *x) : ptr(x) { } - virtual void do_prelock(TABLE **tables, uint count) +static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, + TABLE_LIST *create_table, + List<create_field> *extra_fields, + List<Key> *keys, + List<Item> *items, + MYSQL_LOCK **lock, + TABLEOP_HOOKS *hooks) +{ + TABLE tmp_table; // Used during 'create_field()' + TABLE_SHARE share; + TABLE *table= 0; + uint select_field_count= items->elements; + /* Add selected items to field list */ + List_iterator_fast<Item> it(*items); + Item *item; + Field *tmp_field; + bool not_used; + DBUG_ENTER("create_table_from_items"); + + tmp_table.alias= 0; + tmp_table.timestamp_field= 0; + tmp_table.s= &share; + init_tmp_table_share(&share, "", 0, "", ""); + + tmp_table.s->db_create_options=0; + tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr; + tmp_table.s->db_low_byte_first= + test(create_info->db_type == &myisam_hton || + create_info->db_type == &heap_hton); + tmp_table.null_row=tmp_table.maybe_null=0; + + while ((item=it++)) + { + create_field *cr_field; + Field *field; + if (item->type() == Item::FUNC_ITEM) + field=item->tmp_table_field(&tmp_table); + else + field=create_tmp_field(thd, &tmp_table, item, item->type(), + (Item ***) 0, &tmp_field, 0, 0, 0, 0, 0); + if (!field || + !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? + ((Item_field *)item)->field : + (Field*) 0)))) + DBUG_RETURN(0); + if (item->maybe_null) + cr_field->flags &= ~NOT_NULL_FLAG; + extra_fields->push_back(cr_field); + } + /* + create and lock table + + We don't log the statement, it will be logged later. + + If this is a HEAP table, the automatic DELETE FROM which is written to the + binlog when a HEAP table is opened for the first time since startup, must + not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we + don't want to delete from it) 2) it would be written before the CREATE + TABLE, which is a wrong order. So we keep binary logging disabled when we + open_table(). + NOTE: By locking table which we just have created (or for which we just have + have found that it already exists) separately from other tables used by the + statement we create potential window for deadlock. + TODO: create and open should be done atomic ! + */ + { + tmp_disable_binlog(thd); + if (!mysql_create_table(thd, create_table->db, create_table->table_name, + create_info, *extra_fields, *keys, 0, + select_field_count)) { - if (ptr->get_thd()->current_stmt_binlog_row_based) - ptr->binlog_show_create_table(tables, count); + /* + If we are here in prelocked mode we either create temporary table + or prelocked mode is caused by the SELECT part of this statement. + */ + DBUG_ASSERT(!thd->prelocked_mode || + create_info->options & HA_LEX_CREATE_TMP_TABLE || + thd->lex->requires_prelocking()); + + /* + NOTE: We don't want to ignore set of locked tables here if we are + under explicit LOCK TABLES since it will open gap for deadlock + too wide (and also is not backward compatible). + */ + + if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0, + (MYSQL_LOCK_IGNORE_FLUSH | + ((thd->prelocked_mode == PRELOCKED) ? + MYSQL_OPEN_IGNORE_LOCKED_TABLES:0))))) + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->table_name)); } + reenable_binlog(thd); + if (!table) // open failed + DBUG_RETURN(0); + } + + /* + FIXME: What happens if trigger manages to be created while we are + obtaining this lock ? May be it is sensible just to disable + trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH + save us from that ? + */ + table->reginfo.lock_type=TL_WRITE; + hooks->prelock(&table, 1); // Call prelock hooks + if (! ((*lock)= mysql_lock_tables(thd, &table, 1, + MYSQL_LOCK_IGNORE_FLUSH, ¬_used))) + { + VOID(pthread_mutex_lock(&LOCK_open)); + hash_delete(&open_cache,(byte*) table); + VOID(pthread_mutex_unlock(&LOCK_open)); + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->table_name)); + DBUG_RETURN(0); + } + table->file->extra(HA_EXTRA_WRITE_CACHE); + DBUG_RETURN(table); +} - private: - select_create *ptr; - }; + +class MY_HOOKS : public TABLEOP_HOOKS { +public: + MY_HOOKS(select_create *x) : ptr(x) { } + virtual void do_prelock(TABLE **tables, uint count) + { + if (ptr->get_thd()->current_stmt_binlog_row_based) + ptr->binlog_show_create_table(tables, count); + } + +private: + select_create *ptr; +}; + +int +select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) +{ + DBUG_ENTER("select_create::prepare"); MY_HOOKS hooks(this); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 562224201e7..02711ed8f48 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -319,18 +319,7 @@ static char *get_text(LEX *lex) found_escape=1; if (lex->ptr == lex->end_of_query) return 0; -#ifdef USE_MB - int l; - if (use_mb(cs) && - (l = my_ismbchar(cs, - (const char *)lex->ptr, - (const char *)lex->end_of_query))) { - lex->ptr += l; - continue; - } - else -#endif - yySkip(); + yySkip(); } else if (c == sep) { @@ -360,9 +349,6 @@ static char *get_text(LEX *lex) { uchar *to; - /* Re-use found_escape for tracking state of escapes */ - found_escape= 0; - for (to=start ; str != end ; str++) { #ifdef USE_MB @@ -376,8 +362,7 @@ static char *get_text(LEX *lex) continue; } #endif - if (!found_escape && - !(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) && + if (!(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) && *str == '\\' && str+1 != end) { switch(*++str) { @@ -404,20 +389,14 @@ static char *get_text(LEX *lex) *to++= '\\'; // remember prefix for wildcard /* Fall through */ default: - found_escape= 1; - str--; + *to++= *str; break; } } - else if (!found_escape && *str == sep) - { - found_escape= 1; - } + else if (*str == sep) + *to++= *str++; // Two ' or " else - { *to++ = *str; - found_escape= 0; - } } *to=0; lex->yytoklen=(uint) (to-start); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f0bd85367d0..12c7b106c26 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -110,8 +110,10 @@ enum enum_sql_command { SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN, SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, SQLCOM_SHOW_PLUGINS, + SQLCOM_SHOW_CONTRIBUTORS, SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, - SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, + SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, + SQLCOM_SHOW_SCHEDULER_STATUS, /* This should be the last !!! */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3ff6b7f4cc6..95f3f455351 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2049,7 +2049,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status); ulong id=(ulong) uint4korr(packet); - kill_one_thread(thd,id,false); + sql_kill(thd,id,false); break; } case COM_SET_OPTION: @@ -2468,8 +2468,10 @@ mysql_execute_command(THD *thd) statistic_increment(thd->status_var.com_stat[lex->sql_command], &LOCK_status); +#ifdef HAVE_ROW_BASED_REPLICATION if (lex->binlog_row_based_if_mixed) thd->set_current_stmt_binlog_row_based_if_mixed(); +#endif /*HAVE_ROW_BASED_REPLICATION*/ switch (lex->sql_command) { case SQLCOM_SELECT: @@ -2855,6 +2857,17 @@ mysql_execute_command(THD *thd) res= 1; goto end_with_restore_list; } +#ifdef WITH_PARTITION_STORAGE_ENGINE + { + partition_info *part_info= thd->lex->part_info; + if (part_info && !(part_info= thd->lex->part_info->get_clone())) + { + res= -1; + goto end_with_restore_list; + } + thd->work_part_info= part_info; + } +#endif if (select_lex->item_list.elements) // With select { select_result *result; @@ -2924,15 +2937,6 @@ mysql_execute_command(THD *thd) lex->like_name); else { -#ifdef WITH_PARTITION_STORAGE_ENGINE - partition_info *part_info= thd->lex->part_info; - if (part_info && !(part_info= thd->lex->part_info->get_clone())) - { - res= -1; - goto end_with_restore_list; - } - thd->work_part_info= part_info; -#endif res= mysql_create_table(thd, create_table->db, create_table->table_name, &lex->create_info, lex->create_list, @@ -3527,6 +3531,9 @@ end_with_restore_list: case SQLCOM_SHOW_AUTHORS: res= mysqld_show_authors(thd); break; + case SQLCOM_SHOW_CONTRIBUTORS: + res= mysqld_show_contributors(thd); + break; case SQLCOM_SHOW_PRIVILEGES: res= mysqld_show_privileges(thd); break; @@ -3832,14 +3839,17 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= evex_create_event(thd, lex->et, (uint) lex->create_info.options, - &rows_affected); + res= Events::create_event(thd, lex->et, + (uint) lex->create_info.options, + &rows_affected); break; case SQLCOM_ALTER_EVENT: - res= evex_update_event(thd, lex->et, lex->spname, &rows_affected); + res= Events::update_event(thd, lex->et, lex->spname, + &rows_affected); break; case SQLCOM_DROP_EVENT: - res= evex_drop_event(thd, lex->et, lex->drop_if_exists, &rows_affected); + res= Events::drop_event(thd, lex->et, lex->drop_if_exists, + &rows_affected); default:; } DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", @@ -3877,9 +3887,16 @@ end_with_restore_list: my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); goto error; } - res= evex_show_create_event(thd, lex->spname, lex->et->definer); + res= Events::show_create_event(thd, lex->spname, lex->et->definer); break; } +#ifndef DBUG_OFF + case SQLCOM_SHOW_SCHEDULER_STATUS: + { + res= Events::dump_internal_status(thd); + break; + } +#endif case SQLCOM_CREATE_FUNCTION: // UDF function { if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) @@ -4119,7 +4136,7 @@ end_with_restore_list: MYF(0)); goto error; } - kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY); + sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY); break; } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -5131,7 +5148,9 @@ end: */ if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION) reset_one_shot_variables(thd); +#ifdef HAVE_ROW_BASED_REPLICATION thd->reset_current_stmt_binlog_row_based(); +#endif /*HAVE_ROW_BASED_REPLICATION*/ /* The return value for ROW_COUNT() is "implementation dependent" if the @@ -5888,6 +5907,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) } else { + DBUG_ASSERT(thd->net.report_error); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); query_cache_abort(&thd->net); @@ -6910,22 +6930,26 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, return result; } + /* - kill on thread + kills a thread SYNOPSIS kill_one_thread() thd Thread class id Thread id + only_kill_query Should it kill the query or the connection NOTES This is written such that we have a short lock on LOCK_thread_count */ -void kill_one_thread(THD *thd, ulong id, bool only_kill_query) +uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) { THD *tmp; uint error=ER_NO_SUCH_THREAD; + DBUG_ENTER("kill_one_thread"); + DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query)); VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list I_List_iterator<THD> it(threads); while ((tmp=it++)) @@ -6951,8 +6975,25 @@ void kill_one_thread(THD *thd, ulong id, bool only_kill_query) error=ER_KILL_DENIED_ERROR; pthread_mutex_unlock(&tmp->LOCK_delete); } + DBUG_PRINT("exit", ("%d", error)); + DBUG_RETURN(error); +} + + +/* + kills a thread and sends response - if (!error) + SYNOPSIS + sql_kill() + thd Thread class + id Thread id + only_kill_query Should it kill the query or the connection +*/ + +void sql_kill(THD *thd, ulong id, bool only_kill_query) +{ + uint error; + if (!(error= kill_one_thread(thd, id, only_kill_query))) send_ok(thd); else my_error(error, MYF(0), id); @@ -7420,7 +7461,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, lex->create_info.merge_list.first)) goto err; if (grant_option && want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) + check_grant(thd, want_priv, create_table, 0, 1, 0)) goto err; if (select_lex->item_list.elements) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 381231144c4..36f4144aaff 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4076,6 +4076,7 @@ that are reorganised. tab_part_info->use_default_partitions= FALSE; } tab_part_info->use_default_no_partitions= FALSE; + tab_part_info->is_auto_partitioned= FALSE; } } else if (alter_info->flags == ALTER_DROP_PARTITION) @@ -4091,6 +4092,8 @@ that are reorganised. uint no_parts_dropped= alter_info->partition_names.elements; uint no_parts_found= 0; List_iterator<partition_element> part_it(tab_part_info->partitions); + + tab_part_info->is_auto_partitioned= FALSE; if (!(tab_part_info->part_type == RANGE_PARTITION || tab_part_info->part_type == LIST_PARTITION)) { @@ -4275,7 +4278,10 @@ state of p1. tab_part_info->no_parts= no_parts_remain; } if (!(alter_info->flags & ALTER_TABLE_REORG)) + { tab_part_info->use_default_no_partitions= FALSE; + tab_part_info->is_auto_partitioned= FALSE; + } } else if (alter_info->flags == ALTER_REORGANIZE_PARTITION) { @@ -4294,6 +4300,8 @@ state of p1. uint no_parts_new= thd->work_part_info->partitions.elements; partition_info *alt_part_info= thd->work_part_info; uint check_total_partitions; + + tab_part_info->is_auto_partitioned= FALSE; if (no_parts_reorged > tab_part_info->no_parts) { my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0)); @@ -4510,7 +4518,7 @@ the generated partition syntax in a correct manner. if (alter_info->flags & ALTER_REMOVE_PARTITIONING) { DBUG_PRINT("info", ("Remove partitioning")); - if (!(thd->lex->create_info.used_fields & HA_CREATE_USED_ENGINE)) + if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) { DBUG_PRINT("info", ("No explicit engine used")); create_info->db_type= table->part_info->default_engine_type; @@ -4527,14 +4535,29 @@ the generated partition syntax in a correct manner. beneath. */ thd->work_part_info= table->part_info; - if (thd->lex->create_info.used_fields & HA_CREATE_USED_ENGINE && + if (create_info->used_fields & HA_CREATE_USED_ENGINE && create_info->db_type != table->part_info->default_engine_type) { /* Make sure change of engine happens to all partitions. */ DBUG_PRINT("info", ("partition changed")); - set_engine_all_partitions(thd->work_part_info, create_info->db_type); + if (table->part_info->is_auto_partitioned) + { + /* + If the user originally didn't specify partitioning to be + used we can remove it now. + */ + thd->work_part_info= NULL; + } + else + { + /* + Ensure that all partitions have the proper engine set-up + */ + set_engine_all_partitions(thd->work_part_info, + create_info->db_type); + } *partition_changed= TRUE; } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f72d8fe0146..2212371db92 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3377,7 +3377,10 @@ best_access_path(JOIN *join, uint key= keyuse->key; KEY *keyinfo= table->key_info+key; bool ft_key= (keyuse->keypart == FT_KEYPART); - uint found_ref_or_null= 0; + /* Bitmap of keyparts where the ref access is over 'keypart=const': */ + key_part_map const_part= 0; + /* The or-null keypart in ref-or-null access: */ + key_part_map ref_or_null_part= 0; /* Calculate how many key segments of the current key we can use */ start_key= keyuse; @@ -3389,12 +3392,14 @@ best_access_path(JOIN *join, do { if (!(remaining_tables & keyuse->used_tables) && - !(found_ref_or_null & keyuse->optimize)) + !(ref_or_null_part && (keyuse->optimize & + KEY_OPTIMIZE_REF_OR_NULL))) { found_part|= keyuse->keypart_map; - double tmp= prev_record_reads(join, - (found_ref | - keyuse->used_tables)); + if (!(keyuse->used_tables & ~join->const_table_map)) + const_part|= keyuse->keypart_map; + double tmp= prev_record_reads(join, (found_ref | + keyuse->used_tables)); if (tmp < best_prev_record_reads) { best_part_found_ref= keyuse->used_tables; @@ -3406,8 +3411,8 @@ best_access_path(JOIN *join, If there is one 'key_column IS NULL' expression, we can use this ref_or_null optimisation of this field */ - found_ref_or_null|= (keyuse->optimize & - KEY_OPTIMIZE_REF_OR_NULL); + if (keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL) + ref_or_null_part |= keyuse->keypart_map; } keyuse++; } while (keyuse->table == table && keyuse->key == key && @@ -3443,7 +3448,7 @@ best_access_path(JOIN *join, Check if we found full key */ if (found_part == PREV_BITS(uint,keyinfo->key_parts) && - !found_ref_or_null) + !ref_or_null_part) { /* use eq key */ max_key_part= (uint) ~0; if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) @@ -3455,6 +3460,23 @@ best_access_path(JOIN *join, { if (!found_ref) { /* We found a const key */ + /* + ReuseRangeEstimateForRef-1: + We get here if we've found a ref(const) (c_i are constants): + "(keypart1=c1) AND ... AND (keypartN=cN)" [ref_const_cond] + + If range optimizer was able to construct a "range" + access on this index, then its condition "quick_cond" was + eqivalent to ref_const_cond (*), and we can re-use E(#rows) + from the range optimizer. + + Proof of (*): By properties of range and ref optimizers + quick_cond will be equal or tighther than ref_const_cond. + ref_const_cond already covers "smallest" possible interval - + a singlepoint interval over all keyparts. Therefore, + quick_cond is equivalent to ref_const_cond (if it was an + empty interval we wouldn't have got here). + */ if (table->quick_keys.is_set(key)) records= (double) table->quick_rows[key]; else @@ -3475,6 +3497,23 @@ best_access_path(JOIN *join, if (records < 2.0) records=2.0; /* Can't be as good as a unique */ } + /* + ReuseRangeEstimateForRef-2: We get here if we could not reuse + E(#rows) from range optimizer. Make another try: + + If range optimizer produced E(#rows) for a prefix of the ref + access we're considering, and that E(#rows) is lower then our + current estimate, make an adjustment. The criteria of when we + can make an adjustment is a special case of the criteria used + in ReuseRangeEstimateForRef-3. + */ + if (table->quick_keys.is_set(key) && + const_part & (1 << table->quick_key_parts[key]) && + table->quick_n_ranges[key] == 1 && + records > (double) table->quick_rows[key]) + { + records= (double) table->quick_rows[key]; + } } /* Limit the number of matched rows */ tmp= records; @@ -3503,12 +3542,50 @@ best_access_path(JOIN *join, { max_key_part= max_part_bit(found_part); /* - Check if quick_range could determinate how many rows we - will match + ReuseRangeEstimateForRef-3: + We're now considering a ref[or_null] access via + (t.keypart1=e1 AND ... AND t.keypartK=eK) [ OR + (same-as-above but with one cond replaced + with "t.keypart_i IS NULL")] (**) + + Try re-using E(#rows) from "range" optimizer: + We can do so if "range" optimizer used the same intervals as + in (**). The intervals used by range optimizer may be not + available at this point (as "range" access might have choosen to + create quick select over another index), so we can't compare + them to (**). We'll make indirect judgements instead. + The sufficient conditions for re-use are: + (C1) All e_i in (**) are constants, i.e. found_ref==FALSE. (if + this is not satisfied we have no way to know which ranges + will be actually scanned by 'ref' until we execute the + join) + (C2) max #key parts in 'range' access == K == max_key_part (this + is apparently a necessary requirement) + + We also have a property that "range optimizer produces equal or + tighter set of scan intervals than ref(const) optimizer". Each + of the intervals in (**) are "tightest possible" intervals when + one limits itself to using keyparts 1..K (which we do in #2). + From here it follows that range access used either one, or + both of the (I1) and (I2) intervals: + + (t.keypart1=c1 AND ... AND t.keypartK=eK) (I1) + (same-as-above but with one cond replaced + with "t.keypart_i IS NULL") (I2) + + The remaining part is to exclude the situation where range + optimizer used one interval while we're considering + ref-or-null and looking for estimate for two intervals. This + is done by last limitation: + + (C3) "range optimizer used (have ref_or_null?2:1) intervals" */ - if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] == max_key_part) + if (table->quick_keys.is_set(key) && !found_ref && //(C1) + table->quick_key_parts[key] == max_key_part && //(C2) + table->quick_n_ranges[key] == 1+test(ref_or_null_part)) //(C3) + { tmp= records= (double) table->quick_rows[key]; + } else { /* Check if we have statistic about the distribution */ @@ -3552,21 +3629,37 @@ best_access_path(JOIN *join, } records = (ulong) tmp; } + + if (ref_or_null_part) + { + /* We need to do two key searches to find key */ + tmp *= 2.0; + records *= 2.0; + } + /* - If quick_select was used on a part of this key, we know - the maximum number of rows that the key can match. + ReuseRangeEstimateForRef-4: We get here if we could not reuse + E(#rows) from range optimizer. Make another try: + + If range optimizer produced E(#rows) for a prefix of the ref + access we're considering, and that E(#rows) is lower then our + current estimate, make the adjustment. + + The decision whether we can re-use the estimate from the range + optimizer is the same as in ReuseRangeEstimateForRef-3, + applied to first table->quick_key_parts[key] key parts. */ if (table->quick_keys.is_set(key) && table->quick_key_parts[key] <= max_key_part && + const_part & (1 << table->quick_key_parts[key]) && + table->quick_n_ranges[key] == 1 + test(ref_or_null_part & + const_part) && records > (double) table->quick_rows[key]) - tmp= records= (double) table->quick_rows[key]; - else if (found_ref_or_null) { - /* We need to do two key searches to find key */ - tmp *= 2.0; - records *= 2.0; + tmp= records= (double) table->quick_rows[key]; } } + /* Limit the number of matched rows */ set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); if (table->used_keys.is_set(key)) @@ -4382,344 +4475,11 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, if ((rest_tables & real_table_bit) && !(rest_tables & s->dependent) && (!idx|| !check_interleaving_with_nj(join->positions[idx-1].table, s))) { - double best,best_time,records; - best=best_time=records=DBL_MAX; - KEYUSE *best_key=0; - uint best_max_key_part=0; - my_bool found_constraint= 0; - - if (s->keyuse) - { /* Use key if possible */ - TABLE *table=s->table; - KEYUSE *keyuse,*start_key=0; - double best_records=DBL_MAX; - uint max_key_part=0; - - /* Test how we can use keys */ - rec= s->records/MATCHING_ROWS_IN_OTHER_TABLE; // Assumed records/key - for (keyuse=s->keyuse ; keyuse->table == table ;) - { - key_part_map found_part=0; - table_map found_ref=0; - uint key=keyuse->key; - KEY *keyinfo=table->key_info+key; - bool ft_key=(keyuse->keypart == FT_KEYPART); - uint found_ref_or_null= 0; - - /* Calculate how many key segments of the current key we can use */ - start_key=keyuse; - do - { - uint keypart=keyuse->keypart; - table_map best_part_found_ref= 0; - double best_prev_record_reads= DBL_MAX; - do - { - if (!(rest_tables & keyuse->used_tables) && - !(found_ref_or_null & keyuse->optimize)) - { - found_part|=keyuse->keypart_map; - double tmp= prev_record_reads(join, - (found_ref | - keyuse->used_tables)); - if (tmp < best_prev_record_reads) - { - best_part_found_ref= keyuse->used_tables; - best_prev_record_reads= tmp; - } - if (rec > keyuse->ref_table_rows) - rec= keyuse->ref_table_rows; - /* - If there is one 'key_column IS NULL' expression, we can - use this ref_or_null optimisation of this field - */ - found_ref_or_null|= (keyuse->optimize & - KEY_OPTIMIZE_REF_OR_NULL); - } - keyuse++; - } while (keyuse->table == table && keyuse->key == key && - keyuse->keypart == keypart); - found_ref|= best_part_found_ref; - } while (keyuse->table == table && keyuse->key == key); - - /* - Assume that that each key matches a proportional part of table. - */ - if (!found_part && !ft_key) - continue; // Nothing usable found - if (rec < MATCHING_ROWS_IN_OTHER_TABLE) - rec= MATCHING_ROWS_IN_OTHER_TABLE; // Fix for small tables - - /* - ft-keys require special treatment - */ - if (ft_key) - { - /* - Really, there should be records=0.0 (yes!) - but 1.0 would be probably safer - */ - tmp=prev_record_reads(join,found_ref); - records=1.0; - } - else - { - found_constraint= 1; - /* - Check if we found full key - */ - if (found_part == PREV_BITS(uint,keyinfo->key_parts) && - !found_ref_or_null) - { /* use eq key */ - max_key_part= (uint) ~0; - if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY | - HA_END_SPACE_KEY)) == HA_NOSAME) - { - tmp=prev_record_reads(join,found_ref); - records=1.0; - } - else - { - if (!found_ref) - { // We found a const key - if (table->quick_keys.is_set(key)) - records= (double) table->quick_rows[key]; - else - { - /* quick_range couldn't use key! */ - records= (double) s->records/rec; - } - } - else - { - if (!(records=keyinfo->rec_per_key[keyinfo->key_parts-1])) - { // Prefere longer keys - records= - ((double) s->records / (double) rec * - (1.0 + - ((double) (table->s->max_key_length-keyinfo->key_length) / - (double) table->s->max_key_length))); - if (records < 2.0) - records=2.0; // Can't be as good as a unique - } - } - /* Limit the number of matched rows */ - tmp= records; - set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); - if (table->used_keys.is_set(key)) - { - /* we can use only index tree */ - uint keys_per_block= table->file->block_size/2/ - (keyinfo->key_length+table->file->ref_length)+1; - tmp=record_count*(tmp+keys_per_block-1)/keys_per_block; - } - else - tmp=record_count*min(tmp,s->worst_seeks); - } - } - else - { - /* - Use as much key-parts as possible and a uniq key is better - than a not unique key - Set tmp to (previous record count) * (records / combination) - */ - if ((found_part & 1) && - (!(table->file->index_flags(key,0,0) & HA_ONLY_WHOLE_INDEX) || - found_part == PREV_BITS(uint,keyinfo->key_parts))) - { - max_key_part=max_part_bit(found_part); - /* - Check if quick_range could determinate how many rows we - will match - */ - if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] == max_key_part) - tmp=records= (double) table->quick_rows[key]; - else - { - /* Check if we have statistic about the distribution */ - if ((records=keyinfo->rec_per_key[max_key_part-1])) - tmp=records; - else - { - /* - Assume that the first key part matches 1% of the file - and that the whole key matches 10 (duplicates) or 1 - (unique) records. - Assume also that more key matches proportionally more - records - This gives the formula: - records= (x * (b-a) + a*c-b)/(c-1) - - b = records matched by whole key - a = records matched by first key part (10% of all records?) - c = number of key parts in key - x = used key parts (1 <= x <= c) - */ - double rec_per_key; - rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1] ? - (double) keyinfo->rec_per_key[keyinfo->key_parts-1] : - (double) s->records/rec+1; - if (!s->records) - tmp=0; - else if (rec_per_key/(double) s->records >= 0.01) - tmp=rec_per_key; - else - { - double a=s->records*0.01; - tmp=(max_key_part * (rec_per_key - a) + - a*keyinfo->key_parts - rec_per_key)/ - (keyinfo->key_parts-1); - set_if_bigger(tmp,1.0); - } - records=(ulong) tmp; - } - /* - If quick_select was used on a part of this key, we know - the maximum number of rows that the key can match. - */ - if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] <= max_key_part && - records > (double) table->quick_rows[key]) - tmp= records= (double) table->quick_rows[key]; - else if (found_ref_or_null) - { - /* We need to do two key searches to find key */ - tmp*= 2.0; - records*= 2.0; - } - } - /* Limit the number of matched rows */ - set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); - if (table->used_keys.is_set(key)) - { - /* we can use only index tree */ - uint keys_per_block= table->file->block_size/2/ - (keyinfo->key_length+table->file->ref_length)+1; - tmp=record_count*(tmp+keys_per_block-1)/keys_per_block; - } - else - tmp=record_count*min(tmp,s->worst_seeks); - } - else - tmp=best_time; // Do nothing - } - } /* not ft_key */ - if (tmp < best_time - records/(double) TIME_FOR_COMPARE) - { - best_time=tmp + records/(double) TIME_FOR_COMPARE; - best=tmp; - best_records=records; - best_key=start_key; - best_max_key_part=max_key_part; - } - } - records=best_records; - } - - /* - Don't test table scan if it can't be better. - Prefer key lookup if we would use the same key for scanning. - - Don't do a table scan on InnoDB tables, if we can read the used - parts of the row from any of the used index. - This is because table scans uses index and we would not win - anything by using a table scan. - (see comment in best_access_path() for more details on the below - condition) - */ - if ((records >= s->found_records || best > s->read_time) && - !(s->quick && best_key && s->quick->index == best_key->key && - best_max_key_part >= s->table->quick_key_parts[best_key->key]) && - !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) && - ! s->table->used_keys.is_clear_all() && best_key) && - !(s->table->force_index && best_key && !s->quick)) - { // Check full join - ha_rows rnd_records= s->found_records; - /* - If there is a restriction on the table, assume that 25% of the - rows can be skipped on next part. - This is to force tables that this table depends on before this - table - */ - if (found_constraint) - rnd_records-= rnd_records/4; - - /* - Range optimizer never proposes a RANGE if it isn't better - than FULL: so if RANGE is present, it's always preferred to FULL. - Here we estimate its cost. - */ - if (s->quick) - { - /* - For each record we: - - read record range through 'quick' - - skip rows which does not satisfy WHERE constraints - */ - tmp= record_count * - (s->quick->read_time + - (s->found_records - rnd_records)/(double) TIME_FOR_COMPARE); - } - else - { - /* Estimate cost of reading table. */ - tmp= s->table->file->scan_time(); - if (s->table->map & join->outer_join) // Can't use join cache - { - /* - For each record we have to: - - read the whole table record - - skip rows which does not satisfy join condition - */ - tmp= record_count * - (tmp + - (s->records - rnd_records)/(double) TIME_FOR_COMPARE); - } - else - { - /* We read the table as many times as join buffer becomes full. */ - tmp*= (1.0 + floor((double) cache_record_length(join,idx) * - record_count / - (double) thd->variables.join_buff_size)); - /* - We don't make full cartesian product between rows in the scanned - table and existing records because we skip all rows from the - scanned table, which does not satisfy join condition when - we read the table (see flush_cached_records for details). Here we - take into account cost to read and skip these records. - */ - tmp+= (s->records - rnd_records)/(double) TIME_FOR_COMPARE; - } - } - - /* - We estimate the cost of evaluating WHERE clause for found records - as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus - tmp give us total cost of using TABLE SCAN - */ - if (best == DBL_MAX || - (tmp + record_count/(double) TIME_FOR_COMPARE*rnd_records < - best + record_count/(double) TIME_FOR_COMPARE*records)) - { - /* - If the table has a range (s->quick is set) make_join_select() - will ensure that this will be used - */ - best=tmp; - records= rows2double(rnd_records); - best_key=0; - } - } - join->positions[idx].records_read= records; - join->positions[idx].key=best_key; - join->positions[idx].table= s; - if (!best_key && idx == join->const_tables && - s->table == join->sort_by_table && - join->unit->select_limit_cnt >= records) - join->sort_by_table= (TABLE*) 1; // Must use temporary table - + double records, best; + best_access_path(join, s, thd, rest_tables, idx, record_count, + read_time); + records= join->positions[idx].records_read; + best= join->positions[idx].read_time; /* Go to the next level only if there hasn't been a better key on this level! This will cut down the search for a lot simple cases! @@ -8274,6 +8034,7 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, item->result_field= new_field; else new_field->field_name= name; + new_field->flags|= (org_field->flags & NO_DEFAULT_VALUE_FLAG); if (org_field->maybe_null() || (item && item->maybe_null)) new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join if (org_field->type() == MYSQL_TYPE_VAR_STRING || @@ -8437,6 +8198,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, bool make_copy_field, uint convert_blob_length) { + Field *result; Item::Type orig_type= type; Item *orig_item= 0; @@ -8454,8 +8216,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::SUM_FUNC_ITEM: { Item_sum *item_sum=(Item_sum*) item; - Field *result= item_sum->create_tmp_field(group, table, - convert_blob_length); + result= item_sum->create_tmp_field(group, table, convert_blob_length); if (!result) thd->fatal_error(); return result; @@ -8465,7 +8226,6 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, { Item_field *field= (Item_field*) item; bool orig_modify= modify_item; - Field *result; if (orig_type == Item::REF_ITEM) modify_item= 0; /* @@ -8499,6 +8259,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, convert_blob_length); if (orig_type == Item::REF_ITEM && orig_modify) ((Item_ref*)orig_item)->set_result_field(result); + if (field->field->eq_def(result)) + result->dflt_field= field->field; return result; } /* Fall through */ @@ -8521,10 +8283,10 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, DBUG_ASSERT(((Item_result_field*)item)->result_field); *from_field= ((Item_result_field*)item)->result_field; } - return create_tmp_field_from_item(thd, item, table, (make_copy_field ? 0 : - copy_func), modify_item, - convert_blob_length); - case Item::TYPE_HOLDER: + return create_tmp_field_from_item(thd, item, table, + (make_copy_field ? 0 : copy_func), + modify_item, convert_blob_length); + case Item::TYPE_HOLDER: return ((Item_type_holder *)item)->make_field_by_type(table); default: // Dosen't have to be stored return 0; @@ -8979,6 +8741,33 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, null_count+= (field->field_length & 7); } field->reset(); + + if (field->dflt_field && field->dflt_field->ptr) + { + /* + field->dflt_field is set only in the cases when 'field' can + inherit the default value that is defined for the field referred + by the Item_field object from which 'field' has been created. + For a field created not from a Item_field item dflt_field == 0. + */ + my_ptrdiff_t diff; + Field *orig_field= field->dflt_field; + /* Get the value from default_values */ + diff= (my_ptrdiff_t) (orig_field->table->s->default_values- + orig_field->table->record[0]); + orig_field->move_field_offset(diff); // Points now at default_values + bool is_null= orig_field->is_real_null(); + char *from= orig_field->ptr; + orig_field->move_field_offset(-diff); // Back to record[0] + if (is_null) + field->set_null(); + else + { + field->set_notnull(); + memcpy(field->ptr, from, field->pack_length()); + } + } + if (from_field[i]) { /* Not a table Item */ copy->set(field,from_field[i],save_sum_fields); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5e4324d0003..ac1825d7c84 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -25,6 +25,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "authors.h" +#include "contributors.h" #include "event.h" #include <my_dir.h> @@ -229,6 +230,41 @@ bool mysqld_show_authors(THD *thd) DBUG_RETURN(FALSE); } + +/*************************************************************************** +** List all Contributors. +** Please get permission before updating +***************************************************************************/ + +bool mysqld_show_contributors(THD *thd) +{ + List<Item> field_list; + Protocol *protocol= thd->protocol; + DBUG_ENTER("mysqld_show_contributors"); + + field_list.push_back(new Item_empty_string("Name",40)); + field_list.push_back(new Item_empty_string("Location",40)); + field_list.push_back(new Item_empty_string("Comment",80)); + + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + DBUG_RETURN(TRUE); + + show_table_contributors_st *contributors; + for (contributors= show_table_contributors; contributors->name; contributors++) + { + protocol->prepare_for_resend(); + protocol->store(contributors->name, system_charset_info); + protocol->store(contributors->location, system_charset_info); + protocol->store(contributors->comment, system_charset_info); + if (protocol->write()) + DBUG_RETURN(TRUE); + } + send_eof(thd); + DBUG_RETURN(FALSE); +} + + /*************************************************************************** List all privileges supported ***************************************************************************/ @@ -1286,6 +1322,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, uint part_syntax_len; char *part_syntax; if (table->part_info && + (!table->part_info->is_auto_partitioned) && ((part_syntax= generate_partition_syntax(table->part_info, &part_syntax_len, FALSE,FALSE)))) @@ -2931,7 +2968,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, field->real_type() == MYSQL_TYPE_STRING) // For binary type { uint32 octet_max_length= field->max_length(); - if (octet_max_length != (uint32) 4294967295U) + if (is_blob && octet_max_length != (uint32) 4294967295U) octet_max_length /= field->charset()->mbmaxlen; longlong char_max_len= is_blob ? (longlong) octet_max_length / field->charset()->mbminlen : @@ -3168,17 +3205,18 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, const char *wild, bool full_access, const char *sp_user) { String tmp_string; + String sp_db, sp_name, definer; TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - const char *sp_db, *sp_name, *definer; - sp_db= get_field(thd->mem_root, proc_table->field[0]); - sp_name= get_field(thd->mem_root, proc_table->field[1]); - definer= get_field(thd->mem_root, proc_table->field[11]); + get_field(thd->mem_root, proc_table->field[0], &sp_db); + get_field(thd->mem_root, proc_table->field[1], &sp_name); + get_field(thd->mem_root, proc_table->field[11], &definer); if (!full_access) - full_access= !strcmp(sp_user, definer); - if (!full_access && check_some_routine_access(thd, sp_db, sp_name, - proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE)) + full_access= !strcmp(sp_user, definer.ptr()); + if (!full_access && check_some_routine_access(thd, sp_db.ptr(), sp_name.ptr(), + proc_table->field[2]->val_int() == + TYPE_ENUM_PROCEDURE)) return 0; if (lex->orig_sql_command == SQLCOM_SHOW_STATUS_PROC && @@ -3188,13 +3226,13 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, lex->orig_sql_command == SQLCOM_END) { restore_record(table, s->default_values); - if (!wild || !wild[0] || !wild_compare(sp_name, wild, 0)) + if (!wild || !wild[0] || !wild_compare(sp_name.ptr(), wild, 0)) { int enum_idx= proc_table->field[5]->val_int(); - table->field[3]->store(sp_name, strlen(sp_name), cs); + table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); get_field(thd->mem_root, proc_table->field[3], &tmp_string); table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs); - table->field[2]->store(sp_db, strlen(sp_db), cs); + table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); get_field(thd->mem_root, proc_table->field[2], &tmp_string); table->field[4]->store(tmp_string.ptr(), tmp_string.length(), cs); if (proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION) @@ -3226,7 +3264,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, table->field[17]->store(tmp_string.ptr(), tmp_string.length(), cs); get_field(thd->mem_root, proc_table->field[15], &tmp_string); table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs); - table->field[19]->store(definer, strlen(definer), cs); + table->field[19]->store(definer.ptr(), definer.length(), cs); return schema_table_store_record(thd, table); } } @@ -3389,11 +3427,33 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, if (tables->view) { + Security_context *sctx= thd->security_ctx; + ulong grant= SHOW_VIEW_ACL; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + char *save_table_name= tables->table_name; + if (!my_strcasecmp(system_charset_info, tables->definer.user.str, + sctx->priv_user) && + !my_strcasecmp(system_charset_info, tables->definer.host.str, + sctx->priv_host)) + grant= SHOW_VIEW_ACL; + else + { + tables->table_name= tables->view_name.str; + if (check_access(thd, SHOW_VIEW_ACL , base_name, + &tables->grant.privilege, 0, 1, + test(tables->schema_table))) + grant= get_table_grant(thd, tables); + else + grant= tables->grant.privilege; + } + tables->table_name= save_table_name; +#endif + restore_record(table, s->default_values); table->field[1]->store(tables->view_db.str, tables->view_db.length, cs); - table->field[2]->store(tables->view_name.str, tables->view_name.length, - cs); - table->field[3]->store(tables->query.str, tables->query.length, cs); + table->field[2]->store(tables->view_name.str, tables->view_name.length, cs); + if (grant & SHOW_VIEW_ACL) + table->field[3]->store(tables->query.str, tables->query.length, cs); if (tables->with_check != VIEW_CHECK_NONE) { @@ -4061,8 +4121,9 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) /* type */ sch_table->field[5]->store(STRING_WITH_LEN("RECURRING"), scs); - if (event_reconstruct_interval_expression(&show_str, et.interval, - et.expression)) + if (Events::reconstruct_interval_expression(&show_str, + et.interval, + et.expression)) DBUG_RETURN(1); sch_table->field[7]->set_notnull(); @@ -4092,13 +4153,13 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) } //status - if (et.status == MYSQL_EVENT_ENABLED) + if (et.status == Event_timed::ENABLED) sch_table->field[12]->store(STRING_WITH_LEN("ENABLED"), scs); else sch_table->field[12]->store(STRING_WITH_LEN("DISABLED"), scs); //on_completion - if (et.on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + if (et.on_completion == Event_timed::ON_COMPLETION_DROP) sch_table->field[13]->store(STRING_WITH_LEN("NOT PRESERVE"), scs); else sch_table->field[13]->store(STRING_WITH_LEN("PRESERVE"), scs); @@ -4150,7 +4211,7 @@ int fill_schema_events(THD *thd, TABLE_LIST *tables, COND *cond) thd->reset_n_backup_open_tables_state(&backup); - if ((ret= evex_open_event_table(thd, TL_READ, &event_table))) + if ((ret= Events::open_event_table(thd, TL_READ, &event_table))) { sql_print_error("Table mysql.event is damaged."); ret= 1; @@ -4159,13 +4220,10 @@ int fill_schema_events(THD *thd, TABLE_LIST *tables, COND *cond) event_table->file->ha_index_init(0, 1); - /* - see others' events only if you have PROCESS_ACL !! - thd->lex->verbose is set either if SHOW FULL EVENTS or - in case of SELECT FROM I_S.EVENTS - */ - verbose= (thd->lex->verbose - && (thd->security_ctx->master_access & PROCESS_ACL)); + /* see others' events only if you have PROCESS_ACL !! */ + verbose= ((thd->lex->verbose || + thd->lex->orig_sql_command != SQLCOM_SHOW_EVENTS) && + (thd->security_ctx->master_access & PROCESS_ACL)); if (verbose && thd->security_ctx->user) { @@ -4174,12 +4232,13 @@ int fill_schema_events(THD *thd, TABLE_LIST *tables, COND *cond) } else { - event_table->field[EVEX_FIELD_DEFINER]->store(definer, strlen(definer), scs); + event_table->field[Events::FIELD_DEFINER]-> + store(definer, strlen(definer), scs); key_len= event_table->key_info->key_part[0].store_length; if (thd->lex->select_lex.db) { - event_table->field[EVEX_FIELD_DB]-> + event_table->field[Events::FIELD_DB]-> store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); key_len+= event_table->key_info->key_part[1].store_length; } diff --git a/sql/sql_string.h b/sql/sql_string.h index 0659f684afe..ddae6368228 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -24,8 +24,6 @@ #define NOT_FIXED_DEC 31 #endif -#define STRING_WITH_LEN(X) ((const char*) X), ((uint) (sizeof(X) - 1)) - class String; int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f890f504952..289051c2dd1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3078,6 +3078,7 @@ bool mysql_create_table_internal(THD *thd, } file->set_auto_partitions(part_info); part_info->default_engine_type= create_info->db_type; + part_info->is_auto_partitioned= TRUE; } if (part_info) { @@ -3138,8 +3139,8 @@ bool mysql_create_table_internal(THD *thd, } DBUG_PRINT("info", ("db_type = %d", ha_legacy_type(part_info->default_engine_type))); - if (part_info->check_partition_info( &engine_type, file, - create_info->max_rows)) + if (part_info->check_partition_info(&engine_type, file, + create_info->max_rows)) goto err; part_info->default_engine_type= engine_type; @@ -3198,6 +3199,12 @@ bool mysql_create_table_internal(THD *thd, } else if (create_info->db_type != engine_type) { + /* + We come here when we don't use a partitioned handler. + Since we use a partitioned table it must be "native partitioned". + We have switched engine from defaults, most likely only specified + engines in partition clauses. + */ delete file; if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, engine_type))) { @@ -3419,111 +3426,6 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end) /**************************************************************************** -** Create table from a list of fields and items -****************************************************************************/ - -TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, - TABLE_LIST *create_table, - List<create_field> *extra_fields, - List<Key> *keys, - List<Item> *items, - MYSQL_LOCK **lock, - TABLEOP_HOOKS *hooks) -{ - TABLE tmp_table; // Used during 'create_field()' - TABLE_SHARE share; - TABLE *table= 0; - uint select_field_count= items->elements; - /* Add selected items to field list */ - List_iterator_fast<Item> it(*items); - Item *item; - Field *tmp_field; - bool not_used; - DBUG_ENTER("create_table_from_items"); - - tmp_table.alias= 0; - tmp_table.timestamp_field= 0; - tmp_table.s= &share; - init_tmp_table_share(&share, "", 0, "", ""); - - tmp_table.s->db_create_options=0; - tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr; - tmp_table.s->db_low_byte_first= - test(create_info->db_type == &myisam_hton || - create_info->db_type == &heap_hton); - tmp_table.null_row=tmp_table.maybe_null=0; - - while ((item=it++)) - { - create_field *cr_field; - Field *field; - if (item->type() == Item::FUNC_ITEM) - field=item->tmp_table_field(&tmp_table); - else - field=create_tmp_field(thd, &tmp_table, item, item->type(), - (Item ***) 0, &tmp_field, 0, 0, 0, 0, 0); - if (!field || - !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? - ((Item_field *)item)->field : - (Field*) 0)))) - DBUG_RETURN(0); - if (item->maybe_null) - cr_field->flags &= ~NOT_NULL_FLAG; - extra_fields->push_back(cr_field); - } - /* - create and lock table - - We don't log the statement, it will be logged later. - - If this is a HEAP table, the automatic DELETE FROM which is written to the - binlog when a HEAP table is opened for the first time since startup, must - not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we - don't want to delete from it) 2) it would be written before the CREATE - TABLE, which is a wrong order. So we keep binary logging disabled when we - open_table(). - TODO: create and open should be done atomic ! - */ - { - tmp_disable_binlog(thd); - if (!mysql_create_table(thd, create_table->db, create_table->table_name, - create_info, *extra_fields, *keys, 0, - select_field_count)) - { - if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0, - MYSQL_LOCK_IGNORE_FLUSH))) - quick_rm_table(create_info->db_type, create_table->db, - table_case_name(create_info, create_table->table_name)); - } - reenable_binlog(thd); - if (!table) // open failed - DBUG_RETURN(0); - } - - /* - FIXME: What happens if trigger manages to be created while we are - obtaining this lock ? May be it is sensible just to disable - trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH - save us from that ? - */ - table->reginfo.lock_type=TL_WRITE; - hooks->prelock(&table, 1); // Call prelock hooks - if (! ((*lock)= mysql_lock_tables(thd, &table, 1, - MYSQL_LOCK_IGNORE_FLUSH, ¬_used))) - { - VOID(pthread_mutex_lock(&LOCK_open)); - hash_delete(&open_cache,(byte*) table); - VOID(pthread_mutex_unlock(&LOCK_open)); - quick_rm_table(create_info->db_type, create_table->db, - table_case_name(create_info, create_table->table_name)); - DBUG_RETURN(0); - } - table->file->extra(HA_EXTRA_WRITE_CACHE); - DBUG_RETURN(table); -} - - -/**************************************************************************** ** Alter a table definition ****************************************************************************/ @@ -5098,7 +5000,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ha_resolve_storage_engine_name(old_db_type), ha_resolve_storage_engine_name(new_db_type))); if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) || - ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) + ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED) || + (old_db_type != new_db_type && + ha_check_storage_engine_flag(new_db_type, HTON_ALTER_CANNOT_CREATE))) { DBUG_PRINT("info", ("doesn't support alter")); my_error(ER_ILLEGAL_HA, MYF(0), table_name); @@ -6323,6 +6227,20 @@ copy_data_between_tables(TABLE *from,TABLE *to, (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)) { + if (error == HA_ERR_FOUND_DUPP_KEY) + { + uint key_nr= to->file->get_dup_key(error); + if ((int) key_nr >= 0) + { + const char *err_msg= ER(ER_DUP_ENTRY); + if (key_nr == 0 && + (to->key_info[0].key_part[0].field->flags & AUTO_INCREMENT_FLAG)) + err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE); + to->file->print_keydupp_error(key_nr, err_msg); + break; + } + } + to->file->print_error(error,MYF(0)); break; } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index b2b6b115f7d..0ea87f3dfe4 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1412,8 +1412,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, } if (table.triggers) { - LEX_STRING_WITH_INIT old_table_name(old_table, strlen(old_table)); - LEX_STRING_WITH_INIT new_table_name(new_table, strlen(new_table)); + LEX_STRING old_table_name= { (char *) old_table, strlen(old_table) }; + LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) }; /* Since triggers should be in the same schema as their subject tables moving table with them between two schemas raises too many questions. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 31ef2d8907e..82dcc758ff0 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -204,6 +204,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CONSTRAINT %token CONTAINS_SYM %token CONTINUE_SYM +%token CONTRIBUTORS_SYM %token CONVERT_SYM %token CONVERT_TZ_SYM %token COUNT_SYM @@ -475,6 +476,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NUMERIC_SYM %token NVARCHAR_SYM %token OFFSET_SYM +%token OJ_SYM %token OLD_PASSWORD %token ON %token ONE_SHOT_SYM @@ -567,6 +569,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RTREE_SYM %token SAVEPOINT_SYM %token SCHEDULE_SYM +%token SCHEDULER_SYM %token SECOND_MICROSECOND_SYM %token SECOND_SYM %token SECURITY_SYM @@ -1400,7 +1403,7 @@ opt_ev_status: /* empty */ { $$= 0; } { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->status= MYSQL_EVENT_ENABLED; + lex->et->status= Event_timed::ENABLED; $$= 1; } | DISABLE_SYM @@ -1408,7 +1411,7 @@ opt_ev_status: /* empty */ { $$= 0; } LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->status= MYSQL_EVENT_DISABLED; + lex->et->status= Event_timed::DISABLED; $$= 1; } ; @@ -1472,14 +1475,14 @@ ev_on_completion: { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; + lex->et->on_completion= Event_timed::ON_COMPLETION_PRESERVE; $$= 1; } | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; + lex->et->on_completion= Event_timed::ON_COMPLETION_DROP; $$= 1; } ; @@ -6943,11 +6946,14 @@ table_factor: } expr '}' { + LEX *lex= Lex; YYERROR_UNLESS($3 && $7); add_join_on($7,$10); Lex->pop_context(); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; + if (!($$= lex->current_select->nest_last_join(lex->thd))) + YYABORT; } | select_derived_init get_select_lex select_derived2 { @@ -7492,7 +7498,11 @@ select_var_ident: if (lex->result) ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0)); else - YYABORT; + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); } | ident_or_text { @@ -7504,10 +7514,8 @@ select_var_ident: my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); YYABORT; } - if (! lex->result) - YYABORT; - else - { + if (lex->result) + { my_var *var; ((select_dumpvar *)lex->result)-> var_list.push_back(var= new my_var($1,1,t->offset,t->type)); @@ -7515,6 +7523,14 @@ select_var_ident: if (var) var->sp= lex->sphead; #endif + } + else + { + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); } } ; @@ -8053,15 +8069,24 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS)) YYABORT; } - | opt_full EVENTS_SYM opt_db wild_and_where + | EVENTS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_EVENTS; - lex->select_lex.db= $3; + lex->select_lex.db= $2; if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS)) YYABORT; } + | SCHEDULER_SYM STATUS_SYM + { +#ifndef DBUG_OFF + Lex->sql_command= SQLCOM_SHOW_SCHEDULER_STATUS; +#else + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; +#endif + } | TABLE_SYM STATUS_SYM opt_db wild_and_where { LEX *lex= Lex; @@ -8170,6 +8195,11 @@ show_param: LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_AUTHORS; } + | CONTRIBUTORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; + } | PRIVILEGES { LEX *lex=Lex; @@ -9010,12 +9040,18 @@ simple_ident_q: YYABORT; } + DBUG_ASSERT(!new_row || + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + const bool read_only= + !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); if (!(trg_fld= new Item_trigger_field(Lex->current_context(), new_row ? Item_trigger_field::NEW_ROW: Item_trigger_field::OLD_ROW, $3.str, - Item_trigger_field::AT_READ))) + SELECT_ACL, + read_only))) YYABORT; /* @@ -9492,6 +9528,7 @@ keyword_sp: | ROW_SYM {} | RTREE_SYM {} | SCHEDULE_SYM {} + | SCHEDULER_SYM {} | SECOND_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} @@ -9696,11 +9733,13 @@ sys_option_value: it= new Item_null(); } + DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE && + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); if (!(trg_fld= new Item_trigger_field(Lex->current_context(), Item_trigger_field::NEW_ROW, $2.base_name.str, - Item_trigger_field::AT_UPDATE) - ) || + UPDATE_ACL, FALSE)) || !(sp_fld= new sp_instr_set_trigger_field(lex->sphead-> instructions(), lex->spcont, diff --git a/sql/structs.h b/sql/structs.h index 72237887514..78f00f72df1 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -20,22 +20,6 @@ struct st_table; class Field; -typedef struct st_lex_string -{ - char *str; - uint length; -} LEX_STRING; - -typedef struct st_lex_string_with_init :public st_lex_string -{ - st_lex_string_with_init(const char *str_arg, uint length_arg) - { - str= (char*) str_arg; - length= length_arg; - } -} LEX_STRING_WITH_INIT; - - typedef struct st_date_time_format { uchar positions[8]; char time_separator; /* Separator between hour and minute */ diff --git a/sql/table.cc b/sql/table.cc index bacb703a28c..d0caba7fe9e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -656,7 +656,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, my_free(buff, MYF(0)); goto err; } - next_chunk++; } #else if (partition_info_len) @@ -680,7 +679,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, */ next_chunk+= 4; } + else if (share->mysql_version >= 50110) #endif + { + /* New auto_partitioned indicator introduced in 5.1.11 */ +#ifdef WITH_PARTITION_STORAGE_ENGINE + share->auto_partitioned= *next_chunk; +#endif + next_chunk++; + } keyinfo= share->key_info; for (i= 0; i < keys; i++, keyinfo++) { @@ -1471,6 +1478,8 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, Fix the partition functions and ensure they are not constant functions */ + outparam->part_info->is_auto_partitioned= share->auto_partitioned; + DBUG_PRINT("info", ("autopartitioned = %u", share->auto_partitioned)); if (fix_partition_func(thd, share->normalized_path.str, outparam, is_create_table)) goto err; @@ -2392,9 +2401,7 @@ table_check_intact(TABLE *table, uint table_f_count, table running on a old server will be valid. */ field->sql_type(sql_type); - if (sql_type.length() < table_def->type.length - 1 || - strncmp(sql_type.ptr(), - table_def->type.str, + if (strncmp(sql_type.c_ptr_safe(), table_def->type.str, table_def->type.length - 1)) { sql_print_error("(%s) Expected field %s at position %d to have type " diff --git a/sql/table.h b/sql/table.h index 5fd9cd28585..ffecc60b19c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -214,6 +214,7 @@ typedef struct st_table_share */ bool log_table; #ifdef WITH_PARTITION_STORAGE_ENGINE + bool auto_partitioned; const uchar *partition_info; uint partition_info_len; const uchar *part_state; @@ -263,6 +264,7 @@ struct st_table { ha_rows quick_rows[MAX_KEY]; key_part_map const_key_parts[MAX_KEY]; uint quick_key_parts[MAX_KEY]; + uint quick_n_ranges[MAX_KEY]; /* If this table has TIMESTAMP field with auto-set property (pointed by diff --git a/sql/unireg.cc b/sql/unireg.cc index eb38e6c0592..11aa73bb502 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -130,8 +130,14 @@ bool mysql_create_frm(THD *thd, const char *file_name, /* str_db_type */ create_info->extra_size= (2 + str_db_type.length + 2 + create_info->connect_string.length); - /* Partition */ - create_info->extra_size+= 9; + /* + Partition: + Length of partition info = 4 byte + Potential NULL byte at end of partition info string = 1 byte + Indicator if auto-partitioned table = 1 byte + => Total 6 byte + */ + create_info->extra_size+= 6; #ifdef WITH_PARTITION_STORAGE_ENGINE if (part_info) { @@ -203,17 +209,19 @@ bool mysql_create_frm(THD *thd, const char *file_name, #ifdef WITH_PARTITION_STORAGE_ENGINE if (part_info) { + char auto_partitioned= part_info->is_auto_partitioned ? 1 : 0; int4store(buff, part_info->part_info_len); if (my_write(file, (const byte*)buff, 4, MYF_RW) || my_write(file, (const byte*)part_info->part_info_string, - part_info->part_info_len + 1, MYF_RW)) + part_info->part_info_len + 1, MYF_RW) || + my_write(file, (const byte*)&auto_partitioned, 1, MYF_RW)) goto err; } else #endif { - bzero(buff, 9); - if (my_write(file, (byte*) buff, 9, MYF_RW)) + bzero(buff, 6); + if (my_write(file, (byte*) buff, 6, MYF_RW)) goto err; } for (i= 0; i < keys; i++) diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index e39ee976eb1..f3f20f6b103 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -1239,7 +1239,7 @@ int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt) int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) { DBUG_ENTER("ha_archive::optimize"); - int rc; + int rc= 0; azio_stream writer; char writer_filename[FN_REFLEN]; @@ -1342,7 +1342,20 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) azclose(&writer); share->dirty= FALSE; share->forced_flushes= 0; + + // now we close both our writer and our reader for the rename azclose(&(share->archive_write)); + azclose(&archive); + + // make the file we just wrote be our data file + rc = my_rename(writer_filename,share->data_file_name,MYF(0)); + + /* + now open the shared writer back up + we don't check rc here because we want to open the file back up even + if the optimize failed but we will return rc below so that we will + know it failed. + */ DBUG_PRINT("info", ("Reopening archive data file")); if (!(azopen(&(share->archive_write), share->data_file_name, O_WRONLY|O_APPEND|O_BINARY))) @@ -1352,21 +1365,15 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) goto error; } - my_rename(writer_filename,share->data_file_name,MYF(0)); - /* Now we need to reopen our read descriptor since it has changed. */ - azclose(&archive); if (!(azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY))) { rc= HA_ERR_CRASHED_ON_USAGE; goto error; } - - DBUG_RETURN(0); - error: azclose(&writer); diff --git a/storage/blackhole/ha_blackhole.cc b/storage/blackhole/ha_blackhole.cc index e9fd1c2319d..098e44f39ee 100644 --- a/storage/blackhole/ha_blackhole.cc +++ b/storage/blackhole/ha_blackhole.cc @@ -67,7 +67,7 @@ handlerton blackhole_hton= { NULL, /* Alter table flags */ NULL, /* Alter Tablespace */ NULL, /* Fill FILES table */ - HTON_CAN_RECREATE, + HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE, NULL, /* binlog_func */ NULL, /* binlog_log_query */ NULL /* release_temporary_latches */ diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index de69df90ed5..f15ce7cccb5 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -146,6 +146,11 @@ static byte* tina_get_key(TINA_SHARE *share,uint *length, int get_mmap(TINA_SHARE *share, int write) { DBUG_ENTER("ha_tina::get_mmap"); +#ifdef __NETWARE__ + my_message(errno, "Sorry, no mmap() on Netware", 0); + DBUG_ASSERT(0); + DBUG_RETURN(1); +#else if (share->mapped_file && my_munmap(share->mapped_file, share->file_stat.st_size)) DBUG_RETURN(1); @@ -180,6 +185,7 @@ int get_mmap(TINA_SHARE *share, int write) share->mapped_file= NULL; DBUG_RETURN(0); +#endif /* __NETWARE__ */ } @@ -466,14 +472,28 @@ int tina_end(ha_panic_function type) /* Finds the end of a line. - Currently only supports files written on a UNIX OS. + Supports DOS, Unix, or Mac OS line endings. */ -byte * find_eoln(byte *data, off_t begin, off_t end) +byte * find_eoln(byte *data, off_t begin, off_t end, int *eoln_len) { + off_t dataend= begin; + *eoln_len= 0; + for (off_t x= begin; x < end; x++) - if (data[x] == '\n') - return data + x; + if (data[x] == '\r' || data[x] == '\n') + (*eoln_len)++; + else if (!(*eoln_len)) + dataend++; + else + return data+dataend; + /* + if we only have one record in the file then our for loop will break + before we return. we should still have seen end of line markers and + so we just return the line here + */ + if (*eoln_len > 0) + return data+dataend; return 0; } @@ -621,6 +641,7 @@ int ha_tina::find_current_row(byte *buf) { byte *mapped_ptr; byte *end_ptr; + int eoln_len; DBUG_ENTER("ha_tina::find_current_row"); mapped_ptr= (byte *)share->mapped_file + current_position; @@ -630,7 +651,7 @@ int ha_tina::find_current_row(byte *buf) not to conflict with undergoing concurrent insert. */ if ((end_ptr= find_eoln(share->mapped_file, current_position, - local_saved_data_file_length)) == 0) + local_saved_data_file_length, &eoln_len)) == 0) DBUG_RETURN(HA_ERR_END_OF_FILE); for (Field **field=table->field ; *field ; field++) @@ -678,7 +699,7 @@ int ha_tina::find_current_row(byte *buf) } (*field)->store(buffer.ptr(), buffer.length(), system_charset_info); } - next_position= (end_ptr - share->mapped_file)+1; + next_position= (end_ptr - share->mapped_file)+eoln_len; /* Maybe use \N for null? */ memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */ diff --git a/storage/heap/hp_create.c b/storage/heap/hp_create.c index 329f4b7fa2d..8e185a60262 100644 --- a/storage/heap/hp_create.c +++ b/storage/heap/hp_create.c @@ -85,6 +85,7 @@ int heap_create(const char *name, uint keys, HP_KEYDEF *keydef, if (!my_binary_compare(keyinfo->seg[j].charset)) keyinfo->flag|= HA_END_SPACE_KEY; keyinfo->flag|= HA_VAR_LENGTH_KEY; + length+= 2; /* Save number of bytes used to store length */ keyinfo->seg[j].bit_start= 1; break; @@ -95,6 +96,7 @@ int heap_create(const char *name, uint keys, HP_KEYDEF *keydef, if (!my_binary_compare(keyinfo->seg[j].charset)) keyinfo->flag|= HA_END_SPACE_KEY; keyinfo->flag|= HA_VAR_LENGTH_KEY; + length+= 2; /* Save number of bytes used to store length */ keyinfo->seg[j].bit_start= 2; /* diff --git a/storage/ndb/include/mgmapi/mgmapi.h b/storage/ndb/include/mgmapi/mgmapi.h index 4585e78029a..5a0ffcfe2c6 100644 --- a/storage/ndb/include/mgmapi/mgmapi.h +++ b/storage/ndb/include/mgmapi/mgmapi.h @@ -849,16 +849,6 @@ extern "C" { enum ndb_mgm_event_category category, int level, struct ndb_mgm_reply* reply); - - /** - * Returns the port number where statistics information is sent - * - * @param handle NDB management handle. - * @param reply Reply message. - * @return -1 on error. - */ - int ndb_mgm_get_stat_port(NdbMgmHandle handle, - struct ndb_mgm_reply* reply); #endif /** diff --git a/storage/ndb/include/ndbapi/Ndb.hpp b/storage/ndb/include/ndbapi/Ndb.hpp index 010b85b03a9..42043fbb93a 100644 --- a/storage/ndb/include/ndbapi/Ndb.hpp +++ b/storage/ndb/include/ndbapi/Ndb.hpp @@ -986,6 +986,7 @@ class NdbBlob; class NdbReceiver; class TransporterFacade; class PollGuard; +class Ndb_local_table_info; template <class T> struct Ndb_free_list_t; typedef void (* NdbEventCallback)(NdbEventOperation*, Ndb*, void*); @@ -1262,6 +1263,7 @@ public: #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + int flushIncompleteEvents(Uint64 gci); NdbEventOperation *getEventOperation(NdbEventOperation* eventOp= 0); Uint64 getLatestGCI(); void forceGCP(); @@ -1462,35 +1464,58 @@ public: /** * Return a unique tuple id for a table. The id sequence is - * ascending but may contain gaps. + * ascending but may contain gaps. Methods which have no + * TupleIdRange argument use NDB API dict cache. They may + * not be called from mysqld. * * @param aTableName table name * * @param cacheSize number of values to cache in this Ndb object * - * @return tuple id or 0 on error + * @return 0 or -1 on error, and tupleId in out parameter */ + struct TupleIdRange { + Uint64 m_first_tuple_id; + Uint64 m_last_tuple_id; + void reset() { + m_first_tuple_id = ~(Uint64)0; + m_last_tuple_id = ~(Uint64)0; + }; + }; + int initAutoIncrement(); - Uint64 getAutoIncrementValue(const char* aTableName, - Uint32 cacheSize = 1); - Uint64 getAutoIncrementValue(const NdbDictionary::Table * aTable, - Uint32 cacheSize = 1); - Uint64 readAutoIncrementValue(const char* aTableName); - Uint64 readAutoIncrementValue(const NdbDictionary::Table * aTable); - bool setAutoIncrementValue(const char* aTableName, Uint64 val, - bool increase = false); - bool setAutoIncrementValue(const NdbDictionary::Table * aTable, Uint64 val, - bool increase = false); - Uint64 getTupleIdFromNdb(const char* aTableName, - Uint32 cacheSize = 1000); - Uint64 getTupleIdFromNdb(Uint32 aTableId, - Uint32 cacheSize = 1000); - Uint64 readTupleIdFromNdb(Uint32 aTableId); - bool setTupleIdInNdb(const char* aTableName, Uint64 val, - bool increase); - bool setTupleIdInNdb(Uint32 aTableId, Uint64 val, bool increase); - Uint64 opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op); + int getAutoIncrementValue(const char* aTableName, + Uint64 & tupleId, Uint32 cacheSize); + int getAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint64 & tupleId, Uint32 cacheSize); + int getAutoIncrementValue(const NdbDictionary::Table * aTable, + TupleIdRange & range, Uint64 & tupleId, + Uint32 cacheSize); + int readAutoIncrementValue(const char* aTableName, + Uint64 & tupleId); + int readAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint64 & tupleId); + int readAutoIncrementValue(const NdbDictionary::Table * aTable, + TupleIdRange & range, Uint64 & tupleId); + int setAutoIncrementValue(const char* aTableName, + Uint64 tupleId, bool increase); + int setAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint64 tupleId, bool increase); + int setAutoIncrementValue(const NdbDictionary::Table * aTable, + TupleIdRange & range, Uint64 tupleId, + bool increase); +private: + int getTupleIdFromNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 & tupleId, + Uint32 cacheSize); + int readTupleIdFromNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 & tupleId); + int setTupleIdInNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 tupleId, bool increase); + int opTupleIdOnNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 & opValue, Uint32 op); +public: /** */ @@ -1693,12 +1718,8 @@ private: Uint64 the_last_check_time; Uint64 theFirstTransId; - - // The tupleId is retreived from DB the - // tupleId is unique for each tableid. + // The tupleId is retrieved from DB const NdbDictionary::Table *m_sys_tab_0; - Uint64 theFirstTupleId[2048]; - Uint64 theLastTupleId[2048]; Uint32 theRestartGCI; // the Restart GCI used by DIHNDBTAMPER diff --git a/storage/ndb/include/ndbapi/NdbDictionary.hpp b/storage/ndb/include/ndbapi/NdbDictionary.hpp index b31e2551e89..865fb506f05 100644 --- a/storage/ndb/include/ndbapi/NdbDictionary.hpp +++ b/storage/ndb/include/ndbapi/NdbDictionary.hpp @@ -1132,7 +1132,8 @@ public: _TE_NODE_FAILURE=10, _TE_SUBSCRIBE=11, _TE_UNSUBSCRIBE=12, - _TE_NUL=13 // internal (e.g. INS o DEL within same GCI) + _TE_NUL=13, // internal (e.g. INS o DEL within same GCI) + _TE_ACTIVE=14 // internal (node becomes active) }; #endif /** diff --git a/storage/ndb/include/ndbapi/ndb_cluster_connection.hpp b/storage/ndb/include/ndbapi/ndb_cluster_connection.hpp index 01c700d14b2..a803d010e61 100644 --- a/storage/ndb/include/ndbapi/ndb_cluster_connection.hpp +++ b/storage/ndb/include/ndbapi/ndb_cluster_connection.hpp @@ -110,6 +110,7 @@ public: unsigned no_db_nodes(); unsigned node_id(); + unsigned get_connect_count() const; void init_get_next_node(Ndb_cluster_connection_node_iter &iter); unsigned int get_next_node(Ndb_cluster_connection_node_iter &iter); diff --git a/storage/ndb/include/util/SocketServer.hpp b/storage/ndb/include/util/SocketServer.hpp index e766a0b99c4..c4f7e8c0ade 100644 --- a/storage/ndb/include/util/SocketServer.hpp +++ b/storage/ndb/include/util/SocketServer.hpp @@ -106,7 +106,8 @@ public: void stopSessions(bool wait = false); void foreachSession(void (*f)(Session*, void*), void *data); - + void checkSessions(); + private: struct SessionInstance { Service * m_service; @@ -117,12 +118,13 @@ private: Service * m_service; NDB_SOCKET_TYPE m_socket; }; - MutexVector<SessionInstance> m_sessions; + NdbLockable m_session_mutex; + Vector<SessionInstance> m_sessions; MutexVector<ServiceInstance> m_services; unsigned m_maxSessions; void doAccept(); - void checkSessions(); + void checkSessionsImpl(); void startSession(SessionInstance &); /** diff --git a/storage/ndb/src/common/util/SocketServer.cpp b/storage/ndb/src/common/util/SocketServer.cpp index f0af925cf6d..f9d2c7463be 100644 --- a/storage/ndb/src/common/util/SocketServer.cpp +++ b/storage/ndb/src/common/util/SocketServer.cpp @@ -184,9 +184,12 @@ SocketServer::doAccept(){ SessionInstance s; s.m_service = si.m_service; s.m_session = si.m_service->newSession(childSock); - if(s.m_session != 0){ + if(s.m_session != 0) + { + m_session_mutex.lock(); m_sessions.push_back(s); startSession(m_sessions.back()); + m_session_mutex.unlock(); } continue; @@ -240,10 +243,13 @@ void SocketServer::doRun(){ while(!m_stopThread){ - checkSessions(); + m_session_mutex.lock(); + checkSessionsImpl(); if(m_sessions.size() < m_maxSessions){ + m_session_mutex.unlock(); doAccept(); } else { + m_session_mutex.unlock(); NdbSleep_MilliSleep(200); } } @@ -276,17 +282,30 @@ transfer(NDB_SOCKET_TYPE sock){ void SocketServer::foreachSession(void (*func)(SocketServer::Session*, void *), void *data) { + m_session_mutex.lock(); for(int i = m_sessions.size() - 1; i >= 0; i--){ (*func)(m_sessions[i].m_session, data); } - checkSessions(); + m_session_mutex.unlock(); } void -SocketServer::checkSessions(){ - for(int i = m_sessions.size() - 1; i >= 0; i--){ - if(m_sessions[i].m_session->m_stopped){ - if(m_sessions[i].m_thread != 0){ +SocketServer::checkSessions() +{ + m_session_mutex.lock(); + checkSessionsImpl(); + m_session_mutex.unlock(); +} + +void +SocketServer::checkSessionsImpl() +{ + for(int i = m_sessions.size() - 1; i >= 0; i--) + { + if(m_sessions[i].m_session->m_stopped) + { + if(m_sessions[i].m_thread != 0) + { void* ret; NdbThread_WaitFor(m_sessions[i].m_thread, &ret); NdbThread_Destroy(&m_sessions[i].m_thread); @@ -301,19 +320,26 @@ SocketServer::checkSessions(){ void SocketServer::stopSessions(bool wait){ int i; + m_session_mutex.lock(); for(i = m_sessions.size() - 1; i>=0; i--) { m_sessions[i].m_session->stopSession(); m_sessions[i].m_session->m_stop = true; // to make sure } + m_session_mutex.unlock(); + for(i = m_services.size() - 1; i>=0; i--) m_services[i].m_service->stopSessions(); if(wait){ + m_session_mutex.lock(); while(m_sessions.size() > 0){ - checkSessions(); + checkSessionsImpl(); + m_session_mutex.unlock(); NdbSleep_MilliSleep(100); + m_session_mutex.lock(); } + m_session_mutex.unlock(); } } @@ -348,4 +374,4 @@ sessionThread_C(void* _sc){ } template class MutexVector<SocketServer::ServiceInstance>; -template class MutexVector<SocketServer::SessionInstance>; +template class Vector<SocketServer::SessionInstance>; diff --git a/storage/ndb/src/common/util/socket_io.cpp b/storage/ndb/src/common/util/socket_io.cpp index 83a546de773..58636e6663d 100644 --- a/storage/ndb/src/common/util/socket_io.cpp +++ b/storage/ndb/src/common/util/socket_io.cpp @@ -48,58 +48,66 @@ read_socket(NDB_SOCKET_TYPE socket, int timeout_millis, extern "C" int -readln_socket(NDB_SOCKET_TYPE socket, int timeout_millis, +readln_socket(NDB_SOCKET_TYPE socket, int timeout_millis, char * buf, int buflen){ if(buflen <= 1) return 0; + int sock_flags= fcntl(socket, F_GETFL); + if(fcntl(socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) + return -1; + fd_set readset; FD_ZERO(&readset); FD_SET(socket, &readset); - + struct timeval timeout; timeout.tv_sec = (timeout_millis / 1000); timeout.tv_usec = (timeout_millis % 1000) * 1000; const int selectRes = select(socket + 1, &readset, 0, 0, &timeout); - if(selectRes == 0) + if(selectRes == 0){ return 0; - + } + if(selectRes == -1){ + fcntl(socket, F_SETFL, sock_flags); return -1; } - - int pos = 0; buf[pos] = 0; - while(true){ - const int t = recv(socket, &buf[pos], 1, 0); - if(t != 1){ - return -1; - } - if(buf[pos] == '\n'){ - buf[pos] = 0; - if(pos > 0 && buf[pos-1] == '\r'){ - pos--; - buf[pos] = 0; + buf[0] = 0; + const int t = recv(socket, buf, buflen, MSG_PEEK); + + if(t < 1) + { + fcntl(socket, F_SETFL, sock_flags); + return -1; + } + + for(int i=0; i< t;i++) + { + if(buf[i] == '\n'){ + recv(socket, buf, i+1, 0); + buf[i] = 0; + + if(i > 0 && buf[i-1] == '\r'){ + i--; + buf[i] = 0; } - return pos; - } - pos++; - if(pos == (buflen - 1)){ - buf[pos] = 0; - return buflen; - } - - FD_ZERO(&readset); - FD_SET(socket, &readset); - timeout.tv_sec = (timeout_millis / 1000); - timeout.tv_usec = (timeout_millis % 1000) * 1000; - const int selectRes = select(socket + 1, &readset, 0, 0, &timeout); - if(selectRes != 1){ - return -1; + fcntl(socket, F_SETFL, sock_flags); + return t; } } + + if(t == (buflen - 1)){ + recv(socket, buf, t, 0); + buf[t] = 0; + fcntl(socket, F_SETFL, sock_flags); + return buflen; + } + + return 0; } extern "C" diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp index 90abe2cb809..1b6fef9de37 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp +++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp @@ -1926,6 +1926,7 @@ int Dbtup::interpreterNextLab(Signal* signal, // word read. Thus we set the register to be a 32 bit register. /* ------------------------------------------------------------- */ TregMemBuffer[theRegister]= 0x50; + // arithmetic conversion if big-endian * (Int64*)(TregMemBuffer+theRegister+2)= TregMemBuffer[theRegister+1]; } else if (TnoDataRW == 3) { @@ -1985,6 +1986,11 @@ int Dbtup::interpreterNextLab(Signal* signal, Tlen= TattrNoOfWords + 1; if (Toptype == ZUPDATE) { if (TattrNoOfWords <= 2) { + if (TattrNoOfWords == 1) { + // arithmetic conversion if big-endian + TdataForUpdate[1] = *(Int64*)&TregMemBuffer[theRegister + 2]; + TdataForUpdate[2] = 0; + } if (TregType == 0) { /* --------------------------------------------------------- */ // Write a NULL value into the attribute diff --git a/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp index b0372ec89a2..7c48ebb5e8b 100644 --- a/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp +++ b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp @@ -1645,10 +1645,9 @@ void Ndbcntr::systemErrorLab(Signal* signal, int line) /* |-2048| # 1 00000001 | */ /* | : | : | */ /* | -1 | # 1 00000001 | */ -/* | 0 | 0 | */ -/* | 1 | 0 | */ -/* | : | : | */ -/* | 2047| 0 | */ +/* | 1 | 0 | tupleid sequence now created on first use */ +/* | : | : | v */ +/* | 2048| 0 | v */ /*---------------------------------------------------------------------------*/ void Ndbcntr::createSystableLab(Signal* signal, unsigned index) { @@ -1859,8 +1858,7 @@ void Ndbcntr::crSystab8Lab(Signal* signal) jam(); ckey = 1; ctransidPhase = ZFALSE; - crSystab7Lab(signal); - return; + // skip 2nd loop - tupleid sequence now created on first use }//if signal->theData[0] = ctcConnectionP; signal->theData[1] = reference(); diff --git a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp index 8772e00f027..4d5ac377a5a 100644 --- a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp +++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp @@ -827,7 +827,7 @@ void Qmgr::execCM_REGCONF(Signal* signal) ptrCheckGuard(myNodePtr, MAX_NDB_NODES, nodeRec); ndbrequire(c_start.m_gsn == GSN_CM_REGREQ); - ndbrequire(myNodePtr.p->phase = ZSTARTING); + ndbrequire(myNodePtr.p->phase == ZSTARTING); cpdistref = cmRegConf->presidentBlockRef; cpresident = cmRegConf->presidentNodeId; diff --git a/storage/ndb/src/kernel/blocks/suma/Suma.cpp b/storage/ndb/src/kernel/blocks/suma/Suma.cpp index 867b13e1e40..91f0fab06f8 100644 --- a/storage/ndb/src/kernel/blocks/suma/Suma.cpp +++ b/storage/ndb/src/kernel/blocks/suma/Suma.cpp @@ -2649,6 +2649,22 @@ Suma::reportAllSubscribers(Signal *signal, SubscriptionPtr subPtr, SubscriberPtr subbPtr) { + SubTableData * data = (SubTableData*)signal->getDataPtrSend(); + + if (table_event == NdbDictionary::Event::_TE_SUBSCRIBE) + { + data->gci = m_last_complete_gci + 1; + data->tableId = subPtr.p->m_tableId; + data->operation = NdbDictionary::Event::_TE_ACTIVE; + data->ndbd_nodeid = refToNode(reference()); + data->changeMask = 0; + data->totalLen = 0; + data->req_nodeid = refToNode(subbPtr.p->m_senderRef); + data->senderData = subbPtr.p->m_senderData; + sendSignal(subbPtr.p->m_senderRef, GSN_SUB_TABLE_DATA, signal, + SubTableData::SignalLength, JBB); + } + if (!(subPtr.p->m_options & Subscription::REPORT_SUBSCRIBE)) { return; @@ -2663,7 +2679,6 @@ Suma::reportAllSubscribers(Signal *signal, ndbout_c("reportAllSubscribers subPtr.i: %d subPtr.p->n_subscribers: %d", subPtr.i, subPtr.p->n_subscribers); //#endif - SubTableData * data = (SubTableData*)signal->getDataPtrSend(); data->gci = m_last_complete_gci + 1; data->tableId = subPtr.p->m_tableId; data->operation = table_event; diff --git a/storage/ndb/src/kernel/vm/Configuration.cpp b/storage/ndb/src/kernel/vm/Configuration.cpp index e0e414e5669..12badffe0e0 100644 --- a/storage/ndb/src/kernel/vm/Configuration.cpp +++ b/storage/ndb/src/kernel/vm/Configuration.cpp @@ -49,7 +49,9 @@ extern EventLogger g_eventLogger; enum ndbd_options { OPT_INITIAL = NDB_STD_OPTIONS_LAST, OPT_NODAEMON, - OPT_FOREGROUND + OPT_FOREGROUND, + OPT_NOWAIT_NODES, + OPT_INITIAL_START }; NDB_STD_OPTS_VARS; @@ -88,11 +90,11 @@ static struct my_option my_long_options[] = " (implies --nodaemon)", (gptr*) &_foreground, (gptr*) &_foreground, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "nowait-nodes", NO_ARG, + { "nowait-nodes", OPT_NOWAIT_NODES, "Nodes that will not be waited for during start", (gptr*) &_nowait_nodes, (gptr*) &_nowait_nodes, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "initial-start", NO_ARG, + { "initial-start", OPT_INITIAL_START, "Perform initial start", (gptr*) &_initialstart, (gptr*) &_initialstart, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, diff --git a/storage/ndb/src/mgmapi/mgmapi.cpp b/storage/ndb/src/mgmapi/mgmapi.cpp index 08232471474..6dfb48667aa 100644 --- a/storage/ndb/src/mgmapi/mgmapi.cpp +++ b/storage/ndb/src/mgmapi/mgmapi.cpp @@ -701,10 +701,12 @@ ndb_mgm_get_status(NdbMgmHandle handle) Vector<BaseString> split; tmp.split(split, ":"); if(split.size() != 2){ + SET_ERROR(handle, NDB_MGM_ILLEGAL_NODE_STATUS, buf); return NULL; } if(!(split[0].trim() == "nodes")){ + SET_ERROR(handle, NDB_MGM_ILLEGAL_NODE_STATUS, buf); return NULL; } @@ -752,6 +754,7 @@ ndb_mgm_get_status(NdbMgmHandle handle) if(i+1 != noOfNodes){ free(state); + SET_ERROR(handle, NDB_MGM_ILLEGAL_NODE_STATUS, "Node count mismatch"); return NULL; } @@ -1305,33 +1308,6 @@ ndb_mgm_listen_event(NdbMgmHandle handle, const int filter[]) extern "C" int -ndb_mgm_get_stat_port(NdbMgmHandle handle, struct ndb_mgm_reply* /*reply*/) -{ - SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_get_stat_port"); - const ParserRow<ParserDummy> stat_reply[] = { - MGM_CMD("error", NULL, ""), - MGM_ARG("result", String, Mandatory, "Error message"), - MGM_CMD("get statport reply", NULL, ""), - MGM_ARG("tcpport", Int, Mandatory, "TCP port for statistics"), - MGM_END() - }; - CHECK_HANDLE(handle, -1); - CHECK_CONNECTED(handle, -1); - - Properties args; - const Properties *reply; - reply = ndb_mgm_call(handle, stat_reply, "get statport", &args); - CHECK_REPLY(reply, -1); - - Uint32 port; - reply->get("tcpport", &port); - - delete reply; - return port; -} - -extern "C" -int ndb_mgm_dump_state(NdbMgmHandle handle, int nodeId, int* _args, int _num_args, struct ndb_mgm_reply* /* reply */) { diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp index aee6d4deeb5..1e655c29517 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -2514,6 +2514,7 @@ int MgmtSrvr::abortBackup(Uint32 backupId) { SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit bool next; NodeId nodeId = 0; diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp index be15484688b..2731bfd422b 100644 --- a/storage/ndb/src/mgmsrv/Services.cpp +++ b/storage/ndb/src/mgmsrv/Services.cpp @@ -121,8 +121,6 @@ static const unsigned int MAX_WRITE_TIMEOUT = 100 ; const ParserRow<MgmApiSession> commands[] = { - MGM_CMD("get statport", &MgmApiSession::getStatPort, ""), - MGM_CMD("get config", &MgmApiSession::getConfig, ""), MGM_ARG("version", Int, Mandatory, "Configuration version number"), MGM_ARG("node", Int, Optional, "Node ID"), @@ -502,6 +500,7 @@ MgmApiSession::get_nodeid(Parser_t::Context &, ps.tick= tick; m_mgmsrv.get_socket_server()-> foreachSession(stop_session_if_timed_out,&ps); + m_mgmsrv.get_socket_server()->checkSessions(); error_string = ""; continue; } @@ -649,15 +648,6 @@ MgmApiSession::getConfig_common(Parser_t::Context &, } void -MgmApiSession::getStatPort(Parser_t::Context &, - const class Properties &) { - - m_output->println("get statport reply"); - m_output->println("tcpport: %d", 0); - m_output->println(""); -} - -void MgmApiSession::insertError(Parser<MgmApiSession>::Context &, Properties const &args) { Uint32 node = 0, error = 0; @@ -1559,6 +1549,7 @@ MgmApiSession::purge_stale_sessions(Parser_t::Context &ctx, ps.free_nodes.bitXORC(NodeBitmask()); // invert connected_nodes to get free nodes m_mgmsrv.get_socket_server()->foreachSession(stop_session_if_not_connected,&ps); + m_mgmsrv.get_socket_server()->checkSessions(); m_output->println("purge stale sessions reply"); if (str.length() > 0) diff --git a/storage/ndb/src/mgmsrv/Services.hpp b/storage/ndb/src/mgmsrv/Services.hpp index 975202b96df..abe0233cb33 100644 --- a/storage/ndb/src/mgmsrv/Services.hpp +++ b/storage/ndb/src/mgmsrv/Services.hpp @@ -53,7 +53,6 @@ public: virtual ~MgmApiSession(); void runSession(); - void getStatPort(Parser_t::Context &ctx, const class Properties &args); void getConfig(Parser_t::Context &ctx, const class Properties &args); #ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT void getConfig_old(Parser_t::Context &ctx); diff --git a/storage/ndb/src/ndbapi/ClusterMgr.cpp b/storage/ndb/src/ndbapi/ClusterMgr.cpp index 2ff403d53b3..b108ed3fd41 100644 --- a/storage/ndb/src/ndbapi/ClusterMgr.cpp +++ b/storage/ndb/src/ndbapi/ClusterMgr.cpp @@ -70,6 +70,8 @@ ClusterMgr::ClusterMgr(TransporterFacade & _facade): noOfAliveNodes= 0; noOfConnectedNodes= 0; theClusterMgrThread= 0; + m_connect_count = 0; + m_cluster_state = CS_waiting_for_clean_cache; DBUG_VOID_RETURN; } @@ -174,6 +176,16 @@ ClusterMgr::threadMain( ){ int send_heartbeat_now= global_flag_send_heartbeat_now; global_flag_send_heartbeat_now= 0; + if (m_cluster_state == CS_waiting_for_clean_cache) + { + theFacade.m_globalDictCache.lock(); + unsigned sz= theFacade.m_globalDictCache.get_size(); + theFacade.m_globalDictCache.unlock(); + if (sz) + goto next; + m_cluster_state = CS_waiting_for_first_connect; + } + theFacade.lock_mutex(); for (int i = 1; i < MAX_NODES; i++){ /** @@ -222,6 +234,7 @@ ClusterMgr::threadMain( ){ */ theFacade.unlock_mutex(); +next: // Sleep for 100 ms between each Registration Heartbeat Uint64 before = now; NdbSleep_MilliSleep(100); @@ -445,6 +458,11 @@ ClusterMgr::reportNodeFailed(NodeId nodeId){ theNode.nfCompleteRep = false; if(noOfAliveNodes == 0) { + theFacade.m_globalDictCache.lock(); + theFacade.m_globalDictCache.invalidate_all(); + theFacade.m_globalDictCache.unlock(); + m_connect_count ++; + m_cluster_state = CS_waiting_for_clean_cache; NFCompleteRep rep; for(Uint32 i = 1; i<MAX_NODES; i++){ if(theNodes[i].defined && theNodes[i].nfCompleteRep == false){ diff --git a/storage/ndb/src/ndbapi/ClusterMgr.hpp b/storage/ndb/src/ndbapi/ClusterMgr.hpp index 31682885f90..ca879e7948e 100644 --- a/storage/ndb/src/ndbapi/ClusterMgr.hpp +++ b/storage/ndb/src/ndbapi/ClusterMgr.hpp @@ -57,6 +57,11 @@ private: class TransporterFacade & theFacade; public: + enum Cluster_state { + CS_waiting_for_clean_cache = 0, + CS_waiting_for_first_connect, + CS_connected + }; struct Node { Node(); bool defined; @@ -80,12 +85,13 @@ public: bool isClusterAlive() const; void hb_received(NodeId); + Uint32 m_connect_count; private: Uint32 noOfAliveNodes; Uint32 noOfConnectedNodes; Node theNodes[MAX_NODES]; NdbThread* theClusterMgrThread; - + enum Cluster_state m_cluster_state; /** * Used for controlling start/stop of the thread */ diff --git a/storage/ndb/src/ndbapi/DictCache.cpp b/storage/ndb/src/ndbapi/DictCache.cpp index 43913d6c5be..8a0bf2f8e8b 100644 --- a/storage/ndb/src/ndbapi/DictCache.cpp +++ b/storage/ndb/src/ndbapi/DictCache.cpp @@ -47,6 +47,7 @@ Ndb_local_table_info::Ndb_local_table_info(NdbTableImpl *table_impl) { assert(! is_ndb_blob_table(table_impl)); m_table_impl= table_impl; + m_tuple_id_range.reset(); } Ndb_local_table_info::~Ndb_local_table_info() @@ -134,8 +135,8 @@ void GlobalDictCache::printCache() const unsigned sz = vers->size(); for(unsigned i = 0; i<sz ; i++){ TableVersion tv= (*vers)[i]; - DBUG_PRINT(" ", ("vers[%d]: ver: %d, refCount: %d, status: %d", - sz, tv.m_version, tv.m_refCount, tv.m_status)); + DBUG_PRINT(" ", ("impl: %p vers[%d]: ver: %d, refCount: %d, status: %d", + tv.m_impl, i, tv.m_version, tv.m_refCount, tv.m_status)); if(tv.m_impl != 0) { DBUG_PRINT(" ", ("m_impl: internalname: %s", @@ -178,6 +179,11 @@ GlobalDictCache::get(const char * name) { ver->m_status = DROPPED; retreive = true; // Break loop + if (ver->m_refCount == 0) + { + delete ver->m_impl; + versions->erase(versions->size() - 1); + } break; } ver->m_refCount++; @@ -280,6 +286,45 @@ GlobalDictCache::put(const char * name, NdbTableImpl * tab) DBUG_RETURN(tab); } +unsigned +GlobalDictCache::get_size() +{ + NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0); + int sz = 0; + while(curr != 0){ + sz += curr->theData->size(); + curr = m_tableHash.getNext(curr); + } + if (sz) + { + printCache(); + } + return sz; +} + +void +GlobalDictCache::invalidate_all() +{ + DBUG_ENTER("GlobalDictCache::invalidate_all"); + NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0); + while(curr != 0){ + Vector<TableVersion> * vers = curr->theData; + if (vers->size()) + { + TableVersion * ver = & vers->back(); + ver->m_impl->m_status = NdbDictionary::Object::Invalid; + ver->m_status = DROPPED; + if (ver->m_refCount == 0) + { + delete ver->m_impl; + vers->erase(vers->size() - 1); + } + } + curr = m_tableHash.getNext(curr); + } + DBUG_VOID_RETURN; +} + void GlobalDictCache::release(NdbTableImpl * tab, int invalidate) { @@ -373,6 +418,11 @@ GlobalDictCache::alter_table_rep(const char * name, ver.m_status = DROPPED; ver.m_impl->m_status = altered ? NdbDictionary::Object::Altered : NdbDictionary::Object::Invalid; + if (ver.m_refCount == 0) + { + delete ver.m_impl; + vers->erase(i); + } DBUG_VOID_RETURN; } diff --git a/storage/ndb/src/ndbapi/DictCache.hpp b/storage/ndb/src/ndbapi/DictCache.hpp index f134e6b348e..e909590e03e 100644 --- a/storage/ndb/src/ndbapi/DictCache.hpp +++ b/storage/ndb/src/ndbapi/DictCache.hpp @@ -33,6 +33,10 @@ public: static Ndb_local_table_info *create(NdbTableImpl *table_impl, Uint32 sz=0); static void destroy(Ndb_local_table_info *); NdbTableImpl *m_table_impl; + + // range of cached tuple ids per thread + Ndb::TupleIdRange m_tuple_id_range; + Uint64 m_local_data[1]; // Must be last member. Used to access extra space. private: Ndb_local_table_info(NdbTableImpl *table_impl); @@ -71,6 +75,9 @@ public: void alter_table_rep(const char * name, Uint32 tableId, Uint32 tableVersion, bool altered); + + unsigned get_size(); + void invalidate_all(); public: enum Status { OK = 0, diff --git a/storage/ndb/src/ndbapi/Ndb.cpp b/storage/ndb/src/ndbapi/Ndb.cpp index b963aeeff84..60fdef8111e 100644 --- a/storage/ndb/src/ndbapi/Ndb.cpp +++ b/storage/ndb/src/ndbapi/Ndb.cpp @@ -747,158 +747,271 @@ Remark: Returns a new TupleId to the application. The TupleId comes from SYSTAB_0 where SYSKEY_0 = TableId. It is initialized to (TableId << 48) + 1 in NdbcntrMain.cpp. ****************************************************************************/ -Uint64 -Ndb::getAutoIncrementValue(const char* aTableName, Uint32 cacheSize) +int +Ndb::getAutoIncrementValue(const char* aTableName, + Uint64 & tupleId, Uint32 cacheSize) { - DBUG_ENTER("getAutoIncrementValue"); + DBUG_ENTER("Ndb::getAutoIncrementValue"); + ASSERT_NOT_MYSQLD; BaseString internal_tabname(internalize_table_name(aTableName)); Ndb_local_table_info *info= theDictionary->get_local_table_info(internal_tabname); - if (info == 0) - DBUG_RETURN(~(Uint64)0); - const NdbTableImpl *table= info->m_table_impl; - Uint64 tupleId = getTupleIdFromNdb(table->m_id, cacheSize); - DBUG_PRINT("info", ("value %ul", (ulong) tupleId)); - DBUG_RETURN(tupleId); + if (info == 0) { + theError.code = theDictionary->getNdbError().code; + DBUG_RETURN(-1); + } + const NdbTableImpl* table = info->m_table_impl; + TupleIdRange & range = info->m_tuple_id_range; + if (getTupleIdFromNdb(table, range, tupleId, cacheSize) == -1) + DBUG_RETURN(-1); + DBUG_PRINT("info", ("value %llu", (ulonglong)tupleId)); + DBUG_RETURN(0); } -Uint64 -Ndb::getAutoIncrementValue(const NdbDictionary::Table * aTable, Uint32 cacheSize) +int +Ndb::getAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint64 & tupleId, Uint32 cacheSize) { - DBUG_ENTER("getAutoIncrementValue"); - if (aTable == 0) - DBUG_RETURN(~(Uint64)0); + DBUG_ENTER("Ndb::getAutoIncrementValue"); + ASSERT_NOT_MYSQLD; + assert(aTable != 0); const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable); - Uint64 tupleId = getTupleIdFromNdb(table->m_id, cacheSize); - DBUG_PRINT("info", ("value %ul", (ulong) tupleId)); - DBUG_RETURN(tupleId); + const BaseString& internal_tabname = table->m_internalName; + + Ndb_local_table_info *info= + theDictionary->get_local_table_info(internal_tabname); + if (info == 0) { + theError.code = theDictionary->getNdbError().code; + DBUG_RETURN(-1); + } + TupleIdRange & range = info->m_tuple_id_range; + if (getTupleIdFromNdb(table, range, tupleId, cacheSize) == -1) + DBUG_RETURN(-1); + DBUG_PRINT("info", ("value %llu", (ulonglong)tupleId)); + DBUG_RETURN(0); } -Uint64 -Ndb::getTupleIdFromNdb(const char* aTableName, Uint32 cacheSize) +int +Ndb::getAutoIncrementValue(const NdbDictionary::Table * aTable, + TupleIdRange & range, Uint64 & tupleId, + Uint32 cacheSize) { - const NdbTableImpl* table = theDictionary->getTable(aTableName); - if (table == 0) - return ~(Uint64)0; - return getTupleIdFromNdb(table->m_id, cacheSize); + DBUG_ENTER("Ndb::getAutoIncrementValue"); + assert(aTable != 0); + const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable); + + if (getTupleIdFromNdb(table, range, tupleId, cacheSize) == -1) + DBUG_RETURN(-1); + DBUG_PRINT("info", ("value %llu", (ulonglong)tupleId)); + DBUG_RETURN(0); } -Uint64 -Ndb::getTupleIdFromNdb(Uint32 aTableId, Uint32 cacheSize) +int +Ndb::getTupleIdFromNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 & tupleId, Uint32 cacheSize) { - DBUG_ENTER("getTupleIdFromNdb"); - if ( theFirstTupleId[aTableId] != theLastTupleId[aTableId] ) + DBUG_ENTER("Ndb::getTupleIdFromNdb"); + if (range.m_first_tuple_id != range.m_last_tuple_id) { - theFirstTupleId[aTableId]++; - DBUG_PRINT("info", ("next cached value %ul", - (ulong) theFirstTupleId[aTableId])); - DBUG_RETURN(theFirstTupleId[aTableId]); + assert(range.m_first_tuple_id < range.m_last_tuple_id); + tupleId = ++range.m_first_tuple_id; + DBUG_PRINT("info", ("next cached value %llu", (ulonglong)tupleId)); } - else // theFirstTupleId == theLastTupleId + else { - DBUG_PRINT("info",("reading %u values from database", - (cacheSize == 0) ? 1 : cacheSize)); - DBUG_RETURN(opTupleIdOnNdb(aTableId, (cacheSize == 0) ? 1 : cacheSize, 0)); + if (cacheSize == 0) + cacheSize = 1; + DBUG_PRINT("info", ("reading %u values from database", (uint)cacheSize)); + /* + * reserve next cacheSize entries in db. adds cacheSize to NEXTID + * and returns first tupleId in the new range. + */ + Uint64 opValue = cacheSize; + if (opTupleIdOnNdb(table, range, opValue, 0) == -1) + DBUG_RETURN(-1); + tupleId = opValue; } + DBUG_RETURN(0); } -Uint64 -Ndb::readAutoIncrementValue(const char* aTableName) +int +Ndb::readAutoIncrementValue(const char* aTableName, + Uint64 & tupleId) { - DBUG_ENTER("readAutoIncrementValue"); - const NdbTableImpl* table = theDictionary->getTable(aTableName); - if (table == 0) { - theError= theDictionary->getNdbError(); - DBUG_RETURN(~(Uint64)0); + DBUG_ENTER("Ndb::readAutoIncrementValue"); + ASSERT_NOT_MYSQLD; + BaseString internal_tabname(internalize_table_name(aTableName)); + + Ndb_local_table_info *info= + theDictionary->get_local_table_info(internal_tabname); + if (info == 0) { + theError.code = theDictionary->getNdbError().code; + DBUG_RETURN(-1); } - Uint64 tupleId = readTupleIdFromNdb(table->m_id); - DBUG_PRINT("info", ("value %ul", (ulong) tupleId)); - DBUG_RETURN(tupleId); + const NdbTableImpl* table = info->m_table_impl; + TupleIdRange & range = info->m_tuple_id_range; + if (readTupleIdFromNdb(table, range, tupleId) == -1) + DBUG_RETURN(-1); + DBUG_PRINT("info", ("value %llu", (ulonglong)tupleId)); + DBUG_RETURN(0); } -Uint64 -Ndb::readAutoIncrementValue(const NdbDictionary::Table * aTable) +int +Ndb::readAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint64 & tupleId) { - DBUG_ENTER("readAutoIncrementValue"); - if (aTable == 0) - DBUG_RETURN(~(Uint64)0); + DBUG_ENTER("Ndb::readAutoIncrementValue"); + ASSERT_NOT_MYSQLD; + assert(aTable != 0); const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable); - Uint64 tupleId = readTupleIdFromNdb(table->m_id); - DBUG_PRINT("info", ("value %ul", (ulong) tupleId)); - DBUG_RETURN(tupleId); + const BaseString& internal_tabname = table->m_internalName; + + Ndb_local_table_info *info= + theDictionary->get_local_table_info(internal_tabname); + if (info == 0) { + theError.code = theDictionary->getNdbError().code; + DBUG_RETURN(-1); + } + TupleIdRange & range = info->m_tuple_id_range; + if (readTupleIdFromNdb(table, range, tupleId) == -1) + DBUG_RETURN(-1); + DBUG_PRINT("info", ("value %llu", (ulonglong)tupleId)); + DBUG_RETURN(0); } -Uint64 -Ndb::readTupleIdFromNdb(Uint32 aTableId) +int +Ndb::readAutoIncrementValue(const NdbDictionary::Table * aTable, + TupleIdRange & range, Uint64 & tupleId) { - if ( theFirstTupleId[aTableId] == theLastTupleId[aTableId] ) - // Cache is empty, check next in database - return opTupleIdOnNdb(aTableId, 0, 3); + DBUG_ENTER("Ndb::readAutoIncrementValue"); + assert(aTable != 0); + const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable); - return theFirstTupleId[aTableId] + 1; + if (readTupleIdFromNdb(table, range, tupleId) == -1) + DBUG_RETURN(-1); + DBUG_PRINT("info", ("value %llu", (ulonglong)tupleId)); + DBUG_RETURN(0); } -bool -Ndb::setAutoIncrementValue(const char* aTableName, Uint64 val, bool increase) +int +Ndb::readTupleIdFromNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 & tupleId) { - DBUG_ENTER("setAutoIncrementValue"); + DBUG_ENTER("Ndb::readTupleIdFromNdb"); + if (range.m_first_tuple_id != range.m_last_tuple_id) + { + assert(range.m_first_tuple_id < range.m_last_tuple_id); + tupleId = range.m_first_tuple_id + 1; + } + else + { + /* + * peek at NEXTID. does not reserve it so the value is valid + * only if no other transactions are allowed. + */ + Uint64 opValue = 0; + if (opTupleIdOnNdb(table, range, opValue, 3) == -1) + DBUG_RETURN(-1); + tupleId = opValue; + } + DBUG_RETURN(0); +} + +int +Ndb::setAutoIncrementValue(const char* aTableName, + Uint64 tupleId, bool increase) +{ + DBUG_ENTER("Ndb::setAutoIncrementValue"); + ASSERT_NOT_MYSQLD; BaseString internal_tabname(internalize_table_name(aTableName)); Ndb_local_table_info *info= theDictionary->get_local_table_info(internal_tabname); if (info == 0) { - theError= theDictionary->getNdbError(); - DBUG_RETURN(false); + theError.code = theDictionary->getNdbError().code; + DBUG_RETURN(-1); } - const NdbTableImpl* table= info->m_table_impl; - DBUG_RETURN(setTupleIdInNdb(table->m_id, val, increase)); + const NdbTableImpl* table = info->m_table_impl; + TupleIdRange & range = info->m_tuple_id_range; + if (setTupleIdInNdb(table, range, tupleId, increase) == -1) + DBUG_RETURN(-1); + DBUG_RETURN(0); } -bool -Ndb::setAutoIncrementValue(const NdbDictionary::Table * aTable, Uint64 val, bool increase) +int +Ndb::setAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint64 tupleId, bool increase) { - DBUG_ENTER("setAutoIncrementValue"); - if (aTable == 0) - DBUG_RETURN(~(Uint64)0); + DBUG_ENTER("Ndb::setAutoIncrementValue"); + ASSERT_NOT_MYSQLD; + assert(aTable != 0); const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable); - DBUG_RETURN(setTupleIdInNdb(table->m_id, val, increase)); + const BaseString& internal_tabname = table->m_internalName; + + Ndb_local_table_info *info= + theDictionary->get_local_table_info(internal_tabname); + if (info == 0) { + theError.code = theDictionary->getNdbError().code; + DBUG_RETURN(-1); + } + TupleIdRange & range = info->m_tuple_id_range; + if (setTupleIdInNdb(table, range, tupleId, increase) == -1) + DBUG_RETURN(-1); + DBUG_RETURN(0); } -bool -Ndb::setTupleIdInNdb(const char* aTableName, Uint64 val, bool increase ) +int +Ndb::setAutoIncrementValue(const NdbDictionary::Table * aTable, + TupleIdRange & range, Uint64 tupleId, + bool increase) { - DBUG_ENTER("setTupleIdInNdb(const char*, ...)"); - const NdbTableImpl* table = theDictionary->getTable(aTableName); - if (table == 0) { - theError= theDictionary->getNdbError(); - DBUG_RETURN(false); - } - DBUG_RETURN(setTupleIdInNdb(table->m_id, val, increase)); + DBUG_ENTER("Ndb::setAutoIncrementValue"); + assert(aTable != 0); + const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable); + + if (setTupleIdInNdb(table, range, tupleId, increase) == -1) + DBUG_RETURN(-1); + DBUG_RETURN(0); } -bool -Ndb::setTupleIdInNdb(Uint32 aTableId, Uint64 val, bool increase ) +int +Ndb::setTupleIdInNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 tupleId, bool increase) { - DBUG_ENTER("setTupleIdInNdb(Uint32, ...)"); + DBUG_ENTER("Ndb::setTupleIdInNdb"); if (increase) { - if (theFirstTupleId[aTableId] != theLastTupleId[aTableId]) + if (range.m_first_tuple_id != range.m_last_tuple_id) { - // We have a cache sequence - if (val <= theFirstTupleId[aTableId]+1) - DBUG_RETURN(false); - if (val <= theLastTupleId[aTableId]) + assert(range.m_first_tuple_id < range.m_last_tuple_id); + if (tupleId <= range.m_first_tuple_id + 1) + DBUG_RETURN(0); + if (tupleId <= range.m_last_tuple_id) { - theFirstTupleId[aTableId] = val - 1; - DBUG_RETURN(true); + range.m_first_tuple_id = tupleId - 1; + DBUG_PRINT("info", + ("Setting next auto increment cached value to %llu", + (ulonglong)tupleId)); + DBUG_RETURN(0); } - // else continue; } - DBUG_RETURN((opTupleIdOnNdb(aTableId, val, 2) == val)); + /* + * if tupleId <= NEXTID, do nothing. otherwise update NEXTID to + * tupleId and set cached range to first = last = tupleId - 1. + */ + if (opTupleIdOnNdb(table, range, tupleId, 2) == -1) + DBUG_RETURN(-1); } else - DBUG_RETURN((opTupleIdOnNdb(aTableId, val, 1) == val)); + { + /* + * update NEXTID to given value. reset cached range. + */ + if (opTupleIdOnNdb(table, range, tupleId, 1) == -1) + DBUG_RETURN(-1); + } + DBUG_RETURN(0); } int Ndb::initAutoIncrement() @@ -922,18 +1035,18 @@ int Ndb::initAutoIncrement() return (m_sys_tab_0 == NULL); } -Uint64 -Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) +int +Ndb::opTupleIdOnNdb(const NdbTableImpl* table, + TupleIdRange & range, Uint64 & opValue, Uint32 op) { DBUG_ENTER("Ndb::opTupleIdOnNdb"); + Uint32 aTableId = table->m_id; DBUG_PRINT("enter", ("table=%u value=%llu op=%u", aTableId, opValue, op)); NdbTransaction* tConnection; NdbOperation* tOperation= 0; // Compiler warning if not initialized Uint64 tValue; NdbRecAttr* tRecAttrResult; - int result; - Uint64 ret; CHECK_STATUS_MACRO_ZERO; @@ -961,42 +1074,44 @@ Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) tValue = tRecAttrResult->u_64_value(); - theFirstTupleId[aTableId] = tValue - opValue; - theLastTupleId[aTableId] = tValue - 1; - ret = theFirstTupleId[aTableId]; + range.m_first_tuple_id = tValue - opValue; + range.m_last_tuple_id = tValue - 1; + opValue = range.m_first_tuple_id; // out break; case 1: - tOperation->updateTuple(); + // create on first use + tOperation->writeTuple(); tOperation->equal("SYSKEY_0", aTableId ); tOperation->setValue("NEXTID", opValue); if (tConnection->execute( Commit ) == -1 ) goto error_handler; - theFirstTupleId[aTableId] = ~(Uint64)0; - theLastTupleId[aTableId] = ~(Uint64)0; - ret = opValue; + range.reset(); break; case 2: tOperation->interpretedUpdateTuple(); tOperation->equal("SYSKEY_0", aTableId ); tOperation->load_const_u64(1, opValue); tOperation->read_attr("NEXTID", 2); + // compare NEXTID >= opValue tOperation->branch_le(2, 1, 0); tOperation->write_attr("NEXTID", 1); tOperation->interpret_exit_ok(); tOperation->def_label(0); tOperation->interpret_exit_nok(9999); - if ( (result = tConnection->execute( Commit )) == -1 ) - goto error_handler; - - if (result == 9999) - ret = ~(Uint64)0; + if (tConnection->execute( Commit ) == -1) + { + if (tConnection->theError.code != 9999) + goto error_handler; + } else { - theFirstTupleId[aTableId] = theLastTupleId[aTableId] = opValue - 1; - ret = opValue; + DBUG_PRINT("info", + ("Setting next auto increment value (db) to %llu", + (ulonglong)opValue)); + range.m_first_tuple_id = range.m_last_tuple_id = opValue - 1; } break; case 3: @@ -1005,7 +1120,7 @@ Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) tRecAttrResult = tOperation->getValue("NEXTID"); if (tConnection->execute( Commit ) == -1 ) goto error_handler; - ret = tRecAttrResult->u_64_value(); + opValue = tRecAttrResult->u_64_value(); // out break; default: goto error_handler; @@ -1013,7 +1128,7 @@ Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) this->closeTransaction(tConnection); - DBUG_RETURN(ret); + DBUG_RETURN(0); error_handler: theError.code = tConnection->theError.code; @@ -1023,7 +1138,7 @@ Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) theError.code, tConnection ? tConnection->theError.code : -1, tOperation ? tOperation->theError.code : -1)); - DBUG_RETURN(~(Uint64)0); + DBUG_RETURN(-1); } Uint32 @@ -1287,6 +1402,7 @@ NdbEventOperation* Ndb::createEventOperation(const char* eventName) int Ndb::dropEventOperation(NdbEventOperation* tOp) { DBUG_ENTER("Ndb::dropEventOperation"); + DBUG_PRINT("info", ("name: %s", tOp->getEvent()->getTable()->getName())); // remove it from list NdbEventOperationImpl *op= NdbEventBuffer::getEventOperationImpl(tOp); @@ -1297,6 +1413,8 @@ int Ndb::dropEventOperation(NdbEventOperation* tOp) else theImpl->m_ev_op= op->m_next; + DBUG_PRINT("info", ("first: %s", + theImpl->m_ev_op ? theImpl->m_ev_op->getEvent()->getTable()->getName() : "<empty>")); assert(theImpl->m_ev_op == 0 || theImpl->m_ev_op->m_prev == 0); theEventBuffer->dropEventOperation(tOp); @@ -1321,6 +1439,12 @@ Ndb::pollEvents(int aMillisecondNumber, Uint64 *latestGCI) return theEventBuffer->pollEvents(aMillisecondNumber, latestGCI); } +int +Ndb::flushIncompleteEvents(Uint64 gci) +{ + return theEventBuffer->flushIncompleteEvents(gci); +} + NdbEventOperation *Ndb::nextEvent() { return theEventBuffer->nextEvent(); diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp index 8eb0b37120d..42428c49e26 100644 --- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -79,18 +79,26 @@ is_ndb_blob_table(const NdbTableImpl* t) NdbColumnImpl::NdbColumnImpl() : NdbDictionary::Column(* this), m_attrId(-1), m_facade(this) { + DBUG_ENTER("NdbColumnImpl::NdbColumnImpl"); + DBUG_PRINT("info", ("this: %p", this)); init(); + DBUG_VOID_RETURN; } NdbColumnImpl::NdbColumnImpl(NdbDictionary::Column & f) : NdbDictionary::Column(* this), m_attrId(-1), m_facade(&f) { + DBUG_ENTER("NdbColumnImpl::NdbColumnImpl"); + DBUG_PRINT("info", ("this: %p", this)); init(); + DBUG_VOID_RETURN; } NdbColumnImpl& NdbColumnImpl::operator=(const NdbColumnImpl& col) { + DBUG_ENTER("NdbColumnImpl::operator="); + DBUG_PRINT("info", ("this: %p &col: %p", this, &col)); m_attrId = col.m_attrId; m_name = col.m_name; m_type = col.m_type; @@ -112,13 +120,14 @@ NdbColumnImpl::operator=(const NdbColumnImpl& col) if (col.m_blobTable == NULL) m_blobTable = NULL; else { - m_blobTable = new NdbTableImpl(); + if (m_blobTable == NULL) + m_blobTable = new NdbTableImpl(); m_blobTable->assign(*col.m_blobTable); } m_column_no = col.m_column_no; // Do not copy m_facade !! - return *this; + DBUG_RETURN(*this); } void @@ -261,15 +270,19 @@ NdbColumnImpl::init(Type t) NdbColumnImpl::~NdbColumnImpl() { + DBUG_ENTER("NdbColumnImpl::~NdbColumnImpl"); + DBUG_PRINT("info", ("this: %p", this)); if (m_blobTable != NULL) delete m_blobTable; m_blobTable = NULL; + DBUG_VOID_RETURN; } bool NdbColumnImpl::equal(const NdbColumnImpl& col) const { DBUG_ENTER("NdbColumnImpl::equal"); + DBUG_PRINT("info", ("this: %p &col: %p", this, &col)); if(strcmp(m_name.c_str(), col.m_name.c_str()) != 0){ DBUG_RETURN(false); } @@ -377,24 +390,33 @@ NdbTableImpl::NdbTableImpl() : NdbDictionary::Table(* this), NdbDictObjectImpl(NdbDictionary::Object::UserTable), m_facade(this) { + DBUG_ENTER("NdbTableImpl::NdbTableImpl"); + DBUG_PRINT("info", ("this: %p", this)); init(); + DBUG_VOID_RETURN; } NdbTableImpl::NdbTableImpl(NdbDictionary::Table & f) : NdbDictionary::Table(* this), NdbDictObjectImpl(NdbDictionary::Object::UserTable), m_facade(&f) { + DBUG_ENTER("NdbTableImpl::NdbTableImpl"); + DBUG_PRINT("info", ("this: %p", this)); init(); + DBUG_VOID_RETURN; } NdbTableImpl::~NdbTableImpl() { + DBUG_ENTER("NdbTableImpl::~NdbTableImpl"); + DBUG_PRINT("info", ("this: %p", this)); if (m_index != 0) { delete m_index; m_index = 0; } for (unsigned i = 0; i < m_columns.size(); i++) - delete m_columns[i]; + delete m_columns[i]; + DBUG_VOID_RETURN; } void @@ -636,6 +658,8 @@ NdbTableImpl::equal(const NdbTableImpl& obj) const void NdbTableImpl::assign(const NdbTableImpl& org) { + DBUG_ENTER("NdbColumnImpl::assign"); + DBUG_PRINT("info", ("this: %p &org: %p", this, &org)); /* m_changeMask intentionally not copied */ m_primaryTableId = org.m_primaryTableId; m_internalName.assign(org.m_internalName); @@ -662,7 +686,14 @@ NdbTableImpl::assign(const NdbTableImpl& org) m_columnHashMask, m_columnHash, m_hashValueMask, m_hashpointerValue is state calculated by computeAggregates and buildColumnHash */ - for(unsigned i = 0; i<org.m_columns.size(); i++){ + unsigned i; + for(i = 0; i < m_columns.size(); i++) + { + delete m_columns[i]; + } + m_columns.clear(); + for(i = 0; i < org.m_columns.size(); i++) + { NdbColumnImpl * col = new NdbColumnImpl(); const NdbColumnImpl * iorg = org.m_columns[i]; (* col) = (* iorg); @@ -702,6 +733,7 @@ NdbTableImpl::assign(const NdbTableImpl& org) m_tablespace_name = org.m_tablespace_name; m_tablespace_id= org.m_tablespace_id; m_tablespace_version = org.m_tablespace_version; + DBUG_VOID_RETURN; } void NdbTableImpl::setName(const char * name) @@ -1085,14 +1117,20 @@ NdbEventImpl::NdbEventImpl() : NdbDictionary::Event(* this), NdbDictObjectImpl(NdbDictionary::Object::TypeUndefined), m_facade(this) { + DBUG_ENTER("NdbEventImpl::NdbEventImpl"); + DBUG_PRINT("info", ("this: %p", this)); init(); + DBUG_VOID_RETURN; } NdbEventImpl::NdbEventImpl(NdbDictionary::Event & f) : NdbDictionary::Event(* this), NdbDictObjectImpl(NdbDictionary::Object::TypeUndefined), m_facade(&f) { + DBUG_ENTER("NdbEventImpl::NdbEventImpl"); + DBUG_PRINT("info", ("this: %p", this)); init(); + DBUG_VOID_RETURN; } void NdbEventImpl::init() @@ -1108,10 +1146,13 @@ void NdbEventImpl::init() NdbEventImpl::~NdbEventImpl() { + DBUG_ENTER("NdbEventImpl::~NdbEventImpl"); + DBUG_PRINT("info", ("this: %p", this)); for (unsigned i = 0; i < m_columns.size(); i++) delete m_columns[i]; if (m_tableImpl) delete m_tableImpl; + DBUG_VOID_RETURN; } void NdbEventImpl::setName(const char * name) @@ -1134,11 +1175,14 @@ NdbEventImpl::setTable(const NdbDictionary::Table& table) void NdbEventImpl::setTable(NdbTableImpl *tableImpl) { + DBUG_ENTER("NdbEventImpl::setTable"); + DBUG_PRINT("info", ("this: %p tableImpl: %p", this, tableImpl)); DBUG_ASSERT(tableImpl->m_status != NdbDictionary::Object::Invalid); if (!m_tableImpl) m_tableImpl = new NdbTableImpl(); // Copy table, since event might be accessed from different threads m_tableImpl->assign(*tableImpl); + DBUG_VOID_RETURN; } const NdbDictionary::Table * @@ -1343,9 +1387,6 @@ NdbDictionaryImpl::putTable(NdbTableImpl *impl) Ndb_local_table_info::create(impl, m_local_table_data_size); m_localHash.put(impl->m_internalName.c_str(), info); - - m_ndb.theFirstTupleId[impl->getTableId()] = ~0; - m_ndb.theLastTupleId[impl->getTableId()] = ~0; } int @@ -2205,11 +2246,11 @@ NdbDictionaryImpl::createTable(NdbTableImpl &t) } if (autoIncrement) { // XXX unlikely race condition - t.m_id may no longer be same table - if (! m_ndb.setTupleIdInNdb(t.m_id, initialValue, false)) { - if (m_ndb.theError.code) - m_error.code = m_ndb.theError.code; - else - m_error.code = 4336; + // the tuple id range is not used on input + Ndb::TupleIdRange range; + if (m_ndb.setTupleIdInNdb(&t, range, initialValue, false) == -1) { + assert(m_ndb.theError.code != 0); + m_error.code = m_ndb.theError.code; delete t2; DBUG_RETURN(-1); } @@ -2958,63 +2999,6 @@ NdbDictionaryImpl::removeCachedObject(NdbTableImpl & impl) DBUG_RETURN(0); } -/***************************************************************** - * Get index info - */ -NdbIndexImpl* -NdbDictionaryImpl::getIndexImpl(const char * externalName, - const BaseString& internalName) -{ - ASSERT_NOT_MYSQLD; - Ndb_local_table_info * info = get_local_table_info(internalName); - if(info == 0){ - m_error.code = 4243; - return 0; - } - NdbTableImpl * tab = info->m_table_impl; - - if(tab->m_indexType == NdbDictionary::Object::TypeUndefined) - { - // Not an index - m_error.code = 4243; - return 0; - } - - NdbTableImpl* prim = getTable(tab->m_primaryTable.c_str()); - if(prim == 0){ - m_error.code = 4243; - return 0; - } - - return getIndexImpl(externalName, internalName, *tab, *prim); -} - -NdbIndexImpl* -NdbDictionaryImpl::getIndexImpl(const char * externalName, - const BaseString& internalName, - NdbTableImpl &tab, - NdbTableImpl &prim) -{ - DBUG_ENTER("NdbDictionaryImpl::getIndexImpl"); - DBUG_ASSERT(tab.m_indexType != NdbDictionary::Object::TypeUndefined); - /** - * Create index impl - */ - NdbIndexImpl* idx; - if(NdbDictInterface::create_index_obj_from_table(&idx, &tab, &prim) == 0){ - idx->m_table = &tab; - idx->m_externalName.assign(externalName); - idx->m_internalName.assign(internalName); - idx->m_table_id = prim.getObjectId(); - idx->m_table_version = prim.getObjectVersion(); - // TODO Assign idx to tab->m_index - // Don't do it right now since assign can't asign a table with index - // tab->m_index = idx; - DBUG_RETURN(idx); - } - DBUG_RETURN(0); -} - int NdbDictInterface::create_index_obj_from_table(NdbIndexImpl** dst, NdbTableImpl* tab, @@ -3072,6 +3056,9 @@ NdbDictInterface::create_index_obj_from_table(NdbIndexImpl** dst, tab->m_columns[i]->m_distributionKey = 0; } + idx->m_table_id = prim->getObjectId(); + idx->m_table_version = prim->getObjectVersion(); + * dst = idx; DBUG_PRINT("exit", ("m_id: %d m_version: %d", idx->m_id, idx->m_version)); DBUG_RETURN(0); @@ -3681,9 +3668,12 @@ NdbDictionaryImpl::getEvent(const char * eventName, NdbTableImpl* tab) DBUG_RETURN(NULL); } } + ev->setTable(tab); + releaseTableGlobal(*tab, 0); } - - ev->setTable(tab); + else + ev->setTable(tab); + tab = 0; ev->setTable(m_ndb.externalizeTableName(ev->getTableName())); // get the columns from the attrListBitmask @@ -3944,6 +3934,7 @@ NdbDictionaryImpl::dropBlobEvents(const NdbEventImpl& evnt) if (blob_evnt == NULL) continue; (void)dropEvent(*blob_evnt); + delete blob_evnt; } } else { // loop over MAX_ATTRIBUTES_IN_TABLE ... diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp index 5a7a1ebb0ab..9b63acf6d36 100644 --- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp @@ -617,6 +617,7 @@ public: get_local_table_info(const BaseString& internalTableName); NdbIndexImpl * getIndex(const char * indexName, const char * tableName); + NdbIndexImpl * getIndex(const char * indexName, const NdbTableImpl& prim); NdbEventImpl * getEvent(const char * eventName, NdbTableImpl* = NULL); NdbEventImpl * getBlobEvent(const NdbEventImpl& ev, uint col_no); NdbEventImpl * getEventImpl(const char * internalName); @@ -950,59 +951,42 @@ NdbDictionaryImpl::get_local_table_info(const BaseString& internalTableName) if (info) { m_localHash.put(internalTableName.c_str(), info); - m_ndb.theFirstTupleId[tab->getTableId()] = ~0; - m_ndb.theLastTupleId[tab->getTableId()] = ~0; } } } DBUG_RETURN(info); // autoincrement already initialized } -class InitIndexGlobal : public GlobalCacheInitObject +class InitIndex : public GlobalCacheInitObject { public: const char *m_index_name; - NdbTableImpl &m_prim; + const NdbTableImpl &m_prim; - InitIndexGlobal(NdbDictionaryImpl *dict, - const BaseString &internal_indexname, - const char *index_name, - NdbTableImpl &prim) : - GlobalCacheInitObject(dict, internal_indexname), + InitIndex(const BaseString &internal_indexname, + const char *index_name, + const NdbTableImpl &prim) : + GlobalCacheInitObject(0, internal_indexname), m_index_name(index_name), m_prim(prim) - {} - int init(NdbTableImpl &tab) const - { - tab.m_index= m_dict->getIndexImpl(m_index_name, m_name, tab, m_prim); - if (tab.m_index == 0) - return 1; - tab.m_index->m_table= &tab; - return 0; - } -}; - -class InitIndex : public GlobalCacheInitObject -{ -public: - const char *m_index_name; - - InitIndex(NdbDictionaryImpl *dict, - const BaseString &internal_indexname, - const char *index_name) : - GlobalCacheInitObject(dict, internal_indexname), - m_index_name(index_name) - {} - int init(NdbTableImpl &tab) const - { - DBUG_ASSERT(tab.m_index == 0); - tab.m_index= m_dict->getIndexImpl(m_index_name, m_name); - if (tab.m_index) + {} + + int init(NdbTableImpl &tab) const { + DBUG_ENTER("InitIndex::init"); + DBUG_ASSERT(tab.m_indexType != NdbDictionary::Object::TypeUndefined); + /** + * Create index impl + */ + NdbIndexImpl* idx; + if(NdbDictInterface::create_index_obj_from_table(&idx, &tab, &m_prim) == 0) { - tab.m_index->m_table= &tab; - return 0; + idx->m_table = &tab; + idx->m_externalName.assign(m_index_name); + idx->m_internalName.assign(m_name); + tab.m_index = idx; + DBUG_RETURN(0); } - return 1; + DBUG_RETURN(1); } }; @@ -1019,14 +1003,14 @@ NdbDictionaryImpl::getIndexGlobal(const char * index_name, while (retry) { NdbTableImpl *tab= - fetchGlobalTableImplRef(InitIndexGlobal(this, internal_indexname, - index_name, ndbtab)); + fetchGlobalTableImplRef(InitIndex(internal_indexname, + index_name, ndbtab)); if (tab) { // tab->m_index sould be set. otherwise tab == 0 NdbIndexImpl *idx= tab->m_index; - if (idx->m_table_id != ndbtab.getObjectId() || - idx->m_table_version != ndbtab.getObjectVersion()) + if (idx->m_table_id != (unsigned)ndbtab.getObjectId() || + idx->m_table_version != (unsigned)ndbtab.getObjectVersion()) { releaseIndexGlobal(*idx, 1); retry--; @@ -1067,41 +1051,54 @@ NdbIndexImpl * NdbDictionaryImpl::getIndex(const char * index_name, const char * table_name) { - while (table_name || m_ndb.usingFullyQualifiedNames()) + if (table_name == 0) { - const BaseString internal_indexname( - (table_name) - ? - m_ndb.internalize_index_name(getTable(table_name), index_name) - : - m_ndb.internalize_table_name(index_name)); // Index is also a table - - if (internal_indexname.length()) - { - Ndb_local_table_info *info= m_localHash.get(internal_indexname.c_str()); - NdbTableImpl *tab; - if (info == 0) - { - tab= fetchGlobalTableImplRef(InitIndex(this, internal_indexname, - index_name)); - if (tab) - { - info= Ndb_local_table_info::create(tab, 0); - if (info) - m_localHash.put(internal_indexname.c_str(), info); - else - break; - } - else - break; - } - else - tab= info->m_table_impl; - return tab->m_index; - } - break; + assert(0); + m_error.code= 4243; + return 0; + } + + + NdbTableImpl* prim = getTable(table_name); + if (prim == 0) + { + m_error.code= 4243; + return 0; } + return getIndex(index_name, *prim); +} + +inline +NdbIndexImpl * +NdbDictionaryImpl::getIndex(const char* index_name, + const NdbTableImpl& prim) +{ + + const BaseString + internal_indexname(m_ndb.internalize_index_name(&prim, index_name)); + + Ndb_local_table_info *info= m_localHash.get(internal_indexname.c_str()); + NdbTableImpl *tab; + if (info == 0) + { + tab= fetchGlobalTableImplRef(InitIndex(internal_indexname, + index_name, + prim)); + if (!tab) + goto err; + + info= Ndb_local_table_info::create(tab, 0); + if (!info) + goto err; + m_localHash.put(internal_indexname.c_str(), info); + } + else + tab= info->m_table_impl; + + return tab->m_index; + +err: m_error.code= 4243; return 0; } diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp index 98a8899af4a..a5fbd84e5b0 100644 --- a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp +++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp @@ -70,21 +70,6 @@ print_std(const SubTableData * sdata, LinearSectionPtr ptr[3]) * */ -//#define EVENT_DEBUG -#ifdef EVENT_DEBUG -#define DBUG_ENTER_EVENT(A) DBUG_ENTER(A) -#define DBUG_RETURN_EVENT(A) DBUG_RETURN(A) -#define DBUG_VOID_RETURN_EVENT DBUG_VOID_RETURN -#define DBUG_PRINT_EVENT(A,B) DBUG_PRINT(A,B) -#define DBUG_DUMP_EVENT(A,B,C) DBUG_DUMP(A,B,C) -#else -#define DBUG_ENTER_EVENT(A) -#define DBUG_RETURN_EVENT(A) return(A) -#define DBUG_VOID_RETURN_EVENT return -#define DBUG_PRINT_EVENT(A,B) -#define DBUG_DUMP_EVENT(A,B,C) -#endif - // todo handle several ndb objects // todo free allocated data when closing NdbEventBuffer @@ -168,11 +153,14 @@ NdbEventOperationImpl::init(NdbEventImpl& evnt) m_state= EO_CREATED; + m_node_bit_mask.clear(); #ifdef ndb_event_stores_merge_events_flag m_mergeEvents = m_eventImpl->m_mergeEvents; #else - m_mergeEvents = false; + m_mergeEvents = false; #endif + m_ref_count = 0; + DBUG_PRINT("info", ("m_ref_count = 0 for op: %p", this)); m_has_error= 0; @@ -189,6 +177,17 @@ NdbEventOperationImpl::~NdbEventOperationImpl() // m_bufferHandle->dropSubscribeEvent(m_bufferId); ; // ToDo? We should send stop signal here + if (theMainOp == NULL) + { + NdbEventOperationImpl* tBlobOp = theBlobOpList; + while (tBlobOp != NULL) + { + NdbEventOperationImpl *op = tBlobOp; + tBlobOp = tBlobOp->m_next; + delete op; + } + } + m_ndb->theImpl->theNdbObjectIdMap.unmap(m_oid, this); DBUG_PRINT("exit",("this: %p/%p oid: %u main: %p", this, m_facade, m_oid, theMainOp)); @@ -534,7 +533,11 @@ NdbEventOperationImpl::execute_nolock() } } if (r == 0) + { + m_ref_count++; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", m_ref_count, this)); DBUG_RETURN(0); + } } //Error m_state= EO_ERROR; @@ -661,80 +664,79 @@ NdbEventOperationImpl::execSUB_TABLE_DATA(NdbApiSignal * signal, int NdbEventOperationImpl::receive_event() { - DBUG_ENTER_EVENT("NdbEventOperationImpl::receive_event"); - Uint32 operation= (Uint32)m_data_item->sdata->operation; - DBUG_PRINT_EVENT("info",("sdata->operation %u",operation)); - - if (operation == NdbDictionary::Event::_TE_ALTER) - { - // Parse the new table definition and - // create a table object - NdbDictionary::Dictionary *myDict = m_ndb->getDictionary(); - NdbDictionaryImpl *dict = & NdbDictionaryImpl::getImpl(*myDict); - NdbError error; - NdbDictInterface dif(error); - NdbTableImpl *at; - m_change_mask = m_data_item->sdata->changeMask; - error.code = dif.parseTableInfo(&at, - (Uint32*)m_buffer.get_data(), - m_buffer.length() / 4, - true); - m_buffer.clear(); - if (at) - at->buildColumnHash(); - else - { - DBUG_PRINT_EVENT("info", ("Failed to parse DictTabInfo error %u", - error.code)); - DBUG_RETURN_EVENT(1); - } - - NdbTableImpl *tmp_table_impl= m_eventImpl->m_tableImpl; - m_eventImpl->m_tableImpl = at; - - DBUG_PRINT("info", ("switching table impl 0x%x -> 0x%x", - tmp_table_impl, at)); - - // change the rec attrs to refer to the new table object - int i; - for (i = 0; i < 2; i++) + if (unlikely(operation >= NdbDictionary::Event::_TE_FIRST_NON_DATA_EVENT)) + { + DBUG_ENTER("NdbEventOperationImpl::receive_event"); + DBUG_PRINT("info",("sdata->operation %u this: %p", operation, this)); + if (operation == NdbDictionary::Event::_TE_ALTER) { - NdbRecAttr *p = theFirstPkAttrs[i]; - while (p) + // Parse the new table definition and + // create a table object + NdbDictionary::Dictionary *myDict = m_ndb->getDictionary(); + NdbDictionaryImpl *dict = & NdbDictionaryImpl::getImpl(*myDict); + NdbError error; + NdbDictInterface dif(error); + NdbTableImpl *at; + m_change_mask = m_data_item->sdata->changeMask; + error.code = dif.parseTableInfo(&at, + (Uint32*)m_buffer.get_data(), + m_buffer.length() / 4, + true); + m_buffer.clear(); + if (unlikely(!at)) { - int no = p->getColumn()->getColumnNo(); - NdbColumnImpl *tAttrInfo = at->getColumn(no); - DBUG_PRINT("info", ("rec_attr: 0x%x " - "switching column impl 0x%x -> 0x%x", - p, p->m_column, tAttrInfo)); - p->m_column = tAttrInfo; - p = p->next(); + DBUG_PRINT("info", ("Failed to parse DictTabInfo error %u", + error.code)); + ndbout_c("Failed to parse DictTabInfo error %u", error.code); + DBUG_RETURN(1); } - } - for (i = 0; i < 2; i++) - { - NdbRecAttr *p = theFirstDataAttrs[i]; - while (p) + at->buildColumnHash(); + + NdbTableImpl *tmp_table_impl= m_eventImpl->m_tableImpl; + m_eventImpl->m_tableImpl = at; + + DBUG_PRINT("info", ("switching table impl 0x%x -> 0x%x", + tmp_table_impl, at)); + + // change the rec attrs to refer to the new table object + int i; + for (i = 0; i < 2; i++) { - int no = p->getColumn()->getColumnNo(); - NdbColumnImpl *tAttrInfo = at->getColumn(no); - DBUG_PRINT("info", ("rec_attr: 0x%x " - "switching column impl 0x%x -> 0x%x", - p, p->m_column, tAttrInfo)); - p->m_column = tAttrInfo; - p = p->next(); + NdbRecAttr *p = theFirstPkAttrs[i]; + while (p) + { + int no = p->getColumn()->getColumnNo(); + NdbColumnImpl *tAttrInfo = at->getColumn(no); + DBUG_PRINT("info", ("rec_attr: 0x%x " + "switching column impl 0x%x -> 0x%x", + p, p->m_column, tAttrInfo)); + p->m_column = tAttrInfo; + p = p->next(); + } + } + for (i = 0; i < 2; i++) + { + NdbRecAttr *p = theFirstDataAttrs[i]; + while (p) + { + int no = p->getColumn()->getColumnNo(); + NdbColumnImpl *tAttrInfo = at->getColumn(no); + DBUG_PRINT("info", ("rec_attr: 0x%x " + "switching column impl 0x%x -> 0x%x", + p, p->m_column, tAttrInfo)); + p->m_column = tAttrInfo; + p = p->next(); + } } + if (tmp_table_impl) + delete tmp_table_impl; } - if (tmp_table_impl) - delete tmp_table_impl; - } - - if (unlikely(operation >= NdbDictionary::Event::_TE_FIRST_NON_DATA_EVENT)) - { - DBUG_RETURN_EVENT(1); + DBUG_RETURN(1); } + DBUG_ENTER_EVENT("NdbEventOperationImpl::receive_event"); + DBUG_PRINT_EVENT("info",("sdata->operation %u this: %p", operation, this)); // now move the data into the RecAttrs int is_update= operation == NdbDictionary::Event::_TE_UPDATE; @@ -973,7 +975,15 @@ NdbEventBuffer::~NdbEventBuffer() delete op->m_facade; } - for (unsigned j= 0; j < m_allocated_data.size(); j++) + unsigned j; + Uint32 sz= m_active_gci.size(); + Gci_container* array = (Gci_container*)m_active_gci.getBase(); + for(j = 0; j < sz; j++) + { + array[j].~Gci_container(); + } + + for (j= 0; j < m_allocated_data.size(); j++) { unsigned sz= m_allocated_data[j]->sz; EventBufData *data= m_allocated_data[j]->data; @@ -1085,6 +1095,33 @@ NdbEventBuffer::pollEvents(int aMillisecondNumber, Uint64 *latestGCI) return ret; } +int +NdbEventBuffer::flushIncompleteEvents(Uint64 gci) +{ + /** + * Find min complete gci + */ + Uint32 i; + Uint32 sz= m_active_gci.size(); + Gci_container* array = (Gci_container*)m_active_gci.getBase(); + for(i = 0; i < sz; i++) + { + Gci_container* tmp = array + i; + if (tmp->m_gci && tmp->m_gci < gci) + { + // we have found an old not-completed gci, remove it + ndbout_c("ndb: flushing incomplete epoch %lld (<%lld)", tmp->m_gci, gci); + if(!tmp->m_data.is_empty()) + { + free_list(tmp->m_data); + } + tmp->~Gci_container(); + bzero(tmp, sizeof(Gci_container)); + } + } + return 0; +} + NdbEventOperation * NdbEventBuffer::nextEvent() { @@ -1153,7 +1190,10 @@ NdbEventBuffer::nextEvent() } EventBufData_list::Gci_ops *gci_ops = m_available_data.first_gci_ops(); while (gci_ops && op->getGCI() > gci_ops->m_gci) + { + deleteUsedEventOperations(); gci_ops = m_available_data.next_gci_ops(); + } assert(gci_ops && (op->getGCI() == gci_ops->m_gci)); DBUG_RETURN_EVENT(op->m_facade); } @@ -1173,7 +1213,10 @@ NdbEventBuffer::nextEvent() // free all "per gci unique" collected operations EventBufData_list::Gci_ops *gci_ops = m_available_data.first_gci_ops(); while (gci_ops) + { + deleteUsedEventOperations(); gci_ops = m_available_data.next_gci_ops(); + } DBUG_RETURN_EVENT(0); } @@ -1187,31 +1230,37 @@ NdbEventBuffer::getGCIEventOperations(Uint32* iter, Uint32* event_types) EventBufData_list::Gci_op g = gci_ops->m_gci_op_list[(*iter)++]; if (event_types != NULL) *event_types = g.event_types; - DBUG_PRINT("info", ("gci: %d", (unsigned)gci_ops->m_gci)); + DBUG_PRINT("info", ("gci: %d g.op: %x g.event_types: %x", + (unsigned)gci_ops->m_gci, g.op, g.event_types)); DBUG_RETURN(g.op); } DBUG_RETURN(NULL); } void -NdbEventBuffer::lock() +NdbEventBuffer::deleteUsedEventOperations() { - NdbMutex_Lock(m_mutex); -} -void -NdbEventBuffer::unlock() -{ - NdbMutex_Unlock(m_mutex); -} -void -NdbEventBuffer::add_drop_lock() -{ - NdbMutex_Lock(p_add_drop_mutex); -} -void -NdbEventBuffer::add_drop_unlock() -{ - NdbMutex_Unlock(p_add_drop_mutex); + Uint32 iter= 0; + const NdbEventOperation *op_f; + while ((op_f= getGCIEventOperations(&iter, NULL)) != NULL) + { + NdbEventOperationImpl *op = &op_f->m_impl; + DBUG_ASSERT(op->m_ref_count > 0); + op->m_ref_count--; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", op->m_ref_count, op)); + if (op->m_ref_count == 0) + { + DBUG_PRINT("info", ("deleting op: %p", op)); + DBUG_ASSERT(op->m_node_bit_mask.isclear()); + if (op->m_next) + op->m_next->m_prev = op->m_prev; + if (op->m_prev) + op->m_prev->m_next = op->m_next; + else + m_dropped_ev_op = op->m_next; + delete op->m_facade; + } + } } static @@ -1465,6 +1514,10 @@ NdbEventBuffer::complete_outof_order_gcis() void NdbEventBuffer::report_node_failure(Uint32 node_id) { + NdbEventOperation* op= m_ndb->getEventOperation(0); + if (op == 0) + return; + DBUG_ENTER("NdbEventBuffer::report_node_failure"); SubTableData data; LinearSectionPtr ptr[3]; @@ -1476,15 +1529,24 @@ NdbEventBuffer::report_node_failure(Uint32 node_id) data.req_nodeid = (Uint8)node_id; data.ndbd_nodeid = (Uint8)node_id; data.logType = SubTableData::LOG; + data.gci = m_latestGCI + 1; /** * Insert this event for each operation */ - NdbEventOperation* op= 0; - while((op = m_ndb->getEventOperation(op))) { - NdbEventOperationImpl* impl= &op->m_impl; - data.senderData = impl->m_oid; - insertDataL(impl, &data, ptr); + // no need to lock()/unlock(), receive thread calls this + NdbEventOperationImpl* impl = &op->m_impl; + do if (!impl->m_node_bit_mask.isclear()) + { + data.senderData = impl->m_oid; + insertDataL(impl, &data, ptr); + } while((impl = impl->m_next)); + for (impl = m_dropped_ev_op; impl; impl = impl->m_next) + if (!impl->m_node_bit_mask.isclear()) + { + data.senderData = impl->m_oid; + insertDataL(impl, &data, ptr); + } } DBUG_VOID_RETURN; } @@ -1492,8 +1554,11 @@ NdbEventBuffer::report_node_failure(Uint32 node_id) void NdbEventBuffer::completeClusterFailed() { - DBUG_ENTER("NdbEventBuffer::completeClusterFailed"); + NdbEventOperation* op= m_ndb->getEventOperation(0); + if (op == 0) + return; + DBUG_ENTER("NdbEventBuffer::completeClusterFailed"); SubTableData data; LinearSectionPtr ptr[3]; bzero(&data, sizeof(data)); @@ -1502,73 +1567,79 @@ NdbEventBuffer::completeClusterFailed() data.tableId = ~0; data.operation = NdbDictionary::Event::_TE_CLUSTER_FAILURE; data.logType = SubTableData::LOG; - + data.gci = m_latestGCI + 1; + /** - * Find min not completed GCI + * Insert this event for each operation */ - Uint32 sz= m_active_gci.size(); - Uint64 gci= ~0; - Gci_container* bucket = 0; - Gci_container* array = (Gci_container*)m_active_gci.getBase(); - for(Uint32 i = 0; i<sz; i++) { - if(array[i].m_gcp_complete_rep_count && array[i].m_gci < gci) + // no need to lock()/unlock(), receive thread calls this + NdbEventOperationImpl* impl = &op->m_impl; + do if (!impl->m_node_bit_mask.isclear()) { - bucket= array + i; - gci = bucket->m_gci; - } - } - - if(bucket == 0) - { - /** - * Did not find any not completed GCI's - * lets fake one... - */ - gci = m_latestGCI + 1; - bucket = array + ( gci & ACTIVE_GCI_MASK ); - bucket->m_gcp_complete_rep_count = 1; + data.senderData = impl->m_oid; + insertDataL(impl, &data, ptr); + } while((impl = impl->m_next)); + for (impl = m_dropped_ev_op; impl; impl = impl->m_next) + if (!impl->m_node_bit_mask.isclear()) + { + data.senderData = impl->m_oid; + insertDataL(impl, &data, ptr); + } } - const Uint32 cnt= bucket->m_gcp_complete_rep_count = 1; - /** - * Release all GCI's + * Release all GCI's with m_gci > gci */ - for(Uint32 i = 0; i<sz; i++) + Uint32 i; + Uint32 sz= m_active_gci.size(); + Uint64 gci= data.gci; + Gci_container* bucket = 0; + Gci_container* array = (Gci_container*)m_active_gci.getBase(); + for(i = 0; i < sz; i++) { Gci_container* tmp = array + i; - if(!tmp->m_data.is_empty()) + if (tmp->m_gci > gci) { - free_list(tmp->m_data); -#if 0 - m_free_data_count++; - EventBufData* loop= tmp->m_head; - while(loop != tmp->m_tail) + if(!tmp->m_data.is_empty()) { - m_free_data_count++; - loop = loop->m_next; + free_list(tmp->m_data); } -#endif + tmp->~Gci_container(); + bzero(tmp, sizeof(Gci_container)); + } + else if (tmp->m_gcp_complete_rep_count) + { + if (tmp->m_gci == gci) + { + bucket= tmp; + continue; + } + // we have found an old not-completed gci + // something is wrong, assert in debug, but try so salvage + // in release + ndbout_c("out of order bucket detected at cluster disconnect, " + "data.gci: %u. tmp->m_gci: %u", + (unsigned)data.gci, (unsigned)tmp->m_gci); + assert(false); + if(!tmp->m_data.is_empty()) + { + free_list(tmp->m_data); + } + tmp->~Gci_container(); + bzero(tmp, sizeof(Gci_container)); } - bzero(tmp, sizeof(Gci_container)); } - - bucket->m_gci = gci; - bucket->m_gcp_complete_rep_count = cnt; - - data.gci = gci; - - /** - * Insert this event for each operation - */ - NdbEventOperation* op= 0; - while((op = m_ndb->getEventOperation(op))) + + if (bucket == 0) { - NdbEventOperationImpl* impl= &op->m_impl; - data.senderData = impl->m_oid; - insertDataL(impl, &data, ptr); + // no bucket to complete + DBUG_VOID_RETURN; } + + const Uint32 cnt= bucket->m_gcp_complete_rep_count = 1; + bucket->m_gci = gci; + bucket->m_gcp_complete_rep_count = cnt; /** * And finally complete this GCI @@ -1594,6 +1665,40 @@ NdbEventBuffer::insertDataL(NdbEventOperationImpl *op, { DBUG_ENTER_EVENT("NdbEventBuffer::insertDataL"); Uint64 gci= sdata->gci; + const bool is_data_event = + sdata->operation < NdbDictionary::Event::_TE_FIRST_NON_DATA_EVENT; + + if (!is_data_event) + { + switch (sdata->operation) + { + case NdbDictionary::Event::_TE_NODE_FAILURE: + op->m_node_bit_mask.clear(sdata->ndbd_nodeid); + break; + case NdbDictionary::Event::_TE_ACTIVE: + op->m_node_bit_mask.set(sdata->ndbd_nodeid); + // internal event, do not relay to user + DBUG_RETURN_EVENT(0); + break; + case NdbDictionary::Event::_TE_CLUSTER_FAILURE: + op->m_node_bit_mask.clear(); + DBUG_ASSERT(op->m_ref_count > 0); + op->m_ref_count--; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", op->m_ref_count, op)); + break; + case NdbDictionary::Event::_TE_STOP: + op->m_node_bit_mask.clear(sdata->ndbd_nodeid); + if (op->m_node_bit_mask.isclear()) + { + DBUG_ASSERT(op->m_ref_count > 0); + op->m_ref_count--; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", op->m_ref_count, op)); + } + break; + default: + break; + } + } if ( likely((Uint32)op->mi_type & (1 << (Uint32)sdata->operation)) ) { @@ -1614,8 +1719,6 @@ NdbEventBuffer::insertDataL(NdbEventOperationImpl *op, } const bool is_blob_event = (op->theMainOp != NULL); - const bool is_data_event = - sdata->operation < NdbDictionary::Event::_TE_FIRST_NON_DATA_EVENT; const bool use_hash = op->m_mergeEvents && is_data_event; if (! is_data_event && is_blob_event) @@ -2243,6 +2346,8 @@ void EventBufData_list::append_list(EventBufData_list *list, Uint64 gci) void EventBufData_list::add_gci_op(Gci_op g, bool del) { + DBUG_ENTER_EVENT("EventBufData_list::add_gci_op"); + DBUG_PRINT_EVENT("info", ("p.op: %p g.event_types: %x", g.op, g.event_types)); assert(g.op != NULL); Uint32 i; for (i = 0; i < m_gci_op_count; i++) { @@ -2262,19 +2367,33 @@ EventBufData_list::add_gci_op(Gci_op g, bool del) if (m_gci_op_alloc != 0) { Uint32 bytes = m_gci_op_alloc * sizeof(Gci_op); memcpy(m_gci_op_list, old_list, bytes); + DBUG_PRINT_EVENT("info", ("this: %p delete m_gci_op_list: %p", + this, old_list)); delete [] old_list; } + DBUG_PRINT_EVENT("info", ("this: %p new m_gci_op_list: %p", + this, m_gci_op_list)); m_gci_op_alloc = n; } assert(m_gci_op_count < m_gci_op_alloc); assert(! del); +#ifndef DBUG_OFF + i = m_gci_op_count; +#endif + g.op->m_ref_count++; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", g.op->m_ref_count, g.op)); m_gci_op_list[m_gci_op_count++] = g; } + DBUG_PRINT_EVENT("exit", ("m_gci_op_list[%u].event_types: %x", i, m_gci_op_list[i].event_types)); + DBUG_VOID_RETURN_EVENT; } void EventBufData_list::move_gci_ops(EventBufData_list *list, Uint64 gci) { + DBUG_ENTER_EVENT("EventBufData_list::move_gci_ops"); + DBUG_PRINT_EVENT("info", ("this: %p list: %p gci: %llu", + this, list, gci)); assert(!m_is_not_multi_list); if (!list->m_is_not_multi_list) { @@ -2290,6 +2409,8 @@ EventBufData_list::move_gci_ops(EventBufData_list *list, Uint64 gci) } { Gci_ops *new_gci_ops = new Gci_ops; + DBUG_PRINT_EVENT("info", ("this: %p m_gci_op_list: %p", + new_gci_ops, list->m_gci_op_list)); if (m_gci_ops_list_tail) m_gci_ops_list_tail->m_next = new_gci_ops; else @@ -2308,6 +2429,7 @@ end: list->m_gci_op_list = 0; list->m_gci_ops_list_tail = 0; list->m_gci_op_alloc = 0; + DBUG_VOID_RETURN_EVENT; } NdbEventOperation* @@ -2326,6 +2448,9 @@ NdbEventBuffer::createEventOperation(const char* eventName, delete tOp; DBUG_RETURN(NULL); } + getEventOperationImpl(tOp)->m_ref_count = 1; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", + getEventOperationImpl(tOp)->m_ref_count, getEventOperationImpl(tOp))); DBUG_RETURN(tOp); } @@ -2351,16 +2476,10 @@ NdbEventBuffer::createEventOperation(NdbEventImpl& evnt, void NdbEventBuffer::dropEventOperation(NdbEventOperation* tOp) { + DBUG_ENTER("NdbEventBuffer::dropEventOperation"); NdbEventOperationImpl* op= getEventOperationImpl(tOp); op->stop(); - - op->m_next= m_dropped_ev_op; - op->m_prev= 0; - if (m_dropped_ev_op) - m_dropped_ev_op->m_prev= op; - m_dropped_ev_op= op; - // stop blob event ops if (op->theMainOp == NULL) { @@ -2380,11 +2499,24 @@ NdbEventBuffer::dropEventOperation(NdbEventOperation* tOp) } } - // ToDo, take care of these to be deleted at the - // appropriate time, after we are sure that there - // are _no_ more events coming - - // delete tOp; + DBUG_ASSERT(op->m_ref_count > 0); + op->m_ref_count--; + DBUG_PRINT("info", ("m_ref_count: %u for op: %p", op->m_ref_count, op)); + if (op->m_ref_count == 0) + { + DBUG_PRINT("info", ("deleting op: %p", op)); + DBUG_ASSERT(op->m_node_bit_mask.isclear()); + delete op->m_facade; + } + else + { + op->m_next= m_dropped_ev_op; + op->m_prev= 0; + if (m_dropped_ev_op) + m_dropped_ev_op->m_prev= op; + m_dropped_ev_op= op; + } + DBUG_VOID_RETURN; } void diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp index 8d413cc8d14..bcae650bf44 100644 --- a/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp @@ -25,6 +25,20 @@ #include <UtilBuffer.hpp> #define NDB_EVENT_OP_MAGIC_NUMBER 0xA9F301B4 +//#define EVENT_DEBUG +#ifdef EVENT_DEBUG +#define DBUG_ENTER_EVENT(A) DBUG_ENTER(A) +#define DBUG_RETURN_EVENT(A) DBUG_RETURN(A) +#define DBUG_VOID_RETURN_EVENT DBUG_VOID_RETURN +#define DBUG_PRINT_EVENT(A,B) DBUG_PRINT(A,B) +#define DBUG_DUMP_EVENT(A,B,C) DBUG_DUMP(A,B,C) +#else +#define DBUG_ENTER_EVENT(A) +#define DBUG_RETURN_EVENT(A) return(A) +#define DBUG_VOID_RETURN_EVENT return +#define DBUG_PRINT_EVENT(A,B) +#define DBUG_DUMP_EVENT(A,B,C) +#endif class NdbEventOperationImpl; @@ -149,19 +163,29 @@ EventBufData_list::EventBufData_list() m_gci_ops_list_tail(0), m_gci_op_alloc(0) { + DBUG_ENTER_EVENT("EventBufData_list::EventBufData_list"); + DBUG_PRINT_EVENT("info", ("this: %p", this)); + DBUG_VOID_RETURN_EVENT; } inline EventBufData_list::~EventBufData_list() { + DBUG_ENTER_EVENT("EventBufData_list::~EventBufData_list"); + DBUG_PRINT_EVENT("info", ("this: %p m_is_not_multi_list: %u", + this, m_is_not_multi_list)); if (m_is_not_multi_list) + { + DBUG_PRINT_EVENT("info", ("delete m_gci_op_list: %p", m_gci_op_list)); delete [] m_gci_op_list; + } else { Gci_ops *op = first_gci_ops(); while (op) op = next_gci_ops(); } + DBUG_VOID_RETURN_EVENT; } inline @@ -223,7 +247,11 @@ EventBufData_list::next_gci_ops() Gci_ops *first = m_gci_ops_list; m_gci_ops_list = first->m_next; if (first->m_gci_op_list) + { + DBUG_PRINT_EVENT("info", ("this: %p delete m_gci_op_list: %p", + this, first->m_gci_op_list)); delete [] first->m_gci_op_list; + } delete first; if (m_gci_ops_list == 0) m_gci_ops_list_tail = 0; @@ -339,6 +367,8 @@ public: Uint32 m_eventId; Uint32 m_oid; + Bitmask<(unsigned int)_NDB_NODE_BITMASK_SIZE> m_node_bit_mask; + int m_ref_count; bool m_mergeEvents; EventBufData *m_data_item; @@ -378,10 +408,10 @@ public: void dropEventOperation(NdbEventOperation *); static NdbEventOperationImpl* getEventOperationImpl(NdbEventOperation* tOp); - void add_drop_lock(); - void add_drop_unlock(); - void lock(); - void unlock(); + void add_drop_lock() { NdbMutex_Lock(p_add_drop_mutex); } + void add_drop_unlock() { NdbMutex_Unlock(p_add_drop_mutex); } + void lock() { NdbMutex_Lock(m_mutex); } + void unlock() { NdbMutex_Unlock(m_mutex); } void add_op(); void remove_op(); @@ -402,9 +432,11 @@ public: Uint32 getEventId(int bufferId); int pollEvents(int aMillisecondNumber, Uint64 *latestGCI= 0); + int flushIncompleteEvents(Uint64 gci); NdbEventOperation *nextEvent(); NdbEventOperationImpl* getGCIEventOperations(Uint32* iter, Uint32* event_types); + void deleteUsedEventOperations(); NdbEventOperationImpl *move_data(); diff --git a/storage/ndb/src/ndbapi/Ndbinit.cpp b/storage/ndb/src/ndbapi/Ndbinit.cpp index e41380e6484..91567e99c01 100644 --- a/storage/ndb/src/ndbapi/Ndbinit.cpp +++ b/storage/ndb/src/ndbapi/Ndbinit.cpp @@ -100,10 +100,6 @@ void Ndb::setup(Ndb_cluster_connection *ndb_cluster_connection, theConnectionArray[i] = NULL; }//forg m_sys_tab_0 = NULL; - for (i = 0; i < 2048 ; i++) { - theFirstTupleId[i] = 0; - theLastTupleId[i] = 0; - }//for theImpl->m_dbname.assign(aDataBase); theImpl->m_schemaname.assign(aSchema); diff --git a/storage/ndb/src/ndbapi/SignalSender.cpp b/storage/ndb/src/ndbapi/SignalSender.cpp index a29fe68937b..0ecc98f5f29 100644 --- a/storage/ndb/src/ndbapi/SignalSender.cpp +++ b/storage/ndb/src/ndbapi/SignalSender.cpp @@ -75,7 +75,9 @@ SignalSender::SignalSender(TransporterFacade *facade) { m_cond = NdbCondition_Create(); theFacade = facade; + lock(); m_blockNo = theFacade->open(this, execSignal, execNodeStatus); + unlock(); assert(m_blockNo > 0); } diff --git a/storage/ndb/src/ndbapi/TransporterFacade.hpp b/storage/ndb/src/ndbapi/TransporterFacade.hpp index eb2f162da88..2d47a2febf8 100644 --- a/storage/ndb/src/ndbapi/TransporterFacade.hpp +++ b/storage/ndb/src/ndbapi/TransporterFacade.hpp @@ -319,6 +319,12 @@ TransporterFacade::unlock_mutex() #include "ClusterMgr.hpp" inline +unsigned Ndb_cluster_connection_impl::get_connect_count() const +{ + return m_transporter_facade->theClusterMgr->m_connect_count; +} + +inline bool TransporterFacade::check_send_size(Uint32 node_id, Uint32 send_size) { diff --git a/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp index 544f7424a82..a7e645f5100 100644 --- a/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp +++ b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp @@ -253,6 +253,12 @@ Ndb_cluster_connection::wait_until_ready(int timeout, } while (1); } +unsigned Ndb_cluster_connection::get_connect_count() const +{ + return m_impl.get_connect_count(); +} + + /* diff --git a/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp b/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp index db6e1f9e6ed..6f0df0403d9 100644 --- a/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp +++ b/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp @@ -40,6 +40,7 @@ class Ndb_cluster_connection_impl : public Ndb_cluster_connection void init_get_next_node(Ndb_cluster_connection_node_iter &iter); Uint32 get_next_node(Ndb_cluster_connection_node_iter &iter); + inline unsigned get_connect_count() const; private: friend class Ndb; friend class NdbImpl; diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c index 8185bf38c3e..c05924dacf8 100644 --- a/storage/ndb/src/ndbapi/ndberror.c +++ b/storage/ndb/src/ndbapi/ndberror.c @@ -600,7 +600,6 @@ ErrorBundle ErrorCodes[] = { { 4269, DMEC, IE, "No connection to ndb management server" }, { 4270, DMEC, IE, "Unknown blob error" }, { 4335, DMEC, AE, "Only one autoincrement column allowed per table. Having a table without primary key uses an autoincremented hidden key, i.e. a table without a primary key can not have an autoincremented column" }, - { 4336, DMEC, AE, "Auto-increment value set below current value" }, { 4271, DMEC, AE, "Invalid index object, not retrieved via getIndex()" }, { 4272, DMEC, AE, "Table definition has undefined column" }, { 4273, DMEC, IE, "No blob table in dict cache" }, diff --git a/storage/ndb/test/ndbapi/Makefile.am b/storage/ndb/test/ndbapi/Makefile.am index 9a1e1c5e9ea..b55acd2420d 100644 --- a/storage/ndb/test/ndbapi/Makefile.am +++ b/storage/ndb/test/ndbapi/Makefile.am @@ -24,6 +24,7 @@ testOIBasic \ testOperations \ testRestartGci \ testScan \ +testInterpreter \ testScanInterpreter \ testScanPerf \ testSystemRestart \ @@ -64,6 +65,7 @@ testOIBasic_SOURCES = testOIBasic.cpp testOperations_SOURCES = testOperations.cpp testRestartGci_SOURCES = testRestartGci.cpp testScan_SOURCES = testScan.cpp ScanFunctions.hpp +testInterpreter_SOURCES = testInterpreter.cpp testScanInterpreter_SOURCES = testScanInterpreter.cpp ScanFilter.hpp ScanInterpretTest.hpp testScanPerf_SOURCES = testScanPerf.cpp testSystemRestart_SOURCES = testSystemRestart.cpp diff --git a/storage/ndb/test/ndbapi/testDict.cpp b/storage/ndb/test/ndbapi/testDict.cpp index 7ade359342d..b015e16bae1 100644 --- a/storage/ndb/test/ndbapi/testDict.cpp +++ b/storage/ndb/test/ndbapi/testDict.cpp @@ -1139,9 +1139,13 @@ runCreateAutoincrementTable(NDBT_Context* ctx, NDBT_Step* step){ for (int i = 0; i < 16; i++) { - Uint64 value = myNdb->getAutoIncrementValue(tabname, 1); - - if (value != (startvalue+i)) { + Uint64 value; + if (myNdb->getAutoIncrementValue(tabname, value, 1) == -1) { + g_err << "getAutoIncrementValue failed on " << tabname << endl; + APIERROR(myNdb->getNdbError()); + return NDBT_FAILED; + } + else if (value != (startvalue+i)) { g_err << "value = " << value << " expected " << startvalue+i << endl;; APIERROR(myNdb->getNdbError()); // ret = NDBT_FAILED; diff --git a/storage/ndb/test/ndbapi/testInterpreter.cpp b/storage/ndb/test/ndbapi/testInterpreter.cpp index 0baba33d2b2..5d930d3d555 100644 --- a/storage/ndb/test/ndbapi/testInterpreter.cpp +++ b/storage/ndb/test/ndbapi/testInterpreter.cpp @@ -79,46 +79,46 @@ int runTestIncValue32(NDBT_Context* ctx, NDBT_Step* step){ Ndb* pNdb = GETNDB(step); - NdbConnection* pTrans = pNdb->startTransaction(); - if (pTrans == NULL){ - ERR(pNdb->getNdbError()); - return NDBT_FAILED; - } - - NdbOperation* pOp = pTrans->getNdbOperation(pTab->getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - int check = pOp->interpretedUpdateTuple(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - - // Primary keys - Uint32 pkVal = 1; - check = pOp->equal("KOL1", pkVal ); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Attributes - - // Update column - Uint32 valToIncWith = 1; - check = pOp->incValue("KOL2", valToIncWith); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } + NdbConnection* pTrans = pNdb->startTransaction(); + if (pTrans == NULL){ + ERR(pNdb->getNdbError()); + return NDBT_FAILED; + } + + NdbOperation* pOp = pTrans->getNdbOperation(pTab->getName()); + if (pOp == NULL) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + int check = pOp->interpretedUpdateTuple(); + if( check == -1 ) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + + // Primary keys + Uint32 pkVal = 1; + check = pOp->equal("KOL1", pkVal ); + if( check == -1 ) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + // Attributes + + // Update column + Uint32 valToIncWith = 1; + check = pOp->incValue("KOL2", valToIncWith); + if( check == -1 ) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } NdbRecAttr* valueRec = pOp->getValue("KOL2"); if( valueRec == NULL ) { @@ -142,6 +142,122 @@ int runTestIncValue32(NDBT_Context* ctx, NDBT_Step* step){ return NDBT_OK; } +int runTestBug19537(NDBT_Context* ctx, NDBT_Step* step){ + int result = NDBT_OK; + const NdbDictionary::Table * pTab = ctx->getTab(); + Ndb* pNdb = GETNDB(step); + + if (strcmp(pTab->getName(), "T1") != 0) { + g_err << "runTestBug19537: skip, table != T1" << endl; + return NDBT_OK; + } + + + NdbConnection* pTrans = pNdb->startTransaction(); + if (pTrans == NULL){ + ERR(pNdb->getNdbError()); + return NDBT_FAILED; + } + + NdbOperation* pOp = pTrans->getNdbOperation(pTab->getName()); + if (pOp == NULL) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + if (pOp->interpretedUpdateTuple() == -1) { + ERR(pOp->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + + // Primary keys + const Uint32 pkVal = 1; + if (pOp->equal("KOL1", pkVal) == -1) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + // Load 64-bit constant into register 1 and + // write from register 1 to 32-bit column KOL2 + const Uint64 reg_val = 0x0102030405060708ULL; + + const Uint32* reg_ptr32 = (const Uint32*)®_val; + if (reg_ptr32[0] == 0x05060708 && reg_ptr32[1] == 0x01020304) { + g_err << "runTestBug19537: platform is LITTLE endian" << endl; + } else if (reg_ptr32[0] == 0x01020304 && reg_ptr32[1] == 0x05060708) { + g_err << "runTestBug19537: platform is BIG endian" << endl; + } else { + g_err << "runTestBug19537: impossible platform" + << hex << " [0]=" << reg_ptr32[0] << " [1]=" <<reg_ptr32[1] << endl; + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + if (pOp->load_const_u64(1, reg_val) == -1 || + pOp->write_attr("KOL2", 1) == -1) { + ERR(pOp->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + if (pTrans->execute(Commit) == -1) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + // Read value via a new transaction + + pTrans = pNdb->startTransaction(); + if (pTrans == NULL){ + ERR(pNdb->getNdbError()); + return NDBT_FAILED; + } + + pOp = pTrans->getNdbOperation(pTab->getName()); + if (pOp == NULL) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + Uint32 kol2 = 0x09090909; + if (pOp->readTuple() == -1 || + pOp->equal("KOL1", pkVal) == -1 || + pOp->getValue("KOL2", (char*)&kol2) == 0) { + ERR(pOp->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + if (pTrans->execute(Commit) == -1) { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + // Expected conversion as in C - truncate to lower (logical) word + + if (kol2 == 0x01020304) { + g_err << "runTestBug19537: the bug manifests itself !" << endl; + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + if (kol2 != 0x05060708) { + g_err << "runTestBug19537: impossible KOL2 " << hex << kol2 << endl; + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; + } + + pNdb->closeTransaction(pTrans); + return NDBT_OK; +} + NDBT_TESTSUITE(testInterpreter); TESTCASE("IncValue32", @@ -156,6 +272,12 @@ TESTCASE("IncValue64", INITIALIZER(runTestIncValue64); FINALIZER(runClearTable); } +TESTCASE("Bug19537", + "Test big-endian write_attr of 32 bit integer\n"){ + INITIALIZER(runLoadTable); + INITIALIZER(runTestBug19537); + FINALIZER(runClearTable); +} #if 0 TESTCASE("MaxTransactions", "Start transactions until no more can be created\n"){ diff --git a/storage/ndb/tools/desc.cpp b/storage/ndb/tools/desc.cpp index 37157ef9eec..7c5ce68c950 100644 --- a/storage/ndb/tools/desc.cpp +++ b/storage/ndb/tools/desc.cpp @@ -18,6 +18,7 @@ #include <ndb_opts.h> #include <NDBT.hpp> #include <NdbApi.hpp> +#include <NdbSleep.h> void desc_AutoGrowSpecification(struct NdbDictionary::AutoGrowSpecification ags); int desc_logfilegroup(Ndb *myndb, char* name); @@ -31,6 +32,7 @@ NDB_STD_OPTS_VARS; static const char* _dbname = "TEST_DB"; static int _unqualified = 0; static int _partinfo = 0; +static int _retries = 0; static struct my_option my_long_options[] = { NDB_STD_OPTS("ndb_desc"), @@ -43,6 +45,9 @@ static struct my_option my_long_options[] = { "extra-partition-info", 'p', "Print more info per partition", (gptr*) &_partinfo, (gptr*) &_partinfo, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "retries", 'r', "Retry every second for # retries", + (gptr*) &_retries, (gptr*) &_retries, 0, + GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; static void usage() @@ -231,7 +236,8 @@ int desc_datafile(Ndb_cluster_connection &con, Ndb *myndb, char* name) int desc_table(Ndb *myndb, char* name) { NdbDictionary::Dictionary * dict= myndb->getDictionary(); - NDBT_Table* pTab = (NDBT_Table*)dict->getTable(name); + NDBT_Table* pTab; + while ((pTab = (NDBT_Table*)dict->getTable(name)) == NULL && --_retries >= 0) NdbSleep_SecSleep(1); if (!pTab) return 0; diff --git a/storage/ndb/tools/restore/consumer_restore.cpp b/storage/ndb/tools/restore/consumer_restore.cpp index 3b39a1373a0..6b0d42ee0d2 100644 --- a/storage/ndb/tools/restore/consumer_restore.cpp +++ b/storage/ndb/tools/restore/consumer_restore.cpp @@ -151,9 +151,12 @@ BackupRestore::finalize_table(const TableS & table){ if (table.have_auto_inc()) { Uint64 max_val= table.get_max_auto_val(); - Uint64 auto_val= m_ndb->readAutoIncrementValue(get_table(table.m_dictTable)); - if (max_val+1 > auto_val || auto_val == ~(Uint64)0) - ret= m_ndb->setAutoIncrementValue(get_table(table.m_dictTable), max_val+1, false); + Uint64 auto_val; + int r= m_ndb->readAutoIncrementValue(get_table(table.m_dictTable), auto_val); + if (r == -1 && m_ndb->getNdbError().code != 626) + ret= false; + else if (r == -1 || max_val+1 > auto_val) + ret= m_ndb->setAutoIncrementValue(get_table(table.m_dictTable), max_val+1, false) != -1; } return ret; } diff --git a/storage/ndb/tools/waiter.cpp b/storage/ndb/tools/waiter.cpp index db90bd8bd90..cb02d5e7c36 100644 --- a/storage/ndb/tools/waiter.cpp +++ b/storage/ndb/tools/waiter.cpp @@ -124,6 +124,12 @@ getStatus(){ ndbout << "status==NULL, retries="<<retries<<endl; MGMERR(handle); retries++; + ndb_mgm_disconnect(handle); + if (ndb_mgm_connect(handle,0,0,1)) { + MGMERR(handle); + g_err << "Reconnect failed" << endl; + break; + } continue; } int count = status->no_of_nodes; diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index bf17375c0eb..bd16297f8e7 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -265,7 +265,10 @@ case "$mode" in then # Give extra arguments to mysqld with the my.cnf file. This script may # be overwritten at next upgrade. - $manager --user=$user --pid-file=$pid_file >/dev/null 2>&1 & + "$manager" \ + --mysqld-safe-compatible \ + --user="$user" \ + --pid-file="$pid_file" >/dev/null 2>&1 & wait_for_pid created # Make lock for RedHat / SuSE diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index c57496e6c55..01746d3156e 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -217,8 +217,9 @@ sh -c "PATH=\"${MYSQL_BUILD_PATH:-$PATH}\" \ --with-fast-mutexes \ --with-mysqld-user=%{mysqld_user} \ --with-unix-socket-path=/var/lib/mysql/mysql.sock \ + --with-pic \ --prefix=/ \ - --with-extra-charsets=complex \ + --with-extra-charsets=all \ %if %{YASSL_BUILD} --with-yassl \ %endif @@ -319,7 +320,7 @@ BuildMySQL "--enable-shared \ --with-example-storage-engine \ --with-blackhole-storage-engine \ --with-federated-storage-engine \ - --with-big-tables \ + --with-big-tables \ --with-comment=\"MySQL Community Server - Max (GPL)\"") # We might want to save the config log file @@ -553,7 +554,6 @@ fi %attr(755, root, root) %{_bindir}/myisampack %attr(755, root, root) %{_bindir}/mysql_convert_table_format %attr(755, root, root) %{_bindir}/mysql_create_system_tables -%attr(755, root, root) %{_bindir}/mysql_explain_log %attr(755, root, root) %{_bindir}/mysql_fix_extensions %attr(755, root, root) %{_bindir}/mysql_fix_privilege_tables %attr(755, root, root) %{_bindir}/mysql_install_db @@ -683,6 +683,16 @@ fi # itself - note that they must be ordered by date (important when # merging BK trees) %changelog +* Sat May 20 2006 Kent Boortz <kent@mysql.com> + +- Always compile for PIC, position independent code. + +* Wed May 10 2006 Kent Boortz <kent@mysql.com> + +- Use character set "all" when compiling with Cluster, to make Cluster + nodes independent on the character set directory, and the problem + that two RPM sub packages both wants to install this directory. + * Mon May 01 2006 Kent Boortz <kent@mysql.com> - Use "./libtool --mode=execute" instead of searching for the diff --git a/tests/Makefile.am b/tests/Makefile.am index e9432a4c9d2..8ee64f8179c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,13 @@ if HAVE_YASSL else yassl_dummy_link_fix= endif + +if THREAD_SAFE_CLIENT +LIBMYSQLCLIENT_LA = $(top_builddir)/libmysql_r/libmysqlclient_r.la +else +LIBMYSQLCLIENT_LA = $(top_builddir)/libmysql/libmysqlclient.la +endif + EXTRA_DIST = auto_increment.res auto_increment.tst \ function.res function.tst lock_test.pl lock_test.res \ export.pl big_record.pl \ @@ -42,7 +49,7 @@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ $(openssl_includes) LIBS = @CLIENT_LIBS@ LDADD = @CLIENT_EXTRA_LDFLAGS@ \ - $(top_builddir)/libmysql/libmysqlclient.la + $(LIBMYSQLCLIENT_LA) mysql_client_test_LDADD= $(LDADD) $(CXXLDFLAGS) -L../mysys -lmysys mysql_client_test_SOURCES= mysql_client_test.c $(yassl_dummy_link_fix) insert_test_SOURCES= insert_test.c $(yassl_dummy_link_fix) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index d927b27ab37..e3159cfa5e5 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -12794,25 +12794,26 @@ from t2);"); static void test_bug8378() { #if defined(HAVE_CHARSET_gbk) && !defined(EMBEDDED_LIBRARY) - MYSQL *lmysql; + MYSQL *old_mysql=mysql; char out[9]; /* strlen(TEST_BUG8378)*2+1 */ - int len; + char buf[256]; + int len, rc; myheader("test_bug8378"); if (!opt_silent) fprintf(stdout, "\n Establishing a test connection ..."); - if (!(lmysql= mysql_init(NULL))) + if (!(mysql= mysql_init(NULL))) { myerror("mysql_init() failed"); exit(1); } - if (mysql_options(lmysql, MYSQL_SET_CHARSET_NAME, "gbk")) + if (mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "gbk")) { myerror("mysql_options() failed"); exit(1); } - if (!(mysql_real_connect(lmysql, opt_host, opt_user, + if (!(mysql_real_connect(mysql, opt_host, opt_user, opt_password, current_db, opt_port, opt_unix_socket, 0))) { @@ -12822,12 +12823,18 @@ static void test_bug8378() if (!opt_silent) fprintf(stdout, " OK"); - len= mysql_real_escape_string(lmysql, out, TEST_BUG8378_IN, 4); + len= mysql_real_escape_string(mysql, out, TEST_BUG8378_IN, 4); /* No escaping should have actually happened. */ DIE_UNLESS(memcmp(out, TEST_BUG8378_OUT, len) == 0); - mysql_close(lmysql); + sprintf(buf, "SELECT '%s'", out); + rc=mysql_real_query(mysql, buf, strlen(buf)); + myquery(rc); + + mysql_close(mysql); + + mysql=old_mysql; #endif } diff --git a/unittest/mytap/t/basic.t.c b/unittest/mytap/t/basic.t.c index 95a77755347..bf4c1a9a664 100644 --- a/unittest/mytap/t/basic.t.c +++ b/unittest/mytap/t/basic.t.c @@ -2,7 +2,7 @@ #include "my_config.h" #include <stdlib.h> -#include <tap.h> +#include "../tap.h" int main() { plan(5); diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index af2118417b2..ca7a96d5801 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -311,7 +311,7 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file, /************************ VioSSLAcceptorFd **********************************/ -struct st_VioSSLFd* +struct st_VioSSLFd * new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher) @@ -345,11 +345,9 @@ new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, return ssl_fd; } - void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd) { SSL_CTX_free(fd->ssl_context); my_free((gptr) fd, MYF(0)); } - #endif /* HAVE_OPENSSL */ |