+mysql-wsrep-5.6 (5.6.35-0~u14.04+mos1) mos; urgency=low
+
+ * New release from codership merged with
+ https://github.com/codership/mysql-wsrep/pull/264
+
+ -- Ivan Suzdal <mos-linux@mirantis.com> Thu, 26 Jan 2017 12:42:43 +0000
+
mysql-wsrep-5.6 (5.6.34-0~u14.04+mos3) mos; urgency=low
* Add replace for mysql-server-wsrep-core-5.6 package
+++ /dev/null
-Description: Include return code 255 in list of valid error codes
- Perl 5.20 changed the behaviour of mysqlhotcopy to return 255 on error
- .
- Add this error code to the list of allowable return codes
-Author: James Page <james.page@ubuntu.com>
-Forwarded: no
-
---- a/mysql-test/include/mysqlhotcopy.inc
-+++ b/mysql-test/include/mysqlhotcopy.inc
-@@ -107,7 +107,7 @@ DROP DATABASE hotcopy_save;
- --replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
- --list_files $MYSQLD_DATADIR/hotcopy_save
- --replace_result $MASTER_MYSOCK MASTER_MYSOCK
----error 9,11,110,2304
-+--error 9,11,110,255,2304
- --exec $MYSQLHOTCOPY --quiet -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save
- --replace_result $MASTER_MYSOCK MASTER_MYSOCK
- --exec $MYSQLHOTCOPY --quiet --allowold -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save
scripts__mysqld_safe.sh__signals.patch
fix_standalone_tests.patch
kfreebsd_tests.patch
-fix-mysqlhotcopy-test-failure.patch
MYSQL_VERSION_MAJOR=5
MYSQL_VERSION_MINOR=6
-MYSQL_VERSION_PATCH=34
+MYSQL_VERSION_PATCH=35
MYSQL_VERSION_EXTRA=
if (mysql_query(mysql, "SHOW VARIABLES LIKE 'pid_file'"))
{
- my_printf_error(0, "query failed; error: '%s'", error_flags,
- mysql_error(mysql));
+ my_printf_error(mysql_errno(mysql),
+ "The query to get the server's pid file failed,"
+ " error: '%s'. Continuing.", error_flags,
+ mysql_error(mysql));
}
result = mysql_store_result(mysql);
if (result)
SET(INSTALL_SQLBENCHDIR_DEB ".")
SET(INSTALL_SUPPORTFILESDIR_DEB "support-files")
#
-SET(INSTALL_MYSQLDATADIR_DEB "/var/lib/mysql")
+SET(INSTALL_MYSQLDATADIR_DEB "data")
SET(INSTALL_PLUGINTESTDIR_DEB ${plugin_tests})
SET(INSTALL_SECURE_FILE_PRIVDIR_DEB ${secure_file_priv_path})
SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_DEB ${secure_file_priv_embedded_path})
-# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
INCLUDE(CheckCSourceRuns)
INCLUDE(CheckCSourceCompiles)
+# We require SunStudio 12u2 (CC 5.11)
+IF(NOT FORCE_UNSUPPORTED_COMPILER)
+ IF(CMAKE_C_COMPILER_ID MATCHES "SunPro")
+ # CC -V yields
+ # CC: Studio 12.5 Sun C++ 5.14 SunOS_sparc Dodona 2016/04/04
+ # CC: Sun C++ 5.13 SunOS_sparc Beta 2014/03/11
+ # CC: Sun C++ 5.11 SunOS_sparc 2010/08/13
+ EXECUTE_PROCESS(
+ COMMAND ${CMAKE_CXX_COMPILER} "-V"
+ OUTPUT_VARIABLE stdout
+ ERROR_VARIABLE stderr
+ RESULT_VARIABLE result
+ )
+ STRING(REGEX MATCH "CC: Sun C\\+\\+ 5\\.([0-9]+)" VERSION_STRING ${stderr})
+ IF (NOT CMAKE_MATCH_1 OR CMAKE_MATCH_1 STREQUAL "")
+ STRING(REGEX MATCH "CC: Studio 12\\.5 Sun C\\+\\+ 5\\.([0-9]+)"
+ VERSION_STRING ${stderr})
+ ENDIF()
+ SET(CC_MINOR_VERSION ${CMAKE_MATCH_1})
+ IF(${CC_MINOR_VERSION} LESS 11)
+ MESSAGE(FATAL_ERROR "SunStudio 12u2 or newer is required!")
+ ENDIF()
+ ELSE()
+ MESSAGE(FATAL_ERROR "Unsupported compiler!")
+ ENDIF()
+ENDIF()
+
# Enable 64 bit file offsets
SET(_FILE_OFFSET_BITS 64)
# CMake defined -lthread as thread flag. This crashes in dlopen
# when trying to load plugins workaround with -lpthread
-SET(CMAKE_THREADS_LIBS_INIT -lpthread CACHE INTERNAL "" FORCE)
+SET(CMAKE_THREAD_LIBS_INIT -lpthread CACHE INTERNAL "" FORCE)
# Solaris specific large page support
CHECK_SYMBOL_EXISTS(MHA_MAPSIZE_VA sys/mman.h HAVE_DECL_MHA_MAPSIZE_VA)
SET(HAVE_BMOVE CACHE INTERNAL "")
SET(HAVE_BSD_SIGNALS CACHE INTERNAL "")
SET(HAVE_BSEARCH 1 CACHE INTERNAL "")
-SET(HAVE_BSS_START CACHE INTERNAL "")
SET(HAVE_CHOWN CACHE INTERNAL "")
SET(HAVE_CLOCK_GETTIME CACHE INTERNAL "")
SET(HAVE_COMPRESS CACHE INTERNAL "")
SET(HAVE_CXXABI_H CACHE INTERNAL "")
SET(HAVE_DECL_MADVISE CACHE INTERNAL "")
SET(HAVE_DIRECTIO CACHE INTERNAL "")
-SET(HAVE_DIRENT_H CACHE INTERNAL "")
SET(HAVE_DLERROR CACHE INTERNAL "")
SET(HAVE_DLFCN_H CACHE INTERNAL "")
SET(HAVE_DLOPEN CACHE INTERNAL "")
SET(HAVE_PUTENV 1 CACHE INTERNAL "")
SET(HAVE_PWD_H CACHE INTERNAL "")
SET(HAVE_RDTSCLL CACHE INTERNAL "")
-SET(HAVE_READDIR_R CACHE INTERNAL "")
SET(HAVE_READLINK CACHE INTERNAL "")
SET(HAVE_READ_REAL_TIME CACHE INTERNAL "")
SET(HAVE_REALPATH CACHE INTERNAL "")
# so WSREP_VERSION is produced regardless
# Set the patch version
-SET(WSREP_PATCH_VERSION "18")
+SET(WSREP_PATCH_VERSION "19")
# Obtain patch revision number
SET(WSREP_REVISION $ENV{WSREP_REV})
#cmakedefine HAVE_CXXABI_H 1
#cmakedefine HAVE_NCURSES_H 1
#cmakedefine HAVE_NDIR_H 1
-#cmakedefine HAVE_DIRENT_H 1
#cmakedefine HAVE_DLFCN_H 1
#cmakedefine HAVE_EXECINFO_H 1
#cmakedefine HAVE_FCNTL_H 1
#cmakedefine HAVE_PUTENV 1
#cmakedefine HAVE_RE_COMP 1
#cmakedefine HAVE_REGCOMP 1
-#cmakedefine HAVE_READDIR_R 1
#cmakedefine HAVE_READLINK 1
#cmakedefine HAVE_REALPATH 1
#cmakedefine HAVE_RENAME 1
#cmakedefine HAVE_AIO_READ 1
/* Symbols we may use */
/* used by stacktrace functions */
-#cmakedefine HAVE_BSS_START 1
#cmakedefine HAVE_BACKTRACE 1
#cmakedefine HAVE_BACKTRACE_SYMBOLS 1
#cmakedefine HAVE_BACKTRACE_SYMBOLS_FD 1
IF(NOT LIBRT)
MY_SEARCH_LIBS(clock_gettime rt LIBRT)
ENDIF()
+ MY_SEARCH_LIBS(backtrace execinfo LIBEXECINFO)
+
FIND_PACKAGE(Threads)
SET(CMAKE_REQUIRED_LIBRARIES
- ${LIBM} ${LIBNSL} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT} ${LIBRT})
+ ${LIBM} ${LIBNSL} ${LIBBIND} ${LIBCRYPT} ${LIBSOCKET} ${LIBDL}
+ ${CMAKE_THREAD_LIBS_INIT} ${LIBRT} ${LIBEXECINFO}
+ )
# Need explicit pthread for gcc -fsanitize=address
IF(CMAKE_USE_PTHREADS_INIT AND CMAKE_C_FLAGS MATCHES "-fsanitize=")
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} pthread)
CHECK_INCLUDE_FILES (arpa/inet.h HAVE_ARPA_INET_H)
CHECK_INCLUDE_FILES (crypt.h HAVE_CRYPT_H)
CHECK_INCLUDE_FILES (cxxabi.h HAVE_CXXABI_H)
-CHECK_INCLUDE_FILES (dirent.h HAVE_DIRENT_H)
CHECK_INCLUDE_FILES (dlfcn.h HAVE_DLFCN_H)
CHECK_INCLUDE_FILES (execinfo.h HAVE_EXECINFO_H)
CHECK_INCLUDE_FILES (fcntl.h HAVE_FCNTL_H)
CHECK_FUNCTION_EXISTS (pthread_threadmask HAVE_PTHREAD_THREADMASK)
CHECK_FUNCTION_EXISTS (pthread_yield_np HAVE_PTHREAD_YIELD_NP)
CHECK_FUNCTION_EXISTS (putenv HAVE_PUTENV)
-CHECK_FUNCTION_EXISTS (readdir_r HAVE_READDIR_R)
CHECK_FUNCTION_EXISTS (readlink HAVE_READLINK)
CHECK_FUNCTION_EXISTS (re_comp HAVE_RE_COMP)
CHECK_FUNCTION_EXISTS (regcomp HAVE_REGCOMP)
HAVE_ABI_CXA_DEMANGLE)
ENDIF()
-CHECK_C_SOURCE_COMPILES("
- int main(int argc, char **argv)
- {
- extern char *__bss_start;
- return __bss_start ? 1 : 0;
- }"
-HAVE_BSS_START)
-
CHECK_C_SOURCE_COMPILES("
int main()
{
--- /dev/null
+
+###########################
+## FIXME for 5.1 ##
+###########################
+
+* put this trigger-recreation thing into the init scripts -- what?!
+
+###########################################################################
+# Here are some information that are only of interest for the current and #
+# following Debian maintainers of MySQL. #
+###########################################################################
+
+The debian/ directory is under SVN control, see debian/control for URL.
+
+#
+# Preparing a new version
+#
+The new orig.tar.gz (without non-free documentation) is created in /tmp/ when
+running this command:
+
+debian/rules get-orig-source
+
+#
+# mysqlreport
+#
+The authors e-mail address is <public@codenode.com>.
+
+#
+# Remarks to dependencies
+#
+libwrap0-dev (>= 7.6-8.3)
+ According to bug report 114582 where where build problems on
+ IA-64/sid with at least two prior versions.
+psmisc
+ /usr/bin/killall in the initscript
+
+zlib1g in libmysqlclient-dev:
+ "mysql_config --libs" ads "-lz"
+
+Build-Dep:
+
+debhelper (>=4.1.16):
+ See po-debconf(7).
+
+autoconf (>= 2.13-20), automake1.7
+ Try to get rid of them.
+
+doxygen, tetex-bin, tetex-extra, gs
+ for ndb/docs/*tex
+
+#
+# Remarks to the start scripts
+#
+
+## initscripts rely on mysqladmin from a different package
+We have the problem that "/etc/init.d/mysql stop" relies on mysqladmin which
+is in another package (mysql-client) and a passwordless access that's maybe
+only available if the user configured his /root/.my.cnf. Can this be a problem?
+* normal mode: not because the user is required to have it. Else:
+* purge/remove: not, same as normal mode
+* upgrade: not, same as normal mode
+* first install: not, it depends on mysql-client which at least is unpacked
+ so mysqladmin is there (to ping). It is not yet configured
+ passwordles but if there's a server running then there's a
+ /root/.my.cnf. Anyways, we simply kill anything that's mysqld.
+
+## Passwordless access for the maintainer scripts
+Another issue is that the scripts needs passwordless access. To ensure this
+a debian-sys-maint user is configured which has process and shutdown privs.
+The file with the randomly (that's important!) generated password must be
+present as long as the databases remain installed because else a new install
+would have no access. This file should be used like:
+ mysqladmin --defaults-file=/etc/mysql/debian.cnf restart
+to avoid providing the password in plaintext on a commandline where it would
+be visible to any user via the "ps" command.
+
+## When to start the daemon?
+We aim to give the admin full control on when MySQL is running.
+Issues to be faced here:
+OLD:
+ 1. Debconf asks whether MySQL should be started on boot so update-rc.d is
+ only run if the answer has been yes. The admin is likely to forget
+ this decision but update-rc.d checks for an existing line in
+ /etc/runlevel.conf and leaves it intact.
+ 2. On initial install, if the answer is yes, the daemon has to be started.
+ 3. On upgrades it should only be started if it was already running, everything
+ else is confusing. Especiall relying on an debconf decision made month ago
+ is considered suboptimal. See bug #274264
+ Implementation so far:
+ prerm (called on upgrade before stopping the server):
+ check for a running server and set flag if necessary
+ preinst (called on initial install and before unpacking when upgrading):
+ check for the debconf variable and set flag if necessary
+ postinst (called on initial install and after each upgrade after unpacking):
+ call update-rc.d if debconf says yes
+ call invoce-rc.d if the flag has been set
+ Problems remaining:
+ dpkg-reconfigure and setting mysql start on boot to yes did not start mysql
+ (ok "start on boot" literally does not mean "start now" so that might have been ok)
+NEW:
+ 1. --- no debconf anymore for the sake of simplicity. We have runlevel.conf,
+ the admin should use it
+ 2. On initial install the server is started.
+ 3. On upgrades the server is started exactly if it was running before so the
+ runlevel configuration is irrelevant. It will be preserved by the mean of
+ update-rc.d's builtin check.
+ Implementation:
+ prerm (called on upgrade before stopping the server):
+ check for a running server and set flag if necessary
+ preinst (called on initial install and before unpacking when upgrading):
+ check for $1 beeing (initial) "install" and set flag
+ postinst (called on initial install and after each upgrade after unpacking):
+ call update-rc.d
+ call invoce-rc.d if the flag has been set
--- /dev/null
+all:
+
+distclean:
+ -rm -f Makefile
+
+.PHONY: all distclean clean install check
--- /dev/null
+all:
+
+distclean:
+ -rm -f Makefile
+
+.PHONY: all distclean clean install check
--- /dev/null
+#!/bin/bash
+#
+# This script is executed by "/etc/init.d/mysql" on every (re)start.
+#
+# Changes to this file will be preserved when updating the Debian package.
+#
+
+source /usr/share/mysql/debian-start.inc.sh
+
+MYSQL="/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf"
+MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
+MYUPGRADE="/usr/bin/mysql_upgrade --defaults-extra-file=/etc/mysql/debian.cnf"
+MYCHECK="/usr/bin/mysqlcheck --defaults-file=/etc/mysql/debian.cnf"
+MYCHECK_SUBJECT="WARNING: mysqlcheck has found corrupt tables"
+MYCHECK_PARAMS="--all-databases --fast --silent"
+MYCHECK_RCPT="root"
+
+# The following commands should be run when the server is up but in background
+# where they do not block the server start and in one shell instance so that
+# they run sequentially. They are supposed not to echo anything to stdout.
+# If you want to disable the check for crashed tables comment
+# "check_for_crashed_tables" out.
+# (There may be no output to stdout inside the background process!)
+echo "Checking for tables which need an upgrade, are corrupt or were "
+echo "not closed cleanly."
+(
+ upgrade_system_tables_if_necessary;
+ check_root_accounts;
+ check_for_crashed_tables;
+) >&2 &
+
+exit 0
--- /dev/null
+#!/bin/bash
+#
+# This file is included by /etc/mysql/debian-start
+#
+
+## Check all unclosed tables.
+# - Requires the server to be up.
+# - Is supposed to run silently in background.
+function check_for_crashed_tables() {
+ set -e
+ set -u
+
+ # But do it in the background to not stall the boot process.
+ logger -p daemon.info -i -t$0 "Triggering myisam-recover for all MyISAM tables"
+
+ # Checking for $? is unreliable so the size of the output is checked.
+ # Some table handlers like HEAP do not support CHECK TABLE.
+ tempfile=`tempfile`
+ # We have to use xargs in this case, because a for loop barfs on the
+ # spaces in the thing to be looped over.
+ LC_ALL=C $MYSQL --skip-column-names --batch -e '
+ select concat('\''select count(*) into @discard from `'\'',
+ TABLE_SCHEMA, '\''`.`'\'', TABLE_NAME, '\''`'\'')
+ from information_schema.TABLES where ENGINE='\''MyISAM'\' | \
+ xargs -i $MYSQL --skip-column-names --silent --batch \
+ --force -e "{}" >$tempfile
+ if [ -s $tempfile ]; then
+ (
+ /bin/echo -e "\n" \
+ "Improperly closed tables are also reported if clients are accessing\n" \
+ "the tables *now*. A list of current connections is below.\n";
+ $MYADMIN processlist status
+ ) >> $tempfile
+ # Check for presence as a dependency on mailx would require an MTA.
+ if [ -x /usr/bin/mailx ]; then
+ mailx -e -s"$MYCHECK_SUBJECT" $MYCHECK_RCPT < $tempfile
+ fi
+ (echo "$MYCHECK_SUBJECT"; cat $tempfile) | logger -p daemon.warn -i -t$0
+ fi
+ rm $tempfile
+}
+
+## Check for tables needing an upgrade.
+# - Requires the server to be up.
+# - Is supposed to run silently in background.
+function upgrade_system_tables_if_necessary() {
+ set -e
+ set -u
+
+ logger -p daemon.info -i -t$0 "Upgrading MySQL tables if necessary."
+
+ # Filter all "duplicate column", "duplicate key" and "unknown column"
+ # errors as the script is designed to be idempotent.
+ LC_ALL=C $MYUPGRADE \
+ 2>&1 \
+ | egrep -v '^(1|@had|ERROR (1054|1060|1061))' \
+ | logger -p daemon.warn -i -t$0
+}
+
+## Check for the presence of both, root accounts with and without password.
+# This might have been caused by a bug related to mysql_install_db (#418672).
+function check_root_accounts() {
+ set -e
+ set -u
+
+ logger -p daemon.info -i -t$0 "Checking for insecure root accounts."
+
+ ret=$( echo "SELECT count(*) FROM mysql.user WHERE user='root' and password='';" | $MYSQL --skip-column-names )
+ if [ "$ret" -ne "0" ]; then
+ logger -p daemon.warn -i -t$0 "WARNING: mysql.user contains $ret root accounts without password!"
+ fi
+}
--- /dev/null
+-- Get the hostname, if the hostname has any wildcard character like "_" or "%"
+-- add escape character in front of wildcard character to convert "_" or "%" to
+-- a plain character
+SET @get_hostname= @@hostname;
+SELECT REPLACE((SELECT REPLACE(@get_hostname,'_','\_')),'%','\%') INTO @current_hostname;
+
+-- Fill "user" table with default users allowing root access
+-- from local machine if "user" table didn't exist before
+CREATE TEMPORARY TABLE tmp_user LIKE user;
+INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N');
+REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N');
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N');
+INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
+DROP TABLE tmp_user;
+
+CREATE TEMPORARY TABLE tmp_proxies_priv LIKE proxies_priv;
+INSERT INTO tmp_proxies_priv VALUES ('localhost', 'root', '', '', TRUE, '', now());
+REPLACE INTO tmp_proxies_priv SELECT @current_hostname, 'root', '', '', TRUE, '', now() FROM DUAL WHERE LOWER (@current_hostname) != 'localhost';
+INSERT INTO proxies_priv SELECT * FROM tmp_proxies_priv WHERE @had_proxies_priv_table=0;
+DROP TABLE tmp_proxies_priv;
+FLUSH PRIVILEGES;
+
--- /dev/null
+#!/bin/bash
+echo "$*" 1>&2
--- /dev/null
+Changelog for innotop:
+
+2009-03-09: version 1.7.1
+
+ Changes:
+ * Don't display the CXN column if only one connection is active in
+ the current view
+
+ Bugs fixed:
+ * fixed bug where trying to aggregate the time column would result
+ in a crash if the time column had an undef value in it, which is
+ the case when a thread is in the 'Connect' state
+ * updated innotop.spec file to reflect current version
+
+2009-02-23: version 1.7.0
+
+ Changes:
+ * supports a central config (/etc/innotop/innotop.conf)
+ * changed the default home directory config to ~/.innotop/innotop.conf
+ (away from .ini)
+ * embedded InnoDBParser.pm into innotop so it can be run with no
+ installation
+ * no longer writes a new config file by default
+ * added --skipcentral (skip reading central config) and --write (write
+ a config if none were loaded at start-up)
+ * if no config file is loaded, connect to a MySQL database on
+ localhost using mysql_read_default_group=client
+ * embedded maatkit's DSNParser.pm and added support for --user,
+ --password, --host, --port
+ * changed default mode from T (InnoDB Transactions) to Q (Query List)
+ * in addition to connected threads, now displays running and cached
+ threads in statusbar
+ * don't load connections from a config file if any DSN information or
+ a username or password is specified on the command-line
+
+ Bugs fixed:
+ * fixed bug preventing utilization of command-line options that
+ override default config settings if no config file was loaded
+ * fixed a bug where migrating from an old version of the config will
+ delete ~/innotop.ini, if it exists. Now uses File::Temp::tempfile().
+
+2007-11-09: version 1.6.0
+
+ * S mode crashed on non-numeric values.
+ * New user-defined columns crashed upon restart.
+ * Added --color option to control terminal coloring.
+
+2007-09-18: version 1.5.2
+
+ * Added the ability to monitor InnoDB status from a file.
+ * Changed W mode to L mode; it monitors all locks, not just lock waits.
+
+2007-09-16: version 1.5.1
+
+ * Added C (Command Summary) mode.
+ * Fixed a bug in the 'avg' aggregate function.
+
+2007-09-10: version 1.5.0
+
+ Changes:
+ * Added plugin functionality.
+ * Added group-by functionality.
+ * Moved the configuration file to a directory.
+ * Enhanced filtering and sorting on pivoted tables.
+ * Many small bug fixes.
+
+2007-07-16: version 1.4.3
+
+ Changes:
+ * Added standard --version command-line option
+ * Changed colors to cyan instead of blue; more visible on dark terminals.
+ * Added information to the filter-choosing dialog.
+ * Added column auto-completion when entering a filter expression.
+ * Changed Term::ReadKey from optional to mandatory.
+ * Clarified username in password prompting.
+ * Ten thousand words of documentation!
+
+ Bugs fixed:
+ * innotop crashed in W mode when InnoDB status data was truncated.
+ * innotop didn't display errors in tables if debug was enabled.
+ * The colored() subroutine wasn't being created in non-interactive mode.
+ * Don't prompt to save password except the first time.
+
+2007-05-03: version 1.4.2
+
+ This version contains all changes to the trunk until revision 239; some
+ changes in revisions 240:250 are included.
+
+ MAJOR CHANGES:
+
+ * Quick-filters to easily filter any column in any display
+ * Compatibility with MySQL 3.23 through 6.0
+ * Improved error handling when a server is down, permissions denied, etc
+ * Use additional SHOW INNODB STATUS information in 5.1.x
+ * Make all modes use tables consistently, so they can all be edited,
+ filtered, colored and sorted consistently
+ * Combine V, G and S modes into S mode, with v, g, and s hot-keys
+ * Let DBD driver read MySQL option files; permit connections without
+ user/pass/etc
+ * Compile SQL-like expressions into Perl subroutines; eliminate need to
+ know Perl
+ * Do not save all config data to config file, only save user's customizations
+ * Rewritten and improved command-line option handling
+ * Added --count, --delay, and other command-line options to support
+ run-and-exit operation
+ * Improve built-in variable sets
+ * Improve help screen with three-part balanced-column layout
+ * Simplify table-editor and improve hotkey support
+ * Require Perl to have high-resolution time support (Time::HiRes)
+ * Help the user choose a query to analyze or kill
+ * Enable EXPLAIN, show-full-query in T mode just like Q mode
+ * Let data-extraction access current, previous and incremental data sets
+ all at once
+
+ MINOR CHANGES:
+
+ * Column stabilizing for Q mode
+ * New color rules for T, Q, W modes
+ * Apply slave I/O filter to Q mode
+ * Improve detection of server version and other meta-data
+ * Make connection timeout a config variable
+ * Improve cross-version-compatible SQL syntax
+ * Get some information from the DBD driver instead of asking MySQL for it
+ * Improved error messages
+ * Improve server group creation/editing
+ * Improve connection/thread killing
+ * Fix broken key bindings and restore previously mapped hot-keys for
+ choosing columns
+ * Some documentation updates (but not nearly enough)
+ * Allow the user to specify graphing char in S mode (formerly G mode)
+ * Allow easy switching between variable sets in S mode
+ * Bind 'n' key globally to choose the 'next' server connection
+ * Bind '%' key globally to filter displayed tables
+ * Allow aligning columns on the decimal place for easy readability
+ * Add hide_hdr config variable to hide column headers in tables
+ * Add a feature to smartly run PURGE MASTER LOGS in Replication mode
+ * Enable debug mode as a globally configurable variable
+ * Improve error messages when an expression or filter doesn't compile or has
+ a run-time error; die on error when debug is enabled
+ * Allow user-configurable delays after executing SQL (to let the server
+ settle down before taking another measurement)
+ * Add an expression to show how long until a transaction is finished
+ * Add skip_innodb as a global config variable
+ * Add '%' after percentages to help disambiguate (user-configurable)
+ * Add column to M mode to help see how fast slave is catching up to master
+
+ BUG FIXES:
+
+ * T and W modes had wrong value for wait_status column
+ * Error tracking on connections didn't reset when the connection recovered
+ * wait_timeout on connections couldn't be set before MySQL 4.0.3
+ * There was a crash on 3.23 when wiping deadlocks
+ * Lettercase changes in some result sets (SHOW MASTER/SLAVE STATUS) between
+ MySQL versions crashed innotop
+ * Inactive connections crashed innotop upon access to DBD driver
+ * set_precision did not respect user defaults for number of digits
+ * --inc command-line option could not be negated
+ * InnoDB status parsing was not always parsing all needed information
+ * S mode (formerly G mode) could crash trying to divide non-numeric data
+ * M table didn't show Slave_open_temp_tables variable; incorrect lettercase
+ * DBD drivers with broken AutoCommit would crash innotop
+ * Some key bindings had incorrect labels
+ * Some config-file loading routines could load data for things that didn't
+ exist
+ * Headers printed too often in S mode
+ * High-resolution time was not used even when the user had it
+ * Non-interactive mode printed blank lines sometimes
+ * Q-mode header and statusbar showed different QPS numbers
+ * Formulas for key-cache and query-cache hit ratios were wrong
+ * Mac OS "Darwin" machines were mis-identified as Microsoft Windows
+ * Some multiplications crashed when given undefined input
+ * The commify transformation did not check its input and could crash
+ * Specifying an invalid mode on the command line or config file could crash
+ innotop
+
+2007-03-29: version 1.4.1
+
+ * More tweaks to display of connection errors.
+ * Fixed a problem with skip-innodb in MySQL 5.1.
+ * Fix a bug with dead connections in single-connection mode.
+ * Fix a regex to allow parsing more data from truncated deadlocks.
+ * Don't load active cxns from the config file if the cxn isn't defined.
+
+2007-03-03: version 1.4.0
+
+ * Further tweak error handling and display of connection errors
+ * More centralization of querying
+ * Fix forking so it doesn't kill all database connections
+ * Allow user to run innotop without permissions for GLOBAL variables and status
+
+2007-02-11: version 1.3.6
+
+ * Handle some connection failures so innotop doesn't crash because of one server.
+ * Enable incremental display in more modes.
+ * Tweaks to colorizing, color editor, and default color rules.
+ * Tweaks to default sorting rules.
+ * Use prepared statements for efficiency.
+ * Bug fixes and code cleanups.
+ * Data storage is keyed on clock ticks now.
+
+2007-02-03: version 1.3.5
+
+ * Bug fixes.
+ * More tools for editing configuration from within innotop.
+ * Filters and transformations are constrained to valid values.
+ * Support for colorizing rows.
+ * Sorting by multiple columns.
+ * Compress headers when display is very wide.
+ * Stabilize and limit column widths.
+ * Check config file formats when upgrading so upgrades go smoothly.
+ * Make D mode handle many connections at once.
+ * Extract simple expressions from data sets in column src property.
+ This makes innotop more awk-ish.
+
+2007-01-16: version 1.3
+
+ * Readline support.
+ * Can be used unattended, or in a pipe-and-filter mode
+ where it outputs tab-separated data to standard output.
+ * You can specify a config file on the command line.
+ Config files can be marked read-only.
+ * Monitor multiple servers simultaneously.
+ * Server groups to help manage many servers conveniently.
+ * Monitor master/slave status, and control slaves.
+ * Columns can have user-defined expressions as their data sources.
+ * Better configuration tools.
+ * InnoDB status information is merged into SHOW VARIABLES and
+ SHOW STATUS information, so you can access it all together.
+ * High-precision time support in more places.
+ * Lots of tweaks to make things display more readably and compactly.
+ * Column transformations and filters.
+
+2007-01-16: version 1.0.1
+ * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS.
+ The new project homepage is http://sourceforge.net/projects/innotop/
+ * Tweak default T/Q mode sort columns to match what people expect.
+ * Fix broken InnoDBParser.pm documentation (and hence man page).
+
+2007-01-06: version 1.0
+ * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS.
+ The new project homepage is http://sourceforge.net/projects/innotop/
+ * Prevent control characters from freaking terminal out.
+ * Set timeout to keep busy servers from closing connection.
+ * There is only one InnoDB insert buffer.
+ * Make licenses clear and consistent.
+
+2006-11-14: innotop 0.1.160, InnoDBParser version 1.69
+ * Support for ANSI color on Microsoft Windows (more readable, compact
+ display; thanks Gisbert W. Selke).
+ * Better handling of $ENV{HOME} on Windows.
+ * Added a LICENSE file to the package as per Gentoo bug:
+ http://bugs.gentoo.org/show_bug.cgi?id=147600
+
+2006-11-11: innotop 0.1.157, InnoDBParser version 1.69
+ * Add Microsoft Windows support.
+
+2006-10-19: innotop 0.1.154, InnoDBParser version 1.69
+ * Add O (Open Tables) mode
+ * Add some more checks to handle incomplete InnoDB status information
+
+2006-09-30: innotop 0.1.152, InnoDBParser version 1.69
+ * Figured out what was wrong with package $VERSION variable: it wasn't
+ after the package declaration!
+
+2006-09-28: innotop 0.1.152, InnoDBParser version 1.67
+ * Make more efforts towards crash-resistance and tolerance of completely
+ messed-up inputs. If innotop itself is broken, it is now much harder to
+ tell, because it just keeps on running without complaining.
+ * Fix a small bug parsing out some information and displaying it.
+
+2006-09-05: innotop 0.1.149, InnoDBParser version 1.64
+ * Try to find and eliminate any parsing code that assumes pattern matches
+ will succeed.
+
+2006-09-05: innotop 0.1.149, InnoDBParser version 1.62
+ * Make innotop crash-resistant, so I can declare it STABLE finally.
+ * Instead of using SQL conditional comments, detect MySQL version.
+
+2006-08-22: innotop 0.1.147, InnoDBParser version 1.60
+ * Fix some innotop bugs with undefined values, bad formatting etc.
+
+2006-08-19: innotop 0.1.146, InnoDBParser version 1.60
+ * Make innotop handle some unexpected NULL values in Q mode.
+ * Add OS wait information to W mode, so it is now "everything that waits."
+ * Center section captions better.
+ * Make R mode more readable and compact.
+ * Make InnoDBParser parse lock waits even when they've been waiting 0 secs.
+
+2006-08-12: innotop 0.1.139, InnoDBParser version 1.59
+ * Add more documentation
+ * Tweak V mode to show more info in less space.
+ * Fix a bug in G mode.
+
+2006-08-10: innotop 0.1.132, InnoDBParser version 1.58
+ * Handle yet more types of FK error... it will never end!
+ * Handle some special cases when DEADLOCK info truncated
+ * Add a bit more FK info to F mode in innotop
+ * More tests added to the test suite
+
+2006-08-07: innotop 0.1.131, InnoDBParser version 1.55
+ * Fix another issue with configuration
+ * Handle another type of FK error
+
+2006-08-03: innotop 0.1.130, InnoDBParser version 1.54
+ * Fix an issue loading config file
+ * Add heap_no to 'D' (InnoDB Deadlock) mode to ease deadlock debugging.
+
+2006-08-02: innotop 0.1.128, InnoDBParser version 1.54
+ * Parse lock wait information from the TRANSACTION section.
+ * Even more OS-specific parsing... pain in the butt...
+ * Add 'W' (InnoDB Lock Wait) mode.
+ * Fix some minor display issues with statusbar.
+
+2006-08-02: innotop 0.1.125, InnoDBParser version 1.50
+ * Don't try to get references to Perl built-in functions like time()
+ * Handle more OS-specific variations of InnoDB status text
+ * Add some more information to various places in innotop
+
+2006-08-01: innotop 0.1.123, InnoDBParser version 1.47
+
+ * Enhance S and G modes: clear screen and re-print headers
+ * Don't crash when deadlock data is truncated
+ * Make Analyze mode say how to get back to whatever you came from
+ * Display 'nothing to display' when there is nothing
+ * Add ability to read InnoDB status text from a file (mostly helps test)
+ * Add table of Wait Array Information in Row Op/Semaphore mode
+ * Add table of lock information in InnoDB deadlock mode
+ * Ensure new features in upgrades don't get masked by existing config files
+ * Tweak default column choices for T mode
+ * Enhance foreign key parsing
+ * Enhance physical record and data tuple parsing
+ * Enhance lock parsing (handle old-style and new-style formats)
+
+2006-07-24: innotop 0.1.112, InnoDBParser version 1.36
+
+ * InnoDBParser enhancements for FK error messages.
+ * A fix to innotop to prevent it from crashing while trying to display a FK
+ error message.
+ * Some minor cosmetic changes to number formatting in innotop.
+
+2006-07-22: innotop 0.1.106, InnoDBParser version 1.35
+
+ * InnoDBParser is much more complete and accurate.
+ * Tons of bug fixes.
+ * Add partitions to EXPLAIN mode.
+ * Enhance Q mode header, add T mode header.
+ * Share some configuration variables across modes.
+ * Add formatted time columns to Q, T modes.
+ * Add command-line argument parsing.
+ * Turn off echo when asking for password.
+ * Add option to specify port when connecting.
+ * Let display-optimized-query display multiple notes.
+ * Lots of small improvements, such as showing more info in statusbar.
+
+2006-07-02: innotop 0.1.74, InnoDBParser version 1.24
+
+ * Initial release for public consumption.
--- /dev/null
+#!/usr/bin/perl
+
+# vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3
+
+# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com.
+# Feedback and improvements are gratefully received.
+#
+# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
+# systems, you can issue `man perlgpl' or `man perlartistic' to read these
+
+# 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
+
+use strict;
+use warnings FATAL => 'all';
+
+our $VERSION = '1.7.1';
+
+# Find the home directory; it's different on different OSes.
+our $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';
+
+# Configuration files
+our $default_home_conf = "$homepath/.innotop/innotop.conf";
+our $default_central_conf = "/etc/innotop/innotop.conf";
+our $conf_file = "";
+
+## Begin packages ##
+
+package DSNParser;
+
+use DBI;
+use Data::Dumper;
+$Data::Dumper::Indent = 0;
+$Data::Dumper::Quotekeys = 0;
+use English qw(-no_match_vars);
+
+use constant MKDEBUG => $ENV{MKDEBUG};
+
+# Defaults are built-in, but you can add/replace items by passing them as
+# hashrefs of {key, desc, copy, dsn}. The desc and dsn items are optional.
+# You can set properties with the prop() sub. Don't set the 'opts' property.
+sub new {
+ my ( $class, @opts ) = @_;
+ my $self = {
+ opts => {
+ A => {
+ desc => 'Default character set',
+ dsn => 'charset',
+ copy => 1,
+ },
+ D => {
+ desc => 'Database to use',
+ dsn => 'database',
+ copy => 1,
+ },
+ F => {
+ desc => 'Only read default options from the given file',
+ dsn => 'mysql_read_default_file',
+ copy => 1,
+ },
+ h => {
+ desc => 'Connect to host',
+ dsn => 'host',
+ copy => 1,
+ },
+ p => {
+ desc => 'Password to use when connecting',
+ dsn => 'password',
+ copy => 1,
+ },
+ P => {
+ desc => 'Port number to use for connection',
+ dsn => 'port',
+ copy => 1,
+ },
+ S => {
+ desc => 'Socket file to use for connection',
+ dsn => 'mysql_socket',
+ copy => 1,
+ },
+ u => {
+ desc => 'User for login if not current user',
+ dsn => 'user',
+ copy => 1,
+ },
+ },
+ };
+ foreach my $opt ( @opts ) {
+ MKDEBUG && _d('Adding extra property ' . $opt->{key});
+ $self->{opts}->{$opt->{key}} = { desc => $opt->{desc}, copy => $opt->{copy} };
+ }
+ return bless $self, $class;
+}
+
+# Recognized properties:
+# * autokey: which key to treat a bareword as (typically h=host).
+# * dbidriver: which DBI driver to use; assumes mysql, supports Pg.
+# * required: which parts are required (hashref).
+# * setvars: a list of variables to set after connecting
+sub prop {
+ my ( $self, $prop, $value ) = @_;
+ if ( @_ > 2 ) {
+ MKDEBUG && _d("Setting $prop property");
+ $self->{$prop} = $value;
+ }
+ return $self->{$prop};
+}
+
+sub parse {
+ my ( $self, $dsn, $prev, $defaults ) = @_;
+ if ( !$dsn ) {
+ MKDEBUG && _d('No DSN to parse');
+ return;
+ }
+ MKDEBUG && _d("Parsing $dsn");
+ $prev ||= {};
+ $defaults ||= {};
+ my %given_props;
+ my %final_props;
+ my %opts = %{$self->{opts}};
+ my $prop_autokey = $self->prop('autokey');
+
+ # Parse given props
+ foreach my $dsn_part ( split(/,/, $dsn) ) {
+ if ( my ($prop_key, $prop_val) = $dsn_part =~ m/^(.)=(.*)$/ ) {
+ # Handle the typical DSN parts like h=host, P=3306, etc.
+ $given_props{$prop_key} = $prop_val;
+ }
+ elsif ( $prop_autokey ) {
+ # Handle barewords
+ MKDEBUG && _d("Interpreting $dsn_part as $prop_autokey=$dsn_part");
+ $given_props{$prop_autokey} = $dsn_part;
+ }
+ else {
+ MKDEBUG && _d("Bad DSN part: $dsn_part");
+ }
+ }
+
+ # Fill in final props from given, previous, and/or default props
+ foreach my $key ( keys %opts ) {
+ MKDEBUG && _d("Finding value for $key");
+ $final_props{$key} = $given_props{$key};
+ if ( !defined $final_props{$key}
+ && defined $prev->{$key} && $opts{$key}->{copy} )
+ {
+ $final_props{$key} = $prev->{$key};
+ MKDEBUG && _d("Copying value for $key from previous DSN");
+ }
+ if ( !defined $final_props{$key} ) {
+ $final_props{$key} = $defaults->{$key};
+ MKDEBUG && _d("Copying value for $key from defaults");
+ }
+ }
+
+ # Sanity check props
+ foreach my $key ( keys %given_props ) {
+ die "Unrecognized DSN part '$key' in '$dsn'\n"
+ unless exists $opts{$key};
+ }
+ if ( (my $required = $self->prop('required')) ) {
+ foreach my $key ( keys %$required ) {
+ die "Missing DSN part '$key' in '$dsn'\n" unless $final_props{$key};
+ }
+ }
+
+ return \%final_props;
+}
+
+sub as_string {
+ my ( $self, $dsn ) = @_;
+ return $dsn unless ref $dsn;
+ return join(',',
+ map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
+ grep { defined $dsn->{$_} && $self->{opts}->{$_} }
+ sort keys %$dsn );
+}
+
+sub usage {
+ my ( $self ) = @_;
+ my $usage
+ = "DSN syntax is key=value[,key=value...] Allowable DSN keys:\n"
+ . " KEY COPY MEANING\n"
+ . " === ==== =============================================\n";
+ my %opts = %{$self->{opts}};
+ foreach my $key ( sort keys %opts ) {
+ $usage .= " $key "
+ . ($opts{$key}->{copy} ? 'yes ' : 'no ')
+ . ($opts{$key}->{desc} || '[No description]')
+ . "\n";
+ }
+ if ( (my $key = $self->prop('autokey')) ) {
+ $usage .= " If the DSN is a bareword, the word is treated as the '$key' key.\n";
+ }
+ return $usage;
+}
+
+# Supports PostgreSQL via the dbidriver element of $info, but assumes MySQL by
+# default.
+sub get_cxn_params {
+ my ( $self, $info ) = @_;
+ my $dsn;
+ my %opts = %{$self->{opts}};
+ my $driver = $self->prop('dbidriver') || '';
+ if ( $driver eq 'Pg' ) {
+ $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';'
+ . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" }
+ grep { defined $info->{$_} }
+ qw(h P));
+ }
+ else {
+ $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';'
+ . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" }
+ grep { defined $info->{$_} }
+ qw(F h P S A))
+ . ';mysql_read_default_group=client';
+ }
+ MKDEBUG && _d($dsn);
+ return ($dsn, $info->{u}, $info->{p});
+}
+
+
+# Fills in missing info from a DSN after successfully connecting to the server.
+sub fill_in_dsn {
+ my ( $self, $dbh, $dsn ) = @_;
+ my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name');
+ my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()');
+ $user =~ s/@.*//;
+ $dsn->{h} ||= $vars->{hostname}->{Value};
+ $dsn->{S} ||= $vars->{'socket'}->{Value};
+ $dsn->{P} ||= $vars->{port}->{Value};
+ $dsn->{u} ||= $user;
+ $dsn->{D} ||= $db;
+}
+
+sub get_dbh {
+ my ( $self, $cxn_string, $user, $pass, $opts ) = @_;
+ $opts ||= {};
+ my $defaults = {
+ AutoCommit => 0,
+ RaiseError => 1,
+ PrintError => 0,
+ mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0),
+ };
+ @{$defaults}{ keys %$opts } = values %$opts;
+ my $dbh;
+ my $tries = 2;
+ while ( !$dbh && $tries-- ) {
+ eval {
+ MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {',
+ join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}');
+ $dbh = DBI->connect($cxn_string, $user, $pass, $defaults);
+ # Immediately set character set and binmode on STDOUT.
+ if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
+ my $sql = "/*!40101 SET NAMES $charset*/";
+ MKDEBUG && _d("$dbh: $sql");
+ $dbh->do($sql);
+ MKDEBUG && _d('Enabling charset for STDOUT');
+ if ( $charset eq 'utf8' ) {
+ binmode(STDOUT, ':utf8')
+ or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
+ }
+ else {
+ binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR";
+ }
+ }
+ };
+ if ( !$dbh && $EVAL_ERROR ) {
+ MKDEBUG && _d($EVAL_ERROR);
+ if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) {
+ MKDEBUG && _d("Going to try again without utf8 support");
+ delete $defaults->{mysql_enable_utf8};
+ }
+ if ( !$tries ) {
+ die $EVAL_ERROR;
+ }
+ }
+ }
+ # If setvars exists and it's MySQL connection, set them
+ my $setvars = $self->prop('setvars');
+ if ( $cxn_string =~ m/mysql/i && $setvars ) {
+ my $sql = "SET $setvars";
+ MKDEBUG && _d("$dbh: $sql");
+ eval {
+ $dbh->do($sql);
+ };
+ if ( $EVAL_ERROR ) {
+ MKDEBUG && _d($EVAL_ERROR);
+ }
+ }
+ MKDEBUG && _d('DBH info: ',
+ $dbh,
+ Dumper($dbh->selectrow_hashref(
+ 'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')),
+ ' Connection info: ', ($dbh->{mysql_hostinfo} || 'undef'),
+ ' Character set info: ',
+ Dumper($dbh->selectall_arrayref(
+ 'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})),
+ ' $DBD::mysql::VERSION: ', $DBD::mysql::VERSION,
+ ' $DBI::VERSION: ', $DBI::VERSION,
+ );
+ return $dbh;
+}
+
+# Tries to figure out a hostname for the connection.
+sub get_hostname {
+ my ( $self, $dbh ) = @_;
+ if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) {
+ return $host;
+ }
+ my ( $hostname, $one ) = $dbh->selectrow_array(
+ 'SELECT /*!50038 @@hostname, */ 1');
+ return $hostname;
+}
+
+# Disconnects a database handle, but complains verbosely if there are any active
+# children. These are usually $sth handles that haven't been finish()ed.
+sub disconnect {
+ my ( $self, $dbh ) = @_;
+ MKDEBUG && $self->print_active_handles($dbh);
+ $dbh->disconnect;
+}
+
+sub print_active_handles {
+ my ( $self, $thing, $level ) = @_;
+ $level ||= 0;
+ printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level,
+ $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : ''))
+ or die "Cannot print: $OS_ERROR";
+ foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) {
+ $self->print_active_handles( $handle, $level + 1 );
+ }
+}
+
+sub _d {
+ my ($package, undef, $line) = caller 0;
+ @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
+ map { defined $_ ? $_ : 'undef' }
+ @_;
+ # Use $$ instead of $PID in case the package
+ # does not use English.
+ print "# $package:$line $$ ", @_, "\n";
+}
+
+1;
+
+package InnoDBParser;
+
+use Data::Dumper;
+$Data::Dumper::Sortkeys = 1;
+use English qw(-no_match_vars);
+use List::Util qw(max);
+
+# Some common patterns
+my $d = qr/(\d+)/; # Digit
+my $f = qr/(\d+\.\d+)/; # Float
+my $t = qr/(\d+ \d+)/; # Transaction ID
+my $i = qr/((?:\d{1,3}\.){3}\d+)/; # IP address
+my $n = qr/([^`\s]+)/; # MySQL object name
+my $w = qr/(\w+)/; # Words
+my $fl = qr/([\w\.\/]+) line $d/; # Filename and line number
+my $h = qr/((?:0x)?[0-9a-f]*)/; # Hex
+my $s = qr/(\d{6} .\d:\d\d:\d\d)/; # InnoDB timestamp
+
+# If you update this variable, also update the SYNOPSIS in the pod.
+my %innodb_section_headers = (
+ "TRANSACTIONS" => "tx",
+ "BUFFER POOL AND MEMORY" => "bp",
+ "SEMAPHORES" => "sm",
+ "LOG" => "lg",
+ "ROW OPERATIONS" => "ro",
+ "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib",
+ "FILE I/O" => "io",
+ "LATEST DETECTED DEADLOCK" => "dl",
+ "LATEST FOREIGN KEY ERROR" => "fk",
+);
+
+my %parser_for = (
+ tx => \&parse_tx_section,
+ bp => \&parse_bp_section,
+ sm => \&parse_sm_section,
+ lg => \&parse_lg_section,
+ ro => \&parse_ro_section,
+ ib => \&parse_ib_section,
+ io => \&parse_io_section,
+ dl => \&parse_dl_section,
+ fk => \&parse_fk_section,
+);
+
+my %fk_parser_for = (
+ Transaction => \&parse_fk_transaction_error,
+ Error => \&parse_fk_bad_constraint_error,
+ Cannot => \&parse_fk_cant_drop_parent_error,
+);
+
+# A thread's proc_info can be at least 98 different things I've found in the
+# source. Fortunately, most of them begin with a gerunded verb. These are
+# the ones that don't.
+my %is_proc_info = (
+ 'After create' => 1,
+ 'Execution of init_command' => 1,
+ 'FULLTEXT initialization' => 1,
+ 'Reopen tables' => 1,
+ 'Repair done' => 1,
+ 'Repair with keycache' => 1,
+ 'System lock' => 1,
+ 'Table lock' => 1,
+ 'Thread initialized' => 1,
+ 'User lock' => 1,
+ 'copy to tmp table' => 1,
+ 'discard_or_import_tablespace' => 1,
+ 'end' => 1,
+ 'got handler lock' => 1,
+ 'got old table' => 1,
+ 'init' => 1,
+ 'key cache' => 1,
+ 'locks' => 1,
+ 'malloc' => 1,
+ 'query end' => 1,
+ 'rename result table' => 1,
+ 'rename' => 1,
+ 'setup' => 1,
+ 'statistics' => 1,
+ 'status' => 1,
+ 'table cache' => 1,
+ 'update' => 1,
+);
+
+sub new {
+ bless {}, shift;
+}
+
+# Parse the status and return it.
+# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c
+# Pass in the text to parse, whether to be in debugging mode, which sections
+# to parse (hashref; if empty, parse all), and whether to parse full info from
+# locks and such (probably shouldn't unless you need to).
+sub parse_status_text {
+ my ( $self, $fulltext, $debug, $sections, $full ) = @_;
+
+ die "I can't parse undef" unless defined $fulltext;
+ $fulltext =~ s/[\r\n]+/\n/g;
+
+ $sections ||= {};
+ die '$sections must be a hashref' unless ref($sections) eq 'HASH';
+
+ my %innodb_data = (
+ got_all => 0, # Whether I was able to get the whole thing
+ ts => '', # Timestamp the server put on it
+ last_secs => 0, # Num seconds the averages are over
+ sections => {}, # Parsed values from each section
+ );
+
+ if ( $debug ) {
+ $innodb_data{'fulltext'} = $fulltext;
+ }
+
+ # Get the most basic info about the status: beginning and end, and whether
+ # I got the whole thing (if there has been a big deadlock and there are
+ # too many locks to print, the output might be truncated)
+ my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m;
+ $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ];
+ $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'});
+ ( $innodb_data{'last_secs'} ) = $fulltext
+ =~ m/Per second averages calculated from the last $d seconds/;
+
+ ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/;
+ $innodb_data{'got_all'} = $got_all || 0;
+
+ # Split it into sections. Each section begins with
+ # -----
+ # LABEL
+ # -----
+ my %innodb_sections;
+ my @matches = $fulltext
+ =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs;
+ while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) {
+ $innodb_sections{$name} = [ $text, $end ? 1 : 0 ];
+ }
+ # The Row Operations section is a special case, because instead of ending
+ # with the beginning of another section, it ends with the end of the file.
+ # So this section is complete if the entire file is complete.
+ $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'};
+
+ # Just for sanity's sake, make sure I understand what to do with each
+ # section
+ eval {
+ foreach my $section ( keys %innodb_sections ) {
+ my $header = $innodb_section_headers{$section};
+ die "Unknown section $section in $fulltext\n"
+ unless $header;
+ $innodb_data{'sections'}->{ $header }
+ ->{'fulltext'} = $innodb_sections{$section}->[0];
+ $innodb_data{'sections'}->{ $header }
+ ->{'complete'} = $innodb_sections{$section}->[1];
+ }
+ };
+ if ( $EVAL_ERROR ) {
+ _debug( $debug, $EVAL_ERROR);
+ }
+
+ # ################################################################
+ # Parse the detailed data out of the sections.
+ # ################################################################
+ eval {
+ foreach my $section ( keys %parser_for ) {
+ if ( defined $innodb_data{'sections'}->{$section}
+ && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) {
+ $parser_for{$section}->(
+ $innodb_data{'sections'}->{$section},
+ $innodb_data{'sections'}->{$section}->{'complete'},
+ $debug,
+ $full )
+ or delete $innodb_data{'sections'}->{$section};
+ }
+ else {
+ delete $innodb_data{'sections'}->{$section};
+ }
+ }
+ };
+ if ( $EVAL_ERROR ) {
+ _debug( $debug, $EVAL_ERROR);
+ }
+
+ return \%innodb_data;
+}
+
+# Parses the status text and returns it flattened out as a single hash.
+sub get_status_hash {
+ my ( $self, $fulltext, $debug, $sections, $full ) = @_;
+
+ # Parse the status text...
+ my $innodb_status
+ = $self->parse_status_text($fulltext, $debug, $sections, $full );
+
+ # Flatten the hierarchical structure into a single list by grabbing desired
+ # sections from it.
+ return
+ (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)),
+ (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} }
+ qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads
+ awe_mem_alloc pages_modified writes_pending_lru page_creates_sec
+ reads_pending pages_total buf_pool_hits writes_pending_single_page
+ page_writes_sec pages_read pages_written page_reads_sec
+ writes_pending_flush_list buf_pool_size add_pool_alloc
+ dict_mem_alloc pages_created buf_free complete )),
+ (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} }
+ qw( num_lock_structs history_list_len purge_done_for transactions
+ purge_undo_for is_truncated trx_id_counter complete )),
+ (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} }
+ qw( hash_table_size hash_searches_s non_hash_searches_s
+ bufs_in_node_heap used_cells size free_list_len seg_size inserts
+ merged_recs merges complete )),
+ (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} }
+ qw( log_ios_done pending_chkp_writes last_chkp log_ios_s
+ log_flushed_to log_seq_no pending_log_writes complete )),
+ (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} }
+ qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits
+ mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits
+ waits signal_count reservation_count complete )),
+ (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} }
+ qw( queries_in_queue n_reserved_extents main_thread_state
+ main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec
+ read_views_open num_rows_upd num_rows_ins num_rows_read
+ queries_inside num_rows_del complete )),
+ (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} }
+ qw( trigger parent_table child_index parent_index attempted_op
+ child_db timestring fk_name records col_name reason txn parent_db
+ type child_table parent_col complete )),
+ (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} }
+ qw( pending_buffer_pool_flushes pending_pwrites pending_preads
+ pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios
+ reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s
+ threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs
+ pending_log_flushes complete )),
+ (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} }
+ qw( timestring rolled_back txns complete ));
+
+}
+
+sub ts_to_string {
+ my $parts = shift;
+ return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts);
+}
+
+sub parse_innodb_timestamp {
+ my $text = shift;
+ my ( $y, $m, $d, $h, $i, $s )
+ = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/;
+ die("Can't get timestamp from $text\n") unless $y;
+ $y += 2000;
+ return ( $y, $m, $d, $h, $i, $s );
+}
+
+sub parse_fk_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ my $fulltext = $section->{'fulltext'};
+
+ return 0 unless $fulltext;
+
+ my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m;
+ $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
+ $section->{'timestring'} = ts_to_string($section->{'ts'});
+ $section->{'type'} = $type;
+
+ # Decide which type of FK error happened, and dispatch to the right parser.
+ if ( $type && $fk_parser_for{$type} ) {
+ $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full );
+ }
+
+ delete $section->{'fulltext'} unless $debug;
+
+ return 1;
+}
+
+sub parse_fk_cant_drop_parent_error {
+ my ( $section, $complete, $debug, $fulltext, $full ) = @_;
+
+ # Parse the parent/child table info out
+ @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext
+ =~ m{Cannot $w table `(.*)/(.*)`}m;
+ @{$section}{ qw(child_db child_table) } = $fulltext
+ =~ m{because it is referenced by `(.*)/(.*)`}m;
+
+ ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s;
+ $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm
+ if $section->{'reason'};
+
+ # Certain data may not be present. Make them '' if not present.
+ map { $section->{$_} ||= "" }
+ qw(child_index fk_name col_name parent_col);
+}
+
+# See dict/dict0dict.c, function dict_foreign_error_report
+# I don't care much about these. There are lots of different messages, and
+# they come from someone trying to create a foreign key, or similar
+# statements. They aren't indicative of some transaction trying to insert,
+# delete or update data. Sometimes it is possible to parse out a lot of
+# information about the tables and indexes involved, but often the message
+# contains the DDL string the user entered, which is way too much for this
+# module to try to handle.
+sub parse_fk_bad_constraint_error {
+ my ( $section, $complete, $debug, $fulltext, $full ) = @_;
+
+ # Parse the parent/child table and index info out
+ @{$section}{ qw(child_db child_table) } = $fulltext
+ =~ m{Error in foreign key constraint of table (.*)/(.*):$}m;
+ $section->{'attempted_op'} = 'DDL';
+
+ # FK name, parent info... if possible.
+ @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
+ = $fulltext
+ =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/;
+
+ if ( !defined($section->{'fk_name'}) ) {
+ # Try to parse SQL a user might have typed in a CREATE statement or such
+ @{$section}{ qw(col_name parent_db parent_table parent_col) }
+ = $fulltext
+ =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i;
+ }
+ $section->{'parent_db'} ||= $section->{'child_db'};
+
+ # Name of the child index (index in the same table where the FK is, see
+ # definition of dict_foreign_struct in include/dict0mem.h, where it is
+ # called foreign_index, as opposed to referenced_index which is in the
+ # parent table. This may not be possible to find.
+ @{$section}{ qw(child_index) } = $fulltext
+ =~ m/^The index in the foreign key in table is $n$/m;
+
+ @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms;
+ $section->{'reason'} =~ s/\s+/ /g
+ if $section->{'reason'};
+
+ # Certain data may not be present. Make them '' if not present.
+ map { $section->{$_} ||= "" }
+ qw(child_index fk_name col_name parent_table parent_col);
+}
+
+# see source file row/row0ins.c
+sub parse_fk_transaction_error {
+ my ( $section, $complete, $debug, $fulltext, $full ) = @_;
+
+ # Parse the txn info out
+ my ( $txn ) = $fulltext
+ =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s;
+ if ( $txn ) {
+ $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full );
+ }
+
+ # Parse the parent/child table and index info out. There are two types: an
+ # update or a delete of a parent record leaves a child orphaned
+ # (row_ins_foreign_report_err), and an insert or update of a child record has
+ # no matching parent record (row_ins_foreign_report_add_err).
+
+ @{$section}{ qw(reason child_db child_table) }
+ = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m;
+
+ @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
+ = $fulltext
+ =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/;
+ $section->{'parent_db'} ||= $section->{'child_db'};
+
+ # Special case, which I don't know how to trigger, but see
+ # innobase/row/row0ins.c row_ins_check_foreign_constraint
+ if ( $fulltext =~ m/ibd file does not currently exist!/ ) {
+ my ( $attempted_op, $index, $records )
+ = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm;
+ $section->{'child_index'} = $index;
+ $section->{'attempted_op'} = $attempted_op || '';
+ if ( $records && $full ) {
+ ( $section->{'records'} )
+ = parse_innodb_record_dump( $records, $complete, $debug );
+ }
+ @{$section}{qw(parent_db parent_table)}
+ =~ m/^But the parent table `$n`\.`$n`$/m;
+ }
+ else {
+ my ( $attempted_op, $which, $index )
+ = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m;
+ if ( $which ) {
+ $section->{$which . '_index'} = $index;
+ $section->{'attempted_op'} = $attempted_op || '';
+
+ # Parse out the related records in the other table.
+ my ( $search_index, $records );
+ if ( $which eq 'child' ) {
+ ( $search_index, $records ) = $fulltext
+ =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms;
+ $section->{'parent_index'} = $search_index;
+ }
+ else {
+ ( $search_index, $records ) = $fulltext
+ =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms;
+ $section->{'child_index'} = $search_index;
+ }
+ if ( $records && $full ) {
+ $section->{'records'}
+ = parse_innodb_record_dump( $records, $complete, $debug );
+ }
+ else {
+ $section->{'records'} = '';
+ }
+ }
+ }
+
+ # Parse out the tuple trying to be updated, deleted or inserted.
+ my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m;
+ if ( $trigger ) {
+ $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug );
+ }
+
+ # Certain data may not be present. Make them '' if not present.
+ map { $section->{$_} ||= "" }
+ qw(child_index fk_name col_name parent_table parent_col);
+}
+
+# There are new-style and old-style record formats. See rem/rem0rec.c
+# TODO: write some tests for this
+sub parse_innodb_record_dump {
+ my ( $dump, $complete, $debug ) = @_;
+ return undef unless $dump;
+
+ my $result = {};
+
+ if ( $dump =~ m/PHYSICAL RECORD/ ) {
+ my $style = $dump =~ m/compact format/ ? 'new' : 'old';
+ $result->{'style'} = $style;
+
+ # This is a new-style record.
+ if ( $style eq 'new' ) {
+ @{$result}{qw( heap_no type num_fields info_bits )}
+ = $dump
+ =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m;
+ }
+
+ # OK, it's old-style. Unfortunately there are variations here too.
+ elsif ( $dump =~ m/-byte offs / ) {
+ # Older-old style.
+ @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
+ = $dump
+ =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m;
+ if ( $dump !~ m/-byte offs TRUE/ ) {
+ $result->{'byte_offset'} = 0;
+ }
+ }
+ else {
+ # Newer-old style.
+ @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
+ = $dump
+ =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m;
+ }
+
+ }
+ else {
+ $result->{'style'} = 'tuple';
+ @{$result}{qw( type num_fields )}
+ = $dump =~ m/^(DATA TUPLE): $d fields;$/m;
+ }
+
+ # Fill in default values for things that couldn't be parsed.
+ map { $result->{$_} ||= 0 }
+ qw(heap_no num_fields byte_offset info_bits);
+ map { $result->{$_} ||= '' }
+ qw(style type );
+
+ my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm;
+ $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ];
+
+ return $result;
+}
+
+# New/old-style applies here. See rem/rem0rec.c
+# $text should not include the leading space or the second trailing semicolon.
+sub parse_field {
+ my ( $text, $complete, $debug ) = @_;
+
+ # Sample fields:
+ # '4: SQL NULL, size 4 '
+ # '1: len 6; hex 000000005601; asc V ;'
+ # '6: SQL NULL'
+ # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)'
+ my ( $id, $nullsize, $len, $hex, $asc, $truncated );
+ ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/;
+ if ( !defined($id) ) {
+ ( $id ) = $text =~ m/^$d: SQL NULL$/;
+ }
+ if ( !defined($id) ) {
+ ( $id, $len, $hex, $asc, $truncated )
+ = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/;
+ }
+
+ die "Could not parse this field: '$text'" unless defined $id;
+ return {
+ id => $id,
+ len => defined($len) ? $len : defined($nullsize) ? $nullsize : 0,
+ 'hex' => defined($hex) ? $hex : '',
+ asc => defined($asc) ? $asc : '',
+ trunc => $truncated ? 1 : 0,
+ };
+
+}
+
+sub parse_dl_section {
+ my ( $dl, $complete, $debug, $full ) = @_;
+ return unless $dl;
+ my $fulltext = $dl->{'fulltext'};
+ return 0 unless $fulltext;
+
+ my ( $ts ) = $fulltext =~ m/^$s$/m;
+ return 0 unless $ts;
+
+ $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
+ $dl->{'timestring'} = ts_to_string($dl->{'ts'});
+ $dl->{'txns'} = {};
+
+ my @sections
+ = $fulltext
+ =~ m{
+ ^\*{3}\s([^\n]*) # *** (1) WAITING FOR THIS...
+ (.*?) # Followed by anything, non-greedy
+ (?=(?:^\*{3})|\z) # Followed by another three stars or EOF
+ }gmsx;
+
+
+ # Loop through each section. There are no assumptions about how many
+ # there are, who holds and wants what locks, and who gets rolled back.
+ while ( my ($header, $body) = splice(@sections, 0, 2) ) {
+ my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/;
+ next unless $txn_id;
+ $dl->{'txns'}->{$txn_id} ||= {};
+ my $txn = $dl->{'txns'}->{$txn_id};
+
+ if ( $what eq 'TRANSACTION' ) {
+ $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full );
+ }
+ else {
+ push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full );
+ }
+ }
+
+ @{ $dl }{ qw(rolled_back) }
+ = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m;
+
+ # Make sure certain values aren't undef
+ map { $dl->{$_} ||= '' } qw(rolled_back);
+
+ delete $dl->{'fulltext'} unless $debug;
+ return 1;
+}
+
+sub parse_innodb_record_locks {
+ my ( $text, $complete, $debug, $full ) = @_;
+ my @result;
+
+ foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) {
+ my $hash = {};
+ @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) }
+ = $lock
+ =~ m{^(RECORD|TABLE) LOCKS? (?:space id $d page no $d n bits $d index `?$n`? of )?table `$n(?:/|`\.`)$n` trx id $t lock.mode (\S+)}m;
+ ( $hash->{'special'} )
+ = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m;
+ $hash->{'insert_intention'}
+ = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0;
+ $hash->{'waiting'}
+ = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0;
+
+ # Some things may not be in the text, so make sure they are not
+ # undef.
+ map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id);
+ map { $hash->{$_} ||= "" } qw(index special);
+ push @result, $hash;
+ }
+
+ return @result;
+}
+
+sub parse_tx_text {
+ my ( $txn, $complete, $debug, $full ) = @_;
+
+ my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id )
+ = $txn
+ =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m;
+ my ( $thread_status, $thread_decl_inside )
+ = $txn
+ =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m;
+
+ # Parsing the line that begins 'MySQL thread id' is complicated. The only
+ # thing always in the line is the thread and query id. See function
+ # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc.
+ my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m;
+ my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status );
+
+ if ( $thread_line ) {
+ # These parts can always be gotten.
+ ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m;
+
+ # If it's a master/slave thread, "Has (read|sent) all" may be the thread's
+ # proc_info. In these cases, there won't be any host/ip/user info
+ ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m;
+ if ( defined($query_status) ) {
+ $user = 'system user';
+ }
+
+ # It may be the case that the query id is the last thing in the line.
+ elsif ( $thread_line =~ m/query id \d+ / ) {
+ # The IP address is the only non-word thing left, so it's the most
+ # useful marker for where I have to start guessing.
+ ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m;
+ if ( defined $ip ) {
+ ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/;
+ }
+ else { # OK, there wasn't an IP address.
+ # There might not be ANYTHING except the query status.
+ ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/;
+ if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) {
+ # The remaining tokens are, in order: hostname, user, query_status.
+ # It's basically impossible to know which is which.
+ ( $hostname, $user, $query_status ) = $thread_line
+ =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m;
+ }
+ else {
+ $user = 'system user';
+ }
+ }
+ }
+ }
+
+ my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries )
+ = $txn
+ =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m;
+ my ( $lock_wait_time )
+ = $txn
+ =~ m/^------- TRX HAS BEEN WAITING $d SEC/m;
+
+ my $locks;
+ # If the transaction has locks, grab the locks.
+ if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) {
+ $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)];
+ }
+
+ my ( $tables_in_use, $tables_locked )
+ = $txn
+ =~ m/^mysql tables in use $d, locked $d$/m;
+ my ( $txn_doesnt_see_ge, $txn_sees_lt )
+ = $txn
+ =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m;
+ my $has_read_view = defined($txn_doesnt_see_ge);
+ # Only a certain number of bytes of the query text are included here, at least
+ # under some circumstances. Some versions include 300, some 600.
+ my ( $query_text )
+ = $txn
+ =~ m{
+ ^MySQL\sthread\sid\s[^\n]+\n # This comes before the query text
+ (.*?) # The query text
+ (?= # Followed by any of...
+ ^Trx\sread\sview
+ |^-------\sTRX\sHAS\sBEEN\sWAITING
+ |^TABLE\sLOCK
+ |^RECORD\sLOCKS\sspace\sid
+ |^(?:---)?TRANSACTION
+ |^\*\*\*\s\(\d\)
+ |\Z
+ )
+ }xms;
+ if ( $query_text ) {
+ $query_text =~ s/\s+$//;
+ }
+ else {
+ $query_text = '';
+ }
+
+ my %stuff = (
+ active_secs => $active_secs,
+ has_read_view => $has_read_view,
+ heap_size => $heap_size,
+ hostname => $hostname,
+ ip => $ip,
+ lock_structs => $lock_structs,
+ lock_wait_status => $lock_wait_status,
+ lock_wait_time => $lock_wait_time,
+ mysql_thread_id => $mysql_thread_id,
+ os_thread_id => $os_thread_id,
+ proc_no => $proc_no,
+ query_id => $query_id,
+ query_status => $query_status,
+ query_text => $query_text,
+ row_locks => $row_locks,
+ tables_in_use => $tables_in_use,
+ tables_locked => $tables_locked,
+ thread_decl_inside => $thread_decl_inside,
+ thread_status => $thread_status,
+ txn_doesnt_see_ge => $txn_doesnt_see_ge,
+ txn_id => $txn_id,
+ txn_sees_lt => $txn_sees_lt,
+ txn_status => $txn_status,
+ undo_log_entries => $undo_log_entries,
+ user => $user,
+ );
+ $stuff{'fulltext'} = $txn if $debug;
+ $stuff{'locks'} = $locks if $locks;
+
+ # Some things may not be in the txn text, so make sure they are not
+ # undef.
+ map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs
+ tables_in_use undo_log_entries tables_locked has_read_view
+ thread_decl_inside lock_wait_time proc_no row_locks);
+ map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge
+ txn_sees_lt query_status ip query_text lock_wait_status user);
+ $stuff{'hostname'} ||= $stuff{'ip'};
+
+ return \%stuff;
+}
+
+sub parse_tx_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return unless $section && $section->{'fulltext'};
+ my $fulltext = $section->{'fulltext'};
+ $section->{'transactions'} = [];
+
+ # Handle the individual transactions
+ my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs;
+ foreach my $txn ( @transactions ) {
+ my $stuff = parse_tx_text( $txn, $complete, $debug, $full );
+ delete $stuff->{'fulltext'} unless $debug;
+ push @{$section->{'transactions'}}, $stuff;
+ }
+
+ # Handle the general info
+ @{$section}{ 'trx_id_counter' }
+ = $fulltext =~ m/^Trx id counter $t$/m;
+ @{$section}{ 'purge_done_for', 'purge_undo_for' }
+ = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m;
+ @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions
+ = $fulltext =~ m/^History list length $d$/m;
+ @{$section}{ 'num_lock_structs' }
+ = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m;
+ @{$section}{ 'is_truncated' }
+ = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0;
+
+ # Fill in things that might not be present
+ foreach ( qw(history_list_len) ) {
+ $section->{$_} ||= 0;
+ }
+
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+# I've read the source for this section.
+sub parse_ro_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return unless $section && $section->{'fulltext'};
+ my $fulltext = $section->{'fulltext'};
+
+ # Grab the info
+ @{$section}{ 'queries_inside', 'queries_in_queue' }
+ = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m;
+ ( $section->{ 'read_views_open' } )
+ = $fulltext =~ m/^$d read views open inside InnoDB$/m;
+ ( $section->{ 'n_reserved_extents' } )
+ = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m;
+ @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' }
+ = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m;
+ @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' }
+ = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m;
+ @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' }
+ = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m;
+ $section->{'main_thread_proc_no'} ||= 0;
+
+ map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents);
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+sub parse_lg_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return unless $section;
+ my $fulltext = $section->{'fulltext'};
+
+ # Grab the info
+ ( $section->{ 'log_seq_no' } )
+ = $fulltext =~ m/Log sequence number \s*(\d.*)$/m;
+ ( $section->{ 'log_flushed_to' } )
+ = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m;
+ ( $section->{ 'last_chkp' } )
+ = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m;
+ @{$section}{ 'pending_log_writes', 'pending_chkp_writes' }
+ = $fulltext =~ m/$d pending log writes, $d pending chkp writes/;
+ @{$section}{ 'log_ios_done', 'log_ios_s' }
+ = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#;
+
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+sub parse_ib_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return unless $section && $section->{'fulltext'};
+ my $fulltext = $section->{'fulltext'};
+
+ # Some servers will output ibuf information for tablespace 0, as though there
+ # might be many tablespaces with insert buffers. (In practice I believe
+ # the source code shows there will only ever be one). I have to parse both
+ # cases here, but I assume there will only be one.
+ @{$section}{ 'size', 'free_list_len', 'seg_size' }
+ = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m;
+ @{$section}{ 'inserts', 'merged_recs', 'merges' }
+ = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m;
+
+ @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' }
+ = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m;
+ @{$section}{ 'hash_searches_s', 'non_hash_searches_s' }
+ = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m;
+
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+sub parse_wait_array {
+ my ( $text, $complete, $debug, $full ) = @_;
+ my %result;
+
+ @result{ qw(thread waited_at_filename waited_at_line waited_secs) }
+ = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m;
+
+ # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED,
+ # there will be different text output
+ if ( $text =~ m/^Mutex at/m ) {
+ $result{'request_type'} = 'M';
+ @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) }
+ = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m;
+ @result{ qw( waiters_flag )}
+ = $text =~ m/^waiters flag $d$/m;
+ }
+ else {
+ @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) }
+ = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m;
+ @result{ qw( writer_thread writer_lock_mode ) }
+ = $text =~ m/^a writer \(thread id $d\) has reserved it in mode (.*)$/m;
+ @result{ qw( num_readers waiters_flag )}
+ = $text =~ m/^number of readers $d, waiters flag $d$/m;
+ @result{ qw(last_s_file_name last_s_line ) }
+ = $text =~ m/Last time read locked in file $fl$/m;
+ @result{ qw(last_x_file_name last_x_line ) }
+ = $text =~ m/Last time write locked in file $fl$/m;
+ }
+
+ $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1;
+ $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0;
+
+ # Because there are two code paths, some things won't get set.
+ map { $result{$_} ||= '' }
+ qw(last_s_file_name last_x_file_name writer_lock_mode);
+ map { $result{$_} ||= 0 }
+ qw(num_readers lock_var last_s_line last_x_line writer_thread);
+
+ return \%result;
+}
+
+sub parse_sm_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return 0 unless $section && $section->{'fulltext'};
+ my $fulltext = $section->{'fulltext'};
+
+ # Grab the info
+ @{$section}{ 'reservation_count', 'signal_count' }
+ = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m;
+ @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' }
+ = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m;
+ @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' }
+ = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m;
+
+ # Look for info on waits.
+ my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms;
+ $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ];
+ $section->{'wait_array_size'} = scalar(@waits);
+
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+# I've read the source for this section.
+sub parse_bp_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return unless $section && $section->{'fulltext'};
+ my $fulltext = $section->{'fulltext'};
+
+ # Grab the info
+ @{$section}{ 'total_mem_alloc', 'add_pool_alloc' }
+ = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m;
+ @{$section}{'dict_mem_alloc'} = $fulltext =~ m/Dictionary memory allocated $d/;
+ @{$section}{'awe_mem_alloc'} = $fulltext =~ m/$d MB of AWE memory/;
+ @{$section}{'buf_pool_size'} = $fulltext =~ m/^Buffer pool size\s*$d$/m;
+ @{$section}{'buf_free'} = $fulltext =~ m/^Free buffers\s*$d$/m;
+ @{$section}{'pages_total'} = $fulltext =~ m/^Database pages\s*$d$/m;
+ @{$section}{'pages_modified'} = $fulltext =~ m/^Modified db pages\s*$d$/m;
+ @{$section}{'pages_read', 'pages_created', 'pages_written'}
+ = $fulltext =~ m/^Pages read $d, created $d, written $d$/m;
+ @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'}
+ = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m;
+ @{$section}{'buf_pool_hits', 'buf_pool_reads'}
+ = $fulltext =~ m{Buffer pool hit rate $d / $d$}m;
+ if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) {
+ @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0);
+ @{$section}{'buf_pool_hit_rate'} = '--';
+ }
+ else {
+ @{$section}{'buf_pool_hit_rate'}
+ = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m;
+ }
+ @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m;
+ @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' }
+ = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m;
+
+ map { $section->{$_} ||= 0 }
+ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page
+ awe_mem_alloc dict_mem_alloc);
+ @{$section}{'writes_pending'} = List::Util::sum(
+ @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) });
+
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+# I've read the source for this.
+sub parse_io_section {
+ my ( $section, $complete, $debug, $full ) = @_;
+ return unless $section && $section->{'fulltext'};
+ my $fulltext = $section->{'fulltext'};
+ $section->{'threads'} = {};
+
+ # Grab the I/O thread info
+ my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm;
+ foreach my $thread (@threads) {
+ my ( $tid, $state, $purpose, $event_set )
+ = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m;
+ if ( defined $tid ) {
+ $section->{'threads'}->{$tid} = {
+ thread => $tid,
+ state => $state,
+ purpose => $purpose,
+ event_set => $event_set ? 1 : 0,
+ };
+ }
+ }
+
+ # Grab the reads/writes/flushes info
+ @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' }
+ = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m;
+ @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' }
+ = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m;
+ @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' }
+ = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m;
+ @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' }
+ = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m;
+ @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' }
+ = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m;
+ @{$section}{ 'pending_preads', 'pending_pwrites' }
+ = $fulltext =~ m/$d pending preads, $d pending pwrites$/m;
+ @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0)
+ unless defined($section->{'pending_preads'});
+
+ delete $section->{'fulltext'} unless $debug;
+ return 1;
+}
+
+sub _debug {
+ my ( $debug, $msg ) = @_;
+ if ( $debug ) {
+ die $msg;
+ }
+ else {
+ warn $msg;
+ }
+ return 1;
+}
+
+1;
+
+# end_of_package InnoDBParser
+
+package main;
+
+use sigtrap qw(handler finish untrapped normal-signals);
+
+use Data::Dumper;
+use DBI;
+use English qw(-no_match_vars);
+use File::Basename qw(dirname);
+use File::Temp;
+use Getopt::Long;
+use List::Util qw(max min maxstr sum);
+use POSIX qw(ceil);
+use Time::HiRes qw(time sleep);
+use Term::ReadKey qw(ReadMode ReadKey);
+
+# License and warranty information. {{{1
+# ###########################################################################
+
+my $innotop_license = <<"LICENSE";
+
+This is innotop version $VERSION, a MySQL and InnoDB monitor.
+
+This program is copyright (c) 2006 Baron Schwartz.
+Feedback and improvements are welcome.
+
+THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
+systems, you can issue `man perlgpl' or `man perlartistic' to read these
+licenses.
+
+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.
+LICENSE
+
+# Configuration information and global setup {{{1
+# ###########################################################################
+
+# Really, really, super-global variables.
+my @config_versions = (
+ "000-000-000", "001-003-000", # config file was one big name-value hash.
+ "001-003-000", "001-004-002", # config file contained non-user-defined stuff.
+);
+
+my $clear_screen_sub;
+my $dsn_parser = new DSNParser();
+
+# This defines expected properties and defaults for the column definitions that
+# eventually end up in tbl_meta.
+my %col_props = (
+ hdr => '',
+ just => '-',
+ dec => 0, # Whether to align the column on the decimal point
+ num => 0,
+ label => '',
+ user => 0,
+ src => '',
+ tbl => '', # Helps when writing/reading custom columns in config files
+ minw => 0,
+ maxw => 0,
+ trans => [],
+ agg => 'first', # Aggregate function
+ aggonly => 0, # Whether to show only when tbl_meta->{aggregate} is true
+);
+
+# Actual DBI connections to MySQL servers.
+my %dbhs;
+
+# Command-line parameters {{{2
+# ###########################################################################
+
+my @opt_spec = (
+ { s => 'help', d => 'Show this help message' },
+ { s => 'color|C!', d => 'Use terminal coloring (default)', c => 'color' },
+ { s => 'config|c=s', d => 'Config file to read' },
+ { s => 'nonint|n', d => 'Non-interactive, output tab-separated fields' },
+ { s => 'count=i', d => 'Number of updates before exiting' },
+ { s => 'delay|d=f', d => 'Delay between updates in seconds', c => 'interval' },
+ { s => 'mode|m=s', d => 'Operating mode to start in', c => 'mode' },
+ { s => 'inc|i!', d => 'Measure incremental differences', c => 'status_inc' },
+ { s => 'write|w', d => 'Write running configuration into home directory if no config files were loaded' },
+ { s => 'skipcentral|s', d => 'Skip reading the central configuration file' },
+ { s => 'version', d => 'Output version information and exit' },
+ { s => 'user|u=s', d => 'User for login if not current user' },
+ { s => 'password|p=s', d => 'Password to use for connection' },
+ { s => 'host|h=s', d => 'Connect to host' },
+ { s => 'port|P=i', d => 'Port number to use for connection' },
+);
+
+# This is the container for the command-line options' values to be stored in
+# after processing. Initial values are defaults.
+my %opts = (
+ n => !( -t STDIN && -t STDOUT ), # If in/out aren't to terminals, we're interactive
+);
+# Post-process...
+my %opt_seen;
+foreach my $spec ( @opt_spec ) {
+ my ( $long, $short ) = $spec->{s} =~ m/^(\w+)(?:\|([^!+=]*))?/;
+ $spec->{k} = $short || $long;
+ $spec->{l} = $long;
+ $spec->{t} = $short;
+ $spec->{n} = $spec->{s} =~ m/!/;
+ $opts{$spec->{k}} = undef unless defined $opts{$spec->{k}};
+ die "Duplicate option $spec->{k}" if $opt_seen{$spec->{k}}++;
+}
+
+Getopt::Long::Configure('no_ignore_case', 'bundling');
+GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1;
+
+if ( $opts{version} ) {
+ print "innotop Ver $VERSION\n";
+ exit(0);
+}
+
+if ( $opts{c} and ! -f $opts{c} ) {
+ print $opts{c} . " doesn't exist. Exiting.\n";
+ exit(1);
+}
+if ( $opts{'help'} ) {
+ print "Usage: innotop <options> <innodb-status-file>\n\n";
+ my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec);
+ foreach my $spec ( sort { $a->{l} cmp $b->{l} } @opt_spec ) {
+ my $long = $spec->{n} ? "[no]$spec->{l}" : $spec->{l};
+ my $short = $spec->{t} ? "-$spec->{t}" : '';
+ printf(" --%-${maxw}s %-4s %s\n", $long, $short, $spec->{d});
+ }
+ print <<USAGE;
+
+innotop is a MySQL and InnoDB transaction/status monitor, like 'top' for
+MySQL. It displays queries, InnoDB transactions, lock waits, deadlocks,
+foreign key errors, open tables, replication status, buffer information,
+row operations, logs, I/O operations, load graph, and more. You can
+monitor many servers at once with innotop.
+
+USAGE
+ exit(1);
+}
+
+# Meta-data (table definitions etc) {{{2
+# ###########################################################################
+
+# Expressions {{{3
+# Convenience so I can copy/paste these in several places...
+# ###########################################################################
+my %exprs = (
+ Host => q{my $host = host || hostname || ''; ($host) = $host =~ m/^((?:[\d.]+(?=:))|(?:[a-zA-Z]\w+))/; return $host || ''},
+ Port => q{my ($p) = host =~ m/:(.*)$/; return $p || 0},
+ OldVersions => q{dulint_to_int(IB_tx_trx_id_counter) - dulint_to_int(IB_tx_purge_done_for)},
+ MaxTxnTime => q/max(map{ $_->{active_secs} } @{ IB_tx_transactions }) || 0/,
+ NumTxns => q{scalar @{ IB_tx_transactions } },
+ DirtyBufs => q{ $cur->{IB_bp_pages_modified} / ($cur->{IB_bp_buf_pool_size} || 1) },
+ BufPoolFill => q{ $cur->{IB_bp_pages_total} / ($cur->{IB_bp_buf_pool_size} || 1) },
+ ServerLoad => q{ $cur->{Threads_connected}/(Questions||1)/Uptime_hires },
+ TxnTimeRemain => q{ defined undo_log_entries && defined $pre->{undo_log_entries} && undo_log_entries < $pre->{undo_log_entries} ? undo_log_entries / (($pre->{undo_log_entries} - undo_log_entries)/((active_secs-$pre->{active_secs})||1))||1 : 0},
+ SlaveCatchupRate => ' defined $cur->{seconds_behind_master} && defined $pre->{seconds_behind_master} && $cur->{seconds_behind_master} < $pre->{seconds_behind_master} ? ($pre->{seconds_behind_master}-$cur->{seconds_behind_master})/($cur->{Uptime_hires}-$pre->{Uptime_hires}) : 0',
+ QcacheHitRatio => q{(Qcache_hits||0)/(((Com_select||0)+(Qcache_hits||0))||1)},
+);
+
+# ###########################################################################
+# Column definitions {{{3
+# Defines every column in every table. A named column has the following
+# properties:
+# * hdr Column header/title
+# * label Documentation for humans.
+# * num Whether it's numeric (for sorting).
+# * just Alignment; generated from num, user-overridable in tbl_meta
+# * minw, maxw Auto-generated, user-overridable.
+# Values from this hash are just copied to tbl_meta, which is where everything
+# else in the program should read from.
+# ###########################################################################
+
+my %columns = (
+ active_secs => { hdr => 'SecsActive', num => 1, label => 'Seconds transaction has been active', },
+ add_pool_alloc => { hdr => 'Add\'l Pool', num => 1, label => 'Additonal pool allocated' },
+ attempted_op => { hdr => 'Action', num => 0, label => 'The action that caused the error' },
+ awe_mem_alloc => { hdr => 'AWE Memory', num => 1, label => '[Windows] AWE memory allocated' },
+ binlog_cache_overflow => { hdr => 'Binlog Cache', num => 1, label => 'Transactions too big for binlog cache that went to disk' },
+ binlog_do_db => { hdr => 'Binlog Do DB', num => 0, label => 'binlog-do-db setting' },
+ binlog_ignore_db => { hdr => 'Binlog Ignore DB', num => 0, label => 'binlog-ignore-db setting' },
+ bps_in => { hdr => 'BpsIn', num => 1, label => 'Bytes per second received by the server', },
+ bps_out => { hdr => 'BpsOut', num => 1, label => 'Bytes per second sent by the server', },
+ buf_free => { hdr => 'Free Bufs', num => 1, label => 'Buffers free in the buffer pool' },
+ buf_pool_hit_rate => { hdr => 'Hit Rate', num => 0, label => 'Buffer pool hit rate' },
+ buf_pool_hits => { hdr => 'Hits', num => 1, label => 'Buffer pool hits' },
+ buf_pool_reads => { hdr => 'Reads', num => 1, label => 'Buffer pool reads' },
+ buf_pool_size => { hdr => 'Size', num => 1, label => 'Buffer pool size' },
+ bufs_in_node_heap => { hdr => 'Node Heap Bufs', num => 1, label => 'Buffers in buffer pool node heap' },
+ bytes_behind_master => { hdr => 'ByteLag', num => 1, label => 'Bytes the slave lags the master in binlog' },
+ cell_event_set => { hdr => 'Ending?', num => 1, label => 'Whether the cell event is set' },
+ cell_waiting => { hdr => 'Waiting?', num => 1, label => 'Whether the cell is waiting' },
+ child_db => { hdr => 'Child DB', num => 0, label => 'The database of the child table' },
+ child_index => { hdr => 'Child Index', num => 0, label => 'The index in the child table' },
+ child_table => { hdr => 'Child Table', num => 0, label => 'The child table' },
+ cmd => { hdr => 'Cmd', num => 0, label => 'Type of command being executed', },
+ cnt => { hdr => 'Cnt', num => 0, label => 'Count', agg => 'count', aggonly => 1 },
+ connect_retry => { hdr => 'Connect Retry', num => 1, label => 'Slave connect-retry timeout' },
+ cxn => { hdr => 'CXN', num => 0, label => 'Connection from which the data came', },
+ db => { hdr => 'DB', num => 0, label => 'Current database', },
+ dict_mem_alloc => { hdr => 'Dict Mem', num => 1, label => 'Dictionary memory allocated' },
+ dirty_bufs => { hdr => 'Dirty Buf', num => 1, label => 'Dirty buffer pool pages' },
+ dl_txn_num => { hdr => 'Num', num => 0, label => 'Deadlocked transaction number', },
+ event_set => { hdr => 'Evt Set?', num => 1, label => '[Win32] if a wait event is set', },
+ exec_master_log_pos => { hdr => 'Exec Master Log Pos', num => 1, label => 'Exec Master Log Position' },
+ fk_name => { hdr => 'Constraint', num => 0, label => 'The name of the FK constraint' },
+ free_list_len => { hdr => 'Free List Len', num => 1, label => 'Length of the free list' },
+ has_read_view => { hdr => 'Rd View', num => 1, label => 'Whether the transaction has a read view' },
+ hash_searches_s => { hdr => 'Hash/Sec', num => 1, label => 'Number of hash searches/sec' },
+ hash_table_size => { hdr => 'Size', num => 1, label => 'Number of non-hash searches/sec' },
+ heap_no => { hdr => 'Heap', num => 1, label => 'Heap number' },
+ heap_size => { hdr => 'Heap', num => 1, label => 'Heap size' },
+ history_list_len => { hdr => 'History', num => 1, label => 'History list length' },
+ host_and_domain => { hdr => 'Host', num => 0, label => 'Hostname/IP and domain' },
+ host_and_port => { hdr => 'Host/IP', num => 0, label => 'Hostname or IP address, and port number', },
+ hostname => { hdr => 'Host', num => 0, label => 'Hostname' },
+ index => { hdr => 'Index', num => 0, label => 'The index involved' },
+ index_ref => { hdr => 'Index Ref', num => 0, label => 'Index referenced' },
+ info => { hdr => 'Query', num => 0, label => 'Info or the current query', },
+ insert_intention => { hdr => 'Ins Intent', num => 1, label => 'Whether the thread was trying to insert' },
+ inserts => { hdr => 'Inserts', num => 1, label => 'Inserts' },
+ io_bytes_s => { hdr => 'Bytes/Sec', num => 1, label => 'Average I/O bytes/sec' },
+ io_flush_type => { hdr => 'Flush Type', num => 0, label => 'I/O Flush Type' },
+ io_fsyncs_s => { hdr => 'fsyncs/sec', num => 1, label => 'I/O fsyncs/sec' },
+ io_reads_s => { hdr => 'Reads/Sec', num => 1, label => 'Average I/O reads/sec' },
+ io_writes_s => { hdr => 'Writes/Sec', num => 1, label => 'Average I/O writes/sec' },
+ ip => { hdr => 'IP', num => 0, label => 'IP address' },
+ is_name_locked => { hdr => 'Locked', num => 1, label => 'Whether table is name locked', },
+ key_buffer_hit => { hdr => 'KCacheHit', num => 1, label => 'Key cache hit ratio', },
+ key_len => { hdr => 'Key Length', num => 1, label => 'Number of bytes used in the key' },
+ last_chkp => { hdr => 'Last Checkpoint', num => 0, label => 'Last log checkpoint' },
+ last_errno => { hdr => 'Last Errno', num => 1, label => 'Last error number' },
+ last_error => { hdr => 'Last Error', num => 0, label => 'Last error' },
+ last_s_file_name => { hdr => 'S-File', num => 0, label => 'Filename where last read locked' },
+ last_s_line => { hdr => 'S-Line', num => 1, label => 'Line where last read locked' },
+ last_x_file_name => { hdr => 'X-File', num => 0, label => 'Filename where last write locked' },
+ last_x_line => { hdr => 'X-Line', num => 1, label => 'Line where last write locked' },
+ last_pct => { hdr => 'Pct', num => 1, label => 'Last Percentage' },
+ last_total => { hdr => 'Last Total', num => 1, label => 'Last Total' },
+ last_value => { hdr => 'Last Incr', num => 1, label => 'Last Value' },
+ load => { hdr => 'Load', num => 1, label => 'Server load' },
+ lock_cfile_name => { hdr => 'Crtd File', num => 0, label => 'Filename where lock created' },
+ lock_cline => { hdr => 'Crtd Line', num => 1, label => 'Line where lock created' },
+ lock_mem_addr => { hdr => 'Addr', num => 0, label => 'The lock memory address' },
+ lock_mode => { hdr => 'Mode', num => 0, label => 'The lock mode' },
+ lock_structs => { hdr => 'LStrcts', num => 1, label => 'Number of lock structs' },
+ lock_type => { hdr => 'Type', num => 0, label => 'The lock type' },
+ lock_var => { hdr => 'Lck Var', num => 1, label => 'The lock variable' },
+ lock_wait_time => { hdr => 'Wait', num => 1, label => 'How long txn has waited for a lock' },
+ log_flushed_to => { hdr => 'Flushed To', num => 0, label => 'Log position flushed to' },
+ log_ios_done => { hdr => 'IO Done', num => 1, label => 'Log I/Os done' },
+ log_ios_s => { hdr => 'IO/Sec', num => 1, label => 'Average log I/Os per sec' },
+ log_seq_no => { hdr => 'Sequence No.', num => 0, label => 'Log sequence number' },
+ main_thread_id => { hdr => 'Main Thread ID', num => 1, label => 'Main thread ID' },
+ main_thread_proc_no => { hdr => 'Main Thread Proc', num => 1, label => 'Main thread process number' },
+ main_thread_state => { hdr => 'Main Thread State', num => 0, label => 'Main thread state' },
+ master_file => { hdr => 'File', num => 0, label => 'Master file' },
+ master_host => { hdr => 'Master', num => 0, label => 'Master server hostname' },
+ master_log_file => { hdr => 'Master Log File', num => 0, label => 'Master log file' },
+ master_port => { hdr => 'Master Port', num => 1, label => 'Master port' },
+ master_pos => { hdr => 'Position', num => 1, label => 'Master position' },
+ master_ssl_allowed => { hdr => 'Master SSL Allowed', num => 0, label => 'Master SSL Allowed' },
+ master_ssl_ca_file => { hdr => 'Master SSL CA File', num => 0, label => 'Master SSL Cert Auth File' },
+ master_ssl_ca_path => { hdr => 'Master SSL CA Path', num => 0, label => 'Master SSL Cert Auth Path' },
+ master_ssl_cert => { hdr => 'Master SSL Cert', num => 0, label => 'Master SSL Cert' },
+ master_ssl_cipher => { hdr => 'Master SSL Cipher', num => 0, label => 'Master SSL Cipher' },
+ master_ssl_key => { hdr => 'Master SSL Key', num => 0, label => 'Master SSL Key' },
+ master_user => { hdr => 'Master User', num => 0, label => 'Master username' },
+ max_txn => { hdr => 'MaxTxnTime', num => 1, label => 'MaxTxn' },
+ merged_recs => { hdr => 'Merged Recs', num => 1, label => 'Merged records' },
+ merges => { hdr => 'Merges', num => 1, label => 'Merges' },
+ mutex_os_waits => { hdr => 'Waits', num => 1, label => 'Mutex OS Waits' },
+ mutex_spin_rounds => { hdr => 'Rounds', num => 1, label => 'Mutex Spin Rounds' },
+ mutex_spin_waits => { hdr => 'Spins', num => 1, label => 'Mutex Spin Waits' },
+ mysql_thread_id => { hdr => 'ID', num => 1, label => 'MySQL connection (thread) ID', },
+ name => { hdr => 'Name', num => 0, label => 'Variable Name' },
+ n_bits => { hdr => '# Bits', num => 1, label => 'Number of bits' },
+ non_hash_searches_s => { hdr => 'Non-Hash/Sec', num => 1, label => 'Non-hash searches/sec' },
+ num_deletes => { hdr => 'Del', num => 1, label => 'Number of deletes' },
+ num_deletes_sec => { hdr => 'Del/Sec', num => 1, label => 'Number of deletes' },
+ num_inserts => { hdr => 'Ins', num => 1, label => 'Number of inserts' },
+ num_inserts_sec => { hdr => 'Ins/Sec', num => 1, label => 'Number of inserts' },
+ num_readers => { hdr => 'Readers', num => 1, label => 'Number of readers' },
+ num_reads => { hdr => 'Read', num => 1, label => 'Number of reads' },
+ num_reads_sec => { hdr => 'Read/Sec', num => 1, label => 'Number of reads' },
+ num_res_ext => { hdr => 'BTree Extents', num => 1, label => 'Number of extents reserved for B-Tree' },
+ num_rows => { hdr => 'Row Count', num => 1, label => 'Number of rows estimated to examine' },
+ num_times_open => { hdr => 'In Use', num => 1, label => '# times table is opened', },
+ num_txns => { hdr => 'Txns', num => 1, label => 'Number of transactions' },
+ num_updates => { hdr => 'Upd', num => 1, label => 'Number of updates' },
+ num_updates_sec => { hdr => 'Upd/Sec', num => 1, label => 'Number of updates' },
+ os_file_reads => { hdr => 'OS Reads', num => 1, label => 'OS file reads' },
+ os_file_writes => { hdr => 'OS Writes', num => 1, label => 'OS file writes' },
+ os_fsyncs => { hdr => 'OS fsyncs', num => 1, label => 'OS fsyncs' },
+ os_thread_id => { hdr => 'OS Thread', num => 1, label => 'The operating system thread ID' },
+ p_aio_writes => { hdr => 'Async Wrt', num => 1, label => 'Pending asynchronous I/O writes' },
+ p_buf_pool_flushes => { hdr => 'Buffer Pool Flushes', num => 1, label => 'Pending buffer pool flushes' },
+ p_ibuf_aio_reads => { hdr => 'IBuf Async Rds', num => 1, label => 'Pending insert buffer asynch I/O reads' },
+ p_log_flushes => { hdr => 'Log Flushes', num => 1, label => 'Pending log flushes' },
+ p_log_ios => { hdr => 'Log I/Os', num => 1, label => 'Pending log I/O operations' },
+ p_normal_aio_reads => { hdr => 'Async Rds', num => 1, label => 'Pending asynchronous I/O reads' },
+ p_preads => { hdr => 'preads', num => 1, label => 'Pending p-reads' },
+ p_pwrites => { hdr => 'pwrites', num => 1, label => 'Pending p-writes' },
+ p_sync_ios => { hdr => 'Sync I/Os', num => 1, label => 'Pending synchronous I/O operations' },
+ page_creates_sec => { hdr => 'Creates/Sec', num => 1, label => 'Page creates/sec' },
+ page_no => { hdr => 'Page', num => 1, label => 'Page number' },
+ page_reads_sec => { hdr => 'Reads/Sec', num => 1, label => 'Page reads per second' },
+ page_writes_sec => { hdr => 'Writes/Sec', num => 1, label => 'Page writes per second' },
+ pages_created => { hdr => 'Created', num => 1, label => 'Pages created' },
+ pages_modified => { hdr => 'Dirty Pages', num => 1, label => 'Pages modified (dirty)' },
+ pages_read => { hdr => 'Reads', num => 1, label => 'Pages read' },
+ pages_total => { hdr => 'Pages', num => 1, label => 'Pages total' },
+ pages_written => { hdr => 'Writes', num => 1, label => 'Pages written' },
+ parent_col => { hdr => 'Parent Column', num => 0, label => 'The referred column in the parent table', },
+ parent_db => { hdr => 'Parent DB', num => 0, label => 'The database of the parent table' },
+ parent_index => { hdr => 'Parent Index', num => 0, label => 'The referred index in the parent table' },
+ parent_table => { hdr => 'Parent Table', num => 0, label => 'The parent table' },
+ part_id => { hdr => 'Part ID', num => 1, label => 'Sub-part ID of the query' },
+ partitions => { hdr => 'Partitions', num => 0, label => 'Query partitions used' },
+ pct => { hdr => 'Pct', num => 1, label => 'Percentage' },
+ pending_chkp_writes => { hdr => 'Chkpt Writes', num => 1, label => 'Pending log checkpoint writes' },
+ pending_log_writes => { hdr => 'Log Writes', num => 1, label => 'Pending log writes' },
+ port => { hdr => 'Port', num => 1, label => 'Client port number', },
+ possible_keys => { hdr => 'Poss. Keys', num => 0, label => 'Possible keys' },
+ proc_no => { hdr => 'Proc', num => 1, label => 'Process number' },
+ q_cache_hit => { hdr => 'QCacheHit', num => 1, label => 'Query cache hit ratio', },
+ qps => { hdr => 'QPS', num => 1, label => 'How many queries/sec', },
+ queries_in_queue => { hdr => 'Queries Queued', num => 1, label => 'Queries in queue' },
+ queries_inside => { hdr => 'Queries Inside', num => 1, label => 'Queries inside InnoDB' },
+ query_id => { hdr => 'Query ID', num => 1, label => 'Query ID' },
+ query_status => { hdr => 'Query Status', num => 0, label => 'The query status' },
+ query_text => { hdr => 'Query Text', num => 0, label => 'The query text' },
+ questions => { hdr => 'Questions', num => 1, label => 'How many queries the server has gotten', },
+ read_master_log_pos => { hdr => 'Read Master Pos', num => 1, label => 'Read master log position' },
+ read_views_open => { hdr => 'Rd Views', num => 1, label => 'Number of read views open' },
+ reads_pending => { hdr => 'Pending Reads', num => 1, label => 'Reads pending' },
+ relay_log_file => { hdr => 'Relay File', num => 0, label => 'Relay log file' },
+ relay_log_pos => { hdr => 'Relay Pos', num => 1, label => 'Relay log position' },
+ relay_log_size => { hdr => 'Relay Size', num => 1, label => 'Relay log size' },
+ relay_master_log_file => { hdr => 'Relay Master File', num => 0, label => 'Relay master log file' },
+ replicate_do_db => { hdr => 'Do DB', num => 0, label => 'Replicate-do-db setting' },
+ replicate_do_table => { hdr => 'Do Table', num => 0, label => 'Replicate-do-table setting' },
+ replicate_ignore_db => { hdr => 'Ignore DB', num => 0, label => 'Replicate-ignore-db setting' },
+ replicate_ignore_table => { hdr => 'Ignore Table', num => 0, label => 'Replicate-do-table setting' },
+ replicate_wild_do_table => { hdr => 'Wild Do Table', num => 0, label => 'Replicate-wild-do-table setting' },
+ replicate_wild_ignore_table => { hdr => 'Wild Ignore Table', num => 0, label => 'Replicate-wild-ignore-table setting' },
+ request_type => { hdr => 'Type', num => 0, label => 'Type of lock the thread waits for' },
+ reservation_count => { hdr => 'ResCnt', num => 1, label => 'Reservation Count' },
+ row_locks => { hdr => 'RLocks', num => 1, label => 'Number of row locks' },
+ rw_excl_os_waits => { hdr => 'RW Waits', num => 1, label => 'R/W Excl. OS Waits' },
+ rw_excl_spins => { hdr => 'RW Spins', num => 1, label => 'R/W Excl. Spins' },
+ rw_shared_os_waits => { hdr => 'Sh Waits', num => 1, label => 'R/W Shared OS Waits' },
+ rw_shared_spins => { hdr => 'Sh Spins', num => 1, label => 'R/W Shared Spins' },
+ scan_type => { hdr => 'Type', num => 0, label => 'Scan type in chosen' },
+ seg_size => { hdr => 'Seg. Size', num => 1, label => 'Segment size' },
+ select_type => { hdr => 'Select Type', num => 0, label => 'Type of select used' },
+ signal_count => { hdr => 'Signals', num => 1, label => 'Signal Count' },
+ size => { hdr => 'Size', num => 1, label => 'Size of the tablespace' },
+ skip_counter => { hdr => 'Skip Counter', num => 1, label => 'Skip counter' },
+ slave_catchup_rate => { hdr => 'Catchup', num => 1, label => 'How fast the slave is catching up in the binlog' },
+ slave_io_running => { hdr => 'Slave-IO', num => 0, label => 'Whether the slave I/O thread is running' },
+ slave_io_state => { hdr => 'Slave IO State', num => 0, label => 'Slave I/O thread state' },
+ slave_open_temp_tables => { hdr => 'Temp', num => 1, label => 'Slave open temp tables' },
+ slave_sql_running => { hdr => 'Slave-SQL', num => 0, label => 'Whether the slave SQL thread is running' },
+ slow => { hdr => 'Slow', num => 1, label => 'How many slow queries', },
+ space_id => { hdr => 'Space', num => 1, label => 'Tablespace ID' },
+ special => { hdr => 'Special', num => 0, label => 'Special/Other info' },
+ state => { hdr => 'State', num => 0, label => 'Connection state', maxw => 18, },
+ tables_in_use => { hdr => 'Tbl Used', num => 1, label => 'Number of tables in use' },
+ tables_locked => { hdr => 'Tbl Lck', num => 1, label => 'Number of tables locked' },
+ tbl => { hdr => 'Table', num => 0, label => 'Table', },
+ thread => { hdr => 'Thread', num => 1, label => 'Thread number' },
+ thread_decl_inside => { hdr => 'Thread Inside', num => 0, label => 'What the thread is declared inside' },
+ thread_purpose => { hdr => 'Purpose', num => 0, label => "The thread's purpose" },
+ thread_status => { hdr => 'Thread Status', num => 0, label => 'The thread status' },
+ time => { hdr => 'Time', num => 1, label => 'Time since the last event', },
+ time_behind_master => { hdr => 'TimeLag', num => 1, label => 'Time slave lags master' },
+ timestring => { hdr => 'Timestring', num => 0, label => 'Time the event occurred' },
+ total => { hdr => 'Total', num => 1, label => 'Total' },
+ total_mem_alloc => { hdr => 'Memory', num => 1, label => 'Total memory allocated' },
+ truncates => { hdr => 'Trunc', num => 0, label => 'Whether the deadlock is truncating InnoDB status' },
+ txn_doesnt_see_ge => { hdr => "Txn Won't See", num => 0, label => 'Where txn read view is limited' },
+ txn_id => { hdr => 'ID', num => 0, label => 'Transaction ID' },
+ txn_sees_lt => { hdr => 'Txn Sees', num => 1, label => 'Where txn read view is limited' },
+ txn_status => { hdr => 'Txn Status', num => 0, label => 'Transaction status' },
+ txn_time_remain => { hdr => 'Remaining', num => 1, label => 'Time until txn rollback/commit completes' },
+ undo_log_entries => { hdr => 'Undo', num => 1, label => 'Number of undo log entries' },
+ undo_for => { hdr => 'Undo', num => 0, label => 'Undo for' },
+ until_condition => { hdr => 'Until Condition', num => 0, label => 'Slave until condition' },
+ until_log_file => { hdr => 'Until Log File', num => 0, label => 'Slave until log file' },
+ until_log_pos => { hdr => 'Until Log Pos', num => 1, label => 'Slave until log position' },
+ used_cells => { hdr => 'Cells Used', num => 1, label => 'Number of cells used' },
+ used_bufs => { hdr => 'Used Bufs', num => 1, label => 'Number of buffer pool pages used' },
+ user => { hdr => 'User', num => 0, label => 'Database username', },
+ value => { hdr => 'Value', num => 1, label => 'Value' },
+ versions => { hdr => 'Versions', num => 1, label => 'Number of InnoDB MVCC versions unpurged' },
+ victim => { hdr => 'Victim', num => 0, label => 'Whether this txn was the deadlock victim' },
+ wait_array_size => { hdr => 'Wait Array Size', num => 1, label => 'Wait Array Size' },
+ wait_status => { hdr => 'Lock Status', num => 0, label => 'Status of txn locks' },
+ waited_at_filename => { hdr => 'File', num => 0, label => 'Filename at which thread waits' },
+ waited_at_line => { hdr => 'Line', num => 1, label => 'Line at which thread waits' },
+ waiters_flag => { hdr => 'Waiters', num => 1, label => 'Waiters Flag' },
+ waiting => { hdr => 'Waiting', num => 1, label => 'Whether lock is being waited for' },
+ when => { hdr => 'When', num => 0, label => 'Time scale' },
+ writer_lock_mode => { hdr => 'Wrtr Lck Mode', num => 0, label => 'Writer lock mode' },
+ writer_thread => { hdr => 'Wrtr Thread', num => 1, label => 'Writer thread ID' },
+ writes_pending => { hdr => 'Writes', num => 1, label => 'Number of writes pending' },
+ writes_pending_flush_list => { hdr => 'Flush List Writes', num => 1, label => 'Number of flush list writes pending' },
+ writes_pending_lru => { hdr => 'LRU Writes', num => 1, label => 'Number of LRU writes pending' },
+ writes_pending_single_page => { hdr => '1-Page Writes', num => 1, label => 'Number of 1-page writes pending' },
+);
+
+# Apply a default property or three. By default, columns are not width-constrained,
+# aligned left, and sorted alphabetically, not numerically.
+foreach my $col ( values %columns ) {
+ map { $col->{$_} ||= 0 } qw(num minw maxw);
+ $col->{just} = $col->{num} ? '' : '-';
+}
+
+# Filters {{{3
+# This hash defines every filter that can be applied to a table. These
+# become part of tbl_meta as well. Each filter is just an expression that
+# returns true or false.
+# Properties of each entry:
+# * func: the subroutine
+# * name: the name, repeated
+# * user: whether it's a user-defined filter (saved in config)
+# * text: text of the subroutine
+# * note: explanation
+my %filters = ();
+
+# These are pre-processed to live in %filters above, by compiling them.
+my %builtin_filters = (
+ hide_self => {
+ text => <<' END',
+ return ( !$set->{info} || $set->{info} ne 'SHOW FULL PROCESSLIST' )
+ && ( !$set->{query_text} || $set->{query_text} !~ m/INNODB STATUS$/ );
+ END
+ note => 'Removes the innotop processes from the list',
+ tbls => [qw(innodb_transactions processlist)],
+ },
+ hide_inactive => {
+ text => <<' END',
+ return ( !defined($set->{txn_status}) || $set->{txn_status} ne 'not started' )
+ && ( !defined($set->{cmd}) || $set->{cmd} !~ m/Sleep|Binlog Dump/ )
+ && ( !defined($set->{info}) || $set->{info} =~ m/\S/ );
+ END
+ note => 'Removes processes which are not doing anything',
+ tbls => [qw(innodb_transactions processlist)],
+ },
+ hide_slave_io => {
+ text => <<' END',
+ return !$set->{state} || $set->{state} !~ m/^(?:Waiting for master|Has read all relay)/;
+ END
+ note => 'Removes slave I/O threads from the list',
+ tbls => [qw(processlist slave_io_status)],
+ },
+ table_is_open => {
+ text => <<' END',
+ return $set->{num_times_open} + $set->{is_name_locked};
+ END
+ note => 'Removes tables that are not in use or locked',
+ tbls => [qw(open_tables)],
+ },
+ cxn_is_master => {
+ text => <<' END',
+ return $set->{master_file} ? 1 : 0;
+ END
+ note => 'Removes servers that are not masters',
+ tbls => [qw(master_status)],
+ },
+ cxn_is_slave => {
+ text => <<' END',
+ return $set->{master_host} ? 1 : 0;
+ END
+ note => 'Removes servers that are not slaves',
+ tbls => [qw(slave_io_status slave_sql_status)],
+ },
+ thd_is_not_waiting => {
+ text => <<' END',
+ return $set->{thread_status} !~ m#waiting for i/o request#;
+ END
+ note => 'Removes idle I/O threads',
+ tbls => [qw(io_threads)],
+ },
+);
+foreach my $key ( keys %builtin_filters ) {
+ my ( $sub, $err ) = compile_filter($builtin_filters{$key}->{text});
+ $filters{$key} = {
+ func => $sub,
+ text => $builtin_filters{$key}->{text},
+ user => 0,
+ name => $key, # useful for later
+ note => $builtin_filters{$key}->{note},
+ tbls => $builtin_filters{$key}->{tbls},
+ }
+}
+
+# Variable sets {{{3
+# Sets (arrayrefs) of variables that are used in S mode. They are read/written to
+# the config file.
+my %var_sets = (
+ general => {
+ text => join(
+ ', ',
+ 'set_precision(Questions/Uptime_hires) as QPS',
+ 'set_precision(Com_commit/Uptime_hires) as Commit_PS',
+ 'set_precision((Com_rollback||0)/(Com_commit||1)) as Rollback_Commit',
+ 'set_precision(('
+ . join('+', map { "($_||0)" }
+ qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
+ Com_replace_select Com_select Com_update Com_update_multi))
+ . ')/(Com_commit||1)) as Write_Commit',
+ 'set_precision((Com_select+(Qcache_hits||0))/(('
+ . join('+', map { "($_||0)" }
+ qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
+ Com_replace_select Com_select Com_update Com_update_multi))
+ . ')||1)) as R_W_Ratio',
+ 'set_precision(Opened_tables/Uptime_hires) as Opens_PS',
+ 'percent($cur->{Open_tables}/($cur->{table_cache})) as Table_Cache_Used',
+ 'set_precision(Threads_created/Uptime_hires) as Threads_PS',
+ 'percent($cur->{Threads_cached}/($cur->{thread_cache_size}||1)) as Thread_Cache_Used',
+ 'percent($cur->{Max_used_connections}/($cur->{max_connections}||1)) as CXN_Used_Ever',
+ 'percent($cur->{Threads_connected}/($cur->{max_connections}||1)) as CXN_Used_Now',
+ ),
+ },
+ commands => {
+ text => join(
+ ', ',
+ qw(Uptime Questions Com_delete Com_delete_multi Com_insert
+ Com_insert_select Com_replace Com_replace_select Com_select Com_update
+ Com_update_multi)
+ ),
+ },
+ query_status => {
+ text => join(
+ ',',
+ qw( Uptime Select_full_join Select_full_range_join Select_range
+ Select_range_check Select_scan Slow_queries Sort_merge_passes
+ Sort_range Sort_rows Sort_scan)
+ ),
+ },
+ innodb => {
+ text => join(
+ ',',
+ qw( Uptime Innodb_row_lock_current_waits Innodb_row_lock_time
+ Innodb_row_lock_time_avg Innodb_row_lock_time_max Innodb_row_lock_waits
+ Innodb_rows_deleted Innodb_rows_inserted Innodb_rows_read
+ Innodb_rows_updated)
+ ),
+ },
+ txn => {
+ text => join(
+ ',',
+ qw( Uptime Com_begin Com_commit Com_rollback Com_savepoint
+ Com_xa_commit Com_xa_end Com_xa_prepare Com_xa_recover Com_xa_rollback
+ Com_xa_start)
+ ),
+ },
+ key_cache => {
+ text => join(
+ ',',
+ qw( Uptime Key_blocks_not_flushed Key_blocks_unused Key_blocks_used
+ Key_read_requests Key_reads Key_write_requests Key_writes )
+ ),
+ },
+ query_cache => {
+ text => join(
+ ',',
+ "percent($exprs{QcacheHitRatio}) as Hit_Pct",
+ 'set_precision((Qcache_hits||0)/(Qcache_inserts||1)) as Hit_Ins',
+ 'set_precision((Qcache_lowmem_prunes||0)/Uptime_hires) as Lowmem_Prunes_sec',
+ 'percent(1-((Qcache_free_blocks||0)/(Qcache_total_blocks||1))) as Blocks_used',
+ qw( Qcache_free_blocks Qcache_free_memory Qcache_not_cached Qcache_queries_in_cache)
+ ),
+ },
+ handler => {
+ text => join(
+ ',',
+ qw( Uptime Handler_read_key Handler_read_first Handler_read_next
+ Handler_read_prev Handler_read_rnd Handler_read_rnd_next Handler_delete
+ Handler_update Handler_write)
+ ),
+ },
+ cxns_files_threads => {
+ text => join(
+ ',',
+ qw( Uptime Aborted_clients Aborted_connects Bytes_received Bytes_sent
+ Compression Connections Created_tmp_disk_tables Created_tmp_files
+ Created_tmp_tables Max_used_connections Open_files Open_streams
+ Open_tables Opened_tables Table_locks_immediate Table_locks_waited
+ Threads_cached Threads_connected Threads_created Threads_running)
+ ),
+ },
+ prep_stmt => {
+ text => join(
+ ',',
+ qw( Uptime Com_dealloc_sql Com_execute_sql Com_prepare_sql Com_reset
+ Com_stmt_close Com_stmt_execute Com_stmt_fetch Com_stmt_prepare
+ Com_stmt_reset Com_stmt_send_long_data )
+ ),
+ },
+ innodb_health => {
+ text => join(
+ ',',
+ "$exprs{OldVersions} as OldVersions",
+ qw(IB_sm_mutex_spin_waits IB_sm_mutex_spin_rounds IB_sm_mutex_os_waits),
+ "$exprs{NumTxns} as NumTxns",
+ "$exprs{MaxTxnTime} as MaxTxnTime",
+ qw(IB_ro_queries_inside IB_ro_queries_in_queue),
+ "set_precision($exprs{DirtyBufs} * 100) as dirty_bufs",
+ "set_precision($exprs{BufPoolFill} * 100) as buf_fill",
+ qw(IB_bp_pages_total IB_bp_pages_read IB_bp_pages_written IB_bp_pages_created)
+ ),
+ },
+ innodb_health2 => {
+ text => join(
+ ', ',
+ 'percent(1-((Innodb_buffer_pool_pages_free||0)/($cur->{Innodb_buffer_pool_pages_total}||1))) as BP_page_cache_usage',
+ 'percent(1-((Innodb_buffer_pool_reads||0)/(Innodb_buffer_pool_read_requests||1))) as BP_cache_hit_ratio',
+ 'Innodb_buffer_pool_wait_free',
+ 'Innodb_log_waits',
+ ),
+ },
+ slow_queries => {
+ text => join(
+ ', ',
+ 'set_precision(Slow_queries/Uptime_hires) as Slow_PS',
+ 'set_precision(Select_full_join/Uptime_hires) as Full_Join_PS',
+ 'percent(Select_full_join/(Com_select||1)) as Full_Join_Ratio',
+ ),
+ },
+);
+
+# Server sets {{{3
+# Defines sets of servers between which the user can quickly switch.
+my %server_groups;
+
+# Connections {{{3
+# This hash defines server connections. Each connection is a string that can be passed to
+# the DBI connection. These are saved in the connections section in the config file.
+my %connections;
+# Defines the parts of connections.
+my @conn_parts = qw(user have_user pass have_pass dsn savepass dl_table);
+
+# Graph widths {{{3
+# This hash defines the max values seen for various status/variable values, for graphing.
+# These are stored in their own section in the config file. These are just initial values:
+my %mvs = (
+ Com_select => 50,
+ Com_insert => 50,
+ Com_update => 50,
+ Com_delete => 50,
+ Questions => 100,
+);
+
+# ###########################################################################
+# Valid Term::ANSIColor color strings.
+# ###########################################################################
+my %ansicolors = map { $_ => 1 }
+ qw( black blink blue bold clear concealed cyan dark green magenta on_black
+ on_blue on_cyan on_green on_magenta on_red on_white on_yellow red reset
+ reverse underline underscore white yellow);
+
+# ###########################################################################
+# Valid comparison operators for color rules
+# ###########################################################################
+my %comp_ops = (
+ '==' => 'Numeric equality',
+ '>' => 'Numeric greater-than',
+ '<' => 'Numeric less-than',
+ '>=' => 'Numeric greater-than/equal',
+ '<=' => 'Numeric less-than/equal',
+ '!=' => 'Numeric not-equal',
+ 'eq' => 'String equality',
+ 'gt' => 'String greater-than',
+ 'lt' => 'String less-than',
+ 'ge' => 'String greater-than/equal',
+ 'le' => 'String less-than/equal',
+ 'ne' => 'String not-equal',
+ '=~' => 'Pattern match',
+ '!~' => 'Negated pattern match',
+);
+
+# ###########################################################################
+# Valid aggregate functions.
+# ###########################################################################
+my %agg_funcs = (
+ first => sub {
+ return $_[0]
+ },
+ count => sub {
+ return 0 + @_;
+ },
+ avg => sub {
+ my @args = grep { defined $_ } @_;
+ return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1);
+ },
+ sum => sub {
+ my @args = grep { defined $_ } @_;
+ return sum(@args);
+ }
+);
+
+# ###########################################################################
+# Valid functions for transformations.
+# ###########################################################################
+my %trans_funcs = (
+ shorten => \&shorten,
+ secs_to_time => \&secs_to_time,
+ no_ctrl_char => \&no_ctrl_char,
+ percent => \&percent,
+ commify => \&commify,
+ dulint_to_int => \&dulint_to_int,
+ set_precision => \&set_precision,
+);
+
+# Table definitions {{{3
+# This hash defines every table that can get displayed in every mode. Each
+# table specifies columns and column data sources. The column is
+# defined by the %columns hash.
+#
+# Example: foo => { src => 'bar' } means the foo column (look at
+# $columns{foo} for its definition) gets its data from the 'bar' element of
+# the current data set, whatever that is.
+#
+# These columns are post-processed after being defined, because they get stuff
+# from %columns. After all the config is loaded for columns, there's more
+# post-processing too; the subroutines compiled from src get added to
+# the hash elements for extract_values to use.
+# ###########################################################################
+
+my %tbl_meta = (
+ adaptive_hash_index => {
+ capt => 'Adaptive Hash Index',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ hash_table_size => { src => 'IB_ib_hash_table_size', trans => [qw(shorten)], },
+ used_cells => { src => 'IB_ib_used_cells' },
+ bufs_in_node_heap => { src => 'IB_ib_bufs_in_node_heap' },
+ hash_searches_s => { src => 'IB_ib_hash_searches_s' },
+ non_hash_searches_s => { src => 'IB_ib_non_hash_searches_s' },
+ },
+ visible => [ qw(cxn hash_table_size used_cells bufs_in_node_heap hash_searches_s non_hash_searches_s) ],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'ib',
+ group_by => [],
+ aggregate => 0,
+ },
+ buffer_pool => {
+ capt => 'Buffer Pool',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ total_mem_alloc => { src => 'IB_bp_total_mem_alloc', trans => [qw(shorten)], },
+ awe_mem_alloc => { src => 'IB_bp_awe_mem_alloc', trans => [qw(shorten)], },
+ add_pool_alloc => { src => 'IB_bp_add_pool_alloc', trans => [qw(shorten)], },
+ buf_pool_size => { src => 'IB_bp_buf_pool_size', trans => [qw(shorten)], },
+ buf_free => { src => 'IB_bp_buf_free' },
+ buf_pool_hit_rate => { src => 'IB_bp_buf_pool_hit_rate' },
+ buf_pool_reads => { src => 'IB_bp_buf_pool_reads' },
+ buf_pool_hits => { src => 'IB_bp_buf_pool_hits' },
+ dict_mem_alloc => { src => 'IB_bp_dict_mem_alloc' },
+ pages_total => { src => 'IB_bp_pages_total' },
+ pages_modified => { src => 'IB_bp_pages_modified' },
+ reads_pending => { src => 'IB_bp_reads_pending' },
+ writes_pending => { src => 'IB_bp_writes_pending' },
+ writes_pending_lru => { src => 'IB_bp_writes_pending_lru' },
+ writes_pending_flush_list => { src => 'IB_bp_writes_pending_flush_list' },
+ writes_pending_single_page => { src => 'IB_bp_writes_pending_single_page' },
+ page_creates_sec => { src => 'IB_bp_page_creates_sec' },
+ page_reads_sec => { src => 'IB_bp_page_reads_sec' },
+ page_writes_sec => { src => 'IB_bp_page_writes_sec' },
+ pages_created => { src => 'IB_bp_pages_created' },
+ pages_read => { src => 'IB_bp_pages_read' },
+ pages_written => { src => 'IB_bp_pages_written' },
+ },
+ visible => [ qw(cxn buf_pool_size buf_free pages_total pages_modified buf_pool_hit_rate total_mem_alloc add_pool_alloc)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'bp',
+ group_by => [],
+ aggregate => 0,
+ },
+ # TODO: a new step in set_to_tbl: join result to itself, grouped?
+ # TODO: this would also enable pulling Q and T data together.
+ # TODO: using a SQL-ish language would also allow pivots to be easier -- treat the pivoted data as a view and SELECT from it.
+ cmd_summary => {
+ capt => 'Command Summary',
+ cust => {},
+ cols => {
+ name => { src => 'name' },
+ total => { src => 'total' },
+ value => { src => 'value', agg => 'sum'},
+ pct => { src => 'value/total', trans => [qw(percent)] },
+ last_total => { src => 'last_total' },
+ last_value => { src => 'last_value', agg => 'sum'},
+ last_pct => { src => 'last_value/last_total', trans => [qw(percent)] },
+ },
+ visible => [qw(name value pct last_value last_pct)],
+ filters => [qw()],
+ sort_cols => '-value',
+ sort_dir => '1',
+ innodb => '',
+ group_by => [qw(name)],
+ aggregate => 1,
+ },
+ deadlock_locks => {
+ capt => 'Deadlock Locks',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ mysql_thread_id => { src => 'mysql_thread_id' },
+ dl_txn_num => { src => 'dl_txn_num' },
+ lock_type => { src => 'lock_type' },
+ space_id => { src => 'space_id' },
+ page_no => { src => 'page_no' },
+ heap_no => { src => 'heap_no' },
+ n_bits => { src => 'n_bits' },
+ index => { src => 'index' },
+ db => { src => 'db' },
+ tbl => { src => 'table' },
+ lock_mode => { src => 'lock_mode' },
+ special => { src => 'special' },
+ insert_intention => { src => 'insert_intention' },
+ waiting => { src => 'waiting' },
+ },
+ visible => [ qw(cxn mysql_thread_id waiting lock_mode db tbl index special insert_intention)],
+ filters => [],
+ sort_cols => 'cxn mysql_thread_id',
+ sort_dir => '1',
+ innodb => 'dl',
+ group_by => [],
+ aggregate => 0,
+ },
+ deadlock_transactions => {
+ capt => 'Deadlock Transactions',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ active_secs => { src => 'active_secs' },
+ dl_txn_num => { src => 'dl_txn_num' },
+ has_read_view => { src => 'has_read_view' },
+ heap_size => { src => 'heap_size' },
+ host_and_domain => { src => 'hostname' },
+ hostname => { src => $exprs{Host} },
+ ip => { src => 'ip' },
+ lock_structs => { src => 'lock_structs' },
+ lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
+ mysql_thread_id => { src => 'mysql_thread_id' },
+ os_thread_id => { src => 'os_thread_id' },
+ proc_no => { src => 'proc_no' },
+ query_id => { src => 'query_id' },
+ query_status => { src => 'query_status' },
+ query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
+ row_locks => { src => 'row_locks' },
+ tables_in_use => { src => 'tables_in_use' },
+ tables_locked => { src => 'tables_locked' },
+ thread_decl_inside => { src => 'thread_decl_inside' },
+ thread_status => { src => 'thread_status' },
+ 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ] },
+ timestring => { src => 'timestring' },
+ txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' },
+ txn_id => { src => 'txn_id' },
+ txn_sees_lt => { src => 'txn_sees_lt' },
+ txn_status => { src => 'txn_status' },
+ truncates => { src => 'truncates' },
+ undo_log_entries => { src => 'undo_log_entries' },
+ user => { src => 'user' },
+ victim => { src => 'victim' },
+ wait_status => { src => 'lock_wait_status' },
+ },
+ visible => [ qw(cxn mysql_thread_id timestring user hostname victim time undo_log_entries lock_structs query_text)],
+ filters => [],
+ sort_cols => 'cxn mysql_thread_id',
+ sort_dir => '1',
+ innodb => 'dl',
+ group_by => [],
+ aggregate => 0,
+ },
+ explain => {
+ capt => 'EXPLAIN Results',
+ cust => {},
+ cols => {
+ part_id => { src => 'id' },
+ select_type => { src => 'select_type' },
+ tbl => { src => 'table' },
+ partitions => { src => 'partitions' },
+ scan_type => { src => 'type' },
+ possible_keys => { src => 'possible_keys' },
+ index => { src => 'key' },
+ key_len => { src => 'key_len' },
+ index_ref => { src => 'ref' },
+ num_rows => { src => 'rows' },
+ special => { src => 'extra' },
+ },
+ visible => [ qw(select_type tbl partitions scan_type possible_keys index key_len index_ref num_rows special)],
+ filters => [],
+ sort_cols => '',
+ sort_dir => '1',
+ innodb => '',
+ group_by => [],
+ aggregate => 0,
+ },
+ file_io_misc => {
+ capt => 'File I/O Misc',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ io_bytes_s => { src => 'IB_io_avg_bytes_s' },
+ io_flush_type => { src => 'IB_io_flush_type' },
+ io_fsyncs_s => { src => 'IB_io_fsyncs_s' },
+ io_reads_s => { src => 'IB_io_reads_s' },
+ io_writes_s => { src => 'IB_io_writes_s' },
+ os_file_reads => { src => 'IB_io_os_file_reads' },
+ os_file_writes => { src => 'IB_io_os_file_writes' },
+ os_fsyncs => { src => 'IB_io_os_fsyncs' },
+ },
+ visible => [ qw(cxn os_file_reads os_file_writes os_fsyncs io_reads_s io_writes_s io_bytes_s)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'io',
+ group_by => [],
+ aggregate => 0,
+ },
+ fk_error => {
+ capt => 'Foreign Key Error Info',
+ cust => {},
+ cols => {
+ timestring => { src => 'IB_fk_timestring' },
+ child_db => { src => 'IB_fk_child_db' },
+ child_table => { src => 'IB_fk_child_table' },
+ child_index => { src => 'IB_fk_child_index' },
+ fk_name => { src => 'IB_fk_fk_name' },
+ parent_db => { src => 'IB_fk_parent_db' },
+ parent_table => { src => 'IB_fk_parent_table' },
+ parent_col => { src => 'IB_fk_parent_col' },
+ parent_index => { src => 'IB_fk_parent_index' },
+ attempted_op => { src => 'IB_fk_attempted_op' },
+ },
+ visible => [ qw(timestring child_db child_table child_index parent_db parent_table parent_col parent_index fk_name attempted_op)],
+ filters => [],
+ sort_cols => '',
+ sort_dir => '1',
+ innodb => 'fk',
+ group_by => [],
+ aggregate => 0,
+ },
+ insert_buffers => {
+ capt => 'Insert Buffers',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ inserts => { src => 'IB_ib_inserts' },
+ merged_recs => { src => 'IB_ib_merged_recs' },
+ merges => { src => 'IB_ib_merges' },
+ size => { src => 'IB_ib_size' },
+ free_list_len => { src => 'IB_ib_free_list_len' },
+ seg_size => { src => 'IB_ib_seg_size' },
+ },
+ visible => [ qw(cxn inserts merged_recs merges size free_list_len seg_size)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'ib',
+ group_by => [],
+ aggregate => 0,
+ },
+ innodb_locks => {
+ capt => 'InnoDB Locks',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ db => { src => 'db' },
+ index => { src => 'index' },
+ insert_intention => { src => 'insert_intention' },
+ lock_mode => { src => 'lock_mode' },
+ lock_type => { src => 'lock_type' },
+ lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
+ mysql_thread_id => { src => 'mysql_thread_id' },
+ n_bits => { src => 'n_bits' },
+ page_no => { src => 'page_no' },
+ space_id => { src => 'space_id' },
+ special => { src => 'special' },
+ tbl => { src => 'table' },
+ 'time' => { src => 'active_secs', hdr => 'Active', trans => [ qw(secs_to_time) ] },
+ txn_id => { src => 'txn_id' },
+ waiting => { src => 'waiting' },
+ },
+ visible => [ qw(cxn mysql_thread_id lock_type waiting lock_wait_time time lock_mode db tbl index insert_intention special)],
+ filters => [],
+ sort_cols => 'cxn -lock_wait_time',
+ sort_dir => '1',
+ innodb => 'tx',
+ colors => [
+ { col => 'lock_wait_time', op => '>', arg => 60, color => 'red' },
+ { col => 'lock_wait_time', op => '>', arg => 30, color => 'yellow' },
+ { col => 'lock_wait_time', op => '>', arg => 10, color => 'green' },
+ ],
+ group_by => [],
+ aggregate => 0,
+ },
+ innodb_transactions => {
+ capt => 'InnoDB Transactions',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ active_secs => { src => 'active_secs' },
+ has_read_view => { src => 'has_read_view' },
+ heap_size => { src => 'heap_size' },
+ hostname => { src => $exprs{Host} },
+ ip => { src => 'ip' },
+ wait_status => { src => 'lock_wait_status' },
+ lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
+ lock_structs => { src => 'lock_structs' },
+ mysql_thread_id => { src => 'mysql_thread_id' },
+ os_thread_id => { src => 'os_thread_id' },
+ proc_no => { src => 'proc_no' },
+ query_id => { src => 'query_id' },
+ query_status => { src => 'query_status' },
+ query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
+ txn_time_remain => { src => $exprs{TxnTimeRemain}, trans => [ qw(secs_to_time) ] },
+ row_locks => { src => 'row_locks' },
+ tables_in_use => { src => 'tables_in_use' },
+ tables_locked => { src => 'tables_locked' },
+ thread_decl_inside => { src => 'thread_decl_inside' },
+ thread_status => { src => 'thread_status' },
+ 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ], agg => 'sum' },
+ txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' },
+ txn_id => { src => 'txn_id' },
+ txn_sees_lt => { src => 'txn_sees_lt' },
+ txn_status => { src => 'txn_status', minw => 10, maxw => 10 },
+ undo_log_entries => { src => 'undo_log_entries' },
+ user => { src => 'user', maxw => 10 },
+ cnt => { src => 'mysql_thread_id', minw => 0 },
+ },
+ visible => [ qw(cxn cnt mysql_thread_id user hostname txn_status time undo_log_entries query_text)],
+ filters => [ qw( hide_self hide_inactive ) ],
+ sort_cols => '-active_secs txn_status cxn mysql_thread_id',
+ sort_dir => '1',
+ innodb => 'tx',
+ hide_caption => 1,
+ colors => [
+ { col => 'wait_status', op => 'eq', arg => 'LOCK WAIT', color => 'black on_red' },
+ { col => 'time', op => '>', arg => 600, color => 'red' },
+ { col => 'time', op => '>', arg => 300, color => 'yellow' },
+ { col => 'time', op => '>', arg => 60, color => 'green' },
+ { col => 'time', op => '>', arg => 30, color => 'cyan' },
+ { col => 'txn_status', op => 'eq', arg => 'not started', color => 'white' },
+ ],
+ group_by => [ qw(cxn txn_status) ],
+ aggregate => 0,
+ },
+ io_threads => {
+ capt => 'I/O Threads',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ thread => { src => 'thread' },
+ thread_purpose => { src => 'purpose' },
+ event_set => { src => 'event_set' },
+ thread_status => { src => 'state' },
+ },
+ visible => [ qw(cxn thread thread_purpose thread_status)],
+ filters => [ qw() ],
+ sort_cols => 'cxn thread',
+ sort_dir => '1',
+ innodb => 'io',
+ group_by => [],
+ aggregate => 0,
+ },
+ log_statistics => {
+ capt => 'Log Statistics',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ last_chkp => { src => 'IB_lg_last_chkp' },
+ log_flushed_to => { src => 'IB_lg_log_flushed_to' },
+ log_ios_done => { src => 'IB_lg_log_ios_done' },
+ log_ios_s => { src => 'IB_lg_log_ios_s' },
+ log_seq_no => { src => 'IB_lg_log_seq_no' },
+ pending_chkp_writes => { src => 'IB_lg_pending_chkp_writes' },
+ pending_log_writes => { src => 'IB_lg_pending_log_writes' },
+ },
+ visible => [ qw(cxn log_seq_no log_flushed_to last_chkp log_ios_done log_ios_s)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'lg',
+ group_by => [],
+ aggregate => 0,
+ },
+ master_status => {
+ capt => 'Master Status',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ binlog_do_db => { src => 'binlog_do_db' },
+ binlog_ignore_db => { src => 'binlog_ignore_db' },
+ master_file => { src => 'file' },
+ master_pos => { src => 'position' },
+ binlog_cache_overflow => { src => '(Binlog_cache_disk_use||0)/(Binlog_cache_use||1)', trans => [ qw(percent) ] },
+ },
+ visible => [ qw(cxn master_file master_pos binlog_cache_overflow)],
+ filters => [ qw(cxn_is_master) ],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => '',
+ group_by => [],
+ aggregate => 0,
+ },
+ pending_io => {
+ capt => 'Pending I/O',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ p_normal_aio_reads => { src => 'IB_io_pending_normal_aio_reads' },
+ p_aio_writes => { src => 'IB_io_pending_aio_writes' },
+ p_ibuf_aio_reads => { src => 'IB_io_pending_ibuf_aio_reads' },
+ p_sync_ios => { src => 'IB_io_pending_sync_ios' },
+ p_buf_pool_flushes => { src => 'IB_io_pending_buffer_pool_flushes' },
+ p_log_flushes => { src => 'IB_io_pending_log_flushes' },
+ p_log_ios => { src => 'IB_io_pending_log_ios' },
+ p_preads => { src => 'IB_io_pending_preads' },
+ p_pwrites => { src => 'IB_io_pending_pwrites' },
+ },
+ visible => [ qw(cxn p_normal_aio_reads p_aio_writes p_ibuf_aio_reads p_sync_ios p_log_flushes p_log_ios)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'io',
+ group_by => [],
+ aggregate => 0,
+ },
+ open_tables => {
+ capt => 'Open Tables',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ db => { src => 'database' },
+ tbl => { src => 'table' },
+ num_times_open => { src => 'in_use' },
+ is_name_locked => { src => 'name_locked' },
+ },
+ visible => [ qw(cxn db tbl num_times_open is_name_locked)],
+ filters => [ qw(table_is_open) ],
+ sort_cols => '-num_times_open cxn db tbl',
+ sort_dir => '1',
+ innodb => '',
+ group_by => [],
+ aggregate => 0,
+ },
+ page_statistics => {
+ capt => 'Page Statistics',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ pages_read => { src => 'IB_bp_pages_read' },
+ pages_written => { src => 'IB_bp_pages_written' },
+ pages_created => { src => 'IB_bp_pages_created' },
+ page_reads_sec => { src => 'IB_bp_page_reads_sec' },
+ page_writes_sec => { src => 'IB_bp_page_writes_sec' },
+ page_creates_sec => { src => 'IB_bp_page_creates_sec' },
+ },
+ visible => [ qw(cxn pages_read pages_written pages_created page_reads_sec page_writes_sec page_creates_sec)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'bp',
+ group_by => [],
+ aggregate => 0,
+ },
+ processlist => {
+ capt => 'MySQL Process List',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn', minw => 6, maxw => 10 },
+ mysql_thread_id => { src => 'id', minw => 6, maxw => 0 },
+ user => { src => 'user', minw => 5, maxw => 8 },
+ hostname => { src => $exprs{Host}, minw => 13, maxw => 8, },
+ port => { src => $exprs{Port}, minw => 0, maxw => 0, },
+ host_and_port => { src => 'host', minw => 0, maxw => 0 },
+ db => { src => 'db', minw => 6, maxw => 12 },
+ cmd => { src => 'command', minw => 5, maxw => 0 },
+ time => { src => 'time', minw => 5, maxw => 0, trans => [ qw(secs_to_time) ], agg => 'sum' },
+ state => { src => 'state', minw => 0, maxw => 0 },
+ info => { src => 'info', minw => 0, maxw => 0, trans => [ qw(no_ctrl_char) ] },
+ cnt => { src => 'id', minw => 0, maxw => 0 },
+ },
+ visible => [ qw(cxn cmd cnt mysql_thread_id state user hostname db time info)],
+ filters => [ qw(hide_self hide_inactive hide_slave_io) ],
+ sort_cols => '-time cxn hostname mysql_thread_id',
+ sort_dir => '1',
+ innodb => '',
+ hide_caption => 1,
+ colors => [
+ { col => 'state', op => 'eq', arg => 'Locked', color => 'black on_red' },
+ { col => 'cmd', op => 'eq', arg => 'Sleep', color => 'white' },
+ { col => 'user', op => 'eq', arg => 'system user', color => 'white' },
+ { col => 'cmd', op => 'eq', arg => 'Connect', color => 'white' },
+ { col => 'cmd', op => 'eq', arg => 'Binlog Dump', color => 'white' },
+ { col => 'time', op => '>', arg => 600, color => 'red' },
+ { col => 'time', op => '>', arg => 120, color => 'yellow' },
+ { col => 'time', op => '>', arg => 60, color => 'green' },
+ { col => 'time', op => '>', arg => 30, color => 'cyan' },
+ ],
+ group_by => [qw(cxn cmd)],
+ aggregate => 0,
+ },
+
+ # TODO: some more columns:
+ # kb_used=hdr='BufUsed' minw='0' num='0' src='percent(1 - ((Key_blocks_unused * key_cache_block_size) / (key_buffer_size||1)))' dec='0' trans='' tbl='q_header' just='-' user='1' maxw='0' label='User-defined'
+ # retries=hdr='Retries' minw='0' num='0' src='Slave_retried_transactions' dec='0' trans='' tbl='slave_sql_status' just='-' user='1' maxw='0' label='User-defined'
+ # thd=hdr='Thd' minw='0' num='0' src='Threads_connected' dec='0' trans='' tbl='slave_sql_status' just='-' user='1' maxw='0' label='User-defined'
+
+ q_header => {
+ capt => 'Q-mode Header',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ questions => { src => 'Questions' },
+ qps => { src => 'Questions/Uptime_hires', dec => 1, trans => [qw(shorten)] },
+ load => { src => $exprs{ServerLoad}, dec => 1, trans => [qw(shorten)] },
+ slow => { src => 'Slow_queries', dec => 1, trans => [qw(shorten)] },
+ q_cache_hit => { src => $exprs{QcacheHitRatio}, dec => 1, trans => [qw(percent)] },
+ key_buffer_hit => { src => '1-(Key_reads/(Key_read_requests||1))', dec => 1, trans => [qw(percent)] },
+ bps_in => { src => 'Bytes_received/Uptime_hires', dec => 1, trans => [qw(shorten)] },
+ bps_out => { src => 'Bytes_sent/Uptime_hires', dec => 1, trans => [qw(shorten)] },
+ when => { src => 'when' },
+ },
+ visible => [ qw(cxn when load qps slow q_cache_hit key_buffer_hit bps_in bps_out)],
+ filters => [],
+ sort_cols => 'when cxn',
+ sort_dir => '1',
+ innodb => '',
+ hide_caption => 1,
+ group_by => [],
+ aggregate => 0,
+ },
+ row_operations => {
+ capt => 'InnoDB Row Operations',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ num_inserts => { src => 'IB_ro_num_rows_ins' },
+ num_updates => { src => 'IB_ro_num_rows_upd' },
+ num_reads => { src => 'IB_ro_num_rows_read' },
+ num_deletes => { src => 'IB_ro_num_rows_del' },
+ num_inserts_sec => { src => 'IB_ro_ins_sec' },
+ num_updates_sec => { src => 'IB_ro_upd_sec' },
+ num_reads_sec => { src => 'IB_ro_read_sec' },
+ num_deletes_sec => { src => 'IB_ro_del_sec' },
+ },
+ visible => [ qw(cxn num_inserts num_updates num_reads num_deletes num_inserts_sec
+ num_updates_sec num_reads_sec num_deletes_sec)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'ro',
+ group_by => [],
+ aggregate => 0,
+ },
+ row_operation_misc => {
+ capt => 'Row Operation Misc',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ queries_in_queue => { src => 'IB_ro_queries_in_queue' },
+ queries_inside => { src => 'IB_ro_queries_inside' },
+ read_views_open => { src => 'IB_ro_read_views_open' },
+ main_thread_id => { src => 'IB_ro_main_thread_id' },
+ main_thread_proc_no => { src => 'IB_ro_main_thread_proc_no' },
+ main_thread_state => { src => 'IB_ro_main_thread_state' },
+ num_res_ext => { src => 'IB_ro_n_reserved_extents' },
+ },
+ visible => [ qw(cxn queries_in_queue queries_inside read_views_open main_thread_state)],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'ro',
+ group_by => [],
+ aggregate => 0,
+ },
+ semaphores => {
+ capt => 'InnoDB Semaphores',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ mutex_os_waits => { src => 'IB_sm_mutex_os_waits' },
+ mutex_spin_rounds => { src => 'IB_sm_mutex_spin_rounds' },
+ mutex_spin_waits => { src => 'IB_sm_mutex_spin_waits' },
+ reservation_count => { src => 'IB_sm_reservation_count' },
+ rw_excl_os_waits => { src => 'IB_sm_rw_excl_os_waits' },
+ rw_excl_spins => { src => 'IB_sm_rw_excl_spins' },
+ rw_shared_os_waits => { src => 'IB_sm_rw_shared_os_waits' },
+ rw_shared_spins => { src => 'IB_sm_rw_shared_spins' },
+ signal_count => { src => 'IB_sm_signal_count' },
+ wait_array_size => { src => 'IB_sm_wait_array_size' },
+ },
+ visible => [ qw(cxn mutex_os_waits mutex_spin_waits mutex_spin_rounds
+ rw_excl_os_waits rw_excl_spins rw_shared_os_waits rw_shared_spins
+ signal_count reservation_count )],
+ filters => [],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => 'sm',
+ group_by => [],
+ aggregate => 0,
+ },
+ slave_io_status => {
+ capt => 'Slave I/O Status',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ connect_retry => { src => 'connect_retry' },
+ master_host => { src => 'master_host', hdr => 'Master'},
+ master_log_file => { src => 'master_log_file', hdr => 'File' },
+ master_port => { src => 'master_port' },
+ master_ssl_allowed => { src => 'master_ssl_allowed' },
+ master_ssl_ca_file => { src => 'master_ssl_ca_file' },
+ master_ssl_ca_path => { src => 'master_ssl_ca_path' },
+ master_ssl_cert => { src => 'master_ssl_cert' },
+ master_ssl_cipher => { src => 'master_ssl_cipher' },
+ master_ssl_key => { src => 'master_ssl_key' },
+ master_user => { src => 'master_user' },
+ read_master_log_pos => { src => 'read_master_log_pos', hdr => 'Pos' },
+ relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] },
+ slave_io_running => { src => 'slave_io_running', hdr => 'On?' },
+ slave_io_state => { src => 'slave_io_state', hdr => 'State' },
+ },
+ visible => [ qw(cxn master_host slave_io_running master_log_file relay_log_size read_master_log_pos slave_io_state)],
+ filters => [ qw( cxn_is_slave ) ],
+ sort_cols => 'slave_io_running cxn',
+ colors => [
+ { col => 'slave_io_running', op => 'ne', arg => 'Yes', color => 'black on_red' },
+ ],
+ sort_dir => '1',
+ innodb => '',
+ group_by => [],
+ aggregate => 0,
+ },
+ slave_sql_status => {
+ capt => 'Slave SQL Status',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ exec_master_log_pos => { src => 'exec_master_log_pos', hdr => 'Master Pos' },
+ last_errno => { src => 'last_errno' },
+ last_error => { src => 'last_error' },
+ master_host => { src => 'master_host', hdr => 'Master' },
+ relay_log_file => { src => 'relay_log_file' },
+ relay_log_pos => { src => 'relay_log_pos' },
+ relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] },
+ relay_master_log_file => { src => 'relay_master_log_file', hdr => 'Master File' },
+ replicate_do_db => { src => 'replicate_do_db' },
+ replicate_do_table => { src => 'replicate_do_table' },
+ replicate_ignore_db => { src => 'replicate_ignore_db' },
+ replicate_ignore_table => { src => 'replicate_ignore_table' },
+ replicate_wild_do_table => { src => 'replicate_wild_do_table' },
+ replicate_wild_ignore_table => { src => 'replicate_wild_ignore_table' },
+ skip_counter => { src => 'skip_counter' },
+ slave_sql_running => { src => 'slave_sql_running', hdr => 'On?' },
+ until_condition => { src => 'until_condition' },
+ until_log_file => { src => 'until_log_file' },
+ until_log_pos => { src => 'until_log_pos' },
+ time_behind_master => { src => 'seconds_behind_master', trans => [ qw(secs_to_time) ] },
+ bytes_behind_master => { src => 'master_log_file && master_log_file eq relay_master_log_file ? read_master_log_pos - exec_master_log_pos : 0', trans => [qw(shorten)] },
+ slave_catchup_rate => { src => $exprs{SlaveCatchupRate}, trans => [ qw(set_precision) ] },
+ slave_open_temp_tables => { src => 'Slave_open_temp_tables' },
+ },
+ visible => [ qw(cxn master_host slave_sql_running time_behind_master slave_catchup_rate slave_open_temp_tables relay_log_pos last_error)],
+ filters => [ qw( cxn_is_slave ) ],
+ sort_cols => 'slave_sql_running cxn',
+ sort_dir => '1',
+ innodb => '',
+ colors => [
+ { col => 'slave_sql_running', op => 'ne', arg => 'Yes', color => 'black on_red' },
+ { col => 'time_behind_master', op => '>', arg => 600, color => 'red' },
+ { col => 'time_behind_master', op => '>', arg => 60, color => 'yellow' },
+ { col => 'time_behind_master', op => '==', arg => 0, color => 'white' },
+ ],
+ group_by => [],
+ aggregate => 0,
+ },
+ t_header => {
+ capt => 'T-Mode Header',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ dirty_bufs => { src => $exprs{DirtyBufs}, trans => [qw(percent)] },
+ history_list_len => { src => 'IB_tx_history_list_len' },
+ lock_structs => { src => 'IB_tx_num_lock_structs' },
+ num_txns => { src => $exprs{NumTxns} },
+ max_txn => { src => $exprs{MaxTxnTime}, trans => [qw(secs_to_time)] },
+ undo_for => { src => 'IB_tx_purge_undo_for' },
+ used_bufs => { src => $exprs{BufPoolFill}, trans => [qw(percent)]},
+ versions => { src => $exprs{OldVersions} },
+ },
+ visible => [ qw(cxn history_list_len versions undo_for dirty_bufs used_bufs num_txns max_txn lock_structs)],
+ filters => [ ],
+ sort_cols => 'cxn',
+ sort_dir => '1',
+ innodb => '',
+ colors => [],
+ hide_caption => 1,
+ group_by => [],
+ aggregate => 0,
+ },
+ var_status => {
+ capt => 'Variables & Status',
+ cust => {},
+ cols => {}, # Generated from current varset
+ visible => [], # Generated from current varset
+ filters => [],
+ sort_cols => '',
+ sort_dir => 1,
+ innodb => '',
+ temp => 1, # Do not persist to config file.
+ hide_caption => 1,
+ pivot => 0,
+ group_by => [],
+ aggregate => 0,
+ },
+ wait_array => {
+ capt => 'InnoDB Wait Array',
+ cust => {},
+ cols => {
+ cxn => { src => 'cxn' },
+ thread => { src => 'thread' },
+ waited_at_filename => { src => 'waited_at_filename' },
+ waited_at_line => { src => 'waited_at_line' },
+ 'time' => { src => 'waited_secs', trans => [ qw(secs_to_time) ] },
+ request_type => { src => 'request_type' },
+ lock_mem_addr => { src => 'lock_mem_addr' },
+ lock_cfile_name => { src => 'lock_cfile_name' },
+ lock_cline => { src => 'lock_cline' },
+ writer_thread => { src => 'writer_thread' },
+ writer_lock_mode => { src => 'writer_lock_mode' },
+ num_readers => { src => 'num_readers' },
+ lock_var => { src => 'lock_var' },
+ waiters_flag => { src => 'waiters_flag' },
+ last_s_file_name => { src => 'last_s_file_name' },
+ last_s_line => { src => 'last_s_line' },
+ last_x_file_name => { src => 'last_x_file_name' },
+ last_x_line => { src => 'last_x_line' },
+ cell_waiting => { src => 'cell_waiting' },
+ cell_event_set => { src => 'cell_event_set' },
+ },
+ visible => [ qw(cxn thread time waited_at_filename waited_at_line request_type num_readers lock_var waiters_flag cell_waiting cell_event_set)],
+ filters => [],
+ sort_cols => 'cxn -time',
+ sort_dir => '1',
+ innodb => 'sm',
+ group_by => [],
+ aggregate => 0,
+ },
+);
+
+# Initialize %tbl_meta from %columns and do some checks.
+foreach my $table_name ( keys %tbl_meta ) {
+ my $table = $tbl_meta{$table_name};
+ my $cols = $table->{cols};
+
+ foreach my $col_name ( keys %$cols ) {
+ my $col_def = $table->{cols}->{$col_name};
+ die "I can't find a column named '$col_name' for '$table_name'" unless $columns{$col_name};
+ $columns{$col_name}->{referenced} = 1;
+
+ foreach my $prop ( keys %col_props ) {
+ # Each column gets non-existing values set from %columns or defaults from %col_props.
+ if ( !$col_def->{$prop} ) {
+ $col_def->{$prop}
+ = defined($columns{$col_name}->{$prop})
+ ? $columns{$col_name}->{$prop}
+ : $col_props{$prop};
+ }
+ }
+
+ # Ensure transformations and aggregate functions are valid
+ die "Unknown aggregate function '$col_def->{agg}' "
+ . "for column '$col_name' in table '$table_name'"
+ unless exists $agg_funcs{$col_def->{agg}};
+ foreach my $trans ( @{$col_def->{trans}} ) {
+ die "Unknown transformation '$trans' "
+ . "for column '$col_name' in table '$table_name'"
+ unless exists $trans_funcs{$trans};
+ }
+ }
+
+ # Ensure each column in visible and group_by exists in cols
+ foreach my $place ( qw(visible group_by) ) {
+ foreach my $col_name ( @{$table->{$place}} ) {
+ if ( !exists $cols->{$col_name} ) {
+ die "Column '$col_name' is listed in '$place' for '$table_name', but doesn't exist";
+ }
+ }
+ }
+
+ # Compile sort and color subroutines
+ $table->{sort_func} = make_sort_func($table);
+ $table->{color_func} = make_color_func($table);
+}
+
+# This is for code cleanup:
+{
+ my @unused_cols = grep { !$columns{$_}->{referenced} } sort keys %columns;
+ if ( @unused_cols ) {
+ die "The following columns are not used: "
+ . join(' ', @unused_cols);
+ }
+}
+
+# ###########################################################################
+# Operating modes {{{3
+# ###########################################################################
+my %modes = (
+ B => {
+ hdr => 'InnoDB Buffers',
+ cust => {},
+ note => 'Shows buffer info from InnoDB',
+ action_for => {
+ i => {
+ action => sub { toggle_config('status_inc') },
+ label => 'Toggle incremental status display',
+ },
+ },
+ display_sub => \&display_B,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(buffer_pool page_statistics insert_buffers adaptive_hash_index)],
+ visible_tables => [qw(buffer_pool page_statistics insert_buffers adaptive_hash_index)],
+ },
+ C => {
+ hdr => 'Command Summary',
+ cust => {},
+ note => 'Shows relative magnitude of variables',
+ action_for => {
+ s => {
+ action => sub { get_config_interactive('cmd_filter') },
+ label => 'Choose variable prefix',
+ },
+ },
+ display_sub => \&display_C,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(cmd_summary)],
+ visible_tables => [qw(cmd_summary)],
+ },
+ D => {
+ hdr => 'InnoDB Deadlocks',
+ cust => {},
+ note => 'View InnoDB deadlock information',
+ action_for => {
+ c => {
+ action => sub { edit_table('deadlock_transactions') },
+ label => 'Choose visible columns',
+ },
+ w => {
+ action => \&create_deadlock,
+ label => 'Wipe deadlock status info by creating a deadlock',
+ },
+ },
+ display_sub => \&display_D,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(deadlock_transactions deadlock_locks)],
+ visible_tables => [qw(deadlock_transactions deadlock_locks)],
+ },
+ F => {
+ hdr => 'InnoDB FK Err',
+ cust => {},
+ note => 'View the latest InnoDB foreign key error',
+ action_for => {},
+ display_sub => \&display_F,
+ connections => [],
+ server_group => '',
+ one_connection => 1,
+ tables => [qw(fk_error)],
+ visible_tables => [qw(fk_error)],
+ },
+ I => {
+ hdr => 'InnoDB I/O Info',
+ cust => {},
+ note => 'Shows I/O info (i/o, log...) from InnoDB',
+ action_for => {
+ i => {
+ action => sub { toggle_config('status_inc') },
+ label => 'Toggle incremental status display',
+ },
+ },
+ display_sub => \&display_I,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(io_threads pending_io file_io_misc log_statistics)],
+ visible_tables => [qw(io_threads pending_io file_io_misc log_statistics)],
+ },
+ L => {
+ hdr => 'Locks',
+ cust => {},
+ note => 'Shows transaction locks',
+ action_for => {
+ a => {
+ action => sub { send_cmd_to_servers('CREATE TABLE IF NOT EXISTS test.innodb_lock_monitor(a int) ENGINE=InnoDB', 0, '', []); },
+ label => 'Start the InnoDB Lock Monitor',
+ },
+ o => {
+ action => sub { send_cmd_to_servers('DROP TABLE IF EXISTS test.innodb_lock_monitor', 0, '', []); },
+ label => 'Stop the InnoDB Lock Monitor',
+ },
+ },
+ display_sub => \&display_L,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(innodb_locks)],
+ visible_tables => [qw(innodb_locks)],
+ },
+ M => {
+ hdr => 'Replication Status',
+ cust => {},
+ note => 'Shows replication (master and slave) status',
+ action_for => {
+ a => {
+ action => sub { send_cmd_to_servers('START SLAVE', 0, 'START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE = ?, MASTER_LOG_POS = ?', []); },
+ label => 'Start slave(s)',
+ },
+ i => {
+ action => sub { toggle_config('status_inc') },
+ label => 'Toggle incremental status display',
+ },
+ o => {
+ action => sub { send_cmd_to_servers('STOP SLAVE', 0, '', []); },
+ label => 'Stop slave(s)',
+ },
+ b => {
+ action => sub { purge_master_logs() },
+ label => 'Purge unused master logs',
+ },
+ },
+ display_sub => \&display_M,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(slave_sql_status slave_io_status master_status)],
+ visible_tables => [qw(slave_sql_status slave_io_status master_status)],
+ },
+ O => {
+ hdr => 'Open Tables',
+ cust => {},
+ note => 'Shows open tables in MySQL',
+ action_for => {
+ r => {
+ action => sub { reverse_sort('open_tables'); },
+ label => 'Reverse sort order',
+ },
+ s => {
+ action => sub { choose_sort_cols('open_tables'); },
+ label => "Choose sort column",
+ },
+ },
+ display_sub => \&display_O,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(open_tables)],
+ visible_tables => [qw(open_tables)],
+ },
+ Q => {
+ hdr => 'Query List',
+ cust => {},
+ note => 'Shows queries from SHOW FULL PROCESSLIST',
+ action_for => {
+ a => {
+ action => sub { toggle_filter('processlist', 'hide_self') },
+ label => 'Toggle the innotop process',
+ },
+ c => {
+ action => sub { edit_table('processlist') },
+ label => 'Choose visible columns',
+ },
+ e => {
+ action => sub { analyze_query('e'); },
+ label => "Explain a thread's query",
+ },
+ f => {
+ action => sub { analyze_query('f'); },
+ label => "Show a thread's full query",
+ },
+ h => {
+ action => sub { toggle_visible_table('Q', 'q_header') },
+ label => 'Toggle the header on and off',
+ },
+ i => {
+ action => sub { toggle_filter('processlist', 'hide_inactive') },
+ label => 'Toggle idle processes',
+ },
+ k => {
+ action => sub { kill_query('CONNECTION') },
+ label => "Kill a query's connection",
+ },
+ r => {
+ action => sub { reverse_sort('processlist'); },
+ label => 'Reverse sort order',
+ },
+ s => {
+ action => sub { choose_sort_cols('processlist'); },
+ label => "Change the display's sort column",
+ },
+ x => {
+ action => sub { kill_query('QUERY') },
+ label => "Kill a query",
+ },
+ },
+ display_sub => \&display_Q,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(q_header processlist)],
+ visible_tables => [qw(q_header processlist)],
+ },
+ R => {
+ hdr => 'InnoDB Row Ops',
+ cust => {},
+ note => 'Shows InnoDB row operation and semaphore info',
+ action_for => {
+ i => {
+ action => sub { toggle_config('status_inc') },
+ label => 'Toggle incremental status display',
+ },
+ },
+ display_sub => \&display_R,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(row_operations row_operation_misc semaphores wait_array)],
+ visible_tables => [qw(row_operations row_operation_misc semaphores wait_array)],
+ },
+ S => {
+ hdr => 'Variables & Status',
+ cust => {},
+ note => 'Shows query load statistics a la vmstat',
+ action_for => {
+ '>' => {
+ action => sub { switch_var_set('S_set', 1) },
+ label => 'Switch to next variable set',
+ },
+ '<' => {
+ action => sub { switch_var_set('S_set', -1) },
+ label => 'Switch to prev variable set',
+ },
+ c => {
+ action => sub {
+ choose_var_set('S_set');
+ start_S_mode();
+ },
+ label => "Choose which set to display",
+ },
+ e => {
+ action => \&edit_current_var_set,
+ label => 'Edit the current set of variables',
+ },
+ i => {
+ action => sub { $clear_screen_sub->(); toggle_config('status_inc') },
+ label => 'Toggle incremental status display',
+ },
+ '-' => {
+ action => sub { set_display_precision(-1) },
+ label => 'Decrease fractional display precision',
+ },
+ '+' => {
+ action => sub { set_display_precision(1) },
+ label => 'Increase fractional display precision',
+ },
+ g => {
+ action => sub { set_s_mode('g') },
+ label => 'Switch to graph (tload) view',
+ },
+ s => {
+ action => sub { set_s_mode('s') },
+ label => 'Switch to standard (vmstat) view',
+ },
+ v => {
+ action => sub { set_s_mode('v') },
+ label => 'Switch to pivoted view',
+ },
+ },
+ display_sub => \&display_S,
+ no_clear_screen => 1,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(var_status)],
+ visible_tables => [qw(var_status)],
+ },
+ T => {
+ hdr => 'InnoDB Txns',
+ cust => {},
+ note => 'Shows InnoDB transactions in top-like format',
+ action_for => {
+ a => {
+ action => sub { toggle_filter('innodb_transactions', 'hide_self') },
+ label => 'Toggle the innotop process',
+ },
+ c => {
+ action => sub { edit_table('innodb_transactions') },
+ label => 'Choose visible columns',
+ },
+ e => {
+ action => sub { analyze_query('e'); },
+ label => "Explain a thread's query",
+ },
+ f => {
+ action => sub { analyze_query('f'); },
+ label => "Show a thread's full query",
+ },
+ h => {
+ action => sub { toggle_visible_table('T', 't_header') },
+ label => 'Toggle the header on and off',
+ },
+ i => {
+ action => sub { toggle_filter('innodb_transactions', 'hide_inactive') },
+ label => 'Toggle inactive transactions',
+ },
+ k => {
+ action => sub { kill_query('CONNECTION') },
+ label => "Kill a transaction's connection",
+ },
+ r => {
+ action => sub { reverse_sort('innodb_transactions'); },
+ label => 'Reverse sort order',
+ },
+ s => {
+ action => sub { choose_sort_cols('innodb_transactions'); },
+ label => "Change the display's sort column",
+ },
+ x => {
+ action => sub { kill_query('QUERY') },
+ label => "Kill a query",
+ },
+ },
+ display_sub => \&display_T,
+ connections => [],
+ server_group => '',
+ one_connection => 0,
+ tables => [qw(t_header innodb_transactions)],
+ visible_tables => [qw(t_header innodb_transactions)],
+ },
+);
+
+# ###########################################################################
+# Global key mappings {{{3
+# Keyed on a single character, which is read from the keyboard. Uppercase
+# letters switch modes. Lowercase letters access commands when in a mode.
+# These can be overridden by action_for in %modes.
+# ###########################################################################
+my %action_for = (
+ '$' => {
+ action => \&edit_configuration,
+ label => 'Edit configuration settings',
+ },
+ '?' => {
+ action => \&display_help,
+ label => 'Show help',
+ },
+ '!' => {
+ action => \&display_license,
+ label => 'Show license and warranty',
+ },
+ '^' => {
+ action => \&edit_table,
+ label => "Edit the displayed table(s)",
+ },
+ '#' => {
+ action => \&choose_server_groups,
+ label => 'Select/create server groups',
+ },
+ '@' => {
+ action => \&choose_servers,
+ label => 'Select/create server connections',
+ },
+ '/' => {
+ action => \&add_quick_filter,
+ label => 'Quickly filter what you see',
+ },
+ '\\' => {
+ action => \&clear_quick_filters,
+ label => 'Clear quick-filters',
+ },
+ '%' => {
+ action => \&choose_filters,
+ label => 'Choose and edit table filters',
+ },
+ "\t" => {
+ action => \&next_server_group,
+ label => 'Switch to the next server group',
+ key => 'TAB',
+ },
+ '=' => {
+ action => \&toggle_aggregate,
+ label => 'Toggle aggregation',
+ },
+ # TODO: can these be auto-generated from %modes?
+ B => {
+ action => sub { switch_mode('B') },
+ label => '',
+ },
+ C => {
+ action => sub { switch_mode('C') },
+ label => '',
+ },
+ D => {
+ action => sub { switch_mode('D') },
+ label => '',
+ },
+ F => {
+ action => sub { switch_mode('F') },
+ label => '',
+ },
+ I => {
+ action => sub { switch_mode('I') },
+ label => '',
+ },
+ L => {
+ action => sub { switch_mode('L') },
+ label => '',
+ },
+ M => {
+ action => sub { switch_mode('M') },
+ label => '',
+ },
+ O => {
+ action => sub { switch_mode('O') },
+ label => '',
+ },
+ Q => {
+ action => sub { switch_mode('Q') },
+ label => '',
+ },
+ R => {
+ action => sub { switch_mode('R') },
+ label => '',
+ },
+ S => {
+ action => \&start_S_mode,
+ label => '',
+ },
+ T => {
+ action => sub { switch_mode('T') },
+ label => '',
+ },
+ d => {
+ action => sub { get_config_interactive('interval') },
+ label => 'Change refresh interval',
+ },
+ n => { action => \&next_server, label => 'Switch to the next connection' },
+ p => { action => \&pause, label => 'Pause innotop', },
+ q => { action => \&finish, label => 'Quit innotop', },
+);
+
+# ###########################################################################
+# Sleep times after certain statements {{{3
+# ###########################################################################
+my %stmt_sleep_time_for = ();
+
+# ###########################################################################
+# Config editor key mappings {{{3
+# ###########################################################################
+my %cfg_editor_action = (
+ c => {
+ note => 'Edit columns, etc in the displayed table(s)',
+ func => \&edit_table,
+ },
+ g => {
+ note => 'Edit general configuration',
+ func => \&edit_configuration_variables,
+ },
+ k => {
+ note => 'Edit row-coloring rules',
+ func => \&edit_color_rules,
+ },
+ p => {
+ note => 'Manage plugins',
+ func => \&edit_plugins,
+ },
+ s => {
+ note => 'Edit server groups',
+ func => \&edit_server_groups,
+ },
+ S => {
+ note => 'Edit SQL statement sleep delays',
+ func => \&edit_stmt_sleep_times,
+ },
+ t => {
+ note => 'Choose which table(s) to display in this mode',
+ func => \&choose_mode_tables,
+ },
+);
+
+# ###########################################################################
+# Color editor key mappings {{{3
+# ###########################################################################
+my %color_editor_action = (
+ n => {
+ note => 'Create a new color rule',
+ func => sub {
+ my ( $tbl, $idx ) = @_;
+ my $meta = $tbl_meta{$tbl};
+
+ $clear_screen_sub->();
+ my $col;
+ do {
+ $col = prompt_list(
+ 'Choose the target column for the rule',
+ '',
+ sub { return keys %{$meta->{cols}} },
+ { map { $_ => $meta->{cols}->{$_}->{label} } keys %{$meta->{cols}} });
+ } while ( !$col );
+ ( $col ) = grep { $_ } split(/\W+/, $col);
+ return $idx unless $col && exists $meta->{cols}->{$col};
+
+ $clear_screen_sub->();
+ my $op;
+ do {
+ $op = prompt_list(
+ 'Choose the comparison operator for the rule',
+ '',
+ sub { return keys %comp_ops },
+ { map { $_ => $comp_ops{$_} } keys %comp_ops } );
+ } until ( $op );
+ $op =~ s/\s+//g;
+ return $idx unless $op && exists $comp_ops{$op};
+
+ my $arg;
+ do {
+ $arg = prompt('Specify an argument for the comparison');
+ } until defined $arg;
+
+ my $color;
+ do {
+ $color = prompt_list(
+ 'Choose the color(s) the row should be when the rule matches',
+ '',
+ sub { return keys %ansicolors },
+ { map { $_ => $_ } keys %ansicolors } );
+ } until defined $color;
+ $color = join(' ', unique(grep { exists $ansicolors{$_} } split(/\W+/, $color)));
+ return $idx unless $color;
+
+ push @{$tbl_meta{$tbl}->{colors}}, {
+ col => $col,
+ op => $op,
+ arg => $arg,
+ color => $color
+ };
+ $tbl_meta{$tbl}->{cust}->{colors} = 1;
+
+ return $idx;
+ },
+ },
+ d => {
+ note => 'Remove the selected rule',
+ func => sub {
+ my ( $tbl, $idx ) = @_;
+ my @rules = @{ $tbl_meta{$tbl}->{colors} };
+ return 0 unless @rules > 0 && $idx < @rules && $idx >= 0;
+ splice(@{$tbl_meta{$tbl}->{colors}}, $idx, 1);
+ $tbl_meta{$tbl}->{cust}->{colors} = 1;
+ return $idx == @rules ? $#rules : $idx;
+ },
+ },
+ j => {
+ note => 'Move highlight down one',
+ func => sub {
+ my ( $tbl, $idx ) = @_;
+ my $num_rules = scalar @{$tbl_meta{$tbl}->{colors}};
+ return ($idx + 1) % $num_rules;
+ },
+ },
+ k => {
+ note => 'Move highlight up one',
+ func => sub {
+ my ( $tbl, $idx ) = @_;
+ my $num_rules = scalar @{$tbl_meta{$tbl}->{colors}};
+ return ($idx - 1) % $num_rules;
+ },
+ },
+ '+' => {
+ note => 'Move selected rule up one',
+ func => sub {
+ my ( $tbl, $idx ) = @_;
+ my $meta = $tbl_meta{$tbl};
+ my $dest = $idx == 0 ? scalar(@{$meta->{colors}} - 1) : $idx - 1;
+ my $temp = $meta->{colors}->[$idx];
+ $meta->{colors}->[$idx] = $meta->{colors}->[$dest];
+ $meta->{colors}->[$dest] = $temp;
+ $meta->{cust}->{colors} = 1;
+ return $dest;
+ },
+ },
+ '-' => {
+ note => 'Move selected rule down one',
+ func => sub {
+ my ( $tbl, $idx ) = @_;
+ my $meta = $tbl_meta{$tbl};
+ my $dest = $idx == scalar(@{$meta->{colors}} - 1) ? 0 : $idx + 1;
+ my $temp = $meta->{colors}->[$idx];
+ $meta->{colors}->[$idx] = $meta->{colors}->[$dest];
+ $meta->{colors}->[$dest] = $temp;
+ $meta->{cust}->{colors} = 1;
+ return $dest;
+ },
+ },
+);
+
+# ###########################################################################
+# Plugin editor key mappings {{{3
+# ###########################################################################
+my %plugin_editor_action = (
+ '*' => {
+ note => 'Toggle selected plugin active/inactive',
+ func => sub {
+ my ( $plugins, $idx ) = @_;
+ my $plugin = $plugins->[$idx];
+ $plugin->{active} = $plugin->{active} ? 0 : 1;
+ return $idx;
+ },
+ },
+ j => {
+ note => 'Move highlight down one',
+ func => sub {
+ my ( $plugins, $idx ) = @_;
+ return ($idx + 1) % scalar(@$plugins);
+ },
+ },
+ k => {
+ note => 'Move highlight up one',
+ func => sub {
+ my ( $plugins, $idx ) = @_;
+ return $idx == 0 ? @$plugins - 1 : $idx - 1;
+ },
+ },
+);
+
+# ###########################################################################
+# Table editor key mappings {{{3
+# ###########################################################################
+my %tbl_editor_action = (
+ a => {
+ note => 'Add a column to the table',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
+ my %all_cols = %{ $tbl_meta{$tbl}->{cols} };
+ delete @all_cols{@visible_cols};
+ my $choice = prompt_list(
+ 'Choose a column',
+ '',
+ sub { return keys %all_cols; },
+ { map { $_ => $all_cols{$_}->{label} || $all_cols{$_}->{hdr} } keys %all_cols });
+ if ( $all_cols{$choice} ) {
+ push @{$tbl_meta{$tbl}->{visible}}, $choice;
+ $tbl_meta{$tbl}->{cust}->{visible} = 1;
+ return $choice;
+ }
+ return $col;
+ },
+ },
+ n => {
+ note => 'Create a new column and add it to the table',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+
+ $clear_screen_sub->();
+ print word_wrap("Choose a name for the column. This name is not displayed, and is used only "
+ . "for internal reference. It can contain only lowercase letters, numbers, "
+ . "and underscores.");
+ print "\n\n";
+ do {
+ $col = prompt("Enter column name");
+ $col = '' if $col =~ m/[^a-z0-9_]/;
+ } while ( !$col );
+
+ $clear_screen_sub->();
+ my $hdr;
+ do {
+ $hdr = prompt("Enter column header");
+ } while ( !$hdr );
+
+ $clear_screen_sub->();
+ print "Choose a source for the column's data\n\n";
+ my ( $src, $sub, $err );
+ do {
+ if ( $err ) {
+ print "Error: $err\n\n";
+ }
+ $src = prompt("Enter column source");
+ if ( $src ) {
+ ( $sub, $err ) = compile_expr($src);
+ }
+ } until ( !$err);
+
+ # TODO: this duplicates %col_props.
+ $tbl_meta{$tbl}->{cols}->{$col} = {
+ hdr => $hdr,
+ src => $src,
+ just => '-',
+ num => 0,
+ label => 'User-defined',
+ user => 1,
+ tbl => $tbl,
+ minw => 0,
+ maxw => 0,
+ trans => [],
+ func => $sub,
+ dec => 0,
+ agg => 0,
+ aggonly => 0,
+ };
+
+ $tbl_meta{$tbl}->{visible} = [ unique(@{$tbl_meta{$tbl}->{visible}}, $col) ];
+ $tbl_meta{$tbl}->{cust}->{visible} = 1;
+ return $col;
+ },
+ },
+ d => {
+ note => 'Remove selected column',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
+ my $idx = 0;
+ return $col unless @visible_cols > 1;
+ while ( $visible_cols[$idx] ne $col ) {
+ $idx++;
+ }
+ $tbl_meta{$tbl}->{visible} = [ grep { $_ ne $col } @visible_cols ];
+ $tbl_meta{$tbl}->{cust}->{visible} = 1;
+ return $idx == $#visible_cols ? $visible_cols[$idx - 1] : $visible_cols[$idx + 1];
+ },
+ },
+ e => {
+ note => 'Edit selected column',
+ func => sub {
+ # TODO: make this editor hotkey-driven and give readline support.
+ my ( $tbl, $col ) = @_;
+ $clear_screen_sub->();
+ my $meta = $tbl_meta{$tbl}->{cols}->{$col};
+ my @prop = qw(hdr label src just num minw maxw trans agg); # TODO redundant
+
+ my $answer;
+ do {
+ # Do what the user asked...
+ if ( $answer && grep { $_ eq $answer } @prop ) {
+ # Some properties are arrays, others scalars.
+ my $ini = ref $col_props{$answer} ? join(' ', @{$meta->{$answer}}) : $meta->{$answer};
+ my $val = prompt("New value for $answer", undef, $ini);
+ $val = [ split(' ', $val) ] if ref($col_props{$answer});
+ if ( $answer eq 'trans' ) {
+ $val = [ unique(grep{ exists $trans_funcs{$_} } @$val) ];
+ }
+ @{$meta}{$answer, 'user', 'tbl' } = ( $val, 1, $tbl );
+ }
+
+ my @display_lines = (
+ '',
+ "You are editing column $tbl.$col.\n",
+ );
+
+ push @display_lines, create_table2(
+ \@prop,
+ { map { $_ => $_ } @prop },
+ { map { $_ => ref $meta->{$_} eq 'ARRAY' ? join(' ', @{$meta->{$_}})
+ : ref $meta->{$_} ? '[expression code]'
+ : $meta->{$_}
+ } @prop
+ },
+ { sep => ' ' });
+ draw_screen(\@display_lines, { raw => 1 });
+ print "\n\n"; # One to add space, one to clear readline artifacts
+ $answer = prompt('Edit what? (q to quit)');
+ } while ( $answer ne 'q' );
+
+ return $col;
+ },
+ },
+ j => {
+ note => 'Move highlight down one',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
+ my $idx = 0;
+ while ( $visible_cols[$idx] ne $col ) {
+ $idx++;
+ }
+ return $visible_cols[ ($idx + 1) % @visible_cols ];
+ },
+ },
+ k => {
+ note => 'Move highlight up one',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
+ my $idx = 0;
+ while ( $visible_cols[$idx] ne $col ) {
+ $idx++;
+ }
+ return $visible_cols[ $idx - 1 ];
+ },
+ },
+ '+' => {
+ note => 'Move selected column up one',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ my $meta = $tbl_meta{$tbl};
+ my @visible_cols = @{$meta->{visible}};
+ my $idx = 0;
+ while ( $visible_cols[$idx] ne $col ) {
+ $idx++;
+ }
+ if ( $idx ) {
+ $visible_cols[$idx] = $visible_cols[$idx - 1];
+ $visible_cols[$idx - 1] = $col;
+ $meta->{visible} = \@visible_cols;
+ }
+ else {
+ shift @{$meta->{visible}};
+ push @{$meta->{visible}}, $col;
+ }
+ $meta->{cust}->{visible} = 1;
+ return $col;
+ },
+ },
+ '-' => {
+ note => 'Move selected column down one',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ my $meta = $tbl_meta{$tbl};
+ my @visible_cols = @{$meta->{visible}};
+ my $idx = 0;
+ while ( $visible_cols[$idx] ne $col ) {
+ $idx++;
+ }
+ if ( $idx == $#visible_cols ) {
+ unshift @{$meta->{visible}}, $col;
+ pop @{$meta->{visible}};
+ }
+ else {
+ $visible_cols[$idx] = $visible_cols[$idx + 1];
+ $visible_cols[$idx + 1] = $col;
+ $meta->{visible} = \@visible_cols;
+ }
+ $meta->{cust}->{visible} = 1;
+ return $col;
+ },
+ },
+ f => {
+ note => 'Choose filters',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ choose_filters($tbl);
+ return $col;
+ },
+ },
+ o => {
+ note => 'Edit color rules',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ edit_color_rules($tbl);
+ return $col;
+ },
+ },
+ s => {
+ note => 'Choose sort columns',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ choose_sort_cols($tbl);
+ return $col;
+ },
+ },
+ g => {
+ note => 'Choose group-by (aggregate) columns',
+ func => sub {
+ my ( $tbl, $col ) = @_;
+ choose_group_cols($tbl);
+ return $col;
+ },
+ },
+);
+
+# ###########################################################################
+# Global variables and environment {{{2
+# ###########################################################################
+
+my @this_term_size; # w_chars, h_chars, w_pix, h_pix
+my @last_term_size; # w_chars, h_chars, w_pix, h_pix
+my $char;
+my $windows = $OSNAME =~ m/MSWin/;
+my $have_color = 0;
+my $MAX_ULONG = 4294967295; # 2^32-1
+my $num_regex = qr/^[+-]?(?=\d|\.)\d*(?:\.\d+)?(?:E[+-]?\d+|)$/i;
+my $int_regex = qr/^\d+$/;
+my $bool_regex = qr/^[01]$/;
+my $term = undef;
+my $file = undef; # File to watch for InnoDB monitor output
+my $file_mtime = undef; # Status of watched file
+my $file_data = undef; # Last chunk of text read from file
+my $innodb_parser = InnoDBParser->new;
+
+my $nonfatal_errs = join('|',
+ 'Access denied for user',
+ 'Unknown MySQL server host',
+ 'Unknown database',
+ 'Can\'t connect to local MySQL server through socket',
+ 'Can\'t connect to MySQL server on',
+ 'MySQL server has gone away',
+ 'Cannot call SHOW INNODB STATUS',
+ 'Access denied',
+ 'AutoCommit',
+);
+
+if ( !$opts{n} ) {
+ require Term::ReadLine;
+ $term = Term::ReadLine->new('innotop');
+}
+
+# Stores status, variables, innodb status, master/slave status etc.
+# Keyed on connection name. Each entry is a hashref of current and past data sets,
+# keyed on clock tick.
+my %vars;
+my %info_gotten = (); # Which things have been retrieved for the current clock tick.
+
+# Stores info on currently displayed queries: cxn, connection ID, query text.
+my @current_queries;
+
+my $lines_printed = 0;
+my $clock = 0; # Incremented with every wake-sleep cycle
+my $clearing_deadlocks = 0;
+
+# If terminal coloring is available, use it. The only function I want from
+# the module is the colored() function.
+eval {
+ if ( !$opts{n} ) {
+ if ( $windows ) {
+ require Win32::Console::ANSI;
+ }
+ require Term::ANSIColor;
+ import Term::ANSIColor qw(colored);
+ $have_color = 1;
+ }
+};
+if ( $EVAL_ERROR || $opts{n} ) {
+ # If there was an error, manufacture my own colored() function that does no
+ # coloring.
+ *colored = sub { pop @_; @_; };
+}
+
+if ( $opts{n} ) {
+ $clear_screen_sub = sub {};
+}
+elsif ( $windows ) {
+ $clear_screen_sub = sub { $lines_printed = 0; system("cls") };
+}
+else {
+ my $clear = `clear`;
+ $clear_screen_sub = sub { $lines_printed = 0; print $clear };
+}
+
+# ###########################################################################
+# Config storage. {{{2
+# ###########################################################################
+my %config = (
+ color => {
+ val => $have_color,
+ note => 'Whether to use terminal coloring',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ cmd_filter => {
+ val => 'Com_',
+ note => 'Prefix for values in C mode',
+ conf => [qw(C)],
+ },
+ plugin_dir => {
+ val => "$homepath/.innotop/plugins",
+ note => 'Directory where plugins can be found',
+ conf => 'ALL',
+ },
+ show_percent => {
+ val => 1,
+ note => 'Show the % symbol after percentages',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ skip_innodb => {
+ val => 0,
+ note => 'Disable SHOW INNODB STATUS',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ S_func => {
+ val => 's',
+ note => 'What to display in S mode: graph, status, pivoted status',
+ conf => [qw(S)],
+ pat => qr/^[gsv]$/,
+ },
+ cxn_timeout => {
+ val => 28800,
+ note => 'Connection timeout for keeping unused connections alive',
+ conf => 'ALL',
+ pat => $int_regex,
+ },
+ graph_char => {
+ val => '*',
+ note => 'Character for drawing graphs',
+ conf => [ qw(S) ],
+ pat => qr/^.$/,
+ },
+ show_cxn_errors_in_tbl => {
+ val => 1,
+ note => 'Whether to display connection errors as rows in the table',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ hide_hdr => {
+ val => 0,
+ note => 'Whether to show column headers',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ show_cxn_errors => {
+ val => 1,
+ note => 'Whether to print connection errors to STDOUT',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ readonly => {
+ val => 1,
+ note => 'Whether the config file is read-only',
+ conf => [ qw() ],
+ pat => $bool_regex,
+ },
+ global => {
+ val => 1,
+ note => 'Whether to show GLOBAL variables and status',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ header_highlight => {
+ val => 'bold',
+ note => 'How to highlight table column headers',
+ conf => 'ALL',
+ pat => qr/^(?:bold|underline)$/,
+ },
+ display_table_captions => {
+ val => 1,
+ note => 'Whether to put captions on tables',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ charset => {
+ val => 'ascii',
+ note => 'What type of characters should be displayed in queries (ascii, unicode, none)',
+ conf => 'ALL',
+ pat => qr/^(?:ascii|unicode|none)$/,
+ },
+ auto_wipe_dl => {
+ val => 0,
+ note => 'Whether to auto-wipe InnoDB deadlocks',
+ conf => 'ALL',
+ pat => $bool_regex,
+ },
+ max_height => {
+ val => 30,
+ note => '[Win32] Max window height',
+ conf => 'ALL',
+ },
+ debug => {
+ val => 0,
+ pat => $bool_regex,
+ note => 'Debug mode (more verbose errors, uses more memory)',
+ conf => 'ALL',
+ },
+ num_digits => {
+ val => 2,
+ pat => $int_regex,
+ note => 'How many digits to show in fractional numbers and percents',
+ conf => 'ALL',
+ },
+ debugfile => {
+ val => "$homepath/.innotop/core_dump",
+ note => 'A debug file in case you are interested in error output',
+ },
+ show_statusbar => {
+ val => 1,
+ pat => $bool_regex,
+ note => 'Whether to show the status bar in the display',
+ conf => 'ALL',
+ },
+ mode => {
+ val => "Q",
+ note => "Which mode to start in",
+ cmdline => 1,
+ },
+ status_inc => {
+ val => 0,
+ note => 'Whether to show raw or incremental values for status variables',
+ pat => $bool_regex,
+ },
+ interval => {
+ val => 10,
+ pat => qr/^(?:(?:\d*?[1-9]\d*(?:\.\d*)?)|(?:\d*\.\d*?[1-9]\d*))$/,
+ note => "The interval at which the display will be refreshed. Fractional values allowed.",
+ },
+ num_status_sets => {
+ val => 9,
+ pat => $int_regex,
+ note => 'How many sets of STATUS and VARIABLES values to show',
+ conf => [ qw(S) ],
+ },
+ S_set => {
+ val => 'general',
+ pat => qr/^\w+$/,
+ note => 'Which set of variables to display in S (Variables & Status) mode',
+ conf => [ qw(S) ],
+ },
+);
+
+# ###########################################################################
+# Config file sections {{{2
+# The configuration file is broken up into sections like a .ini file. This
+# variable defines those sections and the subroutines responsible for reading
+# and writing them.
+# ###########################################################################
+my %config_file_sections = (
+ plugins => {
+ reader => \&load_config_plugins,
+ writer => \&save_config_plugins,
+ },
+ group_by => {
+ reader => \&load_config_group_by,
+ writer => \&save_config_group_by,
+ },
+ filters => {
+ reader => \&load_config_filters,
+ writer => \&save_config_filters,
+ },
+ active_filters => {
+ reader => \&load_config_active_filters,
+ writer => \&save_config_active_filters,
+ },
+ visible_tables => {
+ reader => \&load_config_visible_tables,
+ writer => \&save_config_visible_tables,
+ },
+ sort_cols => {
+ reader => \&load_config_sort_cols,
+ writer => \&save_config_sort_cols,
+ },
+ active_columns => {
+ reader => \&load_config_active_columns,
+ writer => \&save_config_active_columns,
+ },
+ tbl_meta => {
+ reader => \&load_config_tbl_meta,
+ writer => \&save_config_tbl_meta,
+ },
+ general => {
+ reader => \&load_config_config,
+ writer => \&save_config_config,
+ },
+ connections => {
+ reader => \&load_config_connections,
+ writer => \&save_config_connections,
+ },
+ active_connections => {
+ reader => \&load_config_active_connections,
+ writer => \&save_config_active_connections,
+ },
+ server_groups => {
+ reader => \&load_config_server_groups,
+ writer => \&save_config_server_groups,
+ },
+ active_server_groups => {
+ reader => \&load_config_active_server_groups,
+ writer => \&save_config_active_server_groups,
+ },
+ max_values_seen => {
+ reader => \&load_config_mvs,
+ writer => \&save_config_mvs,
+ },
+ varsets => {
+ reader => \&load_config_varsets,
+ writer => \&save_config_varsets,
+ },
+ colors => {
+ reader => \&load_config_colors,
+ writer => \&save_config_colors,
+ },
+ stmt_sleep_times => {
+ reader => \&load_config_stmt_sleep_times,
+ writer => \&save_config_stmt_sleep_times,
+ },
+);
+
+# Config file sections have some dependencies, so they have to be read/written in order.
+my @ordered_config_file_sections = qw(general plugins filters active_filters tbl_meta
+ connections active_connections server_groups active_server_groups max_values_seen
+ active_columns sort_cols visible_tables varsets colors stmt_sleep_times
+ group_by);
+
+# All events for which plugins may register themselves. Entries are arrayrefs.
+my %event_listener_for = map { $_ => [] }
+ qw(
+ extract_values
+ set_to_tbl_pre_filter set_to_tbl_pre_sort set_to_tbl_pre_group
+ set_to_tbl_pre_colorize set_to_tbl_pre_transform set_to_tbl_pre_pivot
+ set_to_tbl_pre_create set_to_tbl_post_create
+ draw_screen
+ );
+
+# All variables to which plugins have access.
+my %pluggable_vars = (
+ action_for => \%action_for,
+ agg_funcs => \%agg_funcs,
+ config => \%config,
+ connections => \%connections,
+ dbhs => \%dbhs,
+ filters => \%filters,
+ modes => \%modes,
+ server_groups => \%server_groups,
+ tbl_meta => \%tbl_meta,
+ trans_funcs => \%trans_funcs,
+ var_sets => \%var_sets,
+);
+
+# ###########################################################################
+# Contains logic to generate prepared statements for a given function for a
+# given DB connection. Returns a $sth.
+# ###########################################################################
+my %stmt_maker_for = (
+ INNODB_STATUS => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare(version_ge( $dbh, '5.0.0' )
+ ? 'SHOW ENGINE INNODB STATUS'
+ : 'SHOW INNODB STATUS');
+ },
+ SHOW_VARIABLES => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare($config{global}->{val} && version_ge( $dbh, '4.0.3' )
+ ? 'SHOW GLOBAL VARIABLES'
+ : 'SHOW VARIABLES');
+ },
+ SHOW_STATUS => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare($config{global}->{val} && version_ge( $dbh, '5.0.2' )
+ ? 'SHOW GLOBAL STATUS'
+ : 'SHOW STATUS');
+ },
+ KILL_QUERY => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare(version_ge( $dbh, '5.0.0' )
+ ? 'KILL QUERY ?'
+ : 'KILL ?');
+ },
+ SHOW_MASTER_LOGS => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare('SHOW MASTER LOGS');
+ },
+ SHOW_MASTER_STATUS => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare('SHOW MASTER STATUS');
+ },
+ SHOW_SLAVE_STATUS => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare('SHOW SLAVE STATUS');
+ },
+ KILL_CONNECTION => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare(version_ge( $dbh, '5.0.0' )
+ ? 'KILL CONNECTION ?'
+ : 'KILL ?');
+ },
+ OPEN_TABLES => sub {
+ my ( $dbh ) = @_;
+ return version_ge($dbh, '4.0.0')
+ ? $dbh->prepare('SHOW OPEN TABLES')
+ : undef;
+ },
+ PROCESSLIST => sub {
+ my ( $dbh ) = @_;
+ return $dbh->prepare('SHOW FULL PROCESSLIST');
+ },
+);
+
+# Plugins!
+my %plugins = (
+);
+
+# ###########################################################################
+# Run the program {{{1
+# ###########################################################################
+
+# This config variable is only useful for MS Windows because its terminal
+# can't tell how tall it is.
+if ( !$windows ) {
+ delete $config{max_height};
+}
+
+# Try to lower my priority.
+eval { setpriority(0, 0, getpriority(0, 0) + 10); };
+
+# Print stuff to the screen immediately, don't wait for a newline.
+$OUTPUT_AUTOFLUSH = 1;
+
+# Clear the screen and load the configuration.
+$clear_screen_sub->();
+load_config();
+
+# Override config variables with command-line options
+my %cmdline =
+ map { $_->{c} => $opts{$_->{k}} }
+ grep { exists $_->{c} && exists $opts{$_->{k}} }
+ @opt_spec;
+
+foreach my $name (keys %cmdline) {
+ next if not defined $cmdline{$name};
+ my $val = $cmdline{$name};
+ if ( exists($config{$name}) and (!$config{$name}->{pat} or $val =~ m/$config{$name}->{pat}/ )) {
+ $config{$name}->{val} = $val;
+ }
+}
+
+post_process_tbl_meta();
+
+# Make sure no changes are written to config file in non-interactive mode.
+if ( $opts{n} ) {
+ $config{readonly}->{val} = 1;
+}
+
+eval {
+
+ # Open the file for InnoDB status
+ if ( @ARGV ) {
+ my $filename = shift @ARGV;
+ open $file, "<", $filename
+ or die "Cannot open '$filename': $OS_ERROR";
+ }
+
+ # In certain modes we might have to collect data for two cycles
+ # before printing anything out, so we need to bump up the count one.
+ if ( $opts{n} && $opts{count} && $config{status_inc}->{val}
+ && $config{mode}->{val} =~ m/[S]/ )
+ {
+ $opts{count}++;
+ }
+
+ while (++$clock) {
+
+ my $mode = $config{mode}->{val} || 'Q';
+ if ( !$modes{$mode} ) {
+ die "Mode '$mode' doesn't exist; try one of these:\n"
+ . join("\n", map { " $_ $modes{$_}->{hdr}" } sort keys %modes)
+ . "\n";
+ }
+
+ if ( !$opts{n} ) {
+ @last_term_size = @this_term_size;
+ @this_term_size = Term::ReadKey::GetTerminalSize(\*STDOUT);
+ if ( $windows ) {
+ $this_term_size[0]--;
+ $this_term_size[1]
+ = min($this_term_size[1], $config{max_height}->{val});
+ }
+ die("Can't read terminal size") unless @this_term_size;
+ }
+
+ # If there's no connection to a database server, we need to fix that...
+ if ( !%connections ) {
+ print "You have not defined any database connections.\n\n";
+ add_new_dsn();
+ }
+
+ # See whether there are any connections defined for this mode. If there's only one
+ # connection total, assume the user wants to just use innotop for a single server
+ # and don't ask which server to connect to. Also, if we're monitoring from a file,
+ # we just use the first connection.
+ if ( !get_connections() ) {
+ if ( $file || 1 == scalar keys %connections ) {
+ $modes{$config{mode}->{val}}->{connections} = [ keys %connections ];
+ }
+ else {
+ choose_connections();
+ }
+ }
+
+ # Term::ReadLine might have re-set $OUTPUT_AUTOFLUSH.
+ $OUTPUT_AUTOFLUSH = 1;
+
+ # Prune old data
+ my $sets = $config{num_status_sets}->{val};
+ foreach my $store ( values %vars ) {
+ delete @{$store}{ grep { $_ < $clock - $sets } keys %$store };
+ }
+ %info_gotten = ();
+
+ # Call the subroutine to display this mode.
+ $modes{$mode}->{display_sub}->();
+
+ # It may be time to quit now.
+ if ( $opts{count} && $clock >= $opts{count} ) {
+ finish();
+ }
+
+ # Wait for a bit.
+ if ( $opts{n} ) {
+ sleep($config{interval}->{val});
+ }
+ else {
+ ReadMode('cbreak');
+ $char = ReadKey($config{interval}->{val});
+ ReadMode('normal');
+ }
+
+ # Handle whatever action the key indicates.
+ do_key_action();
+
+ }
+};
+if ( $EVAL_ERROR ) {
+ core_dump( $EVAL_ERROR );
+}
+finish();
+
+# Subroutines {{{1
+# Mode functions{{{2
+# switch_mode {{{3
+sub switch_mode {
+ my $mode = shift;
+ $config{mode}->{val} = $mode;
+}
+
+# Prompting functions {{{2
+# prompt_list {{{3
+# Prompts the user for a value, given a question, initial value,
+# a completion function and a hashref of hints.
+sub prompt_list {
+ die "Can't call in non-interactive mode" if $opts{n};
+ my ( $question, $init, $completion, $hints ) = @_;
+ if ( $hints ) {
+ # Figure out how wide the table will be
+ my $max_name = max(map { length($_) } keys %$hints );
+ $max_name ||= 0;
+ $max_name += 3;
+ my @meta_rows = create_table2(
+ [ sort keys %$hints ],
+ { map { $_ => $_ } keys %$hints },
+ { map { $_ => trunc($hints->{$_}, $this_term_size[0] - $max_name) } keys %$hints },
+ { sep => ' ' });
+ if (@meta_rows > 10) {
+ # Try to split and stack the meta rows next to each other
+ my $split = int(@meta_rows / 2);
+ @meta_rows = stack_next(
+ [@meta_rows[0..$split - 1]],
+ [@meta_rows[$split..$#meta_rows]],
+ { pad => ' | '},
+ );
+ }
+ print join( "\n",
+ '',
+ map { ref $_ ? colored(@$_) : $_ } create_caption('Choose from', @meta_rows), ''),
+ "\n";
+ }
+ $term->Attribs->{completion_function} = $completion;
+ my $answer = $term->readline("$question: ", $init);
+ $OUTPUT_AUTOFLUSH = 1;
+ $answer = '' if !defined($answer);
+ $answer =~ s/\s+$//;
+ return $answer;
+}
+
+# prompt {{{3
+# Prints out a prompt and reads from the keyboard, then validates with the
+# validation regex until the input is correct.
+sub prompt {
+ die "Can't call in non-interactive mode" if $opts{n};
+ my ( $prompt, $regex, $init, $completion ) = @_;
+ my $response;
+ my $success = 0;
+ do {
+ if ( $completion ) {
+ $term->Attribs->{completion_function} = $completion;
+ }
+ $response = $term->readline("$prompt: ", $init);
+ if ( $regex && $response !~ m/$regex/ ) {
+ print "Invalid response.\n\n";
+ }
+ else {
+ $success = 1;
+ }
+ } while ( !$success );
+ $OUTPUT_AUTOFLUSH = 1;
+ $response =~ s/\s+$//;
+ return $response;
+}
+
+# prompt_noecho {{{3
+# Unfortunately, suppressing echo with Term::ReadLine isn't reliable; the user might not
+# have that library, or it might not support that feature.
+sub prompt_noecho {
+ my ( $prompt ) = @_;
+ print colored("$prompt: ", 'underline');
+ my $response;
+ ReadMode('noecho');
+ $response = <STDIN>;
+ chomp($response);
+ ReadMode('normal');
+ return $response;
+}
+
+# do_key_action {{{3
+# Depending on whether a key was read, do something. Keys have certain
+# actions defined in lookup tables. Each mode may have its own lookup table,
+# which trumps the global table -- so keys can be context-sensitive. The key
+# may be read and written in a subroutine, so it's a global.
+sub do_key_action {
+ if ( defined $char ) {
+ my $mode = $config{mode}->{val};
+ my $action
+ = defined($modes{$mode}->{action_for}->{$char})
+ ? $modes{$mode}->{action_for}->{$char}->{action}
+ : defined($action_for{$char})
+ ? $action_for{$char}->{action}
+ : sub{};
+ $action->();
+ }
+}
+
+# pause {{{3
+sub pause {
+ die "Can't call in non-interactive mode" if $opts{n};
+ my $msg = shift;
+ print defined($msg) ? "\n$msg" : "\nPress any key to continue";
+ ReadMode('cbreak');
+ my $char = ReadKey(0);
+ ReadMode('normal');
+ return $char;
+}
+
+# reverse_sort {{{3
+sub reverse_sort {
+ my $tbl = shift;
+ $tbl_meta{$tbl}->{sort_dir} *= -1;
+}
+
+# select_cxn {{{3
+# Selects connection(s). If the mode (or argument list) has only one, returns
+# it without prompt.
+sub select_cxn {
+ my ( $prompt, @cxns ) = @_;
+ if ( !@cxns ) {
+ @cxns = get_connections();
+ }
+ if ( @cxns == 1 ) {
+ return $cxns[0];
+ }
+ my $choices = prompt_list(
+ $prompt,
+ $cxns[0],
+ sub{ return @cxns },
+ { map { $_ => $connections{$_}->{dsn} } @cxns });
+ my @result = unique(grep { my $a = $_; grep { $_ eq $a } @cxns } split(/\s+/, $choices));
+ return @result;
+}
+
+# kill_query {{{3
+# Kills a connection, or on new versions, optionally a query but not connection.
+sub kill_query {
+ my ( $q_or_c ) = @_;
+
+ my $info = choose_thread(
+ sub { 1 },
+ 'Select a thread to kill the ' . $q_or_c,
+ );
+ return unless $info;
+ return unless pause("Kill $info->{id}?") =~ m/y/i;
+
+ eval {
+ do_stmt($info->{cxn}, $q_or_c eq 'QUERY' ? 'KILL_QUERY' : 'KILL_CONNECTION', $info->{id} );
+ };
+
+ if ( $EVAL_ERROR ) {
+ print "\nError: $EVAL_ERROR";
+ pause();
+ }
+}
+
+# set_display_precision {{{3
+sub set_display_precision {
+ my $dir = shift;
+ $config{num_digits}->{val} = min(9, max(0, $config{num_digits}->{val} + $dir));
+}
+
+sub toggle_visible_table {
+ my ( $mode, $table ) = @_;
+ my $visible = $modes{$mode}->{visible_tables};
+ if ( grep { $_ eq $table } @$visible ) {
+ $modes{$mode}->{visible_tables} = [ grep { $_ ne $table } @$visible ];
+ }
+ else {
+ unshift @$visible, $table;
+ }
+ $modes{$mode}->{cust}->{visible_tables} = 1;
+}
+
+# toggle_filter{{{3
+sub toggle_filter {
+ my ( $tbl, $filter ) = @_;
+ my $filters = $tbl_meta{$tbl}->{filters};
+ if ( grep { $_ eq $filter } @$filters ) {
+ $tbl_meta{$tbl}->{filters} = [ grep { $_ ne $filter } @$filters ];
+ }
+ else {
+ push @$filters, $filter;
+ }
+ $tbl_meta{$tbl}->{cust}->{filters} = 1;
+}
+
+# toggle_config {{{3
+sub toggle_config {
+ my ( $key ) = @_;
+ $config{$key}->{val} ^= 1;
+}
+
+# create_deadlock {{{3
+sub create_deadlock {
+ $clear_screen_sub->();
+
+ print "This function will deliberately cause a small deadlock, "
+ . "clearing deadlock information from the InnoDB monitor.\n\n";
+
+ my $answer = prompt("Are you sure you want to proceed? Say 'y' if you do");
+ return 0 unless $answer eq 'y';
+
+ my ( $cxn ) = select_cxn('Clear on which server? ');
+ return unless $cxn && exists($connections{$cxn});
+
+ clear_deadlock($cxn);
+}
+
+# deadlock_thread {{{3
+sub deadlock_thread {
+ my ( $id, $tbl, $cxn ) = @_;
+
+ eval {
+ my $dbh = get_new_db_connection($cxn, 1);
+ my @stmts = (
+ "set transaction isolation level serializable",
+ (version_ge($dbh, '4.0.11') ? "start transaction" : 'begin'),
+ "select * from $tbl where a = $id",
+ "update $tbl set a = $id where a <> $id",
+ );
+
+ foreach my $stmt (@stmts[0..2]) {
+ $dbh->do($stmt);
+ }
+ sleep(1 + $id);
+ $dbh->do($stmts[-1]);
+ };
+ if ( $EVAL_ERROR ) {
+ if ( $EVAL_ERROR !~ m/Deadlock found/ ) {
+ die $EVAL_ERROR;
+ }
+ }
+ exit(0);
+}
+
+# Purges unused binlogs on the master, up to but not including the latest log.
+# TODO: guess which connections are slaves of a given master.
+sub purge_master_logs {
+ my @cxns = get_connections();
+
+ get_master_slave_status(@cxns);
+
+ # Toss out the rows that don't have master/slave status...
+ my @vars =
+ grep { $_ && ($_->{file} || $_->{master_host}) }
+ map { $vars{$_}->{$clock} } @cxns;
+ @cxns = map { $_->{cxn} } @vars;
+
+ # Figure out which master to purge ons.
+ my @masters = map { $_->{cxn} } grep { $_->{file} } @vars;
+ my ( $master ) = select_cxn('Which master?', @masters );
+ return unless $master;
+ my ($master_status) = grep { $_->{cxn} eq $master } @vars;
+
+ # Figure out the result order (not lexical order) of master logs.
+ my @master_logs = get_master_logs($master);
+ my $i = 0;
+ my %master_logs = map { $_->{log_name} => $i++ } @master_logs;
+
+ # Ask which slave(s) are reading from this master.
+ my @slave_status = grep { $_->{master_host} } @vars;
+ my @slaves = map { $_->{cxn} } @slave_status;
+ @slaves = select_cxn("Which slaves are reading from $master?", @slaves);
+ @slave_status = grep { my $item = $_; grep { $item->{cxn} eq $_ } @slaves } @slave_status;
+ return unless @slave_status;
+
+ # Find the minimum binary log in use.
+ my $min_log = min(map { $master_logs{$_->{master_log_file}} } @slave_status);
+ my $log_name = $master_logs[$min_log]->{log_name};
+
+ my $stmt = "PURGE MASTER LOGS TO '$log_name'";
+ send_cmd_to_servers($stmt, 0, 'PURGE {MASTER | BINARY} LOGS {TO "log_name" | BEFORE "date"}', [$master]);
+}
+
+sub send_cmd_to_servers {
+ my ( $cmd, $all, $hint, $cxns ) = @_;
+ if ( $all ) {
+ @$cxns = get_connections();
+ }
+ elsif ( !@$cxns ) {
+ @$cxns = select_cxn('Which servers?', @$cxns);
+ }
+ if ( $hint ) {
+ print "\nHint: $hint\n";
+ }
+ $cmd = prompt('Command to send', undef, $cmd);
+ foreach my $cxn ( @$cxns ) {
+ eval {
+ my $sth = do_query($cxn, $cmd);
+ };
+ if ( $EVAL_ERROR ) {
+ print "Error from $cxn: $EVAL_ERROR\n";
+ }
+ else {
+ print "Success on $cxn\n";
+ }
+ }
+ pause();
+}
+
+# Display functions {{{2
+
+sub set_s_mode {
+ my ( $func ) = @_;
+ $clear_screen_sub->();
+ $config{S_func}->{val} = $func;
+}
+
+# start_S_mode {{{3
+sub start_S_mode {
+ $clear_screen_sub->();
+ switch_mode('S');
+}
+
+# display_B {{{3
+sub display_B {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_innodb_status(\@cxns);
+
+ my @buffer_pool;
+ my @page_statistics;
+ my @insert_buffers;
+ my @adaptive_hash_index;
+ my %rows_for = (
+ buffer_pool => \@buffer_pool,
+ page_statistics => \@page_statistics,
+ insert_buffers => \@insert_buffers,
+ adaptive_hash_index => \@adaptive_hash_index,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ foreach my $cxn ( @cxns ) {
+ my $set = $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+
+ if ( $set->{IB_bp_complete} ) {
+ if ( $wanted{buffer_pool} ) {
+ push @buffer_pool, extract_values($set, $set, $pre, 'buffer_pool');
+ }
+ if ( $wanted{page_statistics} ) {
+ push @page_statistics, extract_values($set, $set, $pre, 'page_statistics');
+ }
+ }
+ if ( $set->{IB_ib_complete} ) {
+ if ( $wanted{insert_buffers} ) {
+ push @insert_buffers, extract_values(
+ $config{status_inc}->{val} ? inc(0, $cxn) : $set, $set, $pre,
+ 'insert_buffers');
+ }
+ if ( $wanted{adaptive_hash_index} ) {
+ push @adaptive_hash_index, extract_values($set, $set, $pre, 'adaptive_hash_index');
+ }
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_C {{{3
+sub display_C {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_status_info(@cxns);
+
+ my @cmd_summary;
+ my %rows_for = (
+ cmd_summary => \@cmd_summary,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ # For now, I'm manually pulling these variables out and pivoting. Eventually a SQL-ish
+ # dialect should let me join a table to a grouped and pivoted table and do this more easily.
+ # TODO: make it so.
+ my $prefix = qr/^$config{cmd_filter}->{val}/; # TODO: this is a total hack
+ my @values;
+ my ($total, $last_total) = (0, 0);
+ foreach my $cxn ( @cxns ) {
+ my $set = $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+ foreach my $key ( keys %$set ) {
+ next unless $key =~ m/$prefix/i;
+ my $val = $set->{$key};
+ next unless defined $val && $val =~ m/^\d+$/;
+ my $last_val = $val - ($pre->{$key} || 0);
+ $total += $val;
+ $last_total += $last_val;
+ push @values, {
+ name => $key,
+ value => $val,
+ last_value => $last_val,
+ };
+ }
+ }
+
+ # Add aggregation and turn into a real set TODO: total hack
+ if ( $wanted{cmd_summary} ) {
+ foreach my $value ( @values ) {
+ @{$value}{qw(total last_total)} = ($total, $last_total);
+ push @cmd_summary, extract_values($value, $value, $value, 'cmd_summary');
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_D {{{3
+sub display_D {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_innodb_status(\@cxns);
+
+ my @deadlock_transactions;
+ my @deadlock_locks;
+ my %rows_for = (
+ deadlock_transactions => \@deadlock_transactions,
+ deadlock_locks => \@deadlock_locks,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ foreach my $cxn ( @cxns ) {
+ my $innodb_status = $vars{$cxn}->{$clock};
+ my $prev_status = $vars{$cxn}->{$clock-1} || $innodb_status;
+
+ if ( $innodb_status->{IB_dl_timestring} ) {
+
+ my $victim = $innodb_status->{IB_dl_rolled_back} || 0;
+
+ if ( %wanted ) {
+ foreach my $txn_id ( keys %{$innodb_status->{IB_dl_txns}} ) {
+ my $txn = $innodb_status->{IB_dl_txns}->{$txn_id};
+ my $pre = $prev_status->{IB_dl_txns}->{$txn_id} || $txn;
+
+ if ( $wanted{deadlock_transactions} ) {
+ my $hash = extract_values($txn->{tx}, $txn->{tx}, $pre->{tx}, 'deadlock_transactions');
+ $hash->{cxn} = $cxn;
+ $hash->{dl_txn_num} = $txn_id;
+ $hash->{victim} = $txn_id == $victim ? 'Yes' : 'No';
+ $hash->{timestring} = $innodb_status->{IB_dl_timestring};
+ $hash->{truncates} = $innodb_status->{IB_dl_complete} ? 'No' : 'Yes';
+ push @deadlock_transactions, $hash;
+ }
+
+ if ( $wanted{deadlock_locks} ) {
+ foreach my $lock ( @{$txn->{locks}} ) {
+ my $hash = extract_values($lock, $lock, $lock, 'deadlock_locks');
+ $hash->{dl_txn_num} = $txn_id;
+ $hash->{cxn} = $cxn;
+ $hash->{mysql_thread_id} = $txn->{tx}->{mysql_thread_id};
+ push @deadlock_locks, $hash;
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_F {{{3
+sub display_F {
+ my @display_lines;
+ my ( $cxn ) = get_connections();
+ get_innodb_status([$cxn]);
+ my $innodb_status = $vars{$cxn}->{$clock};
+
+ if ( $innodb_status->{IB_fk_timestring} ) {
+
+ push @display_lines, 'Reason: ' . $innodb_status->{IB_fk_reason};
+
+ # Display FK errors caused by invalid DML.
+ if ( $innodb_status->{IB_fk_txn} ) {
+ my $txn = $innodb_status->{IB_fk_txn};
+ push @display_lines,
+ '',
+ "User $txn->{user} from $txn->{hostname}, thread $txn->{mysql_thread_id} was executing:",
+ '', no_ctrl_char($txn->{query_text});
+ }
+
+ my @fk_table = create_table2(
+ $tbl_meta{fk_error}->{visible},
+ meta_to_hdr('fk_error'),
+ extract_values($innodb_status, $innodb_status, $innodb_status, 'fk_error'),
+ { just => '-', sep => ' '});
+ push @display_lines, '', @fk_table;
+
+ }
+ else {
+ push @display_lines, '', 'No foreign key error data.';
+ }
+ draw_screen(\@display_lines, { raw => 1 } );
+}
+
+# display_I {{{3
+sub display_I {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_innodb_status(\@cxns);
+
+ my @io_threads;
+ my @pending_io;
+ my @file_io_misc;
+ my @log_statistics;
+ my %rows_for = (
+ io_threads => \@io_threads,
+ pending_io => \@pending_io,
+ file_io_misc => \@file_io_misc,
+ log_statistics => \@log_statistics,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ foreach my $cxn ( @cxns ) {
+ my $set = $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+
+ if ( $set->{IB_io_complete} ) {
+ if ( $wanted{io_threads} ) {
+ my $cur_threads = $set->{IB_io_threads};
+ my $pre_threads = $pre->{IB_io_threads} || $cur_threads;
+ foreach my $key ( sort keys %$cur_threads ) {
+ my $cur_thd = $cur_threads->{$key};
+ my $pre_thd = $pre_threads->{$key} || $cur_thd;
+ my $hash = extract_values($cur_thd, $cur_thd, $pre_thd, 'io_threads');
+ $hash->{cxn} = $cxn;
+ push @io_threads, $hash;
+ }
+ }
+ if ( $wanted{pending_io} ) {
+ push @pending_io, extract_values($set, $set, $pre, 'pending_io');
+ }
+ if ( $wanted{file_io_misc} ) {
+ push @file_io_misc, extract_values(
+ $config{status_inc}->{val} ? inc(0, $cxn) : $set,
+ $set, $pre, 'file_io_misc');
+ }
+ }
+ if ( $set->{IB_lg_complete} && $wanted{log_statistics} ) {
+ push @log_statistics, extract_values($set, $set, $pre, 'log_statistics');
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_L {{{3
+sub display_L {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_innodb_status(\@cxns);
+
+ my @innodb_locks;
+ my %rows_for = (
+ innodb_locks => \@innodb_locks,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ # Get info on locks
+ foreach my $cxn ( @cxns ) {
+ my $set = $vars{$cxn}->{$clock} or next;
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+
+ if ( $wanted{innodb_locks} && defined $set->{IB_tx_transactions} && @{$set->{IB_tx_transactions}} ) {
+
+ my $cur_txns = $set->{IB_tx_transactions};
+ my $pre_txns = $pre->{IB_tx_transactions} || $cur_txns;
+ my %cur_txns = map { $_->{mysql_thread_id} => $_ } @$cur_txns;
+ my %pre_txns = map { $_->{mysql_thread_id} => $_ } @$pre_txns;
+ foreach my $txn ( @$cur_txns ) {
+ foreach my $lock ( @{$txn->{locks}} ) {
+ my %hash = map { $_ => $txn->{$_} } qw(txn_id mysql_thread_id lock_wait_time active_secs);
+ map { $hash{$_} = $lock->{$_} } qw(lock_type space_id page_no n_bits index db table txn_id lock_mode special insert_intention waiting);
+ $hash{cxn} = $cxn;
+ push @innodb_locks, extract_values(\%hash, \%hash, \%hash, 'innodb_locks');
+ }
+ }
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_M {{{3
+sub display_M {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_master_slave_status(@cxns);
+ get_status_info(@cxns);
+
+ my @slave_sql_status;
+ my @slave_io_status;
+ my @master_status;
+ my %rows_for = (
+ slave_sql_status => \@slave_sql_status,
+ slave_io_status => \@slave_io_status,
+ master_status => \@master_status,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ foreach my $cxn ( @cxns ) {
+ my $set = $config{status_inc}->{val} ? inc(0, $cxn) : $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock - 1} || $set;
+ if ( $wanted{slave_sql_status} ) {
+ push @slave_sql_status, extract_values($set, $set, $pre, 'slave_sql_status');
+ }
+ if ( $wanted{slave_io_status} ) {
+ push @slave_io_status, extract_values($set, $set, $pre, 'slave_io_status');
+ }
+ if ( $wanted{master_status} ) {
+ push @master_status, extract_values($set, $set, $pre, 'master_status');
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_O {{{3
+sub display_O {
+ my @display_lines = ('');
+ my @cxns = get_connections();
+ my @open_tables = get_open_tables(@cxns);
+ my @tables = map { extract_values($_, $_, $_, 'open_tables') } @open_tables;
+ push @display_lines, set_to_tbl(\@tables, 'open_tables'), get_cxn_errors(@cxns);
+ draw_screen(\@display_lines);
+}
+
+# display_Q {{{3
+sub display_Q {
+ my @display_lines;
+
+ my @q_header;
+ my @processlist;
+ my %rows_for = (
+ q_header => \@q_header,
+ processlist => \@processlist,
+ );
+
+ my @visible = $opts{n} ? 'processlist' : get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ # Get the data
+ my @cxns = get_connections();
+ my @full_processlist = get_full_processlist(@cxns);
+
+ # Create header
+ if ( $wanted{q_header} ) {
+ get_status_info(@cxns);
+ foreach my $cxn ( @cxns ) {
+ my $set = $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+ my $hash = extract_values($set, $set, $pre, 'q_header');
+ $hash->{cxn} = $cxn;
+ $hash->{when} = 'Total';
+ push @q_header, $hash;
+
+ if ( exists $vars{$cxn}->{$clock - 1} ) {
+ my $inc = inc(0, $cxn);
+ my $hash = extract_values($inc, $set, $pre, 'q_header');
+ $hash->{cxn} = $cxn;
+ $hash->{when} = 'Now';
+ push @q_header, $hash;
+ }
+ }
+ }
+
+ if ( $wanted{processlist} ) {
+ # TODO: save prev values
+ push @processlist, map { extract_values($_, $_, $_, 'processlist') } @full_processlist;
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ next unless $wanted{$tbl};
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ # Save queries in global variable for analysis. The rows in %rows_for have been
+ # filtered, etc as a side effect of set_to_tbl(), so they are the same as the rows
+ # that get pushed to the screen.
+ @current_queries = map {
+ my %hash;
+ @hash{ qw(cxn id db query secs) } = @{$_}{ qw(cxn mysql_thread_id db info secs) };
+ \%hash;
+ } @{$rows_for{processlist}};
+
+ draw_screen(\@display_lines);
+}
+
+# display_R {{{3
+sub display_R {
+ my @display_lines;
+ my @cxns = get_connections();
+ get_innodb_status(\@cxns);
+
+ my @row_operations;
+ my @row_operation_misc;
+ my @semaphores;
+ my @wait_array;
+ my %rows_for = (
+ row_operations => \@row_operations,
+ row_operation_misc => \@row_operation_misc,
+ semaphores => \@semaphores,
+ wait_array => \@wait_array,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+ my $incvar = $config{status_inc}->{val};
+
+ foreach my $cxn ( @cxns ) {
+ my $set = $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+ my $inc; # Only assigned to if wanted
+
+ if ( $set->{IB_ro_complete} ) {
+ if ( $wanted{row_operations} ) {
+ $inc ||= $incvar ? inc(0, $cxn) : $set;
+ push @row_operations, extract_values($inc, $set, $pre, 'row_operations');
+ }
+ if ( $wanted{row_operation_misc} ) {
+ push @row_operation_misc, extract_values($set, $set, $pre, 'row_operation_misc'),
+ }
+ }
+
+ if ( $set->{IB_sm_complete} && $wanted{semaphores} ) {
+ $inc ||= $incvar ? inc(0, $cxn) : $set;
+ push @semaphores, extract_values($inc, $set, $pre, 'semaphores');
+ }
+
+ if ( $set->{IB_sm_wait_array_size} && $wanted{wait_array} ) {
+ foreach my $wait ( @{$set->{IB_sm_waits}} ) {
+ my $hash = extract_values($wait, $wait, $wait, 'wait_array');
+ $hash->{cxn} = $cxn;
+ push @wait_array, $hash;
+ }
+ }
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ draw_screen(\@display_lines);
+}
+
+# display_T {{{3
+sub display_T {
+ my @display_lines;
+
+ my @t_header;
+ my @innodb_transactions;
+ my %rows_for = (
+ t_header => \@t_header,
+ innodb_transactions => \@innodb_transactions,
+ );
+
+ my @visible = $opts{n} ? 'innodb_transactions' : get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+
+ my @cxns = get_connections();
+
+ # If the header is to be shown, buffer pool data is required.
+ get_innodb_status( \@cxns, [ $wanted{t_header} ? qw(bp) : () ] );
+
+ foreach my $cxn ( get_connections() ) {
+ my $set = $vars{$cxn}->{$clock};
+ my $pre = $vars{$cxn}->{$clock-1} || $set;
+
+ next unless $set->{IB_tx_transactions};
+
+ if ( $wanted{t_header} ) {
+ my $hash = extract_values($set, $set, $pre, 't_header');
+ push @t_header, $hash;
+ }
+
+ if ( $wanted{innodb_transactions} ) {
+ my $cur_txns = $set->{IB_tx_transactions};
+ my $pre_txns = $pre->{IB_tx_transactions} || $cur_txns;
+ my %cur_txns = map { $_->{mysql_thread_id} => $_ } @$cur_txns;
+ my %pre_txns = map { $_->{mysql_thread_id} => $_ } @$pre_txns;
+ foreach my $thd_id ( sort keys %cur_txns ) {
+ my $cur_txn = $cur_txns{$thd_id};
+ my $pre_txn = $pre_txns{$thd_id} || $cur_txn;
+ my $hash = extract_values($cur_txn, $cur_txn, $pre_txn, 'innodb_transactions');
+ $hash->{cxn} = $cxn;
+ push @innodb_transactions, $hash;
+ }
+ }
+
+ }
+
+ my $first_table = 0;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+
+ # Save queries in global variable for analysis. The rows in %rows_for have been
+ # filtered, etc as a side effect of set_to_tbl(), so they are the same as the rows
+ # that get pushed to the screen.
+ @current_queries = map {
+ my %hash;
+ @hash{ qw(cxn id db query secs) } = @{$_}{ qw(cxn mysql_thread_id db query_text active_secs) };
+ \%hash;
+ } @{$rows_for{innodb_transactions}};
+
+ draw_screen(\@display_lines);
+}
+
+# display_S {{{3
+sub display_S {
+ my $fmt = get_var_set('S_set');
+ my $func = $config{S_func}->{val};
+ my $inc = $func eq 'g' || $config{status_inc}->{val};
+
+ # The table's meta-data is generated from the compiled var_set.
+ my ( $cols, $visible );
+ if ( $tbl_meta{var_status}->{fmt} && $fmt eq $tbl_meta{var_status}->{fmt} ) {
+ ( $cols, $visible ) = @{$tbl_meta{var_status}}{qw(cols visible)};
+ }
+ else {
+ ( $cols, $visible ) = compile_select_stmt($fmt);
+
+ # Apply missing values to columns. Always apply averages across all connections.
+ map {
+ $_->{agg} = 'avg';
+ $_->{label} = $_->{hdr};
+ } values %$cols;
+
+ $tbl_meta{var_status}->{cols} = $cols;
+ $tbl_meta{var_status}->{visible} = $visible;
+ $tbl_meta{var_status}->{fmt} = $fmt;
+ map { $tbl_meta{var_status}->{cols}->{$_}->{just} = ''} @$visible;
+ }
+
+ my @var_status;
+ my %rows_for = (
+ var_status => \@var_status,
+ );
+
+ my @visible = get_visible_tables();
+ my %wanted = map { $_ => 1 } @visible;
+ my @cxns = get_connections();
+
+ get_status_info(@cxns);
+ get_innodb_status(\@cxns);
+
+ # Set up whether to pivot and how many sets to extract.
+ $tbl_meta{var_status}->{pivot} = $func eq 'v';
+
+ my $num_sets
+ = $func eq 'v'
+ ? $config{num_status_sets}->{val}
+ : 0;
+ foreach my $set ( 0 .. $num_sets ) {
+ my @rows;
+ foreach my $cxn ( @cxns ) {
+ my $vars = $inc ? inc($set, $cxn) : $vars{$cxn}->{$clock - $set};
+ my $cur = $vars{$cxn}->{$clock-$set};
+ my $pre = $vars{$cxn}->{$clock-$set-1} || $cur;
+ next unless $vars && %$vars;
+ my $hash = extract_values($vars, $cur, $pre, 'var_status');
+ push @rows, $hash;
+ }
+ @rows = apply_group_by('var_status', [], @rows);
+ push @var_status, @rows;
+ }
+
+ # Recompile the sort func. TODO: avoid recompiling at every refresh.
+ # Figure out whether the data is all numeric and decide on a sort type.
+ # my $cmp
+ # = scalar(
+ # grep { !defined $_ || $_ !~ m/^\d+$/ }
+ # map { my $col = $_; map { $_->{$col} } @var_status }
+ # $tbl_meta{var_status}->{sort_cols} =~ m/(\w+)/g)
+ # ? 'cmp'
+ # : '<=>';
+ $tbl_meta{var_status}->{sort_func} = make_sort_func($tbl_meta{var_status});
+
+ # ################################################################
+ # Now there is specific display code based on $config{S_func}
+ # ################################################################
+ if ( $func =~ m/s|g/ ) {
+ my $min_width = 4;
+
+ # Clear the screen if the display width changed.
+ if ( @last_term_size && $this_term_size[0] != $last_term_size[0] ) {
+ $lines_printed = 0;
+ $clear_screen_sub->();
+ }
+
+ if ( $func eq 's' ) {
+ # Decide how wide columns should be.
+ my $num_cols = scalar(@$visible);
+ my $width = $opts{n} ? 0 : max($min_width, int(($this_term_size[0] - $num_cols + 1) / $num_cols));
+ my $g_format = $opts{n} ? ( "%s\t" x $num_cols ) : ( "%-${width}s " x $num_cols );
+
+ # Print headers every now and then. Headers can get really long, so compact them.
+ my @hdr = @$visible;
+ if ( $opts{n} ) {
+ if ( $lines_printed == 0 ) {
+ print join("\t", @hdr), "\n";
+ $lines_printed++;
+ }
+ }
+ elsif ( $lines_printed == 0 || $lines_printed > $this_term_size[1] - 2 ) {
+ @hdr = map { donut(crunch($_, $width), $width) } @hdr;
+ print join(' ', map { sprintf( "%${width}s", donut($_, $width)) } @hdr) . "\n";
+ $lines_printed = 1;
+ }
+
+ # Design a column format for the values.
+ my $format
+ = $opts{n}
+ ? join("\t", map { '%s' } @$visible) . "\n"
+ : join(' ', map { "%${width}s" } @hdr) . "\n";
+
+ foreach my $row ( @var_status ) {
+ printf($format, map { defined $_ ? $_ : '' } @{$row}{ @$visible });
+ $lines_printed++;
+ }
+ }
+ else { # 'g' mode
+ # Design a column format for the values.
+ my $num_cols = scalar(@$visible);
+ my $width = $opts{n} ? 0 : int(($this_term_size[0] - $num_cols + 1) / $num_cols);
+ my $format = $opts{n} ? ( "%s\t" x $num_cols ) : ( "%-${width}s " x $num_cols );
+ $format =~ s/\s$/\n/;
+
+ # Print headers every now and then.
+ if ( $opts{n} ) {
+ if ( $lines_printed == 0 ) {
+ print join("\t", @$visible), "\n";
+ print join("\t", map { shorten($mvs{$_}) } @$visible), "\n";
+ }
+ }
+ elsif ( $lines_printed == 0 || $lines_printed > $this_term_size[1] - 2 ) {
+ printf($format, map { donut(crunch($_, $width), $width) } @$visible);
+ printf($format, map { shorten($mvs{$_} || 0) } @$visible);
+ $lines_printed = 2;
+ }
+
+ # Update the max ever seen, and scale by the max ever seen.
+ my $set = $var_status[0];
+ foreach my $col ( @$visible ) {
+ $set->{$col} = 1 unless defined $set->{$col} && $set->{$col} =~ m/$num_regex/;
+ $set->{$col} = ($set->{$col} || 1) / ($set->{Uptime_hires} || 1);
+ $mvs{$col} = max($mvs{$col} || 1, $set->{$col});
+ $set->{$col} /= $mvs{$col};
+ }
+ printf($format, map { ( $config{graph_char}->{val} x int( $width * $set->{$_} )) || '.' } @$visible );
+ $lines_printed++;
+
+ }
+ }
+ else { # 'v'
+ my $first_table = 0;
+ my @display_lines;
+ foreach my $tbl ( @visible ) {
+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
+ push @display_lines, get_cxn_errors(@cxns)
+ if ( $config{debug}->{val} || !$first_table++ );
+ }
+ $clear_screen_sub->();
+ draw_screen( \@display_lines );
+ }
+}
+
+# display_explain {{{3
+sub display_explain {
+ my $info = shift;
+ my $cxn = $info->{cxn};
+ my $db = $info->{db};
+
+ my ( $mods, $query ) = rewrite_for_explain($info->{query});
+
+ my @display_lines;
+
+ if ( $query ) {
+
+ my $part = version_ge($dbhs{$cxn}->{dbh}, '5.1.5') ? 'PARTITIONS' : '';
+ $query = "EXPLAIN $part\n" . $query;
+
+ eval {
+ if ( $db ) {
+ do_query($cxn, "use $db");
+ }
+ my $sth = do_query($cxn, $query);
+
+ my $res;
+ while ( $res = $sth->fetchrow_hashref() ) {
+ map { $res->{$_} ||= '' } ( 'partitions', keys %$res);
+ my @this_table = create_caption("Sub-Part $res->{id}",
+ create_table2(
+ $tbl_meta{explain}->{visible},
+ meta_to_hdr('explain'),
+ extract_values($res, $res, $res, 'explain')));
+ @display_lines = stack_next(\@display_lines, \@this_table, { pad => ' ', vsep => 2 });
+ }
+ };
+
+ if ( $EVAL_ERROR ) {
+ push @display_lines,
+ '',
+ "The query could not be explained. Only SELECT queries can be "
+ . "explained; innotop tries to rewrite certain REPLACE and INSERT queries "
+ . "into SELECT, but this doesn't always succeed.";
+ }
+
+ }
+ else {
+ push @display_lines, '', 'The query could not be explained.';
+ }
+
+ if ( $mods ) {
+ push @display_lines, '', '[This query has been re-written to be explainable]';
+ }
+
+ unshift @display_lines, no_ctrl_char($query);
+ draw_screen(\@display_lines, { raw => 1 } );
+}
+
+# rewrite_for_explain {{{3
+sub rewrite_for_explain {
+ my $query = shift;
+
+ my $mods = 0;
+ my $orig = $query;
+ $mods += $query =~ s/^\s*(?:replace|insert).*?select/select/is;
+ $mods += $query =~ s/^
+ \s*create\s+(?:temporary\s+)?table
+ \s+(?:\S+\s+)as\s+select/select/xis;
+ $mods += $query =~ s/\s+on\s+duplicate\s+key\s+update.*$//is;
+ return ( $mods, $query );
+}
+
+# show_optimized_query {{{3
+sub show_optimized_query {
+ my $info = shift;
+ my $cxn = $info->{cxn};
+ my $db = $info->{db};
+ my $meta = $dbhs{$cxn};
+
+ my @display_lines;
+
+ my ( $mods, $query ) = rewrite_for_explain($info->{query});
+
+ if ( $mods ) {
+ push @display_lines, '[This query has been re-written to be explainable]';
+ }
+
+ if ( $query ) {
+ push @display_lines, no_ctrl_char($info->{query});
+
+ eval {
+ if ( $db ) {
+ do_query($cxn, "use $db");
+ }
+ do_query( $cxn, 'EXPLAIN EXTENDED ' . $query ) or die "Can't explain query";
+ my $sth = do_query($cxn, 'SHOW WARNINGS');
+ my $res = $sth->fetchall_arrayref({});
+
+ if ( $res ) {
+ foreach my $result ( @$res ) {
+ push @display_lines, 'Note:', no_ctrl_char($result->{message});
+ }
+ }
+ else {
+ push @display_lines, '', 'The query optimization could not be generated.';
+ }
+ };
+
+ if ( $EVAL_ERROR ) {
+ push @display_lines, '', "The optimization could not be generated: $EVAL_ERROR";
+ }
+
+ }
+ else {
+ push @display_lines, '', 'The query optimization could not be generated.';
+ }
+
+ draw_screen(\@display_lines, { raw => 1 } );
+}
+
+# display_help {{{3
+sub display_help {
+ my $mode = $config{mode}->{val};
+
+ # Get globally mapped keys, then overwrite them with mode-specific ones.
+ my %keys = map {
+ $_ => $action_for{$_}->{label}
+ } keys %action_for;
+ foreach my $key ( keys %{$modes{$mode}->{action_for}} ) {
+ $keys{$key} = $modes{$mode}->{action_for}->{$key}->{label};
+ }
+ delete $keys{'?'};
+
+ # Split them into three kinds of keys: MODE keys, action keys, and
+ # magic (special character) keys.
+ my @modes = sort grep { m/[A-Z]/ } keys %keys;
+ my @actions = sort grep { m/[a-z]/ } keys %keys;
+ my @magic = sort grep { m/[^A-Z]/i } keys %keys;
+
+ my @display_lines = ( '', 'Switch to a different mode:' );
+
+ # Mode keys
+ my @all_modes = map { "$_ $modes{$_}->{hdr}" } @modes;
+ my @col1 = splice(@all_modes, 0, ceil(@all_modes/3));
+ my @col2 = splice(@all_modes, 0, ceil(@all_modes/2));
+ my $max1 = max(map {length($_)} @col1);
+ my $max2 = max(map {length($_)} @col2);
+ while ( @col1 ) {
+ push @display_lines, sprintf(" %-${max1}s %-${max2}s %s",
+ (shift @col1 || ''),
+ (shift @col2 || ''),
+ (shift @all_modes || ''));
+ }
+
+ # Action keys
+ my @all_actions = map { "$_ $keys{$_}" } @actions;
+ @col1 = splice(@all_actions, 0, ceil(@all_actions/2));
+ $max1 = max(map {length($_)} @col1);
+ push @display_lines, '', 'Actions:';
+ while ( @col1 ) {
+ push @display_lines, sprintf(" %-${max1}s %s",
+ (shift @col1 || ''),
+ (shift @all_actions || ''));
+ }
+
+ # Magic keys
+ my @all_magic = map { sprintf('%4s', $action_for{$_}->{key} || $_) . " $keys{$_}" } @magic;
+ @col1 = splice(@all_magic, 0, ceil(@all_magic/2));
+ $max1 = max(map {length($_)} @col1);
+ push @display_lines, '', 'Other:';
+ while ( @col1 ) {
+ push @display_lines, sprintf("%-${max1}s%s",
+ (shift @col1 || ''),
+ (shift @all_magic || ''));
+ }
+
+ $clear_screen_sub->();
+ draw_screen(\@display_lines, { show_all => 1 } );
+ pause();
+ $clear_screen_sub->();
+}
+
+# show_full_query {{{3
+sub show_full_query {
+ my $info = shift;
+ my @display_lines = no_ctrl_char($info->{query});
+ draw_screen(\@display_lines, { raw => 1 });
+}
+
+# Formatting functions {{{2
+
+# create_table2 {{{3
+# Makes a two-column table, labels on left, data on right.
+# Takes refs of @cols, %labels and %data, %user_prefs
+sub create_table2 {
+ my ( $cols, $labels, $data, $user_prefs ) = @_;
+ my @rows;
+
+ if ( @$cols && %$data ) {
+
+ # Override defaults
+ my $p = {
+ just => '',
+ sep => ':',
+ just1 => '-',
+ };
+ if ( $user_prefs ) {
+ map { $p->{$_} = $user_prefs->{$_} } keys %$user_prefs;
+ }
+
+ # Fix undef values
+ map { $data->{$_} = '' unless defined $data->{$_} } @$cols;
+
+ # Format the table
+ my $max_l = max(map{ length($labels->{$_}) } @$cols);
+ my $max_v = max(map{ length($data->{$_}) } @$cols);
+ my $format = "%$p->{just}${max_l}s$p->{sep} %$p->{just1}${max_v}s";
+ foreach my $col ( @$cols ) {
+ push @rows, sprintf($format, $labels->{$col}, $data->{$col});
+ }
+ }
+ return @rows;
+}
+
+# stack_next {{{3
+# Stacks one display section next to the other. Accepts left-hand arrayref,
+# right-hand arrayref, and options hashref. Tries to stack as high as
+# possible, so
+# aaaaaa
+# bbb
+# can stack ccc next to the bbb.
+# NOTE: this DOES modify its arguments, even though it returns a new array.
+sub stack_next {
+ my ( $left, $right, $user_prefs ) = @_;
+ my @result;
+
+ my $p = {
+ pad => ' ',
+ vsep => 0,
+ };
+ if ( $user_prefs ) {
+ map { $p->{$_} = $user_prefs->{$_} } keys %$user_prefs;
+ }
+
+ # Find out how wide the LHS can be and still let the RHS fit next to it.
+ my $pad = $p->{pad};
+ my $max_r = max( map { length($_) } @$right) || 0;
+ my $max_l = $this_term_size[0] - $max_r - length($pad);
+
+ # Find the minimum row on the LHS that the RHS will fit next to.
+ my $i = scalar(@$left) - 1;
+ while ( $i >= 0 && length($left->[$i]) <= $max_l ) {
+ $i--;
+ }
+ $i++;
+ my $offset = $i;
+
+ if ( $i < scalar(@$left) ) {
+ # Find the max width of the section of the LHS against which the RHS
+ # will sit.
+ my $max_i_in_common = min($i + scalar(@$right) - 1, scalar(@$left) - 1);
+ my $max_width = max( map { length($_) } @{$left}[$i..$max_i_in_common]);
+
+ # Append the RHS onto the LHS until one runs out.
+ while ( $i < @$left && $i - $offset < @$right ) {
+ my $format = "%-${max_width}s$pad%${max_r}s";
+ $left->[$i] = sprintf($format, $left->[$i], $right->[$i - $offset]);
+ $i++;
+ }
+ while ( $i - $offset < @$right ) {
+ # There is more RHS to push on the end of the array
+ push @$left,
+ sprintf("%${max_width}s$pad%${max_r}s", ' ', $right->[$i - $offset]);
+ $i++;
+ }
+ push @result, @$left;
+ }
+ else {
+ # There is no room to put them side by side. Add them below, with
+ # a blank line above them if specified.
+ push @result, @$left;
+ push @result, (' ' x $this_term_size[0]) if $p->{vsep} && @$left;
+ push @result, @$right;
+ }
+ return @result;
+}
+
+# create_caption {{{3
+sub create_caption {
+ my ( $caption, @rows ) = @_;
+ if ( @rows ) {
+
+ # Calculate the width of what will be displayed, so it can be centered
+ # in that space. When the thing is wider than the display, center the
+ # caption in the display.
+ my $width = min($this_term_size[0], max(map { length(ref($_) ? $_->[0] : $_) } @rows));
+
+ my $cap_len = length($caption);
+
+ # It may be narrow enough to pad the sides with underscores and save a
+ # line on the screen.
+ if ( $cap_len <= $width - 6 ) {
+ my $left = int(($width - 2 - $cap_len) / 2);
+ unshift @rows,
+ ("_" x $left) . " $caption " . ("_" x ($width - $left - $cap_len - 2));
+ }
+
+ # The caption is too wide to add underscores on each side.
+ else {
+
+ # Color is supported, so we can use terminal underlining.
+ if ( $config{color}->{val} ) {
+ my $left = int(($width - $cap_len) / 2);
+ unshift @rows, [
+ (" " x $left) . $caption . (" " x ($width - $left - $cap_len)),
+ 'underline',
+ ];
+ }
+
+ # Color is not supported, so we have to add a line underneath to separate the
+ # caption from whatever it's captioning.
+ else {
+ my $left = int(($width - $cap_len) / 2);
+ unshift @rows, ('-' x $width);
+ unshift @rows, (" " x $left) . $caption . (" " x ($width - $left - $cap_len));
+ }
+
+ # The caption is wider than the thing it labels, so we have to pad the
+ # thing it labels to a consistent width.
+ if ( $cap_len > $width ) {
+ @rows = map {
+ ref($_)
+ ? [ sprintf('%-' . $cap_len . 's', $_->[0]), $_->[1] ]
+ : sprintf('%-' . $cap_len . 's', $_);
+ } @rows;
+ }
+
+ }
+ }
+ return @rows;
+}
+
+# create_table {{{3
+# Input: an arrayref of columns, hashref of col info, and an arrayref of hashes
+# Example: [ 'a', 'b' ]
+# { a => spec, b => spec }
+# [ { a => 1, b => 2}, { a => 3, b => 4 } ]
+# The 'spec' is a hashref of hdr => label, just => ('-' or ''). It also supports min and max-widths
+# vi the minw and maxw params.
+# Output: an array of strings, one per row.
+# Example:
+# Column One Column Two
+# ---------- ----------
+# 1 2
+# 3 4
+sub create_table {
+ my ( $cols, $info, $data, $prefs ) = @_;
+ $prefs ||= {};
+ $prefs->{no_hdr} ||= ($opts{n} && $clock != 1);
+
+ # Truncate rows that will surely be off screen even if this is the only table.
+ if ( !$opts{n} && !$prefs->{raw} && !$prefs->{show_all} && $this_term_size[1] < @$data-1 ) {
+ $data = [ @$data[0..$this_term_size[1] - 1] ];
+ }
+
+ my @rows = ();
+
+ if ( @$cols && %$info ) {
+
+ # Fix undef values, collapse whitespace.
+ foreach my $row ( @$data ) {
+ map { $row->{$_} = collapse_ws($row->{$_}) } @$cols;
+ }
+
+ my $col_sep = $opts{n} ? "\t" : ' ';
+
+ # Find each column's max width.
+ my %width_for;
+ if ( !$opts{n} ) {
+ %width_for = map {
+ my $col_name = $_;
+ if ( $info->{$_}->{dec} ) {
+ # Align along the decimal point
+ my $max_rodp = max(0, map { $_->{$col_name} =~ m/([^\s\d-].*)$/ ? length($1) : 0 } @$data);
+ foreach my $row ( @$data ) {
+ my $col = $row->{$col_name};
+ my ( $l, $r ) = $col =~ m/^([\s\d]*)(.*)$/;
+ $row->{$col_name} = sprintf("%s%-${max_rodp}s", $l, $r);
+ }
+ }
+ my $max_width = max( length($info->{$_}->{hdr}), map { length($_->{$col_name}) } @$data);
+ if ( $info->{$col_name}->{maxw} ) {
+ $max_width = min( $max_width, $info->{$col_name}->{maxw} );
+ }
+ if ( $info->{$col_name}->{minw} ) {
+ $max_width = max( $max_width, $info->{$col_name}->{minw} );
+ }
+ $col_name => $max_width;
+ } @$cols;
+ }
+
+ # The table header.
+ if ( !$config{hide_hdr}->{val} && !$prefs->{no_hdr} ) {
+ push @rows, $opts{n}
+ ? join( $col_sep, @$cols )
+ : join( $col_sep, map { sprintf( "%-$width_for{$_}s", trunc($info->{$_}->{hdr}, $width_for{$_}) ) } @$cols );
+ if ( $config{color}->{val} && $config{header_highlight}->{val} ) {
+ push @rows, [ pop @rows, $config{header_highlight}->{val} ];
+ }
+ elsif ( !$opts{n} ) {
+ push @rows, join( $col_sep, map { "-" x $width_for{$_} } @$cols );
+ }
+ }
+
+ # The table data.
+ if ( $opts{n} ) {
+ foreach my $item ( @$data ) {
+ push @rows, join($col_sep, map { $item->{$_} } @$cols );
+ }
+ }
+ else {
+ my $format = join( $col_sep,
+ map { "%$info->{$_}->{just}$width_for{$_}s" } @$cols );
+ foreach my $item ( @$data ) {
+ my $row = sprintf($format, map { trunc($item->{$_}, $width_for{$_}) } @$cols );
+ if ( $config{color}->{val} && $item->{_color} ) {
+ push @rows, [ $row, $item->{_color} ];
+ }
+ else {
+ push @rows, $row;
+ }
+ }
+ }
+ }
+
+ return @rows;
+}
+
+# Aggregates a table. If $group_by is an arrayref of columns, the grouping key
+# is the specified columns; otherwise it's just the empty string (e.g.
+# everything is grouped as one group).
+sub apply_group_by {
+ my ( $tbl, $group_by, @rows ) = @_;
+ my $meta = $tbl_meta{$tbl};
+ my %is_group = map { $_ => 1 } @$group_by;
+ my @non_grp = grep { !$is_group{$_} } keys %{$meta->{cols}};
+
+ my %temp_table;
+ foreach my $row ( @rows ) {
+ my $group_key
+ = @$group_by
+ ? '{' . join('}{', map { defined $_ ? $_ : '' } @{$row}{@$group_by}) . '}'
+ : '';
+ $temp_table{$group_key} ||= [];
+ push @{$temp_table{$group_key}}, $row;
+ }
+
+ # Crush the rows together...
+ my @new_rows;
+ foreach my $key ( sort keys %temp_table ) {
+ my $group = $temp_table{$key};
+ my %new_row;
+ @new_row{@$group_by} = @{$group->[0]}{@$group_by};
+ foreach my $col ( @non_grp ) {
+ my $agg = $meta->{cols}->{$col}->{agg} || 'first';
+ $new_row{$col} = $agg_funcs{$agg}->( map { $_->{$col} } @$group );
+ }
+ push @new_rows, \%new_row;
+ }
+ return @new_rows;
+}
+
+# set_to_tbl {{{3
+# Unifies all the work of filtering, sorting etc. Alters the input.
+# TODO: pull all the little pieces out into subroutines and stick events in each of them.
+sub set_to_tbl {
+ my ( $rows, $tbl ) = @_;
+ my $meta = $tbl_meta{$tbl} or die "No such table $tbl in tbl_meta";
+
+ # don't show cxn if there's only one connection being displayed
+ my @visible;
+ if (scalar @{$modes{$config{mode}->{val}}->{connections}} == 1) {
+ map { push @visible, $_ if $_ !~ /^cxn$/ } @{$meta->{visible}};
+ delete $$rows[0]{cxn} if defined $$rows[0]{cxn};
+ }
+ else {
+ @visible = @{$meta->{visible}};
+ }
+
+ if ( !$meta->{pivot} ) {
+
+ # Hook in event listeners
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_filter}} ) {
+ $listener->set_to_tbl_pre_filter($rows, $tbl);
+ }
+
+ # Apply filters. Note that if the table is pivoted, filtering and sorting
+ # are applied later.
+ foreach my $filter ( @{$meta->{filters}} ) {
+ eval {
+ @$rows = grep { $filters{$filter}->{func}->($_) } @$rows;
+ };
+ if ( $EVAL_ERROR && $config{debug}->{val} ) {
+ die $EVAL_ERROR;
+ }
+ }
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_sort}} ) {
+ $listener->set_to_tbl_pre_sort($rows, $tbl);
+ }
+
+ # Sort. Note that if the table is pivoted, sorting might have the wrong
+ # columns and it could crash. This will only be an issue if it's possible
+ # to toggle pivoting on and off, which it's not at the moment.
+ if ( @$rows && $meta->{sort_func} && !$meta->{aggregate} ) {
+ if ( $meta->{sort_dir} > 0 ) {
+ @$rows = $meta->{sort_func}->( @$rows );
+ }
+ else {
+ @$rows = reverse $meta->{sort_func}->( @$rows );
+ }
+ }
+
+ }
+
+ # Stop altering arguments now.
+ my @rows = @$rows;
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_group}} ) {
+ $listener->set_to_tbl_pre_group(\@rows, $tbl);
+ }
+
+ # Apply group-by.
+ if ( $meta->{aggregate} ) {
+ @rows = apply_group_by($tbl, $meta->{group_by}, @rows);
+
+ # Sort. Note that if the table is pivoted, sorting might have the wrong
+ # columns and it could crash. This will only be an issue if it's possible
+ # to toggle pivoting on and off, which it's not at the moment.
+ if ( @rows && $meta->{sort_func} ) {
+ if ( $meta->{sort_dir} > 0 ) {
+ @rows = $meta->{sort_func}->( @rows );
+ }
+ else {
+ @rows = reverse $meta->{sort_func}->( @rows );
+ }
+ }
+
+ }
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_colorize}} ) {
+ $listener->set_to_tbl_pre_colorize(\@rows, $tbl);
+ }
+
+ if ( !$meta->{pivot} ) {
+ # Colorize. Adds a _color column to rows.
+ if ( @rows && $meta->{color_func} ) {
+ eval {
+ foreach my $row ( @rows ) {
+ $row->{_color} = $meta->{color_func}->($row);
+ }
+ };
+ if ( $EVAL_ERROR ) {
+ pause($EVAL_ERROR);
+ }
+ }
+ }
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_transform}} ) {
+ $listener->set_to_tbl_pre_transform(\@rows, $tbl);
+ }
+
+ # Apply_transformations.
+ if ( @rows ) {
+ my $cols = $meta->{cols};
+ foreach my $col ( keys %{$rows->[0]} ) {
+ # Don't auto-vivify $tbl_meta{tbl}-{cols}->{_color}->{trans}
+ next if $col eq '_color';
+ foreach my $trans ( @{$cols->{$col}->{trans}} ) {
+ map { $_->{$col} = $trans_funcs{$trans}->($_->{$col}) } @rows;
+ }
+ }
+ }
+
+ my ($fmt_cols, $fmt_meta);
+
+ # Pivot.
+ if ( $meta->{pivot} ) {
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_pivot}} ) {
+ $listener->set_to_tbl_pre_pivot(\@rows, $tbl);
+ }
+
+ my @vars = @{$meta->{visible}};
+ my @tmp = map { { name => $_ } } @vars;
+ my @cols = 'name';
+ foreach my $i ( 0..@$rows-1 ) {
+ my $col = "set_$i";
+ push @cols, $col;
+ foreach my $j ( 0..@vars-1 ) {
+ $tmp[$j]->{$col} = $rows[$i]->{$vars[$j]};
+ }
+ }
+ $fmt_meta = { map { $_ => { hdr => $_, just => '-' } } @cols };
+ $fmt_cols = \@cols;
+ @rows = @tmp;
+
+ # Hook in event listeners
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_filter}} ) {
+ $listener->set_to_tbl_pre_filter($rows, $tbl);
+ }
+
+ # Apply filters.
+ foreach my $filter ( @{$meta->{filters}} ) {
+ eval {
+ @rows = grep { $filters{$filter}->{func}->($_) } @rows;
+ };
+ if ( $EVAL_ERROR && $config{debug}->{val} ) {
+ die $EVAL_ERROR;
+ }
+ }
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_sort}} ) {
+ $listener->set_to_tbl_pre_sort($rows, $tbl);
+ }
+
+ # Sort.
+ if ( @rows && $meta->{sort_func} ) {
+ if ( $meta->{sort_dir} > 0 ) {
+ @rows = $meta->{sort_func}->( @rows );
+ }
+ else {
+ @rows = reverse $meta->{sort_func}->( @rows );
+ }
+ }
+
+ }
+ else {
+ # If the table isn't pivoted, just show all columns that are supposed to
+ # be shown; but eliminate aggonly columns if the table isn't aggregated.
+ my $aggregated = $meta->{aggregate};
+ $fmt_cols = [ grep { $aggregated || !$meta->{cols}->{$_}->{aggonly} } @visible ];
+ $fmt_meta = { map { $_ => $meta->{cols}->{$_} } @$fmt_cols };
+
+ # If the table is aggregated, re-order the group_by columns to the left of
+ # the display.
+ if ( $aggregated ) {
+ my %is_group = map { $_ => 1 } @{$meta->{group_by}};
+ $fmt_cols = [ @{$meta->{group_by}}, grep { !$is_group{$_} } @$fmt_cols ];
+ }
+ }
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_pre_create}} ) {
+ $listener->set_to_tbl_pre_create(\@rows, $tbl);
+ }
+
+ @rows = create_table( $fmt_cols, $fmt_meta, \@rows);
+ if ( !$meta->{hide_caption} && !$opts{n} && $config{display_table_captions}->{val} ) {
+ @rows = create_caption($meta->{capt}, @rows)
+ }
+
+ foreach my $listener ( @{$event_listener_for{set_to_tbl_post_create}} ) {
+ $listener->set_to_tbl_post_create(\@rows, $tbl);
+ }
+
+ return @rows;
+}
+
+# meta_to_hdr {{{3
+sub meta_to_hdr {
+ my $tbl = shift;
+ my $meta = $tbl_meta{$tbl};
+ my %labels = map { $_ => $meta->{cols}->{$_}->{hdr} } @{$meta->{visible}};
+ return \%labels;
+}
+
+# commify {{{3
+# From perlfaq5: add commas.
+sub commify {
+ my ( $num ) = @_;
+ $num = 0 unless defined $num;
+ $num =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g;
+ return $num;
+}
+
+# set_precision {{{3
+# Trim to desired precision.
+sub set_precision {
+ my ( $num, $precision ) = @_;
+ $precision = $config{num_digits}->{val} if !defined $precision;
+ sprintf("%.${precision}f", $num);
+}
+
+# percent {{{3
+# Convert to percent
+sub percent {
+ my ( $num ) = @_;
+ $num = 0 unless defined $num;
+ my $digits = $config{num_digits}->{val};
+ return sprintf("%.${digits}f", $num * 100)
+ . ($config{show_percent}->{val} ? '%' : '');
+}
+
+# shorten {{{3
+sub shorten {
+ my ( $num, $opts ) = @_;
+
+ return $num if !defined($num) || $opts{n} || $num !~ m/$num_regex/;
+
+ $opts ||= {};
+ my $pad = defined $opts->{pad} ? $opts->{pad} : '';
+ my $num_digits = defined $opts->{num_digits}
+ ? $opts->{num_digits}
+ : $config{num_digits}->{val};
+ my $force = defined $opts->{force};
+
+ my $n = 0;
+ while ( $num >= 1_024 ) {
+ $num /= 1_024;
+ ++$n;
+ }
+ return sprintf(
+ $num =~ m/\./ || $n || $force
+ ? "%.${num_digits}f%s"
+ : '%d',
+ $num, ($pad,'k','M','G', 'T')[$n]);
+
+}
+
+# Utility functions {{{2
+# unique {{{3
+sub unique {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+# make_color_func {{{3
+sub make_color_func {
+ my ( $tbl ) = @_;
+ my @criteria;
+ foreach my $spec ( @{$tbl->{colors}} ) {
+ next unless exists $comp_ops{$spec->{op}};
+ my $val = $spec->{op} =~ m/^(?:eq|ne|le|ge|lt|gt)$/ ? "'$spec->{arg}'"
+ : $spec->{op} =~ m/^(?:=~|!~)$/ ? "m/" . quotemeta($spec->{arg}) . "/"
+ : $spec->{arg};
+ push @criteria,
+ "( defined \$set->{$spec->{col}} && \$set->{$spec->{col}} $spec->{op} $val ) { return '$spec->{color}'; }";
+ }
+ return undef unless @criteria;
+ my $sub = eval 'sub { my ( $set ) = @_; if ' . join(" elsif ", @criteria) . '}';
+ die if $EVAL_ERROR;
+ return $sub;
+}
+
+# make_sort_func {{{3
+# Gets a list of sort columns from the table, like "+cxn -time" and returns a
+# subroutine that will sort that way.
+sub make_sort_func {
+ my ( $tbl ) = @_;
+ my @criteria;
+
+ # Pivoted tables can be sorted by 'name' and set_x columns; others must be
+ # sorted by existing columns. TODO: this will crash if you toggle between
+ # pivoted and nonpivoted. I have several other 'crash' notes about this if
+ # this ever becomes possible.
+
+ if ( $tbl->{pivot} ) {
+ # Sort type is not really possible on pivoted columns, because a 'column'
+ # contains data from an entire non-pivoted row, so there could be a mix of
+ # numeric and non-numeric data. Thus everything has to be 'cmp' type.
+ foreach my $col ( split(/\s+/, $tbl->{sort_cols} ) ) {
+ next unless $col;
+ my ( $dir, $name ) = $col =~ m/([+-])?(\w+)$/;
+ next unless $name && $name =~ m/^(?:name|set_\d+)$/;
+ $dir ||= '+';
+ my $op = 'cmp';
+ my $df = "''";
+ push @criteria,
+ $dir eq '+'
+ ? "(\$a->{$name} || $df) $op (\$b->{$name} || $df)"
+ : "(\$b->{$name} || $df) $op (\$a->{$name} || $df)";
+ }
+ }
+ else {
+ foreach my $col ( split(/\s+/, $tbl->{sort_cols} ) ) {
+ next unless $col;
+ my ( $dir, $name ) = $col =~ m/([+-])?(\w+)$/;
+ next unless $name && $tbl->{cols}->{$name};
+ $dir ||= '+';
+ my $op = $tbl->{cols}->{$name}->{num} ? "<=>" : "cmp";
+ my $df = $tbl->{cols}->{$name}->{num} ? "0" : "''";
+ push @criteria,
+ $dir eq '+'
+ ? "(\$a->{$name} || $df) $op (\$b->{$name} || $df)"
+ : "(\$b->{$name} || $df) $op (\$a->{$name} || $df)";
+ }
+ }
+ return sub { return @_ } unless @criteria;
+ my $sub = eval 'sub { sort {' . join("||", @criteria) . '} @_; }';
+ die if $EVAL_ERROR;
+ return $sub;
+}
+
+# trunc {{{3
+# Shortens text to specified length.
+sub trunc {
+ my ( $text, $len ) = @_;
+ if ( length($text) <= $len ) {
+ return $text;
+ }
+ return substr($text, 0, $len);
+}
+
+# donut {{{3
+# Takes out the middle of text to shorten it.
+sub donut {
+ my ( $text, $len ) = @_;
+ return $text if length($text) <= $len;
+ my $max = length($text) - $len;
+ my $min = $max - 1;
+
+ # Try to remove a single "word" from somewhere in the center
+ if ( $text =~ s/_[^_]{$min,$max}_/_/ ) {
+ return $text;
+ }
+
+ # Prefer removing the end of a "word"
+ if ( $text =~ s/([^_]+)[^_]{$max}_/$1_/ ) {
+ return $text;
+ }
+
+ $text = substr($text, 0, int($len/2))
+ . "_"
+ . substr($text, int($len/2) + $max + 1);
+ return $text;
+}
+
+# crunch {{{3
+# Removes vowels and compacts repeated letters to shorten text.
+sub crunch {
+ my ( $text, $len ) = @_;
+ return $text if $len && length($text) <= $len;
+ $text =~ s/^IB_\w\w_//;
+ $text =~ s/(?<![_ ])[aeiou]//g;
+ $text =~ s/(.)\1+/$1/g;
+ return $text;
+}
+
+# collapse_ws {{{3
+# Collapses all whitespace to a single space.
+sub collapse_ws {
+ my ( $text ) = @_;
+ return '' unless defined $text;
+ $text =~ s/\s+/ /g;
+ return $text;
+}
+
+# Strips out non-printable characters within fields, which freak terminals out.
+sub no_ctrl_char {
+ my ( $text ) = @_;
+ return '' unless defined $text;
+ my $charset = $config{charset}->{val};
+ if ( $charset && $charset eq 'unicode' ) {
+ $text =~ s/
+ ("(?:(?!(?<!\\)").)*" # Double-quoted string
+ |'(?:(?!(?<!\\)').)*') # Or single-quoted string
+ /$1 =~ m#\p{IsC}# ? "[BINARY]" : $1/egx;
+ }
+ elsif ( $charset && $charset eq 'none' ) {
+ $text =~ s/
+ ("(?:(?!(?<!\\)").)*"
+ |'(?:(?!(?<!\\)').)*')
+ /[TEXT]/gx;
+ }
+ else { # The default is 'ascii'
+ $text =~ s/
+ ("(?:(?!(?<!\\)").)*"
+ |'(?:(?!(?<!\\)').)*')
+ /$1 =~ m#[^\040-\176]# ? "[BINARY]" : $1/egx;
+ }
+ return $text;
+}
+
+# word_wrap {{{3
+# Wraps text at word boundaries so it fits the screen.
+sub word_wrap {
+ my ( $text, $width) = @_;
+ $width ||= $this_term_size[0];
+ $text =~ s/(.{0,$width})(?:\s+|$)/$1\n/g;
+ $text =~ s/ +$//mg;
+ return $text;
+}
+
+# draw_screen {{{3
+# Prints lines to the screen. The first argument is an arrayref. Each
+# element of the array is either a string or an arrayref. If it's a string it
+# just gets printed. If it's an arrayref, the first element is the string to
+# print, and the second is args to colored().
+sub draw_screen {
+ my ( $display_lines, $prefs ) = @_;
+ if ( !$opts{n} && $config{show_statusbar}->{val} ) {
+ unshift @$display_lines, create_statusbar();
+ }
+
+ foreach my $listener ( @{$event_listener_for{draw_screen}} ) {
+ $listener->draw_screen($display_lines);
+ }
+
+ $clear_screen_sub->()
+ if $prefs->{clear} || !$modes{$config{mode}->{val}}->{no_clear_screen};
+ if ( $opts{n} || $prefs->{raw} ) {
+ my $num_lines = 0;
+ print join("\n",
+ map {
+ $num_lines++;
+ ref $_
+ ? colored($_->[0], $_->[1])
+ : $_;
+ }
+ grep { !$opts{n} || $_ } # Suppress empty lines
+ @$display_lines);
+ if ( $opts{n} && $num_lines ) {
+ print "\n";
+ }
+ }
+ else {
+ my $max_lines = $prefs->{show_all}
+ ? scalar(@$display_lines)- 1
+ : min(scalar(@$display_lines), $this_term_size[1]);
+ print join("\n",
+ map {
+ ref $_
+ ? colored(substr($_->[0], 0, $this_term_size[0]), $_->[1])
+ : substr($_, 0, $this_term_size[0]);
+ } @$display_lines[0..$max_lines - 1]);
+ }
+}
+
+# secs_to_time {{{3
+sub secs_to_time {
+ my ( $secs, $fmt ) = @_;
+ $secs ||= 0;
+ return '00:00' unless $secs;
+
+ # Decide what format to use, if not given
+ $fmt ||= $secs >= 86_400 ? 'd'
+ : $secs >= 3_600 ? 'h'
+ : 'm';
+
+ return
+ $fmt eq 'd' ? sprintf(
+ "%d+%02d:%02d:%02d",
+ int($secs / 86_400),
+ int(($secs % 86_400) / 3_600),
+ int(($secs % 3_600) / 60),
+ $secs % 60)
+ : $fmt eq 'h' ? sprintf(
+ "%02d:%02d:%02d",
+ int(($secs % 86_400) / 3_600),
+ int(($secs % 3_600) / 60),
+ $secs % 60)
+ : sprintf(
+ "%02d:%02d",
+ int(($secs % 3_600) / 60),
+ $secs % 60);
+}
+
+# dulint_to_int {{{3
+# Takes a number that InnoDB formats as two ulint integers, like transaction IDs
+# and such, and turns it into a single integer
+sub dulint_to_int {
+ my $num = shift;
+ return 0 unless $num;
+ my ( $high, $low ) = $num =~ m/^(\d+) (\d+)$/;
+ return $low unless $high;
+ return $low + ( $high * $MAX_ULONG );
+}
+
+# create_statusbar {{{3
+sub create_statusbar {
+ my $mode = $config{mode}->{val};
+ my @cxns = sort { $a cmp $b } get_connections();
+
+ my $modeline = ( $config{readonly}->{val} ? '[RO] ' : '' )
+ . $modes{$mode}->{hdr} . " (? for help)";
+ my $mode_width = length($modeline);
+ my $remaining_width = $this_term_size[0] - $mode_width - 1;
+ my $result;
+
+ # The thingie in top-right that says what we're monitoring.
+ my $cxn = '';
+
+ if ( 1 == @cxns && $dbhs{$cxns[0]} && $dbhs{$cxns[0]}->{dbh} ) {
+ $cxn = $dbhs{$cxns[0]}->{dbh}->{mysql_serverinfo} || '';
+ }
+ else {
+ if ( $modes{$mode}->{server_group} ) {
+ $cxn = "Servers: " . $modes{$mode}->{server_group};
+ my $err_count = grep { $dbhs{$_} && $dbhs{$_}->{err_count} } @cxns;
+ if ( $err_count ) {
+ $cxn .= "(" . ( scalar(@cxns) - $err_count ) . "/" . scalar(@cxns) . ")";
+ }
+ }
+ else {
+ $cxn = join(' ', map { ($dbhs{$_}->{err_count} ? '!' : '') . $_ }
+ grep { $dbhs{$_} } @cxns);
+ }
+ }
+
+ if ( 1 == @cxns ) {
+ get_driver_status(@cxns);
+ my $vars = $vars{$cxns[0]}->{$clock};
+ my $inc = inc(0, $cxns[0]);
+
+ # Format server uptime human-readably, calculate QPS...
+ my $uptime = secs_to_time( $vars->{Uptime_hires} );
+ my $qps = ($inc->{Questions}||0) / ($inc->{Uptime_hires}||1);
+ my $ibinfo = '';
+
+ if ( exists $vars->{IB_last_secs} ) {
+ $ibinfo .= "InnoDB $vars->{IB_last_secs}s ";
+ if ( $vars->{IB_got_all} ) {
+ if ( ($mode eq 'T' || $mode eq 'W')
+ && $vars->{IB_tx_is_truncated} ) {
+ $ibinfo .= ':^|';
+ }
+ else {
+ $ibinfo .= ':-)';
+ }
+ }
+ else {
+ $ibinfo .= ':-(';
+ }
+ }
+ $result = sprintf(
+ "%-${mode_width}s %${remaining_width}s",
+ $modeline,
+ join(', ', grep { $_ } (
+ $cxns[0],
+ $uptime,
+ $ibinfo,
+ shorten($qps) . " QPS",
+ ($vars->{Threads} || 0) . "/" . ($vars->{Threads_running} || 0) . "/" . ($vars->{Threads_cached} || 0) . " con/run/cac thds",
+ $cxn)));
+ }
+ else {
+ $result = sprintf(
+ "%-${mode_width}s %${remaining_width}s",
+ $modeline,
+ $cxn);
+ }
+
+ return $config{color}->{val} ? [ $result, 'bold reverse' ] : $result;
+}
+
+# Database connections {{{3
+sub add_new_dsn {
+ my ( $name, $dsn, $dl_table, $have_user, $user, $have_pass, $pass, $savepass ) = @_;
+
+ if ( defined $name ) {
+ $name =~ s/[\s:;]//g;
+ }
+
+ if ( !$name ) {
+ print word_wrap("Choose a name for the connection. It cannot contain "
+ . "whitespace, colons or semicolons."), "\n\n";
+ do {
+ $name = prompt("Enter a name");
+ $name =~ s/[\s:;]//g;
+ } until ( $name );
+ }
+
+ if ( !$dsn ) {
+ do {
+ $clear_screen_sub->();
+ print "Typical DSN strings look like\n DBI:mysql:;host=hostname;port=port\n"
+ . "The db and port are optional and can usually be omitted.\n"
+ . "If you specify 'mysql_read_default_group=mysql' many options can be read\n"
+ . "from your mysql options files (~/.my.cnf, /etc/my.cnf).\n\n";
+ $dsn = prompt("Enter a DSN string", undef, "DBI:mysql:;mysql_read_default_group=mysql;host=$name");
+ } until ( $dsn );
+ }
+ if ( !$dl_table ) {
+ $clear_screen_sub->();
+ my $dl_table = prompt("Optional: enter a table (must not exist) to use when resetting InnoDB deadlock information",
+ undef, 'test.innotop_dl');
+ }
+
+ $connections{$name} = {
+ dsn => $dsn,
+ dl_table => $dl_table,
+ have_user => $have_user,
+ user => $user,
+ have_pass => $have_pass,
+ pass => $pass,
+ savepass => $savepass
+ };
+}
+
+sub add_new_server_group {
+ my ( $name ) = @_;
+
+ if ( defined $name ) {
+ $name =~ s/[\s:;]//g;
+ }
+
+ if ( !$name ) {
+ print word_wrap("Choose a name for the group. It cannot contain "
+ . "whitespace, colons or semicolons."), "\n\n";
+ do {
+ $name = prompt("Enter a name");
+ $name =~ s/[\s:;]//g;
+ } until ( $name );
+ }
+
+ my @cxns;
+ do {
+ $clear_screen_sub->();
+ @cxns = select_cxn("Choose servers for $name", keys %connections);
+ } until ( @cxns );
+
+ $server_groups{$name} = \@cxns;
+ return $name;
+}
+
+sub get_var_set {
+ my ( $name ) = @_;
+ while ( !$name || !exists($var_sets{$config{$name}->{val}}) ) {
+ $name = choose_var_set($name);
+ }
+ return $var_sets{$config{$name}->{val}}->{text};
+}
+
+sub add_new_var_set {
+ my ( $name ) = @_;
+
+ if ( defined $name ) {
+ $name =~ s/\W//g;
+ }
+
+ if ( !$name ) {
+ do {
+ $name = prompt("Enter a name");
+ $name =~ s/\W//g;
+ } until ( $name );
+ }
+
+ my $variables;
+ do {
+ $clear_screen_sub->();
+ $variables = prompt("Enter variables for $name", undef );
+ } until ( $variables );
+
+ $var_sets{$name} = { text => $variables, user => 1 };
+}
+
+sub next_server {
+ my $mode = $config{mode}->{val};
+ my @cxns = sort keys %connections;
+ my ($cur) = get_connections($mode);
+ $cur ||= $cxns[0];
+ my $pos = grep { $_ lt $cur } @cxns;
+ my $newpos = ($pos + 1) % @cxns;
+ $modes{$mode}->{server_group} = '';
+ $modes{$mode}->{connections} = [ $cxns[$newpos] ];
+ $clear_screen_sub->();
+}
+
+sub next_server_group {
+ my $mode = shift || $config{mode}->{val};
+ my @grps = sort keys %server_groups;
+ my $curr = $modes{$mode}->{server_group};
+
+ return unless @grps;
+
+ if ( $curr ) {
+ # Find the current group's position.
+ my $pos = 0;
+ while ( $curr ne $grps[$pos] ) {
+ $pos++;
+ }
+ $modes{$mode}->{server_group} = $grps[ ($pos + 1) % @grps ];
+ }
+ else {
+ $modes{$mode}->{server_group} = $grps[0];
+ }
+}
+
+# Get a list of connection names used in this mode.
+sub get_connections {
+ if ( $file ) {
+ return qw(file);
+ }
+ my $mode = shift || $config{mode}->{val};
+ my @connections = $modes{$mode}->{server_group}
+ ? @{$server_groups{$modes{$mode}->{server_group}}}
+ : @{$modes{$mode}->{connections}};
+ if ( $modes{$mode}->{one_connection} ) {
+ @connections = @connections ? $connections[0] : ();
+ }
+ return unique(@connections);
+}
+
+# Get a list of tables used in this mode. If innotop is running non-interactively, just use the first.
+sub get_visible_tables {
+ my $mode = shift || $config{mode}->{val};
+ my @tbls = @{$modes{$mode}->{visible_tables}};
+ if ( $opts{n} ) {
+ return $tbls[0];
+ }
+ else {
+ return @tbls;
+ }
+}
+
+# Choose from among available connections or server groups.
+# If the mode has a server set in use, prefers that instead.
+sub choose_connections {
+ $clear_screen_sub->();
+ my $mode = $config{mode}->{val};
+ my $meta = { map { $_ => $connections{$_}->{dsn} } keys %connections };
+ foreach my $group ( keys %server_groups ) {
+ $meta->{"#$group"} = join(' ', @{$server_groups{$group}});
+ }
+
+ my $choices = prompt_list("Choose connections or a group for $mode mode",
+ undef, sub { return keys %$meta }, $meta);
+
+ my @choices = unique(grep { $_ } split(/\s+/, $choices));
+ if ( @choices ) {
+ if ( $choices[0] =~ s/^#// && exists $server_groups{$choices[0]} ) {
+ $modes{$mode}->{server_group} = $choices[0];
+ }
+ else {
+ $modes{$mode}->{connections} = [ grep { exists $connections{$_} } @choices ];
+ }
+ }
+}
+
+# Accepts a DB connection name and the name of a prepared query (e.g. status, kill).
+# Also a list of params for the prepared query. This allows not storing prepared
+# statements globally. Returns a $sth that's been executed.
+# ERROR-HANDLING SEMANTICS: if the statement throws an error, propagate, but if the
+# connection has gone away or can't connect, DO NOT. Just return undef.
+sub do_stmt {
+ my ( $cxn, $stmt_name, @args ) = @_;
+
+ return undef if $file;
+
+ # Test if the cxn should not even be tried
+ return undef if $dbhs{$cxn}
+ && $dbhs{$cxn}->{err_count}
+ && ( !$dbhs{$cxn}->{dbh} || !$dbhs{$cxn}->{dbh}->{Active} || $dbhs{$cxn}->{mode} eq $config{mode}->{val} )
+ && $dbhs{$cxn}->{wake_up} > $clock;
+
+ my $sth;
+ my $retries = 1;
+ my $success = 0;
+ TRY:
+ while ( $retries-- >= 0 && !$success ) {
+
+ eval {
+ my $dbh = connect_to_db($cxn);
+
+ # If the prepared query doesn't exist, make it.
+ if ( !exists $dbhs{$cxn}->{stmts}->{$stmt_name} ) {
+ $dbhs{$cxn}->{stmts}->{$stmt_name} = $stmt_maker_for{$stmt_name}->($dbh);
+ }
+
+ $sth = $dbhs{$cxn}->{stmts}->{$stmt_name};
+ if ( $sth ) {
+ $sth->execute(@args);
+ }
+ $success = 1;
+ };
+ if ( $EVAL_ERROR ) {
+ if ( $EVAL_ERROR =~ m/$nonfatal_errs/ ) {
+ handle_cxn_error($cxn, $EVAL_ERROR);
+ }
+ else {
+ die "$cxn $stmt_name: $EVAL_ERROR";
+ }
+ if ( $retries < 0 ) {
+ $sth = undef;
+ }
+ }
+ }
+
+ if ( $sth && $sth->{NUM_OF_FIELDS} ) {
+ sleep($stmt_sleep_time_for{$stmt_name}) if $stmt_sleep_time_for{$stmt_name};
+ return $sth;
+ }
+}
+
+# Keeps track of error count, sleep times till retries, etc etc.
+# When there's an error we retry the connection every so often, increasing in
+# Fibonacci series to prevent too much banging on the server.
+sub handle_cxn_error {
+ my ( $cxn, $err ) = @_;
+ my $meta = $dbhs{$cxn};
+ $meta->{err_count}++;
+
+ # This is used so errors that have to do with permissions needed by the current
+ # mode will get displayed as long as we're in this mode, but get ignored if the
+ # mode changes.
+ $meta->{mode} = $config{mode}->{val};
+
+ # Strip garbage from the error text if possible.
+ $err =~ s/\s+/ /g;
+ if ( $err =~ m/failed: (.*?) at \S*innotop line/ ) {
+ $err = $1;
+ }
+
+ $meta->{last_err} = $err;
+ my $sleep_time = $meta->{this_sleep} + $meta->{prev_sleep};
+ $meta->{prev_sleep} = $meta->{this_sleep};
+ $meta->{this_sleep} = $sleep_time;
+ $meta->{wake_up} = $clock + $sleep_time;
+ if ( $config{show_cxn_errors}->{val} ) {
+ print STDERR "Error at tick $clock $cxn $err" if $config{debug}->{val};
+ }
+}
+
+# Accepts a DB connection name and a (string) query. Returns a $sth that's been
+# executed.
+sub do_query {
+ my ( $cxn, $query ) = @_;
+
+ return undef if $file;
+
+ # Test if the cxn should not even be tried
+ return undef if $dbhs{$cxn}
+ && $dbhs{$cxn}->{err_count}
+ && ( !$dbhs{$cxn}->{dbh} || !$dbhs{$cxn}->{dbh}->{Active} || $dbhs{$cxn}->{mode} eq $config{mode}->{val} )
+ && $dbhs{$cxn}->{wake_up} > $clock;
+
+ my $sth;
+ my $retries = 1;
+ my $success = 0;
+ TRY:
+ while ( $retries-- >= 0 && !$success ) {
+
+ eval {
+ my $dbh = connect_to_db($cxn);
+
+ $sth = $dbh->prepare($query);
+ $sth->execute();
+ $success = 1;
+ };
+ if ( $EVAL_ERROR ) {
+ if ( $EVAL_ERROR =~ m/$nonfatal_errs/ ) {
+ handle_cxn_error($cxn, $EVAL_ERROR);
+ }
+ else {
+ die $EVAL_ERROR;
+ }
+ if ( $retries < 0 ) {
+ $sth = undef;
+ }
+ }
+ }
+
+ return $sth;
+}
+
+sub get_uptime {
+ my ( $cxn ) = @_;
+ $dbhs{$cxn}->{start_time} ||= time();
+ # Avoid dividing by zero
+ return (time() - $dbhs{$cxn}->{start_time}) || .001;
+}
+
+sub connect_to_db {
+ my ( $cxn ) = @_;
+
+ $dbhs{$cxn} ||= {
+ stmts => {}, # bucket for prepared statements.
+ prev_sleep => 0,
+ this_sleep => 1,
+ wake_up => 0,
+ start_time => 0,
+ dbh => undef,
+ };
+ my $href = $dbhs{$cxn};
+
+ if ( !$href->{dbh} || ref($href->{dbh}) !~ m/DBI/ || !$href->{dbh}->ping ) {
+ my $dbh = get_new_db_connection($cxn);
+ @{$href}{qw(dbh err_count wake_up this_sleep start_time prev_sleep)}
+ = ($dbh, 0, 0, 1, 0, 0);
+
+ # Derive and store the server's start time in hi-res
+ my $uptime = $dbh->selectrow_hashref("show status like 'Uptime'")->{value};
+ $href->{start_time} = time() - $uptime;
+
+ # Set timeouts so an unused connection stays alive.
+ # For example, a connection might be used in Q mode but idle in T mode.
+ if ( version_ge($dbh, '4.0.3')) {
+ my $timeout = $config{cxn_timeout}->{val};
+ $dbh->do("set session wait_timeout=$timeout, interactive_timeout=$timeout");
+ }
+ }
+ return $href->{dbh};
+}
+
+# Compares versions like 5.0.27 and 4.1.15-standard-log
+sub version_ge {
+ my ( $dbh, $target ) = @_;
+ my $version = sprintf('%03d%03d%03d', $dbh->{mysql_serverinfo} =~ m/(\d+)/g);
+ return $version ge sprintf('%03d%03d%03d', $target =~ m/(\d+)/g);
+}
+
+# Extracts status values that can be gleaned from the DBD driver without doing a whole query.
+sub get_driver_status {
+ my @cxns = @_;
+ if ( !$info_gotten{driver_status}++ ) {
+ foreach my $cxn ( @cxns ) {
+ next unless $dbhs{$cxn} && $dbhs{$cxn}->{dbh} && $dbhs{$cxn}->{dbh}->{Active};
+ $vars{$cxn}->{$clock} ||= {};
+ my $vars = $vars{$cxn}->{$clock};
+ my %res = map { $_ =~ s/ +/_/g; $_ } $dbhs{$cxn}->{dbh}->{mysql_stat} =~ m/(\w[^:]+): ([\d\.]+)/g;
+ map { $vars->{$_} ||= $res{$_} } keys %res;
+ $vars->{Uptime_hires} ||= get_uptime($cxn);
+ $vars->{cxn} = $cxn;
+ }
+ }
+}
+
+sub get_new_db_connection {
+ my ( $connection, $destroy ) = @_;
+ if ( $file ) {
+ die "You can't connect to a MySQL server while monitoring a file. This is probably a bug.";
+ }
+
+ my $dsn = $connections{$connection}
+ or die "No connection named '$connection' is defined in your configuration";
+
+ # don't ask for a username if mysql_read_default_group=client is in the DSN
+ if ( !defined $dsn->{have_user} and $dsn->{dsn} !~ /mysql_read_default_group=client/ ) {
+ my $answer = prompt("Do you want to specify a username for $connection?", undef, 'n');
+ $dsn->{have_user} = $answer && $answer =~ m/1|y/i;
+ }
+
+ # don't ask for a password if mysql_read_default_group=client is in the DSN
+ if ( !defined $dsn->{have_pass} and $dsn->{dsn} !~ /mysql_read_default_group=client/ ) {
+ my $answer = prompt("Do you want to specify a password for $connection?", undef, 'n');
+ $dsn->{have_pass} = $answer && $answer =~ m/1|y/i;
+ }
+
+ if ( !$dsn->{user} && $dsn->{have_user} ) {
+ my $user = $ENV{USERNAME} || $ENV{USER} || getlogin() || getpwuid($REAL_USER_ID) || undef;
+ $dsn->{user} = prompt("Enter username for $connection", undef, $user);
+ }
+
+ if ( !defined $dsn->{user} ) {
+ $dsn->{user} = '';
+ }
+
+ if ( !$dsn->{pass} && !$dsn->{savepass} && $dsn->{have_pass} ) {
+ $dsn->{pass} = prompt_noecho("Enter password for '$dsn->{user}' on $connection");
+ print "\n";
+ if ( !defined($dsn->{savepass}) ) {
+ my $answer = prompt("Save password in plain text in the config file?", undef, 'y');
+ $dsn->{savepass} = $answer && $answer =~ m/1|y/i;
+ }
+ }
+
+ my $dbh = DBI->connect(
+ $dsn->{dsn}, $dsn->{user}, $dsn->{pass},
+ { RaiseError => 1, PrintError => 0, AutoCommit => 1 });
+ $dbh->{InactiveDestroy} = 1 unless $destroy; # Can't be set in $db_options
+ $dbh->{FetchHashKeyName} = 'NAME_lc'; # Lowercases all column names for fetchrow_hashref
+ return $dbh;
+}
+
+sub get_cxn_errors {
+ my @cxns = @_;
+ return () unless $config{show_cxn_errors_in_tbl}->{val};
+ return
+ map { [ $_ . ': ' . $dbhs{$_}->{last_err}, 'red' ] }
+ grep { $dbhs{$_} && $dbhs{$_}->{err_count} && $dbhs{$_}->{mode} eq $config{mode}->{val} }
+ @cxns;
+}
+
+# Setup and tear-down functions {{{2
+
+# Takes a string and turns it into a hashref you can apply to %tbl_meta tables. The string
+# can be in the form 'foo, bar, foo/bar, foo as bar' much like a SQL SELECT statement.
+sub compile_select_stmt {
+ my ($str) = @_;
+ my @exps = $str =~ m/\s*([^,]+(?i:\s+as\s+[^,\s]+)?)\s*(?=,|$)/g;
+ my %cols;
+ my @visible;
+ foreach my $exp ( @exps ) {
+ my ( $text, $colname );
+ if ( $exp =~ m/as\s+(\w+)\s*/ ) {
+ $colname = $1;
+ $exp =~ s/as\s+(\w+)\s*//;
+ $text = $exp;
+ }
+ else {
+ $text = $colname = $exp;
+ }
+ my ($func, $err) = compile_expr($text);
+ $cols{$colname} = {
+ src => $text,
+ hdr => $colname,
+ num => 0,
+ func => $func,
+ };
+ push @visible, $colname;
+ }
+ return (\%cols, \@visible);
+}
+
+# compile_filter {{{3
+sub compile_filter {
+ my ( $text ) = @_;
+ my ( $sub, $err );
+ eval "\$sub = sub { my \$set = shift; $text }";
+ if ( $EVAL_ERROR ) {
+ $EVAL_ERROR =~ s/at \(eval.*$//;
+ $sub = sub { return $EVAL_ERROR };
+ $err = $EVAL_ERROR;
+ }
+ return ( $sub, $err );
+}
+
+# compile_expr {{{3
+sub compile_expr {
+ my ( $expr ) = @_;
+ # Leave built-in functions alone so they get called as Perl functions, unless
+ # they are the only word in $expr, in which case treat them as hash keys.
+ if ( $expr =~ m/\W/ ) {
+ $expr =~ s/(?<!\{|\$)\b([A-Za-z]\w{2,})\b/is_func($1) ? $1 : "\$set->{$1}"/eg;
+ }
+ else {
+ $expr = "\$set->{$expr}";
+ }
+ my ( $sub, $err );
+ my $quoted = quotemeta($expr);
+ eval qq{
+ \$sub = sub {
+ my (\$set, \$cur, \$pre) = \@_;
+ my \$val = eval { $expr };
+ if ( \$EVAL_ERROR && \$config{debug}->{val} ) {
+ \$EVAL_ERROR =~ s/ at \\(eval.*//s;
+ die "\$EVAL_ERROR in expression $quoted";
+ }
+ return \$val;
+ }
+ };
+ if ( $EVAL_ERROR ) {
+ if ( $config{debug}->{val} ) {
+ die $EVAL_ERROR;
+ }
+ $EVAL_ERROR =~ s/ at \(eval.*$//;
+ $sub = sub { return $EVAL_ERROR };
+ $err = $EVAL_ERROR;
+ }
+ return ( $sub, $err );
+}
+
+# finish {{{3
+# This is a subroutine because it's called from a key to quit the program.
+sub finish {
+ save_config();
+ ReadMode('normal') unless $opts{n};
+ print "\n";
+ exit(0);
+}
+
+# core_dump {{{3
+sub core_dump {
+ my $msg = shift;
+ if ($config{debugfile}->{val} && $config{debug}->{val}) {
+ eval {
+ open my $file, '>>', $config{debugfile}->{val};
+ if ( %vars ) {
+ print $file "Current variables:\n" . Dumper(\%vars);
+ }
+ close $file;
+ };
+ }
+ print $msg;
+}
+
+# migrate_config {{{3
+sub migrate_config {
+
+ my ($old_filename, $new_filename) = @_;
+
+ # don't proceed if old file doesn't exist
+ if ( ! -f $old_filename ) {
+ die "Error migrating '$old_filename': file doesn't exist.\n";
+ }
+ # don't migrate files if new file exists
+ elsif ( -f $new_filename ) {
+ die "Error migrating '$old_filename' to '$new_filename': new file already exists.\n";
+ }
+ # if migrating from one file to another in the same directory, just rename them
+ if (dirname($old_filename) eq dirname($new_filename)) {
+ rename($old_filename, $new_filename)
+ or die "Can't rename '$old_filename' to '$new_filename': $OS_ERROR";
+ }
+ # otherwise, move the existing conf file to a temp file, make the necessary directory structure,
+ # and move the temp conf file to its new home
+ else {
+ my $tmp = File::Temp->new( TEMPLATE => 'innotopXXXXX', DIR => $homepath, SUFFIX => '.conf');
+ my $tmp_filename = $tmp->filename;
+ my $dirname = dirname($new_filename);
+ rename($old_filename, $tmp_filename)
+ or die "Can't rename '$old_filename' to '$tmp_filename': $OS_ERROR";
+ mkdir($dirname) or die "Can't create directory '$dirname': $OS_ERROR";
+ mkdir("$dirname/plugins") or die "Can't create directory '$dirname/plugins': $OS_ERROR";
+ rename($tmp_filename, $new_filename)
+ or die "Can't rename '$tmp_filename' to '$new_filename': $OS_ERROR";
+ }
+}
+
+# load_config {{{3
+sub load_config {
+
+ my ($old_filename, $answer);
+
+ if ( $opts{u} or $opts{p} or $opts{h} or $opts{P} ) {
+ my @params = $dsn_parser->get_cxn_params(\%opts); # dsn=$params[0]
+ add_new_dsn($opts{h} || 'localhost', $params[0], 'test.innotop_dl',
+ $opts{u} ? 1 : 0, $opts{u}, $opts{p} ? 1 : 0, $opts{p});
+ }
+ if ($opts{c}) {
+ $conf_file = $opts{c};
+ }
+ # innotop got upgraded and this is an old config file.
+ elsif ( -f "$homepath/.innotop" or -f "$homepath/.innotop/innotop.ini" ) {
+ $conf_file = $default_home_conf;
+ if ( -f "$homepath/.innotop") {
+ $old_filename = "$homepath/.innotop";
+ }
+ elsif ( -f "$homepath/.innotop/innotop.ini" ) {
+ $old_filename = "$homepath/.innotop/innotop.ini";
+ }
+ $answer = pause("Innotop's default config location has moved to '$conf_file'. Move old config file '$old_filename' there now? y/n");
+ if ( lc $answer eq 'y' ) {
+ migrate_config($old_filename, $conf_file);
+ }
+ else {
+ print "\nInnotop will now exit so you can fix the config file.\n";
+ exit(0);
+ }
+ }
+ elsif ( -f $default_home_conf ) {
+ $conf_file = $default_home_conf;
+ }
+ elsif ( -f $default_central_conf and not $opts{s} ) {
+ $conf_file = $default_central_conf;
+ }
+ else {
+ # If no config file was loaded, set readonly to 0 if the user wants to
+ # write a config
+ $config{readonly}->{val} = 0 if $opts{w};
+ # If no connections have been defined, connect to a MySQL database
+ # on localhost using mysql_read_default_group=client
+ if (!%connections) {
+ add_new_dsn('localhost',
+ 'DBI:mysql:;host=localhost;mysql_read_default_group=client',
+ 'test.innotop_dl');
+ }
+ }
+
+ if ( -f "$conf_file" ) {
+ open my $file, "<", $conf_file or die("Can't open '$conf_file': $OS_ERROR");
+
+ # Check config file version. Just ignore if either innotop or the file has
+ # garbage in the version number.
+ if ( defined(my $line = <$file>) && $VERSION =~ m/\d/ ) {
+ chomp $line;
+ if ( my ($maj, $min, $rev) = $line =~ m/^version=(\d+)\.(\d+)(?:\.(\d+))?$/ ) {
+ $rev ||= 0;
+ my $cfg_ver = sprintf('%03d-%03d-%03d', $maj, $min, $rev);
+ ( $maj, $min, $rev ) = $VERSION =~ m/^(\d+)\.(\d+)(?:\.(\d+))?$/;
+ $rev ||= 0;
+ my $innotop_ver = sprintf('%03d-%03d-%03d', $maj, $min, $rev);
+
+ if ( $cfg_ver gt $innotop_ver ) {
+ pause("The config file is for a newer version of innotop and may not be read correctly.");
+ }
+ else {
+ my @ver_history = @config_versions;
+ while ( my ($start, $end) = splice(@ver_history, 0, 2) ) {
+ # If the config file is between the endpoints and innotop is greater than
+ # the endpoint, innotop has a newer config file format than the file.
+ if ( $cfg_ver ge $start && $cfg_ver lt $end && $innotop_ver ge $end ) {
+ my $msg = "innotop's config file format has changed. Overwrite $conf_file? y or n";
+ if ( pause($msg) eq 'n' ) {
+ $config{readonly}->{val} = 1;
+ print "\ninnotop will not save any configuration changes you make.";
+ pause();
+ print "\n";
+ }
+ close $file;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next unless $line =~ m/^\[([a-z_]+)\]$/;
+ if ( exists $config_file_sections{$1} ) {
+ $config_file_sections{$1}->{reader}->($file);
+ }
+ else {
+ warn "Unknown config file section '$1'";
+ }
+ }
+ close $file or die("Can't close $conf_file: $OS_ERROR");
+ }
+
+}
+
+# Do some post-processing on %tbl_meta: compile src properties into func etc.
+sub post_process_tbl_meta {
+ foreach my $table ( values %tbl_meta ) {
+ foreach my $col_name ( keys %{$table->{cols}} ) {
+ my $col_def = $table->{cols}->{$col_name};
+ my ( $sub, $err ) = compile_expr($col_def->{src});
+ $col_def->{func} = $sub;
+ }
+ }
+}
+
+# load_config_plugins {{{3
+sub load_config_plugins {
+ my ( $file ) = @_;
+
+ # First, find a list of all plugins that exist on disk, and get information about them.
+ my $dir = $config{plugin_dir}->{val};
+ foreach my $p_file ( <$dir/*.pm> ) {
+ my ($package, $desc);
+ eval {
+ open my $p_in, "<", $p_file or die $OS_ERROR;
+ while ( my $line = <$p_in> ) {
+ chomp $line;
+ if ( $line =~ m/^package\s+(.*?);/ ) {
+ $package = $1;
+ }
+ elsif ( $line =~ m/^# description: (.*)/ ) {
+ $desc = $1;
+ }
+ last if $package && $desc;
+ }
+ close $p_in;
+ };
+ if ( $package ) {
+ $plugins{$package} = {
+ file => $p_file,
+ desc => $desc,
+ class => $package,
+ active => 0,
+ };
+ if ( $config{debug}->{val} && $EVAL_ERROR ) {
+ die $EVAL_ERROR;
+ }
+ }
+ }
+
+ # Now read which ones the user has activated. Each line simply represents an active plugin.
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+ next unless $line && $plugins{$line};
+
+ my $obj;
+ eval {
+ require $plugins{$line}->{file};
+ $obj = $line->new(%pluggable_vars);
+ foreach my $event ( $obj->register_for_events() ) {
+ my $queue = $event_listener_for{$event};
+ if ( $queue ) {
+ push @$queue, $obj;
+ }
+ }
+ };
+ if ( $config{debug}->{val} && $EVAL_ERROR ) {
+ die $EVAL_ERROR;
+ }
+ if ( $obj ) {
+ $plugins{$line}->{active} = 1;
+ $plugins{$line}->{object} = $obj;
+ }
+ }
+}
+
+# save_config_plugins {{{3
+sub save_config_plugins {
+ my $file = shift;
+ foreach my $class ( sort keys %plugins ) {
+ next unless $plugins{$class}->{active};
+ print $file "$class\n";
+ }
+}
+
+# load_config_active_server_groups {{{3
+sub load_config_active_server_groups {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $mode, $group ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $mode && $group
+ && exists $modes{$mode} && exists $server_groups{$group};
+ $modes{$mode}->{server_group} = $group;
+ }
+}
+
+# save_config_active_server_groups {{{3
+sub save_config_active_server_groups {
+ my $file = shift;
+ foreach my $mode ( sort keys %modes ) {
+ print $file "$mode=$modes{$mode}->{server_group}\n";
+ }
+}
+
+# load_config_server_groups {{{3
+sub load_config_server_groups {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $name, $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $name && $rest;
+ my @vars = unique(grep { $_ && exists $connections{$_} } split(/\s+/, $rest));
+ next unless @vars;
+ $server_groups{$name} = \@vars;
+ }
+}
+
+# save_config_server_groups {{{3
+sub save_config_server_groups {
+ my $file = shift;
+ foreach my $set ( sort keys %server_groups ) {
+ print $file "$set=", join(' ', @{$server_groups{$set}}), "\n";
+ }
+}
+
+# load_config_varsets {{{3
+sub load_config_varsets {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $name, $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $name && $rest;
+ $var_sets{$name} = {
+ text => $rest,
+ user => 1,
+ };
+ }
+}
+
+# save_config_varsets {{{3
+sub save_config_varsets {
+ my $file = shift;
+ foreach my $varset ( sort keys %var_sets ) {
+ next unless $var_sets{$varset}->{user};
+ print $file "$varset=$var_sets{$varset}->{text}\n";
+ }
+}
+
+# load_config_group_by {{{3
+sub load_config_group_by {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $tbl , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $tbl && exists $tbl_meta{$tbl};
+ my @parts = unique(grep { exists($tbl_meta{$tbl}->{cols}->{$_}) } split(/\s+/, $rest));
+ $tbl_meta{$tbl}->{group_by} = [ @parts ];
+ $tbl_meta{$tbl}->{cust}->{group_by} = 1;
+ }
+}
+
+# save_config_group_by {{{3
+sub save_config_group_by {
+ my $file = shift;
+ foreach my $tbl ( sort keys %tbl_meta ) {
+ next if $tbl_meta{$tbl}->{temp};
+ next unless $tbl_meta{$tbl}->{cust}->{group_by};
+ my $aref = $tbl_meta{$tbl}->{group_by};
+ print $file "$tbl=", join(' ', @$aref), "\n";
+ }
+}
+
+# load_config_filters {{{3
+sub load_config_filters {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key, $rest ) = $line =~ m/^(.+?)=(.*)$/;
+ next unless $key && $rest;
+
+ my %parts = $rest =~ m/(\w+)='((?:(?!(?<!\\)').)*)'/g; # Properties are single-quoted
+ next unless $parts{text} && $parts{tbls};
+
+ foreach my $prop ( keys %parts ) {
+ # Un-escape escaping
+ $parts{$prop} =~ s/\\\\/\\/g;
+ $parts{$prop} =~ s/\\'/'/g;
+ }
+
+ my ( $sub, $err ) = compile_filter($parts{text});
+ my @tbls = unique(split(/\s+/, $parts{tbls}));
+ @tbls = grep { exists $tbl_meta{$_} } @tbls;
+ $filters{$key} = {
+ func => $sub,
+ text => $parts{text},
+ user => 1,
+ name => $key,
+ note => 'User-defined filter',
+ tbls => \@tbls,
+ }
+ }
+}
+
+# save_config_filters {{{3
+sub save_config_filters {
+ my $file = shift;
+ foreach my $key ( sort keys %filters ) {
+ next if !$filters{$key}->{user} || $filters{$key}->{quick};
+ my $text = $filters{$key}->{text};
+ $text =~ s/([\\'])/\\$1/g;
+ my $tbls = join(" ", @{$filters{$key}->{tbls}});
+ print $file "$key=text='$text' tbls='$tbls'\n";
+ }
+}
+
+# load_config_visible_tables {{{3
+sub load_config_visible_tables {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $mode, $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $mode && exists $modes{$mode};
+ $modes{$mode}->{visible_tables} =
+ [ unique(grep { $_ && exists $tbl_meta{$_} } split(/\s+/, $rest)) ];
+ $modes{$mode}->{cust}->{visible_tables} = 1;
+ }
+}
+
+# save_config_visible_tables {{{3
+sub save_config_visible_tables {
+ my $file = shift;
+ foreach my $mode ( sort keys %modes ) {
+ next unless $modes{$mode}->{cust}->{visible_tables};
+ my $tables = $modes{$mode}->{visible_tables};
+ print $file "$mode=", join(' ', @$tables), "\n";
+ }
+}
+
+# load_config_sort_cols {{{3
+sub load_config_sort_cols {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $key && exists $tbl_meta{$key};
+ $tbl_meta{$key}->{sort_cols} = $rest;
+ $tbl_meta{$key}->{cust}->{sort_cols} = 1;
+ $tbl_meta{$key}->{sort_func} = make_sort_func($tbl_meta{$key});
+ }
+}
+
+# save_config_sort_cols {{{3
+sub save_config_sort_cols {
+ my $file = shift;
+ foreach my $tbl ( sort keys %tbl_meta ) {
+ next unless $tbl_meta{$tbl}->{cust}->{sort_cols};
+ my $col = $tbl_meta{$tbl}->{sort_cols};
+ print $file "$tbl=$col\n";
+ }
+}
+
+# load_config_active_filters {{{3
+sub load_config_active_filters {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $tbl , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $tbl && exists $tbl_meta{$tbl};
+ my @parts = unique(grep { exists($filters{$_}) } split(/\s+/, $rest));
+ @parts = grep { grep { $tbl eq $_ } @{$filters{$_}->{tbls}} } @parts;
+ $tbl_meta{$tbl}->{filters} = [ @parts ];
+ $tbl_meta{$tbl}->{cust}->{filters} = 1;
+ }
+}
+
+# save_config_active_filters {{{3
+sub save_config_active_filters {
+ my $file = shift;
+ foreach my $tbl ( sort keys %tbl_meta ) {
+ next if $tbl_meta{$tbl}->{temp};
+ next unless $tbl_meta{$tbl}->{cust}->{filters};
+ my $aref = $tbl_meta{$tbl}->{filters};
+ print $file "$tbl=", join(' ', @$aref), "\n";
+ }
+}
+
+# load_config_active_columns {{{3
+sub load_config_active_columns {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $key && exists $tbl_meta{$key};
+ my @parts = grep { exists($tbl_meta{$key}->{cols}->{$_}) } unique split(/ /, $rest);
+ $tbl_meta{$key}->{visible} = [ @parts ];
+ $tbl_meta{$key}->{cust}->{visible} = 1;
+ }
+}
+
+# save_config_active_columns {{{3
+sub save_config_active_columns {
+ my $file = shift;
+ foreach my $tbl ( sort keys %tbl_meta ) {
+ next unless $tbl_meta{$tbl}->{cust}->{visible};
+ my $aref = $tbl_meta{$tbl}->{visible};
+ print $file "$tbl=", join(' ', @$aref), "\n";
+ }
+}
+
+# save_config_tbl_meta {{{3
+sub save_config_tbl_meta {
+ my $file = shift;
+ foreach my $tbl ( sort keys %tbl_meta ) {
+ foreach my $col ( keys %{$tbl_meta{$tbl}->{cols}} ) {
+ my $meta = $tbl_meta{$tbl}->{cols}->{$col};
+ next unless $meta->{user};
+ print $file "$col=", join(
+ " ",
+ map {
+ # Some properties (trans) are arrays, others scalars
+ my $val = ref($meta->{$_}) ? join(',', @{$meta->{$_}}) : $meta->{$_};
+ $val =~ s/([\\'])/\\$1/g; # Escape backslashes and single quotes
+ "$_='$val'"; # Enclose in single quotes
+ }
+ grep { $_ ne 'func' }
+ keys %$meta
+ ), "\n";
+ }
+ }
+}
+
+# save_config_config {{{3
+sub save_config_config {
+ my $file = shift;
+ foreach my $key ( sort keys %config ) {
+ eval {
+ if ( $key ne 'password' || $config{savepass}->{val} ) {
+ print $file "# $config{$key}->{note}\n"
+ or die "Cannot print to file: $OS_ERROR";
+ my $val = $config{$key}->{val};
+ $val = '' unless defined($val);
+ if ( ref( $val ) eq 'ARRAY' ) {
+ print $file "$key="
+ . join( " ", @$val ) . "\n"
+ or die "Cannot print to file: $OS_ERROR";
+ }
+ elsif ( ref( $val ) eq 'HASH' ) {
+ print $file "$key="
+ . join( " ",
+ map { "$_:$val->{$_}" } keys %$val
+ ) . "\n";
+ }
+ else {
+ print $file "$key=$val\n";
+ }
+ }
+ };
+ if ( $EVAL_ERROR ) { print "$EVAL_ERROR in $key"; };
+ }
+
+}
+
+# load_config_config {{{3
+sub load_config_config {
+ my ( $file ) = @_;
+
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $name, $val ) = $line =~ m/^(.+?)=(.*)$/;
+ next unless defined $name && defined $val;
+
+ # Validate the incoming values...
+ if ( $name && exists( $config{$name} ) ) {
+ if ( !$config{$name}->{pat} || $val =~ m/$config{$name}->{pat}/ ) {
+ $config{$name}->{val} = $val;
+ $config{$name}->{read} = 1;
+ }
+ }
+ }
+}
+
+# load_config_tbl_meta {{{3
+sub load_config_tbl_meta {
+ my ( $file ) = @_;
+
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ # Each tbl_meta section has all the properties defined in %col_props.
+ my ( $col , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $col;
+ my %parts = $rest =~ m/(\w+)='((?:(?!(?<!\\)').)*)'/g; # Properties are single-quoted
+
+ # Each section read from the config file has one extra property: which table it
+ # goes in.
+ my $tbl = $parts{tbl} or die "There's no table for tbl_meta $col";
+ my $meta = $tbl_meta{$tbl} or die "There's no table in tbl_meta named $tbl";
+
+ # The section is user-defined by definition (if that makes sense).
+ $parts{user} = 1;
+
+ # The column may already exist in the table, in which case this is just a
+ # customization.
+ $meta->{cols}->{$col} ||= {};
+
+ foreach my $prop ( keys %col_props ) {
+ if ( !defined($parts{$prop}) ) {
+ die "Undefined property $prop for column $col in table $tbl";
+ }
+
+ # Un-escape escaping
+ $parts{$prop} =~ s/\\\\/\\/g;
+ $parts{$prop} =~ s/\\'/'/g;
+
+ if ( ref $col_props{$prop} ) {
+ if ( $prop eq 'trans' ) {
+ $meta->{cols}->{$col}->{trans}
+ = [ unique(grep { exists $trans_funcs{$_} } split(',', $parts{$prop})) ];
+ }
+ else {
+ $meta->{cols}->{$col}->{$prop} = [ split(',', $parts{$prop}) ];
+ }
+ }
+ else {
+ $meta->{cols}->{$col}->{$prop} = $parts{$prop};
+ }
+ }
+
+ }
+}
+
+# save_config {{{3
+sub save_config {
+ print "\n";
+ return if $config{readonly}->{val};
+ # return if no config file was loaded and -w wasn't specified
+ if (not $conf_file) {
+ if (not $opts{w}) {
+ return;
+ }
+ else {
+ # if no config was loaded but -w was specified,
+ # write to $default_home_conf
+ $conf_file = $default_home_conf;
+ }
+ }
+ elsif ($conf_file and $opts{w}) {
+ print "Loaded config file on start-up, so ignoring -w (see --help)\n"
+ }
+
+ my $dirname = dirname($conf_file);
+
+ # if directories don't exist, create them. This could cause errors
+ # or warnings if a central config doesn't have readonly=1, but being
+ # flexible requires giving the user enough rope to hang themselves with.
+ if ( ! -d $dirname ) {
+ mkdir $dirname
+ or die "Can't create directory '$dirname': $OS_ERROR";
+ }
+ if ( ! -d "$dirname/plugins" ) {
+ mkdir "$dirname/plugins"
+ or warn "Can't create directory '$dirname/plugins': $OS_ERROR\n";
+ }
+
+ # Save to a temp file first, so a crash doesn't destroy the main config file
+ my $tmpfile = File::Temp->new( TEMPLATE => 'innotopXXXXX', DIR => $dirname, SUFFIX => '.conf.tmp');
+ open my $file, "+>", $tmpfile
+ or die("Can't write to $tmpfile: $OS_ERROR");
+ print $file "version=$VERSION\n";
+
+ foreach my $section ( @ordered_config_file_sections ) {
+ die "No such config file section $section" unless $config_file_sections{$section};
+ print $file "\n[$section]\n\n";
+ $config_file_sections{$section}->{writer}->($file);
+ print $file "\n[/$section]\n";
+ }
+
+ # Now clobber the main config file with the temp.
+ close $file or die("Can't close $tmpfile: $OS_ERROR");
+ rename($tmpfile, $conf_file) or die("Can't rename $tmpfile to $conf_file: $OS_ERROR");
+}
+
+# load_config_connections {{{3
+sub load_config_connections {
+ return if $opts{u} or $opts{p} or $opts{h} or $opts{P}; # don't load connections if DSN or user/pass options used
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $key;
+ my %parts = $rest =~ m/(\S+?)=(\S*)/g;
+ my %conn = map { $_ => $parts{$_} || '' } @conn_parts;
+ $connections{$key} = \%conn;
+ }
+}
+
+# save_config_connections {{{3
+sub save_config_connections {
+ my $file = shift;
+ foreach my $conn ( sort keys %connections ) {
+ my $href = $connections{$conn};
+ my @keys = $href->{savepass} ? @conn_parts : grep { $_ ne 'pass' } @conn_parts;
+ print $file "$conn=", join(' ', map { "$_=$href->{$_}" } grep { defined $href->{$_} } @keys), "\n";
+ }
+}
+
+sub load_config_colors {
+ my ( $file ) = @_;
+ my %rule_set_for;
+
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $tbl, $rule ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $tbl && $rule;
+ next unless exists $tbl_meta{$tbl};
+ my %parts = $rule =~ m/(\w+)='((?:(?!(?<!\\)').)*)'/g; # Properties are single-quoted
+ next unless $parts{col} && exists $tbl_meta{$tbl}->{cols}->{$parts{col}};
+ next unless $parts{op} && exists $comp_ops{$parts{op}};
+ next unless defined $parts{arg};
+ next unless defined $parts{color};
+ my @colors = unique(grep { exists $ansicolors{$_} } split(/\W+/, $parts{color}));
+ next unless @colors;
+
+ # Finally! Enough validation...
+ $rule_set_for{$tbl} ||= [];
+ push @{$rule_set_for{$tbl}}, \%parts;
+ }
+
+ foreach my $tbl ( keys %rule_set_for ) {
+ $tbl_meta{$tbl}->{colors} = $rule_set_for{$tbl};
+ $tbl_meta{$tbl}->{color_func} = make_color_func($tbl_meta{$tbl});
+ $tbl_meta{$tbl}->{cust}->{colors} = 1;
+ }
+}
+
+# save_config_colors {{{3
+sub save_config_colors {
+ my $file = shift;
+ foreach my $tbl ( sort keys %tbl_meta ) {
+ my $meta = $tbl_meta{$tbl};
+ next unless $meta->{cust}->{colors};
+ foreach my $rule ( @{$meta->{colors}} ) {
+ print $file "$tbl=", join(
+ ' ',
+ map {
+ my $val = $rule->{$_};
+ $val =~ s/([\\'])/\\$1/g; # Escape backslashes and single quotes
+ "$_='$val'"; # Enclose in single quotes
+ }
+ qw(col op arg color)
+ ), "\n";
+ }
+ }
+}
+
+# load_config_active_connections {{{3
+sub load_config_active_connections {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key , $rest ) = $line =~ m/^(.*?)=(.*)$/;
+ next unless $key && exists $modes{$key};
+ my @parts = grep { exists $connections{$_} } split(/ /, $rest);
+ $modes{$key}->{connections} = [ @parts ] if exists $modes{$key};
+ }
+}
+
+# save_config_active_connections {{{3
+sub save_config_active_connections {
+ my $file = shift;
+ foreach my $mode ( sort keys %modes ) {
+ my @connections = get_connections($mode);
+ print $file "$mode=", join(' ', @connections), "\n";
+ }
+}
+
+# load_config_stmt_sleep_times {{{3
+sub load_config_stmt_sleep_times {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key , $val ) = split('=', $line);
+ next unless $key && defined $val && $val =~ m/$num_regex/;
+ $stmt_sleep_time_for{$key} = $val;
+ }
+}
+
+# save_config_stmt_sleep_times {{{3
+sub save_config_stmt_sleep_times {
+ my $file = shift;
+ foreach my $key ( sort keys %stmt_sleep_time_for ) {
+ print $file "$key=$stmt_sleep_time_for{$key}\n";
+ }
+}
+
+# load_config_mvs {{{3
+sub load_config_mvs {
+ my ( $file ) = @_;
+ while ( my $line = <$file> ) {
+ chomp $line;
+ next if $line =~ m/^#/;
+ last if $line =~ m/^\[/;
+
+ my ( $key , $val ) = split('=', $line);
+ next unless $key && defined $val && $val =~ m/$num_regex/;
+ $mvs{$key} = $val;
+ }
+}
+
+# save_config_mvs {{{3
+sub save_config_mvs {
+ my $file = shift;
+ foreach my $key ( sort keys %mvs ) {
+ print $file "$key=$mvs{$key}\n";
+ }
+}
+
+# edit_configuration {{{3
+sub edit_configuration {
+ my $key = '';
+ while ( $key ne 'q' ) {
+ $clear_screen_sub->();
+ my @display_lines = '';
+
+ if ( $key && $cfg_editor_action{$key} ) {
+ $cfg_editor_action{$key}->{func}->();
+ }
+
+ # Show help
+ push @display_lines, create_caption('What configuration do you want to edit?',
+ create_table2(
+ [ sort keys %cfg_editor_action ],
+ { map { $_ => $_ } keys %cfg_editor_action },
+ { map { $_ => $cfg_editor_action{$_}->{note} } keys %cfg_editor_action },
+ { sep => ' ' }));
+
+ draw_screen(\@display_lines);
+ $key = pause('');
+ }
+}
+
+# edit_configuration_variables {{{3
+sub edit_configuration_variables {
+ $clear_screen_sub->();
+ my $mode = $config{mode}->{val};
+
+ my %config_choices
+ = map { $_ => $config{$_}->{note} || '' }
+ # Only config values that are marked as applying to this mode.
+ grep {
+ my $key = $_;
+ $config{$key}->{conf} &&
+ ( $config{$key}->{conf} eq 'ALL'
+ || grep { $mode eq $_ } @{$config{$key}->{conf}} )
+ } keys %config;
+
+ my $key = prompt_list(
+ "Enter the name of the variable you wish to configure",
+ '',
+ sub{ return keys %config_choices },
+ \%config_choices);
+
+ if ( exists($config_choices{$key}) ) {
+ get_config_interactive($key);
+ }
+}
+
+# edit_color_rules {{{3
+sub edit_color_rules {
+ my ( $tbl ) = @_;
+ $clear_screen_sub->();
+ $tbl ||= choose_visible_table();
+ if ( $tbl && exists($tbl_meta{$tbl}) ) {
+ my $meta = $tbl_meta{$tbl};
+ my @cols = ('', qw(col op arg color));
+ my $info = { map { $_ => { hdr => $_, just => '-', } } @cols };
+ $info->{label}->{maxw} = 30;
+ my $key;
+ my $selected_rule;
+
+ # This loop builds a tabular view of the rules.
+ do {
+
+ # Show help
+ if ( $key && $key eq '?' ) {
+ my @display_lines = '';
+ push @display_lines, create_caption('Editor key mappings',
+ create_table2(
+ [ sort keys %color_editor_action ],
+ { map { $_ => $_ } keys %color_editor_action },
+ { map { $_ => $color_editor_action{$_}->{note} } keys %color_editor_action },
+ { sep => ' ' }));
+ draw_screen(\@display_lines);
+ pause();
+ $key = '';
+ }
+ else {
+
+ # Do the action specified
+ $selected_rule ||= 0;
+ if ( $key && $color_editor_action{$key} ) {
+ $selected_rule = $color_editor_action{$key}->{func}->($tbl, $selected_rule);
+ $selected_rule ||= 0;
+ }
+
+ # Build the table of rules. If the terminal has color, the selected rule
+ # will be highlighted; otherwise a > at the left will indicate.
+ my $data = $meta->{colors} || [];
+ foreach my $i ( 0..@$data - 1 ) {
+ $data->[$i]->{''} = $i == $selected_rule ? '>' : '';
+ }
+ my @display_lines = create_table(\@cols, $info, $data);
+
+ # Highlight selected entry
+ for my $i ( 0 .. $#display_lines ) {
+ if ( $display_lines[$i] =~ m/^>/ ) {
+ $display_lines[$i] = [ $display_lines[$i], 'reverse' ];
+ }
+ }
+
+ # Draw the screen and wait for a command.
+ unshift @display_lines, '',
+ "Editing color rules for $meta->{capt}. Press ? for help, q to "
+ . "quit.", '';
+ draw_screen(\@display_lines);
+ print "\n\n", word_wrap('Rules are applied in order from top to '
+ . 'bottom. The first matching rule wins and prevents the '
+ . 'rest of the rules from being applied.');
+ $key = pause('');
+ }
+ } while ( $key ne 'q' );
+ $meta->{color_func} = make_color_func($meta);
+ }
+}
+
+# add_quick_filter {{{3
+sub add_quick_filter {
+ my $tbl = choose_visible_table();
+ if ( $tbl && exists($tbl_meta{$tbl}) ) {
+ print "\n";
+ my $response = prompt_list(
+ "Enter column name and filter text",
+ '',
+ sub { return keys %{$tbl_meta{$tbl}->{cols}} },
+ ()
+ );
+ my ( $col, $text ) = split(/\s+/, $response, 2);
+
+ # You can't filter on a nonexistent column. But if you filter on a pivoted
+ # table, the columns are different, so on a pivoted table, allow filtering
+ # on the 'name' column.
+ # NOTE: if a table is pivoted and un-pivoted, this will likely cause crashes.
+ # Currently not an issue since there's no way to toggle pivot/nopivot.
+ return unless $col && $text &&
+ (exists($tbl_meta{$tbl}->{cols}->{$col})
+ || ($tbl_meta{$tbl}->{pivot} && $col eq 'name'));
+
+ my ( $sub, $err ) = compile_filter( "defined \$set->{$col} && \$set->{$col} =~ m/$text/" );
+ return if !$sub || $err;
+ my $name = "quick_$tbl.$col";
+ $filters{$name} = {
+ func => $sub,
+ text => $text,
+ user => 1,
+ quick => 1,
+ name => $name,
+ note => 'Quick-filter',
+ tbls => [$tbl],
+ };
+ push @{$tbl_meta{$tbl}->{filters}}, $name;
+ }
+}
+
+# clear_quick_filters {{{3
+sub clear_quick_filters {
+ my $tbl = choose_visible_table(
+ # Only tables that have quick-filters
+ sub {
+ my ( $tbl ) = @_;
+ return scalar grep { $filters{$_}->{quick} } @{ $tbl_meta{$tbl}->{filters} };
+ }
+ );
+ if ( $tbl && exists($tbl_meta{$tbl}) ) {
+ my @current = @{$tbl_meta{$tbl}->{filters}};
+ @current = grep { !$filters{$_}->{quick} } @current;
+ $tbl_meta{$tbl}->{filters} = \@current;
+ }
+}
+
+sub edit_plugins {
+ $clear_screen_sub->();
+
+ my @cols = ('', qw(class desc active));
+ my $info = { map { $_ => { hdr => $_, just => '-', } } @cols };
+ my @rows = map { $plugins{$_} } sort keys %plugins;
+ my $key;
+ my $selected;
+
+ # This loop builds a tabular view of the plugins.
+ do {
+
+ # Show help
+ if ( $key && $key eq '?' ) {
+ my @display_lines = '';
+ push @display_lines, create_caption('Editor key mappings',
+ create_table2(
+ [ sort keys %plugin_editor_action ],
+ { map { $_ => $_ } keys %plugin_editor_action },
+ { map { $_ => $plugin_editor_action{$_}->{note} } keys %plugin_editor_action },
+ { sep => ' ' }));
+ draw_screen(\@display_lines);
+ pause();
+ $key = '';
+ }
+
+ # Do the action specified
+ else {
+ $selected ||= 0;
+ if ( $key && $plugin_editor_action{$key} ) {
+ $selected = $plugin_editor_action{$key}->{func}->(\@rows, $selected);
+ $selected ||= 0;
+ }
+
+ # Build the table of plugins.
+ foreach my $row ( 0.. $#rows ) {
+ $rows[$row]->{''} = $row eq $selected ? '>' : ' ';
+ }
+ my @display_lines = create_table(\@cols, $info, \@rows);
+
+ # Highlight selected entry
+ for my $i ( 0 .. $#display_lines ) {
+ if ( $display_lines[$i] =~ m/^>/ ) {
+ $display_lines[$i] = [ $display_lines[$i], 'reverse' ];
+ }
+ }
+
+ # Draw the screen and wait for a command.
+ unshift @display_lines, '',
+ "Plugin Management. Press ? for help, q to quit.", '';
+ draw_screen(\@display_lines);
+ $key = pause('');
+ }
+ } while ( $key ne 'q' );
+}
+
+# edit_table {{{3
+sub edit_table {
+ $clear_screen_sub->();
+ my ( $tbl ) = @_;
+ $tbl ||= choose_visible_table();
+ if ( $tbl && exists($tbl_meta{$tbl}) ) {
+ my $meta = $tbl_meta{$tbl};
+ my @cols = ('', qw(name hdr label src));
+ my $info = { map { $_ => { hdr => $_, just => '-', } } @cols };
+ $info->{label}->{maxw} = 30;
+ my $key;
+ my $selected_column;
+
+ # This loop builds a tabular view of the tbl_meta's structure, showing each column
+ # in the entry as a row.
+ do {
+
+ # Show help
+ if ( $key && $key eq '?' ) {
+ my @display_lines = '';
+ push @display_lines, create_caption('Editor key mappings',
+ create_table2(
+ [ sort keys %tbl_editor_action ],
+ { map { $_ => $_ } keys %tbl_editor_action },
+ { map { $_ => $tbl_editor_action{$_}->{note} } keys %tbl_editor_action },
+ { sep => ' ' }));
+ draw_screen(\@display_lines);
+ pause();
+ $key = '';
+ }
+ else {
+
+ # Do the action specified
+ $selected_column ||= $meta->{visible}->[0];
+ if ( $key && $tbl_editor_action{$key} ) {
+ $selected_column = $tbl_editor_action{$key}->{func}->($tbl, $selected_column);
+ $selected_column ||= $meta->{visible}->[0];
+ }
+
+ # Build the pivoted view of the table's meta-data. If the terminal has color,
+ # The selected row will be highlighted; otherwise a > at the left will indicate.
+ my $data = [];
+ foreach my $row ( @{$meta->{visible}} ) {
+ my %hash;
+ @hash{ @cols } = @{$meta->{cols}->{$row}}{@cols};
+ $hash{src} = '' if ref $hash{src};
+ $hash{name} = $row;
+ $hash{''} = $row eq $selected_column ? '>' : ' ';
+ push @$data, \%hash;
+ }
+ my @display_lines = create_table(\@cols, $info, $data);
+
+ # Highlight selected entry
+ for my $i ( 0 .. $#display_lines ) {
+ if ( $display_lines[$i] =~ m/^>/ ) {
+ $display_lines[$i] = [ $display_lines[$i], 'reverse' ];
+ }
+ }
+
+ # Draw the screen and wait for a command.
+ unshift @display_lines, '',
+ "Editing table definition for $meta->{capt}. Press ? for help, q to quit.", '';
+ draw_screen(\@display_lines, { clear => 1 });
+ $key = pause('');
+ }
+ } while ( $key ne 'q' );
+ }
+}
+
+# choose_mode_tables {{{3
+# Choose which table(s), and in what order, to display in a given mode.
+sub choose_mode_tables {
+ my $mode = $config{mode}->{val};
+ my @tbls = @{$modes{$mode}->{visible_tables}};
+ my $new = prompt_list(
+ "Choose tables to display",
+ join(' ', @tbls),
+ sub { return @{$modes{$mode}->{tables}} },
+ { map { $_ => $tbl_meta{$_}->{capt} } @{$modes{$mode}->{tables}} }
+ );
+ $modes{$mode}->{visible_tables} =
+ [ unique(grep { $_ && exists $tbl_meta{$_} } split(/\s+/, $new)) ];
+ $modes{$mode}->{cust}->{visible_tables} = 1;
+}
+
+# choose_visible_table {{{3
+sub choose_visible_table {
+ my ( $grep_cond ) = @_;
+ my $mode = $config{mode}->{val};
+ my @tbls
+ = grep { $grep_cond ? $grep_cond->($_) : 1 }
+ @{$modes{$mode}->{visible_tables}};
+ my $tbl = $tbls[0];
+ if ( @tbls > 1 ) {
+ $tbl = prompt_list(
+ "Choose a table",
+ '',
+ sub { return @tbls },
+ { map { $_ => $tbl_meta{$_}->{capt} } @tbls }
+ );
+ }
+ return $tbl;
+}
+
+sub toggle_aggregate {
+ my ( $tbl ) = @_;
+ $tbl ||= choose_visible_table();
+ return unless $tbl && exists $tbl_meta{$tbl};
+ my $meta = $tbl_meta{$tbl};
+ $meta->{aggregate} ^= 1;
+}
+
+sub choose_filters {
+ my ( $tbl ) = @_;
+ $tbl ||= choose_visible_table();
+ return unless $tbl && exists $tbl_meta{$tbl};
+ my $meta = $tbl_meta{$tbl};
+ $clear_screen_sub->();
+
+ print "Choose filters for $meta->{capt}:\n";
+
+ my $ini = join(' ', @{$meta->{filters}});
+ my $val = prompt_list(
+ 'Choose filters',
+ $ini,
+ sub { return keys %filters },
+ {
+ map { $_ => $filters{$_}->{note} }
+ grep { grep { $tbl eq $_ } @{$filters{$_}->{tbls}} }
+ keys %filters
+ }
+ );
+
+ my @choices = unique(split(/\s+/, $val));
+ foreach my $new ( grep { !exists($filters{$_}) } @choices ) {
+ my $answer = prompt("There is no filter called '$new'. Create it?", undef, 'y');
+ if ( $answer eq 'y' ) {
+ create_new_filter($new, $tbl);
+ }
+ }
+ @choices = grep { exists $filters{$_} } @choices;
+ @choices = grep { grep { $tbl eq $_ } @{$filters{$_}->{tbls}} } @choices;
+ $meta->{filters} = [ @choices ];
+ $meta->{cust}->{filters} = 1;
+}
+
+sub choose_group_cols {
+ my ( $tbl ) = @_;
+ $tbl ||= choose_visible_table();
+ return unless $tbl && exists $tbl_meta{$tbl};
+ $clear_screen_sub->();
+ my $meta = $tbl_meta{$tbl};
+ my $curr = join(', ', @{$meta->{group_by}});
+ my $val = prompt_list(
+ 'Group-by columns',
+ $curr,
+ sub { return keys %{$meta->{cols}} },
+ { map { $_ => $meta->{cols}->{$_}->{label} } keys %{$meta->{cols}} });
+ if ( $curr ne $val ) {
+ $meta->{group_by} = [ grep { exists $meta->{cols}->{$_} } $val =~ m/(\w+)/g ];
+ $meta->{cust}->{group_by} = 1;
+ }
+}
+
+sub choose_sort_cols {
+ my ( $tbl ) = @_;
+ $tbl ||= choose_visible_table();
+ return unless $tbl && exists $tbl_meta{$tbl};
+ $clear_screen_sub->();
+ my $meta = $tbl_meta{$tbl};
+
+ my ( $cols, $hints );
+ if ( $meta->{pivot} ) {
+ $cols = sub { qw(name set_0) };
+ $hints = { name => 'name', set_0 => 'set_0' };
+ }
+ else {
+ $cols = sub { return keys %{$meta->{cols}} };
+ $hints = { map { $_ => $meta->{cols}->{$_}->{label} } keys %{$meta->{cols}} };
+ }
+
+ my $val = prompt_list(
+ 'Sort columns (reverse sort with -col)',
+ $meta->{sort_cols},
+ $cols,
+ $hints );
+ if ( $meta->{sort_cols} ne $val ) {
+ $meta->{sort_cols} = $val;
+ $meta->{cust}->{sort_cols} = 1;
+ $tbl_meta{$tbl}->{sort_func} = make_sort_func($tbl_meta{$tbl});
+ }
+}
+
+# create_new_filter {{{3
+sub create_new_filter {
+ my ( $filter, $tbl ) = @_;
+ $clear_screen_sub->();
+
+ if ( !$filter || $filter =~ m/\W/ ) {
+ print word_wrap("Choose a name for the filter. This name is not displayed, and is only used "
+ . "for internal reference. It can only contain lowercase letters, numbers, and underscores.");
+ print "\n\n";
+ do {
+ $filter = prompt("Enter filter name");
+ } while ( !$filter || $filter =~ m/\W/ );
+ }
+
+ my $completion = sub { keys %{$tbl_meta{$tbl}->{cols}} };
+ my ( $err, $sub, $body );
+ do {
+ $clear_screen_sub->();
+ print word_wrap("A filter is a Perl subroutine that accepts a hashref of columns "
+ . "called \$set, and returns a true value if the filter accepts the row. Example:\n"
+ . " \$set->{active_secs} > 5\n"
+ . "will only allow rows if their active_secs column is greater than 5.");
+ print "\n\n";
+ if ( $err ) {
+ print "There's an error in your filter expression: $err\n\n";
+ }
+ $body = prompt("Enter subroutine body", undef, undef, $completion);
+ ( $sub, $err ) = compile_filter($body);
+ } while ( $err );
+
+ $filters{$filter} = {
+ func => $sub,
+ text => $body,
+ user => 1,
+ name => $filter,
+ note => 'User-defined filter',
+ tbls => [$tbl],
+ };
+}
+
+# get_config_interactive {{{3
+sub get_config_interactive {
+ my $key = shift;
+ $clear_screen_sub->();
+
+ # Print help first.
+ print "Enter a new value for '$key' ($config{$key}->{note}).\n";
+
+ my $current = ref($config{$key}->{val}) ? join(" ", @{$config{$key}->{val}}) : $config{$key}->{val};
+
+ my $new_value = prompt('Enter a value', $config{$key}->{pat}, $current);
+ $config{$key}->{val} = $new_value;
+}
+
+sub edit_current_var_set {
+ my $mode = $config{mode}->{val};
+ my $name = $config{"${mode}_set"}->{val};
+ my $variables = $var_sets{$name}->{text};
+
+ my $new = $variables;
+ do {
+ $clear_screen_sub->();
+ $new = prompt("Enter variables for $name", undef, $variables);
+ } until ( $new );
+
+ if ( $new ne $variables ) {
+ @{$var_sets{$name}}{qw(text user)} = ( $new, 1);
+ }
+}
+
+
+sub choose_var_set {
+ my ( $key ) = @_;
+ $clear_screen_sub->();
+
+ my $new_value = prompt_list(
+ 'Choose a set of values to display, or enter the name of a new one',
+ $config{$key}->{val},
+ sub { return keys %var_sets },
+ { map { $_ => $var_sets{$_}->{text} } keys %var_sets });
+
+ if ( !exists $var_sets{$new_value} ) {
+ add_new_var_set($new_value);
+ }
+
+ $config{$key}->{val} = $new_value if exists $var_sets{$new_value};
+}
+
+sub switch_var_set {
+ my ( $cfg_var, $dir ) = @_;
+ my @var_sets = sort keys %var_sets;
+ my $cur = $config{$cfg_var}->{val};
+ my $pos = grep { $_ lt $cur } @var_sets;
+ my $newpos = ($pos + $dir) % @var_sets;
+ $config{$cfg_var}->{val} = $var_sets[$newpos];
+ $clear_screen_sub->();
+}
+
+# Online configuration and prompting functions {{{2
+
+# edit_stmt_sleep_times {{{3
+sub edit_stmt_sleep_times {
+ $clear_screen_sub->();
+ my $stmt = prompt_list('Specify a statement', '', sub { return sort keys %stmt_maker_for });
+ return unless $stmt && exists $stmt_maker_for{$stmt};
+ $clear_screen_sub->();
+ my $curr_val = $stmt_sleep_time_for{$stmt} || 0;
+ my $new_val = prompt('Specify a sleep delay after calling this SQL', $num_regex, $curr_val);
+ if ( $new_val ) {
+ $stmt_sleep_time_for{$stmt} = $new_val;
+ }
+ else {
+ delete $stmt_sleep_time_for{$stmt};
+ }
+}
+
+# edit_server_groups {{{3
+# Choose which server connections are in a server group. First choose a group,
+# then choose which connections are in it.
+sub edit_server_groups {
+ $clear_screen_sub->();
+ my $mode = $config{mode}->{val};
+ my $group = $modes{$mode}->{server_group};
+ my %curr = %server_groups;
+ my $new = choose_or_create_server_group($group, 'to edit');
+ $clear_screen_sub->();
+ if ( exists $curr{$new} ) {
+ # Don't do this step if the user just created a new server group,
+ # because part of that process was to choose connections.
+ my $cxns = join(' ', @{$server_groups{$new}});
+ my @conns = choose_or_create_connection($cxns, 'for this group');
+ $server_groups{$new} = \@conns;
+ }
+}
+
+# choose_server_groups {{{3
+sub choose_server_groups {
+ $clear_screen_sub->();
+ my $mode = $config{mode}->{val};
+ my $group = $modes{$mode}->{server_group};
+ my $new = choose_or_create_server_group($group, 'for this mode');
+ $modes{$mode}->{server_group} = $new if exists $server_groups{$new};
+}
+
+sub choose_or_create_server_group {
+ my ( $group, $prompt ) = @_;
+ my $new = '';
+
+ my @available = sort keys %server_groups;
+
+ if ( @available ) {
+ print "You can enter the name of a new group to create it.\n";
+
+ $new = prompt_list(
+ "Choose a server group $prompt",
+ $group,
+ sub { return @available },
+ { map { $_ => join(' ', @{$server_groups{$_}}) } @available });
+
+ $new =~ s/\s.*//;
+
+ if ( !exists $server_groups{$new} ) {
+ my $answer = prompt("There is no server group called '$new'. Create it?", undef, "y");
+ if ( $answer eq 'y' ) {
+ add_new_server_group($new);
+ }
+ }
+ }
+ else {
+ $new = add_new_server_group();
+ }
+ return $new;
+}
+
+sub choose_or_create_connection {
+ my ( $cxns, $prompt ) = @_;
+ print "You can enter the name of a new connection to create it.\n";
+
+ my @available = sort keys %connections;
+ my $new_cxns = prompt_list(
+ "Choose connections $prompt",
+ $cxns,
+ sub { return @available },
+ { map { $_ => $connections{$_}->{dsn} } @available });
+
+ my @new = unique(grep { !exists $connections{$_} } split(/\s+/, $new_cxns));
+ foreach my $new ( @new ) {
+ my $answer = prompt("There is no connection called '$new'. Create it?", undef, "y");
+ if ( $answer eq 'y' ) {
+ add_new_dsn($new);
+ }
+ }
+
+ return unique(grep { exists $connections{$_} } split(/\s+/, $new_cxns));
+}
+
+# choose_servers {{{3
+sub choose_servers {
+ $clear_screen_sub->();
+ my $mode = $config{mode}->{val};
+ my $cxns = join(' ', get_connections());
+ my @chosen = choose_or_create_connection($cxns, 'for this mode');
+ $modes{$mode}->{connections} = \@chosen;
+ $modes{$mode}->{server_group} = ''; # Clear this because it overrides {connections}
+}
+
+# display_license {{{3
+sub display_license {
+ $clear_screen_sub->();
+
+ print $innotop_license;
+
+ pause();
+}
+
+# Data-retrieval functions {{{2
+# get_status_info {{{3
+# Get SHOW STATUS and SHOW VARIABLES together.
+sub get_status_info {
+ my @cxns = @_;
+ if ( !$info_gotten{status}++ ) {
+ foreach my $cxn ( @cxns ) {
+ $vars{$cxn}->{$clock} ||= {};
+ my $vars = $vars{$cxn}->{$clock};
+
+ my $sth = do_stmt($cxn, 'SHOW_STATUS') or next;
+ my $res = $sth->fetchall_arrayref();
+ map { $vars->{$_->[0]} = $_->[1] || 0 } @$res;
+
+ # Calculate hi-res uptime and add cxn to the hash. This duplicates get_driver_status,
+ # but it's most important to have consistency.
+ $vars->{Uptime_hires} ||= get_uptime($cxn);
+ $vars->{cxn} = $cxn;
+
+ # Add SHOW VARIABLES to the hash
+ $sth = do_stmt($cxn, 'SHOW_VARIABLES') or next;
+ $res = $sth->fetchall_arrayref();
+ map { $vars->{$_->[0]} = $_->[1] || 0 } @$res;
+ }
+ }
+}
+
+# Chooses a thread for explaining, killing, etc...
+# First arg is a func that can be called in grep.
+sub choose_thread {
+ my ( $grep_cond, $prompt ) = @_;
+
+ # Narrow the list to queries that can be explained.
+ my %thread_for = map {
+ # Eliminate innotop's own threads.
+ $_ => $dbhs{$_}->{dbh} ? $dbhs{$_}->{dbh}->{mysql_thread_id} : 0
+ } keys %connections;
+
+ my @candidates = grep {
+ $_->{id} != $thread_for{$_->{cxn}} && $grep_cond->($_)
+ } @current_queries;
+ return unless @candidates;
+
+ # Find out which server.
+ my @cxns = unique map { $_->{cxn} } @candidates;
+ my ( $cxn ) = select_cxn('On which server', @cxns);
+ return unless $cxn && exists($connections{$cxn});
+
+ # Re-filter the list of candidates to only those on this server
+ @candidates = grep { $_->{cxn} eq $cxn } @candidates;
+
+ # Find out which thread to do.
+ my $info;
+ if ( @candidates > 1 ) {
+
+ # Sort longest-active first, then longest-idle.
+ my $sort_func = sub {
+ my ( $a, $b ) = @_;
+ return $a->{query} && !$b->{query} ? 1
+ : $b->{query} && !$a->{query} ? -1
+ : ($a->{time} || 0) <=> ($b->{time} || 0);
+ };
+ my @threads = map { $_->{id} } reverse sort { $sort_func->($a, $b) } @candidates;
+
+ print "\n";
+ my $thread = prompt_list($prompt,
+ $threads[0],
+ sub { return @threads });
+ return unless $thread && $thread =~ m/$int_regex/;
+
+ # Find the info hash of that query on that server.
+ ( $info ) = grep { $thread == $_->{id} } @candidates;
+ }
+ else {
+ $info = $candidates[0];
+ }
+ return $info;
+}
+
+# analyze_query {{{3
+# Allows the user to show fulltext, explain, show optimized...
+sub analyze_query {
+ my ( $action ) = @_;
+
+ my $info = choose_thread(
+ sub { $_[0]->{query} },
+ 'Select a thread to analyze',
+ );
+ return unless $info;
+
+ my %actions = (
+ e => \&display_explain,
+ f => \&show_full_query,
+ o => \&show_optimized_query,
+ );
+ do {
+ $actions{$action}->($info);
+ print "\n";
+ $action = pause('Press e to explain, f for full query, o for optimized query');
+ } while ( exists($actions{$action}) );
+}
+
+# inc {{{3
+# Returns the difference between two sets of variables/status/innodb stuff.
+sub inc {
+ my ( $offset, $cxn ) = @_;
+ my $vars = $vars{$cxn};
+ if ( $offset < 0 ) {
+ return $vars->{$clock};
+ }
+ elsif ( exists $vars{$clock - $offset} && !exists $vars->{$clock - $offset - 1} ) {
+ return $vars->{$clock - $offset};
+ }
+ my $cur = $vars->{$clock - $offset};
+ my $pre = $vars->{$clock - $offset - 1};
+ return {
+ # Numeric variables get subtracted, non-numeric get passed straight through.
+ map {
+ $_ =>
+ ( (defined $cur->{$_} && $cur->{$_} =~ m/$num_regex/)
+ ? $cur->{$_} - ($pre->{$_} || 0)
+ : $cur->{$_} )
+ } keys %{$cur}
+ };
+}
+
+# extract_values {{{3
+# Arguments are a set of values (which may be incremental, derived from
+# current and previous), current, and previous values.
+# TODO: there are a few places that don't remember prev set so can't pass it.
+sub extract_values {
+ my ( $set, $cur, $pre, $tbl ) = @_;
+
+ # Hook in event listeners
+ foreach my $listener ( @{$event_listener_for{extract_values}} ) {
+ $listener->extract_values($set, $cur, $pre, $tbl);
+ }
+
+ my $result = {};
+ my $meta = $tbl_meta{$tbl};
+ my $cols = $meta->{cols};
+ foreach my $key ( keys %$cols ) {
+ my $info = $cols->{$key}
+ or die "Column '$key' doesn't exist in $tbl";
+ die "No func defined for '$key' in $tbl"
+ unless $info->{func};
+ eval {
+ $result->{$key} = $info->{func}->($set, $cur, $pre)
+ };
+ if ( $EVAL_ERROR ) {
+ if ( $config{debug}->{val} ) {
+ die $EVAL_ERROR;
+ }
+ $result->{$key} = $info->{num} ? 0 : '';
+ }
+ }
+ return $result;
+}
+
+# get_full_processlist {{{3
+sub get_full_processlist {
+ my @cxns = @_;
+ my @result;
+ foreach my $cxn ( @cxns ) {
+ my $stmt = do_stmt($cxn, 'PROCESSLIST') or next;
+ my $arr = $stmt->fetchall_arrayref({});
+ push @result, map { $_->{cxn} = $cxn; $_ } @$arr;
+ }
+ return @result;
+}
+
+# get_open_tables {{{3
+sub get_open_tables {
+ my @cxns = @_;
+ my @result;
+ foreach my $cxn ( @cxns ) {
+ my $stmt = do_stmt($cxn, 'OPEN_TABLES') or next;
+ my $arr = $stmt->fetchall_arrayref({});
+ push @result, map { $_->{cxn} = $cxn; $_ } @$arr;
+ }
+ return @result;
+}
+
+# get_innodb_status {{{3
+sub get_innodb_status {
+ my ( $cxns, $addl_sections ) = @_;
+ if ( !$config{skip_innodb}->{val} && !$info_gotten{innodb_status}++ ) {
+
+ # Determine which sections need to be parsed
+ my %sections_required =
+ map { $tbl_meta{$_}->{innodb} => 1 }
+ grep { $_ && $tbl_meta{$_}->{innodb} }
+ get_visible_tables();
+
+ # Add in any other sections the caller requested.
+ foreach my $sec ( @$addl_sections ) {
+ $sections_required{$sec} = 1;
+ }
+
+ foreach my $cxn ( @$cxns ) {
+ my $innodb_status_text;
+
+ if ( $file ) { # Try to fetch status text from the file.
+ my @stat = stat($file);
+
+ # Initialize the file.
+ if ( !$file_mtime ) {
+ # Initialize to 130k from the end of the file (because the limit
+ # on the size of innodb status is 128k even with Google's patches)
+ # and try to grab the last status from the file.
+ sysseek($file, (-128 * 1_024), 2);
+ }
+
+ # Read from the file.
+ my $buffer;
+ if ( !$file_mtime || $file_mtime != $stat[9] ) {
+ $file_data = '';
+ while ( sysread($file, $buffer, 4096) ) {
+ $file_data .= $buffer;
+ }
+ $file_mtime = $stat[9];
+ }
+
+ # Delete everything but the last InnoDB status text from the file.
+ $file_data =~ s/\A.*(?=^=====================================\n...... ........ INNODB MONITOR OUTPUT)//ms;
+ $innodb_status_text = $file_data;
+ }
+
+ else {
+ my $stmt = do_stmt($cxn, 'INNODB_STATUS') or next;
+ $innodb_status_text = $stmt->fetchrow_hashref()->{status};
+ }
+
+ next unless $innodb_status_text
+ && substr($innodb_status_text, 0, 100) =~ m/INNODB MONITOR OUTPUT/;
+
+ # Parse and merge into %vars storage
+ my %innodb_status = (
+ $innodb_parser->get_status_hash(
+ $innodb_status_text,
+ $config{debug}->{val},
+ \%sections_required,
+ 0, # don't parse full lock information
+ )
+ );
+ if ( !$innodb_status{IB_got_all} && $config{auto_wipe_dl}->{val} ) {
+ clear_deadlock($cxn);
+ }
+
+ # Merge using a hash slice, which is the fastest way
+ $vars{$cxn}->{$clock} ||= {};
+ my $hash = $vars{$cxn}->{$clock};
+ @{$hash}{ keys %innodb_status } = values %innodb_status;
+ $hash->{cxn} = $cxn;
+ $hash->{Uptime_hires} ||= get_uptime($cxn);
+ }
+ }
+}
+
+# clear_deadlock {{{3
+sub clear_deadlock {
+ my ( $cxn ) = @_;
+ return if $clearing_deadlocks++;
+ my $tbl = $connections{$cxn}->{dl_table};
+ return unless $tbl;
+
+ eval {
+ # Set up the table for creating a deadlock.
+ my $engine = version_ge($dbhs{$cxn}->{dbh}, '4.1.2') ? 'engine' : 'type';
+ return unless do_query($cxn, "drop table if exists $tbl");
+ return unless do_query($cxn, "create table $tbl(a int) $engine=innodb");
+ return unless do_query($cxn, "delete from $tbl");
+ return unless do_query($cxn, "insert into $tbl(a) values(0), (1)");
+ return unless do_query($cxn, "commit"); # Or the children will block against the parent
+
+ # Fork off two children to deadlock against each other.
+ my %children;
+ foreach my $child ( 0..1 ) {
+ my $pid = fork();
+ if ( defined($pid) && $pid == 0 ) { # I am a child
+ deadlock_thread( $child, $tbl, $cxn );
+ }
+ elsif ( !defined($pid) ) {
+ die("Unable to fork for clearing deadlocks!\n");
+ }
+ # I already exited if I'm a child, so I'm the parent.
+ $children{$child} = $pid;
+ }
+
+ # Wait for the children to exit.
+ foreach my $child ( keys %children ) {
+ my $pid = waitpid($children{$child}, 0);
+ }
+
+ # Clean up.
+ do_query($cxn, "drop table $tbl");
+ };
+ if ( $EVAL_ERROR ) {
+ print $EVAL_ERROR;
+ pause();
+ }
+
+ $clearing_deadlocks = 0;
+}
+
+sub get_master_logs {
+ my @cxns = @_;
+ my @result;
+ if ( !$info_gotten{master_logs}++ ) {
+ foreach my $cxn ( @cxns ) {
+ my $stmt = do_stmt($cxn, 'SHOW_MASTER_LOGS') or next;
+ push @result, @{$stmt->fetchall_arrayref({})};
+ }
+ }
+ return @result;
+}
+
+# get_master_slave_status {{{3
+sub get_master_slave_status {
+ my @cxns = @_;
+ if ( !$info_gotten{replication_status}++ ) {
+ foreach my $cxn ( @cxns ) {
+ $vars{$cxn}->{$clock} ||= {};
+ my $vars = $vars{$cxn}->{$clock};
+ $vars->{cxn} = $cxn;
+
+ my $stmt = do_stmt($cxn, 'SHOW_MASTER_STATUS') or next;
+ my $res = $stmt->fetchall_arrayref({})->[0];
+ @{$vars}{ keys %$res } = values %$res;
+ $stmt = do_stmt($cxn, 'SHOW_SLAVE_STATUS') or next;
+ $res = $stmt->fetchall_arrayref({})->[0];
+ @{$vars}{ keys %$res } = values %$res;
+ $vars->{Uptime_hires} ||= get_uptime($cxn);
+ }
+ }
+}
+
+sub is_func {
+ my ( $word ) = @_;
+ return defined(&$word)
+ || eval "my \$x= sub { $word }; 1"
+ || $EVAL_ERROR !~ m/^Bareword/;
+}
+
+# Documentation {{{1
+# ############################################################################
+# I put this last as per the Dog book.
+# ############################################################################
+=pod
+
+=head1 NAME
+
+innotop - MySQL and InnoDB transaction/status monitor.
+
+=head1 SYNOPSIS
+
+To monitor servers normally:
+
+ innotop
+
+To monitor InnoDB status information from a file:
+
+ innotop /var/log/mysql/mysqld.err
+
+To run innotop non-interactively in a pipe-and-filter configuration:
+
+ innotop --count 5 -d 1 -n
+
+To monitor a database on another system using a particular username and password:
+
+ innotop -u <username> -p <password> -h <hostname>
+
+=head1 DESCRIPTION
+
+innotop monitors MySQL servers. Each of its modes shows you a different aspect
+of what's happening in the server. For example, there's a mode for monitoring
+replication, one for queries, and one for transactions. innotop refreshes its
+data periodically, so you see an updating view.
+
+innotop has lots of features for power users, but you can start and run it with
+virtually no configuration. If you're just getting started, see
+L<"QUICK-START">. Press '?' at any time while running innotop for
+context-sensitive help.
+
+=head1 QUICK-START
+
+To start innotop, open a terminal or command prompt. If you have installed
+innotop on your system, you should be able to just type "innotop" and press
+Enter; otherwise, you will need to change to innotop's directory and type "perl
+innotop".
+
+With no options specified, innotop will attempt to connect to a MySQL server on
+localhost using mysql_read_default_group=client for other connection
+parameters. If you need to specify a different username and password, use the
+-u and -p options, respectively. To monitor a MySQL database on another
+host, use the -h option.
+
+After you've connected, innotop should show you something like the following:
+
+ [RO] Query List (? for help) localhost, 01:11:19, 449.44 QPS, 14/7/163 con/run
+
+ CXN When Load QPS Slow QCacheHit KCacheHit BpsIn BpsOut
+ localhost Total 0.00 1.07k 697 0.00% 98.17% 476.83k 242.83k
+
+ CXN Cmd ID User Host DB Time Query
+ localhost Query 766446598 test 10.0.0.1 foo 00:02 INSERT INTO table (
+
+
+(This sample is truncated at the right so it will fit on a terminal when running
+'man innotop')
+
+If your server is busy, you'll see more output. Notice the first line on the
+screen, which tells you that readonly is set to true ([RO]), what mode you're
+in and what server you're connected to. You can change to other modes with
+keystrokes; press 'T' to switch to a list of InnoDB transactions, for example.
+
+Press the '?' key to see what keys are active in the current mode. You can
+press any of these keys and innotop will either take the requested action or
+prompt you for more input. If your system has Term::ReadLine support, you can
+use TAB and other keys to auto-complete and edit input.
+
+To quit innotop, press the 'q' key.
+
+=head1 OPTIONS
+
+innotop is mostly configured via its configuration file, but some of the
+configuration options can come from the command line. You can also specify a
+file to monitor for InnoDB status output; see L<"MONITORING A FILE"> for more
+details.
+
+You can negate some options by prefixing the option name with --no. For
+example, --noinc (or --no-inc) negates L<"--inc">.
+
+=over
+
+=item --color
+
+Enable or disable terminal coloring. Corresponds to the L<"color"> config file
+setting.
+
+=item --config
+
+Specifies a configuration file to read. This option is non-sticky, that is to
+say it does not persist to the configuration file itself.
+
+=item --count
+
+Refresh only the specified number of times (ticks) before exiting. Each refresh
+is a pause for L<"interval"> seconds, followed by requesting data from MySQL
+connections and printing it to the terminal.
+
+=item --delay
+
+Specifies the amount of time to pause between ticks (refreshes). Corresponds to
+the configuration option L<"interval">.
+
+=item --help
+
+Print a summary of command-line usage and exit.
+
+=item --host
+
+Host to connect to.
+
+=item --inc
+
+Specifies whether innotop should display absolute numbers or relative numbers
+(offsets from their previous values). Corresponds to the configuration option
+L<"status_inc">.
+
+=item --mode
+
+Specifies the mode in which innotop should start. Corresponds to the
+configuration option L<"mode">.
+
+=item --nonint
+
+Enable non-interactive operation. See L<"NON-INTERACTIVE OPERATION"> for more.
+
+=item --password
+
+Password to use for connection.
+
+=item --port
+
+Port to use for connection.
+
+=item --skipcentral
+
+Don't read the central configuration file.
+
+=item --user
+
+User to use for connection.
+
+=item --version
+
+Output version information and exit.
+
+=item --write
+
+Sets the configuration option L<"readonly"> to 0, making innotop write the
+running configuration to ~/.innotop/innotop.conf on exit, if no configuration
+file was loaded at start-up.
+
+=back
+
+=head1 HOTKEYS
+
+innotop is interactive, and you control it with key-presses.
+
+=over
+
+=item *
+
+Uppercase keys switch between modes.
+
+=item *
+
+Lowercase keys initiate some action within the current mode.
+
+=item *
+
+Other keys do something special like change configuration or show the
+innotop license.
+
+=back
+
+Press '?' at any time to see the currently active keys and what they do.
+
+=head1 MODES
+
+Each of innotop's modes retrieves and displays a particular type of data from
+the servers you're monitoring. You switch between modes with uppercase keys.
+The following is a brief description of each mode, in alphabetical order. To
+switch to the mode, press the key listed in front of its heading in the
+following list:
+
+=over
+
+=item B: InnoDB Buffers
+
+This mode displays information about the InnoDB buffer pool, page statistics,
+insert buffer, and adaptive hash index. The data comes from SHOW INNODB STATUS.
+
+This mode contains the L<"buffer_pool">, L<"page_statistics">,
+L<"insert_buffers">, and L<"adaptive_hash_index"> tables by default.
+
+=item C: Command Summary
+
+This mode is similar to mytop's Command Summary mode. It shows the
+L<"cmd_summary"> table, which looks something like the following:
+
+ Command Summary (? for help) localhost, 25+07:16:43, 2.45 QPS, 3 thd, 5.0.40
+ _____________________ Command Summary _____________________
+ Name Value Pct Last Incr Pct
+ Select_scan 3244858 69.89% 2 100.00%
+ Select_range 1354177 29.17% 0 0.00%
+ Select_full_join 39479 0.85% 0 0.00%
+ Select_full_range_join 4097 0.09% 0 0.00%
+ Select_range_check 0 0.00% 0 0.00%
+
+The command summary table is built by extracting variables from
+L<"STATUS_VARIABLES">. The variables must be numeric and must match the prefix
+given by the L<"cmd_filter"> configuration variable. The variables are then
+sorted by value descending and compared to the last variable, as shown above.
+The percentage columns are percentage of the total of all variables in the
+table, so you can see the relative weight of the variables.
+
+The example shows what you see if the prefix is "Select_". The default
+prefix is "Com_". You can choose a prefix with the 's' key.
+
+It's rather like running SHOW VARIABLES LIKE "prefix%" with memory and
+nice formatting.
+
+Values are aggregated across all servers. The Pct columns are not correctly
+aggregated across multiple servers. This is a known limitation of the grouping
+algorithm that may be fixed in the future.
+
+=item D: InnoDB Deadlocks
+
+This mode shows the transactions involved in the last InnoDB deadlock. A second
+table shows the locks each transaction held and waited for. A deadlock is
+caused by a cycle in the waits-for graph, so there should be two locks held and
+one waited for unless the deadlock information is truncated.
+
+InnoDB puts deadlock information before some other information in the SHOW
+INNODB STATUS output. If there are a lot of locks, the deadlock information can
+grow very large, and there is a limit on the size of the SHOW INNODB
+STATUS output. A large deadlock can fill the entire output, or even be
+truncated, and prevent you from seeing other information at all. If you are
+running innotop in another mode, for example T mode, and suddenly you don't see
+anything, you might want to check and see if a deadlock has wiped out the data
+you need.
+
+If it has, you can create a small deadlock to replace the large one. Use the
+'w' key to 'wipe' the large deadlock with a small one. This will not work
+unless you have defined a deadlock table for the connection (see L<"SERVER
+CONNECTIONS">).
+
+You can also configure innotop to automatically detect when a large deadlock
+needs to be replaced with a small one (see L<"auto_wipe_dl">).
+
+This mode displays the L<"deadlock_transactions"> and L<"deadlock_locks"> tables
+by default.
+
+=item F: InnoDB Foreign Key Errors
+
+This mode shows the last InnoDB foreign key error information, such as the
+table where it happened, when and who and what query caused it, and so on.
+
+InnoDB has a huge variety of foreign key error messages, and many of them are
+just hard to parse. innotop doesn't always do the best job here, but there's
+so much code devoted to parsing this messy, unparseable output that innotop is
+likely never to be perfect in this regard. If innotop doesn't show you what
+you need to see, just look at the status text directly.
+
+This mode displays the L<"fk_error"> table by default.
+
+=item I: InnoDB I/O Info
+
+This mode shows InnoDB's I/O statistics, including the I/O threads, pending I/O,
+file I/O miscellaneous, and log statistics. It displays the L<"io_threads">,
+L<"pending_io">, L<"file_io_misc">, and L<"log_statistics"> tables by default.
+
+=item L: Locks
+
+This mode shows information about current locks. At the moment only InnoDB
+locks are supported, and by default you'll only see locks for which transactions
+are waiting. This information comes from the TRANSACTIONS section of the InnoDB
+status text. If you have a very busy server, you may have frequent lock waits;
+it helps to be able to see which tables and indexes are the "hot spot" for
+locks. If your server is running pretty well, this mode should show nothing.
+
+You can configure MySQL and innotop to monitor not only locks for which a
+transaction is waiting, but those currently held, too. You can do this with the
+InnoDB Lock Monitor (L<http://dev.mysql.com/doc/en/innodb-monitor.html>). It's
+not documented in the MySQL manual, but creating the lock monitor with the
+following statement also affects the output of SHOW INNODB STATUS, which innotop
+uses:
+
+ CREATE TABLE innodb_lock_monitor(a int) ENGINE=INNODB;
+
+This causes InnoDB to print its output to the MySQL file every 16 seconds or so,
+as stated in the manual, but it also makes the normal SHOW INNODB STATUS output
+include lock information, which innotop can parse and display (that's the
+undocumented feature).
+
+This means you can do what may have seemed impossible: to a limited extent
+(InnoDB truncates some information in the output), you can see which transaction
+holds the locks something else is waiting for. You can also enable and disable
+the InnoDB Lock Monitor with the key mappings in this mode.
+
+This mode displays the L<"innodb_locks"> table by default. Here's a sample of
+the screen when one connection is waiting for locks another connection holds:
+
+ _________________________________ InnoDB Locks __________________________
+ CXN ID Type Waiting Wait Active Mode DB Table Index
+ localhost 12 RECORD 1 00:10 00:10 X test t1 PRIMARY
+ localhost 12 TABLE 0 00:10 00:10 IX test t1
+ localhost 12 RECORD 1 00:10 00:10 X test t1 PRIMARY
+ localhost 11 TABLE 0 00:00 00:25 IX test t1
+ localhost 11 RECORD 0 00:00 00:25 X test t1 PRIMARY
+
+You can see the first connection, ID 12, is waiting for a lock on the PRIMARY
+key on test.t1, and has been waiting for 10 seconds. The second connection
+isn't waiting, because the Waiting column is 0, but it holds locks on the same
+index. That tells you connection 11 is blocking connection 12.
+
+=item M: Master/Slave Replication Status
+
+This mode shows the output of SHOW SLAVE STATUS and SHOW MASTER STATUS in three
+tables. The first two divide the slave's status into SQL and I/O thread status,
+and the last shows master status. Filters are applied to eliminate non-slave
+servers from the slave tables, and non-master servers from the master table.
+
+This mode displays the L<"slave_sql_status">, L<"slave_io_status">, and
+L<"master_status"> tables by default.
+
+=item O: Open Tables
+
+This section comes from MySQL's SHOW OPEN TABLES command. By default it is
+filtered to show tables which are in use by one or more queries, so you can
+get a quick look at which tables are 'hot'. You can use this to guess which
+tables might be locked implicitly.
+
+This mode displays the L<"open_tables"> mode by default.
+
+=item Q: Query List
+
+This mode displays the output from SHOW FULL PROCESSLIST, much like B<mytop>'s
+query list mode. This mode does B<not> show InnoDB-related information. This
+is probably one of the most useful modes for general usage.
+
+There is an informative header that shows general status information about
+your server. You can toggle it on and off with the 'h' key. By default,
+innotop hides inactive processes and its own process. You can toggle these on
+and off with the 'i' and 'a' keys.
+
+You can EXPLAIN a query from this mode with the 'e' key. This displays the
+query's full text, the results of EXPLAIN, and in newer MySQL versions, even
+the optimized query resulting from EXPLAIN EXTENDED. innotop also tries to
+rewrite certain queries to make them EXPLAIN-able. For example, INSERT/SELECT
+statements are rewritable.
+
+This mode displays the L<"q_header"> and L<"processlist"> tables by default.
+
+=item R: InnoDB Row Operations and Semaphores
+
+This mode shows InnoDB row operations, row operation miscellaneous, semaphores,
+and information from the wait array. It displays the L<"row_operations">,
+L<"row_operation_misc">, L<"semaphores">, and L<"wait_array"> tables by default.
+
+=item S: Variables & Status
+
+This mode calculates statistics, such as queries per second, and prints them out
+in several different styles. You can show absolute values, or incremental values
+between ticks.
+
+You can switch between the views by pressing a key. The 's' key prints a
+single line each time the screen updates, in the style of B<vmstat>. The 'g'
+key changes the view to a graph of the same numbers, sort of like B<tload>.
+The 'v' key changes the view to a pivoted table of variable names on the left,
+with successive updates scrolling across the screen from left to right. You can
+choose how many updates to put on the screen with the L<"num_status_sets">
+configuration variable.
+
+Headers may be abbreviated to fit on the screen in interactive operation. You
+choose which variables to display with the 'c' key, which selects from
+predefined sets, or lets you create your own sets. You can edit the current set
+with the 'e' key.
+
+This mode doesn't really display any tables like other modes. Instead, it uses
+a table definition to extract and format the data, but it then transforms the
+result in special ways before outputting it. It uses the L<"var_status"> table
+definition for this.
+
+=item T: InnoDB Transactions
+
+This mode shows transactions from the InnoDB monitor's output, in B<top>-like
+format. This mode is the reason I wrote innotop.
+
+You can kill queries or processes with the 'k' and 'x' keys, and EXPLAIN a query
+with the 'e' or 'f' keys. InnoDB doesn't print the full query in transactions,
+so explaining may not work right if the query is truncated.
+
+The informational header can be toggled on and off with the 'h' key. By
+default, innotop hides inactive transactions and its own transaction. You can
+toggle this on and off with the 'i' and 'a' keys.
+
+This mode displays the L<"t_header"> and L<"innodb_transactions"> tables by
+default.
+
+=back
+
+=head1 INNOTOP STATUS
+
+The first line innotop displays is a "status bar" of sorts. What it contains
+depends on the mode you're in, and what servers you're monitoring. The first
+few words are always [RO] (if readonly is set to 1), the innotop mode, such as
+"InnoDB Txns" for T mode, followed by a reminder to press '?' for help at any
+time.
+
+=head2 ONE SERVER
+
+The simplest case is when you're monitoring a single server. In this case, the
+name of the connection is next on the status line. This is the name you gave
+when you created the connection -- most likely the MySQL server's hostname.
+This is followed by the server's uptime.
+
+If you're in an InnoDB mode, such as T or B, the next word is "InnoDB" followed
+by some information about the SHOW INNODB STATUS output used to render the
+screen. The first word is the number of seconds since the last SHOW INNODB
+STATUS, which InnoDB uses to calculate some per-second statistics. The next is
+a smiley face indicating whether the InnoDB output is truncated. If the smiley
+face is a :-), all is well; there is no truncation. A :^| means the transaction
+list is so long, InnoDB has only printed out some of the transactions. Finally,
+a frown :-( means the output is incomplete, which is probably due to a deadlock
+printing too much lock information (see L<"D: InnoDB Deadlocks">).
+
+The next two words indicate the server's queries per second (QPS) and how many
+threads (connections) exist. Finally, the server's version number is the last
+thing on the line.
+
+=head2 MULTIPLE SERVERS
+
+If you are monitoring multiple servers (see L<"SERVER CONNECTIONS">), the status
+line does not show any details about individual servers. Instead, it shows the
+names of the connections that are active. Again, these are connection names you
+specified, which are likely to be the server's hostname. A connection that has
+an error is prefixed with an exclamation point.
+
+If you are monitoring a group of servers (see L<"SERVER GROUPS">), the status
+line shows the name of the group. If any connection in the group has an
+error, the group's name is followed by the fraction of the connections that
+don't have errors.
+
+See L<"ERROR HANDLING"> for more details about innotop's error handling.
+
+=head2 MONITORING A FILE
+
+If you give a filename on the command line, innotop will not connect to ANY
+servers at all. It will watch the specified file for InnoDB status output and
+use that as its data source. It will always show a single connection called
+'file'. And since it can't connect to a server, it can't determine how long the
+server it's monitoring has been up; so it calculates the server's uptime as time
+since innotop started running.
+
+=head1 SERVER ADMINISTRATION
+
+While innotop is primarily a monitor that lets you watch and analyze your
+servers, it can also send commands to servers. The most frequently useful
+commands are killing queries and stopping or starting slaves.
+
+You can kill a connection, or in newer versions of MySQL kill a query but not a
+connection, from L<"Q: Query List"> and L<"T: InnoDB Transactions"> modes.
+Press 'k' to issue a KILL command, or 'x' to issue a KILL QUERY command.
+innotop will prompt you for the server and/or connection ID to kill (innotop
+does not prompt you if there is only one possible choice for any input).
+innotop pre-selects the longest-running query, or the oldest connection.
+Confirm the command with 'y'.
+
+In L<"M: Master/Slave Replication Status"> mode, you can start and stop slaves
+with the 'a' and 'o' keys, respectively. You can send these commands to many
+slaves at once. innotop fills in a default command of START SLAVE or STOP SLAVE
+for you, but you can actually edit the command and send anything you wish, such
+as SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1 to make the slave skip one binlog event
+when it starts.
+
+You can also ask innotop to calculate the earliest binlog in use by any slave
+and issue a PURGE MASTER LOGS on the master. Use the 'b' key for this. innotop
+will prompt you for a master to run the command on, then prompt you for the
+connection names of that master's slaves (there is no way for innotop to
+determine this reliably itself). innotop will find the minimum binlog in use by
+these slave connections and suggest it as the argument to PURGE MASTER LOGS.
+
+=head1 SERVER CONNECTIONS
+
+When you create a server connection using '@', innotop asks you for a series of
+inputs, as follows:
+
+=over
+
+=item DSN
+
+A DSN is a Data Source Name, which is the initial argument passed to the DBI
+module for connecting to a server. It is usually of the form
+
+ DBI:mysql:;mysql_read_default_group=mysql;host=HOSTNAME
+
+Since this DSN is passed to the DBD::mysql driver, you should read the driver's
+documentation at L<"http://search.cpan.org/dist/DBD-mysql/lib/DBD/mysql.pm"> for
+the exact details on all the options you can pass the driver in the DSN. You
+can read more about DBI at L<http://dbi.perl.org/docs/>, and especially at
+L<http://search.cpan.org/~timb/DBI/DBI.pm>.
+
+The mysql_read_default_group=mysql option lets the DBD driver read your MySQL
+options files, such as ~/.my.cnf on UNIX-ish systems. You can use this to avoid
+specifying a username or password for the connection.
+
+=item InnoDB Deadlock Table
+
+This optional item tells innotop a table name it can use to deliberately create
+a small deadlock (see L<"D: InnoDB Deadlocks">). If you specify this option,
+you just need to be sure the table doesn't exist, and that innotop can create
+and drop the table with the InnoDB storage engine. You can safely omit or just
+accept the default if you don't intend to use this.
+
+=item Username
+
+innotop will ask you if you want to specify a username. If you say 'y', it will
+then prompt you for a user name. If you have a MySQL option file that specifies
+your username, you don't have to specify a username.
+
+The username defaults to your login name on the system you're running innotop on.
+
+=item Password
+
+innotop will ask you if you want to specify a password. Like the username, the
+password is optional, but there's an additional prompt that asks if you want to
+save the password in the innotop configuration file. If you don't save it in
+the configuration file, innotop will prompt you for a password each time it
+starts. Passwords in the innotop configuration file are saved in plain text,
+not encrypted in any way.
+
+=back
+
+Once you finish answering these questions, you should be connected to a server.
+But innotop isn't limited to monitoring a single server; you can define many
+server connections and switch between them by pressing the '@' key. See
+L<"SWITCHING BETWEEN CONNECTIONS">.
+
+=head1 SERVER GROUPS
+
+If you have multiple MySQL instances, you can put them into named groups, such
+as 'all', 'masters', and 'slaves', which innotop can monitor all together.
+
+You can choose which group to monitor with the '#' key, and you can press the
+TAB key to switch to the next group. If you're not currently monitoring a
+group, pressing TAB selects the first group.
+
+To create a group, press the '#' key and type the name of your new group, then
+type the names of the connections you want the group to contain.
+
+=head1 SWITCHING BETWEEN CONNECTIONS
+
+innotop lets you quickly switch which servers you're monitoring. The most basic
+way is by pressing the '@' key and typing the name(s) of the connection(s) you
+want to use. This setting is per-mode, so you can monitor different connections
+in each mode, and innotop remembers which connections you choose.
+
+You can quickly switch to the 'next' connection in alphabetical order with the
+'n' key. If you're monitoring a server group (see L<"SERVER GROUPS">) this will
+switch to the first connection.
+
+You can also type many connection names, and innotop will fetch and display data
+from them all. Just separate the connection names with spaces, for example
+"server1 server2." Again, if you type the name of a connection that doesn't
+exist, innotop will prompt you for connection information and create the
+connection.
+
+Another way to monitor multiple connections at once is with server groups. You
+can use the TAB key to switch to the 'next' group in alphabetical order, or if
+you're not monitoring any groups, TAB will switch to the first group.
+
+innotop does not fetch data in parallel from connections, so if you are
+monitoring a large group or many connections, you may notice increased delay
+between ticks.
+
+When you monitor more than one connection, innotop's status bar changes. See
+L<"INNOTOP STATUS">.
+
+=head1 ERROR HANDLING
+
+Error handling is not that important when monitoring a single connection, but is
+crucial when you have many active connections. A crashed server or lost
+connection should not crash innotop. As a result, innotop will continue to run
+even when there is an error; it just won't display any information from the
+connection that had an error. Because of this, innotop's behavior might confuse
+you. It's a feature, not a bug!
+
+innotop does not continue to query connections that have errors, because they
+may slow innotop and make it hard to use, especially if the error is a problem
+connecting and causes a long time-out. Instead, innotop retries the connection
+occasionally to see if the error still exists. If so, it will wait until some
+point in the future. The wait time increases in ticks as the Fibonacci series,
+so it tries less frequently as time passes.
+
+Since errors might only happen in certain modes because of the SQL commands
+issued in those modes, innotop keeps track of which mode caused the error. If
+you switch to a different mode, innotop will retry the connection instead of
+waiting.
+
+By default innotop will display the problem in red text at the bottom of the
+first table on the screen. You can disable this behavior with the
+L<"show_cxn_errors_in_tbl"> configuration option, which is enabled by default.
+If the L<"debug"> option is enabled, innotop will display the error at the
+bottom of every table, not just the first. And if L<"show_cxn_errors"> is
+enabled, innotop will print the error text to STDOUT as well. Error messages
+might only display in the mode that caused the error, depending on the mode and
+whether innotop is avoiding querying that connection.
+
+=head1 NON-INTERACTIVE OPERATION
+
+You can run innotop in non-interactive mode, in which case it is entirely
+controlled from the configuration file and command-line options. To start
+innotop in non-interactive mode, give the L"<--nonint"> command-line option.
+This changes innotop's behavior in the following ways:
+
+=over
+
+=item *
+
+Certain Perl modules are not loaded. Term::Readline is not loaded, since
+innotop doesn't prompt interactively. Term::ANSIColor and Win32::Console::ANSI
+modules are not loaded. Term::ReadKey is still used, since innotop may have to
+prompt for connection passwords when starting up.
+
+=item *
+
+innotop does not clear the screen after each tick.
+
+=item *
+
+innotop does not persist any changes to the configuration file.
+
+=item *
+
+If L<"--count"> is given and innotop is in incremental mode (see L<"status_inc">
+and L<"--inc">), innotop actually refreshes one more time than specified so it
+can print incremental statistics. This suppresses output during the first
+tick, so innotop may appear to hang.
+
+=item *
+
+innotop only displays the first table in each mode. This is so the output can
+be easily processed with other command-line utilities such as awk and sed. To
+change which tables display in each mode, see L<"TABLES">. Since L<"Q: Query
+List"> mode is so important, innotop automatically disables the L<"q_header">
+table. This ensures you'll see the L<"processlist"> table, even if you have
+innotop configured to show the q_header table during interactive operation.
+Similarly, in L<"T: InnoDB Transactions"> mode, the L<"t_header"> table is
+suppressed so you see only the L<"innodb_transactions"> table.
+
+=item *
+
+All output is tab-separated instead of being column-aligned with whitespace, and
+innotop prints the full contents of each table instead of only printing one
+screenful at a time.
+
+=item *
+
+innotop only prints column headers once instead of every tick (see
+L<"hide_hdr">). innotop does not print table captions (see
+L<"display_table_captions">). innotop ensures there are no empty lines in the
+output.
+
+=item *
+
+innotop does not honor the L<"shorten"> transformation, which normally shortens
+some numbers to human-readable formats.
+
+=item *
+
+innotop does not print a status line (see L<"INNOTOP STATUS">).
+
+=back
+
+=head1 CONFIGURING
+
+Nearly everything about innotop is configurable. Most things are possible to
+change with built-in commands, but you can also edit the configuration file.
+
+While running innotop, press the '$' key to bring up the configuration editing
+dialog. Press another key to select the type of data you want to edit:
+
+=over
+
+=item S: Statement Sleep Times
+
+Edits SQL statement sleep delays, which make innotop pause for the specified
+amount of time after executing a statement. See L<"SQL STATEMENTS"> for a
+definition of each statement and what it does. By default innotop does not
+delay after any statements.
+
+This feature is included so you can customize the side-effects caused by
+monitoring your server. You may not see any effects, but some innotop users
+have noticed that certain MySQL versions under very high load with InnoDB
+enabled take longer than usual to execute SHOW GLOBAL STATUS. If innotop calls
+SHOW FULL PROCESSLIST immediately afterward, the processlist contains more
+queries than the machine actually averages at any given moment. Configuring
+innotop to pause briefly after calling SHOW GLOBAL STATUS alleviates this
+effect.
+
+Sleep times are stored in the L<"stmt_sleep_times"> section of the configuration
+file. Fractional-second sleeps are supported, subject to your hardware's
+limitations.
+
+=item c: Edit Columns
+
+Starts the table editor on one of the displayed tables. See L<"TABLE EDITOR">.
+An alternative way to start the table editor without entering the configuration
+dialog is with the '^' key.
+
+=item g: General Configuration
+
+Starts the configuration editor to edit global and mode-specific configuration
+variables (see L<"MODES">). innotop prompts you to choose a variable from among
+the global and mode-specific ones depending on the current mode.
+
+=item k: Row-Coloring Rules
+
+Starts the row-coloring rules editor on one of the displayed table(s). See
+L<"COLORS"> for details.
+
+=item p: Manage Plugins
+
+Starts the plugin configuration editor. See L<"PLUGINS"> for details.
+
+=item s: Server Groups
+
+Lets you create and edit server groups. See L<"SERVER GROUPS">.
+
+=item t: Choose Displayed Tables
+
+Lets you choose which tables to display in this mode. See L<"MODES"> and
+L<"TABLES">.
+
+=back
+
+=head1 CONFIGURATION FILE
+
+innotop's default configuration file locations are $HOME/.innotop and
+/etc/innotop/innotop.conf, and they are looked for in that order. If the first
+configuration file exists, the second will not be processed. Those can be
+overridden with the L<"--config"> command-line option. You can edit it by hand
+safely, however innotop reads the configuration file when it starts, and, if
+readonly is set to 0, writes it out again when it exits. Thus, if readonly is
+set to 0, any changes you make by hand while innotop is running will be lost.
+
+innotop doesn't store its entire configuration in the configuration file. It
+has a huge set of default configuration values that it holds only in memory,
+and the configuration file only overrides these defaults. When you customize a
+default setting, innotop notices, and then stores the customizations into the
+file. This keeps the file size down, makes it easier to edit, and makes
+upgrades easier.
+
+A configuration file is read-only be default. You can override that with
+L<"--write">. See L<"readonly">.
+
+The configuration file is arranged into sections like an INI file. Each
+section begins with [section-name] and ends with [/section-name]. Each
+section's entries have a different syntax depending on the data they need to
+store. You can put comments in the file; any line that begins with a #
+character is a comment. innotop will not read the comments, so it won't write
+them back out to the file when it exits. Comments in read-only configuration
+files are still useful, though.
+
+The first line in the file is innotop's version number. This lets innotop
+notice when the file format is not backwards-compatible, and upgrade smoothly
+without destroying your customized configuration.
+
+The following list describes each section of the configuration file and the data
+it contains:
+
+=over
+
+=item general
+
+The 'general' section contains global configuration variables and variables that
+may be mode-specific, but don't belong in any other section. The syntax is a
+simple key=value list. innotop writes a comment above each value to help you
+edit the file by hand.
+
+=over
+
+=item S_func
+
+Controls S mode presentation (see L<"S: Variables & Status">). If g, values are
+graphed; if s, values are like vmstat; if p, values are in a pivoted table.
+
+=item S_set
+
+Specifies which set of variables to display in L<"S: Variables & Status"> mode.
+See L<"VARIABLE SETS">.
+
+=item auto_wipe_dl
+
+Instructs innotop to automatically wipe large deadlocks when it notices them.
+When this happens you may notice a slight delay. At the next tick, you will
+usually see the information that was being truncated by the large deadlock.
+
+=item charset
+
+Specifies what kind of characters to allow through the L<"no_ctrl_char">
+transformation. This keeps non-printable characters from confusing a
+terminal when you monitor queries that contain binary data, such as images.
+
+The default is 'ascii', which considers anything outside normal ASCII to be a
+control character. The other allowable values are 'unicode' and 'none'. 'none'
+considers every character a control character, which can be useful for
+collapsing ALL text fields in queries.
+
+=item cmd_filter
+
+This is the prefix that filters variables in L<"C: Command Summary"> mode.
+
+=item color
+
+Whether terminal coloring is permitted.
+
+=item cxn_timeout
+
+On MySQL versions 4.0.3 and newer, this variable is used to set the connection's
+timeout, so MySQL doesn't close the connection if it is not used for a while.
+This might happen because a connection isn't monitored in a particular mode, for
+example.
+
+=item debug
+
+This option enables more verbose errors and makes innotop more strict in some
+places. It can help in debugging filters and other user-defined code. It also
+makes innotop write a lot of information to L<"debugfile"> when there is a
+crash.
+
+=item debugfile
+
+A file to which innotop will write information when there is a crash. See
+L<"FILES">.
+
+=item display_table_captions
+
+innotop displays a table caption above most tables. This variable suppresses or
+shows captions on all tables globally. Some tables are configured with the
+hide_caption property, which overrides this.
+
+=item global
+
+Whether to show GLOBAL variables and status. innotop only tries to do this on
+servers which support the GLOBAL option to SHOW VARIABLES and SHOW STATUS. In
+some MySQL versions, you need certain privileges to do this; if you don't have
+them, innotop will not be able to fetch any variable and status data. This
+configuration variable lets you run innotop and fetch what data you can even
+without the elevated privileges.
+
+I can no longer find or reproduce the situation where GLOBAL wasn't allowed, but
+I know there was one.
+
+=item graph_char
+
+Defines the character to use when drawing graphs in L<"S: Variables & Status">
+mode.
+
+=item header_highlight
+
+Defines how to highlight column headers. This only works if Term::ANSIColor is
+available. Valid values are 'bold' and 'underline'.
+
+=item hide_hdr
+
+Hides column headers globally.
+
+=item interval
+
+The interval at which innotop will refresh its data (ticks). The interval is
+implemented as a sleep time between ticks, so the true interval will vary
+depending on how long it takes innotop to fetch and render data.
+
+This variable accepts fractions of a second.
+
+=item mode
+
+The mode in which innotop should start. Allowable arguments are the same as the
+key presses that select a mode interactively. See L<"MODES">.
+
+=item num_digits
+
+How many digits to show in fractional numbers and percents. This variable's
+range is between 0 and 9 and can be set directly from L<"S: Variables & Status">
+mode with the '+' and '-' keys. It is used in the L<"set_precision">,
+L<"shorten">, and L<"percent"> transformations.
+
+=item num_status_sets
+
+Controls how many sets of status variables to display in pivoted L<"S: Variables
+& Status"> mode. It also controls the number of old sets of variables innotop
+keeps in its memory, so the larger this variable is, the more memory innotop
+uses.
+
+=item plugin_dir
+
+Specifies where plugins can be found. By default, innotop stores plugins in the
+'plugins' subdirectory of your innotop configuration directory.
+
+=item readonly
+
+Whether the configuration file is readonly. This cannot be set interactively.
+
+=item show_cxn_errors
+
+Makes innotop print connection errors to STDOUT. See L<"ERROR HANDLING">.
+
+=item show_cxn_errors_in_tbl
+
+Makes innotop display connection errors as rows in the first table on screen.
+See L<"ERROR HANDLING">.
+
+=item show_percent
+
+Adds a '%' character after the value returned by the L<"percent">
+transformation.
+
+=item show_statusbar
+
+Controls whether to show the status bar in the display. See L<"INNOTOP
+STATUS">.
+
+=item skip_innodb
+
+Disables fetching SHOW INNODB STATUS, in case your server(s) do not have InnoDB
+enabled and you don't want innotop to try to fetch it. This can also be useful
+when you don't have the SUPER privilege, required to run SHOW INNODB STATUS.
+
+=item status_inc
+
+Whether to show absolute or incremental values for status variables.
+Incremental values are calculated as an offset from the last value innotop saw
+for that variable. This is a global setting, but will probably become
+mode-specific at some point. Right now it is honored a bit inconsistently; some
+modes don't pay attention to it.
+
+=back
+
+=item plugins
+
+This section holds a list of package names of active plugins. If the plugin
+exists, innotop will activate it. See L<"PLUGINS"> for more information.
+
+=item filters
+
+This section holds user-defined filters (see L<"FILTERS">). Each line is in the
+format filter_name=text='filter text' tbls='table list'.
+
+The filter text is the text of the subroutine's code. The table list is a list
+of tables to which the filter can apply. By default, user-defined filters apply
+to the table for which they were created, but you can manually override that by
+editing the definition in the configuration file.
+
+=item active_filters
+
+This section stores which filters are active on each table. Each line is in the
+format table_name=filter_list.
+
+=item tbl_meta
+
+This section stores user-defined or user-customized columns (see L<"COLUMNS">).
+Each line is in the format col_name=properties, where the properties are a
+name=quoted-value list.
+
+=item connections
+
+This section holds the server connections you have defined. Each line is in
+the format name=properties, where the properties are a name=value list. The
+properties are self-explanatory, and the only one that is treated specially is
+'pass' which is only present if 'savepass' is set. This section of the
+configuration file will be skipped if any DSN, username, or password
+command-line options are used. See L<"SERVER CONNECTIONS">.
+
+=item active_connections
+
+This section holds a list of which connections are active in each mode. Each
+line is in the format mode_name=connection_list.
+
+=item server_groups
+
+This section holds server groups. Each line is in the format
+name=connection_list. See L<"SERVER GROUPS">.
+
+=item active_server_groups
+
+This section holds a list of which server group is active in each mode. Each
+line is in the format mode_name=server_group.
+
+=item max_values_seen
+
+This section holds the maximum values seen for variables. This is used to scale
+the graphs in L<"S: Variables & Status"> mode. Each line is in the format
+name=value.
+
+=item active_columns
+
+This section holds table column lists. Each line is in the format
+tbl_name=column_list. See L<"COLUMNS">.
+
+=item sort_cols
+
+This section holds the sort definition. Each line is in the format
+tbl_name=column_list. If a column is prefixed with '-', that column sorts
+descending. See L<"SORTING">.
+
+=item visible_tables
+
+This section defines which tables are visible in each mode. Each line is in the
+format mode_name=table_list. See L<"TABLES">.
+
+=item varsets
+
+This section defines variable sets for use in L<"S: Status & Variables"> mode.
+Each line is in the format name=variable_list. See L<"VARIABLE SETS">.
+
+=item colors
+
+This section defines colorization rules. Each line is in the format
+tbl_name=property_list. See L<"COLORS">.
+
+=item stmt_sleep_times
+
+This section contains statement sleep times. Each line is in the format
+statement_name=sleep_time. See L<"S: Statement Sleep Times">.
+
+=item group_by
+
+This section contains column lists for table group_by expressions. Each line is
+in the format tbl_name=column_list. See L<"GROUPING">.
+
+=back
+
+=head1 CUSTOMIZING
+
+You can customize innotop a great deal. For example, you can:
+
+=over
+
+=item *
+
+Choose which tables to display, and in what order.
+
+=item *
+
+Choose which columns are in those tables, and create new columns.
+
+=item *
+
+Filter which rows display with built-in filters, user-defined filters, and
+quick-filters.
+
+=item *
+
+Sort the rows to put important data first or group together related rows.
+
+=item *
+
+Highlight rows with color.
+
+=item *
+
+Customize the alignment, width, and formatting of columns, and apply
+transformations to columns to extract parts of their values or format the values
+as you wish (for example, shortening large numbers to familiar units).
+
+=item *
+
+Design your own expressions to extract and combine data as you need. This gives
+you unlimited flexibility.
+
+=back
+
+All these and more are explained in the following sections.
+
+=head2 TABLES
+
+A table is what you'd expect: a collection of columns. It also has some other
+properties, such as a caption. Filters, sorting rules, and colorization rules
+belong to tables and are covered in later sections.
+
+Internally, table meta-data is defined in a data structure called %tbl_meta.
+This hash holds all built-in table definitions, which contain a lot of default
+instructions to innotop. The meta-data includes the caption, a list of columns
+the user has customized, a list of columns, a list of visible columns, a list of
+filters, color rules, a sort-column list, sort direction, and some information
+about the table's data sources. Most of this is customizable via the table
+editor (see L<"TABLE EDITOR">).
+
+You can choose which tables to show by pressing the '$' key. See L<"MODES"> and
+L<"TABLES">.
+
+The table life-cycle is as follows:
+
+=over
+
+=item *
+
+Each table begins with a data source, which is an array of hashes. See below
+for details on data sources.
+
+=item *
+
+Each element of the data source becomes a row in the final table.
+
+=item *
+
+For each element in the data source, innotop extracts values from the source and
+creates a row. This row is another hash, which later steps will refer to as
+$set. The values innotop extracts are determined by the table's columns. Each
+column has an extraction subroutine, compiled from an expression (see
+L<"EXPRESSIONS">). The resulting row is a hash whose keys are named the same as
+the column name.
+
+=item *
+
+innotop filters the rows, removing those that don't need to be displayed. See
+L<"FILTERS">.
+
+=item *
+
+innotop sorts the rows. See L<"SORTING">.
+
+=item *
+
+innotop groups the rows together, if specified. See L<"GROUPING">.
+
+=item *
+
+innotop colorizes the rows. See L<"COLORS">.
+
+=item *
+
+innotop transforms the column values in each row. See L<"TRANSFORMATIONS">.
+
+=item *
+
+innotop optionally pivots the rows (see L<"PIVOTING">), then filters and sorts
+them.
+
+=item *
+
+innotop formats and justifies the rows as a table. During this step, innotop
+applies further formatting to the column values, including alignment, maximum
+and minimum widths. innotop also does final error checking to ensure there are
+no crashes due to undefined values. innotop then adds a caption if specified,
+and the table is ready to print.
+
+=back
+
+The lifecycle is slightly different if the table is pivoted, as noted above. To
+clarify, if the table is pivoted, the process is extract, group, transform,
+pivot, filter, sort, create. If it's not pivoted, the process is extract,
+filter, sort, group, color, transform, create. This slightly convoluted process
+doesn't map all that well to SQL, but pivoting complicates things pretty
+thoroughly. Roughly speaking, filtering and sorting happen as late as needed to
+effect the final result as you might expect, but as early as possible for
+efficiency.
+
+Each built-in table is described below:
+
+=over
+
+=item adaptive_hash_index
+
+Displays data about InnoDB's adaptive hash index. Data source:
+L<"STATUS_VARIABLES">.
+
+=item buffer_pool
+
+Displays data about InnoDB's buffer pool. Data source: L<"STATUS_VARIABLES">.
+
+=item cmd_summary
+
+Displays weighted status variables. Data source: L<"STATUS_VARIABLES">.
+
+=item deadlock_locks
+
+Shows which locks were held and waited for by the last detected deadlock. Data
+source: L<"DEADLOCK_LOCKS">.
+
+=item deadlock_transactions
+
+Shows transactions involved in the last detected deadlock. Data source:
+L<"DEADLOCK_TRANSACTIONS">.
+
+=item explain
+
+Shows the output of EXPLAIN. Data source: L<"EXPLAIN">.
+
+=item file_io_misc
+
+Displays data about InnoDB's file and I/O operations. Data source:
+L<"STATUS_VARIABLES">.
+
+=item fk_error
+
+Displays various data about InnoDB's last foreign key error. Data source:
+L<"STATUS_VARIABLES">.
+
+=item innodb_locks
+
+Displays InnoDB locks. Data source: L<"INNODB_LOCKS">.
+
+=item innodb_transactions
+
+Displays data about InnoDB's current transactions. Data source:
+L<"INNODB_TRANSACTIONS">.
+
+=item insert_buffers
+
+Displays data about InnoDB's insert buffer. Data source: L<"STATUS_VARIABLES">.
+
+=item io_threads
+
+Displays data about InnoDB's I/O threads. Data source: L<"IO_THREADS">.
+
+=item log_statistics
+
+Displays data about InnoDB's logging system. Data source: L<"STATUS_VARIABLES">.
+
+=item master_status
+
+Displays replication master status. Data source: L<"STATUS_VARIABLES">.
+
+=item open_tables
+
+Displays open tables. Data source: L<"OPEN_TABLES">.
+
+=item page_statistics
+
+Displays InnoDB page statistics. Data source: L<"STATUS_VARIABLES">.
+
+=item pending_io
+
+Displays InnoDB pending I/O operations. Data source: L<"STATUS_VARIABLES">.
+
+=item processlist
+
+Displays current MySQL processes (threads/connections). Data source:
+L<"PROCESSLIST">.
+
+=item q_header
+
+Displays various status values. Data source: L<"STATUS_VARIABLES">.
+
+=item row_operation_misc
+
+Displays data about InnoDB's row operations. Data source:
+L<"STATUS_VARIABLES">.
+
+=item row_operations
+
+Displays data about InnoDB's row operations. Data source:
+L<"STATUS_VARIABLES">.
+
+=item semaphores
+
+Displays data about InnoDB's semaphores and mutexes. Data source:
+L<"STATUS_VARIABLES">.
+
+=item slave_io_status
+
+Displays data about the slave I/O thread. Data source:
+L<"STATUS_VARIABLES">.
+
+=item slave_sql_status
+
+Displays data about the slave SQL thread. Data source: L<"STATUS_VARIABLES">.
+
+=item t_header
+
+Displays various InnoDB status values. Data source: L<"STATUS_VARIABLES">.
+
+=item var_status
+
+Displays user-configurable data. Data source: L<"STATUS_VARIABLES">.
+
+=item wait_array
+
+Displays data about InnoDB's OS wait array. Data source: L<"OS_WAIT_ARRAY">.
+
+=back
+
+=head2 COLUMNS
+
+Columns belong to tables. You can choose a table's columns by pressing the '^'
+key, which starts the L<"TABLE EDITOR"> and lets you choose and edit columns.
+Pressing 'e' from within the table editor lets you edit the column's properties:
+
+=over
+
+=item *
+
+hdr: a column header. This appears in the first row of the table.
+
+=item *
+
+just: justification. '-' means left-justified and '' means right-justified,
+just as with printf formatting codes (not a coincidence).
+
+=item *
+
+dec: whether to further align the column on the decimal point.
+
+=item *
+
+num: whether the column is numeric. This affects how values are sorted
+(lexically or numerically).
+
+=item *
+
+label: a small note about the column, which appears in dialogs that help the
+user choose columns.
+
+=item *
+
+src: an expression that innotop uses to extract the column's data from its
+source (see L<"DATA SOURCES">). See L<"EXPRESSIONS"> for more on expressions.
+
+=item *
+
+minw: specifies a minimum display width. This helps stabilize the display,
+which makes it easier to read if the data is changing frequently.
+
+=item *
+
+maxw: similar to minw.
+
+=item *
+
+trans: a list of column transformations. See L<"TRANSFORMATIONS">.
+
+=item *
+
+agg: an aggregate function. See L<"GROUPING">. The default is L<"first">.
+
+=item *
+
+aggonly: controls whether the column only shows when grouping is enabled on the
+table (see L<"GROUPING">). By default, this is disabled. This means columns
+will always be shown by default, whether grouping is enabled or not. If a
+column's aggonly is set true, the column will appear when you toggle grouping on
+the table. Several columns are set this way, such as the count column on
+L<"processlist"> and L<"innodb_transactions">, so you don't see a count when the
+grouping isn't enabled, but you do when it is.
+
+=back
+
+=head2 FILTERS
+
+Filters remove rows from the display. They behave much like a WHERE clause in
+SQL. innotop has several built-in filters, which remove irrelevant information
+like inactive queries, but you can define your own as well. innotop also lets
+you create quick-filters, which do not get saved to the configuration file, and
+are just an easy way to quickly view only some rows.
+
+You can enable or disable a filter on any table. Press the '%' key (mnemonic: %
+looks kind of like a line being filtered between two circles) and choose which
+table you want to filter, if asked. You'll then see a list of possible filters
+and a list of filters currently enabled for that table. Type the names of
+filters you want to apply and press Enter.
+
+=head3 USER-DEFINED FILTERS
+
+If you type a name that doesn't exist, innotop will prompt you to create the
+filter. Filters are easy to create if you know Perl, and not hard if you don't.
+What you're doing is creating a subroutine that returns true if the row should
+be displayed. The row is a hash reference passed to your subroutine as $set.
+
+For example, imagine you want to filter the processlist table so you only see
+queries that have been running more than five minutes. Type a new name for your
+filter, and when prompted for the subroutine body, press TAB to initiate your
+terminal's auto-completion. You'll see the names of the columns in the
+L<"processlist"> table (innotop generally tries to help you with auto-completion
+lists). You want to filter on the 'time' column. Type the text "$set->{time} >
+300" to return true when the query is more than five minutes old. That's all
+you need to do.
+
+In other words, the code you're typing is surrounded by an implicit context,
+which looks like this:
+
+ sub filter {
+ my ( $set ) = @_;
+ # YOUR CODE HERE
+ }
+
+If your filter doesn't work, or if something else suddenly behaves differently,
+you might have made an error in your filter, and innotop is silently catching
+the error. Try enabling L<"debug"> to make innotop throw an error instead.
+
+=head3 QUICK-FILTERS
+
+innotop's quick-filters are a shortcut to create a temporary filter that doesn't
+persist when you restart innotop. To create a quick-filter, press the '/' key.
+innotop will prompt you for the column name and filter text. Again, you can use
+auto-completion on column names. The filter text can be just the text you want
+to "search for." For example, to filter the L<"processlist"> table on queries
+that refer to the products table, type '/' and then 'info product'.
+
+The filter text can actually be any Perl regular expression, but of course a
+literal string like 'product' works fine as a regular expression.
+
+Behind the scenes innotop compiles the quick-filter into a specially tagged
+filter that is otherwise like any other filter. It just isn't saved to the
+configuration file.
+
+To clear quick-filters, press the '\' key and innotop will clear them all at
+once.
+
+=head2 SORTING
+
+innotop has sensible built-in defaults to sort the most important rows to the
+top of the table. Like anything else in innotop, you can customize how any
+table is sorted.
+
+To start the sort dialog, start the L<"TABLE EDITOR"> with the '^' key, choose a
+table if necessary, and press the 's' key. You'll see a list of columns you can
+use in the sort expression and the current sort expression, if any. Enter a
+list of columns by which you want to sort and press Enter. If you want to
+reverse sort, prefix the column name with a minus sign. For example, if you
+want to sort by column a ascending, then column b descending, type 'a -b'. You
+can also explicitly add a + in front of columns you want to sort ascending, but
+it's not required.
+
+Some modes have keys mapped to open this dialog directly, and to quickly reverse
+sort direction. Press '?' as usual to see which keys are mapped in any mode.
+
+=head2 GROUPING
+
+innotop can group, or aggregate, rows together (the terms are used
+interchangeably). This is quite similar to an SQL GROUP BY clause. You can
+specify to group on certain columns, or if you don't specify any, the entire set
+of rows is treated as one group. This is quite like SQL so far, but unlike SQL,
+you can also select un-grouped columns. innotop actually aggregates every
+column. If you don't explicitly specify a grouping function, the default is
+'first'. This is basically a convenience so you don't have to specify an
+aggregate function for every column you want in the result.
+
+You can quickly toggle grouping on a table with the '=' key, which toggles its
+aggregate property. This property doesn't persist to the config file.
+
+The columns by which the table is grouped are specified in its group_by
+property. When you turn grouping on, innotop places the group_by columns at the
+far left of the table, even if they're not supposed to be visible. The rest of
+the visible columns appear in order after them.
+
+Two tables have default group_by lists and a count column built in:
+L<"processlist"> and L<"innodb_transactions">. The grouping is by connection
+and status, so you can quickly see how many queries or transactions are in a
+given status on each server you're monitoring. The time columns are aggregated
+as a sum; other columns are left at the default 'first' aggregation.
+
+By default, the table shown in L<"S: Variables & Status"> mode also uses
+grouping so you can monitor variables and status across many servers. The
+default aggregation function in this mode is 'avg'.
+
+Valid grouping functions are defined in the %agg_funcs hash. They include
+
+=over
+
+=item first
+
+Returns the first element in the group.
+
+=item count
+
+Returns the number of elements in the group, including undefined elements, much
+like SQL's COUNT(*).
+
+=item avg
+
+Returns the average of defined elements in the group.
+
+=item sum
+
+Returns the sum of elements in the group.
+
+=back
+
+Here's an example of grouping at work. Suppose you have a very busy server with
+hundreds of open connections, and you want to see how many connections are in
+what status. Using the built-in grouping rules, you can press 'Q' to enter
+L<"Q: Query List"> mode. Press '=' to toggle grouping (if necessary, select the
+L<"processlist"> table when prompted).
+
+Your display might now look like the following:
+
+ Query List (? for help) localhost, 32:33, 0.11 QPS, 1 thd, 5.0.38-log
+
+ CXN Cmd Cnt ID User Host Time Query
+ localhost Query 49 12933 webusr localhost 19:38 SELECT * FROM
+ localhost Sending Da 23 2383 webusr localhost 12:43 SELECT col1,
+ localhost Sleep 120 140 webusr localhost 5:18:12
+ localhost Statistics 12 19213 webusr localhost 01:19 SELECT * FROM
+
+That's actually quite a worrisome picture. You've got a lot of idle connections
+(Sleep), and some connections executing queries (Query and Sending Data).
+That's okay, but you also have a lot in Statistics status, collectively spending
+over a minute. That means the query optimizer is having a really hard time
+optimizing your statements. Something is wrong; it should normally take
+milliseconds to optimize queries. You might not have seen this pattern if you
+didn't look at your connections in aggregate. (This is a made-up example, but
+it can happen in real life).
+
+=head2 PIVOTING
+
+innotop can pivot a table for more compact display, similar to a Pivot Table in
+a spreadsheet (also known as a crosstab). Pivoting a table makes columns into
+rows. Assume you start with this table:
+
+ foo bar
+ === ===
+ 1 3
+ 2 4
+
+After pivoting, the table will look like this:
+
+ name set0 set1
+ ==== ==== ====
+ foo 1 2
+ bar 3 4
+
+To get reasonable results, you might need to group as well as pivoting.
+innotop currently does this for L<"S: Variables & Status"> mode.
+
+=head2 COLORS
+
+By default, innotop highlights rows with color so you can see at a glance which
+rows are more important. You can customize the colorization rules and add your
+own to any table. Open the table editor with the '^' key, choose a table if
+needed, and press 'o' to open the color editor dialog.
+
+The color editor dialog displays the rules applied to the table, in the order
+they are evaluated. Each row is evaluated against each rule to see if the rule
+matches the row; if it does, the row gets the specified color, and no further
+rules are evaluated. The rules look like the following:
+
+ state eq Locked black on_red
+ cmd eq Sleep white
+ user eq system user white
+ cmd eq Connect white
+ cmd eq Binlog Dump white
+ time > 600 red
+ time > 120 yellow
+ time > 60 green
+ time > 30 cyan
+
+This is the default rule set for the L<"processlist"> table. In order of
+priority, these rules make locked queries black on a red background, "gray out"
+connections from replication and sleeping queries, and make queries turn from
+cyan to red as they run longer.
+
+(For some reason, the ANSI color code "white" is actually a light gray. Your
+terminal's display may vary; experiment to find colors you like).
+
+You can use keystrokes to move the rules up and down, which re-orders their
+priority. You can also delete rules and add new ones. If you add a new rule,
+innotop prompts you for the column, an operator for the comparison, a value
+against which to compare the column, and a color to assign if the rule matches.
+There is auto-completion and prompting at each step.
+
+The value in the third step needs to be correctly quoted. innotop does not try
+to quote the value because it doesn't know whether it should treat the value as
+a string or a number. If you want to compare the column against a string, as
+for example in the first rule above, you should enter 'Locked' surrounded by
+quotes. If you get an error message about a bareword, you probably should have
+quoted something.
+
+=head2 EXPRESSIONS
+
+Expressions are at the core of how innotop works, and are what enables you to
+extend innotop as you wish. Recall the table lifecycle explained in
+L<"TABLES">. Expressions are used in the earliest step, where it extracts
+values from a data source to form rows.
+
+It does this by calling a subroutine for each column, passing it the source data
+set, a set of current values, and a set of previous values. These are all
+needed so the subroutine can calculate things like the difference between this
+tick and the previous tick.
+
+The subroutines that extract the data from the set are compiled from
+expressions. This gives significantly more power than just naming the values to
+fill the columns, because it allows the column's value to be calculated from
+whatever data is necessary, but avoids the need to write complicated and lengthy
+Perl code.
+
+innotop begins with a string of text that can look as simple as a value's name
+or as complicated as a full-fledged Perl expression. It looks at each
+'bareword' token in the string and decides whether it's supposed to be a key
+into the $set hash. A bareword is an unquoted value that isn't already
+surrounded by code-ish things like dollar signs or curly brackets. If innotop
+decides that the bareword isn't a function or other valid Perl code, it converts
+it into a hash access. After the whole string is processed, innotop compiles a
+subroutine, like this:
+
+ sub compute_column_value {
+ my ( $set, $cur, $pre ) = @_;
+ my $val = # EXPANDED STRING GOES HERE
+ return $val;
+ }
+
+Here's a concrete example, taken from the header table L<"q_header"> in L<"Q:
+Query List"> mode. This expression calculates the qps, or Queries Per Second,
+column's values, from the values returned by SHOW STATUS:
+
+ Questions/Uptime_hires
+
+innotop decides both words are barewords, and transforms this expression into
+the following Perl code:
+
+ $set->{Questions}/$set->{Uptime_hires}
+
+When surrounded by the rest of the subroutine's code, this is executable Perl
+that calculates a high-resolution queries-per-second value.
+
+The arguments to the subroutine are named $set, $cur, and $pre. In most cases,
+$set and $cur will be the same values. However, if L<"status_inc"> is set, $cur
+will not be the same as $set, because $set will already contain values that are
+the incremental difference between $cur and $pre.
+
+Every column in innotop is computed by subroutines compiled in the same fashion.
+There is no difference between innotop's built-in columns and user-defined
+columns. This keeps things consistent and predictable.
+
+=head2 TRANSFORMATIONS
+
+Transformations change how a value is rendered. For example, they can take a
+number of seconds and display it in H:M:S format. The following transformations
+are defined:
+
+=over
+
+=item commify
+
+Adds commas to large numbers every three decimal places.
+
+=item dulint_to_int
+
+Accepts two unsigned integers and converts them into a single longlong. This is
+useful for certain operations with InnoDB, which uses two integers as
+transaction identifiers, for example.
+
+=item no_ctrl_char
+
+Removes quoted control characters from the value. This is affected by the
+L<"charset"> configuration variable.
+
+This transformation only operates within quoted strings, for example, values to
+a SET clause in an UPDATE statement. It will not alter the UPDATE statement,
+but will collapse the quoted string to [BINARY] or [TEXT], depending on the
+charset.
+
+=item percent
+
+Converts a number to a percentage by multiplying it by two, formatting it with
+L<"num_digits"> digits after the decimal point, and optionally adding a percent
+sign (see L<"show_percent">).
+
+=item secs_to_time
+
+Formats a number of seconds as time in days+hours:minutes:seconds format.
+
+=item set_precision
+
+Formats numbers with L<"num_digits"> number of digits after the decimal point.
+
+=item shorten
+
+Formats a number as a unit of 1024 (k/M/G/T) and with L<"num_digits"> number of
+digits after the decimal point.
+
+=back
+
+=head2 TABLE EDITOR
+
+The innotop table editor lets you customize tables with keystrokes. You start
+the table editor with the '^' key. If there's more than one table on the
+screen, it will prompt you to choose one of them. Once you do, innotop will
+show you something like this:
+
+ Editing table definition for Buffer Pool. Press ? for help, q to quit.
+
+ name hdr label src
+ cxn CXN Connection from which cxn
+ buf_pool_size Size Buffer pool size IB_bp_buf_poo
+ buf_free Free Bufs Buffers free in the b IB_bp_buf_fre
+ pages_total Pages Pages total IB_bp_pages_t
+ pages_modified Dirty Pages Pages modified (dirty IB_bp_pages_m
+ buf_pool_hit_rate Hit Rate Buffer pool hit rate IB_bp_buf_poo
+ total_mem_alloc Memory Total memory allocate IB_bp_total_m
+ add_pool_alloc Add'l Pool Additonal pool alloca IB_bp_add_poo
+
+The first line shows which table you're editing, and reminds you again to press
+'?' for a list of key mappings. The rest is a tabular representation of the
+table's columns, because that's likely what you're trying to edit. However, you
+can edit more than just the table's columns; this screen can start the filter
+editor, color rule editor, and more.
+
+Each row in the display shows a single column in the table you're editing, along
+with a couple of its properties such as its header and source expression (see
+L<"EXPRESSIONS">).
+
+The key mappings are Vim-style, as in many other places. Pressing 'j' and 'k'
+moves the highlight up or down. You can then (d)elete or (e)dit the highlighted
+column. You can also (a)dd a column to the table. This actually just activates
+one of the columns already defined for the table; it prompts you to choose from
+among the columns available but not currently displayed. Finally, you can
+re-order the columns with the '+' and '-' keys.
+
+You can do more than just edit the columns with the table editor, you can also
+edit other properties, such as the table's sort expression and group-by
+expression. Press '?' to see the full list, of course.
+
+If you want to really customize and create your own column, as opposed to just
+activating a built-in one that's not currently displayed, press the (n)ew key,
+and innotop will prompt you for the information it needs:
+
+=over
+
+=item *
+
+The column name: this needs to be a word without any funny characters, e.g. just
+letters, numbers and underscores.
+
+=item *
+
+The column header: this is the label that appears at the top of the column, in
+the table header. This can have spaces and funny characters, but be careful not
+to make it too wide and waste space on-screen.
+
+=item *
+
+The column's data source: this is an expression that determines what data from
+the source (see L<"TABLES">) innotop will put into the column. This can just be
+the name of an item in the source, or it can be a more complex expression, as
+described in L<"EXPRESSIONS">.
+
+=back
+
+Once you've entered the required data, your table has a new column. There is no
+difference between this column and the built-in ones; it can have all the same
+properties and behaviors. innotop will write the column's definition to the
+configuration file, so it will persist across sessions.
+
+Here's an example: suppose you want to track how many times your slaves have
+retried transactions. According to the MySQL manual, the
+Slave_retried_transactions status variable gives you that data: "The total
+number of times since startup that the replication slave SQL thread has retried
+transactions. This variable was added in version 5.0.4." This is appropriate to
+add to the L<"slave_sql_status"> table.
+
+To add the column, switch to the replication-monitoring mode with the 'M' key,
+and press the '^' key to start the table editor. When prompted, choose
+slave_sql_status as the table, then press 'n' to create the column. Type
+'retries' as the column name, 'Retries' as the column header, and
+'Slave_retried_transactions' as the source. Now the column is created, and you
+see the table editor screen again. Press 'q' to exit the table editor, and
+you'll see your column at the end of the table.
+
+=head1 VARIABLE SETS
+
+Variable sets are used in L<"S: Variables & Status"> mode to define more easily
+what variables you want to monitor. Behind the scenes they are compiled to a
+list of expressions, and then into a column list so they can be treated just
+like columns in any other table, in terms of data extraction and
+transformations. However, you're protected from the tedious details by a syntax
+that ought to feel very natural to you: a SQL SELECT list.
+
+The data source for variable sets, and indeed the entire S mode, is the
+combination of SHOW STATUS, SHOW VARIABLES, and SHOW INNODB STATUS. Imagine
+that you had a huge table with one column per variable returned from those
+statements. That's the data source for variable sets. You can now query this
+data source just like you'd expect. For example:
+
+ Questions, Uptime, Questions/Uptime as QPS
+
+Behind the scenes innotop will split that variable set into three expressions,
+compile them and turn them into a table definition, then extract as usual. This
+becomes a "variable set," or a "list of variables you want to monitor."
+
+innotop lets you name and save your variable sets, and writes them to the
+configuration file. You can choose which variable set you want to see with the
+'c' key, or activate the next and previous sets with the '>' and '<' keys.
+There are many built-in variable sets as well, which should give you a good
+start for creating your own. Press 'e' to edit the current variable set, or
+just to see how it's defined. To create a new one, just press 'c' and type its
+name.
+
+You may want to use some of the functions listed in L<"TRANSFORMATIONS"> to help
+format the results. In particular, L<"set_precision"> is often useful to limit
+the number of digits you see. Extending the above example, here's how:
+
+ Questions, Uptime, set_precision(Questions/Uptime) as QPS
+
+Actually, this still needs a little more work. If your L<"interval"> is less
+than one second, you might be dividing by zero because Uptime is incremental in
+this mode by default. Instead, use Uptime_hires:
+
+ Questions, Uptime, set_precision(Questions/Uptime_hires) as QPS
+
+This example is simple, but it shows how easy it is to choose which variables
+you want to monitor.
+
+=head1 PLUGINS
+
+innotop has a simple but powerful plugin mechanism by which you can extend
+or modify its existing functionality, and add new functionality. innotop's
+plugin functionality is event-based: plugins register themselves to be called
+when events happen. They then have a chance to influence the event.
+
+An innotop plugin is a Perl module placed in innotop's L<"plugin_dir">
+directory. On UNIX systems, you can place a symbolic link to the module instead
+of putting the actual file there. innotop automatically discovers the file. If
+there is a corresponding entry in the L<"plugins"> configuration file section,
+innotop loads and activates the plugin.
+
+The module must conform to innotop's plugin interface. Additionally, the source
+code of the module must be written in such a way that innotop can inspect the
+file and determine the package name and description.
+
+=head2 Package Source Convention
+
+innotop inspects the plugin module's source to determine the Perl package name.
+It looks for a line of the form "package Foo;" and if found, considers the
+plugin's package name to be Foo. Of course the package name can be a valid Perl
+package name, with double semicolons and so on.
+
+It also looks for a description in the source code, to make the plugin editor
+more human-friendly. The description is a comment line of the form "#
+description: Foo", where "Foo" is the text innotop will consider to be the
+plugin's description.
+
+=head2 Plugin Interface
+
+The innotop plugin interface is quite simple: innotop expects the plugin to be
+an object-oriented module it can call certain methods on. The methods are
+
+=over
+
+=item new(%variables)
+
+This is the plugin's constructor. It is passed a hash of innotop's variables,
+which it can manipulate (see L<"Plugin Variables">). It must return a reference
+to the newly created plugin object.
+
+At construction time, innotop has only loaded the general configuration and
+created the default built-in variables with their default contents (which is
+quite a lot). Therefore, the state of the program is exactly as in the innotop
+source code, plus the configuration variables from the L<"general"> section in
+the config file.
+
+If your plugin manipulates the variables, it is changing global data, which is
+shared by innotop and all plugins. Plugins are loaded in the order they're
+listed in the config file. Your plugin may load before or after another plugin,
+so there is a potential for conflict or interaction between plugins if they
+modify data other plugins use or modify.
+
+=item register_for_events()
+
+This method must return a list of events in which the plugin is interested, if
+any. See L<"Plugin Events"> for the defined events. If the plugin returns an
+event that's not defined, the event is ignored.
+
+=item event handlers
+
+The plugin must implement a method named the same as each event for which it has
+registered. In other words, if the plugin returns qw(foo bar) from
+register_for_events(), it must have foo() and bar() methods. These methods are
+callbacks for the events. See L<"Plugin Events"> for more details about each
+event.
+
+=back
+
+=head2 Plugin Variables
+
+The plugin's constructor is passed a hash of innotop's variables, which it can
+manipulate. It is probably a good idea if the plugin object saves a copy of it
+for later use. The variables are defined in the innotop variable
+%pluggable_vars, and are as follows:
+
+=over
+
+=item action_for
+
+A hashref of key mappings. These are innotop's global hot-keys.
+
+=item agg_funcs
+
+A hashref of functions that can be used for grouping. See L<"GROUPING">.
+
+=item config
+
+The global configuration hash.
+
+=item connections
+
+A hashref of connection specifications. These are just specifications of how to
+connect to a server.
+
+=item dbhs
+
+A hashref of innotop's database connections. These are actual DBI connection
+objects.
+
+=item filters
+
+A hashref of filters applied to table rows. See L<"FILTERS"> for more.
+
+=item modes
+
+A hashref of modes. See L<"MODES"> for more.
+
+=item server_groups
+
+A hashref of server groups. See L<"SERVER GROUPS">.
+
+=item tbl_meta
+
+A hashref of innotop's table meta-data, with one entry per table (see
+L<"TABLES"> for more information).
+
+=item trans_funcs
+
+A hashref of transformation functions. See L<"TRANSFORMATIONS">.
+
+=item var_sets
+
+A hashref of variable sets. See L<"VARIABLE SETS">.
+
+=back
+
+=head2 Plugin Events
+
+Each event is defined somewhere in the innotop source code. When innotop runs
+that code, it executes the callback function for each plugin that expressed its
+interest in the event. innotop passes some data for each event. The events are
+defined in the %event_listener_for variable, and are as follows:
+
+=over
+
+=item extract_values($set, $cur, $pre, $tbl)
+
+This event occurs inside the function that extracts values from a data source.
+The arguments are the set of values, the current values, the previous values,
+and the table name.
+
+=item set_to_tbl
+
+Events are defined at many places in this subroutine, which is responsible for
+turning an arrayref of hashrefs into an arrayref of lines that can be printed to
+the screen. The events all pass the same data: an arrayref of rows and the name
+of the table being created. The events are set_to_tbl_pre_filter,
+set_to_tbl_pre_sort,set_to_tbl_pre_group, set_to_tbl_pre_colorize,
+set_to_tbl_pre_transform, set_to_tbl_pre_pivot, set_to_tbl_pre_create,
+set_to_tbl_post_create.
+
+=item draw_screen($lines)
+
+This event occurs inside the subroutine that prints the lines to the screen.
+$lines is an arrayref of strings.
+
+=back
+
+=head2 Simple Plugin Example
+
+The easiest way to explain the plugin functionality is probably with a simple
+example. The following module adds a column to the beginning of every table and
+sets its value to 1.
+
+ use strict;
+ use warnings FATAL => 'all';
+
+ package Innotop::Plugin::Example;
+ # description: Adds an 'example' column to every table
+
+ sub new {
+ my ( $class, %vars ) = @_;
+ # Store reference to innotop's variables in $self
+ my $self = bless { %vars }, $class;
+
+ # Design the example column
+ my $col = {
+ hdr => 'Example',
+ just => '',
+ dec => 0,
+ num => 1,
+ label => 'Example',
+ src => 'example', # Get data from this column in the data source
+ tbl => '',
+ trans => [],
+ };
+
+ # Add the column to every table.
+ my $tbl_meta = $vars{tbl_meta};
+ foreach my $tbl ( values %$tbl_meta ) {
+ # Add the column to the list of defined columns
+ $tbl->{cols}->{example} = $col;
+ # Add the column to the list of visible columns
+ unshift @{$tbl->{visible}}, 'example';
+ }
+
+ # Be sure to return a reference to the object.
+ return $self;
+ }
+
+ # I'd like to be called when a data set is being rendered into a table, please.
+ sub register_for_events {
+ my ( $self ) = @_;
+ return qw(set_to_tbl_pre_filter);
+ }
+
+ # This method will be called when the event fires.
+ sub set_to_tbl_pre_filter {
+ my ( $self, $rows, $tbl ) = @_;
+ # Set the example column's data source to the value 1.
+ foreach my $row ( @$rows ) {
+ $row->{example} = 1;
+ }
+ }
+
+ 1;
+
+=head2 Plugin Editor
+
+The plugin editor lets you view the plugins innotop discovered and activate or
+deactivate them. Start the editor by pressing $ to start the configuration
+editor from any mode. Press the 'p' key to start the plugin editor. You'll see
+a list of plugins innotop discovered. You can use the 'j' and 'k' keys to move
+the highlight to the desired one, then press the * key to toggle it active or
+inactive. Exit the editor and restart innotop for the changes to take effect.
+
+=head1 SQL STATEMENTS
+
+innotop uses a limited set of SQL statements to retrieve data from MySQL for
+display. The statements are customized depending on the server version against
+which they are executed; for example, on MySQL 5 and newer, INNODB_STATUS
+executes "SHOW ENGINE INNODB STATUS", while on earlier versions it executes
+"SHOW INNODB STATUS". The statements are as follows:
+
+ Statement SQL executed
+ =================== ===============================
+ INNODB_STATUS SHOW [ENGINE] INNODB STATUS
+ KILL_CONNECTION KILL
+ KILL_QUERY KILL QUERY
+ OPEN_TABLES SHOW OPEN TABLES
+ PROCESSLIST SHOW FULL PROCESSLIST
+ SHOW_MASTER_LOGS SHOW MASTER LOGS
+ SHOW_MASTER_STATUS SHOW MASTER STATUS
+ SHOW_SLAVE_STATUS SHOW SLAVE STATUS
+ SHOW_STATUS SHOW [GLOBAL] STATUS
+ SHOW_VARIABLES SHOW [GLOBAL] VARIABLES
+
+=head1 DATA SOURCES
+
+Each time innotop extracts values to create a table (see L<"EXPRESSIONS"> and
+L<"TABLES">), it does so from a particular data source. Largely because of the
+complex data extracted from SHOW INNODB STATUS, this is slightly messy. SHOW
+INNODB STATUS contains a mixture of single values and repeated values that form
+nested data sets.
+
+Whenever innotop fetches data from MySQL, it adds two extra bits to each set:
+cxn and Uptime_hires. cxn is the name of the connection from which the data
+came. Uptime_hires is a high-resolution version of the server's Uptime status
+variable, which is important if your L<"interval"> setting is sub-second.
+
+Here are the kinds of data sources from which data is extracted:
+
+=over
+
+=item STATUS_VARIABLES
+
+This is the broadest category, into which the most kinds of data fall. It
+begins with the combination of SHOW STATUS and SHOW VARIABLES, but other sources
+may be included as needed, for example, SHOW MASTER STATUS and SHOW SLAVE
+STATUS, as well as many of the non-repeated values from SHOW INNODB STATUS.
+
+=item DEADLOCK_LOCKS
+
+This data is extracted from the transaction list in the LATEST DETECTED DEADLOCK
+section of SHOW INNODB STATUS. It is nested two levels deep: transactions, then
+locks.
+
+=item DEADLOCK_TRANSACTIONS
+
+This data is from the transaction list in the LATEST DETECTED DEADLOCK
+section of SHOW INNODB STATUS. It is nested one level deep.
+
+=item EXPLAIN
+
+This data is from the result set returned by EXPLAIN.
+
+=item INNODB_TRANSACTIONS
+
+This data is from the TRANSACTIONS section of SHOW INNODB STATUS.
+
+=item IO_THREADS
+
+This data is from the list of threads in the the FILE I/O section of SHOW INNODB
+STATUS.
+
+=item INNODB_LOCKS
+
+This data is from the TRANSACTIONS section of SHOW INNODB STATUS and is nested
+two levels deep.
+
+=item OPEN_TABLES
+
+This data is from SHOW OPEN TABLES.
+
+=item PROCESSLIST
+
+This data is from SHOW FULL PROCESSLIST.
+
+=item OS_WAIT_ARRAY
+
+This data is from the SEMAPHORES section of SHOW INNODB STATUS and is nested one
+level deep. It comes from the lines that look like this:
+
+ --Thread 1568861104 has waited at btr0cur.c line 424 ....
+
+=back
+
+=head1 MYSQL PRIVILEGES
+
+=over
+
+=item *
+
+You must connect to MySQL as a user who has the SUPER privilege for many of the
+functions.
+
+=item *
+
+If you don't have the SUPER privilege, you can still run some functions, but you
+won't necessarily see all the same data.
+
+=item *
+
+You need the PROCESS privilege to see the list of currently running queries in Q
+mode.
+
+=item *
+
+You need special privileges to start and stop slave servers.
+
+=item *
+
+You need appropriate privileges to create and drop the deadlock tables if needed
+(see L<"SERVER CONNECTIONS">).
+
+=back
+
+=head1 SYSTEM REQUIREMENTS
+
+You need Perl to run innotop, of course. You also need a few Perl modules: DBI,
+DBD::mysql, Term::ReadKey, and Time::HiRes. These should be included with most
+Perl distributions, but in case they are not, I recommend using versions
+distributed with your operating system or Perl distribution, not from CPAN.
+Term::ReadKey in particular has been known to cause problems if installed from
+CPAN.
+
+If you have Term::ANSIColor, innotop will use it to format headers more readably
+and compactly. (Under Microsoft Windows, you also need Win32::Console::ANSI for
+terminal formatting codes to be honored). If you install Term::ReadLine,
+preferably Term::ReadLine::Gnu, you'll get nice auto-completion support.
+
+I run innotop on Gentoo GNU/Linux, Debian and Ubuntu, and I've had feedback from
+people successfully running it on Red Hat, CentOS, Solaris, and Mac OSX. I
+don't see any reason why it won't work on other UNIX-ish operating systems, but
+I don't know for sure. It also runs on Windows under ActivePerl without
+problem.
+
+innotop has been used on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26,
+5.1.15, and 5.2.3. If it doesn't run correctly for you, that is a bug that
+should be reported.
+
+=head1 FILES
+
+$HOMEDIR/.innotop and/or /etc/innotop are used to store
+configuration information. Files include the configuration file innotop.conf,
+the core_dump file which contains verbose error messages if L<"debug"> is
+enabled, and the plugins/ subdirectory.
+
+=head1 GLOSSARY OF TERMS
+
+=over
+
+=item tick
+
+A tick is a refresh event, when innotop re-fetches data from connections and
+displays it.
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+The following people and organizations are acknowledged for various reasons.
+Hopefully no one has been forgotten.
+
+Allen K. Smith,
+Aurimas Mikalauskas,
+Bartosz Fenski,
+Brian Miezejewski,
+Christian Hammers,
+Cyril Scetbon,
+Dane Miller,
+David Multer,
+Dr. Frank Ullrich,
+Giuseppe Maxia,
+Google.com Site Reliability Engineers,
+Google Code,
+Jan Pieter Kunst,
+Jari Aalto,
+Jay Pipes,
+Jeremy Zawodny,
+Johan Idren,
+Kristian Kohntopp,
+Lenz Grimmer,
+Maciej Dobrzanski,
+Michiel Betel,
+MySQL AB,
+Paul McCullagh,
+Sebastien Estienne,
+Sourceforge.net,
+Steven Kreuzer,
+The Gentoo MySQL Team,
+Trevor Price,
+Yaar Schnitman,
+and probably more people that have not been included.
+
+(If your name has been misspelled, it's probably out of fear of putting
+international characters into this documentation; earlier versions of Perl might
+not be able to compile it then).
+
+=head1 COPYRIGHT, LICENSE AND WARRANTY
+
+This program is copyright (c) 2006 Baron Schwartz.
+Feedback and improvements are welcome.
+
+THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
+systems, you can issue `man perlgpl' or `man perlartistic' to read these
+licenses.
+
+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.
+
+Execute innotop and press '!' to see this information at any time.
+
+=head1 AUTHOR
+
+Originally written by Baron Schwartz; currently maintained by Aaron Racine.
+
+=head1 BUGS
+
+You can report bugs, ask for improvements, and get other help and support at
+L<http://code.google.com/p/innotop/>. There are mailing lists, a source code
+browser, a bug tracker, etc. Please use these instead of contacting the
+maintainer or author directly, as it makes our job easier and benefits others if the
+discussions are permanent and public. Of course, if you need to contact us in
+private, please do.
+
+=cut
--- /dev/null
+.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.07)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.el \{\
+. de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "INNOTOP 1"
+.TH INNOTOP 1 "2009-03-09" "perl v5.10.0" "User Contributed Perl Documentation"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+innotop \- MySQL and InnoDB transaction/status monitor.
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+To monitor servers normally:
+.PP
+.Vb 1
+\& innotop
+.Ve
+.PP
+To monitor InnoDB status information from a file:
+.PP
+.Vb 1
+\& innotop /var/log/mysql/mysqld.err
+.Ve
+.PP
+To run innotop non-interactively in a pipe-and-filter configuration:
+.PP
+.Vb 1
+\& innotop \-\-count 5 \-d 1 \-n
+.Ve
+.PP
+To monitor a database on another system using a particular username and password:
+.PP
+.Vb 1
+\& innotop \-u <username> \-p <password> \-h <hostname>
+.Ve
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+innotop monitors MySQL servers. Each of its modes shows you a different aspect
+of what's happening in the server. For example, there's a mode for monitoring
+replication, one for queries, and one for transactions. innotop refreshes its
+data periodically, so you see an updating view.
+.PP
+innotop has lots of features for power users, but you can start and run it with
+virtually no configuration. If you're just getting started, see
+\&\*(L"QUICK-START\*(R". Press '?' at any time while running innotop for
+context-sensitive help.
+.SH "QUICK-START"
+.IX Header "QUICK-START"
+To start innotop, open a terminal or command prompt. If you have installed
+innotop on your system, you should be able to just type \*(L"innotop\*(R" and press
+Enter; otherwise, you will need to change to innotop's directory and type \*(L"perl
+innotop\*(R".
+.PP
+With no options specified, innotop will attempt to connect to a MySQL server on
+localhost using mysql_read_default_group=client for other connection
+parameters. If you need to specify a different username and password, use the
+\&\-u and \-p options, respectively. To monitor a MySQL database on another
+host, use the \-h option.
+.PP
+After you've connected, innotop should show you something like the following:
+.PP
+.Vb 1
+\& [RO] Query List (? for help) localhost, 01:11:19, 449.44 QPS, 14/7/163 con/run
+\&
+\& CXN When Load QPS Slow QCacheHit KCacheHit BpsIn BpsOut
+\& localhost Total 0.00 1.07k 697 0.00% 98.17% 476.83k 242.83k
+\&
+\& CXN Cmd ID User Host DB Time Query
+\& localhost Query 766446598 test 10.0.0.1 foo 00:02 INSERT INTO table (
+.Ve
+.PP
+(This sample is truncated at the right so it will fit on a terminal when running
+\&'man innotop')
+.PP
+If your server is busy, you'll see more output. Notice the first line on the
+screen, which tells you that readonly is set to true ([\s-1RO\s0]), what mode you're
+in and what server you're connected to. You can change to other modes with
+keystrokes; press 'T' to switch to a list of InnoDB transactions, for example.
+.PP
+Press the '?' key to see what keys are active in the current mode. You can
+press any of these keys and innotop will either take the requested action or
+prompt you for more input. If your system has Term::ReadLine support, you can
+use \s-1TAB\s0 and other keys to auto-complete and edit input.
+.PP
+To quit innotop, press the 'q' key.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+innotop is mostly configured via its configuration file, but some of the
+configuration options can come from the command line. You can also specify a
+file to monitor for InnoDB status output; see \*(L"\s-1MONITORING\s0 A \s-1FILE\s0\*(R" for more
+details.
+.PP
+You can negate some options by prefixing the option name with \-\-no. For
+example, \-\-noinc (or \-\-no\-inc) negates \*(L"\-\-inc\*(R".
+.IP "\-\-color" 4
+.IX Item "--color"
+Enable or disable terminal coloring. Corresponds to the \*(L"color\*(R" config file
+setting.
+.IP "\-\-config" 4
+.IX Item "--config"
+Specifies a configuration file to read. This option is non-sticky, that is to
+say it does not persist to the configuration file itself.
+.IP "\-\-count" 4
+.IX Item "--count"
+Refresh only the specified number of times (ticks) before exiting. Each refresh
+is a pause for \*(L"interval\*(R" seconds, followed by requesting data from MySQL
+connections and printing it to the terminal.
+.IP "\-\-delay" 4
+.IX Item "--delay"
+Specifies the amount of time to pause between ticks (refreshes). Corresponds to
+the configuration option \*(L"interval\*(R".
+.IP "\-\-help" 4
+.IX Item "--help"
+Print a summary of command-line usage and exit.
+.IP "\-\-host" 4
+.IX Item "--host"
+Host to connect to.
+.IP "\-\-inc" 4
+.IX Item "--inc"
+Specifies whether innotop should display absolute numbers or relative numbers
+(offsets from their previous values). Corresponds to the configuration option
+\&\*(L"status_inc\*(R".
+.IP "\-\-mode" 4
+.IX Item "--mode"
+Specifies the mode in which innotop should start. Corresponds to the
+configuration option \*(L"mode\*(R".
+.IP "\-\-nonint" 4
+.IX Item "--nonint"
+Enable non-interactive operation. See \*(L"NON-INTERACTIVE \s-1OPERATION\s0\*(R" for more.
+.IP "\-\-password" 4
+.IX Item "--password"
+Password to use for connection.
+.IP "\-\-port" 4
+.IX Item "--port"
+Port to use for connection.
+.IP "\-\-skipcentral" 4
+.IX Item "--skipcentral"
+Don't read the central configuration file.
+.IP "\-\-user" 4
+.IX Item "--user"
+User to use for connection.
+.IP "\-\-version" 4
+.IX Item "--version"
+Output version information and exit.
+.IP "\-\-write" 4
+.IX Item "--write"
+Sets the configuration option \*(L"readonly\*(R" to 0, making innotop write the
+running configuration to ~/.innotop/innotop.conf on exit, if no configuration
+file was loaded at start-up.
+.SH "HOTKEYS"
+.IX Header "HOTKEYS"
+innotop is interactive, and you control it with key-presses.
+.IP "\(bu" 4
+Uppercase keys switch between modes.
+.IP "\(bu" 4
+Lowercase keys initiate some action within the current mode.
+.IP "\(bu" 4
+Other keys do something special like change configuration or show the
+innotop license.
+.PP
+Press '?' at any time to see the currently active keys and what they do.
+.SH "MODES"
+.IX Header "MODES"
+Each of innotop's modes retrieves and displays a particular type of data from
+the servers you're monitoring. You switch between modes with uppercase keys.
+The following is a brief description of each mode, in alphabetical order. To
+switch to the mode, press the key listed in front of its heading in the
+following list:
+.IP "B: InnoDB Buffers" 4
+.IX Item "B: InnoDB Buffers"
+This mode displays information about the InnoDB buffer pool, page statistics,
+insert buffer, and adaptive hash index. The data comes from \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0.
+.Sp
+This mode contains the \*(L"buffer_pool\*(R", \*(L"page_statistics\*(R",
+\&\*(L"insert_buffers\*(R", and \*(L"adaptive_hash_index\*(R" tables by default.
+.IP "C: Command Summary" 4
+.IX Item "C: Command Summary"
+This mode is similar to mytop's Command Summary mode. It shows the
+\&\*(L"cmd_summary\*(R" table, which looks something like the following:
+.Sp
+.Vb 8
+\& Command Summary (? for help) localhost, 25+07:16:43, 2.45 QPS, 3 thd, 5.0.40
+\& _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_ Command Summary _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_
+\& Name Value Pct Last Incr Pct
+\& Select_scan 3244858 69.89% 2 100.00%
+\& Select_range 1354177 29.17% 0 0.00%
+\& Select_full_join 39479 0.85% 0 0.00%
+\& Select_full_range_join 4097 0.09% 0 0.00%
+\& Select_range_check 0 0.00% 0 0.00%
+.Ve
+.Sp
+The command summary table is built by extracting variables from
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R". The variables must be numeric and must match the prefix
+given by the \*(L"cmd_filter\*(R" configuration variable. The variables are then
+sorted by value descending and compared to the last variable, as shown above.
+The percentage columns are percentage of the total of all variables in the
+table, so you can see the relative weight of the variables.
+.Sp
+The example shows what you see if the prefix is \*(L"Select_\*(R". The default
+prefix is \*(L"Com_\*(R". You can choose a prefix with the 's' key.
+.Sp
+It's rather like running \s-1SHOW\s0 \s-1VARIABLES\s0 \s-1LIKE\s0 \*(L"prefix%\*(R" with memory and
+nice formatting.
+.Sp
+Values are aggregated across all servers. The Pct columns are not correctly
+aggregated across multiple servers. This is a known limitation of the grouping
+algorithm that may be fixed in the future.
+.IP "D: InnoDB Deadlocks" 4
+.IX Item "D: InnoDB Deadlocks"
+This mode shows the transactions involved in the last InnoDB deadlock. A second
+table shows the locks each transaction held and waited for. A deadlock is
+caused by a cycle in the waits-for graph, so there should be two locks held and
+one waited for unless the deadlock information is truncated.
+.Sp
+InnoDB puts deadlock information before some other information in the \s-1SHOW\s0
+\&\s-1INNODB\s0 \s-1STATUS\s0 output. If there are a lot of locks, the deadlock information can
+grow very large, and there is a limit on the size of the \s-1SHOW\s0 \s-1INNODB\s0
+\&\s-1STATUS\s0 output. A large deadlock can fill the entire output, or even be
+truncated, and prevent you from seeing other information at all. If you are
+running innotop in another mode, for example T mode, and suddenly you don't see
+anything, you might want to check and see if a deadlock has wiped out the data
+you need.
+.Sp
+If it has, you can create a small deadlock to replace the large one. Use the
+\&'w' key to 'wipe' the large deadlock with a small one. This will not work
+unless you have defined a deadlock table for the connection (see \*(L"\s-1SERVER\s0
+\&\s-1CONNECTIONS\s0\*(R").
+.Sp
+You can also configure innotop to automatically detect when a large deadlock
+needs to be replaced with a small one (see \*(L"auto_wipe_dl\*(R").
+.Sp
+This mode displays the \*(L"deadlock_transactions\*(R" and \*(L"deadlock_locks\*(R" tables
+by default.
+.IP "F: InnoDB Foreign Key Errors" 4
+.IX Item "F: InnoDB Foreign Key Errors"
+This mode shows the last InnoDB foreign key error information, such as the
+table where it happened, when and who and what query caused it, and so on.
+.Sp
+InnoDB has a huge variety of foreign key error messages, and many of them are
+just hard to parse. innotop doesn't always do the best job here, but there's
+so much code devoted to parsing this messy, unparseable output that innotop is
+likely never to be perfect in this regard. If innotop doesn't show you what
+you need to see, just look at the status text directly.
+.Sp
+This mode displays the \*(L"fk_error\*(R" table by default.
+.IP "I: InnoDB I/O Info" 4
+.IX Item "I: InnoDB I/O Info"
+This mode shows InnoDB's I/O statistics, including the I/O threads, pending I/O,
+file I/O miscellaneous, and log statistics. It displays the \*(L"io_threads\*(R",
+\&\*(L"pending_io\*(R", \*(L"file_io_misc\*(R", and \*(L"log_statistics\*(R" tables by default.
+.IP "L: Locks" 4
+.IX Item "L: Locks"
+This mode shows information about current locks. At the moment only InnoDB
+locks are supported, and by default you'll only see locks for which transactions
+are waiting. This information comes from the \s-1TRANSACTIONS\s0 section of the InnoDB
+status text. If you have a very busy server, you may have frequent lock waits;
+it helps to be able to see which tables and indexes are the \*(L"hot spot\*(R" for
+locks. If your server is running pretty well, this mode should show nothing.
+.Sp
+You can configure MySQL and innotop to monitor not only locks for which a
+transaction is waiting, but those currently held, too. You can do this with the
+InnoDB Lock Monitor (<http://dev.mysql.com/doc/en/innodb\-monitor.html>). It's
+not documented in the MySQL manual, but creating the lock monitor with the
+following statement also affects the output of \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0, which innotop
+uses:
+.Sp
+.Vb 1
+\& CREATE TABLE innodb_lock_monitor(a int) ENGINE=INNODB;
+.Ve
+.Sp
+This causes InnoDB to print its output to the MySQL file every 16 seconds or so,
+as stated in the manual, but it also makes the normal \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0 output
+include lock information, which innotop can parse and display (that's the
+undocumented feature).
+.Sp
+This means you can do what may have seemed impossible: to a limited extent
+(InnoDB truncates some information in the output), you can see which transaction
+holds the locks something else is waiting for. You can also enable and disable
+the InnoDB Lock Monitor with the key mappings in this mode.
+.Sp
+This mode displays the \*(L"innodb_locks\*(R" table by default. Here's a sample of
+the screen when one connection is waiting for locks another connection holds:
+.Sp
+.Vb 7
+\& _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_ InnoDB Locks _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_
+\& CXN ID Type Waiting Wait Active Mode DB Table Index
+\& localhost 12 RECORD 1 00:10 00:10 X test t1 PRIMARY
+\& localhost 12 TABLE 0 00:10 00:10 IX test t1
+\& localhost 12 RECORD 1 00:10 00:10 X test t1 PRIMARY
+\& localhost 11 TABLE 0 00:00 00:25 IX test t1
+\& localhost 11 RECORD 0 00:00 00:25 X test t1 PRIMARY
+.Ve
+.Sp
+You can see the first connection, \s-1ID\s0 12, is waiting for a lock on the \s-1PRIMARY\s0
+key on test.t1, and has been waiting for 10 seconds. The second connection
+isn't waiting, because the Waiting column is 0, but it holds locks on the same
+index. That tells you connection 11 is blocking connection 12.
+.IP "M: Master/Slave Replication Status" 4
+.IX Item "M: Master/Slave Replication Status"
+This mode shows the output of \s-1SHOW\s0 \s-1SLAVE\s0 \s-1STATUS\s0 and \s-1SHOW\s0 \s-1MASTER\s0 \s-1STATUS\s0 in three
+tables. The first two divide the slave's status into \s-1SQL\s0 and I/O thread status,
+and the last shows master status. Filters are applied to eliminate non-slave
+servers from the slave tables, and non-master servers from the master table.
+.Sp
+This mode displays the \*(L"slave_sql_status\*(R", \*(L"slave_io_status\*(R", and
+\&\*(L"master_status\*(R" tables by default.
+.IP "O: Open Tables" 4
+.IX Item "O: Open Tables"
+This section comes from MySQL's \s-1SHOW\s0 \s-1OPEN\s0 \s-1TABLES\s0 command. By default it is
+filtered to show tables which are in use by one or more queries, so you can
+get a quick look at which tables are 'hot'. You can use this to guess which
+tables might be locked implicitly.
+.Sp
+This mode displays the \*(L"open_tables\*(R" mode by default.
+.IP "Q: Query List" 4
+.IX Item "Q: Query List"
+This mode displays the output from \s-1SHOW\s0 \s-1FULL\s0 \s-1PROCESSLIST\s0, much like \fBmytop\fR's
+query list mode. This mode does \fBnot\fR show InnoDB-related information. This
+is probably one of the most useful modes for general usage.
+.Sp
+There is an informative header that shows general status information about
+your server. You can toggle it on and off with the 'h' key. By default,
+innotop hides inactive processes and its own process. You can toggle these on
+and off with the 'i' and 'a' keys.
+.Sp
+You can \s-1EXPLAIN\s0 a query from this mode with the 'e' key. This displays the
+query's full text, the results of \s-1EXPLAIN\s0, and in newer MySQL versions, even
+the optimized query resulting from \s-1EXPLAIN\s0 \s-1EXTENDED\s0. innotop also tries to
+rewrite certain queries to make them EXPLAIN-able. For example, \s-1INSERT/SELECT\s0
+statements are rewritable.
+.Sp
+This mode displays the \*(L"q_header\*(R" and \*(L"processlist\*(R" tables by default.
+.IP "R: InnoDB Row Operations and Semaphores" 4
+.IX Item "R: InnoDB Row Operations and Semaphores"
+This mode shows InnoDB row operations, row operation miscellaneous, semaphores,
+and information from the wait array. It displays the \*(L"row_operations\*(R",
+\&\*(L"row_operation_misc\*(R", \*(L"semaphores\*(R", and \*(L"wait_array\*(R" tables by default.
+.IP "S: Variables & Status" 4
+.IX Item "S: Variables & Status"
+This mode calculates statistics, such as queries per second, and prints them out
+in several different styles. You can show absolute values, or incremental values
+between ticks.
+.Sp
+You can switch between the views by pressing a key. The 's' key prints a
+single line each time the screen updates, in the style of \fBvmstat\fR. The 'g'
+key changes the view to a graph of the same numbers, sort of like \fBtload\fR.
+The 'v' key changes the view to a pivoted table of variable names on the left,
+with successive updates scrolling across the screen from left to right. You can
+choose how many updates to put on the screen with the \*(L"num_status_sets\*(R"
+configuration variable.
+.Sp
+Headers may be abbreviated to fit on the screen in interactive operation. You
+choose which variables to display with the 'c' key, which selects from
+predefined sets, or lets you create your own sets. You can edit the current set
+with the 'e' key.
+.Sp
+This mode doesn't really display any tables like other modes. Instead, it uses
+a table definition to extract and format the data, but it then transforms the
+result in special ways before outputting it. It uses the \*(L"var_status\*(R" table
+definition for this.
+.IP "T: InnoDB Transactions" 4
+.IX Item "T: InnoDB Transactions"
+This mode shows transactions from the InnoDB monitor's output, in \fBtop\fR\-like
+format. This mode is the reason I wrote innotop.
+.Sp
+You can kill queries or processes with the 'k' and 'x' keys, and \s-1EXPLAIN\s0 a query
+with the 'e' or 'f' keys. InnoDB doesn't print the full query in transactions,
+so explaining may not work right if the query is truncated.
+.Sp
+The informational header can be toggled on and off with the 'h' key. By
+default, innotop hides inactive transactions and its own transaction. You can
+toggle this on and off with the 'i' and 'a' keys.
+.Sp
+This mode displays the \*(L"t_header\*(R" and \*(L"innodb_transactions\*(R" tables by
+default.
+.SH "INNOTOP STATUS"
+.IX Header "INNOTOP STATUS"
+The first line innotop displays is a \*(L"status bar\*(R" of sorts. What it contains
+depends on the mode you're in, and what servers you're monitoring. The first
+few words are always [\s-1RO\s0] (if readonly is set to 1), the innotop mode, such as
+\&\*(L"InnoDB Txns\*(R" for T mode, followed by a reminder to press '?' for help at any
+time.
+.SS "\s-1ONE\s0 \s-1SERVER\s0"
+.IX Subsection "ONE SERVER"
+The simplest case is when you're monitoring a single server. In this case, the
+name of the connection is next on the status line. This is the name you gave
+when you created the connection \*(-- most likely the MySQL server's hostname.
+This is followed by the server's uptime.
+.PP
+If you're in an InnoDB mode, such as T or B, the next word is \*(L"InnoDB\*(R" followed
+by some information about the \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0 output used to render the
+screen. The first word is the number of seconds since the last \s-1SHOW\s0 \s-1INNODB\s0
+\&\s-1STATUS\s0, which InnoDB uses to calculate some per-second statistics. The next is
+a smiley face indicating whether the InnoDB output is truncated. If the smiley
+face is a :\-), all is well; there is no truncation. A :^| means the transaction
+list is so long, InnoDB has only printed out some of the transactions. Finally,
+a frown :\-( means the output is incomplete, which is probably due to a deadlock
+printing too much lock information (see \*(L"D: InnoDB Deadlocks\*(R").
+.PP
+The next two words indicate the server's queries per second (\s-1QPS\s0) and how many
+threads (connections) exist. Finally, the server's version number is the last
+thing on the line.
+.SS "\s-1MULTIPLE\s0 \s-1SERVERS\s0"
+.IX Subsection "MULTIPLE SERVERS"
+If you are monitoring multiple servers (see \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R"), the status
+line does not show any details about individual servers. Instead, it shows the
+names of the connections that are active. Again, these are connection names you
+specified, which are likely to be the server's hostname. A connection that has
+an error is prefixed with an exclamation point.
+.PP
+If you are monitoring a group of servers (see \*(L"\s-1SERVER\s0 \s-1GROUPS\s0\*(R"), the status
+line shows the name of the group. If any connection in the group has an
+error, the group's name is followed by the fraction of the connections that
+don't have errors.
+.PP
+See \*(L"\s-1ERROR\s0 \s-1HANDLING\s0\*(R" for more details about innotop's error handling.
+.SS "\s-1MONITORING\s0 A \s-1FILE\s0"
+.IX Subsection "MONITORING A FILE"
+If you give a filename on the command line, innotop will not connect to \s-1ANY\s0
+servers at all. It will watch the specified file for InnoDB status output and
+use that as its data source. It will always show a single connection called
+\&'file'. And since it can't connect to a server, it can't determine how long the
+server it's monitoring has been up; so it calculates the server's uptime as time
+since innotop started running.
+.SH "SERVER ADMINISTRATION"
+.IX Header "SERVER ADMINISTRATION"
+While innotop is primarily a monitor that lets you watch and analyze your
+servers, it can also send commands to servers. The most frequently useful
+commands are killing queries and stopping or starting slaves.
+.PP
+You can kill a connection, or in newer versions of MySQL kill a query but not a
+connection, from \*(L"Q: Query List\*(R" and \*(L"T: InnoDB Transactions\*(R" modes.
+Press 'k' to issue a \s-1KILL\s0 command, or 'x' to issue a \s-1KILL\s0 \s-1QUERY\s0 command.
+innotop will prompt you for the server and/or connection \s-1ID\s0 to kill (innotop
+does not prompt you if there is only one possible choice for any input).
+innotop pre-selects the longest-running query, or the oldest connection.
+Confirm the command with 'y'.
+.PP
+In \*(L"Slave Replication Status\*(R"\*(L" in \*(R"M: Master mode, you can start and stop slaves
+with the 'a' and 'o' keys, respectively. You can send these commands to many
+slaves at once. innotop fills in a default command of \s-1START\s0 \s-1SLAVE\s0 or \s-1STOP\s0 \s-1SLAVE\s0
+for you, but you can actually edit the command and send anything you wish, such
+as \s-1SET\s0 \s-1GLOBAL\s0 SQL_SLAVE_SKIP_COUNTER=1 to make the slave skip one binlog event
+when it starts.
+.PP
+You can also ask innotop to calculate the earliest binlog in use by any slave
+and issue a \s-1PURGE\s0 \s-1MASTER\s0 \s-1LOGS\s0 on the master. Use the 'b' key for this. innotop
+will prompt you for a master to run the command on, then prompt you for the
+connection names of that master's slaves (there is no way for innotop to
+determine this reliably itself). innotop will find the minimum binlog in use by
+these slave connections and suggest it as the argument to \s-1PURGE\s0 \s-1MASTER\s0 \s-1LOGS\s0.
+.SH "SERVER CONNECTIONS"
+.IX Header "SERVER CONNECTIONS"
+When you create a server connection using '@', innotop asks you for a series of
+inputs, as follows:
+.IP "\s-1DSN\s0" 4
+.IX Item "DSN"
+A \s-1DSN\s0 is a Data Source Name, which is the initial argument passed to the \s-1DBI\s0
+module for connecting to a server. It is usually of the form
+.Sp
+.Vb 1
+\& DBI:mysql:;mysql_read_default_group=mysql;host=HOSTNAME
+.Ve
+.Sp
+Since this \s-1DSN\s0 is passed to the DBD::mysql driver, you should read the driver's
+documentation at \*(L"/search.cpan.org/dist/DBD\-mysql/lib/DBD/mysql.pm\*(R"\*(L" in \*(R"http: for
+the exact details on all the options you can pass the driver in the \s-1DSN\s0. You
+can read more about \s-1DBI\s0 at <http://dbi.perl.org/docs/>, and especially at
+<http://search.cpan.org/~timb/DBI/DBI.pm>.
+.Sp
+The mysql_read_default_group=mysql option lets the \s-1DBD\s0 driver read your MySQL
+options files, such as ~/.my.cnf on UNIX-ish systems. You can use this to avoid
+specifying a username or password for the connection.
+.IP "InnoDB Deadlock Table" 4
+.IX Item "InnoDB Deadlock Table"
+This optional item tells innotop a table name it can use to deliberately create
+a small deadlock (see \*(L"D: InnoDB Deadlocks\*(R"). If you specify this option,
+you just need to be sure the table doesn't exist, and that innotop can create
+and drop the table with the InnoDB storage engine. You can safely omit or just
+accept the default if you don't intend to use this.
+.IP "Username" 4
+.IX Item "Username"
+innotop will ask you if you want to specify a username. If you say 'y', it will
+then prompt you for a user name. If you have a MySQL option file that specifies
+your username, you don't have to specify a username.
+.Sp
+The username defaults to your login name on the system you're running innotop on.
+.IP "Password" 4
+.IX Item "Password"
+innotop will ask you if you want to specify a password. Like the username, the
+password is optional, but there's an additional prompt that asks if you want to
+save the password in the innotop configuration file. If you don't save it in
+the configuration file, innotop will prompt you for a password each time it
+starts. Passwords in the innotop configuration file are saved in plain text,
+not encrypted in any way.
+.PP
+Once you finish answering these questions, you should be connected to a server.
+But innotop isn't limited to monitoring a single server; you can define many
+server connections and switch between them by pressing the '@' key. See
+\&\*(L"\s-1SWITCHING\s0 \s-1BETWEEN\s0 \s-1CONNECTIONS\s0\*(R".
+.SH "SERVER GROUPS"
+.IX Header "SERVER GROUPS"
+If you have multiple MySQL instances, you can put them into named groups, such
+as 'all', 'masters', and 'slaves', which innotop can monitor all together.
+.PP
+You can choose which group to monitor with the '#' key, and you can press the
+\&\s-1TAB\s0 key to switch to the next group. If you're not currently monitoring a
+group, pressing \s-1TAB\s0 selects the first group.
+.PP
+To create a group, press the '#' key and type the name of your new group, then
+type the names of the connections you want the group to contain.
+.SH "SWITCHING BETWEEN CONNECTIONS"
+.IX Header "SWITCHING BETWEEN CONNECTIONS"
+innotop lets you quickly switch which servers you're monitoring. The most basic
+way is by pressing the '@' key and typing the name(s) of the connection(s) you
+want to use. This setting is per-mode, so you can monitor different connections
+in each mode, and innotop remembers which connections you choose.
+.PP
+You can quickly switch to the 'next' connection in alphabetical order with the
+\&'n' key. If you're monitoring a server group (see \*(L"\s-1SERVER\s0 \s-1GROUPS\s0\*(R") this will
+switch to the first connection.
+.PP
+You can also type many connection names, and innotop will fetch and display data
+from them all. Just separate the connection names with spaces, for example
+\&\*(L"server1 server2.\*(R" Again, if you type the name of a connection that doesn't
+exist, innotop will prompt you for connection information and create the
+connection.
+.PP
+Another way to monitor multiple connections at once is with server groups. You
+can use the \s-1TAB\s0 key to switch to the 'next' group in alphabetical order, or if
+you're not monitoring any groups, \s-1TAB\s0 will switch to the first group.
+.PP
+innotop does not fetch data in parallel from connections, so if you are
+monitoring a large group or many connections, you may notice increased delay
+between ticks.
+.PP
+When you monitor more than one connection, innotop's status bar changes. See
+\&\*(L"\s-1INNOTOP\s0 \s-1STATUS\s0\*(R".
+.SH "ERROR HANDLING"
+.IX Header "ERROR HANDLING"
+Error handling is not that important when monitoring a single connection, but is
+crucial when you have many active connections. A crashed server or lost
+connection should not crash innotop. As a result, innotop will continue to run
+even when there is an error; it just won't display any information from the
+connection that had an error. Because of this, innotop's behavior might confuse
+you. It's a feature, not a bug!
+.PP
+innotop does not continue to query connections that have errors, because they
+may slow innotop and make it hard to use, especially if the error is a problem
+connecting and causes a long time-out. Instead, innotop retries the connection
+occasionally to see if the error still exists. If so, it will wait until some
+point in the future. The wait time increases in ticks as the Fibonacci series,
+so it tries less frequently as time passes.
+.PP
+Since errors might only happen in certain modes because of the \s-1SQL\s0 commands
+issued in those modes, innotop keeps track of which mode caused the error. If
+you switch to a different mode, innotop will retry the connection instead of
+waiting.
+.PP
+By default innotop will display the problem in red text at the bottom of the
+first table on the screen. You can disable this behavior with the
+\&\*(L"show_cxn_errors_in_tbl\*(R" configuration option, which is enabled by default.
+If the \*(L"debug\*(R" option is enabled, innotop will display the error at the
+bottom of every table, not just the first. And if \*(L"show_cxn_errors\*(R" is
+enabled, innotop will print the error text to \s-1STDOUT\s0 as well. Error messages
+might only display in the mode that caused the error, depending on the mode and
+whether innotop is avoiding querying that connection.
+.SH "NON-INTERACTIVE OPERATION"
+.IX Header "NON-INTERACTIVE OPERATION"
+You can run innotop in non-interactive mode, in which case it is entirely
+controlled from the configuration file and command-line options. To start
+innotop in non-interactive mode, give the L\*(L"<\-\-nonint\*(R"> command-line option.
+This changes innotop's behavior in the following ways:
+.IP "\(bu" 4
+Certain Perl modules are not loaded. Term::Readline is not loaded, since
+innotop doesn't prompt interactively. Term::ANSIColor and Win32::Console::ANSI
+modules are not loaded. Term::ReadKey is still used, since innotop may have to
+prompt for connection passwords when starting up.
+.IP "\(bu" 4
+innotop does not clear the screen after each tick.
+.IP "\(bu" 4
+innotop does not persist any changes to the configuration file.
+.IP "\(bu" 4
+If \*(L"\-\-count\*(R" is given and innotop is in incremental mode (see \*(L"status_inc\*(R"
+and \*(L"\-\-inc\*(R"), innotop actually refreshes one more time than specified so it
+can print incremental statistics. This suppresses output during the first
+tick, so innotop may appear to hang.
+.IP "\(bu" 4
+innotop only displays the first table in each mode. This is so the output can
+be easily processed with other command-line utilities such as awk and sed. To
+change which tables display in each mode, see \*(L"\s-1TABLES\s0\*(R". Since \*(L"Q: Query
+List\*(R" mode is so important, innotop automatically disables the \*(L"q_header\*(R"
+table. This ensures you'll see the \*(L"processlist\*(R" table, even if you have
+innotop configured to show the q_header table during interactive operation.
+Similarly, in \*(L"T: InnoDB Transactions\*(R" mode, the \*(L"t_header\*(R" table is
+suppressed so you see only the \*(L"innodb_transactions\*(R" table.
+.IP "\(bu" 4
+All output is tab-separated instead of being column-aligned with whitespace, and
+innotop prints the full contents of each table instead of only printing one
+screenful at a time.
+.IP "\(bu" 4
+innotop only prints column headers once instead of every tick (see
+\&\*(L"hide_hdr\*(R"). innotop does not print table captions (see
+\&\*(L"display_table_captions\*(R"). innotop ensures there are no empty lines in the
+output.
+.IP "\(bu" 4
+innotop does not honor the \*(L"shorten\*(R" transformation, which normally shortens
+some numbers to human-readable formats.
+.IP "\(bu" 4
+innotop does not print a status line (see \*(L"\s-1INNOTOP\s0 \s-1STATUS\s0\*(R").
+.SH "CONFIGURING"
+.IX Header "CONFIGURING"
+Nearly everything about innotop is configurable. Most things are possible to
+change with built-in commands, but you can also edit the configuration file.
+.PP
+While running innotop, press the '$' key to bring up the configuration editing
+dialog. Press another key to select the type of data you want to edit:
+.IP "S: Statement Sleep Times" 4
+.IX Item "S: Statement Sleep Times"
+Edits \s-1SQL\s0 statement sleep delays, which make innotop pause for the specified
+amount of time after executing a statement. See \*(L"\s-1SQL\s0 \s-1STATEMENTS\s0\*(R" for a
+definition of each statement and what it does. By default innotop does not
+delay after any statements.
+.Sp
+This feature is included so you can customize the side-effects caused by
+monitoring your server. You may not see any effects, but some innotop users
+have noticed that certain MySQL versions under very high load with InnoDB
+enabled take longer than usual to execute \s-1SHOW\s0 \s-1GLOBAL\s0 \s-1STATUS\s0. If innotop calls
+\&\s-1SHOW\s0 \s-1FULL\s0 \s-1PROCESSLIST\s0 immediately afterward, the processlist contains more
+queries than the machine actually averages at any given moment. Configuring
+innotop to pause briefly after calling \s-1SHOW\s0 \s-1GLOBAL\s0 \s-1STATUS\s0 alleviates this
+effect.
+.Sp
+Sleep times are stored in the \*(L"stmt_sleep_times\*(R" section of the configuration
+file. Fractional-second sleeps are supported, subject to your hardware's
+limitations.
+.IP "c: Edit Columns" 4
+.IX Item "c: Edit Columns"
+Starts the table editor on one of the displayed tables. See \*(L"\s-1TABLE\s0 \s-1EDITOR\s0\*(R".
+An alternative way to start the table editor without entering the configuration
+dialog is with the '^' key.
+.IP "g: General Configuration" 4
+.IX Item "g: General Configuration"
+Starts the configuration editor to edit global and mode-specific configuration
+variables (see \*(L"\s-1MODES\s0\*(R"). innotop prompts you to choose a variable from among
+the global and mode-specific ones depending on the current mode.
+.IP "k: Row-Coloring Rules" 4
+.IX Item "k: Row-Coloring Rules"
+Starts the row-coloring rules editor on one of the displayed table(s). See
+\&\*(L"\s-1COLORS\s0\*(R" for details.
+.IP "p: Manage Plugins" 4
+.IX Item "p: Manage Plugins"
+Starts the plugin configuration editor. See \*(L"\s-1PLUGINS\s0\*(R" for details.
+.IP "s: Server Groups" 4
+.IX Item "s: Server Groups"
+Lets you create and edit server groups. See \*(L"\s-1SERVER\s0 \s-1GROUPS\s0\*(R".
+.IP "t: Choose Displayed Tables" 4
+.IX Item "t: Choose Displayed Tables"
+Lets you choose which tables to display in this mode. See \*(L"\s-1MODES\s0\*(R" and
+\&\*(L"\s-1TABLES\s0\*(R".
+.SH "CONFIGURATION FILE"
+.IX Header "CONFIGURATION FILE"
+innotop's default configuration file locations are \f(CW$HOME\fR/.innotop and
+/etc/innotop/innotop.conf, and they are looked for in that order. If the first
+configuration file exists, the second will not be processed. Those can be
+overridden with the \*(L"\-\-config\*(R" command-line option. You can edit it by hand
+safely, however innotop reads the configuration file when it starts, and, if
+readonly is set to 0, writes it out again when it exits. Thus, if readonly is
+set to 0, any changes you make by hand while innotop is running will be lost.
+.PP
+innotop doesn't store its entire configuration in the configuration file. It
+has a huge set of default configuration values that it holds only in memory,
+and the configuration file only overrides these defaults. When you customize a
+default setting, innotop notices, and then stores the customizations into the
+file. This keeps the file size down, makes it easier to edit, and makes
+upgrades easier.
+.PP
+A configuration file is read-only be default. You can override that with
+\&\*(L"\-\-write\*(R". See \*(L"readonly\*(R".
+.PP
+The configuration file is arranged into sections like an \s-1INI\s0 file. Each
+section begins with [section\-name] and ends with [/section\-name]. Each
+section's entries have a different syntax depending on the data they need to
+store. You can put comments in the file; any line that begins with a #
+character is a comment. innotop will not read the comments, so it won't write
+them back out to the file when it exits. Comments in read-only configuration
+files are still useful, though.
+.PP
+The first line in the file is innotop's version number. This lets innotop
+notice when the file format is not backwards-compatible, and upgrade smoothly
+without destroying your customized configuration.
+.PP
+The following list describes each section of the configuration file and the data
+it contains:
+.IP "general" 4
+.IX Item "general"
+The 'general' section contains global configuration variables and variables that
+may be mode-specific, but don't belong in any other section. The syntax is a
+simple key=value list. innotop writes a comment above each value to help you
+edit the file by hand.
+.RS 4
+.IP "S_func" 4
+.IX Item "S_func"
+Controls S mode presentation (see \*(L"S: Variables & Status\*(R"). If g, values are
+graphed; if s, values are like vmstat; if p, values are in a pivoted table.
+.IP "S_set" 4
+.IX Item "S_set"
+Specifies which set of variables to display in \*(L"S: Variables & Status\*(R" mode.
+See \*(L"\s-1VARIABLE\s0 \s-1SETS\s0\*(R".
+.IP "auto_wipe_dl" 4
+.IX Item "auto_wipe_dl"
+Instructs innotop to automatically wipe large deadlocks when it notices them.
+When this happens you may notice a slight delay. At the next tick, you will
+usually see the information that was being truncated by the large deadlock.
+.IP "charset" 4
+.IX Item "charset"
+Specifies what kind of characters to allow through the \*(L"no_ctrl_char\*(R"
+transformation. This keeps non-printable characters from confusing a
+terminal when you monitor queries that contain binary data, such as images.
+.Sp
+The default is 'ascii', which considers anything outside normal \s-1ASCII\s0 to be a
+control character. The other allowable values are 'unicode' and 'none'. 'none'
+considers every character a control character, which can be useful for
+collapsing \s-1ALL\s0 text fields in queries.
+.IP "cmd_filter" 4
+.IX Item "cmd_filter"
+This is the prefix that filters variables in \*(L"C: Command Summary\*(R" mode.
+.IP "color" 4
+.IX Item "color"
+Whether terminal coloring is permitted.
+.IP "cxn_timeout" 4
+.IX Item "cxn_timeout"
+On MySQL versions 4.0.3 and newer, this variable is used to set the connection's
+timeout, so MySQL doesn't close the connection if it is not used for a while.
+This might happen because a connection isn't monitored in a particular mode, for
+example.
+.IP "debug" 4
+.IX Item "debug"
+This option enables more verbose errors and makes innotop more strict in some
+places. It can help in debugging filters and other user-defined code. It also
+makes innotop write a lot of information to \*(L"debugfile\*(R" when there is a
+crash.
+.IP "debugfile" 4
+.IX Item "debugfile"
+A file to which innotop will write information when there is a crash. See
+\&\*(L"\s-1FILES\s0\*(R".
+.IP "display_table_captions" 4
+.IX Item "display_table_captions"
+innotop displays a table caption above most tables. This variable suppresses or
+shows captions on all tables globally. Some tables are configured with the
+hide_caption property, which overrides this.
+.IP "global" 4
+.IX Item "global"
+Whether to show \s-1GLOBAL\s0 variables and status. innotop only tries to do this on
+servers which support the \s-1GLOBAL\s0 option to \s-1SHOW\s0 \s-1VARIABLES\s0 and \s-1SHOW\s0 \s-1STATUS\s0. In
+some MySQL versions, you need certain privileges to do this; if you don't have
+them, innotop will not be able to fetch any variable and status data. This
+configuration variable lets you run innotop and fetch what data you can even
+without the elevated privileges.
+.Sp
+I can no longer find or reproduce the situation where \s-1GLOBAL\s0 wasn't allowed, but
+I know there was one.
+.IP "graph_char" 4
+.IX Item "graph_char"
+Defines the character to use when drawing graphs in \*(L"S: Variables & Status\*(R"
+mode.
+.IP "header_highlight" 4
+.IX Item "header_highlight"
+Defines how to highlight column headers. This only works if Term::ANSIColor is
+available. Valid values are 'bold' and 'underline'.
+.IP "hide_hdr" 4
+.IX Item "hide_hdr"
+Hides column headers globally.
+.IP "interval" 4
+.IX Item "interval"
+The interval at which innotop will refresh its data (ticks). The interval is
+implemented as a sleep time between ticks, so the true interval will vary
+depending on how long it takes innotop to fetch and render data.
+.Sp
+This variable accepts fractions of a second.
+.IP "mode" 4
+.IX Item "mode"
+The mode in which innotop should start. Allowable arguments are the same as the
+key presses that select a mode interactively. See \*(L"\s-1MODES\s0\*(R".
+.IP "num_digits" 4
+.IX Item "num_digits"
+How many digits to show in fractional numbers and percents. This variable's
+range is between 0 and 9 and can be set directly from \*(L"S: Variables & Status\*(R"
+mode with the '+' and '\-' keys. It is used in the \*(L"set_precision\*(R",
+\&\*(L"shorten\*(R", and \*(L"percent\*(R" transformations.
+.IP "num_status_sets" 4
+.IX Item "num_status_sets"
+Controls how many sets of status variables to display in pivoted \*(L"S: Variables
+& Status\*(R" mode. It also controls the number of old sets of variables innotop
+keeps in its memory, so the larger this variable is, the more memory innotop
+uses.
+.IP "plugin_dir" 4
+.IX Item "plugin_dir"
+Specifies where plugins can be found. By default, innotop stores plugins in the
+\&'plugins' subdirectory of your innotop configuration directory.
+.IP "readonly" 4
+.IX Item "readonly"
+Whether the configuration file is readonly. This cannot be set interactively.
+.IP "show_cxn_errors" 4
+.IX Item "show_cxn_errors"
+Makes innotop print connection errors to \s-1STDOUT\s0. See \*(L"\s-1ERROR\s0 \s-1HANDLING\s0\*(R".
+.IP "show_cxn_errors_in_tbl" 4
+.IX Item "show_cxn_errors_in_tbl"
+Makes innotop display connection errors as rows in the first table on screen.
+See \*(L"\s-1ERROR\s0 \s-1HANDLING\s0\*(R".
+.IP "show_percent" 4
+.IX Item "show_percent"
+Adds a '%' character after the value returned by the \*(L"percent\*(R"
+transformation.
+.IP "show_statusbar" 4
+.IX Item "show_statusbar"
+Controls whether to show the status bar in the display. See \*(L"\s-1INNOTOP\s0
+\&\s-1STATUS\s0\*(R".
+.IP "skip_innodb" 4
+.IX Item "skip_innodb"
+Disables fetching \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0, in case your server(s) do not have InnoDB
+enabled and you don't want innotop to try to fetch it. This can also be useful
+when you don't have the \s-1SUPER\s0 privilege, required to run \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0.
+.IP "status_inc" 4
+.IX Item "status_inc"
+Whether to show absolute or incremental values for status variables.
+Incremental values are calculated as an offset from the last value innotop saw
+for that variable. This is a global setting, but will probably become
+mode-specific at some point. Right now it is honored a bit inconsistently; some
+modes don't pay attention to it.
+.RE
+.RS 4
+.RE
+.IP "plugins" 4
+.IX Item "plugins"
+This section holds a list of package names of active plugins. If the plugin
+exists, innotop will activate it. See \*(L"\s-1PLUGINS\s0\*(R" for more information.
+.IP "filters" 4
+.IX Item "filters"
+This section holds user-defined filters (see \*(L"\s-1FILTERS\s0\*(R"). Each line is in the
+format filter_name=text='filter text' tbls='table list'.
+.Sp
+The filter text is the text of the subroutine's code. The table list is a list
+of tables to which the filter can apply. By default, user-defined filters apply
+to the table for which they were created, but you can manually override that by
+editing the definition in the configuration file.
+.IP "active_filters" 4
+.IX Item "active_filters"
+This section stores which filters are active on each table. Each line is in the
+format table_name=filter_list.
+.IP "tbl_meta" 4
+.IX Item "tbl_meta"
+This section stores user-defined or user-customized columns (see \*(L"\s-1COLUMNS\s0\*(R").
+Each line is in the format col_name=properties, where the properties are a
+name=quoted\-value list.
+.IP "connections" 4
+.IX Item "connections"
+This section holds the server connections you have defined. Each line is in
+the format name=properties, where the properties are a name=value list. The
+properties are self-explanatory, and the only one that is treated specially is
+\&'pass' which is only present if 'savepass' is set. This section of the
+configuration file will be skipped if any \s-1DSN\s0, username, or password
+command-line options are used. See \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R".
+.IP "active_connections" 4
+.IX Item "active_connections"
+This section holds a list of which connections are active in each mode. Each
+line is in the format mode_name=connection_list.
+.IP "server_groups" 4
+.IX Item "server_groups"
+This section holds server groups. Each line is in the format
+name=connection_list. See \*(L"\s-1SERVER\s0 \s-1GROUPS\s0\*(R".
+.IP "active_server_groups" 4
+.IX Item "active_server_groups"
+This section holds a list of which server group is active in each mode. Each
+line is in the format mode_name=server_group.
+.IP "max_values_seen" 4
+.IX Item "max_values_seen"
+This section holds the maximum values seen for variables. This is used to scale
+the graphs in \*(L"S: Variables & Status\*(R" mode. Each line is in the format
+name=value.
+.IP "active_columns" 4
+.IX Item "active_columns"
+This section holds table column lists. Each line is in the format
+tbl_name=column_list. See \*(L"\s-1COLUMNS\s0\*(R".
+.IP "sort_cols" 4
+.IX Item "sort_cols"
+This section holds the sort definition. Each line is in the format
+tbl_name=column_list. If a column is prefixed with '\-', that column sorts
+descending. See \*(L"\s-1SORTING\s0\*(R".
+.IP "visible_tables" 4
+.IX Item "visible_tables"
+This section defines which tables are visible in each mode. Each line is in the
+format mode_name=table_list. See \*(L"\s-1TABLES\s0\*(R".
+.IP "varsets" 4
+.IX Item "varsets"
+This section defines variable sets for use in \*(L"S: Status & Variables\*(R" mode.
+Each line is in the format name=variable_list. See \*(L"\s-1VARIABLE\s0 \s-1SETS\s0\*(R".
+.IP "colors" 4
+.IX Item "colors"
+This section defines colorization rules. Each line is in the format
+tbl_name=property_list. See \*(L"\s-1COLORS\s0\*(R".
+.IP "stmt_sleep_times" 4
+.IX Item "stmt_sleep_times"
+This section contains statement sleep times. Each line is in the format
+statement_name=sleep_time. See \*(L"S: Statement Sleep Times\*(R".
+.IP "group_by" 4
+.IX Item "group_by"
+This section contains column lists for table group_by expressions. Each line is
+in the format tbl_name=column_list. See \*(L"\s-1GROUPING\s0\*(R".
+.SH "CUSTOMIZING"
+.IX Header "CUSTOMIZING"
+You can customize innotop a great deal. For example, you can:
+.IP "\(bu" 4
+Choose which tables to display, and in what order.
+.IP "\(bu" 4
+Choose which columns are in those tables, and create new columns.
+.IP "\(bu" 4
+Filter which rows display with built-in filters, user-defined filters, and
+quick-filters.
+.IP "\(bu" 4
+Sort the rows to put important data first or group together related rows.
+.IP "\(bu" 4
+Highlight rows with color.
+.IP "\(bu" 4
+Customize the alignment, width, and formatting of columns, and apply
+transformations to columns to extract parts of their values or format the values
+as you wish (for example, shortening large numbers to familiar units).
+.IP "\(bu" 4
+Design your own expressions to extract and combine data as you need. This gives
+you unlimited flexibility.
+.PP
+All these and more are explained in the following sections.
+.SS "\s-1TABLES\s0"
+.IX Subsection "TABLES"
+A table is what you'd expect: a collection of columns. It also has some other
+properties, such as a caption. Filters, sorting rules, and colorization rules
+belong to tables and are covered in later sections.
+.PP
+Internally, table meta-data is defined in a data structure called \f(CW%tbl_meta\fR.
+This hash holds all built-in table definitions, which contain a lot of default
+instructions to innotop. The meta-data includes the caption, a list of columns
+the user has customized, a list of columns, a list of visible columns, a list of
+filters, color rules, a sort-column list, sort direction, and some information
+about the table's data sources. Most of this is customizable via the table
+editor (see \*(L"\s-1TABLE\s0 \s-1EDITOR\s0\*(R").
+.PP
+You can choose which tables to show by pressing the '$' key. See \*(L"\s-1MODES\s0\*(R" and
+\&\*(L"\s-1TABLES\s0\*(R".
+.PP
+The table life-cycle is as follows:
+.IP "\(bu" 4
+Each table begins with a data source, which is an array of hashes. See below
+for details on data sources.
+.IP "\(bu" 4
+Each element of the data source becomes a row in the final table.
+.IP "\(bu" 4
+For each element in the data source, innotop extracts values from the source and
+creates a row. This row is another hash, which later steps will refer to as
+\&\f(CW$set\fR. The values innotop extracts are determined by the table's columns. Each
+column has an extraction subroutine, compiled from an expression (see
+\&\*(L"\s-1EXPRESSIONS\s0\*(R"). The resulting row is a hash whose keys are named the same as
+the column name.
+.IP "\(bu" 4
+innotop filters the rows, removing those that don't need to be displayed. See
+\&\*(L"\s-1FILTERS\s0\*(R".
+.IP "\(bu" 4
+innotop sorts the rows. See \*(L"\s-1SORTING\s0\*(R".
+.IP "\(bu" 4
+innotop groups the rows together, if specified. See \*(L"\s-1GROUPING\s0\*(R".
+.IP "\(bu" 4
+innotop colorizes the rows. See \*(L"\s-1COLORS\s0\*(R".
+.IP "\(bu" 4
+innotop transforms the column values in each row. See \*(L"\s-1TRANSFORMATIONS\s0\*(R".
+.IP "\(bu" 4
+innotop optionally pivots the rows (see \*(L"\s-1PIVOTING\s0\*(R"), then filters and sorts
+them.
+.IP "\(bu" 4
+innotop formats and justifies the rows as a table. During this step, innotop
+applies further formatting to the column values, including alignment, maximum
+and minimum widths. innotop also does final error checking to ensure there are
+no crashes due to undefined values. innotop then adds a caption if specified,
+and the table is ready to print.
+.PP
+The lifecycle is slightly different if the table is pivoted, as noted above. To
+clarify, if the table is pivoted, the process is extract, group, transform,
+pivot, filter, sort, create. If it's not pivoted, the process is extract,
+filter, sort, group, color, transform, create. This slightly convoluted process
+doesn't map all that well to \s-1SQL\s0, but pivoting complicates things pretty
+thoroughly. Roughly speaking, filtering and sorting happen as late as needed to
+effect the final result as you might expect, but as early as possible for
+efficiency.
+.PP
+Each built-in table is described below:
+.IP "adaptive_hash_index" 4
+.IX Item "adaptive_hash_index"
+Displays data about InnoDB's adaptive hash index. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "buffer_pool" 4
+.IX Item "buffer_pool"
+Displays data about InnoDB's buffer pool. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "cmd_summary" 4
+.IX Item "cmd_summary"
+Displays weighted status variables. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "deadlock_locks" 4
+.IX Item "deadlock_locks"
+Shows which locks were held and waited for by the last detected deadlock. Data
+source: \*(L"\s-1DEADLOCK_LOCKS\s0\*(R".
+.IP "deadlock_transactions" 4
+.IX Item "deadlock_transactions"
+Shows transactions involved in the last detected deadlock. Data source:
+\&\*(L"\s-1DEADLOCK_TRANSACTIONS\s0\*(R".
+.IP "explain" 4
+.IX Item "explain"
+Shows the output of \s-1EXPLAIN\s0. Data source: \*(L"\s-1EXPLAIN\s0\*(R".
+.IP "file_io_misc" 4
+.IX Item "file_io_misc"
+Displays data about InnoDB's file and I/O operations. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "fk_error" 4
+.IX Item "fk_error"
+Displays various data about InnoDB's last foreign key error. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "innodb_locks" 4
+.IX Item "innodb_locks"
+Displays InnoDB locks. Data source: \*(L"\s-1INNODB_LOCKS\s0\*(R".
+.IP "innodb_transactions" 4
+.IX Item "innodb_transactions"
+Displays data about InnoDB's current transactions. Data source:
+\&\*(L"\s-1INNODB_TRANSACTIONS\s0\*(R".
+.IP "insert_buffers" 4
+.IX Item "insert_buffers"
+Displays data about InnoDB's insert buffer. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "io_threads" 4
+.IX Item "io_threads"
+Displays data about InnoDB's I/O threads. Data source: \*(L"\s-1IO_THREADS\s0\*(R".
+.IP "log_statistics" 4
+.IX Item "log_statistics"
+Displays data about InnoDB's logging system. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "master_status" 4
+.IX Item "master_status"
+Displays replication master status. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "open_tables" 4
+.IX Item "open_tables"
+Displays open tables. Data source: \*(L"\s-1OPEN_TABLES\s0\*(R".
+.IP "page_statistics" 4
+.IX Item "page_statistics"
+Displays InnoDB page statistics. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "pending_io" 4
+.IX Item "pending_io"
+Displays InnoDB pending I/O operations. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "processlist" 4
+.IX Item "processlist"
+Displays current MySQL processes (threads/connections). Data source:
+\&\*(L"\s-1PROCESSLIST\s0\*(R".
+.IP "q_header" 4
+.IX Item "q_header"
+Displays various status values. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "row_operation_misc" 4
+.IX Item "row_operation_misc"
+Displays data about InnoDB's row operations. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "row_operations" 4
+.IX Item "row_operations"
+Displays data about InnoDB's row operations. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "semaphores" 4
+.IX Item "semaphores"
+Displays data about InnoDB's semaphores and mutexes. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "slave_io_status" 4
+.IX Item "slave_io_status"
+Displays data about the slave I/O thread. Data source:
+\&\*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "slave_sql_status" 4
+.IX Item "slave_sql_status"
+Displays data about the slave \s-1SQL\s0 thread. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "t_header" 4
+.IX Item "t_header"
+Displays various InnoDB status values. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "var_status" 4
+.IX Item "var_status"
+Displays user-configurable data. Data source: \*(L"\s-1STATUS_VARIABLES\s0\*(R".
+.IP "wait_array" 4
+.IX Item "wait_array"
+Displays data about InnoDB's \s-1OS\s0 wait array. Data source: \*(L"\s-1OS_WAIT_ARRAY\s0\*(R".
+.SS "\s-1COLUMNS\s0"
+.IX Subsection "COLUMNS"
+Columns belong to tables. You can choose a table's columns by pressing the '^'
+key, which starts the \*(L"\s-1TABLE\s0 \s-1EDITOR\s0\*(R" and lets you choose and edit columns.
+Pressing 'e' from within the table editor lets you edit the column's properties:
+.IP "\(bu" 4
+hdr: a column header. This appears in the first row of the table.
+.IP "\(bu" 4
+just: justification. '\-' means left-justified and '' means right-justified,
+just as with printf formatting codes (not a coincidence).
+.IP "\(bu" 4
+dec: whether to further align the column on the decimal point.
+.IP "\(bu" 4
+num: whether the column is numeric. This affects how values are sorted
+(lexically or numerically).
+.IP "\(bu" 4
+label: a small note about the column, which appears in dialogs that help the
+user choose columns.
+.IP "\(bu" 4
+src: an expression that innotop uses to extract the column's data from its
+source (see \*(L"\s-1DATA\s0 \s-1SOURCES\s0\*(R"). See \*(L"\s-1EXPRESSIONS\s0\*(R" for more on expressions.
+.IP "\(bu" 4
+minw: specifies a minimum display width. This helps stabilize the display,
+which makes it easier to read if the data is changing frequently.
+.IP "\(bu" 4
+maxw: similar to minw.
+.IP "\(bu" 4
+trans: a list of column transformations. See \*(L"\s-1TRANSFORMATIONS\s0\*(R".
+.IP "\(bu" 4
+agg: an aggregate function. See \*(L"\s-1GROUPING\s0\*(R". The default is \*(L"first\*(R".
+.IP "\(bu" 4
+aggonly: controls whether the column only shows when grouping is enabled on the
+table (see \*(L"\s-1GROUPING\s0\*(R"). By default, this is disabled. This means columns
+will always be shown by default, whether grouping is enabled or not. If a
+column's aggonly is set true, the column will appear when you toggle grouping on
+the table. Several columns are set this way, such as the count column on
+\&\*(L"processlist\*(R" and \*(L"innodb_transactions\*(R", so you don't see a count when the
+grouping isn't enabled, but you do when it is.
+.SS "\s-1FILTERS\s0"
+.IX Subsection "FILTERS"
+Filters remove rows from the display. They behave much like a \s-1WHERE\s0 clause in
+\&\s-1SQL\s0. innotop has several built-in filters, which remove irrelevant information
+like inactive queries, but you can define your own as well. innotop also lets
+you create quick-filters, which do not get saved to the configuration file, and
+are just an easy way to quickly view only some rows.
+.PP
+You can enable or disable a filter on any table. Press the '%' key (mnemonic: %
+looks kind of like a line being filtered between two circles) and choose which
+table you want to filter, if asked. You'll then see a list of possible filters
+and a list of filters currently enabled for that table. Type the names of
+filters you want to apply and press Enter.
+.PP
+\fIUSER-DEFINED \s-1FILTERS\s0\fR
+.IX Subsection "USER-DEFINED FILTERS"
+.PP
+If you type a name that doesn't exist, innotop will prompt you to create the
+filter. Filters are easy to create if you know Perl, and not hard if you don't.
+What you're doing is creating a subroutine that returns true if the row should
+be displayed. The row is a hash reference passed to your subroutine as \f(CW$set\fR.
+.PP
+For example, imagine you want to filter the processlist table so you only see
+queries that have been running more than five minutes. Type a new name for your
+filter, and when prompted for the subroutine body, press \s-1TAB\s0 to initiate your
+terminal's auto-completion. You'll see the names of the columns in the
+\&\*(L"processlist\*(R" table (innotop generally tries to help you with auto-completion
+lists). You want to filter on the 'time' column. Type the text \*(L"$set\->{time} >
+300\*(R" to return true when the query is more than five minutes old. That's all
+you need to do.
+.PP
+In other words, the code you're typing is surrounded by an implicit context,
+which looks like this:
+.PP
+.Vb 4
+\& sub filter {
+\& my ( $set ) = @_;
+\& # YOUR CODE HERE
+\& }
+.Ve
+.PP
+If your filter doesn't work, or if something else suddenly behaves differently,
+you might have made an error in your filter, and innotop is silently catching
+the error. Try enabling \*(L"debug\*(R" to make innotop throw an error instead.
+.PP
+\fIQUICK-FILTERS\fR
+.IX Subsection "QUICK-FILTERS"
+.PP
+innotop's quick-filters are a shortcut to create a temporary filter that doesn't
+persist when you restart innotop. To create a quick-filter, press the '/' key.
+innotop will prompt you for the column name and filter text. Again, you can use
+auto-completion on column names. The filter text can be just the text you want
+to \*(L"search for.\*(R" For example, to filter the \*(L"processlist\*(R" table on queries
+that refer to the products table, type '/' and then 'info product'.
+.PP
+The filter text can actually be any Perl regular expression, but of course a
+literal string like 'product' works fine as a regular expression.
+.PP
+Behind the scenes innotop compiles the quick-filter into a specially tagged
+filter that is otherwise like any other filter. It just isn't saved to the
+configuration file.
+.PP
+To clear quick-filters, press the '\e' key and innotop will clear them all at
+once.
+.SS "\s-1SORTING\s0"
+.IX Subsection "SORTING"
+innotop has sensible built-in defaults to sort the most important rows to the
+top of the table. Like anything else in innotop, you can customize how any
+table is sorted.
+.PP
+To start the sort dialog, start the \*(L"\s-1TABLE\s0 \s-1EDITOR\s0\*(R" with the '^' key, choose a
+table if necessary, and press the 's' key. You'll see a list of columns you can
+use in the sort expression and the current sort expression, if any. Enter a
+list of columns by which you want to sort and press Enter. If you want to
+reverse sort, prefix the column name with a minus sign. For example, if you
+want to sort by column a ascending, then column b descending, type 'a \-b'. You
+can also explicitly add a + in front of columns you want to sort ascending, but
+it's not required.
+.PP
+Some modes have keys mapped to open this dialog directly, and to quickly reverse
+sort direction. Press '?' as usual to see which keys are mapped in any mode.
+.SS "\s-1GROUPING\s0"
+.IX Subsection "GROUPING"
+innotop can group, or aggregate, rows together (the terms are used
+interchangeably). This is quite similar to an \s-1SQL\s0 \s-1GROUP\s0 \s-1BY\s0 clause. You can
+specify to group on certain columns, or if you don't specify any, the entire set
+of rows is treated as one group. This is quite like \s-1SQL\s0 so far, but unlike \s-1SQL\s0,
+you can also select un-grouped columns. innotop actually aggregates every
+column. If you don't explicitly specify a grouping function, the default is
+\&'first'. This is basically a convenience so you don't have to specify an
+aggregate function for every column you want in the result.
+.PP
+You can quickly toggle grouping on a table with the '=' key, which toggles its
+aggregate property. This property doesn't persist to the config file.
+.PP
+The columns by which the table is grouped are specified in its group_by
+property. When you turn grouping on, innotop places the group_by columns at the
+far left of the table, even if they're not supposed to be visible. The rest of
+the visible columns appear in order after them.
+.PP
+Two tables have default group_by lists and a count column built in:
+\&\*(L"processlist\*(R" and \*(L"innodb_transactions\*(R". The grouping is by connection
+and status, so you can quickly see how many queries or transactions are in a
+given status on each server you're monitoring. The time columns are aggregated
+as a sum; other columns are left at the default 'first' aggregation.
+.PP
+By default, the table shown in \*(L"S: Variables & Status\*(R" mode also uses
+grouping so you can monitor variables and status across many servers. The
+default aggregation function in this mode is 'avg'.
+.PP
+Valid grouping functions are defined in the \f(CW%agg_funcs\fR hash. They include
+.IP "first" 4
+.IX Item "first"
+Returns the first element in the group.
+.IP "count" 4
+.IX Item "count"
+Returns the number of elements in the group, including undefined elements, much
+like \s-1SQL\s0's \s-1COUNT\s0(*).
+.IP "avg" 4
+.IX Item "avg"
+Returns the average of defined elements in the group.
+.IP "sum" 4
+.IX Item "sum"
+Returns the sum of elements in the group.
+.PP
+Here's an example of grouping at work. Suppose you have a very busy server with
+hundreds of open connections, and you want to see how many connections are in
+what status. Using the built-in grouping rules, you can press 'Q' to enter
+\&\*(L"Q: Query List\*(R" mode. Press '=' to toggle grouping (if necessary, select the
+\&\*(L"processlist\*(R" table when prompted).
+.PP
+Your display might now look like the following:
+.PP
+.Vb 1
+\& Query List (? for help) localhost, 32:33, 0.11 QPS, 1 thd, 5.0.38\-log
+\&
+\& CXN Cmd Cnt ID User Host Time Query
+\& localhost Query 49 12933 webusr localhost 19:38 SELECT * FROM
+\& localhost Sending Da 23 2383 webusr localhost 12:43 SELECT col1,
+\& localhost Sleep 120 140 webusr localhost 5:18:12
+\& localhost Statistics 12 19213 webusr localhost 01:19 SELECT * FROM
+.Ve
+.PP
+That's actually quite a worrisome picture. You've got a lot of idle connections
+(Sleep), and some connections executing queries (Query and Sending Data).
+That's okay, but you also have a lot in Statistics status, collectively spending
+over a minute. That means the query optimizer is having a really hard time
+optimizing your statements. Something is wrong; it should normally take
+milliseconds to optimize queries. You might not have seen this pattern if you
+didn't look at your connections in aggregate. (This is a made-up example, but
+it can happen in real life).
+.SS "\s-1PIVOTING\s0"
+.IX Subsection "PIVOTING"
+innotop can pivot a table for more compact display, similar to a Pivot Table in
+a spreadsheet (also known as a crosstab). Pivoting a table makes columns into
+rows. Assume you start with this table:
+.PP
+.Vb 4
+\& foo bar
+\& === ===
+\& 1 3
+\& 2 4
+.Ve
+.PP
+After pivoting, the table will look like this:
+.PP
+.Vb 4
+\& name set0 set1
+\& ==== ==== ====
+\& foo 1 2
+\& bar 3 4
+.Ve
+.PP
+To get reasonable results, you might need to group as well as pivoting.
+innotop currently does this for \*(L"S: Variables & Status\*(R" mode.
+.SS "\s-1COLORS\s0"
+.IX Subsection "COLORS"
+By default, innotop highlights rows with color so you can see at a glance which
+rows are more important. You can customize the colorization rules and add your
+own to any table. Open the table editor with the '^' key, choose a table if
+needed, and press 'o' to open the color editor dialog.
+.PP
+The color editor dialog displays the rules applied to the table, in the order
+they are evaluated. Each row is evaluated against each rule to see if the rule
+matches the row; if it does, the row gets the specified color, and no further
+rules are evaluated. The rules look like the following:
+.PP
+.Vb 9
+\& state eq Locked black on_red
+\& cmd eq Sleep white
+\& user eq system user white
+\& cmd eq Connect white
+\& cmd eq Binlog Dump white
+\& time > 600 red
+\& time > 120 yellow
+\& time > 60 green
+\& time > 30 cyan
+.Ve
+.PP
+This is the default rule set for the \*(L"processlist\*(R" table. In order of
+priority, these rules make locked queries black on a red background, \*(L"gray out\*(R"
+connections from replication and sleeping queries, and make queries turn from
+cyan to red as they run longer.
+.PP
+(For some reason, the \s-1ANSI\s0 color code \*(L"white\*(R" is actually a light gray. Your
+terminal's display may vary; experiment to find colors you like).
+.PP
+You can use keystrokes to move the rules up and down, which re-orders their
+priority. You can also delete rules and add new ones. If you add a new rule,
+innotop prompts you for the column, an operator for the comparison, a value
+against which to compare the column, and a color to assign if the rule matches.
+There is auto-completion and prompting at each step.
+.PP
+The value in the third step needs to be correctly quoted. innotop does not try
+to quote the value because it doesn't know whether it should treat the value as
+a string or a number. If you want to compare the column against a string, as
+for example in the first rule above, you should enter 'Locked' surrounded by
+quotes. If you get an error message about a bareword, you probably should have
+quoted something.
+.SS "\s-1EXPRESSIONS\s0"
+.IX Subsection "EXPRESSIONS"
+Expressions are at the core of how innotop works, and are what enables you to
+extend innotop as you wish. Recall the table lifecycle explained in
+\&\*(L"\s-1TABLES\s0\*(R". Expressions are used in the earliest step, where it extracts
+values from a data source to form rows.
+.PP
+It does this by calling a subroutine for each column, passing it the source data
+set, a set of current values, and a set of previous values. These are all
+needed so the subroutine can calculate things like the difference between this
+tick and the previous tick.
+.PP
+The subroutines that extract the data from the set are compiled from
+expressions. This gives significantly more power than just naming the values to
+fill the columns, because it allows the column's value to be calculated from
+whatever data is necessary, but avoids the need to write complicated and lengthy
+Perl code.
+.PP
+innotop begins with a string of text that can look as simple as a value's name
+or as complicated as a full-fledged Perl expression. It looks at each
+\&'bareword' token in the string and decides whether it's supposed to be a key
+into the \f(CW$set\fR hash. A bareword is an unquoted value that isn't already
+surrounded by code-ish things like dollar signs or curly brackets. If innotop
+decides that the bareword isn't a function or other valid Perl code, it converts
+it into a hash access. After the whole string is processed, innotop compiles a
+subroutine, like this:
+.PP
+.Vb 5
+\& sub compute_column_value {
+\& my ( $set, $cur, $pre ) = @_;
+\& my $val = # EXPANDED STRING GOES HERE
+\& return $val;
+\& }
+.Ve
+.PP
+Here's a concrete example, taken from the header table \*(L"q_header\*(R" in \*(L"Q:
+Query List\*(R" mode. This expression calculates the qps, or Queries Per Second,
+column's values, from the values returned by \s-1SHOW\s0 \s-1STATUS:\s0
+.PP
+.Vb 1
+\& Questions/Uptime_hires
+.Ve
+.PP
+innotop decides both words are barewords, and transforms this expression into
+the following Perl code:
+.PP
+.Vb 1
+\& $set\->{Questions}/$set\->{Uptime_hires}
+.Ve
+.PP
+When surrounded by the rest of the subroutine's code, this is executable Perl
+that calculates a high-resolution queries-per-second value.
+.PP
+The arguments to the subroutine are named \f(CW$set\fR, \f(CW$cur\fR, and \f(CW$pre\fR. In most cases,
+\&\f(CW$set\fR and \f(CW$cur\fR will be the same values. However, if \*(L"status_inc\*(R" is set, \f(CW$cur\fR
+will not be the same as \f(CW$set\fR, because \f(CW$set\fR will already contain values that are
+the incremental difference between \f(CW$cur\fR and \f(CW$pre\fR.
+.PP
+Every column in innotop is computed by subroutines compiled in the same fashion.
+There is no difference between innotop's built-in columns and user-defined
+columns. This keeps things consistent and predictable.
+.SS "\s-1TRANSFORMATIONS\s0"
+.IX Subsection "TRANSFORMATIONS"
+Transformations change how a value is rendered. For example, they can take a
+number of seconds and display it in H:M:S format. The following transformations
+are defined:
+.IP "commify" 4
+.IX Item "commify"
+Adds commas to large numbers every three decimal places.
+.IP "dulint_to_int" 4
+.IX Item "dulint_to_int"
+Accepts two unsigned integers and converts them into a single longlong. This is
+useful for certain operations with InnoDB, which uses two integers as
+transaction identifiers, for example.
+.IP "no_ctrl_char" 4
+.IX Item "no_ctrl_char"
+Removes quoted control characters from the value. This is affected by the
+\&\*(L"charset\*(R" configuration variable.
+.Sp
+This transformation only operates within quoted strings, for example, values to
+a \s-1SET\s0 clause in an \s-1UPDATE\s0 statement. It will not alter the \s-1UPDATE\s0 statement,
+but will collapse the quoted string to [\s-1BINARY\s0] or [\s-1TEXT\s0], depending on the
+charset.
+.IP "percent" 4
+.IX Item "percent"
+Converts a number to a percentage by multiplying it by two, formatting it with
+\&\*(L"num_digits\*(R" digits after the decimal point, and optionally adding a percent
+sign (see \*(L"show_percent\*(R").
+.IP "secs_to_time" 4
+.IX Item "secs_to_time"
+Formats a number of seconds as time in days+hours:minutes:seconds format.
+.IP "set_precision" 4
+.IX Item "set_precision"
+Formats numbers with \*(L"num_digits\*(R" number of digits after the decimal point.
+.IP "shorten" 4
+.IX Item "shorten"
+Formats a number as a unit of 1024 (k/M/G/T) and with \*(L"num_digits\*(R" number of
+digits after the decimal point.
+.SS "\s-1TABLE\s0 \s-1EDITOR\s0"
+.IX Subsection "TABLE EDITOR"
+The innotop table editor lets you customize tables with keystrokes. You start
+the table editor with the '^' key. If there's more than one table on the
+screen, it will prompt you to choose one of them. Once you do, innotop will
+show you something like this:
+.PP
+.Vb 1
+\& Editing table definition for Buffer Pool. Press ? for help, q to quit.
+\&
+\& name hdr label src
+\& cxn CXN Connection from which cxn
+\& buf_pool_size Size Buffer pool size IB_bp_buf_poo
+\& buf_free Free Bufs Buffers free in the b IB_bp_buf_fre
+\& pages_total Pages Pages total IB_bp_pages_t
+\& pages_modified Dirty Pages Pages modified (dirty IB_bp_pages_m
+\& buf_pool_hit_rate Hit Rate Buffer pool hit rate IB_bp_buf_poo
+\& total_mem_alloc Memory Total memory allocate IB_bp_total_m
+\& add_pool_alloc Add\*(Aql Pool Additonal pool alloca IB_bp_add_poo
+.Ve
+.PP
+The first line shows which table you're editing, and reminds you again to press
+\&'?' for a list of key mappings. The rest is a tabular representation of the
+table's columns, because that's likely what you're trying to edit. However, you
+can edit more than just the table's columns; this screen can start the filter
+editor, color rule editor, and more.
+.PP
+Each row in the display shows a single column in the table you're editing, along
+with a couple of its properties such as its header and source expression (see
+\&\*(L"\s-1EXPRESSIONS\s0\*(R").
+.PP
+The key mappings are Vim-style, as in many other places. Pressing 'j' and 'k'
+moves the highlight up or down. You can then (d)elete or (e)dit the highlighted
+column. You can also (a)dd a column to the table. This actually just activates
+one of the columns already defined for the table; it prompts you to choose from
+among the columns available but not currently displayed. Finally, you can
+re-order the columns with the '+' and '\-' keys.
+.PP
+You can do more than just edit the columns with the table editor, you can also
+edit other properties, such as the table's sort expression and group-by
+expression. Press '?' to see the full list, of course.
+.PP
+If you want to really customize and create your own column, as opposed to just
+activating a built-in one that's not currently displayed, press the (n)ew key,
+and innotop will prompt you for the information it needs:
+.IP "\(bu" 4
+The column name: this needs to be a word without any funny characters, e.g. just
+letters, numbers and underscores.
+.IP "\(bu" 4
+The column header: this is the label that appears at the top of the column, in
+the table header. This can have spaces and funny characters, but be careful not
+to make it too wide and waste space on-screen.
+.IP "\(bu" 4
+The column's data source: this is an expression that determines what data from
+the source (see \*(L"\s-1TABLES\s0\*(R") innotop will put into the column. This can just be
+the name of an item in the source, or it can be a more complex expression, as
+described in \*(L"\s-1EXPRESSIONS\s0\*(R".
+.PP
+Once you've entered the required data, your table has a new column. There is no
+difference between this column and the built-in ones; it can have all the same
+properties and behaviors. innotop will write the column's definition to the
+configuration file, so it will persist across sessions.
+.PP
+Here's an example: suppose you want to track how many times your slaves have
+retried transactions. According to the MySQL manual, the
+Slave_retried_transactions status variable gives you that data: \*(L"The total
+number of times since startup that the replication slave \s-1SQL\s0 thread has retried
+transactions. This variable was added in version 5.0.4.\*(R" This is appropriate to
+add to the \*(L"slave_sql_status\*(R" table.
+.PP
+To add the column, switch to the replication-monitoring mode with the 'M' key,
+and press the '^' key to start the table editor. When prompted, choose
+slave_sql_status as the table, then press 'n' to create the column. Type
+\&'retries' as the column name, 'Retries' as the column header, and
+\&'Slave_retried_transactions' as the source. Now the column is created, and you
+see the table editor screen again. Press 'q' to exit the table editor, and
+you'll see your column at the end of the table.
+.SH "VARIABLE SETS"
+.IX Header "VARIABLE SETS"
+Variable sets are used in \*(L"S: Variables & Status\*(R" mode to define more easily
+what variables you want to monitor. Behind the scenes they are compiled to a
+list of expressions, and then into a column list so they can be treated just
+like columns in any other table, in terms of data extraction and
+transformations. However, you're protected from the tedious details by a syntax
+that ought to feel very natural to you: a \s-1SQL\s0 \s-1SELECT\s0 list.
+.PP
+The data source for variable sets, and indeed the entire S mode, is the
+combination of \s-1SHOW\s0 \s-1STATUS\s0, \s-1SHOW\s0 \s-1VARIABLES\s0, and \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0. Imagine
+that you had a huge table with one column per variable returned from those
+statements. That's the data source for variable sets. You can now query this
+data source just like you'd expect. For example:
+.PP
+.Vb 1
+\& Questions, Uptime, Questions/Uptime as QPS
+.Ve
+.PP
+Behind the scenes innotop will split that variable set into three expressions,
+compile them and turn them into a table definition, then extract as usual. This
+becomes a \*(L"variable set,\*(R" or a \*(L"list of variables you want to monitor.\*(R"
+.PP
+innotop lets you name and save your variable sets, and writes them to the
+configuration file. You can choose which variable set you want to see with the
+\&'c' key, or activate the next and previous sets with the '>' and '<' keys.
+There are many built-in variable sets as well, which should give you a good
+start for creating your own. Press 'e' to edit the current variable set, or
+just to see how it's defined. To create a new one, just press 'c' and type its
+name.
+.PP
+You may want to use some of the functions listed in \*(L"\s-1TRANSFORMATIONS\s0\*(R" to help
+format the results. In particular, \*(L"set_precision\*(R" is often useful to limit
+the number of digits you see. Extending the above example, here's how:
+.PP
+.Vb 1
+\& Questions, Uptime, set_precision(Questions/Uptime) as QPS
+.Ve
+.PP
+Actually, this still needs a little more work. If your \*(L"interval\*(R" is less
+than one second, you might be dividing by zero because Uptime is incremental in
+this mode by default. Instead, use Uptime_hires:
+.PP
+.Vb 1
+\& Questions, Uptime, set_precision(Questions/Uptime_hires) as QPS
+.Ve
+.PP
+This example is simple, but it shows how easy it is to choose which variables
+you want to monitor.
+.SH "PLUGINS"
+.IX Header "PLUGINS"
+innotop has a simple but powerful plugin mechanism by which you can extend
+or modify its existing functionality, and add new functionality. innotop's
+plugin functionality is event-based: plugins register themselves to be called
+when events happen. They then have a chance to influence the event.
+.PP
+An innotop plugin is a Perl module placed in innotop's \*(L"plugin_dir\*(R"
+directory. On \s-1UNIX\s0 systems, you can place a symbolic link to the module instead
+of putting the actual file there. innotop automatically discovers the file. If
+there is a corresponding entry in the \*(L"plugins\*(R" configuration file section,
+innotop loads and activates the plugin.
+.PP
+The module must conform to innotop's plugin interface. Additionally, the source
+code of the module must be written in such a way that innotop can inspect the
+file and determine the package name and description.
+.SS "Package Source Convention"
+.IX Subsection "Package Source Convention"
+innotop inspects the plugin module's source to determine the Perl package name.
+It looks for a line of the form \*(L"package Foo;\*(R" and if found, considers the
+plugin's package name to be Foo. Of course the package name can be a valid Perl
+package name, with double semicolons and so on.
+.PP
+It also looks for a description in the source code, to make the plugin editor
+more human-friendly. The description is a comment line of the form \*(L"#
+description: Foo\*(R", where \*(L"Foo\*(R" is the text innotop will consider to be the
+plugin's description.
+.SS "Plugin Interface"
+.IX Subsection "Plugin Interface"
+The innotop plugin interface is quite simple: innotop expects the plugin to be
+an object-oriented module it can call certain methods on. The methods are
+.IP "new(%variables)" 4
+.IX Item "new(%variables)"
+This is the plugin's constructor. It is passed a hash of innotop's variables,
+which it can manipulate (see \*(L"Plugin Variables\*(R"). It must return a reference
+to the newly created plugin object.
+.Sp
+At construction time, innotop has only loaded the general configuration and
+created the default built-in variables with their default contents (which is
+quite a lot). Therefore, the state of the program is exactly as in the innotop
+source code, plus the configuration variables from the \*(L"general\*(R" section in
+the config file.
+.Sp
+If your plugin manipulates the variables, it is changing global data, which is
+shared by innotop and all plugins. Plugins are loaded in the order they're
+listed in the config file. Your plugin may load before or after another plugin,
+so there is a potential for conflict or interaction between plugins if they
+modify data other plugins use or modify.
+.IP "\fIregister_for_events()\fR" 4
+.IX Item "register_for_events()"
+This method must return a list of events in which the plugin is interested, if
+any. See \*(L"Plugin Events\*(R" for the defined events. If the plugin returns an
+event that's not defined, the event is ignored.
+.IP "event handlers" 4
+.IX Item "event handlers"
+The plugin must implement a method named the same as each event for which it has
+registered. In other words, if the plugin returns qw(foo bar) from
+\&\fIregister_for_events()\fR, it must have \fIfoo()\fR and \fIbar()\fR methods. These methods are
+callbacks for the events. See \*(L"Plugin Events\*(R" for more details about each
+event.
+.SS "Plugin Variables"
+.IX Subsection "Plugin Variables"
+The plugin's constructor is passed a hash of innotop's variables, which it can
+manipulate. It is probably a good idea if the plugin object saves a copy of it
+for later use. The variables are defined in the innotop variable
+\&\f(CW%pluggable_vars\fR, and are as follows:
+.IP "action_for" 4
+.IX Item "action_for"
+A hashref of key mappings. These are innotop's global hot-keys.
+.IP "agg_funcs" 4
+.IX Item "agg_funcs"
+A hashref of functions that can be used for grouping. See \*(L"\s-1GROUPING\s0\*(R".
+.IP "config" 4
+.IX Item "config"
+The global configuration hash.
+.IP "connections" 4
+.IX Item "connections"
+A hashref of connection specifications. These are just specifications of how to
+connect to a server.
+.IP "dbhs" 4
+.IX Item "dbhs"
+A hashref of innotop's database connections. These are actual \s-1DBI\s0 connection
+objects.
+.IP "filters" 4
+.IX Item "filters"
+A hashref of filters applied to table rows. See \*(L"\s-1FILTERS\s0\*(R" for more.
+.IP "modes" 4
+.IX Item "modes"
+A hashref of modes. See \*(L"\s-1MODES\s0\*(R" for more.
+.IP "server_groups" 4
+.IX Item "server_groups"
+A hashref of server groups. See \*(L"\s-1SERVER\s0 \s-1GROUPS\s0\*(R".
+.IP "tbl_meta" 4
+.IX Item "tbl_meta"
+A hashref of innotop's table meta-data, with one entry per table (see
+\&\*(L"\s-1TABLES\s0\*(R" for more information).
+.IP "trans_funcs" 4
+.IX Item "trans_funcs"
+A hashref of transformation functions. See \*(L"\s-1TRANSFORMATIONS\s0\*(R".
+.IP "var_sets" 4
+.IX Item "var_sets"
+A hashref of variable sets. See \*(L"\s-1VARIABLE\s0 \s-1SETS\s0\*(R".
+.SS "Plugin Events"
+.IX Subsection "Plugin Events"
+Each event is defined somewhere in the innotop source code. When innotop runs
+that code, it executes the callback function for each plugin that expressed its
+interest in the event. innotop passes some data for each event. The events are
+defined in the \f(CW%event_listener_for\fR variable, and are as follows:
+.ie n .IP "extract_values($set, $cur, $pre, $tbl)" 4
+.el .IP "extract_values($set, \f(CW$cur\fR, \f(CW$pre\fR, \f(CW$tbl\fR)" 4
+.IX Item "extract_values($set, $cur, $pre, $tbl)"
+This event occurs inside the function that extracts values from a data source.
+The arguments are the set of values, the current values, the previous values,
+and the table name.
+.IP "set_to_tbl" 4
+.IX Item "set_to_tbl"
+Events are defined at many places in this subroutine, which is responsible for
+turning an arrayref of hashrefs into an arrayref of lines that can be printed to
+the screen. The events all pass the same data: an arrayref of rows and the name
+of the table being created. The events are set_to_tbl_pre_filter,
+set_to_tbl_pre_sort,set_to_tbl_pre_group, set_to_tbl_pre_colorize,
+set_to_tbl_pre_transform, set_to_tbl_pre_pivot, set_to_tbl_pre_create,
+set_to_tbl_post_create.
+.IP "draw_screen($lines)" 4
+.IX Item "draw_screen($lines)"
+This event occurs inside the subroutine that prints the lines to the screen.
+\&\f(CW$lines\fR is an arrayref of strings.
+.SS "Simple Plugin Example"
+.IX Subsection "Simple Plugin Example"
+The easiest way to explain the plugin functionality is probably with a simple
+example. The following module adds a column to the beginning of every table and
+sets its value to 1.
+.PP
+.Vb 2
+\& use strict;
+\& use warnings FATAL => \*(Aqall\*(Aq;
+\&
+\& package Innotop::Plugin::Example;
+\& # description: Adds an \*(Aqexample\*(Aq column to every table
+\&
+\& sub new {
+\& my ( $class, %vars ) = @_;
+\& # Store reference to innotop\*(Aqs variables in $self
+\& my $self = bless { %vars }, $class;
+\&
+\& # Design the example column
+\& my $col = {
+\& hdr => \*(AqExample\*(Aq,
+\& just => \*(Aq\*(Aq,
+\& dec => 0,
+\& num => 1,
+\& label => \*(AqExample\*(Aq,
+\& src => \*(Aqexample\*(Aq, # Get data from this column in the data source
+\& tbl => \*(Aq\*(Aq,
+\& trans => [],
+\& };
+\&
+\& # Add the column to every table.
+\& my $tbl_meta = $vars{tbl_meta};
+\& foreach my $tbl ( values %$tbl_meta ) {
+\& # Add the column to the list of defined columns
+\& $tbl\->{cols}\->{example} = $col;
+\& # Add the column to the list of visible columns
+\& unshift @{$tbl\->{visible}}, \*(Aqexample\*(Aq;
+\& }
+\&
+\& # Be sure to return a reference to the object.
+\& return $self;
+\& }
+\&
+\& # I\*(Aqd like to be called when a data set is being rendered into a table, please.
+\& sub register_for_events {
+\& my ( $self ) = @_;
+\& return qw(set_to_tbl_pre_filter);
+\& }
+\&
+\& # This method will be called when the event fires.
+\& sub set_to_tbl_pre_filter {
+\& my ( $self, $rows, $tbl ) = @_;
+\& # Set the example column\*(Aqs data source to the value 1.
+\& foreach my $row ( @$rows ) {
+\& $row\->{example} = 1;
+\& }
+\& }
+\&
+\& 1;
+.Ve
+.SS "Plugin Editor"
+.IX Subsection "Plugin Editor"
+The plugin editor lets you view the plugins innotop discovered and activate or
+deactivate them. Start the editor by pressing $ to start the configuration
+editor from any mode. Press the 'p' key to start the plugin editor. You'll see
+a list of plugins innotop discovered. You can use the 'j' and 'k' keys to move
+the highlight to the desired one, then press the * key to toggle it active or
+inactive. Exit the editor and restart innotop for the changes to take effect.
+.SH "SQL STATEMENTS"
+.IX Header "SQL STATEMENTS"
+innotop uses a limited set of \s-1SQL\s0 statements to retrieve data from MySQL for
+display. The statements are customized depending on the server version against
+which they are executed; for example, on MySQL 5 and newer, \s-1INNODB_STATUS\s0
+executes \*(L"\s-1SHOW\s0 \s-1ENGINE\s0 \s-1INNODB\s0 \s-1STATUS\s0\*(R", while on earlier versions it executes
+\&\*(L"\s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0\*(R". The statements are as follows:
+.PP
+.Vb 12
+\& Statement SQL executed
+\& =================== ===============================
+\& INNODB_STATUS SHOW [ENGINE] INNODB STATUS
+\& KILL_CONNECTION KILL
+\& KILL_QUERY KILL QUERY
+\& OPEN_TABLES SHOW OPEN TABLES
+\& PROCESSLIST SHOW FULL PROCESSLIST
+\& SHOW_MASTER_LOGS SHOW MASTER LOGS
+\& SHOW_MASTER_STATUS SHOW MASTER STATUS
+\& SHOW_SLAVE_STATUS SHOW SLAVE STATUS
+\& SHOW_STATUS SHOW [GLOBAL] STATUS
+\& SHOW_VARIABLES SHOW [GLOBAL] VARIABLES
+.Ve
+.SH "DATA SOURCES"
+.IX Header "DATA SOURCES"
+Each time innotop extracts values to create a table (see \*(L"\s-1EXPRESSIONS\s0\*(R" and
+\&\*(L"\s-1TABLES\s0\*(R"), it does so from a particular data source. Largely because of the
+complex data extracted from \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0, this is slightly messy. \s-1SHOW\s0
+\&\s-1INNODB\s0 \s-1STATUS\s0 contains a mixture of single values and repeated values that form
+nested data sets.
+.PP
+Whenever innotop fetches data from MySQL, it adds two extra bits to each set:
+cxn and Uptime_hires. cxn is the name of the connection from which the data
+came. Uptime_hires is a high-resolution version of the server's Uptime status
+variable, which is important if your \*(L"interval\*(R" setting is sub-second.
+.PP
+Here are the kinds of data sources from which data is extracted:
+.IP "\s-1STATUS_VARIABLES\s0" 4
+.IX Item "STATUS_VARIABLES"
+This is the broadest category, into which the most kinds of data fall. It
+begins with the combination of \s-1SHOW\s0 \s-1STATUS\s0 and \s-1SHOW\s0 \s-1VARIABLES\s0, but other sources
+may be included as needed, for example, \s-1SHOW\s0 \s-1MASTER\s0 \s-1STATUS\s0 and \s-1SHOW\s0 \s-1SLAVE\s0
+\&\s-1STATUS\s0, as well as many of the non-repeated values from \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0.
+.IP "\s-1DEADLOCK_LOCKS\s0" 4
+.IX Item "DEADLOCK_LOCKS"
+This data is extracted from the transaction list in the \s-1LATEST\s0 \s-1DETECTED\s0 \s-1DEADLOCK\s0
+section of \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0. It is nested two levels deep: transactions, then
+locks.
+.IP "\s-1DEADLOCK_TRANSACTIONS\s0" 4
+.IX Item "DEADLOCK_TRANSACTIONS"
+This data is from the transaction list in the \s-1LATEST\s0 \s-1DETECTED\s0 \s-1DEADLOCK\s0
+section of \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0. It is nested one level deep.
+.IP "\s-1EXPLAIN\s0" 4
+.IX Item "EXPLAIN"
+This data is from the result set returned by \s-1EXPLAIN\s0.
+.IP "\s-1INNODB_TRANSACTIONS\s0" 4
+.IX Item "INNODB_TRANSACTIONS"
+This data is from the \s-1TRANSACTIONS\s0 section of \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0.
+.IP "\s-1IO_THREADS\s0" 4
+.IX Item "IO_THREADS"
+This data is from the list of threads in the the \s-1FILE\s0 I/O section of \s-1SHOW\s0 \s-1INNODB\s0
+\&\s-1STATUS\s0.
+.IP "\s-1INNODB_LOCKS\s0" 4
+.IX Item "INNODB_LOCKS"
+This data is from the \s-1TRANSACTIONS\s0 section of \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0 and is nested
+two levels deep.
+.IP "\s-1OPEN_TABLES\s0" 4
+.IX Item "OPEN_TABLES"
+This data is from \s-1SHOW\s0 \s-1OPEN\s0 \s-1TABLES\s0.
+.IP "\s-1PROCESSLIST\s0" 4
+.IX Item "PROCESSLIST"
+This data is from \s-1SHOW\s0 \s-1FULL\s0 \s-1PROCESSLIST\s0.
+.IP "\s-1OS_WAIT_ARRAY\s0" 4
+.IX Item "OS_WAIT_ARRAY"
+This data is from the \s-1SEMAPHORES\s0 section of \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0 and is nested one
+level deep. It comes from the lines that look like this:
+.Sp
+.Vb 1
+\& \-\-Thread 1568861104 has waited at btr0cur.c line 424 ....
+.Ve
+.SH "MYSQL PRIVILEGES"
+.IX Header "MYSQL PRIVILEGES"
+.IP "\(bu" 4
+You must connect to MySQL as a user who has the \s-1SUPER\s0 privilege for many of the
+functions.
+.IP "\(bu" 4
+If you don't have the \s-1SUPER\s0 privilege, you can still run some functions, but you
+won't necessarily see all the same data.
+.IP "\(bu" 4
+You need the \s-1PROCESS\s0 privilege to see the list of currently running queries in Q
+mode.
+.IP "\(bu" 4
+You need special privileges to start and stop slave servers.
+.IP "\(bu" 4
+You need appropriate privileges to create and drop the deadlock tables if needed
+(see \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R").
+.SH "SYSTEM REQUIREMENTS"
+.IX Header "SYSTEM REQUIREMENTS"
+You need Perl to run innotop, of course. You also need a few Perl modules: \s-1DBI\s0,
+DBD::mysql, Term::ReadKey, and Time::HiRes. These should be included with most
+Perl distributions, but in case they are not, I recommend using versions
+distributed with your operating system or Perl distribution, not from \s-1CPAN\s0.
+Term::ReadKey in particular has been known to cause problems if installed from
+\&\s-1CPAN\s0.
+.PP
+If you have Term::ANSIColor, innotop will use it to format headers more readably
+and compactly. (Under Microsoft Windows, you also need Win32::Console::ANSI for
+terminal formatting codes to be honored). If you install Term::ReadLine,
+preferably Term::ReadLine::Gnu, you'll get nice auto-completion support.
+.PP
+I run innotop on Gentoo GNU/Linux, Debian and Ubuntu, and I've had feedback from
+people successfully running it on Red Hat, CentOS, Solaris, and Mac \s-1OSX\s0. I
+don't see any reason why it won't work on other UNIX-ish operating systems, but
+I don't know for sure. It also runs on Windows under ActivePerl without
+problem.
+.PP
+innotop has been used on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26,
+5.1.15, and 5.2.3. If it doesn't run correctly for you, that is a bug that
+should be reported.
+.SH "FILES"
+.IX Header "FILES"
+\&\f(CW$HOMEDIR\fR/.innotop and/or /etc/innotop are used to store
+configuration information. Files include the configuration file innotop.conf,
+the core_dump file which contains verbose error messages if \*(L"debug\*(R" is
+enabled, and the plugins/ subdirectory.
+.SH "GLOSSARY OF TERMS"
+.IX Header "GLOSSARY OF TERMS"
+.IP "tick" 4
+.IX Item "tick"
+A tick is a refresh event, when innotop re-fetches data from connections and
+displays it.
+.SH "ACKNOWLEDGEMENTS"
+.IX Header "ACKNOWLEDGEMENTS"
+The following people and organizations are acknowledged for various reasons.
+Hopefully no one has been forgotten.
+.PP
+Allen K. Smith,
+Aurimas Mikalauskas,
+Bartosz Fenski,
+Brian Miezejewski,
+Christian Hammers,
+Cyril Scetbon,
+Dane Miller,
+David Multer,
+Dr. Frank Ullrich,
+Giuseppe Maxia,
+Google.com Site Reliability Engineers,
+Google Code,
+Jan Pieter Kunst,
+Jari Aalto,
+Jay Pipes,
+Jeremy Zawodny,
+Johan Idren,
+Kristian Kohntopp,
+Lenz Grimmer,
+Maciej Dobrzanski,
+Michiel Betel,
+MySQL \s-1AB\s0,
+Paul McCullagh,
+Sebastien Estienne,
+Sourceforge.net,
+Steven Kreuzer,
+The Gentoo MySQL Team,
+Trevor Price,
+Yaar Schnitman,
+and probably more people that have not been included.
+.PP
+(If your name has been misspelled, it's probably out of fear of putting
+international characters into this documentation; earlier versions of Perl might
+not be able to compile it then).
+.SH "COPYRIGHT, LICENSE AND WARRANTY"
+.IX Header "COPYRIGHT, LICENSE AND WARRANTY"
+This program is copyright (c) 2006 Baron Schwartz.
+Feedback and improvements are welcome.
+.PP
+\&\s-1THIS\s0 \s-1PROGRAM\s0 \s-1IS\s0 \s-1PROVIDED\s0 \*(L"\s-1AS\s0 \s-1IS\s0\*(R" \s-1AND\s0 \s-1WITHOUT\s0 \s-1ANY\s0 \s-1EXPRESS\s0 \s-1OR\s0 \s-1IMPLIED\s0
+\&\s-1WARRANTIES\s0, \s-1INCLUDING\s0, \s-1WITHOUT\s0 \s-1LIMITATION\s0, \s-1THE\s0 \s-1IMPLIED\s0 \s-1WARRANTIES\s0 \s-1OF\s0
+\&\s-1MERCHANTIBILITY\s0 \s-1AND\s0 \s-1FITNESS\s0 \s-1FOR\s0 A \s-1PARTICULAR\s0 \s-1PURPOSE\s0.
+.PP
+This program is free software; you can redistribute it and/or modify it under
+the terms of the \s-1GNU\s0 General Public License as published by the Free Software
+Foundation, version 2; \s-1OR\s0 the Perl Artistic License. On \s-1UNIX\s0 and similar
+systems, you can issue `man perlgpl' or `man perlartistic' to read these
+licenses.
+.PP
+You should have received a copy of the \s-1GNU\s0 General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, \s-1MA\s0 02111\-1307 \s-1USA\s0.
+.PP
+Execute innotop and press '!' to see this information at any time.
+.SH "AUTHOR"
+.IX Header "AUTHOR"
+Originally written by Baron Schwartz; currently maintained by Aaron Racine.
+.SH "BUGS"
+.IX Header "BUGS"
+You can report bugs, ask for improvements, and get other help and support at
+<http://code.google.com/p/innotop/>. There are mailing lists, a source code
+browser, a bug tracker, etc. Please use these instead of contacting the
+maintainer or author directly, as it makes our job easier and benefits others if the
+discussions are permanent and public. Of course, if you need to contact us in
+private, please do.
--- /dev/null
+#
+# The MySQL database server configuration file.
+#
+# You can copy this to one of:
+# - "/etc/mysql/my.cnf" to set global options,
+# - "~/.my.cnf" to set user-specific options.
+#
+# One can use all long options that the program supports.
+# Run program with --help to get a list of available options and with
+# --print-defaults to see which it would actually understand and use.
+#
+# For explanations see
+# http://dev.mysql.com/doc/mysql/en/server-system-variables.html
+
+# This will be passed to all mysql clients
+# It has been reported that passwords should be enclosed with ticks/quotes
+# escpecially if they contain "#" chars...
+# Remember to edit /etc/mysql/debian.cnf when changing the socket location.
+[client]
+port = 3306
+socket = /var/run/mysqld/mysqld.sock
+
+# Here is entries for some specific programs
+# The following values assume you have at least 32M ram
+
+# This was formally known as [safe_mysqld]. Both versions are currently parsed.
+[mysqld_safe]
+socket = /var/run/mysqld/mysqld.sock
+nice = 0
+
+[mysqld]
+#
+# * Basic Settings
+#
+user = mysql
+pid-file = /var/run/mysqld/mysqld.pid
+socket = /var/run/mysqld/mysqld.sock
+port = 3306
+basedir = /usr
+datadir = /var/lib/mysql
+tmpdir = /tmp
+lc-messages-dir = /usr/share/mysql
+skip-external-locking
+#
+# Instead of skip-networking the default is now to listen only on
+# localhost which is more compatible and is not less secure.
+bind-address = 127.0.0.1
+#
+# * Fine Tuning
+#
+key_buffer = 16M
+max_allowed_packet = 16M
+thread_stack = 192K
+thread_cache_size = 8
+# This replaces the startup script and checks MyISAM tables if needed
+# the first time they are touched
+myisam-recover = BACKUP
+#max_connections = 100
+#table_cache = 64
+#thread_concurrency = 10
+#
+# * Query Cache Configuration
+#
+query_cache_limit = 1M
+query_cache_size = 16M
+#
+# * Logging and Replication
+#
+# Both location gets rotated by the cronjob.
+# Be aware that this log type is a performance killer.
+# As of 5.1 you can enable the log at runtime!
+#general_log_file = /var/log/mysql/mysql.log
+#general_log = 1
+#
+# Error log - should be very few entries.
+#
+log_error = /var/log/mysql/error.log
+#
+# Here you can see queries with especially long duration
+#log_slow_queries = /var/log/mysql/mysql-slow.log
+#long_query_time = 2
+#log-queries-not-using-indexes
+#
+# The following can be used as easy to replay backup logs or for replication.
+# note: if you are setting up a replication slave, see README.Debian about
+# other settings you may need to change.
+#server-id = 1
+#log_bin = /var/log/mysql/mysql-bin.log
+expire_logs_days = 10
+max_binlog_size = 100M
+#binlog_do_db = include_database_name
+#binlog_ignore_db = include_database_name
+#
+# * InnoDB
+#
+# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
+# Read the manual for more InnoDB related options. There are many!
+#
+# * Security Features
+#
+# Read the manual, too, if you want chroot!
+# chroot = /var/lib/mysql/
+#
+# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
+#
+# ssl-ca=/etc/mysql/cacert.pem
+# ssl-cert=/etc/mysql/server-cert.pem
+# ssl-key=/etc/mysql/server-key.pem
+
+
+
+[mysqldump]
+quick
+quote-names
+max_allowed_packet = 16M
+
+[mysql]
+#no-auto-rehash # faster start of mysql but no tab completition
+
+[isamchk]
+key_buffer = 16M
+
+#
+# * IMPORTANT: Additional settings that can override those from this file!
+# The files must end with '.cnf', otherwise they'll be ignored.
+#
+!includedir /etc/mysql/conf.d/
--- /dev/null
+#
+# The MySQL 5.6 database server configuration file.
+#
+# This custom MySQL 5.6 specific configuration file
+# adds on top of the existing default my.cnf file at
+# - /etc/mysql/my.cnf.
+#
+# Please add any extra MySQL 5.6 options in this file
+# for sake of clarity.
+#
+# You may uncomment any existing option to enable it
+#
+# sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
--- /dev/null
+W: mysql-dfsg source: maintainer-script-lacks-debhelper-token debian/mysql-server.postinst
+W: mysql-server: possible-bashism-in-maintainer-script postinst:68 'p{("a".."z","A".."Z",0..9)[int(rand(62))]}'
--- /dev/null
+.so man1/mysql_config.1
--- /dev/null
+.so man1/mysql.1
--- /dev/null
+[mysqld_safe]
+syslog
--- /dev/null
+#!/usr/bin/perl -w
+
+# mysqlreport v3.5 Apr 16 2008
+# http://hackmysql.com/mysqlreport
+
+# mysqlreport makes an easy-to-read report of important MySQL status values.
+# Copyright 2006-2008 Daniel Nichter
+#
+# 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.
+#
+# The GNU General Public License is available at:
+# http://www.gnu.org/copyleft/gpl.html
+
+use strict;
+use File::Temp qw(tempfile);
+use DBI;
+use Getopt::Long;
+eval { require Term::ReadKey; };
+my $RK = ($@ ? 0 : 1);
+
+sub have_op;
+
+my $WIN = ($^O eq 'MSWin32' ? 1 : 0);
+my %op;
+my %mycnf; # ~/.my.cnf
+my ($tmpfile_fh, $tmpfile);
+my ($stat_name, $stat_val, $stat_label);
+my $MySQL_version;
+my (%stats, %vars); # SHOW STATUS, SHOW VARIABLES
+my (%DMS_vals, %Com_vals, %ib_vals);
+my ($dbh, $query);
+my ($questions, $key_read_ratio, $key_write_ratio, $dms, $slow_query_t);
+my ($key_cache_block_size, $key_buffer_used, $key_buffer_usage);
+my ($qc_mem_used, $qc_hi_r, $qc_ip_r); # Query Cache
+my $have_innodb_vals;
+my ($ib_bp_used, $ib_bp_total, $ib_bp_read_ratio);
+my ($relative_live, $relative_infiles);
+my $real_uptime;
+my (%stats_present, %stats_past); # For relative reports
+
+GetOptions (
+ \%op,
+ "user=s",
+ "password:s",
+ "host=s",
+ "port=s",
+ "socket=s",
+ "no-mycnf",
+ "infile|in=s",
+ "outfile=s",
+ "flush-status",
+ "email=s",
+ "r|relative:i",
+ "c|report-count=i",
+ "detach",
+ "help|?",
+ "debug"
+);
+
+show_help_and_exit() if $op{'help'};
+
+get_user_mycnf() unless $op{'no-mycnf'};
+
+# Command line options override ~/.my.cnf
+$mycnf{'host'} = $op{'host'} if have_op 'host';
+$mycnf{'port'} = $op{'port'} if have_op 'port';
+$mycnf{'socket'} = $op{'socket'} if have_op 'socket';
+$mycnf{'user'} = $op{'user'} if have_op 'user';
+
+$mycnf{'user'} ||= $ENV{'USER'};
+
+if(exists $op{'password'})
+{
+ if($op{'password'} eq '') # Prompt for password
+ {
+ Term::ReadKey::ReadMode(2) if $RK;
+ print "Password for database user $mycnf{'user'}: ";
+ chomp($mycnf{'pass'} = <STDIN>);
+ Term::ReadKey::ReadMode(0), print "\n" if $RK;
+ }
+ else { $mycnf{'pass'} = $op{'password'}; } # Use password given on command line
+}
+
+$op{'com'} ||= 3;
+$op{'c'} ||= 1; # Used in collect_reports() if --r given integer value
+
+$relative_live = 0;
+$relative_infiles = 0;
+
+if(defined $op{'r'})
+{
+ if($op{r}) { $relative_live = 1; } # if -r was given an integer value
+ else { $relative_infiles = 1; }
+}
+
+# The report is written to a tmp file first.
+# Later it will be moved to $op{'outfile'} or emailed $op{'email'} if needed.
+($tmpfile_fh, $tmpfile) = tempfile() or die "Cannot open temporary file for writing: $!\n";
+
+if($op{'detach'})
+{
+ $SIG{'TERM'} = 'sig_handler';
+
+ if(fork())
+ {
+ print "mysqlreport has forked and detached.\n";
+ print "While running detached, mysqlreport writes reports to '$tmpfile'.\n";
+
+ exit;
+ }
+
+ open(STDIN, "</dev/null");
+ open(STDOUT, "> $tmpfile") or die "Cannot dup STDOUT: $!\n";
+ open(STDERR, "> $tmpfile") or die "Cannot dup STDERR: $!\n";
+}
+
+select $tmpfile_fh;
+$| = 1 if ($op{'detach'} || $relative_live);
+
+print "tmp file: $tmpfile\n" if $op{debug};
+
+# Connect to MySQL
+if(!$op{'infile'} && !$relative_infiles)
+{
+ connect_to_MySQL();
+}
+
+$have_innodb_vals = 1; # This might be set to 0 later in get_MySQL_version()
+
+if(defined $op{'r'})
+{
+ if($relative_live)
+ {
+ print STDERR "mysqlreport is writing relative reports to '$tmpfile'.\n" unless $op{'detach'};
+ get_MySQL_version();
+ collect_reports();
+ }
+
+ if($relative_infiles) { read_relative_infiles(); }
+}
+else
+{
+ if(!$op{'infile'})
+ {
+ get_MySQL_version();
+ get_vals();
+ get_vars();
+ }
+ else
+ {
+ read_infile($op{'infile'});
+ }
+
+ get_Com_values();
+
+ set_myisam_vals();
+ set_ib_vals() if $have_innodb_vals;
+
+ write_report();
+}
+
+exit_tasks_and_cleanup();
+
+exit;
+
+#
+# Subroutines
+#
+sub show_help_and_exit
+{
+ print <<"HELP";
+mysqlreport v3.5 Apr 16 2008
+mysqlreport makes an easy-to-read report of important MySQL status values.
+
+Command line options (abbreviations work):
+ --user USER Connect to MySQL as USER
+ --password PASS Use PASS or prompt for MySQL user's password
+ --host ADDRESS Connect to MySQL at ADDRESS
+ --port PORT Connect to MySQL at PORT
+ --socket SOCKET Connect to MySQL at SOCKET
+ --no-mycnf Don't read ~/.my.cnf
+ --infile FILE Read status values from FILE instead of MySQL
+ --outfile FILE Write report to FILE
+ --email ADDRESS Email report to ADDRESS (doesn't work on Windows)
+ --flush-status Issue FLUSH STATUS; after getting current values
+ --relative X Generate relative reports. If X is an integer,
+ reports are live from the MySQL server X seconds apart.
+ If X is a list of infiles (file1 file2 etc.),
+ reports are generated from the infiles in the order
+ that they are given.
+ --report-count N Collect N number of live relative reports (default 1)
+ --detach Fork and detach from terminal (run in background)
+ --help Prints this
+ --debug Print debugging information
+
+Visit http://hackmysql.com/mysqlreport for more information.
+HELP
+
+ exit;
+}
+
+sub get_user_mycnf
+{
+ print "get_user_mycnf\n" if $op{debug};
+
+ return if $WIN;
+ open MYCNF, "$ENV{HOME}/.my.cnf" or return;
+ while(<MYCNF>)
+ {
+ if(/^(.+?)\s*=\s*"?(.+?)"?\s*$/)
+ {
+ $mycnf{$1} = $2;
+ print "get_user_mycnf: read '$1 = $2'\n" if $op{debug};
+ }
+ }
+ $mycnf{'pass'} ||= $mycnf{'password'} if exists $mycnf{'password'};
+ close MYCNF;
+}
+
+sub connect_to_MySQL
+{
+ print "connect_to_MySQL\n" if $op{debug};
+
+ my $dsn;
+
+ if($mycnf{'socket'} && -S $mycnf{'socket'})
+ {
+ $dsn = "DBI:mysql:mysql_socket=$mycnf{socket}";
+ }
+ elsif($mycnf{'host'})
+ {
+ $dsn = "DBI:mysql:host=$mycnf{host}" . ($mycnf{port} ? ";port=$mycnf{port}" : "");
+ }
+ else
+ {
+ $dsn = "DBI:mysql:host=localhost";
+ }
+
+ print "connect_to_MySQL: DBI DSN: $dsn\n" if $op{debug};
+
+ $dbh = DBI->connect($dsn, $mycnf{'user'}, $mycnf{'pass'}) or die;
+}
+
+sub collect_reports
+{
+ print "collect_reports\n" if $op{debug};
+
+ my $i;
+
+ get_vals();
+ get_vars();
+
+ get_Com_values();
+
+ %stats_past = %stats;
+
+ set_myisam_vals();
+ set_ib_vals() if $have_innodb_vals;
+
+ print "#\n# Beginning report, 0 0:0:0\n#\n";
+
+ write_report();
+
+ for($i = 0; $i < $op{'c'}; $i++)
+ {
+ $dbh->disconnect();
+
+ sleep($op{'r'});
+
+ connect_to_MySQL();
+
+ print "\n#\n# Interval report " , $i + 1 , ", +", sec_to_dhms(($i + 1) * $op{'r'}), "\n#\n";
+
+ get_vals();
+
+ write_relative_report();
+ }
+}
+
+sub read_relative_infiles
+{
+ print "read_relative_infiles\n" if $op{debug};
+
+ my $slurp; # Used to check infiles for multiple sets of status values
+ my $n_stats; # Number of multiple sets of status values in an infile
+ my $infile;
+ my $report_n; # Report number
+
+ $report_n = 1;
+
+ foreach $infile (@ARGV)
+ {
+ # Read all of infile into $slurp
+ open INFILE, "< $infile" or warn and next;
+ $slurp = do { local $/; <INFILE> };
+ close INFILE;
+
+ $n_stats = 0;
+
+ # Count number of status value sets
+ $n_stats++ while $slurp =~ /Aborted_clients/g;
+
+ print "read_relative_infiles: found $n_stats sets of status values in file '$infile'\n"
+ if $op{debug};
+
+ if($n_stats == 1)
+ {
+ read_infile($infile);
+ relative_infile_report($report_n++);
+ }
+
+ if($n_stats > 1)
+ {
+ my @tmpfile_fh;
+ my @tmpfile_name;
+ my $i;
+ my $stat_n; # Status value set number
+
+ # Create a tmp file for each set of status values
+ for($i = 0; $i < $n_stats; $i++)
+ {
+ my ($fh, $name) = tempfile()
+ or die "read_relative_infiles: cannot open temporary file for writing: $!\n";
+
+ push(@tmpfile_fh, $fh);
+ push(@tmpfile_name, $name);
+
+ print "read_relative_infiles: created tmp file '$name' for set $i\n" if $op{debug};
+ }
+
+ $i = 0;
+ $stat_n = 0;
+
+ select $tmpfile_fh[$i];
+
+ # Read infile again and copy each set of status values to seperate tmp files
+ open INFILE, "< $infile" or warn and next;
+ while(<INFILE>)
+ {
+ next if /^\+/;
+ next if /^$/;
+
+ # The infile must begin with the system variable values.
+ # Therefore, the first occurance of Aborted_clients indicates the beginning
+ # of the first set of status values if no sets have occured yet ($stat_n == 0).
+ # In this case, the following status values are printed to the current fh,
+ # along with the system variable values read thus far, until Aborted_clients
+ # occurs again. Then begins the second and subsequent sets of status values.
+
+ if(/Aborted_clients/)
+ {
+ print and next if $stat_n++ == 0;
+ select $tmpfile_fh[++$i];
+ }
+
+ print;
+ }
+ close INFILE;
+
+ # Re-select the main tmp file into which the reports are being written.
+ select $tmpfile_fh;
+
+ for($i = 0; $i < $n_stats; $i++)
+ {
+ close $tmpfile_fh[$i];
+
+ print "read_relative_infiles: reading set $i tmp file '$tmpfile_name[$i]'\n"
+ if $op{debug};
+
+ read_infile($tmpfile_name[$i]);
+ relative_infile_report($report_n++);
+
+ if($WIN) { `del $tmpfile_name[$i]`; }
+ else { `rm -f $tmpfile_name[$i]`; }
+
+ print "read_relative_infiles: deleted set $i tmp file '$tmpfile_name[$i]'\n"
+ if $op{debug};
+ }
+
+ } # if($n_stats > 1)
+ } # foreach $infile (@files)
+}
+
+sub relative_infile_report
+{
+ print "relative_infile_report\n" if $op{debug};
+
+ my $report_n = shift;
+
+ if($report_n == 1)
+ {
+ get_Com_values();
+
+ %stats_past = %stats;
+
+ set_myisam_vals();
+ set_ib_vals() if $have_innodb_vals;
+
+ print "#\n# Beginning report, 0 0:0:0\n#\n";
+
+ write_report();
+ }
+ else
+ {
+ print "\n#\n# Interval report ", $report_n - 1, ", +",
+ sec_to_dhms($stats{Uptime} - $stats_past{Uptime}),
+ "\n#\n";
+
+ write_relative_report();
+ }
+}
+
+sub get_vals
+{
+ print "get_vals\n" if $op{debug};
+
+ my @row;
+
+ # Get status values
+ if($MySQL_version >= 50002)
+ {
+ $query = $dbh->prepare("SHOW GLOBAL STATUS;");
+ }
+ else
+ {
+ $query = $dbh->prepare("SHOW STATUS;");
+ }
+ $query->execute();
+ while(@row = $query->fetchrow_array()) { $stats{$row[0]} = $row[1]; }
+
+ $real_uptime = $stats{'Uptime'};
+}
+
+sub get_vars
+{
+ print "get_vars\n" if $op{debug};
+
+ my @row;
+
+ # Get server system variables
+ $query = $dbh->prepare("SHOW VARIABLES;");
+ $query->execute();
+ while(@row = $query->fetchrow_array()) { $vars{$row[0]} = $row[1]; }
+
+ # table_cache was renamed to table_open_cache in MySQL 5.1.3
+ if($MySQL_version >= 50103)
+ {
+ $vars{'table_cache'} = $vars{'table_open_cache'};
+ }
+}
+
+sub read_infile
+{
+ print "read_infile\n" if $op{debug};
+
+ my $infile = shift;
+
+ # Default required system variable values if not set in INFILE.
+ # As of mysqlreport v3.5 the direct output from SHOW VARIABLES;
+ # can be put into INFILE instead. See http://hackmysql.com/mysqlreportdoc
+ # for details.
+ $vars{'version'} = "0.0.0" if !exists $vars{'version'};
+ $vars{'table_cache'} = 64 if !exists $vars{'table_cache'};
+ $vars{'max_connections'} = 100 if !exists $vars{'max_connections'};
+ $vars{'key_buffer_size'} = 8388600 if !exists $vars{'key_buffer_size'}; # 8M
+ $vars{'thread_cache_size'} = 0 if !exists $vars{'thread_cache_size'};
+ $vars{'tmp_table_size'} = 0 if !exists $vars{'tmp_table_size'};
+ $vars{'long_query_time'} = '?' if !exists $vars{'long_query_time'};
+ $vars{'log_slow_queries'} = '?' if !exists $vars{'log_slow_queries'};
+
+ # One should also add:
+ # key_cache_block_size
+ # query_cache_size
+ # to INFILE if needed.
+
+ open INFILE, "< $infile" or die "Cannot open INFILE '$infile': $!\n";
+
+ while(<INFILE>)
+ {
+ last if !defined $_;
+
+ next if /^\+/; # skip divider lines
+ next if /^$/; # skip blank lines
+
+ next until /(Aborted_clients|back_log|=)/;
+
+ if($1 eq 'Aborted_clients') # status values
+ {
+ print "read_infile: start stats\n" if $op{debug};
+
+ while($_)
+ {
+ chomp;
+ if(/([A-Za-z_]+)[\s\t|]+(\d+)/)
+ {
+ $stats{$1} = $2;
+ print "read_infile: save $1 = $2\n" if $op{debug};
+ }
+ else { print "read_infile: ignore '$_'\n" if $op{debug}; }
+
+ last if $1 eq 'Uptime'; # exit while() if end of status values
+ $_ = <INFILE>; # otherwise, read next line of status values
+ }
+ }
+ elsif($1 eq 'back_log') # system variable values
+ {
+ print "read_infile: start vars\n" if $op{debug};
+
+ while($_)
+ {
+ chomp;
+ if(/([A-Za-z_]+)[\s\t|]+([\w\.\-]+)/) # This will exclude some vars
+ { # like pid_file which we don't need
+ $vars{$1} = $2;
+ print "read_infile: save $1 = $2\n" if $op{debug};
+ }
+ else { print "read_infile: ignore '$_'\n" if $op{debug}; }
+
+ last if $1 eq 'wait_timeout'; # exit while() if end of vars
+ $_ = <INFILE>; # otherwise, read next line of vars
+ }
+ }
+ elsif($1 eq '=') # old style, manually added system variable values
+ {
+ print "read_infile: start old vars\n" if $op{debug};
+
+ while($_ && $_ =~ /=/)
+ {
+ chomp;
+ if(/^\s*(\w+)\s*=\s*([0-9.]+)(M*)\s*$/) # e.g.: key_buffer_size = 128M
+ {
+ $vars{$1} = ($3 ? $2 * 1024 * 1024 : $2);
+ print "read_infile: read '$_' as $1 = $vars{$1}\n" if $op{debug};
+ }
+ else { print "read_infile: ignore '$_'\n" if $op{debug}; }
+
+ $_ = <INFILE>; # otherwise, read next line of old vars
+ }
+
+ redo;
+ }
+ else
+ {
+ print "read_infile: unrecognized line: '$_'\n" if $op{debug};
+ }
+ }
+
+ close INFILE;
+
+ $real_uptime = $stats{'Uptime'};
+
+ $vars{'table_cache'} = $vars{'table_open_cache'} if exists $vars{'table_open_cache'};
+
+ get_MySQL_version();
+}
+
+sub get_MySQL_version
+{
+ print "get_MySQL_version\n" if $op{debug};
+
+ return if $MySQL_version;
+
+ my ($major, $minor, $patch);
+
+ if($op{'infile'} || $relative_infiles)
+ {
+ ($major, $minor, $patch) = ($vars{'version'} =~ /(\d{1,2})\.(\d{1,2})\.(\d{1,2})/);
+ }
+ else
+ {
+ my @row;
+
+ $query = $dbh->prepare("SHOW VARIABLES LIKE 'version';");
+ $query->execute();
+ @row = $query->fetchrow_array();
+ ($major, $minor, $patch) = ($row[1] =~ /(\d{1,2})\.(\d{1,2})\.(\d{1,2})/);
+ }
+
+ $MySQL_version = sprintf("%d%02d%02d", $major, $minor, $patch);
+
+ # Innodb_ status values were added in 5.0.2
+ if($MySQL_version < 50002)
+ {
+ $have_innodb_vals = 0;
+ print "get_MySQL_version: no InnoDB reports because MySQL version is older than 5.0.2\n" if $op{debug};
+ }
+}
+
+sub set_myisam_vals
+{
+ print "set_myisam_vals\n" if $op{debug};
+
+ $questions = $stats{'Questions'};
+
+ $key_read_ratio = sprintf "%.2f",
+ ($stats{'Key_read_requests'} ?
+ 100 - ($stats{'Key_reads'} / $stats{'Key_read_requests'}) * 100 :
+ 0);
+
+ $key_write_ratio = sprintf "%.2f",
+ ($stats{'Key_write_requests'} ?
+ 100 - ($stats{'Key_writes'} / $stats{'Key_write_requests'}) * 100 :
+ 0);
+
+ $key_cache_block_size = (defined $vars{'key_cache_block_size'} ?
+ $vars{'key_cache_block_size'} :
+ 1024);
+
+ $key_buffer_used = $stats{'Key_blocks_used'} * $key_cache_block_size;
+
+ if(defined $stats{'Key_blocks_unused'}) # MySQL 4.1.2+
+ {
+ $key_buffer_usage = $vars{'key_buffer_size'} -
+ ($stats{'Key_blocks_unused'} * $key_cache_block_size);
+ }
+ else { $key_buffer_usage = -1; }
+
+ # Data Manipulation Statements: http://dev.mysql.com/doc/refman/5.0/en/data-manipulation.html
+ %DMS_vals =
+ (
+ SELECT => $stats{'Com_select'},
+ INSERT => $stats{'Com_insert'} + $stats{'Com_insert_select'},
+ REPLACE => $stats{'Com_replace'} + $stats{'Com_replace_select'},
+ UPDATE => $stats{'Com_update'} +
+ (exists $stats{'Com_update_multi'} ? $stats{'Com_update_multi'} : 0),
+ DELETE => $stats{'Com_delete'} +
+ (exists $stats{'Com_delete_multi'} ? $stats{'Com_delete_multi'} : 0)
+ );
+
+ $dms = $DMS_vals{SELECT} + $DMS_vals{INSERT} + $DMS_vals{REPLACE} + $DMS_vals{UPDATE} + $DMS_vals{DELETE};
+
+ $slow_query_t = format_u_time($vars{long_query_time});
+
+}
+
+sub set_ib_vals
+{
+ print "set_ib_vals\n" if $op{debug};
+
+ $ib_bp_used = ($stats{'Innodb_buffer_pool_pages_total'} -
+ $stats{'Innodb_buffer_pool_pages_free'}) *
+ $stats{'Innodb_page_size'};
+
+ $ib_bp_total = $stats{'Innodb_buffer_pool_pages_total'} * $stats{'Innodb_page_size'};
+
+ $ib_bp_read_ratio = sprintf "%.2f",
+ ($stats{'Innodb_buffer_pool_read_requests'} ?
+ 100 - ($stats{'Innodb_buffer_pool_reads'} /
+ $stats{'Innodb_buffer_pool_read_requests'}) * 100 :
+ 0);
+}
+
+sub write_relative_report
+{
+ print "write_relative_report\n" if $op{debug};
+
+ %stats_present = %stats;
+
+ for(keys %stats)
+ {
+ if($stats_past{$_} =~ /\d+/)
+ {
+ if($stats_present{$_} >= $stats_past{$_}) # Avoid negative values
+ {
+ $stats{$_} = $stats_present{$_} - $stats_past{$_};
+ }
+ }
+ }
+
+ # These values are either "at present" or "high water marks".
+ # Therefore, it is more logical to not relativize these values.
+ # Doing otherwise causes strange and misleading values.
+ $stats{'Key_blocks_used'} = $stats_present{'Key_blocks_used'};
+ $stats{'Open_tables'} = $stats_present{'Open_tables'};
+ $stats{'Max_used_connections'} = $stats_present{'Max_used_connections'};
+ $stats{'Threads_running'} = $stats_present{'Threads_running'};
+ $stats{'Threads_connected'} = $stats_present{'Threads_connected'};
+ $stats{'Threads_cached'} = $stats_present{'Threads_cached'};
+ $stats{'Qcache_free_blocks'} = $stats_present{'Qcache_free_blocks'};
+ $stats{'Qcache_total_blocks'} = $stats_present{'Qcache_total_blocks'};
+ $stats{'Qcache_free_memory'} = $stats_present{'Qcache_free_memory'};
+ if($have_innodb_vals)
+ {
+ $stats{'Innodb_page_size'} = $stats_present{'Innodb_page_size'};
+ $stats{'Innodb_buffer_pool_pages_data'} = $stats_present{'Innodb_buffer_pool_pages_data'};
+ $stats{'Innodb_buffer_pool_pages_dirty'} = $stats_present{'Innodb_buffer_pool_pages_dirty'};
+ $stats{'Innodb_buffer_pool_pages_free'} = $stats_present{'Innodb_buffer_pool_pages_free'};
+ $stats{'Innodb_buffer_pool_pages_latched'} = $stats_present{'Innodb_buffer_pool_pages_latched'};
+ $stats{'Innodb_buffer_pool_pages_misc'} = $stats_present{'Innodb_buffer_pool_pages_misc'};
+ $stats{'Innodb_buffer_pool_pages_total'} = $stats_present{'Innodb_buffer_pool_pages_total'};
+ $stats{'Innodb_data_pending_fsyncs'} = $stats_present{'Innodb_data_pending_fsyncs'};
+ $stats{'Innodb_data_pending_reads'} = $stats_present{'Innodb_data_pending_reads'};
+ $stats{'Innodb_data_pending_writes'} = $stats_present{'Innodb_data_pending_writes'};
+
+ # Innodb_row_lock_ values were added in MySQL 5.0.3
+ if($MySQL_version >= 50003)
+ {
+ $stats{'Innodb_row_lock_current_waits'} = $stats_present{'Innodb_row_lock_current_waits'};
+ $stats{'Innodb_row_lock_time_avg'} = $stats_present{'Innodb_row_lock_time_avg'};
+ $stats{'Innodb_row_lock_time_max'} = $stats_present{'Innodb_row_lock_time_max'};
+ }
+ }
+
+ get_Com_values();
+
+ %stats_past = %stats_present;
+
+ set_myisam_vals();
+ set_ib_vals() if $have_innodb_vals;
+
+ write_report();
+}
+
+sub write_report
+{
+ print "write_report\n" if $op{debug};
+
+ $~ = 'MYSQL_TIME', write;
+ $~ = 'KEY_BUFF_MAX', write;
+ if($key_buffer_usage != -1) { $~ = 'KEY_BUFF_USAGE', write }
+ $~ = 'KEY_RATIOS', write;
+ write_DTQ();
+ $~ = 'SLOW_DMS', write;
+ write_DMS();
+ write_Com();
+ $~ = 'SAS', write;
+ write_qcache();
+ $~ = 'REPORT_END', write;
+ $~ = 'TAB', write;
+
+ write_InnoDB() if $have_innodb_vals;
+}
+
+sub sec_to_dhms # Seconds to days hours:minutes:seconds
+{
+ my $s = shift;
+ my ($d, $h, $m) = (0, 0, 0);
+
+ return '0 0:0:0' if $s <= 0;
+
+ if($s >= 86400)
+ {
+ $d = int $s / 86400;
+ $s -= $d * 86400;
+ }
+
+ if($s >= 3600)
+ {
+ $h = int $s / 3600;
+ $s -= $h * 3600;
+ }
+
+ $m = int $s / 60;
+ $s -= $m * 60;
+
+ return "$d $h:$m:$s";
+}
+
+sub make_short
+{
+ my ($number, $kb, $d) = @_;
+ my $n = 0;
+ my $short;
+
+ $d ||= 2;
+
+ if($kb) { while ($number > 1023) { $number /= 1024; $n++; }; }
+ else { while ($number > 999) { $number /= 1000; $n++; }; }
+
+ $short = sprintf "%.${d}f%s", $number, ('','k','M','G','T')[$n];
+ if($short =~ /^(.+)\.(00)$/) { return $1; } # 12.00 -> 12 but not 12.00k -> 12k
+
+ return $short;
+}
+
+# What began as a simple but great idea has become the new standard:
+# long_query_time in microseconds. For MySQL 5.1.21+ and 6.0.4+ this
+# is now standard. For 4.1 and 5.0 patches, the architects of this
+# idea provide: http://www.mysqlperformanceblog.com/mysql-patches/
+# Relevant notes in MySQL manual:
+# http://dev.mysql.com/doc/refman/5.1/en/slow-query-log.html
+# http://dev.mysql.com/doc/refman/6.0/en/slow-query-log.html
+#
+# The format_u_time sub simply beautifies long_query_time.
+
+sub format_u_time # format microsecond (µ) time value
+{
+ # 0.000000 - 0.000999 = 0 - 999 µ
+ # 0.001000 - 0.999999 = 1 ms - 999.999 ms
+ # 1.000000 - n.nnnnnn = 1 s - n.nnnnn s
+
+ my $t = shift;
+ my $f; # formatted µ time
+ my $u = chr(($WIN ? 230 : 181));
+
+ $t = 0 if $t < 0;
+
+ if($t > 0 && $t <= 0.000999)
+ {
+ $f = ($t * 1000000) . " $u";
+ }
+ elsif($t >= 0.001000 && $t <= 0.999999)
+ {
+ $f = ($t * 1000) . ' ms';
+ }
+ elsif($t >= 1)
+ {
+ $f = ($t * 1) . ' s'; # * 1 to remove insignificant zeros
+ }
+ else
+ {
+ $f = 0; # $t should = 0 at this point
+ }
+
+ return $f;
+}
+
+sub perc # Percentage
+{
+ my($is, $of) = @_;
+ $is = 0 if (not defined $is);
+ return sprintf "%.2f", ($is * 100) / ($of ||= 1);
+}
+
+sub t # Time average per second
+{
+ my $val = shift;
+ return 0 if !$val;
+ return(make_short($val / $stats{'Uptime'}, 0, 1));
+}
+
+sub email_report # Email given report to $op{'email'}
+{
+ print "email_report\n" if $op{debug};
+
+ return if $WIN;
+
+ my $report = shift;
+
+ open SENDMAIL, "|/usr/sbin/sendmail -t";
+ print SENDMAIL "From: mysqlreport\n";
+ print SENDMAIL "To: $op{email}\n";
+ print SENDMAIL "Subject: MySQL status report on " . ($mycnf{'host'} || 'localhost') . "\n\n";
+ print SENDMAIL `cat $report`;
+ close SENDMAIL;
+}
+
+sub cat_report # Print given report to screen
+{
+ print "cat_report\n" if $op{debug};
+
+ my $report = shift;
+ my @report;
+
+ open REPORT, "< $report";
+ @report = <REPORT>;
+ close REPORT;
+ print @report;
+}
+
+sub get_Com_values
+{
+ print "get_Com_values\n" if $op{debug};
+
+ %Com_vals = ();
+
+ # Make copy of just the Com_ values
+ for(keys %stats)
+ {
+ if(grep /^Com_/, $_ and $stats{$_} > 0)
+ {
+ /^Com_(.*)/;
+ $Com_vals{$1} = $stats{$_};
+ }
+ }
+
+ # Remove DMS values
+ delete $Com_vals{'select'};
+ delete $Com_vals{'insert'};
+ delete $Com_vals{'insert_select'};
+ delete $Com_vals{'replace'};
+ delete $Com_vals{'replace_select'};
+ delete $Com_vals{'update'};
+ delete $Com_vals{'update_multi'} if exists $Com_vals{'update_multi'};
+ delete $Com_vals{'delete'};
+ delete $Com_vals{'delete_multi'} if exists $Com_vals{'delete_multi'};
+}
+
+sub write_DTQ # Write DTQ report in descending order by values
+{
+ print "write_DTQ\n" if $op{debug};
+
+ $~ = 'DTQ';
+
+ my %DTQ;
+ my $first = 1;
+
+ # Total Com values
+ $stat_val = 0;
+ for(values %Com_vals) { $stat_val += $_; }
+ $DTQ{'Com_'} = $stat_val;
+
+ $DTQ{'DMS'} = $dms;
+ $DTQ{'QC Hits'} = $stats{'Qcache_hits'} if $stats{'Qcache_hits'} != 0;
+ $DTQ{'COM_QUIT'} = int (($stats{'Connections'} - 2) - ($stats{'Aborted_clients'} / 2));
+
+ $stat_val = 0;
+ for(values %DTQ) { $stat_val += $_; }
+ if($questions != $stat_val)
+ {
+ $DTQ{($questions > $stat_val ? '+Unknown' : '-Unknown')} = abs $questions - $stat_val;
+ }
+
+ for(sort { $DTQ{$b} <=> $DTQ{$a} } keys(%DTQ))
+ {
+ if($first) { $stat_label = '%Total:'; $first = 0; }
+ else { $stat_label = ''; }
+
+ $stat_name = $_;
+ $stat_val = $DTQ{$_};
+ write;
+ }
+}
+
+sub write_DMS # Write DMS report in descending order by values
+{
+ print "write_DMS\n" if $op{debug};
+
+ $~ = 'DMS';
+
+ for(sort { $DMS_vals{$b} <=> $DMS_vals{$a} } keys(%DMS_vals))
+ {
+ $stat_name = $_;
+ $stat_val = $DMS_vals{$_};
+ write;
+ }
+}
+
+sub write_Com # Write COM report in descending order by values
+{
+ print "write_Com\n" if $op{debug};
+
+ my $i = $op{'com'};
+
+ $~ = 'COM_1';
+
+ # Total Com values and write first line of COM report
+ $stat_label = '%Total:' unless $op{'dtq'};
+ $stat_val = 0;
+ for(values %Com_vals) { $stat_val += $_; }
+ write;
+
+ $~ = 'COM_2';
+
+ # Sort remaining Com values, print only the top $op{'com'} number of values
+ for(sort { $Com_vals{$b} <=> $Com_vals{$a} } keys(%Com_vals))
+ {
+ $stat_name = $_;
+ $stat_val = $Com_vals{$_};
+ write;
+
+ last if !(--$i);
+ }
+}
+
+sub write_qcache
+{
+ print "write_qcache\n" if $op{debug};
+
+ # Query cache was added in 4.0.1, but have_query_cache was added in 4.0.2,
+ # ergo this method is slightly more reliable
+ return if not exists $vars{'query_cache_size'};
+ return if $vars{'query_cache_size'} == 0;
+
+ $qc_mem_used = $vars{'query_cache_size'} - $stats{'Qcache_free_memory'};
+ $qc_hi_r = sprintf "%.2f", $stats{'Qcache_hits'} / ($stats{'Qcache_inserts'} ||= 1);
+ $qc_ip_r = sprintf "%.2f", $stats{'Qcache_inserts'} / ($stats{'Qcache_lowmem_prunes'} ||= 1);
+
+ $~ = 'QCACHE';
+ write;
+}
+
+sub write_InnoDB
+{
+ print "write_InnoDB\n" if $op{debug};
+
+ return if not defined $stats{'Innodb_page_size'};
+
+ $stats{'Innodb_buffer_pool_pages_latched'} = 0 if not defined $stats{'Innodb_buffer_pool_pages_latched'};
+
+ $~ = 'IB';
+ write;
+
+ # Innodb_row_lock_ values were added in MySQL 5.0.3
+ if($MySQL_version >= 50003)
+ {
+ $~ = 'IB_LOCK';
+ write;
+ }
+
+ # Data, Pages, Rows
+ $~ = 'IB_DPR';
+ write;
+}
+
+sub have_op
+{
+ my $key = shift;
+ return 1 if (exists $op{$key} && $op{$key} ne '');
+ return 0;
+}
+
+sub sig_handler
+{
+ print "\nReceived signal at " , scalar localtime , "\n";
+ exit_tasks_and_cleanup();
+ exit;
+}
+
+sub exit_tasks_and_cleanup
+{
+ print "exit_tasks_and_cleanup\n" if $op{debug};
+
+ close $tmpfile_fh;
+ select STDOUT unless $op{'detach'};
+
+ email_report($tmpfile) if $op{'email'};
+
+ cat_report($tmpfile) unless $op{'detach'};
+
+ if($op{'outfile'})
+ {
+ if($WIN) { `move $tmpfile $op{outfile}`; }
+ else { `mv $tmpfile $op{outfile}`; }
+ }
+ else
+ {
+ if($WIN) { `del $tmpfile`; }
+ else { `rm -f $tmpfile`; }
+ }
+
+ if(!$op{'infile'} && !$relative_infiles)
+ {
+ if($op{'flush-status'})
+ {
+ $query = $dbh->prepare("FLUSH STATUS;");
+ $query->execute();
+ }
+
+ $query->finish();
+ $dbh->disconnect();
+ }
+}
+
+#
+# Formats
+#
+
+format MYSQL_TIME =
+MySQL @<<<<<<<<<<<<<<<< uptime @<<<<<<<<<<< @>>>>>>>>>>>>>>>>>>>>>>>>
+$vars{'version'}, sec_to_dhms($real_uptime), (($op{infile} || $relative_infiles) ? '' : scalar localtime)
+.
+
+format KEY_BUFF_MAX =
+
+__ Key _________________________________________________________________
+Buffer used @>>>>>> of @>>>>>> %Used: @>>>>>
+make_short($key_buffer_used, 1), make_short($vars{'key_buffer_size'}, 1), perc($key_buffer_used, $vars{'key_buffer_size'})
+.
+
+format KEY_BUFF_USAGE =
+ Current @>>>>>> %Usage: @>>>>>
+make_short($key_buffer_usage, 1), perc($key_buffer_usage, $vars{'key_buffer_size'})
+.
+
+format KEY_RATIOS =
+Write hit @>>>>>%
+$key_write_ratio
+Read hit @>>>>>%
+$key_read_ratio
+
+__ Questions ___________________________________________________________
+Total @>>>>>>>> @>>>>>/s
+make_short($questions), t($questions)
+.
+
+format DTQ =
+ @<<<<<<< @>>>>>>>> @>>>>>/s @>>>>>> @>>>>>
+$stat_name, make_short($stat_val), t($stat_val), $stat_label, perc($stat_val, $questions)
+.
+
+format SLOW_DMS =
+Slow @<<<<<<< @>>>>>> @>>>>>/s @>>>>> %DMS: @>>>>> Log: @>>
+$slow_query_t, make_short($stats{'Slow_queries'}), t($stats{'Slow_queries'}), perc($stats{'Slow_queries'}, $questions), perc($stats{'Slow_queries'}, $dms), $vars{'log_slow_queries'}
+DMS @>>>>>>>> @>>>>>/s @>>>>>
+make_short($dms), t($dms), perc($dms, $questions)
+.
+
+format DMS =
+ @<<<<<<< @>>>>>>>> @>>>>>/s @>>>>> @>>>>>
+$stat_name, make_short($stat_val), t($stat_val), perc($stat_val, $questions), perc($stat_val, $dms)
+.
+
+format COM_1 =
+Com_ @>>>>>>>> @>>>>>/s @>>>>>
+make_short($stat_val), t($stat_val), perc($stat_val, $questions)
+.
+
+format COM_2 =
+ @<<<<<<<<<< @>>>>>> @>>>>>/s @>>>>>
+$stat_name, make_short($stat_val), t($stat_val), perc($stat_val, $questions)
+.
+
+format SAS =
+
+__ SELECT and Sort _____________________________________________________
+Scan @>>>>>> @>>>>/s %SELECT: @>>>>>
+make_short($stats{'Select_scan'}), t($stats{'Select_scan'}), perc($stats{'Select_scan'}, $stats{'Com_select'})
+Range @>>>>>> @>>>>/s @>>>>>
+make_short($stats{'Select_range'}), t($stats{'Select_range'}), perc($stats{'Select_range'}, $stats{'Com_select'})
+Full join @>>>>>> @>>>>/s @>>>>>
+make_short($stats{'Select_full_join'}), t($stats{'Select_full_join'}), perc($stats{'Select_full_join'}, $stats{'Com_select'})
+Range check @>>>>>> @>>>>/s @>>>>>
+make_short($stats{'Select_range_check'}), t($stats{'Select_range_check'}), perc($stats{'Select_range_check'}, $stats{'Com_select'})
+Full rng join @>>>>>> @>>>>/s @>>>>>
+make_short($stats{'Select_full_range_join'}), t($stats{'Select_full_range_join'}), perc($stats{'Select_full_range_join'}, $stats{'Com_select'})
+Sort scan @>>>>>> @>>>>/s
+make_short($stats{'Sort_scan'}), t($stats{'Sort_scan'})
+Sort range @>>>>>> @>>>>/s
+make_short($stats{'Sort_range'}), t($stats{'Sort_range'})
+Sort mrg pass @>>>>>> @>>>>/s
+make_short($stats{'Sort_merge_passes'}), t($stats{'Sort_merge_passes'})
+.
+
+format QCACHE =
+
+__ Query Cache _________________________________________________________
+Memory usage @>>>>>> of @>>>>>> %Used: @>>>>>
+make_short($qc_mem_used, 1), make_short($vars{'query_cache_size'}, 1), perc($qc_mem_used, $vars{'query_cache_size'})
+Block Fragmnt @>>>>>%
+perc($stats{'Qcache_free_blocks'}, $stats{'Qcache_total_blocks'})
+Hits @>>>>>> @>>>>/s
+make_short($stats{'Qcache_hits'}), t($stats{'Qcache_hits'})
+Inserts @>>>>>> @>>>>/s
+make_short($stats{'Qcache_inserts'}), t($stats{'Qcache_inserts'})
+Insrt:Prune @>>>>>>:1 @>>>>/s
+make_short($qc_ip_r), t($stats{'Qcache_inserts'} - $stats{'Qcache_lowmem_prunes'})
+Hit:Insert @>>>>>>:1
+$qc_hi_r, t($qc_hi_r)
+.
+
+# Not really the end...
+format REPORT_END =
+
+__ Table Locks _________________________________________________________
+Waited @>>>>>>>> @>>>>>/s %Total: @>>>>>
+make_short($stats{'Table_locks_waited'}), t($stats{'Table_locks_waited'}), perc($stats{'Table_locks_waited'}, $stats{'Table_locks_waited'} + $stats{'Table_locks_immediate'});
+Immediate @>>>>>>>> @>>>>>/s
+make_short($stats{'Table_locks_immediate'}), t($stats{'Table_locks_immediate'})
+
+__ Tables ______________________________________________________________
+Open @>>>>>>>> of @>>> %Cache: @>>>>>
+$stats{'Open_tables'}, $vars{'table_cache'}, perc($stats{'Open_tables'}, $vars{'table_cache'})
+Opened @>>>>>>>> @>>>>>/s
+make_short($stats{'Opened_tables'}), t($stats{'Opened_tables'})
+
+__ Connections _________________________________________________________
+Max used @>>>>>>>> of @>>> %Max: @>>>>>
+$stats{'Max_used_connections'}, $vars{'max_connections'}, perc($stats{'Max_used_connections'}, $vars{'max_connections'})
+Total @>>>>>>>> @>>>>>/s
+make_short($stats{'Connections'}), t($stats{'Connections'})
+
+__ Created Temp ________________________________________________________
+Disk table @>>>>>>>> @>>>>>/s
+make_short($stats{'Created_tmp_disk_tables'}), t($stats{'Created_tmp_disk_tables'})
+Table @>>>>>>>> @>>>>>/s Size: @>>>>>
+make_short($stats{'Created_tmp_tables'}), t($stats{'Created_tmp_tables'}), make_short($vars{'tmp_table_size'}, 1, 1)
+File @>>>>>>>> @>>>>>/s
+make_short($stats{'Created_tmp_files'}), t($stats{'Created_tmp_files'})
+.
+
+format TAB =
+
+__ Threads _____________________________________________________________
+Running @>>>>>>>> of @>>>
+$stats{'Threads_running'}, $stats{'Threads_connected'}
+Cached @>>>>>>>> of @>>> %Hit: @>>>>>
+$stats{'Threads_cached'}, $vars{'thread_cache_size'}, make_short(100 - perc($stats{'Threads_created'}, $stats{'Connections'}))
+Created @>>>>>>>> @>>>>>/s
+make_short($stats{'Threads_created'}), t($stats{'Threads_created'})
+Slow @>>>>>>>> @>>>>>/s
+$stats{'Slow_launch_threads'}, t($stats{'Slow_launch_threads'})
+
+__ Aborted _____________________________________________________________
+Clients @>>>>>>>> @>>>>>/s
+make_short($stats{'Aborted_clients'}), t($stats{'Aborted_clients'})
+Connects @>>>>>>>> @>>>>>/s
+make_short($stats{'Aborted_connects'}), t($stats{'Aborted_connects'})
+
+__ Bytes _______________________________________________________________
+Sent @>>>>>>>> @>>>>>/s
+make_short($stats{'Bytes_sent'}), t($stats{'Bytes_sent'})
+Received @>>>>>>>> @>>>>>/s
+make_short($stats{'Bytes_received'}), t($stats{'Bytes_received'})
+.
+
+format IB =
+
+__ InnoDB Buffer Pool __________________________________________________
+Usage @>>>>>> of @>>>>>> %Used: @>>>>>
+make_short($ib_bp_used, 1), make_short($ib_bp_total, 1), perc($ib_bp_used, $ib_bp_total)
+Read hit @>>>>>%
+$ib_bp_read_ratio;
+Pages
+ Free @>>>>>>>> %Total: @>>>>>
+make_short($stats{'Innodb_buffer_pool_pages_free'}), perc($stats{'Innodb_buffer_pool_pages_free'}, $stats{'Innodb_buffer_pool_pages_total'})
+ Data @>>>>>>>> @>>>>> %Drty: @>>>>>
+make_short($stats{'Innodb_buffer_pool_pages_data'}), perc($stats{'Innodb_buffer_pool_pages_data'}, $stats{'Innodb_buffer_pool_pages_total'}), perc($stats{'Innodb_buffer_pool_pages_dirty'}, $stats{'Innodb_buffer_pool_pages_data'})
+ Misc @>>>>>>>> @>>>>>
+ $stats{'Innodb_buffer_pool_pages_misc'}, perc($stats{'Innodb_buffer_pool_pages_misc'}, $stats{'Innodb_buffer_pool_pages_total'})
+ Latched @>>>>>>>> @>>>>>
+$stats{'Innodb_buffer_pool_pages_latched'}, perc($stats{'Innodb_buffer_pool_pages_latched'}, $stats{'Innodb_buffer_pool_pages_total'})
+Reads @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_buffer_pool_read_requests'}), t($stats{'Innodb_buffer_pool_read_requests'})
+ From file @>>>>>>>> @>>>>>/s @>>>>>
+make_short($stats{'Innodb_buffer_pool_reads'}), t($stats{'Innodb_buffer_pool_reads'}), perc($stats{'Innodb_buffer_pool_reads'}, $stats{'Innodb_buffer_pool_read_requests'})
+ Ahead Rnd @>>>>>>>> @>>>>>/s
+$stats{'Innodb_buffer_pool_read_ahead_rnd'}, t($stats{'Innodb_buffer_pool_read_ahead_rnd'})
+ Ahead Sql @>>>>>>>> @>>>>>/s
+$stats{'Innodb_buffer_pool_read_ahead_seq'}, t($stats{'Innodb_buffer_pool_read_ahead_seq'})
+Writes @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_buffer_pool_write_requests'}), t($stats{'Innodb_buffer_pool_write_requests'})
+Flushes @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_buffer_pool_pages_flushed'}), t($stats{'Innodb_buffer_pool_pages_flushed'})
+Wait Free @>>>>>>>> @>>>>>/s
+$stats{'Innodb_buffer_pool_wait_free'}, t($stats{'Innodb_buffer_pool_wait_free'})
+.
+
+format IB_LOCK =
+
+__ InnoDB Lock _________________________________________________________
+Waits @>>>>>>>> @>>>>>/s
+$stats{'Innodb_row_lock_waits'}, t($stats{'Innodb_row_lock_waits'})
+Current @>>>>>>>>
+$stats{'Innodb_row_lock_current_waits'}
+Time acquiring
+ Total @>>>>>>>> ms
+$stats{'Innodb_row_lock_time'}
+ Average @>>>>>>>> ms
+$stats{'Innodb_row_lock_time_avg'}
+ Max @>>>>>>>> ms
+$stats{'Innodb_row_lock_time_max'}
+.
+
+format IB_DPR =
+
+__ InnoDB Data, Pages, Rows ____________________________________________
+Data
+ Reads @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_data_reads'}), t($stats{'Innodb_data_reads'})
+ Writes @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_data_writes'}), t($stats{'Innodb_data_writes'})
+ fsync @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_data_fsyncs'}), t($stats{'Innodb_data_fsyncs'})
+ Pending
+ Reads @>>>>>>>>
+$stats{'Innodb_data_pending_reads'}, t($stats{'Innodb_data_pending_reads'})
+ Writes @>>>>>>>>
+$stats{'Innodb_data_pending_writes'}, t($stats{'Innodb_data_pending_writes'})
+ fsync @>>>>>>>>
+$stats{'Innodb_data_pending_fsyncs'}, t($stats{'Innodb_data_pending_fsyncs'})
+
+Pages
+ Created @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_pages_created'}), t($stats{'Innodb_pages_created'})
+ Read @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_pages_read'}), t($stats{'Innodb_pages_read'})
+ Written @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_pages_written'}), t($stats{'Innodb_pages_written'})
+
+Rows
+ Deleted @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_rows_deleted'}), t($stats{'Innodb_rows_deleted'})
+ Inserted @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_rows_inserted'}), t($stats{'Innodb_rows_inserted'})
+ Read @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_rows_read'}), t($stats{'Innodb_rows_read'})
+ Updated @>>>>>>>> @>>>>>/s
+make_short($stats{'Innodb_rows_updated'}), t($stats{'Innodb_rows_updated'})
+.
--- /dev/null
+.TH "mysqlreport" "1" "2.5 2006-09-01 (docrev 2006-05-19)" "Daniel Nichter" "MYSQL"
+.SH "NAME"
+.LP
+mysqlreport \- Makes a friendly report of important MySQL status values
+.SH "SYNTAX"
+.LP
+mysqlreport [\fIoptions\fP]
+.SH "DESCRIPTION"
+.LP
+mysqlreport makes a friendly report of important MySQL status values. Actually,
+it makes a friendly report of nearly every status value from SHOW STATUS.
+Unlike SHOW STATUS which simply dumps over 100 values to screen in one long
+list, mysqlreport interprets and formats the values and presents the basic
+values and many more inferred values in a human\-readable format. Numerous
+example reports are available at the mysqlreport web page at
+http://hackmysql.com/mysqlreport.
+
+The benefit of mysqlreport is that it allows you to very quickly see a wide
+array of performance indicators for your MySQL server which would otherwise
+need to be calculated by hand from all the various SHOW STATUS values. For
+example, the Index Read Ratio is an important value but it's not present in
+SHOW STATUS; it's an inferred value (the ratio of Key_reads to
+Key_read_requests).
+
+This documentation outlines all the command line options in mysqlreport, most
+of which control which reports are printed. This document does not address
+how to interpret these reports; that topic is covered in the document Guide
+To Understanding mysqlreport at http://hackmysql.com/mysqlreportguide.
+
+.SH "OPTIONS"
+Technically, command line options are in the form \-\-option, but \-option works
+too. All options can be abbreviated if the abbreviation is unique. For example,
+option \-\-host can be abbreviated \-\-ho but not \-\-h because \-\-h is ambiguous: it
+could mean \-\-host or \-\-help.
+
+.LP
+
+.TP
+\fB\-\-help\fR
+Output help information and exit.
+
+.TP
+\fB\-\-user USER\fR
+
+.TP
+\fB\-\-password\fR
+As of version 2.3 \-\-password can take the password on the
+command line like "\-\-password FOO". Using \-\-password
+alone without giving a password on the command line
+causes mysqlreport to prompt for a password.
+
+.TP
+\fB\-\-host ADDRESS\fR
+
+.TP
+\fB\-\-port PORT\fR
+
+.TP
+\fB\-\-socket SOCKET\fR
+
+.TP
+\fB\-\-no\-mycnf\fR
+\-\-no\-mycnf makes mysqlreport not read ~/.my.cnf which it does by default
+otherwise. \-\-user and \-\-password always override values from ~/.my.cnf.
+
+.TP
+\fB\-\-dtq\fR
+Print Distribution of Total Queries (DTQ) report (under
+Total in Questions report). Queries (or Questions) can
+be divided into four main areas: DMS (see \-\-dms below),
+Com_ (see \-\-com below), COM_QUIT (see COM_QUIT and
+Questions at http://hackmysql.com/com_quit), and
+Unknown. \-\-dtq lists the number of queries in each of
+these areas in descending order.
+
+.TP
+\fB\-\-dms\fR
+Print Data Manipulation Statements (DMS) report (under
+DMS in Questions report). DMS are those from the MySQL
+manual section 13.2. Data Manipulation Statements.
+(Currently, mysqlreport considers only SELECT, INSERT,
+REPLACE, UPDATE, and DELETE.) Each DMS is listed in
+descending order by count.
+
+.TP
+\fB\-\-com N\fR
+Print top N number of non\-DMS Com_ status values in
+descending order (after DMS in Questions report). If N
+is not given, default is 3. Such non\-DMS Com_ values
+include Com_change_db, Com_show_tables, Com_rollback,
+etc.
+
+.TP
+\fB\-\-sas\fR
+Print report for Select_ and Sort_ status values (after
+Questions report). See MySQL Select and Sort Status
+Variables at http://hackmysql.com/selectandsort.
+
+.TP
+\fB\-\-tab\fR
+Print Threads, Aborted, and Bytes status reports (after
+Created temp report). As of mysqlreport v2.3 the
+Threads report reports on all Threads_ status values.
+
+.TP
+\fB\-\-qcache\fR
+Print Query Cache report.
+.TP
+\fB\-\-all\fR
+Equivalent to "\-\-dtq \-\-dms \-\-com 3 \-\-sas \-\-qcache".
+(Notice \-\-tab is not invoked by \-\-all.)
+
+.TP
+\fB\-\-infile FILE\fR
+Instead of getting SHOW STATUS values from MySQL, read
+values from FILE. FILE is often a copy of the output of
+SHOW STATUS including formatting characters (|, +, \-).
+mysqlreport expects FILE to have the format
+" value number " where value is only alpha and
+underscore characters (A\-Z and _) and number is a
+positive integer. Anything before, between, or after
+value and number is ignored. mysqlreport also needs
+the following MySQL server variables: version,
+table_cache, max_connections, key_buffer_size,
+query_cache_size. These values can be specified in
+INFILE in the format "name = value" where name is one
+of the aforementioned server variables and value is a
+positive integer with or without a trailing M and
+possible periods (for version). For example, to specify
+an 18M key_buffer_size: key_buffer_size = 18M. Or, a
+256 table_cache: table_cache = 256. The M implies
+Megabytes not million, so 18M means 18,874,368 not
+18,000,000. If these server variables are not specified
+the following defaults are used (respectively) which
+may cause strange values to be reported: 0.0.0, 64,
+100, 8M, 0.
+
+.TP
+\fB\-\-outfile FILE\fR
+After printing the report to screen, print the report
+to FILE too. Internally, mysqlreport always writes the
+report to a temp file first: /tmp/mysqlreport.PID on
+*nix, c:\mysqlreport.PID on Windows (PID is the
+script's process ID). Then it prints the temp file to
+screen. Then if \-\-outfile is specified, the temp file
+is copied to OUTFILE. After \-\-email (below), the temp
+file is deleted.
+
+.TP
+\fB\-\-email ADDRESS\fR
+After printing the report to screen, email the report
+to ADDRESS. This option requires sendmail in
+/usr/sbin/, therefore it does not work on Windows.
+/usr/sbin/sendmail can be a sym link to qmail, for
+example, or any MTA that emulates sendmail's \-t
+command line option and operation. The FROM: field is
+"mysqlreport", SUBJECT: is "MySQL status report".
+
+.TP
+\fB\-\-flush\-status\fR
+Execute a "FLUSH STATUS;" after generating the reports.
+If you do not have permissions in MySQL to do this an
+error from DBD::mysql::st will be printed after the
+reports.
+
+.SH "AUTHORS"
+.LP
+Daniel Nichter
+
+If mysqlreport breaks, send me a message from
+http://hackmysql.com/feedback
+with the error.
+
+.SH "SEE ALSO"
+.LP
+mytop(1)
+.LP
+The comprehensive Guide To Understanding mysqlreport at
+http://hackmysql.com/mysqlreportguide.
+
--- /dev/null
+# This file is intensionally empty to disable apparmor by default
--- /dev/null
+mysql-wsrep-5.6 (5.6.30-25.15-1) UNRELEASED; urgency=low
+
+ * Bump version numbers
+
+ -- Otto Kekäläinen <otto@seravo.fi> Wed, 22 Jun 2016 11:58:20 +0300
--- /dev/null
+debian/libmysqlclient18.links
+debian/libmysqlclient-dev.links
--- /dev/null
+Source: mysql-wsrep-5.6
+Section: database
+Priority: optional
+Maintainer: Codership Oy <info@codership.com>
+Build-Depends: bison,
+ chrpath,
+ cmake,
+ debhelper (>= 8.1.3~),
+ dh-apparmor,
+ dpkg-dev (>= 1.16.1~),
+ libaio-dev[linux-any],
+ libedit-dev,
+ libncurses5-dev (>= 5.0-6),
+ libwrap0-dev (>= 7.6-8.3),
+ lsb-release,
+ perl,
+ po-debconf,
+ psmisc,
+ zlib1g-dev (>= 1:1.1.3-5),
+ libssl-dev
+Standards-Version: 3.9.3
+Homepage: http://galeracluster.com/
+Vcs-Git: https://github.com/codership/mysql-wsrep.git
+Vcs-Browser: https://github.com/codership/mysql-wsrep
+
+Package: mysql-wsrep-libmysqlclient18
+Section: libs
+Architecture: any
+Depends: mysql-wsrep-common-5.6, ${misc:Depends}, ${shlibs:Depends}
+Pre-Depends: multiarch-support, ${misc:Pre-Depends}
+Replaces: libmysqlclient18
+Provides: libmysqlclient18
+Multi-Arch: same
+Description: MySQL database client library
+ MySQL is a fast, stable and true multi-user, multi-threaded SQL database
+ server. SQL (Structured Query Language) is the most popular database query
+ language in the world. The main goals of MySQL are speed, robustness and
+ ease of use.
+ .
+ This package includes the client library.
+
+Package: mysql-wsrep-libmysqlclient-dev
+Architecture: any
+Section: libdevel
+Depends: mysql-wsrep-libmysqlclient18 (= ${binary:Version}),
+ zlib1g-dev,
+ ${misc:Depends},
+ ${shlibs:Depends}
+Description: MySQL database development files
+ MySQL is a fast, stable and true multi-user, multi-threaded SQL database
+ server. SQL (Structured Query Language) is the most popular database query
+ language in the world. The main goals of MySQL are speed, robustness and
+ ease of use.
+ .
+ This package includes development libraries and header files.
+
+Package: mysql-wsrep-common-5.6
+Architecture: all
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Multi-Arch: foreign
+Provides: mysql-common, mysql-common-5.6
+Replaces: mysql-common, mysql-common-5.6
+Description: MySQL 5.6 specific common files, e.g. /etc/mysql/conf.d/my-5.6.cnf
+ MySQL is a fast, stable and true multi-user, multi-threaded SQL database
+ server. SQL (Structured Query Language) is the most popular database query
+ language in the world. The main goals of MySQL are speed, robustness and
+ ease of use.
+ .
+ This package also includes files needed by all versions of the client library,
+ e.g. /etc/mysql/my.cnf.
+
+Package: mysql-wsrep-client-5.6
+Architecture: any
+Depends: debianutils (>=1.6),
+ mysql-wsrep-common-5.6,
+ mysql-wsrep-libmysqlclient18,
+ ${misc:Depends},
+ ${perl:Depends},
+ ${shlibs:Depends}
+Provides: virtual-mysql-client
+Breaks: mysql-client-5.5,
+ mysql-client-5.6,
+ mysql-client-core-5.5,
+ mysql-client-core-5.6,
+ virtual-mysql-client
+Replaces: mysql-client-5.5,
+ mysql-client-5.6,
+ mysql-client-core-5.5,
+ mysql-client-core-5.6,
+ virtual-mysql-client
+Recommends: libdbd-mysql-perl (>= 1.2202),
+ libdbi-perl,
+ libterm-readkey-perl
+Description: MySQL database client binaries
+ MySQL is a fast, stable and true multi-user, multi-threaded SQL database
+ server. SQL (Structured Query Language) is the most popular database query
+ language in the world. The main goals of MySQL are speed, robustness and
+ ease of use.
+ .
+ This package includes the client binaries and the additional tools
+ innotop and mysqlreport.
+
+Package: mysql-wsrep-server-5.6
+Architecture: any
+Recommends: libhtml-template-perl
+Suggests: mailx, tinyca
+Pre-Depends: adduser (>= 3.40), debconf, mysql-wsrep-common-5.6
+Depends: initscripts,
+ libdbi-perl,
+ lsb-base (>= 3.0-10),
+ lsof,
+ mysql-wsrep-client-5.6 (>= ${binary:Version}),
+ passwd,
+ perl (>= 5.6),
+ psmisc,
+ rsync,
+ socat,
+ ${misc:Depends},
+ ${shlibs:Depends}
+Provides: virtual-mysql-server
+Breaks: mysql-server-5.5,
+ mysql-server-5.6,
+ mysql-server-core-5.5,
+ mysql-server-core-5.6,
+ virtual-mysql-server
+Replaces: mysql-server-5.5, mysql-server-5.6, virtual-mysql-server
+Description: MySQL database wsrep server binaries and system database setup
+ MySQL is a fast, stable and true multi-user, multi-threaded SQL database
+ server. SQL (Structured Query Language) is the most popular database query
+ language in the world. The main goals of MySQL are speed, robustness and
+ ease of use.
+ .
+ This package contains all the infrastructure needed to setup system
+ databases.
+
+Package: mysql-wsrep-testsuite-5.6
+Architecture: any
+Depends: mysql-wsrep-client-5.6 (= ${binary:Version}),
+ mysql-wsrep-server-5.6 (= ${binary:Version}),
+ python,
+ ${misc:Depends},
+ ${shlibs:Depends}
+Provides: virtual-mysql-testsuite
+Breaks: mysql-testsuite-5.5, mysql-testsuite-5.6, virtual-mysql-testsuite
+Replaces: mysql-testsuite-5.5, mysql-testsuite-5.6, virtual-mysql-testsuite
+Description: MySQL 5.6 testsuite
+ MySQL is a fast, stable, and true multi-user, multi-threaded SQL database
+ server. SQL (Structured Query Language) is the most popular database query
+ language in the world. The main goals of MySQL are speed, robustness and
+ ease of use.
+ .
+ This package includes the MySQL testsuite.
+
+Package: mysql-wsrep-5.6
+Architecture: any
+Depends: mysql-wsrep-client-5.6 (= ${binary:Version}),
+ mysql-wsrep-server-5.6 (= ${binary:Version}),
+ ${misc:Depends}
+Description: Metapackage that installs mysql-wsrep client and server packages.
--- /dev/null
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: MySQL 5.6
+Upstream-Contact: http://bugs.mysql.com/
+Source: http://dev.mysql.com/downloads/mysql/5.6.html
+Comments:
+ The file Docs/mysql.info is removed from the upstream source
+ because it is incompatible with the Debian Free Software Guidelines.
+ See debian/README.source for how this repacking was done.
+ .
+ Originally produced by a modified version of licensecheck2dep5
+ from CDBS by Clint Byrum <clint@ubuntu.com>. Hand modified to reduce
+ redundancy in the output and add appropriate license text.
+ .
+ Also, MySQL carries the "FOSS License Exception" specified in README
+ .
+ Quoting from README:
+ .
+ MySQL FOSS License Exception We want free and open source
+ software applications under certain licenses to be able to use
+ specified GPL-licensed MySQL client libraries despite the fact
+ that not all such FOSS licenses are compatible with version
+ 2 of the GNU General Public License. Therefore there are
+ special exceptions to the terms and conditions of the GPLv2
+ as applied to these client libraries, which are identified
+ and described in more detail in the FOSS License Exception at
+ <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
+ .
+ The text of the Above URL is quoted below, as of Aug 17, 2011.
+ .
+ > FOSS License Exception
+ > .
+ > Updated July 1, 2010
+ > .
+ > What is the FOSS License Exception? Oracle's Free and Open Source
+ > Software ("FOSS") License Exception (formerly known as the FLOSS
+ > License Exception) allows developers of FOSS applications to include
+ > Oracle's MySQL Client Libraries (also referred to as "MySQL Drivers"
+ > or "MySQL Connectors") with their FOSS applications. MySQL Client
+ > Libraries are typically licensed pursuant to version 2 of the General
+ > Public License ("GPL"), but this exception permits distribution of
+ > certain MySQL Client Libraries with a developer's FOSS applications
+ > licensed under the terms of another FOSS license listed below,
+ > even though such other FOSS license may be incompatible with the GPL.
+ > .
+ > The following terms and conditions describe the circumstances under
+ > which Oracle's FOSS License Exception applies.
+ > .
+ > Oracle's FOSS License Exception Terms and Conditions Definitions.
+ > "Derivative Work" means a derivative work, as defined under applicable
+ > copyright law, formed entirely from the Program and one or more
+ > FOSS Applications.
+ > .
+ > "FOSS Application" means a free and open source software application
+ > distributed subject to a license listed in the section below titled
+ > "FOSS License List."
+ > .
+ > "FOSS Notice" means a notice placed by Oracle or MySQL in a copy
+ > of the MySQL Client Libraries stating that such copy of the MySQL
+ > Client Libraries may be distributed under Oracle's or MySQL's FOSS
+ > (or FLOSS) License Exception.
+ > .
+ > "Independent Work" means portions of the Derivative Work that are not
+ > derived from the Program and can reasonably be considered independent
+ > and separate works.
+ > .
+ > "Program" means a copy of Oracle's MySQL Client Libraries that
+ > contains a FOSS Notice.
+ > .
+ > A FOSS application developer ("you" or "your") may distribute a
+ > Derivative Work provided that you and the Derivative Work meet all
+ > of the following conditions: You obey the GPL in all respects for
+ > the Program and all portions (including modifications) of the Program
+ > included in the Derivative Work (provided that this condition does not
+ > apply to Independent Works); The Derivative Work does not include any
+ > work licensed under the GPL other than the Program; You distribute
+ > Independent Works subject to a license listed in the section below
+ > titled "FOSS License List"; You distribute Independent Works in
+ > object code or executable form with the complete corresponding
+ > machine-readable source code on the same medium and under the same
+ > FOSS license applying to the object code or executable forms; All
+ > works that are aggregated with the Program or the Derivative Work
+ > on a medium or volume of storage are not derivative works of the
+ > Program, Derivative Work or FOSS Application, and must reasonably
+ > be considered independent and separate works. Oracle reserves all
+ > rights not expressly granted in these terms and conditions. If all
+ > of the above conditions are not met, then this FOSS License Exception
+ > does not apply to you or your Derivative Work.
+ > .
+ > FOSS License List
+ > .
+ > License Name Version(s)/Copyright Date
+ > Release Early Certified Software
+ > Academic Free License 2.0
+ > Apache Software License 1.0/1.1/2.0
+ > Apple Public Source License 2.0
+ > Artistic license From Perl 5.8.0
+ > BSD license "July 22 1999"
+ > Common Development and Distribution License (CDDL) 1.0
+ > Common Public License 1.0
+ > Eclipse Public License 1.0
+ > European Union Public License (EUPL)[1] 1.1
+ > GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1/3.0
+ > GNU General Public License (GPL) 3.0
+ > IBM Public License 1.0
+ > Jabber Open Source License 1.0
+ > MIT License (As listed in file MIT-License.txt) -
+ > Mozilla Public License (MPL) 1.0/1.1
+ > Open Software License 2.0
+ > OpenSSL license (with original SSLeay license) "2003" ("1998")
+ > PHP License 3.0/3.01
+ > Python license (CNRI Python License) -
+ > Python Software Foundation License 2.1.1
+ > Sleepycat License "1999"
+ > University of Illinois/NCSA Open Source License -
+ > W3C License "2001"
+ > X11 License "2001"
+ > Zlib/libpng License -
+ > Zope Public License 2.0
+ > [1] When an Independent Work is licensed under a "Compatible License"
+ > pursuant to the EUPL, the Compatible License rather than the EUPL is
+ > the applicable license for purposes of these FOSS License Exception
+ > Terms and Conditions.
+ .
+ The above text is subject to this copyright notice:
+ © 2010, Oracle and/or its affiliates.
+
+Files: cmd-line-utils/libedit/config.h
+ dbug/example1.c
+ dbug/example2.c
+ dbug/example3.c
+ dbug/factorial.c
+ dbug/main.c
+ dbug/my_main.c
+ dbug/remove_function_from_trace.pl
+ dbug/tests.c
+ dbug/tests-t.pl
+ extra/yassl/src/dummy.cpp
+ include/probes_mysql_nodtrace.h
+ libmysqld/resource.h
+ mysql-test/*
+ regex/*
+ sql-bench/graph-compare-results.sh
+ storage/ndb/bin/*
+ storage/ndb/demos/*
+ support-files/binary-configure.sh
+ support-files/my-huge.cnf.sh
+ support-files/my-innodb-heavy-4G.cnf.sh
+ support-files/my-large.cnf.sh
+ support-files/my-medium.cnf.sh
+ support-files/my-small.cnf.sh
+ support-files/mysqld_multi.server.sh
+ support-files/mysql-log-rotate.sh
+ support-files/mysql.server-sys5.sh
+Copyright: UNKNOWN
+Comment: These files fall under the blanket license specified in the file
+ COPYING and README
+License: GPL-2
+ GPLv2 Disclaimer
+ For the avoidance of doubt, except that if any license choice
+ other than GPL or LGPL is available it will apply instead,
+ Oracle elects to use only the General Public License version 2
+ (GPLv2) at this time for any software where a choice of GPL
+ license versions is made available with the language indicating
+ that GPLv2 or any later version may be used, or where a choice
+ of which version of the GPL is applied is otherwise unspecified.
+
+Files: BUILD/*
+ Docs/*
+ client/*
+ client/echo.c
+ client/get_password.c
+ cmake/*
+ dbug/dbug_add_tags.pl
+ extra/*
+ include/*
+ libmysql/*
+ libmysqld/*
+ libservices/*
+ mysql-test/include/have_perfschema.inc
+ mysql-test/include/have_perfschema.inc
+ mysql-test/lib/mtr_cases.pm
+ mysql-test/lib/mtr_gcov.pl
+ mysql-test/lib/mtr_gprof.pl
+ mysql-test/lib/mtr_io.pl
+ mysql-test/lib/mtr_match.pm
+ mysql-test/lib/mtr_misc.pl
+ mysql-test/lib/mtr_process.pl
+ mysql-test/lib/mtr_report.pm
+ mysql-test/lib/mtr_results.pm
+ mysql-test/lib/mtr_stress.pl
+ mysql-test/lib/mtr_unique.pm
+ mysql-test/lib/My/ConfigFactory.pm
+ mysql-test/lib/My/Config.pm
+ mysql-test/lib/My/CoreDump.pm
+ mysql-test/lib/My/File/*
+ mysql-test/lib/My/Find.pm
+ mysql-test/lib/My/Handles.pm
+ mysql-test/lib/My/Options.pm
+ mysql-test/lib/My/Platform.pm
+ mysql-test/lib/My/SafeProcess/Base.pm
+ mysql-test/lib/My/SafeProcess.pm
+ mysql-test/lib/My/SafeProcess/safe_kill_win.cc
+ mysql-test/lib/My/SafeProcess/safe_process.cc
+ mysql-test/lib/My/SafeProcess/safe_process.pl
+ mysql-test/lib/My/SafeProcess/safe_process_win.cc
+ mysql-test/lib/My/SysInfo.pm
+ mysql-test/lib/My/Test.pm
+ mysql-test/lib/t/*
+ mysql-test/lib/v1/mtr_cases.pl
+ mysql-test/lib/v1/mtr_gcov.pl
+ mysql-test/lib/v1/mtr_gprof.pl
+ mysql-test/lib/v1/mtr_im.pl
+ mysql-test/lib/v1/mtr_io.pl
+ mysql-test/lib/v1/mtr_match.pl
+ mysql-test/lib/v1/mtr_misc.pl
+ mysql-test/lib/v1/mtr_process.pl
+ mysql-test/lib/v1/mtr_report.pl
+ mysql-test/lib/v1/mtr_stress.pl
+ mysql-test/lib/v1/mtr_timer.pl
+ mysql-test/lib/v1/mtr_unique.pl
+ mysql-test/lib/v1/My/*
+ mysql-test/lib/v1/My/*
+ mysql-test/lib/v1/mysql-test-run.pl
+ mysql-test/mysql-stress-test.pl
+ mysql-test/mysql-test-run.pl
+ mysql-test/std_data/*
+ mysql-test/suite/perfschema/include/*
+ mysql-test/suite/perfschema_stress/include/*
+ mysql-test/suite/perfschema_stress/include/*
+ mysys/*
+ packaging/WiX/ca/*
+ plugin/audit_null/*
+ plugin/auth/*
+ plugin/daemon_example/*
+ plugin/fulltext/*
+ plugin/semisync/semisync_slave.cc
+ plugin/semisync/semisync_slave.h
+ scripts/*
+ sql/*
+ sql-common/*
+ storage/*
+ strings/*
+ support-files/config.huge.ini.sh
+ support-files/config.medium.ini.sh
+ support-files/config.small.ini.sh
+ support-files/MacOSX/Description.plist.sh
+ support-files/MacOSX/Info.plist.sh
+ support-files/MacOSX/StartupParameters.plist.sh
+ support-files/MySQL-shared-compat.spec.sh
+ support-files/mysql.spec.sh
+ support-files/ndb-config-2-node.ini.sh
+ tests/*
+ unittest/*
+ vio/*
+Copyright: 1979-2008 MySQL AB
+ 1995-2010 MySQL AB Sun Microsystems Inc
+ 1994-1997,2000-2011 Oracle and/or its affiliates.
+License: GPL-2
+
+Files: storage/innobase/*
+Copyright: 1994-2011 Innobase Oy.
+License: GPL-2
+
+Files: cmd-line-utils/readline/*
+Copyright: 1987-2006 Free Software Foundation Inc
+License: GPL-2+
+
+Files: cmd-line-utils/libedit/*
+Copyright: 1989-1990,1992-1993 The Regents of the University of California.
+License: BSD (3 clause)
+
+Files: cmd-line-utils/libedit/filecomplete.c
+ cmd-line-utils/libedit/filecomplete.h
+ cmd-line-utils/libedit/np/fgetln.c
+ cmd-line-utils/libedit/read.h
+ cmd-line-utils/libedit/readline.c
+ cmd-line-utils/libedit/readline/*
+Copyright: 1997-2001 The NetBSD Foundation Inc
+License: BSD (2 clause)
+ This code is derived from software contributed to The NetBSD Foundation
+ by Jaromir Dolecek.
+ .
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+Files: client/completion_hash.h
+ scripts/mysqlaccess.sh
+ scripts/mysql_fix_extensions.sh
+ scripts/mysql_setpermission.sh
+ sql-bench/*
+ storage/myisam/ftbench/ft-test-run.sh
+ storage/myisam/mi_test_all.sh
+ storage/ndb/test/run-test/atrt-analyze-result.sh
+ storage/ndb/test/run-test/atrt-clear-result.sh
+ storage/ndb/test/run-test/atrt-gather-result.sh
+ storage/ndb/test/run-test/atrt-setup.sh
+ storage/ndb/test/run-test/make-config.sh
+ storage/ndb/test/run-test/make-html-reports.sh
+ storage/ndb/test/run-test/make-index.sh
+ storage/ndb/test/run-test/ndb-autotest.sh
+ strings/strxmov.c
+ strings/strxnmov.c
+ support-files/MacOSX/postflight.sh
+ support-files/MacOSX/preflight.sh
+Copyright: 2000-2009 MySQL AB Sun Microsystems Inc
+ 2000-2007 MySQL AB
+License: LGPL
+
+Files: storage/archive/azio.c
+ storage/archive/azlib.h
+ zlib/*
+Copyright: 1995-2005 Jean-loup Gailly and Mark Adler
+License: zlib/libpng
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+ .
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+ .
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+Files: sql-bench/innotest1.sh
+ sql-bench/innotest1a.sh
+ sql-bench/innotest1b.sh
+ sql-bench/innotest2.sh
+ sql-bench/innotest2a.sh
+ sql-bench/innotest2b.sh
+Copyright: 2000-2002 Innobase Oy & MySQL AB
+Comment: These files fall under the blanket license specified in the file COPYING
+License: GPL-2
+
+Files: storage/innobase/btr/btr0sea.c
+ storage/innobase/include/log0log.h
+ storage/innobase/include/os0sync.h
+ storage/innobase/log/log0log.c
+ storage/innobase/row/row0sel.c
+Copyright: 1995-1997,2009-2010 Innobase Oy.
+ 2008-2009 Google Inc
+License: GPL-2
+
+Files: storage/innobase/btr/btr0cur.c
+ storage/innobase/buf/buf0buf.c
+ storage/innobase/include/sync0rw.h
+ storage/innobase/include/sync0sync.h
+ storage/innobase/sync/*
+Copyright: 1994-2011 Oracle and/or its affiliates.
+ 2008 Google Inc
+License: GPL-2
+
+Files: storage/myisam/rt_index.h
+ storage/myisam/rt_key.c
+ storage/myisam/rt_mbr.c
+ storage/myisam/rt_mbr.h
+ storage/myisam/sp_defs.h
+Copyright: 2000,2002-2006 MySQL AB & Ramil Kalimullin
+License: GPL-2
+
+Files: storage/innobase/include/ut0bh.h
+ storage/innobase/trx/trx0rseg.c
+ storage/innobase/ut/ut0bh.c
+ storage/innobase/ut/ut0ut.c
+Copyright: 1996,2010-2011 Oracle Corpn.
+License: GPL-2
+
+Files: plugin/semisync/semisync.cc
+ plugin/semisync/semisync.h
+ plugin/semisync/semisync_slave_plugin.cc
+Copyright: 2008 MySQL AB
+ 2007 Google Inc
+License: GPL-2
+
+Files: strings/ctype-bin.c
+ strings/ctype-eucjpms.c
+ strings/ctype-ujis.c
+Copyright: 2000,2002,2005-2011 Oracle and/or its affiliates. & tommy@valley.ne.jp
+License: LGPL
+ On Debian and systems the full text of the GNU Library General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/LGPL-2`
+
+Files: scripts/mysqld_safe.sh
+ support-files/mysql-multi.server.sh
+ support-files/mysql.server.sh
+Copyright: 1996 Abandoned TCX DataKonsult AB & Monty Program KB & Detron HB
+License: public-domain
+ This file is public domain and comes with NO WARRANTY of any kind
+
+Files: sql/sql_yacc.cc
+ sql/sql_yacc.h
+Copyright: 1984,1989-1990,2000-2006 Free Software Foundation, Inc.
+License: GPL-2+
+
+Files: storage/innobase/include/pars0grm.h
+ storage/innobase/pars/pars0grm.c
+Copyright: 1995-2009 Innobase Oy.
+ 1984,1989-1990,2000-2004 Free Software Foundation Inc.
+License: GPL-2
+ As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison.
+ .
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free Software
+ Foundation; version 2 of the License.
+ .
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Files: storage/innobase/include/srv0srv.h
+ storage/innobase/srv/srv0start.c
+Copyright: 1995-1996,2010-2011 Innobase Oy.
+ 2008-2009 Google Inc
+ 2009 Percona Inc
+License: GPL-2
+
+Files: plugin/semisync/semisync_master.cc
+ plugin/semisync/semisync_master_plugin.cc
+Copyright: 2008-2009 MySQL AB Sun Microsystems Inc
+ 2007 Google Inc
+License: GPL-2
+
+Files: storage/innobase/include/os0file.h
+ storage/innobase/os/os0file.c
+Copyright: 1995-2010 Innobase Oy.
+ 2009 Percona Inc
+License: GPL-2
+
+Files: include/t_ctype.h
+ strings/t_ctype.h
+Copyright: 2000 MySQL AB
+ 1998 Theppitak Karoonboonyanan
+ 1998-1999 Pruet Boonma
+License: GPL-2
+
+Files: cmd-line-utils/libedit/np/strlcat.c
+ cmd-line-utils/libedit/np/strlcpy.c
+Copyright: 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+License: ISC
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
+ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
+ FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Files: sql/nt_servc.cc
+ sql/nt_servc.h
+Copyright: 1998 Abandoned Irena Pancirov - Irnet Snc
+License: public-domain
+ This file is public domain and comes with NO WARRANTY of any kind
+
+Files: dbug/dbug.c
+ dbug/dbug_long.h
+Copyright: 1987 Abandoned Fred Fish
+License: public-domain
+ N O T I C E
+ .
+ Copyright Abandoned, 1987, Fred Fish
+ .
+ .
+ This previously copyrighted work has been placed into the public
+ domain by the author and may be freely used for any purpose,
+ private or commercial.
+ .
+ Because of the number of inquiries I was receiving about the use
+ of this product in commercially developed works I have decided to
+ simply make it public domain to further its unrestricted use. I
+ specifically would be most happy to see this material become a
+ part of the standard Unix distributions by AT&T and the Berkeley
+ Computer Science Research Group, and a standard part of the GNU
+ system from the Free Software Foundation.
+ .
+ I would appreciate it, as a courtesy, if this notice is left in
+ all copies and derivative works. Thank you.
+ .
+ The author makes no warranty of any kind with respect to this
+ product and explicitly disclaims any implied warranties of mer-
+ chantability or fitness for any particular purpose.
+
+Files: cmd-line-utils/libedit/np/vis.c
+Copyright: 1989-1993 The Regents of the University of California.
+ 1999-2005 The NetBSD Foundation Inc
+License: BSD (3 clause)
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+Files: scripts/dheadgen.pl
+Copyright: 2008-2009 Sun Microsystems Inc
+License: BSD (3 clause)
+
+Files: storage/ndb/test/src/getarg.c
+Copyright: 1997-2000 - Kungliga Tekniska Högskolan
+License: BSD (3 clause)
+
+Files: storage/ndb/test/include/getarg.h
+Copyright: 2003 MySQL AB
+ 1997-1999 Kungliga Tekniska Högskolan
+License: BSD (3 clause) GPL-2
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ .
+ Copyright (c) 1997, 1999 Kungliga Tekniska Högskolan
+ (Royal Institute of Technology, Stockholm, Sweden).
+ All rights reserved.
+ .
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ .
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ .
+ 3. Neither the name of the Institute nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+Files: storage/innobase/handler/ha_innodb.cc
+Copyright: 2008-2009 Google Inc
+ 2009 Percona Inc
+ 2000-2011 MySQL AB & Innobase Oy.
+License: GPL-2
+
+Files: plugin/semisync/semisync_master.h
+Copyright: 2008-2009 MySQL AB Sun Microsystems Inc
+ 2007 Google Inc
+License: GPL-2
+
+Files: storage/innobase/srv/srv0srv.c
+Copyright: 2008-2009 Google Inc
+ 1995-2011 Oracle and/or its affiliates.
+ 2009 Percona Inc
+License: GPL-2
+
+Files: storage/innobase/ut/ut0rbt.c
+Copyright: 2007-2010 Innobase Oy.
+ 2007 Oracle/Innobase Oy
+License: GPL-2
+
+Files: strings/ctype-win1250ch.c
+Copyright: 2002-2010 Oracle and/or its affiliates.
+ 2001 Jan Pazdziora
+License: GPL-2
+
+Files: strings/ctype-tis620.c
+Copyright: 1998 Theppitak Karoonboonyanan <thep@links.nectec.or.th>
+ 1989-1991 Samphan Raruenrom <samphan@thai.com>
+ 2000-2010 Oracle and/or its affiliates.
+ 2003 Sathit Jittanupat
+ 2001 Korakot Chaovavanich <korakot@iname.com> and
+ 1998-1999 Pruet Boonma <pruet@eng.cmu.ac.th>
+License: GPL-2
+
+Files: storage/innobase/handler/ha_innodb.h
+Copyright: 2000-2010 MySQL AB & Innobase Oy.
+License: GPL-2
+
+Files: strings/dtoa.c
+Copyright: 2007-2010 Oracle and/or its affiliates.
+ 1991,2000-2001 Lucent Technologies
+License: LGPL
+
+Files: scripts/mysqldumpslow.sh
+Copyright: 2000-2002,2005-2009 MySQL AB Sun Microsystems Inc
+License: LGPL
+
+Files: libmysqld/lib_sql.cc
+Copyright: 2000 SWsoft company
+License: SWsoft
+ This material is provided "as is", with absolutely no warranty expressed
+ or implied. Any use is at your own risk.
+ .
+ Permission to use or copy this software for any purpose is hereby granted
+ without fee, provided the above notices are retained on all copies.
+ Permission to modify the code and to distribute modified code is granted,
+ provided the above notices are retained, and a notice that the code was
+ modified is included with the above copyright notice.
+
+Files: tests/mail_to_db.pl
+Copyright: 1998 Abandoned TCX DataKonsult AB & Monty Program KB & Detron HB
+License: public-domain
+ This file is public domain and comes with NO WARRANTY of any kind
+
+Files: dbug/dbug_analyze.c
+Copyright: 1987 June Binayak Banerjee
+License: public-domain
+ This program may be freely distributed under the same terms and
+ conditions as Fred Fish's Dbug package.
+
+Files: regex/regexp.c
+Copyright: 1986 University of Toronto
+License: BSD-like
+ Permission is granted to anyone to use this software for any
+ purpose on any computer system, and to redistribute it freely,
+ subject to the following restrictions:
+ .
+ 1. The author is not responsible for the consequences of use of
+ this software, no matter how awful, even if they arise
+ from defects in it.
+ .
+ 2. The origin of this software must not be misrepresented, either
+ by explicit claim or by omission.
+ .
+ 3. Altered versions must be plainly marked as such, and must not
+ be misrepresented as being the original software.
+
+License: GPL-2
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ .
+ On Debian and systems the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2`
+
+License: GPL-2+
+ This file is part of GNU Readline, a library for reading lines
+ of text with interactive input and history editing.
+ .
+ Readline 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, or (at your option) any
+ later version.
+ .
+ Readline 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 Readline; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ .
+ On Debian and systems the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2`
+
+License: LGPL
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+ .
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+ .
+ On Debian and systems the full text of the GNU Library General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/LGPL-2`
+
+License: BSD (3 clause)
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
--- /dev/null
+[DEFAULT]
+# Ignore requirement to use branch name 'master' to make it easier
+# for contributors to work with feature and bugfix branches
+ignore-branch = True
+
+upstream-branch = 5.6
+upstream-tree = branch
+pristine-tar = False
--- /dev/null
+usr/lib/*/libmysqlservices.a
--- /dev/null
+This package was requested in http://bugs.debian.org/508406 because it
+is needed by programs want to include the embedded MySQL into their
+shared libraries.
+
+In order to get the full compile flags, use /bin/mysql_config_pic
--- /dev/null
+usr/bin/mysql_config_pic
+usr/lib/mysql/libmysqld_pic.a
--- /dev/null
+debian/tmp/usr/share/man/man1/mysql_config_pic.1
--- /dev/null
+'''apport package hook for mysql-5.6
+
+(c) 2009 Canonical Ltd.
+Author: Mathias Gug <mathias.gug@canonical.com>
+'''
+
+from __future__ import print_function, unicode_literals
+import os, os.path
+
+from apport.hookutils import *
+
+def _add_my_conf_files(report, filename):
+ key = 'MySQLConf' + path_to_key(filename)
+ report[key] = ""
+ for line in read_file(filename).split('\n'):
+ try:
+ if 'password' in line.split('=')[0]:
+ line = "%s = @@APPORTREPLACED@@" % (line.split('=')[0])
+ report[key] += line + '\n'
+ except IndexError:
+ continue
+
+def add_info(report):
+ attach_conffiles(report, 'mysql-server-5.6', conffiles=None)
+ key = 'Logs' + path_to_key('/var/log/daemon.log')
+ report[key] = ""
+ for line in read_file('/var/log/daemon.log').split('\n'):
+ try:
+ if 'mysqld' in line.split()[4]:
+ report[key] += line + '\n'
+ except IndexError:
+ continue
+ if os.path.exists('/var/log/mysql/error.log'):
+ key = 'Logs' + path_to_key('/var/log/mysql/error.log')
+ report[key] = ""
+ for line in read_file('/var/log/mysql/error.log').split('\n'):
+ report[key] += line + '\n'
+ attach_mac_events(report, '/usr/sbin/mysqld')
+ attach_file(report,'/etc/apparmor.d/usr.sbin.mysqld')
+ _add_my_conf_files(report, '/etc/mysql/my.cnf')
+ for f in os.listdir('/etc/mysql/conf.d'):
+ _add_my_conf_files(report, os.path.join('/etc/mysql/conf.d', f))
+ try:
+ report['MySQLVarLibDirListing'] = str(os.listdir('/var/lib/mysql'))
+ except OSError:
+ report['MySQLVarLibDirListing'] = str(False)
+
+if __name__ == '__main__':
+ report = {}
+ add_info(report)
+ for key in report:
+ print('%s: %s' % (key, report[key].split('\n', 1)[0]))
--- /dev/null
+FAQ:
+
+Q: My <tab> completition is gone, why?
+A: You have "no-auto-rehash" in the "[mysql]" section of /etc/mysql/my.cnf!
--- /dev/null
+usr/bin/
+usr/share/man/man1/
+usr/share/perl5/
--- /dev/null
+debian/additions/innotop/changelog.innotop
+README
--- /dev/null
+debian/tmp/usr/bin/mysqlaccess.conf
--- /dev/null
+# this executable reprsent the embedded mysql server client
+debian/additions/innotop/innotop usr/bin/
+debian/additions/mysqlreport usr/bin/
+usr/bin/innochecksum
+usr/bin/myisam_ftdump
+usr/bin/mysql
+usr/bin/mysql_client_test
+usr/bin/mysql_config_editor
+usr/bin/mysql_find_rows
+usr/bin/mysql_fix_extensions
+usr/bin/mysql_plugin
+usr/bin/mysql_waitpid
+usr/bin/mysqlaccess
+usr/bin/mysqladmin
+usr/bin/mysqlbug
+usr/bin/mysqlcheck
+usr/bin/mysqldump
+usr/bin/mysqldumpslow
+usr/bin/mysqlimport
+usr/bin/mysqlshow
+usr/bin/mysqlslap
--- /dev/null
+usr/bin/mysqlcheck usr/bin/mysqlrepair
+usr/bin/mysqlcheck usr/bin/mysqlanalyze
+usr/bin/mysqlcheck usr/bin/mysqloptimize
+usr/share/man/man1/mysqlcheck.1.gz usr/share/man/man1/mysqlrepair.1.gz
+usr/share/man/man1/mysqlcheck.1.gz usr/share/man/man1/mysqlanalyze.1.gz
+usr/share/man/man1/mysqlcheck.1.gz usr/share/man/man1/mysqloptimize.1.gz
--- /dev/null
+# Will look at man pages later
+# 2012-03-08 periapt
+# still no update
+# 2012-01-09 jesusch-guest
+mysql-client-5.6: binary-without-manpage usr/bin/innochecksum
+# These long lines reproduce actual output and to reformat them
+# would damage the integrity of the man page.
+mysql-client-5.6: manpage-has-errors-from-man usr/share/man/man1/mysqladmin.1.gz 28: warning [p 1, 1.5i]: can't break line
+mysql-client-5.6: manpage-has-errors-from-man usr/share/man/man1/mysqldump.1.gz 2151: warning [p 12, 6.5i, div `3tbd2,1', 0.2i]: can't break line
--- /dev/null
+debian/additions/innotop/innotop.1
+debian/tmp/usr/share/man/man1/mysqlman.1
+debian/additions/mysqlreport.1
+debian/tmp/usr/share/man/man1/mysql_embedded.1
--- /dev/null
+etc/mysql/conf.d/
--- /dev/null
+debian/additions/my.cnf etc/mysql/
+debian/additions/my5.6.cnf etc/mysql/conf.d/
--- /dev/null
+#!/bin/bash
+
+set -e
+
+if [ "$1" = "purge" ]; then
+ rmdir /etc/mysql 2>/dev/null || true
+fi
+
+#DEBHELPER#
--- /dev/null
+The examples directory includes files that might be needed by some
+developers:
+- header files not installed by default
+- the example file udf_example.c
--- /dev/null
+usr/include/
+usr/lib/
--- /dev/null
+sql/udf_example.cc
--- /dev/null
+usr/bin/mysql_config
+usr/include/mysql/*
+usr/lib/*/libmysqlclient.a
+usr/lib/*/libmysqlclient.so
+usr/lib/*/libmysqlclient_r.a
+usr/lib/*/libmysqlclient_r.so
+usr/share/aclocal/mysql.m4
--- /dev/null
+usr/lib/*/libmysqlclient.so.18*
+usr/lib/*/libmysqlclient_r.so.18*
--- /dev/null
+# I take this issue seriously but as per bug #590905
+# it will need playing with in experimental. - periapt
+libmysqlclient18: no-symbols-control-file usr/lib/i386-linux-gnu/libmysqlclient.so.18.1.0
--- /dev/null
+mysql-dfsg-5.1 (5.1.36-1) unstable; urgency=low
+
+ * Please read http://dev.mysql.com/doc/refman/5.1/en/upgrading-from-5-0.html
+ * Make sure to do a REPAIR TABLE on all tables that use UTF-8 and have a
+ FULLTEXT index.
+
+ -- Christian Hammers <ch@debian.org> Sat, 4 Jul 2009 02:31:21 +0200
+
+mysql-dfsg-5.0 (5.1.14beta-2) unstable; urgency=low
+
+ * The BerkeleyDB Storage Engine is no longer supported. If the options
+ have-bdb or skip-bdb are found, MySQL will not start. If you have BDB
+ tables, you should change them to use another storage engine before
+ upgrading to 5.1.
+
+ -- Monty Taylor <mordred@inaugust.com> Thu, 18 Jan 2007 12:28:21 -0800
+
+mysql-dfsg-5.0 (5.0.45-2) unstable; urgency=low
+
+ * Binary logging is now disabled by default. If you really need it (e.g. on
+ a replication master), remove the comment from the log_bin line in my.cnf.
+
+ -- Norbert Tretkowski <norbert@tretkowski.de> Sat, 10 Nov 2007 16:26:35 +0100
+
+mysql-dfsg-5.0 (5.0.18-9) unstable; urgency=low
+
+ * Rotation of the binary logs is now configured in /etc/mysql/my.cnf with
+ "expire-logs-days" which defaults to 20 days. The old file
+ /etc/mysql/debian-log-rotate.conf should be removed together with
+ /etc/cron.daily/mysql-server after this value has been adjusted. Note that
+ the old variable defined the number of files whereas the new one defines
+ a time span in days.
+
+ -- Christian Hammers <ch@debian.org> Tue, 24 Jan 2006 22:18:21 +0100
--- /dev/null
+* MYSQL WON'T START OR STOP?:
+=============================
+You may never ever delete the special mysql user "debian-sys-maint". This
+user together with the credentials in /etc/mysql/debian.cnf are used by the
+init scripts to stop the server as they would require knowledge of the mysql
+root users password else.
+So in most of the times you can fix the situation by making sure that the
+debian.cnf file contains the right password, e.g. by setting a new one
+(remember to do a "flush privileges" then).
+
+* WHAT TO DO AFTER UPGRADES:
+============================
+The privilege tables are automatically updated so all there is left is read
+the changelogs on dev.mysql.com to see if any changes affect custom apps.
+
+* WHAT TO DO AFTER INSTALLATION:
+================================
+The MySQL manual describes certain steps to do at this stage in a separate
+chapter. They are not necessary as the Debian packages does them
+automatically.
+
+The only thing that is left over for the admin is
+ - setting the passwords
+ - creating new users and databases
+ - read the rest of this text
+
+* DOWNGRADING TO 4.0 or 4.1:
+============================
+Unsupported. Period.
+But if you do and get problems or make interesting experiences, mail me, it
+might help others.
+Ok, if you really want, I would recommend to "mysqldump --opt" all tables,
+then purge 4.1, delete /var/lib/mysql, install 4.0 and insert the dumps. Be
+carefully, though, with the "mysql" table, you might not simply overwrite that
+one as the password for the mysql "debian-sys-maint" user is stored in
+/etc/mysql/debian.cnf and needed by /etc/init.d/ to start mysql and check if
+it's alive.
+
+* SOME APPLICATION CAN NO LONGER CONNECT:
+=========================================
+This application is probably linked against libmysqlclient12 or below and
+somebody has created a mysql user with new-style passwords.
+The old_passwords=1 option in /etc/mysql/my.cnf might help. If not the
+application that inserted the user has to be changed or the application that
+tries to connect updated to libmysqlclient14 or -15.
+
+* NETWORKING:
+=============
+For security reasons, the Debian package has enabled networking only on the
+loop-back device using "bind-address" in /etc/mysql/my.cnf. Check with
+"netstat -tlnp" where it is listening. If your connection is aborted
+immediately see if "mysqld: all" or similar is in /etc/hosts.allow and read
+hosts_access(5).
+
+* WHERE IS THE DOCUMENTATION?:
+==============================
+Unfortunately due to licensing restrictions, debian currently not able
+to provide the mysql-doc package in any format. For the most up to date
+documentation, please go to http://dev.mysql.com/doc.
+
+* PASSWORDS:
+============
+It is strongly recommended to set a password for the mysql root user (which
+ /usr/bin/mysql -u root -D mysql -e "update user set password=password('new-password') where user='root'"
+ /usr/bin/mysql -u root -e "flush privileges"
+If you already had a password set add "-p" before "-u" to the lines above.
+
+
+If you are tired to type the password in every time or want to automate your
+scripts you can store it in the file $HOME/.my.cnf. It should be chmod 0600
+(-rw------- username username .my.cnf) to ensure that nobody else can read
+it. Every other configuration parameter can be stored there, too. You will
+find an example below and more information in the MySQL manual in
+/usr/share/doc/mysql-doc or www.mysql.com.
+
+ATTENTION: It is necessary, that a .my.cnf from root always contains a "user"
+line wherever there is a "password" line, else, the Debian maintenance
+scripts, that use /etc/mysql/debian.cnf, will use the username
+"debian-sys-maint" but the password that is in root's .my.cnf. Also note,
+that every change you make in the /root/.my.cnf will affect the mysql cron
+script, too.
+
+ # an example of $HOME/.my.cnf
+ [client]
+ user = your-mysql-username
+ password = enter-your-good-new-password-here
+
+* BIG_ROWS FOR EVEN MORE ROWS IN A TABLE:
+=========================================
+If you ever run out of rows in a table there is the possibility of building
+the package with "-DBIG_ROWS" which, according to a MySQL employee on
+packagers@lists.mysql.com should lead to a 64bit row index (I guess > 2^32
+rows) but also to an approx. 5% performance loss.
+
+* BerkeleyDB Storage Engine
+===========================
+Support for BerkeleyDB has been removed in 5.1, and consequently both the
+have-bdb and skip-bdb configuration options will cause the server to fail.
+Removing the options from /etc/mysql/my.cnf will fix this problem.
+
+* FURTHER NOTES ON REPLICATION
+===============================
+If the MySQL server is acting as a replication slave, you should not
+set --tmpdir to point to a directory on a memory-based filesystem or to
+a directory that is cleared when the server host restarts. A replication
+slave needs some of its temporary files to survive a machine restart so
+that it can replicate temporary tables or LOAD DATA INFILE operations. If
+files in the temporary file directory are lost when the server restarts,
+replication fails.
--- /dev/null
+#!/bin/bash
+
+set -e
+
+. /usr/share/debconf/confmodule
+
+if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi
+${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2 }
+
+CNF=/etc/mysql/my.cnf
+
+# Beware that there are two ypwhich one of them needs the 2>/dev/null!
+if test -n "`which ypwhich 2>/dev/null`" && ypwhich >/dev/null 2>&1; then
+ db_input high mysql-server-5.6/nis_warning || true
+ db_go
+fi
+
+# only ask this question on fresh installs, during "reconfiguration" and when
+# not upgrading from an existing 5.0 installation.
+# there is also an additional check for empty root passwords in the
+# postinst script when the tools are available for us to use.
+if [ "$1" = "configure" ] && ([ -z "$2" ] && [ ! -e "/var/lib/mysql/debian-5.0.flag" ] ) || [ "$1" = "reconfigure" ]; then
+ while :; do
+ RET=""
+ db_input high mysql-server/root_password || true
+ db_go
+ db_get mysql-server/root_password
+ # if password isn't empty we ask for password verification
+ if [ -z "$RET" ]; then
+ db_fset mysql-server/root_password seen false
+ db_fset mysql-server/root_password_again seen false
+ break
+ fi
+ ROOT_PW="$RET"
+ db_input high mysql-server/root_password_again || true
+ db_go
+ db_get mysql-server/root_password_again
+ if [ "$RET" == "$ROOT_PW" ]; then
+ ROOT_PW=''
+ break
+ fi
+ db_fset mysql-server/password_mismatch seen false
+ db_input critical mysql-server/password_mismatch
+ db_set mysql-server/root_password ""
+ db_set mysql-server/root_password_again ""
+ db_go
+ done
+fi
--- /dev/null
+etc/init.d
+etc/logrotate.d
+etc/mysql/conf.d
+usr/bin
+usr/share/mysql
+var/lib/mysql-upgrade
+usr/sbin
+usr/share/man/man8
--- /dev/null
+debian/tmp/usr/share/mysql/wsrep.cnf
+debian/tmp/usr/share/mysql/magic
+debian/tmp/usr/lib/mysql/plugin/daemon_example.ini
--- /dev/null
+#usr/share/mysql/config.huge.ini
+#usr/share/mysql/config.medium.ini
+#usr/share/mysql/config.small.ini
+#usr/share/mysql/ndb-config-2-node.ini
+debian/additions/mysqld_safe_syslog.cnf etc/mysql/conf.d/
+etc/apparmor.d/usr.sbin.mysqld
+etc/init.d/mysql
+etc/mysql/debian-start
+usr/bin/msql2mysql
+usr/bin/my_print_defaults
+usr/bin/myisamchk
+usr/bin/myisamlog
+usr/bin/myisampack
+usr/bin/mysql_convert_table_format
+usr/bin/mysql_install_db
+usr/bin/mysql_secure_installation
+usr/bin/mysql_setpermission
+usr/bin/mysql_tzinfo_to_sql
+usr/bin/mysql_upgrade
+usr/bin/mysql_zap
+usr/bin/mysqlbinlog
+usr/bin/mysqld_multi
+usr/bin/mysqld_safe
+usr/bin/mysqlhotcopy
+usr/bin/mysqltest
+usr/bin/perror
+usr/bin/replace
+usr/bin/resolve_stack_dump
+usr/bin/resolveip
+usr/bin/wsrep_sst_common
+usr/bin/wsrep_sst_mysqldump
+usr/bin/wsrep_sst_rsync
+usr/bin/wsrep_sst_xtrabackup
+usr/lib/mysql/plugin/*.so
+usr/sbin/mysqld
+usr/sbin/mysqld
+usr/share/doc/mysql-server-5.6/
+usr/share/mysql/bulgarian
+usr/share/mysql/charsets
+usr/share/mysql/charsets
+usr/share/mysql/czech
+usr/share/mysql/czech
+usr/share/mysql/danish
+usr/share/mysql/danish
+usr/share/mysql/debian-start.inc.sh
+usr/share/mysql/debian_create_root_user.sql
+usr/share/mysql/dictionary.txt
+usr/share/mysql/dutch
+usr/share/mysql/dutch
+usr/share/mysql/echo_stderr
+usr/share/mysql/english
+usr/share/mysql/english
+usr/share/mysql/errmsg-utf8.txt
+usr/share/mysql/estonian
+usr/share/mysql/estonian
+usr/share/mysql/fill_help_tables.sql
+usr/share/mysql/french
+usr/share/mysql/french
+usr/share/mysql/german
+usr/share/mysql/german
+usr/share/mysql/greek
+usr/share/mysql/greek
+usr/share/mysql/hungarian
+usr/share/mysql/hungarian
+usr/share/mysql/innodb_memcached_config.sql
+usr/share/mysql/italian
+usr/share/mysql/italian
+usr/share/mysql/japanese
+usr/share/mysql/japanese
+usr/share/mysql/korean
+usr/share/mysql/korean
+usr/share/mysql/mysql_security_commands.sql
+usr/share/mysql/mysql_system_tables.sql
+usr/share/mysql/mysql_system_tables_data.sql
+usr/share/mysql/mysql_test_data_timezone.sql
+usr/share/mysql/mysqld_multi.server
+usr/share/mysql/norwegian
+usr/share/mysql/norwegian
+usr/share/mysql/norwegian-ny
+usr/share/mysql/norwegian-ny
+usr/share/mysql/polish
+usr/share/mysql/polish
+usr/share/mysql/portuguese
+usr/share/mysql/portuguese
+usr/share/mysql/romanian
+usr/share/mysql/romanian
+usr/share/mysql/russian
+usr/share/mysql/russian
+usr/share/mysql/serbian
+usr/share/mysql/serbian
+usr/share/mysql/slovak
+usr/share/mysql/slovak
+usr/share/mysql/spanish
+usr/share/mysql/spanish
+usr/share/mysql/swedish
+usr/share/mysql/swedish
+usr/share/mysql/ukrainian
+usr/share/mysql/ukrainian
+usr/share/mysql/wsrep.cnf
+usr/share/mysql/my-default.cnf
--- /dev/null
+# These long lines reproduce actual output and to reformat them
+# would damage the integrity of the man page.
+# mysql-server-5.6: manpage-has-errors-from-man usr/share/man/man1/mysqlbinlog.1.gz 1405: warning [p 9, 7.5i, div `3tbd3,2', 0.8i]: can't break line
+# These are random occurrences of a pseudo word in a binary.
+mysql-server-5.6: spelling-error-in-binary usr/sbin/mysqld yuR your
--- /dev/null
+/etc/init.d/mysql\[[0-9]+\]: Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists\!$
+/etc/init.d/mysql\[[0-9]+\]: '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$
+/etc/mysql/debian-start\[[0-9]+\]: Checking for crashed MySQL tables\.$
+mysqld\[[0-9]+\]: $
+mysqld\[[0-9]+\]: Version: .* socket: '/var/run/mysqld/mysqld.sock' port: 3306$
+mysqld\[[0-9]+\]: Warning: Ignoring user change to 'mysql' because the user was set to 'mysql' earlier on the command line$
+mysqld_safe\[[0-9]+\]: started$
+usermod\[[0-9]+\]: change user `mysql' GID from `([0-9]+)' to `\1'$
+usermod\[[0-9]+\]: change user `mysql' shell from `/bin/false' to `/bin/false'$
--- /dev/null
+/etc/init.d/mysql\[[0-9]+\]: [0-9]+ processes alive and '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$
+/etc/init.d/mysql\[[0-9]+\]: Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists\!$
+/etc/init.d/mysql\[[0-9]+\]: '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$
+/etc/mysql/debian-start\[[0-9]+\]: Checking for crashed MySQL tables\.$
+mysqld\[[0-9]+\]: ?$
+mysqld\[[0-9]+\]: .*InnoDB: Shutdown completed
+mysqld\[[0-9]+\]: .*InnoDB: Started;
+mysqld\[[0-9]+\]: .*InnoDB: Starting shutdown\.\.\.$
+mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Normal shutdown$
+mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: ready for connections\.$
+mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Shutdown complete$
+mysqld\[[0-9]+\]: /usr/sbin/mysqld: ready for connections\.$
+mysqld\[[0-9]+\]: .*/usr/sbin/mysqld: Shutdown Complete$
+mysqld\[[0-9]+\]: Version: .* socket
+mysqld\[[0-9]+\]: Warning: Ignoring user change to 'mysql' because the user was set to 'mysql' earlier on the command line$
+mysqld_safe\[[0-9]+\]: ?$
+mysqld_safe\[[0-9]+\]: able to use the new GRANT command!$
+mysqld_safe\[[0-9]+\]: ended$
+mysqld_safe\[[0-9]+\]: http://www.mysql.com$
+mysqld_safe\[[0-9]+\]: NOTE: If you are upgrading from a MySQL <= 3.22.10 you should run$
+mysqld_safe\[[0-9]+\]: PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !$
+mysqld_safe\[[0-9]+\]: Please report any problems with the /usr/bin/mysqlbug script!$
+mysqld_safe\[[0-9]+\]: See the manual for more instructions.$
+mysqld_safe\[[0-9]+\]: started$
+mysqld_safe\[[0-9]+\]: Support MySQL by buying support/licenses at https://order.mysql.com$
+mysqld_safe\[[0-9]+\]: The latest information about MySQL is available on the web at$
+mysqld_safe\[[0-9]+\]: the /usr/bin/mysql_fix_privilege_tables. Otherwise you will not be$
+mysqld_safe\[[0-9]+\]: To do so, start the server, then issue the following commands:$
+mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root -h app109 password 'new-password'$
+mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root password 'new-password'$
+usermod\[[0-9]+\]: change user `mysql' GID from `([0-9]+)' to `\1'$
+usermod\[[0-9]+\]: change user `mysql' shell from `/bin/false' to `/bin/false'$
--- /dev/null
+/etc/init.d/mysql\[[0-9]+\]: [0-9]+ processes alive and '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$
+/etc/init.d/mysql\[[0-9]+\]: Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists\!$
+/etc/init.d/mysql\[[0-9]+\]: '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$
+/etc/mysql/debian-start\[[0-9]+\]: Checking for crashed MySQL tables\.$
+mysqld\[[0-9]+\]: ?$
+mysqld\[[0-9]+\]: .*InnoDB: Shutdown completed
+mysqld\[[0-9]+\]: .*InnoDB: Started;
+mysqld\[[0-9]+\]: .*InnoDB: Starting shutdown\.\.\.$
+mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Normal shutdown$
+mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: ready for connections\.$
+mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Shutdown complete$
+mysqld\[[0-9]+\]: /usr/sbin/mysqld: ready for connections\.$
+mysqld\[[0-9]+\]: .*/usr/sbin/mysqld: Shutdown Complete$
+mysqld\[[0-9]+\]: Version: .* socket
+mysqld\[[0-9]+\]: Warning: Ignoring user change to 'mysql' because the user was set to 'mysql' earlier on the command line$
+mysqld_safe\[[0-9]+\]: ?$
+mysqld_safe\[[0-9]+\]: able to use the new GRANT command!$
+mysqld_safe\[[0-9]+\]: ended$
+mysqld_safe\[[0-9]+\]: http://www.mysql.com$
+mysqld_safe\[[0-9]+\]: NOTE: If you are upgrading from a MySQL <= 3.22.10 you should run$
+mysqld_safe\[[0-9]+\]: PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !$
+mysqld_safe\[[0-9]+\]: Please report any problems with the /usr/bin/mysqlbug script!$
+mysqld_safe\[[0-9]+\]: See the manual for more instructions.$
+mysqld_safe\[[0-9]+\]: started$
+mysqld_safe\[[0-9]+\]: Support MySQL by buying support/licenses at https://order.mysql.com$
+mysqld_safe\[[0-9]+\]: The latest information about MySQL is available on the web at$
+mysqld_safe\[[0-9]+\]: the /usr/bin/mysql_fix_privilege_tables. Otherwise you will not be$
+mysqld_safe\[[0-9]+\]: To do so, start the server, then issue the following commands:$
+mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root -h app109 password 'new-password'$
+mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root password 'new-password'$
+usermod\[[0-9]+\]: change user `mysql' GID from `([0-9]+)' to `\1'$
+usermod\[[0-9]+\]: change user `mysql' shell from `/bin/false' to `/bin/false'$
--- /dev/null
+# - I put everything in one block and added sharedscripts, so that mysql gets
+# flush-logs'd only once.
+# Else the binary logs would automatically increase by n times every day.
+# - The error log is obsolete, messages go to syslog now.
+/var/log/mysql.log /var/log/mysql/*log {
+ daily
+ rotate 7
+ missingok
+ create 640 mysql adm
+ compress
+ sharedscripts
+ postrotate
+ test -x /usr/bin/mysqladmin || exit 0
+ # If this fails, check debian.conf!
+ MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
+ if [ -z "`$MYADMIN ping 2>/dev/null`" ]; then
+ # Really no mysqld or rather a missing debian-sys-maint user?
+ # If this occurs and is not a error please report a bug.
+ #if ps cax | grep -q mysqld; then
+ if killall -q -s0 -umysql mysqld; then
+ exit 1
+ fi
+ else
+ $MYADMIN flush-logs
+ fi
+ endscript
+}
--- /dev/null
+description "MySQL 5.6 Server"
+author "Mario Limonciello <superm1@ubuntu.com>"
+
+start on runlevel [2345]
+stop on starting rc RUNLEVEL=[016]
+
+respawn
+respawn limit 2 5
+
+env HOME=/etc/mysql
+umask 007
+
+# The default of 5 seconds is too low for mysql which needs to flush buffers
+kill timeout 300
+
+pre-start script
+ ## Fetch a particular option from mysql's invocation.
+ # Usage: void mysqld_get_param option
+ mysqld_get_param() {
+ /usr/sbin/mysqld --print-defaults \
+ | tr " " "\n" \
+ | grep -- "--$1" \
+ | tail -n 1 \
+ | cut -d= -f2
+ }
+
+ # priority can be overriden and "-s" adds output to stderr
+ ERR_LOGGER="logger -p daemon.err -t /etc/init/mysql.conf -i"
+
+ #Sanity checks
+ [ -r $HOME/my.cnf ]
+ [ -d /var/run/mysqld ] || install -m 755 -o mysql -g root -d /var/run/mysqld
+ /lib/init/apparmor-profile-load usr.sbin.mysqld
+
+ # check for diskspace shortage
+ datadir=`mysqld_get_param datadir`
+ BLOCKSIZE=`LC_ALL=C df --portability $datadir/. | tail -n 1 | awk '{print $4}'`
+ if [ $BLOCKSIZE -le 4096 ] ; then
+ echo "$0: ERROR: The partition with $datadir is too full!" >&2
+ echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER
+ exit 1
+ fi
+end script
+
+exec /usr/sbin/mysqld
+
+post-start script
+ for i in `seq 1 30` ; do
+ /usr/bin/mysqladmin --defaults-file="${HOME}"/debian.cnf ping && {
+ exec "${HOME}"/debian-start
+ # should not reach this line
+ exit 2
+ }
+ statusnow=`status`
+ if echo $statusnow | grep -q 'stop/' ; then
+ exit 0
+ elif echo $statusnow | grep -q 'respawn/' ; then
+ exit 1
+ fi
+ sleep 1
+ done
+ exit 1
+end script
--- /dev/null
+#!/bin/bash
+
+set -e
+
+. /usr/share/debconf/confmodule
+
+if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi
+${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2 }
+
+export PATH=$PATH:/sbin:/usr/sbin:/bin:/usr/bin
+
+# This command can be used as pipe to syslog. With "-s" it also logs to stderr.
+ERR_LOGGER="logger -p daemon.err -t mysqld_safe -i"
+
+invoke() {
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d mysql $1
+ else
+ /etc/init.d/mysql $1
+ fi
+}
+
+MYSQL_BOOTSTRAP="/usr/sbin/mysqld --bootstrap --user=mysql --skip-grant-tables"
+
+test_mysql_access() {
+ mysql --no-defaults -u root -h localhost </dev/null >/dev/null 2>&1
+}
+
+# call with $1 = "online" to connect to the server, otherwise it bootstraps
+set_mysql_rootpw() {
+ # forget we ever saw the password. don't use reset to keep the seen status
+ db_set mysql-server/root_password ""
+ db_set mysql-server/root_password_again ""
+
+ tfile=`mktemp`
+ if [ ! -f "$tfile" ]; then
+ return 1
+ fi
+
+ # this avoids us having to call "test" or "[" on $rootpw
+ cat << EOF > $tfile
+USE mysql;
+UPDATE user SET password=PASSWORD("$rootpw") WHERE user='root';
+FLUSH PRIVILEGES;
+EOF
+ if grep -q 'PASSWORD("")' $tfile; then
+ retval=0
+ elif [ "$1" = "online" ]; then
+ mysql --no-defaults -u root -h localhost <$tfile >/dev/null
+ retval=$?
+ else
+ $MYSQL_BOOTSTRAP <$tfile
+ retval=$?
+ fi
+ rm -f $tfile
+ return $retval
+}
+
+# This is necessary because mysql_install_db removes the pid file in /var/run
+# and because changed configuration options should take effect immediately.
+# In case the server wasn't running at all it should be ok if the stop
+# script fails. I can't tell at this point because of the cleaned /var/run.
+set +e; invoke stop; set -e
+
+case "$1" in
+ configure)
+ mysql_datadir=/usr/share/mysql
+ mysql_statedir=/var/lib/mysql
+ mysql_rundir=/var/run/mysqld
+ mysql_logdir=/var/log/mysql
+ mysql_cfgdir=/etc/mysql
+ mysql_upgradedir=/var/lib/mysql-upgrade
+ mysql_filesdir=/var/lib/mysql-files
+
+ # Ensure the existence and right permissions for the database and
+ # log files.
+
+ # MySQL 5.6.34 needs this
+
+ if [ ! -d ${mysql_filesdir} -a ! -L ${mysql_filesdir} ];
+ then
+ mkdir ${mysql_filesdir}
+ chown mysql:mysql ${mysql_filesdir}
+ chmod 770 ${mysql_filesdir}
+ fi
+
+ if [ ! -d "$mysql_statedir" -a ! -L "$mysql_statedir" ]; then mkdir "$mysql_statedir"; fi
+ if [ ! -d "$mysql_statedir/mysql" -a ! -L "$mysql_statedir/mysql" ]; then mkdir "$mysql_statedir/mysql"; fi
+ if [ ! -d "$mysql_logdir" -a ! -L "$mysql_logdir" ]; then mkdir "$mysql_logdir"; fi
+ # When creating an ext3 jounal on an already mounted filesystem like e.g.
+ # /var/lib/mysql, you get a .journal file that is not modifyable by chown.
+ # The mysql_datadir must not be writable by the mysql user under any
+ # circumstances as it contains scripts that are executed by root.
+ set +e
+ chown -R 0:0 $mysql_datadir
+ chown -R mysql $mysql_statedir
+ chmod 700 $mysql_statedir $mysql_statedir/mysql
+ if [ ! -d "$mysql_rundir" ]; then mkdir "$mysql_rundir"; fi
+ chown -R mysql $mysql_rundir
+ touch $mysql_logdir/error.log
+ chown -R mysql:adm $mysql_logdir
+ chmod 0750 $mysql_logdir
+ chmod 0640 $mysql_logdir/error.log
+ set -e
+
+ # This is important to avoid dataloss when there is a removed
+ # mysql-server version from Woody lying around which used the same
+ # data directory and then somewhen gets purged by the admin.
+ db_set mysql-server/postrm_remove_database false || true
+
+ # To avoid downgrades.
+ touch $mysql_statedir/debian-5.6.flag
+
+ # initiate databases. Output is not allowed by debconf :-(
+ # Debian: can safely run on upgrades with existing databases
+
+ set +e
+ tfile_select_db=`mktemp`
+ echo "USE mysql;" > $tfile_select_db
+ MYSQL_EXTRA_OPTS=" --default-storage-engine=myisam --lc-messages-dir=/usr/share/mysql/english/.."
+ MYSQL_CMDLINE=$MYSQL_BOOTSTRAP$MYSQL_EXTRA_OPTS
+ cat $tfile_select_db $mysql_datadir/mysql_system_tables.sql $mysql_datadir/debian_create_root_user.sql $mysql_datadir/fill_help_tables.sql | $MYSQL_CMDLINE 2>&1 | $ERR_LOGGER
+ rm $tfile_select_db
+ set -e
+
+ ## On every reconfiguration the maintenance user is recreated.
+ #
+ # - It is easier to regenerate the password every time but as people
+ # use fancy rsync scripts and file alteration monitors, the existing
+ # password is used and existing files not touched.
+ # - The mysqld statement is like that in mysql_install_db because the
+ # server is not already running. This has some implications:
+ # - The amount of newlines and semicolons in the query is important!
+ # - GRANT is not possible with --skip-grant-tables and "INSERT
+ # (user,host..) VALUES" is not --ansi compliant
+ # - The echo is just for readability. ash's buildin has no "-e" so use /bin/echo.
+ # - The Super_priv, Show_db_priv, Create_tmp_table_priv and Lock_tables_priv
+ # may not be present as old Woody 3.23 databases did not have it and the
+ # admin might not already have run mysql_upgrade which adds them.
+ # As the binlog cron scripts to need at least the Super_priv, I do first
+ # the old query which always succeeds and then the new which may or may not.
+
+ # recreate the credentials file if not present or without mysql_upgrade stanza
+ dc=$mysql_cfgdir/debian.cnf;
+ if [ -e "$dc" -a -n "`fgrep mysql_upgrade $dc 2>/dev/null`" ]; then
+ pass="`sed -n 's/^[ ]*password *= *// p' $dc | head -n 1`"
+ else
+ pass=`perl -e 'print map{("a".."z","A".."Z",0..9)[int(rand(62))]}(1..16)'`;
+ if [ ! -d "$mysql_cfgdir" ]; then install -o 0 -g 0 -m 0755 -d $mysql_cfgdir; fi
+ umask 066
+ cat /dev/null > $dc
+ umask 022
+ echo "# Automatically generated for Debian scripts. DO NOT TOUCH!" >>$dc
+ echo "[client]" >>$dc
+ echo "host = localhost" >>$dc
+ echo "user = debian-sys-maint" >>$dc
+ echo "password = $pass" >>$dc
+ echo "socket = $mysql_rundir/mysqld.sock" >>$dc
+ echo "[mysql_upgrade]" >>$dc
+ echo "host = localhost" >>$dc
+ echo "user = debian-sys-maint" >>$dc
+ echo "password = $pass" >>$dc
+ echo "socket = $mysql_rundir/mysqld.sock" >>$dc
+ echo "basedir = /usr" >>$dc
+ fi
+ # If this dir chmod go+w then the admin did it. But this file should not.
+ chown 0:0 $dc
+ chmod 0600 $dc
+
+ replace_query=`echo -e \
+ "USE mysql;\n" \
+ "REPLACE INTO user SET " \
+ " host='localhost', user='debian-sys-maint', password=password('$pass'), " \
+ " Select_priv='Y', Insert_priv='Y', Update_priv='Y', Delete_priv='Y', " \
+ " Create_priv='Y', Drop_priv='Y', Reload_priv='Y', Shutdown_priv='Y', " \
+ " Process_priv='Y', File_priv='Y', Grant_priv='Y', References_priv='Y', " \
+ " Index_priv='Y', Alter_priv='Y', Super_priv='Y', Show_db_priv='Y', "\
+ " Create_tmp_table_priv='Y', Lock_tables_priv='Y', Execute_priv='Y', "\
+ " Repl_slave_priv='Y', Repl_client_priv='Y', Create_view_priv='Y', "\
+ " Show_view_priv='Y', Create_routine_priv='Y', Alter_routine_priv='Y', "\
+ " Create_user_priv='Y', Event_priv='Y', Trigger_priv='Y'; "`;
+ # Engines supported by etch should be installed per default. The query sequence is supposed
+ # to be aborted if the CREATE TABLE fails due to an already existent table in which case the
+ # admin might already have chosen to remove one or more plugins. Newlines are necessary.
+ install_plugins=`echo -e \
+ "USE mysql;\n" \
+ "CREATE TABLE IF NOT EXISTS plugin (name char(64) COLLATE utf8_bin NOT NULL DEFAULT '', " \
+ " dl char(128) COLLATE utf8_bin NOT NULL DEFAULT '', " \
+ " PRIMARY KEY (name)) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='MySQL plugins';\n" \
+ "INSERT INTO plugin VALUES ('innodb', 'ha_innodb.so');\n" \
+ "INSERT INTO plugin VALUES ('federated', 'ha_federated.so');\n" \
+ "INSERT INTO plugin VALUES ('blackhole', 'ha_blackhole.so');\n" \
+ "INSERT INTO plugin VALUES ('archive', 'ha_archive.so');" `
+
+ db_get mysql-server/root_password && rootpw="$RET"
+ if ! set_mysql_rootpw; then
+ password_error="yes"
+ fi
+
+ echo "$replace_query" | $MYSQL_BOOTSTRAP 2>&1 | $ERR_LOGGER
+ set +e
+ echo "$install_plugins" | $MYSQL_BOOTSTRAP 2>&1 | $ERR_LOGGER
+ set -e
+ ;;
+
+ abort-upgrade|abort-remove|abort-configure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument '$1'" 1>&2
+ exit 1
+ ;;
+esac
+
+# here we check to see if we can connect as root without a password
+# this should catch upgrades from previous versions where the root
+# password wasn't set. if there is a password, or if the connection
+# fails for any other reason, nothing happens.
+if [ "$1" = "configure" ]; then
+ if test_mysql_access; then
+ db_input medium mysql-server/root_password || true
+ db_go
+ db_get mysql-server/root_password && rootpw="$RET"
+
+ if ! set_mysql_rootpw "online"; then
+ password_error="yes"
+ fi
+ fi
+
+ if [ "$password_error" = "yes" ]; then
+ db_input high mysql-server/error_setting_password || true
+ db_go
+ fi
+
+fi
+
+db_stop # in case invoke failes
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
+#!/bin/bash
+
+set -e
+
+# It is possible that Debconf has already been removed, too.
+if [ -f /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+fi
+
+if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi
+${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2 }
+
+MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
+
+# Try to stop the server in a sane way. If it does not success let the admin
+# do it himself. No database directories should be removed while the server
+# is running!
+stop_server() {
+ set +e
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d mysql stop
+ else
+ /etc/init.d/mysql stop
+ fi
+ errno=$?
+ set -e
+
+ if [ "$?" != 0 ]; then
+ echo "Trying to stop the MySQL server resulted in exitcode $?." 1>&2
+ echo "Stop it yourself and try again!" 1>&2
+ exit 1
+ fi
+}
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ if [ -n "`$MYADMIN ping 2>/dev/null`" ]; then
+ stop_server
+ sleep 2
+ fi
+ ;;
+ *)
+ echo "postrm called with unknown argument '$1'" 1>&2
+ exit 1
+ ;;
+esac
+
+#
+# - Do NOT purge logs or data if another mysql-sever* package is installed (#307473)
+# - Remove the mysql user only after all his owned files are purged.
+#
+if [ "$1" = "purge" -a ! \( -x /usr/sbin/mysqld -o -L /usr/sbin/mysqld \) ]; then
+ # we remove the mysql user only after all his owned files are purged
+ rm -f /var/log/mysql.{log,err}{,.0,.[1234567].gz}
+ rm -rf /var/log/mysql
+
+ db_input high mysql-server-5.6/postrm_remove_databases || true
+ db_go || true
+ db_get mysql-server-5.6/postrm_remove_databases || true
+ if [ "$RET" = "true" ]; then
+ # never remove the debian.cnf when the databases are still existing
+ # else we ran into big trouble on the next install!
+ rm -f /etc/mysql/debian.cnf
+ rm -rf /var/lib/mysql
+ rm -rf /var/run/mysqld
+ userdel mysql || true
+ fi
+fi
+
+# (normally) Automatically added by dh_installdebconf
+if [ "$1" = purge ] && [ -e /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+ db_purge
+fi
+# (normally) End automatically added section
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
+#!/bin/bash
+#
+# summary of how this script can be called:
+# * <new-preinst> install
+# * <new-preinst> install <old-version>
+# * <new-preinst> upgrade <old-version>
+# * <old-preinst> abort-upgrade <new-version>
+#
+
+set -e
+
+. /usr/share/debconf/confmodule
+
+if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi
+${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2 }
+
+export PATH=$PATH:/sbin:/usr/sbin:/bin:/usr/bin
+MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
+DATADIR=/var/lib/mysql
+LOGDIR=/var/log/mysql
+UPGRADEDIR=/var/lib/mysql-upgrade
+
+# Try to stop the server in a sane way. If it does not success let the admin
+# do it himself. No database directories should be removed while the server
+# is running! Another mysqld in e.g. a different chroot is fine for us.
+stop_server() {
+ if [ ! -x /etc/init.d/mysql ]; then return; fi
+
+ set +e
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ cmd="invoke-rc.d mysql stop"
+ else
+ cmd="/etc/init.d/mysql stop"
+ fi
+ $cmd
+ errno=$?
+ set -e
+
+ # 0=ok, 100=no init script (fresh install)
+ if [ "$errno" != 0 -a "$errno" != 100 ]; then
+ echo "${cmd/ */} returned $errno" 1>&2
+ echo "There is a MySQL server running, but we failed in our attempts to stop it." 1>&2
+ echo "Stop it yourself and try again!" 1>&2
+ db_stop
+ exit 1
+ fi
+}
+
+################################ main() ##########################
+
+this_version=5.6
+
+# Abort if an NDB cluster is in use.
+if egrep -qi -r '^[^#]*ndb.connectstring|^[[:space:]]*\[[[:space:]]*ndb_mgmd' /etc/mysql/; then
+ db_fset mysql-server/no_upgrade_when_using_ndb seen false || true
+ db_input high mysql-server/no_upgrade_when_using_ndb || true
+ db_go
+ db_stop
+ exit 1
+fi
+
+# Abort if skip-bdb option is enabled, required for 5.0 -> 5.1 upgrades.
+#TODO
+
+# Safe the user from stupidities.
+show_downgrade_warning=0
+for i in `ls $DATADIR/debian-*.flag 2>/dev/null`; do
+ found_version=`echo $i | sed 's/.*debian-\([0-9\.]\+\).flag/\1/'`
+ if dpkg --compare-versions "$this_version" '<<' "$found_version"; then
+ show_downgrade_warning=1
+ break;
+ fi
+done
+if [ "$show_downgrade_warning" = 1 ]; then
+ db_fset mysql-server-$this_version/really_downgrade seen false || true
+ db_input medium mysql-server-$this_version/really_downgrade || true
+ db_go
+ db_get mysql-server-$this_version/really_downgrade || true
+ if [ "$RET" = "true" ]; then
+ rm -f $DATADIR/debian-*.flag
+ touch $DATADIR/debian-$this_version.flag
+ else
+ echo "Aborting downgrade from (at least) $found_version to $this_version." 1>&2
+ echo "If are sure you want to downgrade to $this_version, remove the file" 1>&2
+ echo "$DATADIR/debian-*.flag and try installing again." 1>&2
+ db_stop
+ exit 1
+ fi
+fi
+
+# to be sure
+stop_server
+
+# If we use NIS then errors should be tolerated. It's up to the
+# user to ensure that the mysql user is correctly setup.
+# Beware that there are two ypwhich one of them needs the 2>/dev/null!
+if test -n "`which ypwhich 2>/dev/null`" && ypwhich >/dev/null 2>&1; then
+ set +e
+fi
+
+#
+# Now we have to ensure the following state:
+# /etc/passwd: mysql:x:100:101:MySQL Server:/nonexistent:/bin/false
+# /etc/group: mysql:x:101:
+#
+# Sadly there could any state be present on the system so we have to
+# modify everything carefully i.e. not doing a chown before creating
+# the user etc...
+#
+
+# creating mysql group if he isn't already there
+if ! getent group mysql >/dev/null; then
+ # Adding system group: mysql.
+ addgroup --system mysql >/dev/null
+fi
+
+# creating mysql user if he isn't already there
+if ! getent passwd mysql >/dev/null; then
+ # Adding system user: mysql.
+ adduser \
+ --system \
+ --disabled-login \
+ --ingroup mysql \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "MySQL Server" \
+ --shell /bin/false \
+ mysql >/dev/null
+fi
+
+# end of NIS tolerance zone
+set -e
+
+# if there's a symlink, let's store where it's pointing, because otherwise
+# it's going to be lost in some situations
+for dir in DATADIR LOGDIR; do
+ checkdir=`eval echo "$"$dir`
+ if [ -L "$checkdir" ]; then
+ mkdir -p "$UPGRADEDIR"
+ cp -d "$checkdir" "$UPGRADEDIR/$dir.link"
+ fi
+done
+
+# creating mysql home directory
+if [ ! -d $DATADIR -a ! -L $DATADIR ]; then
+ mkdir $DATADIR
+fi
+
+# checking disc space
+if LC_ALL=C BLOCKSIZE= df --portability $DATADIR/. | tail -n 1 | awk '{ exit ($4>1000) }'; then
+ echo "ERROR: There's not enough space in $DATADIR/" 1>&2
+ db_stop
+ exit 1
+fi
+
+# Since the home directory was created before putting the user into
+# the mysql group and moreover we cannot guarantee that the
+# permissions were correctly *before* calling this script, we fix them now.
+# In case we use NIS and no mysql user is present then this script should
+# better fail now than later..
+# The "set +e" is necessary as e.g. a ".journal" of a ext3 partition is
+# not chgrp'able (#318435).
+set +e
+chown mysql:mysql $DATADIR
+find $DATADIR -follow -not -group mysql -print0 2>/dev/null \
+ | xargs -0 --no-run-if-empty chgrp mysql
+set -e
+
+# Some files below /etc/ were possibly in the mysql-server-5.0/etch package
+# before. They get overwritten by current ones to avoid unnecessary dpkg questions.
+while read md5 file; do
+ if [ "`md5sum $file 2>/dev/null`" = "$md5 $file" ]; then
+ cp /usr/share/mysql-common/internal-use-only/`echo $file | sed 's°/°_°g'` $file
+ fi
+done <<EOT
+6691f2fdc5c6d27ff0260eb79813e1bc /etc/init.d/mysql
+b53b9552d44661361d39157c3c7c51d3 /etc/logrotate.d/mysql-server
+57f3e58f72582ca55100dc1ba0f1a8ae /etc/mysql/debian-start
+EOT
+
+db_stop
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
+#!/bin/bash
+
+set -e
+
+. /usr/share/debconf/confmodule
+
+if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi
+${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2 }
+
+#DEBHELPER#
--- /dev/null
+# These templates have been reviewed by the debian-l10n-english
+# team
+#
+# If modifications/additions/rewording are needed, please ask
+# for an advice to debian-l10n-english@lists.debian.org
+#
+# Even minor modifications require translation updates and such
+# changes should be coordinated with translators and reviewers.
+
+Template: mysql-server-5.6/really_downgrade
+Type: boolean
+Default: false
+_Description: Really proceed with downgrade?
+ A file named /var/lib/mysql/debian-*.flag exists on this system.
+ .
+ Such a file is an indication that a mysql-server package with a higher
+ version has been installed previously.
+ .
+ There is no guarantee that the version you're currently installing
+ will be able to use the current databases.
+
+Template: mysql-server-5.6/nis_warning
+Type: note
+#flag:translate!:3,5
+_Description: Important note for NIS/YP users
+ Using MySQL under NIS/YP requires a mysql user account to be added on
+ the local system with:
+ .
+ adduser --system --group --home /var/lib/mysql mysql
+ .
+ You should also check the permissions and ownership of the
+ /var/lib/mysql directory:
+ .
+ /var/lib/mysql: drwxr-xr-x mysql mysql
+
+Template: mysql-server-5.6/postrm_remove_databases
+Type: boolean
+Default: false
+_Description: Remove all MySQL databases?
+ The /var/lib/mysql directory which contains the MySQL databases is about
+ to be removed.
+ .
+ If you're removing the MySQL package in order to later install a more
+ recent version or if a different mysql-server package is already
+ using it, the data should be kept.
+
+Template: mysql-server-5.6/start_on_boot
+Type: boolean
+Default: true
+_Description: Start the MySQL server on boot?
+ The MySQL server can be launched automatically at boot time or manually
+ with the '/etc/init.d/mysql start' command.
+
+Template: mysql-server/root_password
+Type: password
+_Description: New password for the MySQL "root" user:
+ While not mandatory, it is highly recommended that you set a password
+ for the MySQL administrative "root" user.
+ .
+ If this field is left blank, the password will not be changed.
+
+Template: mysql-server/root_password_again
+Type: password
+_Description: Repeat password for the MySQL "root" user:
+
+Template: mysql-server/error_setting_password
+Type: error
+_Description: Unable to set password for the MySQL "root" user
+ An error occurred while setting the password for the MySQL
+ administrative user. This may have happened because the account
+ already has a password, or because of a communication problem with
+ the MySQL server.
+ .
+ You should check the account's password after the package installation.
+ .
+ Please read the /usr/share/doc/mysql-server-5.6/README.Debian file
+ for more information.
+
+Template: mysql-server/password_mismatch
+Type: error
+_Description: Password input error
+ The two passwords you entered were not the same. Please try again.
+
+Template: mysql-server/no_upgrade_when_using_ndb
+Type: error
+_Description: NDB Cluster seems to be in use
+ MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new
+ mysql-cluster-server package and remove all lines starting with "ndb" from
+ all config files below /etc/mysql/.
--- /dev/null
+/usr/lib/mysql-testsuite
+usr/share/mysql/mysql-test/suite/wsrep
+usr/share/mysql/mysql-test/suite/wsrep/t
+usr/share/mysql/mysql-test/suite/wsrep/r
--- /dev/null
+usr/share/mysql-test/* usr/lib/mysql-testsuite/
--- /dev/null
+# These are random occurrences of a pseudo word in a binary.
+mysql-testsuite-5.6: spelling-error-in-binary usr/bin/mysql_client_test_embedded yuR your
+mysql-testsuite-5.6: spelling-error-in-binary usr/bin/mysql_embedded yuR your
+mysql-testsuite-5.6: spelling-error-in-binary usr/bin/mysqltest_embedded yuR your
--- /dev/null
+From: Clint Byrum <clint@ubuntu.com>
+Description: makes mtr look in the standard location from the
+ mysql-testsuite-5.5 package.
+Forwarded: not-needed
+
+--- a/mysql-test/lib/mtr_cases.pm
++++ b/mysql-test/lib/mtr_cases.pm
+@@ -287,7 +287,8 @@ sub collect_one_suite($)
+ else
+ {
+ $suitedir= my_find_dir($::basedir,
+- ["share/mysql-test/suite",
++ ["lib/mysql-testsuite/suite",
++ "share/mysql-test/suite",
+ "mysql-test/suite",
+ "internal/mysql-test/suite",
+ "mysql-test",
--- /dev/null
+Author: Pino Toscano <pino@debian.org>
+Subject: cmake options for GNU/Hurd
+Bug: http://bugs.mysql.com/bug.php?id=64685
+Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=651002
+Last-Update: 2012-03-18
+Reviewed-by: Nicholas Bamber <nicholas@periapt.co.uk>
+--- /dev/null
++++ b/cmake/os/GNU.cmake
+@@ -0,0 +1,20 @@
++# This file includes GNU/Hurd specific options and quirks, related to system checks
++
++INCLUDE(CheckSymbolExists)
++
++SET(_GNU_SOURCE 1)
++
++# Fix CMake (< 2.8) flags. -rdynamic exports too many symbols.
++FOREACH(LANG C CXX)
++ STRING(REPLACE "-rdynamic" ""
++ CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS
++ "${CMAKE_SHARED_LIBRARY_LINK_${LANG}_FLAGS}"
++ )
++ENDFOREACH()
++
++# Ensure we have clean build for shared libraries
++# without unresolved symbols
++SET(LINK_FLAG_NO_UNDEFINED "-Wl,--no-undefined")
++
++# 64 bit file offset support flag
++SET(_FILE_OFFSET_BITS 64)
+--- a/mysql-test/lib/My/Platform.pm
++++ b/mysql-test/lib/My/Platform.pm
+@@ -110,6 +110,9 @@ sub check_socket_path_length {
+ # This may not be true, but we can't test for it on AIX due to Perl bug
+ # See Bug #45771
+ return 0 if ($^O eq 'aix');
++ # Similarly the path length is hidden.
++ # See Debian bug #651002
++ return 0 if ($^O eq 'gnu');
+
+ require IO::Socket::UNIX;
+
--- /dev/null
+Author: Nicholas Bamber <nicholas@periapt.co.uk>
+Debian-Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=540153
+Subject: tests not getting started on kFreeBSD
+ As per #670722 I found that the test socket created to test
+ file name truncation was barfing even for the shortened form.
+Forwarded: no
+Last-Update: 2012-04-28
+--- a/mysql-test/lib/My/Platform.pm
++++ b/mysql-test/lib/My/Platform.pm
+@@ -113,6 +113,8 @@ sub check_socket_path_length {
+ # Similarly the path length is hidden.
+ # See Debian bug #651002
+ return 0 if ($^O eq 'gnu');
++ # See Debian bug #670722 - failing on kFreeBSD even after setting short path
++ return 0 if length $path < 40;
+
+ require IO::Socket::UNIX;
+
--- /dev/null
+Author: Christian Hammers <ch@debian.org>
+Subject: Executes /etc/init.d/mysql on signals
+ Note if you check the bug report on mysql.com the patch is accepted
+ as interesting but they were waiting on Christian Hammers to sign an SCA.
+ Meanwhile this bug report fell off in the transition from 5.0 to 5.1.
+Bug: http://bugs.mysql.com/bug.php?id=31361
+Debian-Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=527623
+Last-Update: 2012-03-07
+Reviewed-by: Nicholas Bamber <nicholas@periapt.co.uk>
+
+--- a/scripts/mysqld_safe.sh
++++ b/scripts/mysqld_safe.sh
+@@ -29,9 +29,6 @@ err_log=
+ syslog_tag_mysqld=mysqld
+ syslog_tag_mysqld_safe=mysqld_safe
+
+-trap '' 1 2 3 15 # we shouldn't let anyone kill us
+-trap '' 13 # not even SIGPIPE
+-
+ # MySQL-specific environment variable. First off, it's not really a umask,
+ # it's the desired mode. Second, it follows umask(2), not umask(3) in that
+ # octal needs to be explicit. Our shell might be a proper sh without printf,
+@@ -154,7 +151,7 @@ eval_log_error () {
+ # sed buffers output (only GNU sed supports a -u (unbuffered) option)
+ # which means that messages may not get sent to syslog until the
+ # mysqld process quits.
+- cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error"
++ cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error & wait"
+ ;;
+ *)
+ echo "Internal program error (non-fatal):" \
+@@ -815,6 +815,13 @@ mysqld daemon not started"
+ fi
+
+ #
++# From now on, we catch signals to do a proper shutdown of mysqld
++# when signalled to do so.
++#
++trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf refresh & wait' 1 # HUP
++trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf shutdown' 2 3 15 # INT QUIT and TERM
++
++#
+ # Uncomment the following lines if you want all tables to be automatically
+ # checked and repaired during startup. You should add sensible key_buffer
+ # and sort_buffer values to my.cnf to improve check performance or require
--- /dev/null
+hurd.patch
+scripts__mysqld_safe.sh__signals.patch
+fix_standalone_tests.patch
+kfreebsd_tests.patch
--- /dev/null
+Author <nicholas@periapt.co.uk>
+Subject: spelling errors
+ Preceeding -> preceding
+Last-Update: 2012-05-19
+Forwarded: no
+--- a/libevent/event.3
++++ b/libevent/event.3
+@@ -253,7 +253,7 @@ the type of event which will be either
+ or
+ .Va EV_WRITE .
+ Additionally, an event which has registered interest in more than one of the
+-preceeding events, via bitwise-OR to
++preceding events, via bitwise-OR to
+ .Fn event_set ,
+ can provide its callback function with a bitwise-OR of more than one triggered
+ event.
+--- a/mysql-test/extra/rpl_tests/rpl_ddl.test
++++ b/mysql-test/extra/rpl_tests/rpl_ddl.test
+@@ -98,8 +98,8 @@
+ # --> less switching of AUTOCOMMIT mode on master side.
+ #
+ # 4. Never use a test object, which was direct or indirect affected by a
+-# preceeding test sequence again.
+-# If one preceeding test sequence hits a (sometimes not visible,
++# preceding test sequence again.
++# If one preceding test sequence hits a (sometimes not visible,
+ # because the sql error code of the statement might be 0) bug
+ # and these rules are ignored, a following test sequence might earn ugly
+ # effects like failing 'sync_slave_with_master', crashes of the slave or
+--- a/mysql-test/extra/rpl_tests/rpl_row_basic.test
++++ b/mysql-test/extra/rpl_tests/rpl_row_basic.test
+@@ -221,7 +221,7 @@ INSERT INTO t7 VALUES (1,3), (2,6), (3,9
+ SELECT * FROM t7 ORDER BY C1;
+
+ # since bug#31552/31609 idempotency is not default any longer. In order
+-# the preceeding test INSERT INTO t7 to pass the mode is switched
++# the preceding test INSERT INTO t7 to pass the mode is switched
+ # temprorarily
+ set @@global.slave_exec_mode= 'IDEMPOTENT';
+
+@@ -260,7 +260,7 @@ INSERT INTO t8 VALUES (1,2,3), (2,4,6),
+ SELECT * FROM t8 ORDER BY a;
+
+ # since bug#31552/31609 idempotency is not default any longer. In order
+-# the preceeding test INSERT INTO t8 to pass the mode is switched
++# the preceding test INSERT INTO t8 to pass the mode is switched
+ # temprorarily
+ set @@global.slave_exec_mode= 'IDEMPOTENT';
+
+--- a/mysql-test/include/wait_until_count_sessions.inc
++++ b/mysql-test/include/wait_until_count_sessions.inc
+@@ -10,7 +10,7 @@
+ # 1. We wait for $current_sessions <= $count_sessions because in the use case
+ # with count_sessions.inc before and wait_until_count_sessions.inc after
+ # the core of the test it could happen that the disconnects of sessions
+-# belonging to the preceeding test are not finished.
++# belonging to the preceding test are not finished.
+ # sessions at test begin($count_sessions) = m + n
+ # sessions of the previous test which will be soon disconnected = n (n >= 0)
+ # sessions at test end ($current sessions, assuming the test disconnects
+--- a/mysql-test/suite/funcs_1/views/func_view.inc
++++ b/mysql-test/suite/funcs_1/views/func_view.inc
+@@ -285,7 +285,7 @@ INSERT INTO t1_values SET
+ # other interesting value
+ # numbers -> 0
+ # strings, blobs, binaries -> not full length of used data type, "exotic"
+-# characters and preceeding and trailing spaces
++# characters and preceding and trailing spaces
+ # FIXME enum, set ??
+ INSERT INTO t1_values SET
+ my_char_30 = ' ---äÖüß@µ*$-- ',
+--- a/mysql-test/suite/funcs_1/views/views_master.inc
++++ b/mysql-test/suite/funcs_1/views/views_master.inc
+@@ -545,7 +545,7 @@ let $message= Testcase 3.3.1.7 ;
+ # view names are accepted, at creation time, alteration time,
+ # and drop time.
+ ###############################################################################
+-# Note(mleich): non-qualified view name means a view name without preceeding
++# Note(mleich): non-qualified view name means a view name without preceding
+ # database name
+ --disable_warnings
+ DROP VIEW IF EXISTS v1 ;
+--- a/mysql-test/suite/rpl/t/rpl_ddl.test
++++ b/mysql-test/suite/rpl/t/rpl_ddl.test
+@@ -13,10 +13,10 @@
+ # sequences start.
+ #
+ # 2. Never use a test object, which was direct or indirect affected by a
+-# preceeding test sequence again.
++# preceding test sequence again.
+ # Except table d1.t1 where ONLY DML is allowed.
+ #
+-# If one preceeding test sequence hits a (sometimes not good visible,
++# If one preceding test sequence hits a (sometimes not good visible,
+ # because the sql error code of the statement might be 0) bug
+ # and these rules are ignored, a following test sequence might earn ugly
+ # effects like failing 'sync_slave_with_master', crashes of the slave or
+--- a/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test
++++ b/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test
+@@ -239,7 +239,7 @@ INSERT INTO t1 VALUES (1,'master,slave')
+ UPDATE t1 SET a = 5, b = 'slave' WHERE a = 1;
+ SELECT * FROM t1 ORDER BY a;
+ # since bug#31552/31609 idempotency is not default any longer. In
+-# order for the preceeding test UPDATE t1 to pass, the mode is switched
++# order for the preceding test UPDATE t1 to pass, the mode is switched
+ # temprorarily
+ set @@global.slave_exec_mode= 'IDEMPOTENT';
+ --echo **** On Master ****
+--- a/mysql-test/suite/rpl_ndb/t/rpl_ndb_ddl.test
++++ b/mysql-test/suite/rpl_ndb/t/rpl_ndb_ddl.test
+@@ -13,10 +13,10 @@
+ # sequences start.
+ #
+ # 2. Never use a test object, which was direct or indirect affected by a
+-# preceeding test sequence again.
++# preceding test sequence again.
+ # Except table d1.t1 where ONLY DML is allowed.
+ #
+-# If one preceeding test sequence hits a (sometimes not good visible,
++# If one preceding test sequence hits a (sometimes not good visible,
+ # because the sql error code of the statement might be 0) bug
+ # and these rules are ignored, a following test sequence might earn ugly
+ # effects like failing 'sync_slave_with_master', crashes of the slave or
+--- a/sql/abstract_query_plan.cc
++++ b/sql/abstract_query_plan.cc
+@@ -336,7 +336,7 @@ namespace AQP
+ {
+ /*
+ use_quick == 2 means that the decision on which access method to use
+- will be taken late (as rows from the preceeding operation arrive).
++ will be taken late (as rows from the preceding operation arrive).
+ This operation is therefor not pushable.
+ */
+ DBUG_PRINT("info",
+--- a/sql/ha_ndbcluster_push.cc
++++ b/sql/ha_ndbcluster_push.cc
+@@ -1271,7 +1271,7 @@ ndb_pushed_builder_ctx::build_key(const
+ if (m_join_scope.contain(referred_table_no))
+ {
+ // Locate the parent operation for this 'join_items[]'.
+- // May refer any of the preceeding parent tables
++ // May refer any of the preceding parent tables
+ const NdbQueryOperationDef* const parent_op= m_tables[referred_table_no].m_op;
+ DBUG_ASSERT(parent_op != NULL);
+
+--- a/sql/log_event.cc
++++ b/sql/log_event.cc
+@@ -4625,7 +4625,7 @@ int Query_log_event::do_apply_event(Rela
+ if ((error= rows_event_stmt_cleanup(const_cast<Relay_log_info*>(rli), thd)))
+ {
+ const_cast<Relay_log_info*>(rli)->report(ERROR_LEVEL, error,
+- "Error in cleaning up after an event preceeding the commit; "
++ "Error in cleaning up after an event preceding the commit; "
+ "the group log file/position: %s %s",
+ const_cast<Relay_log_info*>(rli)->get_group_master_log_name(),
+ llstr(const_cast<Relay_log_info*>(rli)->get_group_master_log_pos(),
+--- a/sql/rpl_utility.cc
++++ b/sql/rpl_utility.cc
+@@ -1564,7 +1564,7 @@ bool Deferred_log_events::execute(Relay_
+ void Deferred_log_events::rewind()
+ {
+ /*
+- Reset preceeding Query log event events which execution was
++ Reset preceding Query log event events which execution was
+ deferred because of slave side filtering.
+ */
+ if (!is_empty())
+--- a/sql/sql_optimizer.cc
++++ b/sql/sql_optimizer.cc
+@@ -971,7 +971,7 @@ JOIN::optimize()
+ }
+ }
+ }
+- else if (order && // ORDER BY wo/ preceeding GROUP BY
++ else if (order && // ORDER BY wo/ preceding GROUP BY
+ (simple_order || skip_sort_order)) // which is possibly skippable
+ {
+ if (test_if_skip_sort_order(tab, order, m_select_limit, false,
+--- a/sql/sql_rewrite.cc
++++ b/sql/sql_rewrite.cc
+@@ -40,7 +40,7 @@
+
+
+ /**
+- Append a key/value pair to a string, with an optional preceeding comma.
++ Append a key/value pair to a string, with an optional preceding comma.
+ For numeric values.
+
+ @param str The string to append to
+@@ -72,7 +72,7 @@ bool append_int(String *str, bool comma,
+
+ /**
+ Append a key/value pair to a string if the value is non-NULL,
+- with an optional preceeding comma.
++ with an optional preceding comma.
+
+ @param str The string to append to
+ @param comma Prepend a comma?
+--- a/sql/sql_yacc.cc
++++ b/sql/sql_yacc.cc
+@@ -35151,7 +35151,7 @@ yyreduce:
+ {
+ /*
+ Not in trigger assigning value to new row,
+- and option_type preceeding local variable is illegal.
++ and option_type preceding local variable is illegal.
+ */
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+--- a/sql/sql_yacc.yy
++++ b/sql/sql_yacc.yy
+@@ -14573,7 +14573,7 @@ opt_var_ident_type:
+ | SESSION_SYM '.' { $$=OPT_SESSION; }
+ ;
+
+-// Option values with preceeding option_type.
++// Option values with preceding option_type.
+ option_value_following_option_type:
+ internal_variable_name equal set_expr_or_default
+ {
+@@ -14590,7 +14590,7 @@ option_value_following_option_type:
+ {
+ /*
+ Not in trigger assigning value to new row,
+- and option_type preceeding local variable is illegal.
++ and option_type preceding local variable is illegal.
+ */
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+@@ -14598,7 +14598,7 @@ option_value_following_option_type:
+ }
+ ;
+
+-// Option values without preceeding option_type.
++// Option values without preceding option_type.
+ option_value_no_option_type:
+ internal_variable_name equal
+ {
+--- a/storage/myisam/mi_rnext.c
++++ b/storage/myisam/mi_rnext.c
+@@ -65,7 +65,7 @@ int mi_rnext(MI_INFO *info, uchar *buf,
+ Normally SQL layer would never request "search next" if
+ "search first" failed. But HANDLER may do anything.
+
+- As mi_rnext() without preceeding mi_rkey()/mi_rfirst()
++ As mi_rnext() without preceding mi_rkey()/mi_rfirst()
+ equals to mi_rfirst(), we must restore original state
+ as if failing mi_rfirst() was not called.
+ */
--- /dev/null
+[type: gettext/rfc822deb] mysql-wsrep-server-5.6.templates
--- /dev/null
+# translation of templates.po to Arabic
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Ossama M. Khayat <okhayat@yahoo.com>, 2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: templates\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2007-05-01 13:04+0300\n"
+"Last-Translator: Ossama M. Khayat <okhayat@yahoo.com>\n"
+"Language-Team: Arabic <support@arabeyes.org>\n"
+"Language: ar\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=6; plural=n==1 ? 0 : n==0 ? 1 : n==2 ? 2: n%100>=3 && "
+"n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+": n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+": n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+": n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+": n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+": n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+": n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "هل فعلاً تريد التثبيط؟"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "هناك ملف مسمى /var/lib/mysql/debian-*.flag موجود على هذا النظام."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "Such file is an indication that a mysql-server package with a higher "
+#| "version has been installed earlier."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"هذا الملف دلالة على أن نسخة أحدث من حزمة mysql-server تم تثبيتها مسبقاً."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"ليست هناك أية ضمانة أن النسخة التي تقوم بتثبيتها ستكون قادرة على استخدام "
+"قواعد البيانات الحالية."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "ملاحظة هامة لمستخدمي NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid ""
+#| "You should also check the permissions and the owner of the /var/lib/mysql "
+#| "directory:"
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr "عليك أيضاً أن تقوم بالتأكد من صلاحيات مالك الملف /var/lib/mysql: "
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "إزالة جميع قواعد بيانات MySQL؟"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr "الدليل /var/lib/mysql الذي يحتوي قواعد بيانات MySQL ستتم إزالته."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"إن كنت تقوم بإزالة حزمة MySQL كي تقوم لاحقاً بتثبيت نسخة أحدث أو إن كانت حزمة "
+"mysql-server مختلفة تستخدمها، فيجب إبقاء البيانات."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "تشغيل خادم MySQL عند الإقلاع؟"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"يمكن تشغيل خادم MySQL آلياً وقت الإقلاع أو يدوياً باستخدام الأمر '/etc/init.d/"
+"mysql start'."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "كلمة المرور الجديدة لمستخد \"root\" الخاص بـMySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"مع أنه ليس إجبارياً، ولكن من المستحسن أن تقوم بتعيين كلمة مرور خاصة بمستخدم "
+"MySQL الإداري \"root\"."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "If that field is left blank, the password will not be changed."
+msgid "If this field is left blank, the password will not be changed."
+msgstr "إن ترك الحقل فارغاً، فلن يتم تغيير كلمة المرور."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+#, fuzzy
+#| msgid "New password for the MySQL \"root\" user:"
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "كلمة المرور الجديدة لمستخد \"root\" الخاص بـMySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "تعذر تعيين كلمة مرور للمستخدم \"root\" الخاص بـMySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"حدث خطأ أثناء تعيين كلمة المرور لمستخدم MySQL الإداري. قد يكون هذا حدث بسبب "
+"أن حساب المستخدم له كلمة مرور معيّنة مسبقاً، أو بسبب مشكلة في الاتصال مع خادم "
+"MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "You should check the account's password after tha package installation."
+msgid "You should check the account's password after the package installation."
+msgstr "يجب عليك التحقق من كلمة مرور الحساب عقب تثبيت الحزمة."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"الرجاء قراءة الملف /usr/share/doc/mysql-server-5.6/README.Debian للمزيد من "
+"المعلومات."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "كي تستخدم MySQL، يجب إضافة المُدخلات التالية الخاصة بالمستخدمين والمجموعات "
+#~ "إلى النظام:"
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "هل تريد دعم اتصالات MySQL من الأجهزة التي تعمل على ديبيان \"sarge\" أو "
+#~ "أقدم؟"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "في إصدارات عملاء MySQL القديمة من ديبيان، لم تكن كلمات المرور تحفظ بشكل "
+#~ "آمن. ولقد حل هذه المشكلة بعدها، غير أن العملاء (مثل PHP) المتصلين من "
+#~ "أجهزة تعمل على ديبيان Sarge 3.1 لن يكونوا قادرين على الاتصال باستخدام "
+#~ "الحسابات الحديثة أو الحسابات التي تم تغيير كلمة مرورها."
--- /dev/null
+# mysql-dfsg (debconf) translation to Catalan.
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+# Aleix Badia i Bosch <abadia@ica.es> 2004
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-4.1\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2004-01-31 19:20GMT\n"
+"Last-Translator: Aleix Badia i Bosch <abadia@ica.es>\n"
+"Language-Team: Debian L10n Catalan <debian-l10n-catalan@lists.debian.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Aquest fitxer indica que anteriorment s'ha instaŀlat un paquet mysql-server "
+"amb una versió posterior."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"No hi ha cap garantia que la versió que esteu instaŀlant actualment puga "
+"emprar les bases de dades actuals."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid "Important note for NIS/YP users!"
+msgid "Important note for NIS/YP users"
+msgstr "Nota important pels usuaris de NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Si empreu MySQL sota NIS/YP, heu d'afegir un compte d'usuari mysql al "
+"sistema local amb:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"També hauríeu de comprovar els permisos i propietaris del directori /var/"
+"lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"El directori /var/lib/mysql que conté les bases de dades de MySQL està a "
+"punt deser suprimit."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Si esteu suprimint el paquet MySQL per a posteriorment instaŀlar una versió "
+"més recent, o si un paquet mysql-server diferent ja l'està emprant, les "
+"dades s'haurien de mantenir."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+#| msgid "Should MySQL start on boot?"
+msgid "Start the MySQL server on boot?"
+msgstr "Voleu que el MySQL s'iniciï a l'arrencada ?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"El MySQL es pot executar automàticament a l'arrencada o manualment amb "
+"l'ordre «/etc/init.d/mysql start»."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Tot i que no és requerida, és molt recomanable que establiu una "
+"contrasenya per a «root», l'usuari administratiu del MySQL."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"S'ha produït un error en establir la contrasenya de l'usuari administratiu "
+"del MySQL. Això pot haver passat perquè el compte ja té una una "
+"contrasenya, o per un problema de comunicació amb el servidor de MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Hauríeu de comprovar la contrasenya del compte després de la instaŀlació "
+"del paquet."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.1/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Llegiu el fitxer /usr/share/doc/mysql-server-5.1/README.Debian per a obtenir "
+"més informació."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"Les dos contrasenyes que heu introduït no són la mateixa. Proveu-ho de nou."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "MySQL-5.1 no longer provides NDB Cluster support. Please migrate to the "
+#~ "new "
+#~ msgstr ""
+#~ "El MySQL-5.1 ja no implementa el clúster NDB. Migreu al nou paquet mysql-"
+#~ "cluster i suprimiu totes les línies que comencen per «ndb» de tots els "
+#~ "fitxers de configuració sota /etc/mysql/."
--- /dev/null
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2007-05-01 13:01+0200\n"
+"Last-Translator: Miroslav Kure <kurem@debian.cz>\n"
+"Language-Team: Czech <debian-l10n-czech@lists.debian.org>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Opravdu pokračovat v degradaci?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "V systému existuje soubor /var/lib/mysql/debian-*.flag."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "Such file is an indication that a mysql-server package with a higher "
+#| "version has been installed earlier."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr "To znamená, že již byl nainstalován balík mysql-server s vyšší verzí."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Neexistuje žádná záruka, že momentálně instalovaná verze bude umět pracovat "
+"se stávajícími databázemi."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Důležitá poznámka pro uživatele NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid ""
+#| "You should also check the permissions and the owner of the /var/lib/mysql "
+#| "directory:"
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Také byste měli zkontrolovat vlastníka a oprávnění adresáře /var/lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Odstranit všechny MySQL databáze?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Adresář /var/lib/mysql, ve kterém se nachází MySQL databáze, bude odstraněn."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Jestliže odstraňujete balík MySQL za účelem instalace novější verze MySQL, "
+"nebo pokud tato data souběžně využívá jiný balík mysql-server, měli byste "
+"data ponechat."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Spustit MySQL server při startu systému?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL se může spouštět automaticky při startu systému, nebo ručně příkazem '/"
+"etc/init.d/mysql start'."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nové heslo MySQL uživatele \"root\":"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Přestože to není nezbytné, je silně doporučeno nastavit heslo u "
+"správcovského MySQL účtu \"root\"."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "If that field is left blank, the password will not be changed."
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Ponecháte-li pole prázdné, heslo se nezmění."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+#, fuzzy
+#| msgid "New password for the MySQL \"root\" user:"
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Nové heslo MySQL uživatele \"root\":"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Nelze nastavit heslo MySQL uživatele \"root\""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Během nastavování hesla pro správcovského uživatele MySQL se vyskytla chyba. "
+"To se mohlo stát třeba proto, protože uživatel již měl heslo nastaveno, nebo "
+"protože nastal problém v komunikaci s MySQL serverem."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "You should check the account's password after tha package installation."
+msgid "You should check the account's password after the package installation."
+msgstr "Po instalaci balíku byste měli heslo ověřit."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Více informací naleznete v /usr/share/doc/mysql-server-5.6/README.Debian."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "Abyste mohli MySQL používat, musíte v systému založit následující "
+#~ "uživatele a skupiny:"
+
+#~ msgid "Cannot upgrade if ISAM tables are present!"
+#~ msgstr "Aktualizace nelze provést pokud jsou přítomny tabulky ISAM!"
+
+#~ msgid ""
+#~ "Recent versions of MySQL can no longer use the old ISAM table format and "
+#~ "it is necessary to convert your tables to e.g. MyISAM before upgrading by "
+#~ "using \"mysql_convert_table_format\" or \"ALTER TABLE x ENGINE=MyISAM\". "
+#~ "The installation of mysql-server-5.6 will now abort. In case your old "
+#~ "mysql-server-4.1 gets removed nevertheless just reinstall it to convert "
+#~ "those tables."
+#~ msgstr ""
+#~ "Poslední verze MySQL již nemohou používat starý formát tabulek ISAM a "
+#~ "před aktualizací je nutné převést tyto tabulky např. do formátu MyISAM "
+#~ "pomocí \"mysql_convert_table_format\" nebo \"ALTER TABLE x ENGINE=MyISAM"
+#~ "\". Instalace mysql-server-5.6 se nyní přeruší. V případě, že se mezitím "
+#~ "odinstaloval původní mysql-server-4.1, jednoduše jej znovu nainstalujte a "
+#~ "tabulky převeďte."
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "Podporovat MySQL připojení z počítačů používajících Debian Sarge nebo "
+#~ "starší?"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "Způsob, jakým se dříve ukládala hesla, nebyl příliš bezpečný. To se nyní "
+#~ "zlepšilo, ale nevýhodou je, že se klienti z Debianu 3.1 Sarge (např. PHP) "
+#~ "nebudou moci připojit na nové účty, nebo na účty, u nichž se heslo "
+#~ "změnilo."
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "Abyste mohli mysql používat, musíte do následujících souborů přidat "
+#~ "ekvivalentního uživatele a skupinu a zajistit, že /var/lib/mysql má "
+#~ "správná práva (uid/gid se mohou lišit)."
+
+#~ msgid "Remove the databases used by all MySQL versions?"
+#~ msgstr "Odstranit databáze používané všemi verzemi MySQL?"
+
+#~ msgid ""
+#~ "If you do not provide a password no changes will be made to the account."
+#~ msgstr "Nezadáte-li heslo, žádné změny se s účtem neprovedou."
+
+#~ msgid ""
+#~ "When installation finishes, you should verify that the account is "
+#~ "properly protected with a password (see README.Debian for more "
+#~ "information)."
+#~ msgstr ""
+#~ "Po skončení instalace byste měli ověřit, že je účet chráněn heslem (více "
+#~ "informací naleznete v souboru README.Debian)."
+
+#~ msgid "Update Hints"
+#~ msgstr "Poznámky k aktualizaci"
+
+#~ msgid ""
+#~ "You have to run \"mysql_upgrade\" after the upgrade, else tables can be "
+#~ "corrupted! This script also enhances the privilege tables but is not "
+#~ "supposed to give any user more rights that he had before,"
+#~ msgstr ""
+#~ "Po aktualizaci ještě musíte spustit \"mysql_upgrade\", protože jinak by "
+#~ "se tabulky mohly narušit! Tento skript také rozšiřuje tabulky privilegií, "
+#~ "ovšem neměl by uživatelům přidat více práv, než měli dosud."
+
+#~ msgid "Please also read http://www.mysql.com/doc/en/Upgrade.html"
+#~ msgstr "Také si přečtěte http://www.mysql.com/doc/en/Upgrade.html"
+
+#~ msgid ""
+#~ "MySQL will only install if you have a non-numeric hostname that is "
+#~ "resolvable via the /etc/hosts file. E.g. if the \"hostname\" command "
+#~ "returns \"myhostname\" then there must be a line like \"10.0.0.1 "
+#~ "myhostname\"."
+#~ msgstr ""
+#~ "MySQL se nainstaluje pouze v případě, že používáte nenumerické jméno "
+#~ "počítače, které se dá přeložit přes soubor /etc/hosts. Např. když příkaz "
+#~ "\"hostname\" vrátí \"diamond\", tak v /etc/hosts musí existovat obdobný "
+#~ "řádek jako \"10.0.0.1 diamond\"."
+
+#~ msgid ""
+#~ "A new mysql user \"debian-sys-maint\" will be created. This mysql account "
+#~ "is used in the start/stop and cron scripts. Don't delete."
+#~ msgstr ""
+#~ "Bude vytvořen nový mysql uživatel \"debian-sys-maint\". Tento mysql účet "
+#~ "se používá ve startovacích, ukončovacích a cronových skriptech. Nemažte "
+#~ "jej."
+
+#~ msgid ""
+#~ "Please remember to set a PASSWORD for the MySQL root user! If you use a /"
+#~ "root/.my.cnf, always write the \"user\" and the \"password\" lines in "
+#~ "there, never only the password!"
+#~ msgstr ""
+#~ "Nezapomeňte nastavit heslo pro účet administrátora MySQL! Používáte-li /"
+#~ "root/.my.cnf, vždy zde zadejte jak řádek \"user\", tak řádek \"password"
+#~ "\". Nikdy zde nezadávejte jenom heslo!"
+
+#~ msgid ""
+#~ "Should I remove the complete /var/lib/mysql directory tree which is used "
+#~ "by all MySQL versions, not necessarily only the one you are about to "
+#~ "purge?"
+#~ msgstr ""
+#~ "Mám odstranit kompletní adresářový strom /var/lib/mysql, který se používá "
+#~ "pro všechny verze MySQL, tedy ne nutně pouze pro verzi, kterou se "
+#~ "chystáte vyčistit?"
--- /dev/null
+# Danish translation mysql-5.1.
+# Copyright (C) 2010 mysql-5.1 & nedenstående oversættere.
+# This file is distributed under the same license as the mysql-5.1 package.
+# Claus Hindsgaul <claus_h@image.dk> 2005, 2006, 2007.
+# Joe Hansen <joedalton2@yahoo.dk>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-5.1\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2010-10-07 05:26+0100\n"
+"Last-Translator: Joe Hansen <joedalton2@yahoo.dk>\n"
+"Language-Team: Danish <debian-l10n-danish@lists.debian.org> \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Ønsker du virkelig at fortsætte nedgraderingen?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "Der er en fil med navnet /var/lib/mysql/debian-*.flag på dette system."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Sådan en fil tyder på, at der tidligere har været installeret en højere "
+"version af mysql-server-pakken."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Det kan ikke garanteres at den version, du er ved at installere, kan benytte "
+"data fra de eksisterende databaser."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Vigtig oplysning til NIS/YP-brugere"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Brug af MySQL under NIS/YP kræver at en mysql-brugerkonto tilføjes på det "
+"lokale system med:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Du bør også tjekke rettighederne og ejerskabet af mappen /var/lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Fjern alle MySQL-databaser?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Mappen /var/lib/mysql, der indeholder MySQL-databaserne, er ved at blive "
+"fjernet."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Hvis du fjerner MySQL-pakken for senere at installere en nyere version, "
+"eller hvis en anden mysql-server-pakke allerede benytter den, bør dataene "
+"bevares."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Start MySQL-serveren under systemopstart?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL-serveren kan enten startes op automatisk under systemopstarten, eller "
+"manuelt med kommandoen '/etc/init.d/mysql start'."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Ny adgangskode for MySQL's »rootbruger«:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Selvom det ikke kræves, anbefales det kraftigt, at du sætter en adgangskode "
+"for MySQL's administrationsbruger »root«."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Hvis du lader dette felt stå tomt, vil adgangskoden ikke blive ændret."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Gentag adgangskode for MySQL's »root-bruger«:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Kunne ikke sætte adgangskoden for MySQL's »root-bruger«"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Der opstod en fejl, da adgangskoden for MySQL's administrationsbruger blev "
+"forsøgt ændret. Dette kan være sket, fordi brugeren allerede har en "
+"adgangskode, eller fordi der var problemer med at kommunikere med MySQL-"
+"serveren."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr "Du bør tjekke kontoens adgangskode efter pakkeinstallationen."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.1/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Læs venligst filen /usr/share/doc/mysql-server-5.1/README.Debian for "
+"yderligere oplysninger."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Inddatafejl for adgangskode"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"De to adgangskoder du indtastede var ikke de samme. Forsøg venligst igen."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB-cluster ser ud til at være i brug"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+#, fuzzy
+#| msgid ""
+#| "MySQL-5.1 no longer provides NDB Cluster support. Please migrate to the "
+#| "new mysql-cluster package and remove all lines starting with \"ndb\" from "
+#| "all config files below /etc/mysql/."
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.1 tilbyder ikke længere NDB-clusterunderstøttelse. Migrer venligst "
+"til den nye pakke mysql-cluster og fjern alle linjer der starter med »ndb« "
+"fra alle konfigurationsfiler under /etc/mysql/."
--- /dev/null
+# translation of mysql-dfsg-5.6_5.6.8-1_de.po to Deutsch
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans#
+# Developers do not need to manually edit POT or PO files.
+#
+# Alwin Meschede <ameschede@gmx.de>, 2006, 2007.
+# Thomas Mueller <thomas.mueller@tmit.eu>, 2009.
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg 5.6.23-2\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-06-03 10:33+0200\n"
+"Last-Translator: Thomas Mueller <thomas.mueller@tmit.eu>\n"
+"Language-Team: german <debian-l10n-german@lists.debian.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Möchten Sie wirklich eine ältere Version einspielen?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+"Auf diesem System existiert eine Datei mit dem Namen /var/lib/mysql/debian-*."
+"flag"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Diese Datei ist ein Hinweis darauf, dass früher ein MySQL-Server-Paket mit "
+"einer höheren Version installiert war."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Es kann nicht garantiert werden, dass die gegenwärtig zu installierende "
+"Version dessen Daten benutzen kann."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Wichtige Anmerkung für NIS/YP-Benutzer!"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Falls MySQL mit NIS/YP genutzt wird, ist ein »mysql«-Benutzerkonto auf dem "
+"lokalen System notwendig:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Sie sollten außerdem Besitzer und Zugriffsrechte des Verzeichnisses /var/lib/"
+"mysql überprüfen:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Alle MySQL-Datenbanken entfernen?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Das Verzeichnis /var/lib/mysql mit den MySQL-Datenbanken soll entfernt "
+"werden."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Falls geplant ist, nur eine höhere Version von MySQL zu installieren oder "
+"ein anderes mysql-server-Paket dieses bereits benutzt, sollten die Daten "
+"behalten werden."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Soll der MySQL-Server automatisch beim Booten starten?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"Der MySQL-Dienst kann entweder automatisch beim Systemstart oder manuell "
+"durch Eingabe des Befehls »/etc/init.d/mysql start« gestartet werden."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Neues Passwort für den MySQL »root«-Benutzer:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Obwohl es nicht zwingend erforderlich ist, wird nachdrücklich empfohlen für "
+"den administrativen MySQL »root«-Benutzer ein Passwort zu setzen."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Wenn dieses Feld freigelassen wird, wird das Passwort nicht geändert."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Wiederholen Sie das Passwort für den MySQL-»root«-Benutzer:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Konnte für den MySQL-»root«-Benutzer kein Passwort setzen"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Beim setzen des Passworts für den administrativen MySQL-Benutzer ist ein "
+"Fehler aufgetreten. Dies könnte daran liegen, dass der Benutzer bereits ein "
+"Passwort hat oder dass es ein Problem mit der Kommunikation mit dem MySQL-"
+"Server gibt."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Sie sollten das Passwort des administrativen Benutzers nach der "
+"Paketinstallation prüfen."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Für weitere Informationen lesen Sie /usr/share/doc/mysql-server-5.6/README."
+"Debian."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Passwort-Eingabefehler"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"Die beiden von Ihnen eingegebenen Passwörter sind nicht identisch. Bitte "
+"erneut versuchen."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB-Cluster scheint gerade benutzt zu werden"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 bietet keine NDB-Clusterunterstützung mehr. Bitte migrieren Sie "
+"Ihr System zum neuen »mysql-cluster-server«-Paket und entfernen Sie alle "
+"Zeilen, die mit »ndb« beginnen aus allen Konfigurationsdateien im "
+"Verzeichnis /etc/mysql/."
--- /dev/null
+# mysql-5.6 translation to spanish
+# Copyright (C) 2005-2012 Software in the Public Interest, SPI Inc.
+# This file is distributed under the same license as the mysql-5.6 package.
+#
+# Changes:
+# - Initial translation
+# Jesus Aneiros, 2006
+# - Updated
+# Javier Fernandez-Sanguino, 2006-2012
+# - Revision
+# Nacho Barrientos Arias
+# Fernando Cerezal
+# David Martínez Moreno
+# Ricardo Mones
+# Carlos Galisteo
+# Javier Fernandez-Sanguino
+# Fernando C. Estrada
+#
+# Traductores, si no conoce el formato PO, merece la pena leer la
+# documentación de gettext, especialmente las secciones dedicadas a este
+# formato, por ejemplo ejecutando:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Equipo de traducción al español, por favor lean antes de traducir
+# los siguientes documentos:
+#
+# - El proyecto de traducción de Debian al español
+# http://www.debian.org/intl/spanish/
+# especialmente las notas y normas de traducción en
+# http://www.debian.org/intl/spanish/notas
+#
+# - La guía de traducción de po's de debconf:
+# /usr/share/doc/po-debconf/README-trans
+# o http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Si tiene dudas o consultas sobre esta traducción consulte con el último
+# traductor (campo Last-Translator) y ponga en copia a la lista de
+# traducción de Debian al español (<debian-l10n-spanish@lists.debian.org>)
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-5.6\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-06-17 09:49-0500\n"
+"Last-Translator: Javier Fernández-Sanguino <jfs@debian.org>\n"
+"Language-Team: Debian l10 Spanish <debian-l10n-spanish@lists.debian.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "¿Desea realmente continuar con la desactualización?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+"Existe un fichero con el nombre /var/lib/mysql/debian-*.flag en este sistema."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Este fichero indica que se instaló previamente una versión superior del "
+"paquete mysql-server."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"No se puede garantizar que la versión que está instalando pueda usar la base "
+"de datos actual."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Nota importante para los usuarios de NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Utilizar MySQL con NIS/YP requiere que una cuenta de usuario de mysql sea "
+"agregada en el sistema local como:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"También debería comprobar los permisos y el propietario del directorio: /var/"
+"lib/mysql"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "¿Desea eliminar todas las bases de datos MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"El directorio /var/lib/mysql contiene bases de datos MySQL que van a "
+"eliminarse."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Debería mantener los datos si tiene planificado instalar una versión de "
+"MySQL más reciente o si hay un paquete «mysql-server» distinto que los está "
+"utilizando."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "¿Desea que el servidor MySQL se ejecute al iniciar el sistema?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"El servidor MySQL puede iniciarse en el momento de arranque del sistema o "
+"manualmente si escribe la orden «/etc/init.d/mysql start»."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nueva contraseña para el usuario «root» de MySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Se recomienda que configure una contraseña para el usuario "
+"«root» (administrador) de MySQL, aunque no es obligatorio."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "No se modificará la contraseña si deja el espacio en blanco."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Nueva contraseña para el usuario «root» de MySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "No se pudo fijar la contraseña para el usuario «root» de MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Se produjo un error mientras intentaba fijar la contraseña para el usuario "
+"administrador de MySQL. Esto puede haber sucedido porque la cuenta ya tenía "
+"una contraseña o porque se produjo un error de comunicación con el servidor "
+"MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Debería comprobar la contraseña de la cuenta después de la instalación del "
+"paquete."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Consulte /usr/share/doc/mysql-server-5.6/README.Debian para más información."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Se ha producido un error al introducir la contraseña"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"Las dos contraseñas que ha introducido son distintas. Intente de nuevo."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB Cluster parece estar en uso"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 ya no brinda soporte para NDB Cluster. Migre al nuevo paquete "
+"mysql-cluster-server y elimine todas las líneas que empiecen con \"ndb\" de "
+"todos los ficheros de configuración bajo /etc/mysql/."
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "Deben añadirse las siguientes entradas para usuarios y grupos en el "
+#~ "sistema para poder utilizar MySQL:"
+
+#~ msgid "Cannot upgrade if ISAM tables are present!"
+#~ msgstr "¡No se puede actualizar si ya hay tablas ISAM!"
+
+#~ msgid ""
+#~ "Recent versions of MySQL can no longer use the old ISAM table format and "
+#~ "it is necessary to convert your tables to e.g. MyISAM before upgrading by "
+#~ "using \"mysql_convert_table_format\" or \"ALTER TABLE x ENGINE=MyISAM\". "
+#~ "The installation of mysql-server-5.6 will now abort. In case your old "
+#~ "mysql-server-4.1 gets removed nevertheless just reinstall it to convert "
+#~ "those tables."
+#~ msgstr ""
+#~ "Las versiones recientes de MySQL ya no soportan el antiguo formato de "
+#~ "tabla ISAM. Antes de realizar la actualización es necesario convertir sus "
+#~ "tablas a por ejemplo, MyISAM, usando «mysql_convert_table_format» o "
+#~ "«ALTER TABLE x ENGINE=MyISAM». Se va a interrumpir ahora la instalación "
+#~ "de mysql-server-5.6. Si aún así su mysql-server-4.1 se elimina aún así, "
+#~ "puede reinstalarlo para convertir ese tipo de tablas."
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "¿Soportar las conexiones MySQL establecidadas desde sistemas que ejecutan "
+#~ "Debian Sarge o versiones anteriores?"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "No era muy segura la forma en la que se almacenaban las contraseñas en "
+#~ "versiones anteriores del cliente de MySQL en Debian. Este problema se ha "
+#~ "mejorado posteriormente con el inconveniente, sin embargo, de que "
+#~ "clientes (por ejemplo, PHP) en sistemas que ejecutan Debian 3.1 «Sarge» "
+#~ "no podrán conectarse a cuentas que son nuevas o a las que se le haya "
+#~ "cambiado la contraseña."
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "Para utilizar mysql debe instalar un usuario y grupo equivalente al "
+#~ "siguiente y asegurarse de que /var/lib/mysql tiene los permisos correctos "
+#~ "(los valores del «uid» y del «gid» pueden ser diferentes)."
+
+#~ msgid ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+#~ msgstr ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+
+#~ msgid "/etc/group: mysql:x:101:"
+#~ msgstr "/etc/group: mysql:x:101:"
+
+#~ msgid "/var/lib/mysql: drwxr-xr-x mysql mysql"
+#~ msgstr "/var/lib/mysql: drwxr-xr-x mysql mysql"
+
+#~ msgid "Remove the databases used by all MySQL versions?"
+#~ msgstr ""
+#~ "¿Eliminar las bases de datos utilizadas por todas las versiones de MySQL?"
+
+#~ msgid ""
+#~ "If you do not provide a password no changes will be made to the account."
+#~ msgstr ""
+#~ "No se hará ningún cambio en la cuenta si no introduce una contraseña."
+
+#~ msgid ""
+#~ "When installation finishes, you should verify that the account is "
+#~ "properly protected with a password (see README.Debian for more "
+#~ "information)."
+#~ msgstr ""
+#~ "Debería confirmar que la contraseña está correctamente protegida con una "
+#~ "contraseña cuando termine la instalación (consulte el fichero README."
+#~ "Debian si desea más información)."
+
+#~ msgid "Install Hints"
+#~ msgstr "Sugerencias para la instalación"
+
+#~ msgid ""
+#~ "On upgrades from MySQL 3.23, as shipped with Debian Woody, symlinks in "
+#~ "place of /var/lib/mysql or /var/log/mysql gets accidently removed and "
+#~ "have manually be restored."
+#~ msgstr ""
+#~ "Al actualizar a la versión de MySQL 3.23, la vrsión proporcionada en "
+#~ "Debian Woody, se eliminan de manera accidental, los enlaces simbólicos a "
+#~ "«/var/lib/mysql» o «/var/log/mysql» y tienen que restaurarse manualmente."
+
+#~ msgid ""
+#~ "MySQL will only install if you have a non-numeric hostname that is "
+#~ "resolvable via the /etc/hosts file. E.g. if the \"hostname\" command "
+#~ "returns \"myhostname\" then there must be a line like \"10.0.0.1 "
+#~ "myhostname\"."
+#~ msgstr ""
+#~ "Sólo se instalará MySQL si tiene un nombre de equipo que no sea una "
+#~ "dirección IP y pueda resolverse a través del fichero /etc/hosts. Por "
+#~ "ejemplo, si la orden «hostname» devuelve «MiNombreEquipo» entonces deberá "
+#~ "existir una línea «10.0.0.1 MiNombreEquipo» en dicho fichero."
+
+#~ msgid ""
+#~ "A new mysql user \"debian-sys-maint\" will be created. This mysql account "
+#~ "is used in the start/stop and cron scripts. Don't delete."
+#~ msgstr ""
+#~ "Se creará un nuevo usuario «debian-sys-maint». Esta cuenta de mysql se "
+#~ "utilizará en los scripts de inicio y parada y en los scripts «cron». No "
+#~ "la elimine."
+
+#~ msgid ""
+#~ "Please remember to set a PASSWORD for the MySQL root user! If you use a /"
+#~ "root/.my.cnf, always write the \"user\" and the \"password\" lines in "
+#~ "there, never only the password!"
+#~ msgstr ""
+#~ "¡Por favor, recuerde crear una CONTRASEÑA para el usuario «root» de "
+#~ "MySQL! ¡Si utiliza /root/.my.cnf debe escribir las líneas «user» y "
+#~ "«password» en dicho fichero, no incluya sólo la contraseña!"
+
+#~ msgid ""
+#~ "Should I remove the complete /var/lib/mysql directory tree which is used "
+#~ "by all MySQL versions, not necessarily only the one you are about to "
+#~ "purge?"
+#~ msgstr ""
+#~ "¿Debería eliminar el árbol de directorio /var/lib/mysql completo? Tenga "
+#~ "en cuenta que lo utilizan todas las versiones de MySQL y no sólo la que "
+#~ "está a punto de purgar."
--- /dev/null
+# translation of eu.po to Euskara
+# Piarres BEobide <pi@beobide.net>, 2006.
+# Piarres Beobide <pi@beobide.net>, 2009.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+msgid ""
+msgstr ""
+"Project-Id-Version: eu\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2009-07-29 11:59+0200\n"
+"Last-Translator: Piarres Beobide <pi@beobide.net>\n"
+"Language-Team: Euskara <debian-l10n-eu@lists.debian.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: KBabel 1.11.4\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Benetan bertsio zaharragora itzuli nahi duzu?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "Sisteman badago /var/lib/mysql/debian-*.flag izeneko fitxategi bat."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "Such file is an indication that a mysql-server package with a higher "
+#| "version has been installed earlier."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Fitxategi honek aurretik bertsio berriagoko mysql-zerbitzari pakete bat "
+"instalatu dela adierazten du."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Ezin da ziurtatu instalatzen ari zaren bertsio honek dauden datubaseak "
+"erabili ahal izango dituenik."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "NIS/YP erabiltzaileentzat ohar garrantzitsua"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid ""
+#| "You should also check the permissions and the owner of the /var/lib/mysql "
+#| "directory:"
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Honetaz gain /var/lib/mysql direktorioaren jabea eta baimenak egiaztatu "
+"beharko zenituzke:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Ezabatu MySQL datubase guztiak?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr "MySQL datubaseak dituen /var/lib/mysql direktorioa ezabatua izango da."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"MySQL paketea beranduago bertsio berriago bat instalatzeko kentzen ari "
+"bazara, edo beste mysql-server pakete bat berau erabiltzen ari bada, datuak "
+"mantendu egin beharko lirateke."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Abioan MySQL zerbitzaria abiarazi?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"Sistema abioan MySQL automatikoki abiarazi daiteke edo eskuz '/etc/init.d/"
+"mysql start' eginaz."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "MySQL \"root\" erabiltzailearen pasahitz berria:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Derrigorrezkoa ez denean, oso gomendagarria da MySQL administratzaile \"root"
+"\" erabiltzaileari pasahitz bat ezartzea."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "If that field is left blank, the password will not be changed."
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Eremua hau zurian utziaz gero ez da pasahitza aldatuko."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Errepikatu MySQL \"root\" erabiltzailearen pasahitza:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Ezin da MySQL \"root\" erabiltzailearen pasahitza ezarri"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Errore bat gertatu da MySQL administratzaile kontuaren pasahitza ezartzean. "
+"Hau erabiltzaileak dagoeneko pasahitz bat duelako edo MySQL "
+"zerbitzariarekiko konexioan erroreak daudelako gertatu daiteke."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Kontuaren pasahitza egiaztatu beharko zenuke paketea instalatu aurretik."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Mesedez irakurri /usr/share/doc/mysql-server-5.6/README.Debian fitxategia "
+"xehetasun gehiagorako."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Pasahitz sarrera errorea"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr "Idatzi dituzun bi pasahitzak ez dira berdina. Mesedez saiatu berriz."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "Dirudienez NDB Cluster-a erabilia dago"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+#, fuzzy
+#| msgid ""
+#| "MySQL-5.6 has orphaned NDB Cluster support. Please migrate to the new "
+#| "mysql-cluster package and remove all lines starting with \"ndb\" from all "
+#| "config files below /etc/mysql/."
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6-ek NDB cluster euskarri umezurtz bat behar du. Mesedez migratu "
+"mysql-cluster pakete berrira eta /etc/mysql/ azpiko konfigurazio fitxategi "
+"guztietan \"ndb\"-ez hasten diren lerro guztiak ezabatu."
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "MySQL erabili ahal izateko, hurrengo erabiltzaile eta taldeak gehitu "
+#~ "behar dira sisteman:"
--- /dev/null
+# Translation of mysql-dfsg-* debconf templates to French
+# Copyright (C) 2004-2009 Debian French l10n team <debian-l10n-french@lists.debian.org>
+# This file is distributed under the same license as the mysql-dfsg-* packages.
+#
+# Translators:
+# Christian Perrier <bubulle@debian.org>, 2004, 2006, 2007, 2009, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: fr\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-05-22 08:30+0200\n"
+"Last-Translator: Christian Perrier <bubulle@debian.org>\n"
+"Language-Team: French <debian-l10n-french@lists.debian.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"debian.org>\n"
+"X-Generator: Lokalize 1.2\n"
+"Plural-Forms: Plural-Forms: nplurals=2; plural=n>1;\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Faut-il vraiment revenir à la version précédente ?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "Un fichier /var/lib/mysql/debian-*.flag est présent sur ce système."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Cela indique qu'une version plus récente du paquet mysql-server a été "
+"précédemment installée."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr "Il n'est pas garanti que cette version puisse en utiliser les données."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Note importante pour les utilisateurs NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"L'utilisation de MySQL avec NIS/YP impose l'ajout d'un compte local "
+"« mysql » avec la commande :"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Vous devez également vérifier le propriétaire et les permissions du "
+"répertoire /var/lib/mysql :"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Faut-il supprimer toutes les bases de données MySQL ?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Le répertoire /var/lib/mysql qui contient les bases de données de MySQL va "
+"être supprimé."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Si vous prévoyez d'installer une version plus récente de MySQL ou si un "
+"autre paquet mysql-server les utilise déjà, vous devriez les conserver."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Faut-il lancer MySQL au démarrage ?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL peut être lancé soit au démarrage, soit en entrant la commande « /etc/"
+"init.d/mysql start »."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nouveau mot de passe du superutilisateur de MySQL :"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Il est très fortement recommandé d'établir un mot de passe pour le compte "
+"d'administration de MySQL (« root »)."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Si ce champ est laissé vide, le mot de passe ne sera pas changé."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Confirmation du mot de passe du superutilisateur de MySQL :"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr ""
+"Impossible de changer le mot de passe de l'utilisateur « root » de MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Une erreur s'est produite lors du changement de mot de passe du compte "
+"d'administration. Un mot de passe existait peut-être déjà ou il n'a pas été "
+"possible de communiquer avec le serveur MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Vous devriez vérifier le mot de passe de ce compte après l'installation du "
+"paquet."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Veuillez consulter le fichier /usr/share/doc/mysql-server-5.6/README.Debian "
+"pour plus d'informations."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Erreur de saisie du mot de passe"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"Le mot de passe et sa confirmation ne sont pas identiques. Veuillez "
+"recommencer."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "Abandon de la gestion de NDB"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"La version 5.6 de MySQL ne gère plus les grappes NDB. Vous devriez utiliser "
+"le paquet mysql-cluster-server et supprimer toutes les lignes commençant par "
+"« ndb » des fichiers de configuration situés dans /etc/mysql."
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "Pour pouvoir utiliser MySQL, les utilisateurs et les groupes suivants "
+#~ "doivent être ajoutés au système :"
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "Gérer les connexions d'hôtes qui utilisent les versions Debian « sarge » "
+#~ "ou antérieures ?"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "La méthode de stockage des mots de passe n'était pas très sûre dans les "
+#~ "version précédentes de ce paquet. Cette méthode a été améliorée mais les "
+#~ "modifications empêchent la connexion avec de nouveaux comptes ou des "
+#~ "comptes dont le mot de passe a été modifié, pour les clients (p. ex. PHP) "
+#~ "depuis des hôtes qui utilisent Debian 3.1 « sarge »."
--- /dev/null
+# Galician translation of mysql-dfsg-5.6's debconf templates
+# This file is distributed under the same license as the mysql-dfsg-5.6 package.
+# Jacobo Tarrio <jtarrio@debian.org>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2007-04-20 09:44+0200\n"
+"Last-Translator: Jacobo Tarrio <jtarrio@debian.org>\n"
+"Language-Team: Galician <proxecto@trasno.net>\n"
+"Language: gl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "¿Quere pasar a unha versión anterior?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "Neste sistema hai un ficheiro chamado /var/lib/mysql/debian-*.flag."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "Such file is an indication that a mysql-server package with a higher "
+#| "version has been installed earlier."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Este ficheiro indica que antes se instalou un paquete mysql-server cunha "
+"versión superior."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Non se pode garantir que a versión que está a instalar poida empregar as "
+"bases de datos actuais."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Nota importante para os usuarios de NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid ""
+#| "You should also check the permissions and the owner of the /var/lib/mysql "
+#| "directory:"
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Tamén debería comprobar os permisos e o propietario do directorio /var/lib/"
+"mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "¿Eliminar tódalas bases de datos de MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Hase eliminar o directorio /var/lib/mysql, que contén as bases de datos de "
+"MySQL."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Se está a eliminar o paquete MySQL para instalar despois unha versión máis "
+"recente ou se xa hai un paquete mysql-server diferente a empregalo, debería "
+"conservar os datos."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "¿Iniciar o servidor MySQL co ordenador?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"Pódese iniciar automaticamente o servidor MySQL ao iniciar o ordenador, ou "
+"manualmente coa orde \"/etc/init.d/mysql start\"."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Novo contrasinal para o usuario \"root\" de MySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Aínda que non é obrigatorio, recoméndase encarecidamente que estableza un "
+"contrasinal para o usuario administrativo \"root\" de MySQL."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "If that field is left blank, the password will not be changed."
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Se deixa o campo en branco, non se ha cambiar o contrasinal."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+#, fuzzy
+#| msgid "New password for the MySQL \"root\" user:"
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Novo contrasinal para o usuario \"root\" de MySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Non se puido establecer o contrasinal do usuario \"root\" de MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Houbo un erro ao establecer o contrasinal do usuario administrativo de "
+"MySQL. Puido ocorrer porque o usuario xa teña un contrasinal ou debido a un "
+"problema de comunicacións co servidor MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "You should check the account's password after tha package installation."
+msgid "You should check the account's password after the package installation."
+msgstr "Debería comprobar o contrasinal da conta trala instalación do paquete."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Consulte o ficheiro /usr/share/doc/mysql-server-5.6/README.Debian para máis "
+"información."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "Para empregar MySQL deberían engadirse ao sistema as seguintes entradas "
+#~ "de usuarios e grupos:"
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "¿Soportar as conexións a MySQL de máquinas que empreguen Debian \"sarge\" "
+#~ "ou anterior?"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "Nas versións antigas dos clientes MySQL de Debian, os contrasinais non se "
+#~ "armacenaban de xeito seguro. Isto mellorouse desde aquela; nembargantes, "
+#~ "os clientes (tales coma PHP) das máquinas que executen Debian 3.1 Sarge "
+#~ "non se han poder conectar a contas recentes ou a contas nas que se "
+#~ "cambiara o contrasinal."
--- /dev/null
+# Italian (it) translation of debconf templates for mysql-dfsg-5.6\r
+# Copyright (C) 2009 Software in the Public Interest\r
+# This file is distributed under the same license as the mysql-dfsg-5.6 package.\r
+# Luca Monducci <luca.mo@tiscali.it>, 2006 - 2009.\r
+# \r
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6 5.6.8 italian debconf templates\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2009-08-08 11:03+0200\n"
+"Last-Translator: Luca Monducci <luca.mo@tiscali.it>\n"
+"Language-Team: Italian <debian-l10n-italian@lists.debian.org>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Procedere realmente con l'abbassamento di versione?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+"Su questo sistema esiste un file con nome /var/lib/mysql/debian-*.flag."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Quel file indica che in precedenza è stata installata una versione superiore "
+"del pacchetto mysql-server."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Non è garantito che la versione che si sta installando sia in grado di usare "
+"i database presenti."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Nota importante per gli utenti NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Per usare MySQL con NIS/YP è necessario aggiungere al sistema locale un "
+"account utente per mysql con:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Inoltre si devono verificare i permessi e la proprietà della directory /var/"
+"lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Eliminare tutti i database MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"La directory /var/lib/mysql contenente i database di MySQL sta per essere "
+"eliminata."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Se si rimuove il pacchetto MySQL per poi installare una versione più recente "
+"oppure se sono già in uso da un altro pacchetto mysql-server, i dati non "
+"devono essere eliminati."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Lanciare il server MySQL all'avvio?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"Il server MySQL può essere lanciato automaticamente all'avvio del sistema "
+"oppure manualmente con il comando «/etc/init.d/mysql start»."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nuova password per l'utente «root» di MySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Sebbene non sia obbligatoria, si raccomanda d'impostare una password per "
+"l'utente d'amministrazione «root» di MySQL."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Se questo campo è lasciato vuoto, la password non viene cambiata."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Ripetere la password per l'utente «root» di MySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Impossibile impostare la password per l'utente «root» di MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Si è verificato un errore durante l'impostazione della password per l'utente "
+"d'amministrazione di MySQL. Questo può essere accaduto perché l'utente ha "
+"già una password oppure a causa di un problema di connessione con il server "
+"MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Al termine dell'installazione si deve verificare la password dell'account."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Per maggiori informazioni si consulti il file /usr/share/doc/mysql-"
+"server-5.6/README.Debian."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Errore di inserimento della password"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr "Le due password inserite sono diverse. Riprovare."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "È in uso un cluster NDB"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+#, fuzzy
+#| msgid ""
+#| "MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the "
+#| "new mysql-cluster package and remove all lines starting with \"ndb\" from "
+#| "all config files below /etc/mysql/."
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 non fornisce più il supporto per i cluster NDB. Si dovrebbe "
+"migrare al nuovo pacchetto mysql-cluster e rimuovere tutte le righe che "
+"iniziano per \"ndb\" da tutti i file di configurazione sotto /etc/mysql/."
--- /dev/null
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6 5.6.8-1\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2009-09-01 08:25+0900\n"
+"Last-Translator: Hideki Yamane (Debian-JP) <henrich@debian.or.jp>\n"
+"Language-Team: Japanese <debian-japanese@lists.debian.org>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "本当にダウングレードを実行しますか?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+"このシステムには /var/lib/mysql/debian-*.flag という名前のファイルが存在して"
+"います。"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"このファイルが意味するのは、以前により新しいバージョンの mysql-server パッ"
+"ケージがインストールされていたことを示します。"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"このデータベースを現在インストールしようとしているバージョンで使えるかどうか"
+"は保証できません。"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "NIS/YP ユーザへの重要な注意"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"NIS/YP 配下で MySQL を使うにはローカルのシステムに mysql のユーザアカウントを"
+"追加するのが必要です。"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr "/var/lib/mysql の所有者権限をチェックする必要もあります。"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "すべての MySQL データベースを削除しますか?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"MySQL データベースを含んでいるディレクトリ /var/lib/mysql を削除しようとして"
+"います。"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"後でより新しいバージョンのものをインストールしようとするために MySQL パッケー"
+"ジを削除しようとしている、あるいは別の mysql-server パッケージを既に使ってい"
+"る場合、データは保持する必要があります。"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "MySQL をシステム起動時に開始しますか?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL の起動方法について、システム起動時に自動的に開始するか、あるいは '/etc/"
+"init.d/mysql start' と手で入力して起動するかを選べます。"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "MySQL の \"root\" ユーザに対する新しいパスワード:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"強制ではありませんが、MySQL を管理する \"root\" ユーザのパスワードを設定する"
+"ことを強くお勧めします。"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "この値を空のままにしておいた場合は、パスワードは変更されません。"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "MySQL の \"root\" ユーザに対する新しいパスワード:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "MySQL の \"root\" ユーザのパスワードを設定できません"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"MySQL の管理者ユーザに対してパスワードを設定しようとした際、エラーが発生しま"
+"した。これは既に管理者ユーザにパスワードが設定されていたか、MySQL サーバとの"
+"接続に問題があったためだと思われます。"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"パッケージのインストール後、アカウントのパスワードを確認する必要があります。"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"詳細は /usr/share/doc/mysql-server-5.6/README.Debian を参照してください。"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "パスワード入力エラー"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr "入力された二つのパスワードが一致しません。再入力してください。"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB クラスタが利用されているようです"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+#, fuzzy
+#| msgid ""
+#| "MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the "
+#| "new mysql-cluster package and remove all lines starting with \"ndb\" from "
+#| "all config files below /etc/mysql/."
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 では NDB クラスタのサポートを提供しなくなっています。新たな mysql-"
+"cluster パッケージに移行して、/etc/mysql 以下の設定ファイルすべてから「ndb」"
+"で始まる行を削除してください。"
--- /dev/null
+# translation of mysql_nb.po to Norwegian Bokmål
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Bjørn Steensrud <bjornst@powertech.no>, 2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql_nb\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2007-02-18 12:13+0100\n"
+"Last-Translator: Bjørn Steensrud <bjornst@powertech.no>\n"
+"Language-Team: Norwegian Bokmål <i18n-nb@lister.ping.uio.no>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.2\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid "Do you really want to downgrade?"
+msgid "Really proceed with downgrade?"
+msgstr "Er du sikker på at du vil nedgradere?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "WARNING: The file /var/lib/mysql/debian-*.flag exists. This indicates "
+#| "that a mysql-server package with a higher version has been installed "
+#| "before. It can not be guaranteed that this version can use its data."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"ADVARSEL: Fila /var/lib/mysql/debian-*.flag finnes. Dette viser at en mysql-"
+"server-pakke med et høyere versjonsnummer har vært installert før. Det kan "
+"ikke garanteres at denne versjonen kan bruke data fra den høyere versjonen."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid "Important note for NIS/YP users!"
+msgid "Important note for NIS/YP users"
+msgstr "Viktig merknad for NIS/YP-brukere!"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+#, fuzzy
+#| msgid ""
+#| "The script is about to remove the data directory /var/lib/mysql. If it is "
+#| "planned to just install a higher MySQL version or if a different mysql-"
+#| "server package is already using it, the data should be kept."
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Dette skriptet skal til å fjerne data-mappa /var/lib/mysql. Denne mappa bør "
+"beholdes hvis det bare skal installeres en høyere MySQL-versjon, eller hvis "
+"en annen mysql-server-pakke allerede bruker den."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+#| msgid "Should MySQL start on boot?"
+msgid "Start the MySQL server on boot?"
+msgstr "Skal MySQL startes ved maskinoppstart?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+#| msgid ""
+#| "The MySQL can start automatically on boot time or only if you manually "
+#| "type '/etc/init.d/mysql start'."
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL kan startes automatisk når maskinen starter, eller bare hvis du "
+"skriver «/etc/init.d/mysql start»."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "New password for MySQL \"root\" user:"
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nytt passord for MySQLs «root»-bruker:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid ""
+#| "It is highly recommended that you set a password for the MySQL "
+#| "administrative \"root\" user."
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Det anbefales sterkt at du oppgir et passord for den administrative «root»-"
+"brukeren i MySQl."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+#, fuzzy
+#| msgid "New password for MySQL \"root\" user:"
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Nytt passord for MySQLs «root»-bruker:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid "Unable to set password for MySQL \"root\" user"
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Klarer ikke angi passord for MySQLs «root»-bruker"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "It seems an error occurred while setting the password for the MySQL "
+#| "administrative user. This may have happened because the user already has "
+#| "a password, or because there was a problem communicating with the MySQL "
+#| "server."
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Det ser ut til at det oppsto en feil mens det ble satt et passord for MySQLs "
+"administrative bruker. Dette kan være fordi brukeren allerede har et "
+"passord, eller fordi det var et kommunikasjonsproblem med MySQL-tjeneren."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "Skal MySQL-tilkoblinger støttes fra vertsmaskiner som kjører Debian "
+#~ "«sarge» eller eldre?"
+
+#, fuzzy
+#~| msgid ""
+#~| "The way passwords were stored was not very secure. This has been "
+#~| "improved with the drawback that clients (e.g. PHP) from hosts running "
+#~| "Debian 3.1 Sarge will not be able to connect to account which are new or "
+#~| "whose password have been changed. See /usr/share/doc/mysql-server-5.6/"
+#~| "README.Debian."
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "Passord ble tidligere lagret på en lite sikker måte. Dette er nå "
+#~ "forbedret, med den ulempen at klienter (f.eks. PHP) fra verter som kjører "
+#~ "Debian 3.1 Sarge ikke vil kunne koble til en konto som er ny eller har "
+#~ "fått endret passordet. Se /usr/share/doc/mysql-server-5.6/README.Debian."
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "For å bruke MySQL må du installere en bruker og gruppe tilsvarende den "
+#~ "nedenfor og se til at /var/lib/mysql har riktige rettigheter (uid/gid kan "
+#~ "være forskjellig)."
+
+#~ msgid ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+#~ msgstr ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+
+#~ msgid "/etc/group: mysql:x:101:"
+#~ msgstr "/etc/group: mysql:x:101:"
+
+#~ msgid "/var/lib/mysql: drwxr-xr-x mysql mysql"
+#~ msgstr "/var/lib/mysql: drwxr-xr-x mysql mysql"
+
+#~ msgid "Remove the databases used by all MySQL versions?"
+#~ msgstr "Skal databasene brukt av alle MySQL-versjoner fjernes?"
+
+#~ msgid ""
+#~ "If you do not provide a password no changes will be made to the account."
+#~ msgstr ""
+#~ "Hvis du ikke oppgir et passord blir det ikke gjort noen endringer med "
+#~ "kontoen."
+
+#~ msgid ""
+#~ "When installation finishes, you should verify that the account is "
+#~ "properly protected with a password (see README.Debian for more "
+#~ "information)."
+#~ msgstr ""
+#~ "Når installasjonen er ferdig bør det sjekkes at kontoen er ordentlig "
+#~ "beskyttet med et passord (mer informasjon finnes i README.Debian)."
--- /dev/null
+# Dutch mysql-dfsg-5.6 po-debconf translation,
+# Copyright (C) 2006 THE PACKAGE'S COPYRIGHT HOLDER
+#
+# Vincent Zweije <zweije@xs4all.nl>, 2006.
+# Eric Spreen <erispre@gmail.com, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-5.6 5.6.6-1\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-05-23 10:26+0200\n"
+"Last-Translator: Jeroen Schot <schot@a-eskwadraat.nl>\n"
+"Language-Team: Debian l10n Dutch <debian-l10n-dutch@lists.debian.org>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Wilt u echt een oude versie herstellen?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+"Er bestaat een bestand genaamd /var/lib/mysql/debian-*.flag op dit systeem."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Zulk een bestand geeft aan dat er eerder een pakket mysql-server met een "
+"hogere versie is geïnstalleerd."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Er is geen garantie dat de versie die u op dit moment installeert de huidige "
+"databases kan gebruiken."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Belangrijke opmerking voor gebruikers van NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Het gebruik van MySQL onder NIS/YP vereist dat een MySQL gebruikersaccount "
+"wordt toegevoegd aan het lokale systeem met:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"U dient ook de permissies en eigenaren van de map /var/lib/mysql te "
+"controleren:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Wilt u alle MySQL-databases verwijderen?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"De map /var/lib/mysql die de MySQL-databases bevat staat op het punt om "
+"verwijderd te worden."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Als u het MySQL-pakket verwijdert om later een meer recente versie te "
+"installeren of als een ander mysql-server pakket het al gebruikt, zou de "
+"data behouden moeten worden."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Moet MySQL starten als de computer start?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"De MySQL-server kan automatisch worden gestart bij het starten van de "
+"computer of slechts wanneer u '/etc/init.d/mysql start' handmatig uitvoert."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nieuw wachtwoord voor de MySQL \"root\"-gebruiker:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Hoewel niet verplicht, wordt het sterk aangeraden een wachtwoord in te "
+"stellen voor de administratieve MySQL \"root\"-gebruiker."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+"Als dit veld leeg wordt gelaten, zal het wachtwoord niet worden veranderd."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Herhaal het wachtwoord voor de MySQL \"root\"-gebruiker:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Kan het wachtwoord voor de MySQL \"root\"-gebruiker niet instellen"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Er is een fout opgetreden bij het instellen van het wachtwoord voor de MySQL "
+"administratieve gebruiker. Dat kan komen doordat de gebruiker al een "
+"wachtwoord heeft, of omdat er een probleem was bij het communiceren met de "
+"MySQL-server."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"U zou het wachtwoord van het account moeten controleren nadat het pakket is "
+"geïnstalleerd."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Leest u het bestand /usr/share/doc/mysql-server-5.6/README.Debian voor meer "
+"informatie."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Fout bij invoer wachtwoord"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"De twee wachtwoorden die u hebt ingevoerd zijn niet gelijk. Probeert u het "
+"opnieuw."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "De NDB-cluster lijkt in gebruik te zijn"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 biedt niet langer ondersteuning voor NDB Cluster. Migreer naar het "
+"nieuwe pakket mysql-cluster en verwijder alle regels die beginnen met \"ndb"
+"\" van alle configuratiebestanden onder /etc/mysql/."
--- /dev/null
+# Portuguese translation for mysql-dfsg-5.6's debconf messages
+# Copyright (C) 2006 Miguel Figueiredo <elmig@debianpt.org>
+# This file is distributed under the same license as the mysql-dfsg-5.6 package.
+# Miguel Figueiredo <elmig@debianpt.org>, 2012
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-05-28 22:40+0100\n"
+"Last-Translator: Miguel Figueiredo <elmig@debianpt.org>\n"
+"Language-Team: Portuguese <traduz@debianpt.org>\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Deseja mesmo fazer downgrade?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+"Existe, neste sistema, um ficheiro chamado /var/lib/mysql/debian-*.flag."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"A existência de tal ficheiro é um indicador que anteriormente foi instalado "
+"um pacote mysql-server com um número de versão superior."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Não existe nenhuma garantia que a versão que está actualmente a instalar "
+"seja capaz de utilizar as bases de dados actuais."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Nota importante para utilizadores de NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Utilizar MySQL com NIS/YP necessita que seja adicionada uma conta de "
+"utilizador de mysql ao sistema local com:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Deve também verificar as permissões e o dono do directório /var/lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Remover todas as bases de dados MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"O directório /var/lib/mysql que contém as bases de dados MySQL está prestes "
+"a ser removido."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Se está a remover o pacote MySQL de modo a posteriormente instalar uma "
+"versão mais recente ou se um pacote mysql-server já está os está a utilizar, "
+"os dados devem ser mantidos."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Iniciar o servidor MySQL no arranque?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"O MySQL pode ser automaticamente lançado no arranque ou manualmente através "
+"do comando '/etc/init.d/mysql start'."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nova palavra-passe para o utilizador \"root\" do MySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Embora não seja obrigatório, é fortemente recomendado que defina uma palavra-"
+"passe para o utilizador administrativo \"root\" do MySQL."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+"Se este campo for deixado em branco, a palavra-passe não irá ser alterada."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Repita a palavra-passe para o utilizador \"root\" de MySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr ""
+"Não foi possível definir a palavra-passe para o utilizador \"root\" do MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Ocorreu um erro enquanto era definida a palavra-passe para o utilizador "
+"administrativo do MySQL. Isto pode ter acontecido porque a conta já tem uma "
+"palavra-passe, ou porque ocorreu um problema ao comunicação com o servidor "
+"MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+"Você deve verificar a palavra-passe da conta após a instalação do pacote."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Para mais informações, por favor leia o ficheiro /usr/share/doc/mysql-"
+"server-5.6/README.Debian."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Erro de entrada da palavra-passe"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"As duas palavras-passe que introduziu não são as mesmas. Por favor tente "
+"novamente."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB Cluster parece estar a ser utilizado"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 já não disponibiliza suporte para NDB Cluster. Por favor migre "
+"para o novo pacote mysql-cluster-server e remova todas as linhas que comecem "
+"por \"ndb\" em todos os ficheiros de configuração em /etc/mysql/."
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "Para utilizar o MySQL, têm de ser acrescentadas as seguintes entradas "
+#~ "para os utilizadores e grupos:"
+
+#~ msgid "Cannot upgrade if ISAM tables are present!"
+#~ msgstr "Não é possível actualizar se estiverem presentes tabelas ISAM!"
+
+#~ msgid ""
+#~ "Recent versions of MySQL can no longer use the old ISAM table format and "
+#~ "it is necessary to convert your tables to e.g. MyISAM before upgrading by "
+#~ "using \"mysql_convert_table_format\" or \"ALTER TABLE x ENGINE=MyISAM\". "
+#~ "The installation of mysql-server-5.6 will now abort. In case your old "
+#~ "mysql-server-4.1 gets removed nevertheless just reinstall it to convert "
+#~ "those tables."
+#~ msgstr ""
+#~ "As versões recentes de MySQL já não podem utilizar o antigo formato de "
+#~ "tabelas ISAM e é por isso necessário converter as suas tabelas pra e.g. "
+#~ "MyISAM antes da actualização, utilizando \"mysql_convert_table_format\" "
+#~ "ou \"ALTER TABLE x ENGINE=MyISAM\". A instalação de mysql-server-5.6 irá "
+#~ "agora ser cancelada. Se o seu antigo mysql-server-4.1 for removido apenas "
+#~ "reinstale para converter essas tabelas."
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "Suportar ligações MySQL de máquinas que corram Debian \"sarge\" ou mais "
+#~ "antigos?"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "Nas versões antigas de clientes de MySQL em Debian, as palavras-passe não "
+#~ "eram guardadas de forma segura. Isto foi melhorado desde aí, no entanto "
+#~ "os clientes (como o PHP) de máquinas que corram Debian 3.1 Sarge não irão "
+#~ "conseguir ligar-se a contas novas ou cuja palavra-passe foi alterada."
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "Para utilizar mysql e instalar um utilizador e grupo equivalentes para o "
+#~ "seguinte e assegurar-se que /var/lib/mysql têm as permissões correctas (o "
+#~ "uid/gid podem ser diferentes)."
+
+#~ msgid ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+#~ msgstr ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+
+#~ msgid "/etc/group: mysql:x:101:"
+#~ msgstr "/etc/group: mysql:x:101:"
+
+#~ msgid "/var/lib/mysql: drwxr-xr-x mysql mysql"
+#~ msgstr "/var/lib/mysql: drwxr-xr-x mysql mysql"
+
+#~ msgid "Remove the databases used by all MySQL versions?"
+#~ msgstr "Remover as bases de dados utilizadas por todas as versões de MySQL?"
+
+#~ msgid ""
+#~ "If you do not provide a password no changes will be made to the account."
+#~ msgstr ""
+#~ "Se não disponibilizar uma password não serão feitas alterações nesta "
+#~ "conta."
+
+#~ msgid ""
+#~ "When installation finishes, you should verify that the account is "
+#~ "properly protected with a password (see README.Debian for more "
+#~ "information)."
+#~ msgstr ""
+#~ "Quando terminar a instalação, deve verificar se a conta está devidamente "
+#~ "protegida com uma password (para mais informações veja README.Debian)."
--- /dev/null
+# Brazilian Portuguese (pt_BR) debconf template translation for
+# Debian's mysql-dfsg source package.
+# Debian-BR Project <debian-l10n-portuguese@lists.debian.org>
+# André Luís Lopes, <andrelop@debian.org> , 2004
+# André Luís Lopes, <andrelop@debian.org> , 2006
+# André Luís Lopes, <andrelop@debian.org> , 2007
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2007-04-21 15:59-0300\n"
+"Last-Translator: André Luís Lopes <andrelop@debian.org>\n"
+"Language-Team: Debian-BR Project <debian-l10n-portuguese@lists.debian.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"pt_BR utf-8\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Realmente proceder com o rebaixamento de versão?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "Um arquivo de nome /var/lib/mysql/debian-*.flag existe no sistema."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "Such file is an indication that a mysql-server package with a higher "
+#| "version has been installed earlier."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"A presença de um arquivo como este é uma indicação de que um pacote mysql-"
+"server com um número de versão mais alto já foi instalado anteriormente."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Não há garantias de que a versão que você está instalando no momento "
+"conseguirá utilizar as bases de dados existentes."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Aviso importante para usuários NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid ""
+#| "You should also check the permissions and the owner of the /var/lib/mysql "
+#| "directory:"
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Você deverá também checar as permissões e o dono do diretório /var/lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Remover todas as bases de dados do MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"O diretório /var/lib/mysql, o qual contém as bases de dados do MySQL, está "
+"prestes a ser removido."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Caso você esteja removendo o pacote MySQL para posteriormente instalar uma "
+"versão mais recente ou, caso uma versão diferente do pacote mysql-server "
+"esteja sendo utilizada, os dados deverão ser mantidos."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Iniciar o servidor MySQL junto a inicialização da máquina?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"O servidor MySQL pode ser iniciado automaticamente junto a inicialização da "
+"máquina ou manualmente com o comando '/etc/init.d/mysql start'."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nova senha para o usuário \"root\" do MySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Apesar de não ser mandatório, é altamente recomendado que você defina uma "
+"senha para o usuário administrativo \"root\" do MySQL."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "If that field is left blank, the password will not be changed."
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Caso este campo seja deixado em branco, a senha não sera mudada."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+#, fuzzy
+#| msgid "New password for the MySQL \"root\" user:"
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Nova senha para o usuário \"root\" do MySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Impossível definir senha para o usuário \"root\" do MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Um erro ocorreu durante a definição da senha para o usuário administrativo "
+"do MySQL. Isso pode ter acontecido devido a esse usuário já possuir uma "
+"senha definida ou devido a ocorrência de um problema de comunicação com o "
+"servidor MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "You should check the account's password after tha package installation."
+msgid "You should check the account's password after the package installation."
+msgstr "Você deverá checar a senha dessa conta após a instalação deste pacote."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Por favor, leia o arquivo /usr/share/doc/mysql-server-5.6/README.Debian para "
+"maiores informações."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "Para utilizar o MySQL, as seguintes entradas para usuários e grupos devem "
+#~ "ser adicionadas ao sistema:"
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "Suportar conexões MySQL originadas de hosts executando o Debian \"sarge\" "
+#~ "ou mais antigos ?"
+
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "Em versões antigas dos clientes MySQL no Debian, as senhas não eram "
+#~ "armazenadas de forma segura. Isto foi corrigido desde então, porém, "
+#~ "clientes (como o PHP) em hosts executando o Debian 3.1 Sarge não serão "
+#~ "capazes de conectar em contas recentes ou contas as quais as senhas "
+#~ "tenham sido modificadas."
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "Para utilizar o MySQL, você deve instalar um usuário e um grupo "
+#~ "equivalentes ao usuário e grupo a seguir para se certificar de que o "
+#~ "diretório /var/lib/mysql possua as permissões correctas (o uid/gid podem "
+#~ "ser diferentes)."
+
+#~ msgid ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+#~ msgstr ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+
+#~ msgid "/etc/group: mysql:x:101:"
+#~ msgstr "/etc/group: mysql:x:101:"
+
+#~ msgid "/var/lib/mysql: drwxr-xr-x mysql mysql"
+#~ msgstr "/var/lib/mysql: drwxr-xr-x mysql mysql"
+
+#~ msgid "Remove the databases used by all MySQL versions?"
+#~ msgstr "Remover as bases de dados utilizadas por todas as versões do MySQL?"
+
+#~ msgid ""
+#~ "If you do not provide a password no changes will be made to the account."
+#~ msgstr ""
+#~ "Caso você não forneça uma senha, nenhuma mudança será feita na conta."
+
+#~ msgid ""
+#~ "When installation finishes, you should verify that the account is "
+#~ "properly protected with a password (see README.Debian for more "
+#~ "information)."
+#~ msgstr ""
+#~ "Quando a instalação finalizar, você deverá verificar se a conta está "
+#~ "apropriadamente protegida com uma senha (consulte o arquivo README.Debian "
+#~ "para maiores informações)."
+
+#~ msgid "internal"
+#~ msgstr "interno"
+
+#~ msgid "Only internally used."
+#~ msgstr "Somente utilizado internamente."
+
+#, fuzzy
+#~ msgid "Update Hints"
+#~ msgstr "Dicas de atualização"
+
+#, fuzzy
+#~ msgid ""
+#~ "Rarely, e.g. on new major versions, the privilege system is improved. To "
+#~ "make use of it mysql_fix_privilege_tables must be executed manually. The "
+#~ "script is not supposed to give any user more rights that he had before,"
+#~ msgstr ""
+#~ "Raramente, por exemplo, em novas versões maiores, o sistema de "
+#~ "privilégios é melhorado. Para fazer uso disso, o script "
+#~ "mysql_fix_privilege_tables deve ser executado manualmente. O script não "
+#~ "atribuirá a nenhum usuário mais direitos do que os mesmos já possuíam "
+#~ "anteriormente."
+
+#~ msgid "Please also read http://www.mysql.com/doc/en/Upgrade.html"
+#~ msgstr "Por favor, leia http://www.mysql.com/doc/en/Upgrade.html"
+
+#, fuzzy
+#~ msgid "Install Hints"
+#~ msgstr "Dicas de instalação"
+
+#, fuzzy
+#~ msgid ""
+#~ "MySQL will only install if you have a non-numeric hostname that is "
+#~ "resolvable via the /etc/hosts file. E.g. if the \"hostname\" command "
+#~ "returns \"myhostname\" then there must be a line like \"10.0.0.1 "
+#~ "myhostname\"."
+#~ msgstr ""
+#~ "O MySQL será instalado somente caso você possua um nome de host NÃO "
+#~ "NUMÉRICO que possa ser resolvido através do arquivo /etc/hosts, ou seja, "
+#~ "caso o comando \"hostname\" retorne \"myhostname\", uma linha como "
+#~ "\"10.0.0.1 myhostname\" deverá existir no arquivo /etc/hosts."
+
+#~ msgid ""
+#~ "A new mysql user \"debian-sys-maint\" will be created. This mysql account "
+#~ "is used in the start/stop and cron scripts. Don't delete."
+#~ msgstr ""
+#~ "Um novo usuário MySQL de nome \"debian-sys-maint\" será criado. Essa "
+#~ "conta MySQL é utilizada pelos scripts de inicialização/parada e pelos "
+#~ "scripts cron. Não remova esse usuário."
+
+#, fuzzy
+#~ msgid ""
+#~ "Please remember to set a PASSWORD for the MySQL root user! If you use a /"
+#~ "root/.my.cnf, always write the \"user\" and the \"password\" lines in "
+#~ "there, never only the password!"
+#~ msgstr ""
+#~ "Por favor, lembre-se de definir uma SENHA para o usuário root do MySQL ! "
+#~ "Caso você utilize um arquivo /root/.my.cnf, sempre inclua as linhas \"user"
+#~ "\" e \"password\" nesse arquivo, nunca somente a senha ! Consulte o "
+#~ "arquivo /usr/share/doc/mysql-server/README.Debian para mais informações."
+
+#~ msgid ""
+#~ "Should I remove all databases below /var/lib/mysql as you are purging the "
+#~ "mysql-server package?"
+#~ msgstr ""
+#~ "Todas as base de dados sob o diretório /var/lib/mysql devem ser removidas "
+#~ "quando você remover o pacote pacote mysql-server ?"
+
+#~ msgid ""
+#~ "Networking is disabled by default for security reasons. You can enable it "
+#~ "by commenting out the skip-networking option in /etc/mysql/my.cnf."
+#~ msgstr ""
+#~ "O suporte ao funcionamento em rede está desativado por padrão por "
+#~ "questões de segurança. Você poderá ativá-lo comentando a opção 'skip-"
+#~ "networking' no arquivo /etc/mysql/my.cnf."
+
+#~ msgid "security and update notice"
+#~ msgstr "aviso de segurança e actualização"
+
+#~ msgid ""
+#~ "Should I remove everything below /var/lib/mysql when you purge the mysql-"
+#~ "server package with the \"dpkg --purge mysql-server\" command (i.e. "
+#~ "remove everything including the configuration) somewhen? (default is not)"
+#~ msgstr ""
+#~ "Devo remover tudo abaixo de /var/lib/mysql quando fizer o purge do pacote "
+#~ "mysql-server com o comando \"dpkg --purge mysql-server\" (ou seja, "
+#~ "remover tudo incluíndo a configuração)? (o padrão é não remover)"
+
+#~ msgid "Make MySQL reachable via network?"
+#~ msgstr "Fazer com que o MySQL seja acessível via rede?"
+
+#~ msgid ""
+#~ "Should MySQL listen on a network reachable TCP port? This is not "
+#~ "necessary for use on a single computer and could be a security problem."
+#~ msgstr ""
+#~ "O MySQL deve aguardar ligações numa porta TCP acessível via rede? Isto "
+#~ "não é necessário para uso num único computador e pode ser um problema de "
+#~ "segurança."
+
+#~ msgid "Enable chroot mode?"
+#~ msgstr "Activar o modo chroot?"
+
+#~ msgid ""
+#~ "MySQL is able to jail itself into the /var/lib/mysql_jail directory so "
+#~ "that users cannot modify any files outside this directory. This improves "
+#~ "resistence against crackers, too, as they are not able to modify system "
+#~ "files."
+#~ msgstr ""
+#~ "O MySQL é capaz de se prender no diretório /var/lib/mysql_jail, assim os "
+#~ "utilizadores não poderão modificar ficheiros fora deste directório. Isto "
+#~ "aumenta também a resistência contra crackers, pois eles não poderão "
+#~ "modificar arquivos de sistema."
+
+#~ msgid "Please run mysql_fix_privilege_tables !"
+#~ msgstr "Por favor execute mysql_fix_privilege_tables !"
+
+#~ msgid ""
+#~ "I will ensure secure permissions of /var/lib/mysql by replacing GIDs "
+#~ "other than root and mysql with mysql."
+#~ msgstr ""
+#~ "Permissões seguras para o diretório /var/lib/mysql serão asseguradas "
+#~ "substituíndo GIDs diferentes de root e mysql por mysql."
+
+#~ msgid ""
+#~ "Instructions how to enable SSL support are in /usr/share/doc/mysql-server/"
+#~ msgstr ""
+#~ "Instruções sobre como activar o suporte de SSL estão disponíveis no "
+#~ "directório /usr/share/doc/mysql-server/."
+
+#, fuzzy
+#~ msgid "mysql_fix_privileges_tables should be executed"
+#~ msgstr "mysql_fix_privileges_tables será executado"
+
+#, fuzzy
+#~ msgid ""
+#~ "The latest MySQL versions have an enhanced, more fine grained, privilege "
+#~ "system. To make use of it, some new fields must be added to the tables "
+#~ "in the \"mysql\" database. This will not happen automatically."
+#~ msgstr ""
+#~ "As últimas versões do MySQL possuem um sistema de privilégios melhorado e "
+#~ "mais refinado. Para utilizá-lo, alguns novos campos devem ser adicionados "
+#~ "as tabelas na base de dados \"mysql\". Isto é feito pelo script "
+#~ "mysql_fix_privileges_tables durante esta actualização independente do "
+#~ "servidor estar a correr ou não !"
+
+#~ msgid ""
+#~ "This script is not supposed to give any user more rights that he had "
+#~ "before, if you encounter such a case, please contact me."
+#~ msgstr ""
+#~ "Este script não deverá fornecer mais direitos a um utilizador além dos "
+#~ "quais ele já possua anteriormente. SE encontrar um caso desses, por favor "
+#~ "entre em contacto com o mantainer deste pacote Debian."
--- /dev/null
+# Romanian translation of mysql-dfsg.
+# Copyright (C) 2006 THE mysql-dfsg'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the mysql-dfsg package.
+#
+# Stan Ioan-Eugen <stan.ieugen@gmail.com>, 2006.
+msgid ""
+msgstr ""
+"Project-Id-Version: po-debconf://mysql-dfsg\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2006-12-20 21:27+0200\n"
+"Last-Translator: stan ioan-eugen <stan.ieugen@gmail.com>\n"
+"Language-Team: romanian <debian-l10n-romanian@lists.debian.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid "Do you really want to downgrade?"
+msgid "Really proceed with downgrade?"
+msgstr "Sunteţi sigur că doriţi să instalaţi o versiune mai veche?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+#, fuzzy
+#| msgid ""
+#| "WARNING: The file /var/lib/mysql/debian-*.flag exists. This indicates "
+#| "that a mysql-server package with a higher version has been installed "
+#| "before. It can not be guaranteed that this version can use its data."
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"AVERTISMENT: Fişierul /var/lib/mysql/debian-*.flag există. Acest lucru "
+"indică faptul că anterior a fost instalată o versiune nouă a pachetului "
+"mysql-server. Nu se poate garanta că versiunea instalată acum poate folosi "
+"datele versiunii instalate anterior."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid "Important note for NIS/YP users!"
+msgid "Important note for NIS/YP users"
+msgstr "Notă importantă pentru utilizatorii NIS/YP!"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+#, fuzzy
+#| msgid ""
+#| "The script is about to remove the data directory /var/lib/mysql. If it is "
+#| "planned to just install a higher MySQL version or if a different mysql-"
+#| "server package is already using it, the data should be kept."
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Scriptul urmează să şteargă directorul de date /var/lib/mysql. Dacă plănuiţi "
+"doar să instalaţi o versiune nouă MySQL sau datele sunt folosite de către un "
+"alt pachet mysql-server, atunci ar trebui păstraţi datele."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+#| msgid "Should MySQL start on boot?"
+msgid "Start the MySQL server on boot?"
+msgstr "Doriţi ca MySQL să pornească la initializarea sistemului?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+#| msgid ""
+#| "The MySQL can start automatically on boot time or only if you manually "
+#| "type '/etc/init.d/mysql start'."
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL poate porni automat la iniţializarea sistemului sau doar dacă rulaţi "
+"comanda „/etc/init.d/mysql start”."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid "New password for MySQL \"root\" user:"
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Noua parolă pentru utilizatorul „root” al MySQL:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+#, fuzzy
+#| msgid ""
+#| "It is highly recommended that you set a password for the MySQL "
+#| "administrative \"root\" user."
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Este recomandat să stabiliţi o parolă pentru utilizatorul administrativ "
+"„root” al MySQL."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+#, fuzzy
+#| msgid "New password for MySQL \"root\" user:"
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Noua parolă pentru utilizatorul „root” al MySQL:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid "Unable to set password for MySQL \"root\" user"
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Nu s-a putut stabili parola pentru utilizatorul „root” al MySQL"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "It seems an error occurred while setting the password for the MySQL "
+#| "administrative user. This may have happened because the user already has "
+#| "a password, or because there was a problem communicating with the MySQL "
+#| "server."
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Se pare că a intervenit o eroare în stabilirea parolei pentru utilizatorul "
+"administrativ al MySQL. Acest lucru se poate întâmpla dacă utilizatorul are "
+"deja o parolă, sau a existat o problemă în comunicarea cu serverul MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid "Cannot upgrade if ISAM tables are present!"
+#~ msgstr "Nu se poate face actualizarea dacă sunt prezente tabele ISAM!"
+
+#~ msgid ""
+#~ "Recent versions of MySQL can no longer use the old ISAM table format and "
+#~ "it is necessary to convert your tables to e.g. MyISAM before upgrading by "
+#~ "using \"mysql_convert_table_format\" or \"ALTER TABLE x ENGINE=MyISAM\". "
+#~ "The installation of mysql-server-5.6 will now abort. In case your old "
+#~ "mysql-server-4.1 gets removed nevertheless just reinstall it to convert "
+#~ "those tables."
+#~ msgstr ""
+#~ "Versiunile recente MySQL nu mai pot folosi vechiul format de tabele ISAM "
+#~ "şieste necesar să convertiţi tabelele dumneavoastră de ex. la formatul "
+#~ "MyISAM înainte de a face actualizarea folosind comanda "
+#~ "„mysql_convert_table_format” sau „ALTER TABLE x ENGINE=MyISAM”. "
+#~ "Instalarea mysql-server-5.6 va eşua. În caz că ştergeţiversiunea "
+#~ "anterioară mysql-server-4.1 va trebui reinstalată pentru a converti "
+#~ "tabelele."
+
+#~ msgid ""
+#~ "Support MySQL connections from hosts running Debian \"sarge\" or older?"
+#~ msgstr ""
+#~ "Suportaţi conexiuni MySQL de la staţii ce rulează sistemul Debian „sarge” "
+#~ "sau mai vechi?"
+
+#, fuzzy
+#~| msgid ""
+#~| "The way passwords were stored was not very secure. This has been "
+#~| "improved with the drawback that clients (e.g. PHP) from hosts running "
+#~| "Debian 3.1 Sarge will not be able to connect to account which are new or "
+#~| "whose password have been changed. See /usr/share/doc/mysql-server-5.6/"
+#~| "README.Debian."
+#~ msgid ""
+#~ "In old versions of MySQL clients on Debian, passwords were not stored "
+#~ "securely. This has been improved since then, however clients (such as "
+#~ "PHP) from hosts running Debian 3.1 Sarge will not be able to connect to "
+#~ "recent accounts or accounts whose password have been changed."
+#~ msgstr ""
+#~ "Modul în care erau păstrate parolele nu era foarte sigur. Acest lucru a "
+#~ "fost îmbunătăţitcu dezajantajul că clienţii (de ex. PHP) de pe staţii ce "
+#~ "rulează sistemul Debian 3.1 Sargenu se vor putea conecta la conturi noi "
+#~ "sau ale căror parole au fost schimbate. Citiţi /usr/share/doc/mysql-"
+#~ "server-5.6/README.Debian."
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "Pentru a folosi mysql trebuie să adăugaţi un utilizator şi grup "
+#~ "echivalent şi să vă asiguraţi că /var/lib/mysql are permisiunile "
+#~ "stabilite corect (uid/gid pot aveavalori diferite)."
+
+#~ msgid ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+#~ msgstr ""
+#~ "/etc/passwd:\tmysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+
+#~ msgid "/etc/group: mysql:x:101:"
+#~ msgstr "/etc/group:\tmysql:x:101:"
+
+#~ msgid "/var/lib/mysql: drwxr-xr-x mysql mysql"
+#~ msgstr "/var/lib/mysql:\tdrwxr-xr-x\tmysql\tmysql"
+
+#~ msgid "Remove the databases used by all MySQL versions?"
+#~ msgstr "Doriţi să ştergeţi bazele de date folosite de toate versiune MySQL?"
+
+#~ msgid ""
+#~ "If you do not provide a password no changes will be made to the account."
+#~ msgstr ""
+#~ "Dacă nu introduceţi nici o parolă, nici o schimbare nu va fi luată în "
+#~ "considerare."
+
+#~ msgid ""
+#~ "When installation finishes, you should verify that the account is "
+#~ "properly protected with a password (see README.Debian for more "
+#~ "information)."
+#~ msgstr ""
+#~ "După finalizarea instalării, ar trebui să verificaţi dacă contul este "
+#~ "protejat cu o parolă (citiţi fişierul README.Debian pentru informaţii "
+#~ "suplimentare)."
--- /dev/null
+# translation of ru.po to Russian
+# Russian messages:
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans#
+# Developers do not need to manually edit POT or PO files.
+#
+# Ilgiz Kalmetev <translator@ilgiz.pp.ru>, 2003.
+# Yuriy Talakan' <yt@amur.elektra.ru>, 2005, 2006.
+# Yuriy Talakan' <yt@drsk.ru>, 2007.
+# Yuri Kozlov <yuray@komyakino.ru>, 2009, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6 5.6.8-1\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-05-23 21:14+0400\n"
+"Last-Translator: Yuri Kozlov <yuray@komyakino.ru>\n"
+"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.2\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Действительно установить более старую версию?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "В системе найден файл /var/lib/mysql/debian-*.flag."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Это означает, что ранее уже был установлен пакет mysql-server более новой "
+"версии."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Нет гарантий, что версия, которая устанавливается сейчас, будет способна "
+"работать с имеющимися базами данных."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Важное замечание для пользователей NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Использование MySQL в NIS/YP требует добавления учётной записи mysql в "
+"локальную систему с:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr "Также проверьте права доступа и владельца каталога /var/lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Удалить все базы данных MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Запрос на удаление каталога /var/lib/mysql, содержащий базы данных MySQL."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Если вы удаляете пакет MySQL для установки более новой версии MySQL, или "
+"есть другие пакеты mysql-server, использующие этот каталог, то данные лучше "
+"сохранить."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Запускать MySQL при загрузке системы?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"Сервер MySQL можно запускать автоматически при загрузке системы или вручную "
+"по команде '/etc/init.d/mysql start'."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Новый пароль для MySQL пользователя «root»:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Хотя и необязательно, но настоятельно рекомендуется установить пароль для "
+"административного пользователя MySQL «root»."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Если оставить поле пустым, то пароль изменён не будет."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Повторите ввод пароля для MySQL пользователя «root»:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Невозможно задать пароль MySQL пользователю «root»"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"В процессе задания пароля административного MySQL пользователя произошла "
+"ошибка. Это могло произойти, если у пользователя уже был задан пароль, или "
+"из-за проблем соединения с сервером MySQL."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr "Проверьте пароль учётной записи после установки пакета."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr "Подробности см. в файле /usr/share/doc/mysql-server-5.6/README.Debian."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Ошибка ввода пароля"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr "Два введённых пароля не одинаковы. Повторите ввод."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB Cluster уже используется"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.6 больше не поддерживает NDB Cluster. Переходите на новый пакет "
+"mysql-cluster-server и удалите все строки, начинающиеся с «ndb», из всех "
+"файлов настройки в каталоге /etc/mysql/."
--- /dev/null
+# Slovak translations for mysql-5.1 package
+# Slovenské preklady pre balík mysql-5.1.
+# Copyright (C) 2011 THE mysql-5.1'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the mysql-5.1 package.
+# Slavko <linux@slavino.sk>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-5.1 5.1.49-3\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2011-01-04 12:55+0100\n"
+"Last-Translator: Slavko <linux@slavino.sk>\n"
+"Language-Team: Slovak <nomail>\n"
+"Language: sk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Naozaj pokračovať v znížení verzie?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "Súbor s menom /var/lib/mysql/debian-*.flag už v systéme existuje."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"Takýto súbor udáva, že už bol predtým nainštalovaný balík mysql-server s "
+"vyššou verziou."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Neexistuje žiadna záruka, že aktuálne inštalovaná verzia dokáže pracovať s "
+"existujúcimi databázami."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Dôležitá poznámka pre používateľov NIS/YP"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"Používanie MySQL pod NIS/YP vyžaduje aby bol používateľský účet mysql "
+"pridaný do lokálneho systému pomocou:"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Mali by ste tiež skontrolovať vlastníctvo a práva k adresáru /var/lib/mysql:"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Odstrániť všetky databázy MySQL?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Adresár /var/lib/mysql, ktorý obsahuje databázy MySQL, bude odstránený."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Ak odstraňujete balík MySQL kvôli neskoršej inštalácii najnovšej verzie "
+"alebo ak ich používa aj iný balík mysql-server, mali by ste údaje ponechať."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Spúšťať MySQL server pri štarte systému?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL server môže byť automaticky spúšťaný pri štarte systému alebo manuálne "
+"príkazom „/etc/init.d/mysql start”."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nové heslo MySQL používateľa „root”:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Hoci to nie je vyslovene nutné, je silne odporúčané nastaviť heslo "
+"správcovského MySQL účtu \"root\"."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Ak toto pole ponecháte prázdne, heslo nebude zmenené."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Zopakujte heslo MySQL používateľa „root“:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Nemožno nastaviť heslo MySQL používateľa „root“"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Pri nastavovaní hesla správcu MySQL nastala chyba. Toto môže nastať, ak už "
+"účet má nastavené heslo alebo kvôli problémom pri komunikácii s MySQL "
+"serverom."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr "Po inštalácii balíka by ste mali skontrolovať heslo účtu."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+#, fuzzy
+#| msgid ""
+#| "Please read the /usr/share/doc/mysql-server-5.1/README.Debian file for "
+#| "more information."
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Ďalšie podrobností nájdete v súbore /usr/share/doc/mysql-server-5.1/README."
+"Debian."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Chyba pri zadávaní hesla"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr "Heslá, ktoré ste zadali sa nezhodujú. Skúste prosím znova."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "Vyzerá to, že používate NDB Cluster"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+#, fuzzy
+#| msgid ""
+#| "MySQL-5.1 no longer provides NDB Cluster support. Please migrate to the "
+#| "new mysql-cluster package and remove all lines starting with \"ndb\" from "
+#| "all config files below /etc/mysql/."
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"MySQL-5.1 už nepodporuje NDB Cluster. Použite prosím nový balík mysql-"
+"cluster a zo všetkých konfiguračných súborov v /etc/mysql/ odstráňte všetky "
+"riadky, ktoré začínajú na „ndb”."
--- /dev/null
+# Translation of mysql-5.6 debconf template to Swedish\r
+# Copyright (C) 2009, 2012 Martin Bagge <brother@bsnet.se>\r
+# This file is distributed under the same license as the mysql-5.6 package.\r
+# \r
+# Andreas Henriksson <andreas@fatal.se>, 2007\r
+# Martin Bagge <brother@bsnet.se>, 2009, 2012\r
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-5.6 5.0.21-3\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2012-05-30 00:29+0100\n"
+"Last-Translator: Martin Bagge / brother <brother@bsnet.se>\n"
+"Language-Team: Swedish <debian-l10n-swedish@lists.debian.org>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Swedish\n"
+"X-Poedit-Country: Sweden\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr "Vill du verkligen genomföra nedgraderingen?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr "En fil med namnet /var/lib/mysql/debian-*.flag hittades i systemet."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+"En sådan fil är en indikation på att paketet mysql-server med ett högre "
+"versionsnummer har installerats tidigare."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+"Det finns ingen garanti för att den version som du håller på att installera "
+"kommer att kunna använda de aktuella databaserna."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr "Viktig information för NIS/YP-användare"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+"För att kunna använda MySQL under NIS/YP måste ett användarkonto för MySQL "
+"läggas till i systemet."
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+"Du bör också kontrollera rättigheterna och ägaren av katalogen /var/lib/"
+"mysql."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr "Ta bort alla MySQL-databaser?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+"Katalogen /var/lib/mysql som innehåller MySQL-databaser kommer att tas bort."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+"Om avinstallationen av MySQL-paketet görs för att senare kunna installera en "
+"nyare version eller om en annan MySQL-server redan använder filerna ska de "
+"inte raderas."
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr "Ska MySQL startas vid systemets uppstart?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL-servern kan startas vid systemets uppstart eller manuellt med "
+"kommandot \"/etc/init.d/mysql start\"."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr "Nytt lösenord för MySQLs \"root\"-användare:"
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+"Det är inte obligatoriskt men starkt rekommenderat att du sätter ett "
+"lösenord för MySQLs administrativa \"root\"-användare."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Om detta fält lämnas tom kommer lösenordet inte att ändras."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr "Repetera lösenordet för MySQLs \"root\"-användare:"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr "Kunde inte sätta lösenord för MySQLs \"root\"-användare"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+"Ett fel uppstod när det skulle sättas ett lösenord för MySQLs administrativa "
+"användare (\"root\"). Detta kan ha skett för att användaren redan har ett "
+"lösenord satt, eller på grund av problem med att kommunicera med MySQL-"
+"servern."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr "Du bör kontrollera kontots lösenord efter installationen av paketet."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+"Läs filen /usr/share/doc/mysql-server-5.6/README.Debian för mer information."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr "Fel vid inmatning av lösenord"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr "De två lösenorden du angav stämde inte överrens. Prova igen."
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr "NDB-kluster används inte"
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+"Stödet för NDB-kluster har tagits bort i MySQL-5.6. Migrera till det nya "
+"paketet mysql-cluster-server och ta bort alla rader som inleds med \"ndb\" "
+"från alla inställlningsfiler i /etc/mysql/."
+
+#~ msgid ""
+#~ "To use MySQL, the following entries for users and groups should be added "
+#~ "to the system:"
+#~ msgstr ""
+#~ "För att använda MySQL måste följande användare och grupper läggas till i "
+#~ "systemet:"
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-wsrep-5.6\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid "Important note for NIS/YP users"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid "Start the MySQL server on boot?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
--- /dev/null
+# Turkish translation of mysql-server.
+# This file is distributed under the same license as the mysql-server package.
+# Gürkan Aslan <gurkan@iaslan.com>, 2004
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mysql-dfsg-4.1\n"
+"Report-Msgid-Bugs-To: mysql-wsrep-5.6@packages.debian.org\n"
+"POT-Creation-Date: 2014-11-23 01:05+0200\n"
+"PO-Revision-Date: 2004-06-05 08:53+0300\n"
+"Last-Translator: Gürkan Aslan <gurkan@iaslan.com>\n"
+"Language-Team: Turkish <debian-l10n-turkish@lists.debian.org>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "Really proceed with downgrade?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid "A file named /var/lib/mysql/debian-*.flag exists on this system."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"Such a file is an indication that a mysql-server package with a higher "
+"version has been installed previously."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:2001
+msgid ""
+"There is no guarantee that the version you're currently installing will be "
+"able to use the current databases."
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+#, fuzzy
+#| msgid "Important note for NIS/YP users!"
+msgid "Important note for NIS/YP users"
+msgstr "NIS/YP kullanıcıları için önemli not!"
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"Using MySQL under NIS/YP requires a mysql user account to be added on the "
+"local system with:"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:3001
+msgid ""
+"You should also check the permissions and ownership of the /var/lib/mysql "
+"directory:"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid "Remove all MySQL databases?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"The /var/lib/mysql directory which contains the MySQL databases is about to "
+"be removed."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:4001
+msgid ""
+"If you're removing the MySQL package in order to later install a more recent "
+"version or if a different mysql-server package is already using it, the data "
+"should be kept."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+#| msgid "Should MySQL start on boot?"
+msgid "Start the MySQL server on boot?"
+msgstr "MySQL açılış sırasında başlatılsın mı?"
+
+#. Type: boolean
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:5001
+#, fuzzy
+msgid ""
+"The MySQL server can be launched automatically at boot time or manually with "
+"the '/etc/init.d/mysql start' command."
+msgstr ""
+"MySQL açılış sırasında veya '/etc/init.d/mysql start' komutunu vermeniz "
+"halinde elle başlatılabilir. Eğer açılışta otomatik olarak başlatılmasını "
+"istiyorsanız burada 'evet'i seçin."
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "New password for the MySQL \"root\" user:"
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid ""
+"While not mandatory, it is highly recommended that you set a password for "
+"the MySQL administrative \"root\" user."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:6001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:7001
+msgid "Repeat password for the MySQL \"root\" user:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "Unable to set password for the MySQL \"root\" user"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"An error occurred while setting the password for the MySQL administrative "
+"user. This may have happened because the account already has a password, or "
+"because of a communication problem with the MySQL server."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid "You should check the account's password after the package installation."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:8001
+msgid ""
+"Please read the /usr/share/doc/mysql-server-5.6/README.Debian file for more "
+"information."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:9001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid "NDB Cluster seems to be in use"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../mysql-wsrep-server-5.6.templates:10001
+msgid ""
+"MySQL-5.6 no longer provides NDB Cluster support. Please migrate to the new "
+"mysql-cluster-server package and remove all lines starting with \"ndb\" from "
+"all config files below /etc/mysql/."
+msgstr ""
+
+#~ msgid ""
+#~ "To use mysql you must install an equivalent user and group to the "
+#~ "following and ensure yourself that /var/lib/mysql has the right "
+#~ "permissions (the uid/gid may be different)."
+#~ msgstr ""
+#~ "Mysql'i kullanmak için aşağıdakiyle eşdeğer bir kullanıcı ve grup "
+#~ "tanımlamalı, ve /var/lib/mysql izinlerinin uygun şekilde ayarlandığından "
+#~ "emin olmalısınız (uid/gid farklı olabilir)."
+
+#~ msgid ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+#~ msgstr ""
+#~ "/etc/passwd: mysql:x:100:101:MySQL Server:/var/lib/mysql:/bin/false"
+
+#~ msgid "/etc/group: mysql:x:101:"
+#~ msgstr "/etc/group: mysql:x:101:"
+
+#~ msgid "/var/lib/mysql: drwxr-xr-x mysql mysql"
+#~ msgstr "/var/lib/mysql: drwxr-xr-x mysql mysql"
+
+#, fuzzy
+#~ msgid "Please also read http://www.mysql.com/doc/en/Upgrade.html"
+#~ msgstr "Lütfen http://www.mysql.com/doc/en/Upgrade.html belgesini okuyun"
+
+#, fuzzy
+#~ msgid ""
+#~ "MySQL will only install if you have a non-numeric hostname that is "
+#~ "resolvable via the /etc/hosts file. E.g. if the \"hostname\" command "
+#~ "returns \"myhostname\" then there must be a line like \"10.0.0.1 "
+#~ "myhostname\"."
+#~ msgstr ""
+#~ "MySQL sadece /etc/hosts dosyası yoluyla çözülebilir NUMERİK OLMAYAN bir "
+#~ "makine adına sahipseniz kurulacaktır. Örneğin, eğer \"hostname\" komutu "
+#~ "\"makinem\" ismini döndürüyorsa, bu dosya içinde \"10.0.0.1 makinem\" "
+#~ "gibi bir satır olmalıdır."
+
+#, fuzzy
+#~ msgid ""
+#~ "A new mysql user \"debian-sys-maint\" will be created. This mysql account "
+#~ "is used in the start/stop and cron scripts. Don't delete."
+#~ msgstr ""
+#~ "Yeni mysql kullanıcısı \"debian-sys-maint\" yaratılacak. Bu hesap, "
+#~ "başlangıç betiklerinde ve cron içinde kullanılıyor. Bu hesabı silmeyin."
+
+#, fuzzy
+#~ msgid ""
+#~ "Please remember to set a PASSWORD for the MySQL root user! If you use a /"
+#~ "root/.my.cnf, always write the \"user\" and the \"password\" lines in "
+#~ "there, never only the password!"
+#~ msgstr ""
+#~ "Lütfen MySQL root kullanıcısı için bir PAROLA girmeyi unutmayın! Eğer /"
+#~ "root/.my.cnf kullanıyorsanız, \"user\" ve \"password\" satırlarını her "
+#~ "zaman buraya ekleyin, sadece parolayı değil! Daha fazla bilgi için /usr/"
+#~ "share/doc/mysql-server/README.Debian dosyasını okuyun."
+
+#, fuzzy
+#~ msgid ""
+#~ "Should I remove all databases below /var/lib/mysql as you are purging the "
+#~ "mysql-server package?"
+#~ msgstr ""
+#~ "mysql-server paketi kaldırıldıktan sonra bütün veritabanları silinsin mi?"
+
+#~ msgid ""
+#~ "Networking is disabled by default for security reasons. You can enable it "
+#~ "by commenting out the skip-networking option in /etc/mysql/my.cnf."
+#~ msgstr ""
+#~ "Ağ, öntanımlı olarak güvenlik gerekçeleriyle devre dışı bırakıldı. Bu "
+#~ "özelliği /etc/mysql/my.cnf dosyası içindeki \"skip-networking\" "
+#~ "seçeneğini kaldırarak etkinleştirebilirsiniz."
+
+#~ msgid "security and update notice"
+#~ msgstr "güvenlik ve güncelleme duyurusu"
+
+#~ msgid ""
+#~ "Should I remove everything below /var/lib/mysql when you purge the mysql-"
+#~ "server package with the \"dpkg --purge mysql-server\" command (i.e. "
+#~ "remove everything including the configuration) somewhen? (default is not)"
+#~ msgstr ""
+#~ "mysql-server paketini temizlemek için \"dpkg --purge mysql-server\" "
+#~ "komutunu kullandığınızda (yani yapılandırma dahil herşeyi silmek) /var/"
+#~ "lib/mysql altındaki herşeyi sileyim mi? (öntanımlı cevap hayır'dır)."
+
+#~ msgid "Please run mysql_fix_privilege_tables !"
+#~ msgstr "Lütfen mysql_fix_privilege_tables komutunu çalıştırın!"
+
+#~ msgid ""
+#~ "I will ensure secure permissions of /var/lib/mysql by replacing GIDs "
+#~ "other than root and mysql with mysql."
+#~ msgstr ""
+#~ "/var/lib/mysql'in izinlerinin güvenli olmasını sağlamak amacıyla, buna "
+#~ "ait GID'leri root ve mysql'den farklı olacak şekilde değiştireceğim."
+
+#~ msgid ""
+#~ "Instructions how to enable SSL support are in /usr/share/doc/mysql-server/"
+#~ msgstr ""
+#~ "SSL desteğini nasıl etkinleştirebileceğinize ilişkin talimatlar /usr/"
+#~ "share/doc/mysql-server/ içinde."
+
+#~ msgid "mysql_fix_privileges_tables will be executed"
+#~ msgstr "mysql_fix_privileges_tables çalıştırılacak"
+
+#~ msgid ""
+#~ "The latest MySQL versions have an enhanced, more fine grained, privilege "
+#~ "system. To make use of it, some new fields must be added to the tables "
+#~ "in the \"mysql\" database. This is done by the "
+#~ "mysql_fix_privilege_tables script during this upgrade regardless of if "
+#~ "the server is currently running or not!"
+#~ msgstr ""
+#~ "En son MySQL sürümleri zenginleştirilmiş, daha ayrıntılandırılmış bir "
+#~ "ayrıcalık (privilege) sistemine sahiptir. Yeni sistemi kullanmak için, "
+#~ "\"mysql\" veritabanındaki tablolara bazı yeni alanlar eklenmelidir. Bu "
+#~ "işlem, sunucunun çalışıp çalışmamasına bağlı olmaksızın "
+#~ "mysql_fix_privilege_tables betiği tarafından bu yükseltme sırasında "
+#~ "yapılır."
+
+#~ msgid ""
+#~ "This script is not supposed to give any user more rights that he had "
+#~ "before, if you encounter such a case, please contact me."
+#~ msgstr ""
+#~ "Bu betiğin hiç bir kullanıcıya öncekinden daha fazla hak kazandırmadığı "
+#~ "varsayılıyor. Eğer bunun aksinde bir durumla karşılaşırsanız, lütfen "
+#~ "benimle bağlantıya geçin."
+
+#~ msgid "Make MySQL reachable via network?"
+#~ msgstr "MySQL network üzerinden ulaşılabilir olsun mu?"
+
+#~ msgid ""
+#~ "Should MySQL listen on a network reachable TCP port? This is not "
+#~ "necessary for use on a single computer and could be a security problem."
+#~ msgstr ""
+#~ "MySQL ağ üzerinde ulaşılabilen bir TCP portunu dinlesin mi? Tek olan bir "
+#~ "bilgisayar için bu ayar gerekli değildir ve bir güvenlik sorunu "
+#~ "oluşturabilir."
+
+#~ msgid "Enable chroot mode?"
+#~ msgstr "chroot kipi etkinleştirilsin mi?"
+
+#~ msgid ""
+#~ "MySQL is able to jail itself into the /var/lib/mysql_jail directory so "
+#~ "that users cannot modify any files outside this directory. This improves "
+#~ "resistence against crackers, too, as they are not able to modify system "
+#~ "files."
+#~ msgstr ""
+#~ "MySQL kendini /var/lib/mysql_jail dizinine hapsederek kullanıcıların bu "
+#~ "dizin dışındaki hiç bir dosyayı değiştirmemesini sağlayabilir. Bu "
+#~ "düzenleme, sistem dosyalarını değiştirmelerini engelleyeceğinden, "
+#~ "cracker'lara karşı dayanıklılığı arttırır."
--- /dev/null
+#!/usr/bin/make -f
+
+export DH_VERBOSE=1
+
+# enable Debian Hardening
+# see: https://wiki.debian.org/Hardening
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all,-pie
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/buildflags.mk
+
+ARCH := $(shell dpkg-architecture -qDEB_BUILD_ARCH)
+ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
+BUILDDIR := builddir
+BUILDDIR_PIC := builddir-pic
+builddir = $(if $(findstring -pic,$@),$(BUILDDIR_PIC),$(BUILDDIR))
+DEBVERSION := $(shell dpkg-parsechangelog | awk '/^Version: / { print $$2 }' | sed 's/^.*-//' )
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+DEB_BUILD_GNU_SYSTEM ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_SYSTEM)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_SOURCE_PACKAGE ?= $(strip $(shell egrep '^Source: ' debian/control | cut -f 2 -d ':'))
+DEB_VERSION ?= $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ' ')
+DEB_NOEPOCH_VERSION ?= $(shell echo $(DEB_VERSION) | cut -d: -f2-)
+DEB_UPSTREAM_VERSION ?= $(shell echo $(DEB_NOEPOCH_VERSION) | sed 's/-[^-]*$$//')
+DEB_UPSTREAM_VERSION_MAJOR_MINOR := $(shell echo $(DEB_UPSTREAM_VERSION) | sed -r -n 's/^([0-9]+\.[0-9]+).*/\1/p')
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+DISTRIBUTION := $(shell lsb_release -i -s)
+EXPORTED_SOURCE_TARBALL := debian/mysql-source-5.6.tar.gz
+TESTSUITE_FAIL_CMD:=true
+TMP := $(CURDIR)/debian/tmp/
+USE_ASSEMBLER:=--enable-assembler
+
+
+export MYSQL_BUILD_CC=$(DEB_HOST_GNU_TYPE)-gcc
+export MYSQL_BUILD_CXX=$(DEB_HOST_GNU_TYPE)-g++
+
+
+ifneq (,$(filter $(ARCH), i386 kfreebsd-i386 hurd-i386))
+ TAOCRYPT_OPT="-DTAOCRYPT_DISABLE_X86ASM"
+endif
+
+# make test-bt is the testsuite run by the MySQL build team
+# before a release, but it is long
+MAKE_TEST_TARGET:=test-mtr
+ifneq ($(findstring fulltest,$(DEB_BUILD_OPTIONS)),)
+ MAKE_TEST_TARGET:=test-bt
+endif
+ifeq ($(ARCH_OS),hurd)
+# Tests not fully working under Hurd
+# See http://bugs.mysql.com/bug.php?id=64685
+ MAKE_TEST_TARGET:=test
+endif
+
+
+# This causes seg11 crashes if LDAP is used for groups in /etc/nsswitch.conf
+# so it is disabled by default although, according to MySQL, it brings >10%
+# performance gain if enabled. See #299382.
+ifeq ($(STATIC_MYSQLD), 1)
+ USE_STATIC_MYSQLD:=--with-mysqld-ldflags=-all-static
+endif
+
+override_dh_auto_clean:
+ @echo "RULES.$@"
+ dh_testdir
+ dh_testroot
+ [ ! -d mysql-test/var ] || rm -rf mysql-test/var
+ rm -rf $(BUILDDIR) $(BUILDDIR_PIC) .pc
+ debconf-updatepo
+ rm -f $(EXPORTED_SOURCE_TARBALL)
+ rm -f cmake/os/GNU.cmake
+
+override_dh_prep:
+
+override_dh_auto_configure: configure-stamp
+
+configure-pic-stamp: FORCE_FPIC_CFLAGS=-fPIC
+configure-pic-stamp: FORCE_FPIC=-DWITH_PIC=On
+
+configure-stamp configure-pic-stamp:
+ @echo "RULES.$@"
+ dh_testdir
+ ( test -d $(builddir) || mkdir $(builddir) ) && cd $(builddir) && \
+ sh -c 'PATH=$${MYSQL_BUILD_PATH:-"/usr/local/bin:/usr/bin:/bin"} \
+ CC=$${MYSQL_BUILD_CC:-gcc} \
+ CFLAGS=$${MYSQL_BUILD_CFLAGS:-"-O2 -DBIG_JOINS=1 ${FORCE_FPIC_CFLAGS} -fno-strict-aliasing ${TAOCRYPT_OPT}"} \
+ CXX=$${MYSQL_BUILD_CXX:-g++} \
+ CXXFLAGS=$${MYSQL_BUILD_CXXFLAGS:-"-O3 -DBIG_JOINS=1 -felide-constructors -fpermissive ${FORCE_FPIC_CFLAGS} -fno-strict-aliasing ${TAOCRYPT_OPT}"} \
+ cmake -DCMAKE_INSTALL_PREFIX=/usr \
+ -DCMAKE_VERBOSE_MAKEFILE=ON \
+ $(FORCE_FPIC) \
+ -DMYSQL_UNIX_ADDR=/var/run/mysqld/mysqld.sock \
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+ -DWITH_WSREP=1 \
+ -DWITH_LIBWRAP=ON \
+ -DWITH_ZLIB=system \
+ -DWITH_EDITLINE=system \
+ $(USE_STATIC_MYSQLD) \
+ -DWITH_SSL=system \
+ -DCOMPILATION_COMMENT="($(DISTRIBUTION))" \
+ -DMYSQL_SERVER_SUFFIX="-$(DEBVERSION)" \
+ -DSYSTEM_TYPE="debian-$(DEB_BUILD_GNU_SYSTEM)" \
+ -DINSTALL_LAYOUT=RPM \
+ -DINSTALL_LIBDIR=lib/$(DEB_HOST_MULTIARCH) \
+ -DINSTALL_PLUGINDIR=lib/mysql/plugin \
+ -DWITH_EMBEDDED_SERVER=ON \
+ -DWITH_ARCHIVE_STORAGE_ENGINE=ON \
+ -DWITH_BLACKHOLE_STORAGE_ENGINE=ON \
+ -DWITH_FEDERATED_STORAGE_ENGINE=ON \
+ -DWITH_EXTRA_CHARSETS=all ..'
+ touch $@
+
+override_dh_auto_build: build-stamp
+
+build-stamp:
+ @echo "RULES.$@"
+ [ -f $(EXPORTED_SOURCE_TARBALL) ] || tar -zcf $(EXPORTED_SOURCE_TARBALL) \
+ --exclude=debian . \
+ --transform="s,^\./,mysql-5.6/,"
+ cd $(builddir) && $(MAKE)
+ touch $@
+
+build-pic-stamp:
+ # Don't call this section from dh_auto_build as
+ # linmysqld does not exsist in mysql-wsrep
+ @echo "RULES.$@"
+ cd $(builddir) && $(MAKE) -C scripts
+ cd $(builddir) && $(MAKE) -C libmysqld
+ touch $@
+
+override_dh_auto_test:
+ @echo "RULES.$@"
+ifeq ($(findstring nocheck,$(DEB_BUILD_OPTIONS)),)
+ cp unittest/unit.pl $(builddir)/unittest/
+ cp -r mysql-test/* $(builddir)/mysql-test/
+ cp -r sql/share/* $(builddir)/sql/share/
+ cp -r scripts/*sql $(builddir)/scripts/
+ cd $(builddir) && $(MAKE) $(MAKE_TEST_TARGET) || $(TESTSUITE_FAIL_CMD) ;
+endif
+
+override_dh_auto_install: auto_install-stamp
+
+auto_install-stamp:
+ @echo "RULES.$@"
+ dh_testdir
+ dh_testroot
+ # make install (trailing slash needed for innobase)
+ cd $(builddir) && $(MAKE) install DESTDIR=$(TMP)/
+ # install libmysqld built with -FPIC
+ install -d -m 0755 -o root -g root $(TMP)/usr/lib/mysql
+ # linmysqld does not exsist in mysql-wsrep
+ # install -m 0644 -o root -g root $(BUILDDIR_PIC)/libmysqld/libmysqld.a $(TMP)/usr/lib/mysql/libmysqld_pic.a
+ ## mysql_config won't report the -fPIC, so give libmysqld-pic users a way to get their flags
+ # install -m 0755 -o root -g root $(BUILDDIR_PIC)/scripts/mysql_config $(TMP)/usr/bin/mysql_config_pic
+ mkdir -p $(TMP)/usr/share/doc/mysql-server-5.6
+ install -m 0644 Docs/README-wsrep $(TMP)/usr/share/doc/mysql-server-5.6
+ nm -n $(BUILDDIR)/sql/mysqld |gzip -9 > $(TMP)/usr/share/doc/mysql-server-5.6/mysqld.sym.gz
+ install -m 0644 support-files/wsrep.cnf $(TMP)/usr/share/mysql
+ # This seems to be the only safe place to fix permissions issues
+ # not handled by dh_fixperms.
+ mkdir -p $(TMP)/etc/mysql/conf.d/
+ touch $(TMP)/etc/mysql/conf.d/.keepme
+ install -D -m 0755 $(builddir)/support-files/mysql.server $(TMP)/etc/init.d/mysql
+ install -m 0755 debian/additions/echo_stderr $(TMP)/usr/share/mysql/
+ install -m 0755 debian/additions/debian-start $(TMP)/etc/mysql/
+ install -m 0755 debian/additions/debian-start.inc.sh $(TMP)/usr/share/mysql/
+ install -m 0644 debian/additions/mysql_config_pic.1 $(TMP)/usr/share/man/man1
+ install -m 0644 debian/additions/mysql_embedded.1 $(TMP)/usr/share/man/man1
+ install -m 0644 debian/additions/debian_create_root_user.sql $(TMP)/usr/share/mysql/
+ # install AppArmor profile
+ install -D -m 644 debian/apparmor-profile $(TMP)/etc/apparmor.d/usr.sbin.mysqld
+ chmod 0755 $(TMP)/usr/share/mysql-test/std_data/checkDBI_DBD-mysql.pl
+ # install Apport hook
+ install -D -m 644 debian/mysql-server-5.6.py $(TMP)/usr/share/apport/package-hooks/source_mysql-5.6.py
+ # chmod 0644 $(TMP)/usr/share/mysql/*.ini
+ chmod 0644 $(TMP)/usr/bin/mysqlaccess.conf
+ touch $@
+
+# Use the --fail-missing to check for stuff that we should be installing
+# Exclusion Package Type
+# usr/share/man/ various man
+# usr/share/mysql/magic M-server-5.6 example
+# usr/bin/mysqlaccess.conf M-client-5.6 example
+# usr/lib/mysql/plugin/daemon_example.ini M-server-5.6 example
+# usr/share/mysql/my- M-server-5.6 example
+# The following exclusions have been inherited from pre short form
+# debian/rules days.
+# usr/share/mysql/mi_test_all
+# usr/share/mysql/mysql-log-rotate
+# usr/share/mysql/mysql.server
+# usr/share/mysql/binary-configure
+# NOTE(james-page) usr/include -> end can be dropped when 5.6 becomes default
+# NOTE(james-page) skip --fail-missing until package is complete 5.5 replacement
+#override_dh_install:
+# dh_install --fail-missing \
+# -Xusr/share/man/ \
+# -Xusr/share/info/mysql.info \
+# -Xusr/share/mysql/magic \
+# -Xusr/share/mysql/solaris/postinstall-solaris \
+# -Xusr/lib/mysql/plugin/daemon_example.ini \
+# -Xusr/bin/mysqlaccess.conf \
+# -Xusr/share/mysql/mi_test_all \
+# -Xusr/share/mysql/mysql-log-rotate \
+# -Xusr/share/mysql/mysql.server \
+# -Xusr/share/mysql/binary-configure \
+# -Xusr/share/mysql/my- \
+# -Xusr/include \
+# -Xusr/bin/mysql_config \
+# -Xusr/share/aclocal/mysql.m4 \
+# -Xlibmysql
+
+override_dh_installlogrotate-arch:
+ dh_installlogrotate --name mysql-server
+ dh_apparmor -pmysql-server-5.6 --profile-name=usr.sbin.mysqld
+
+# Start mysql in runlevel 19 before 20 where apache, proftpd etc gets
+# started which might depend on a running database server.
+override_dh_installinit-arch:
+ dh_installinit --name=mysql -- defaults 19 21
+
+override_dh_installcron-arch:
+ dh_installcron --name mysql-server
+
+override_dh_strip:
+ @echo "Notice: not stripping debug symbols from any binaries"
+
+binary: binary-indep binary-arch
+
+get-orig-source:
+ uscan --force-download --verbose
+
+%:
+ dh $@ --parallel
--- /dev/null
+# The mysql-server-5.6.postrm script handles
+# what debhelper normally would.
+# This is apparently related to #526464.
+maintainer-script-lacks-debhelper-token debian/mysql-server-5.6.postrm
+# Probably best to keep the debhelper version low in
+# case someone needs to backport
+package-needs-versioned-debhelper-build-depends 9
--- /dev/null
+3.0 (quilt)
--- /dev/null
+#!/bin/sh
+# autopkgtest check: Build and run a program against libmysqlclient, to verify that the
+# headers and pkg-config file are installed correctly
+# (C) 2012 Canonical Ltd.
+# Author: Daniel Kessel <d.kessel@gmx.de>
+
+echo "test 'build' starting"
+set -e
+
+WORKDIR=$(mktemp -d)
+trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM
+cd $WORKDIR
+
+cat <<EOF > libmysqltest.c
+#include <stdio.h>
+#include <mysql.h>
+
+int main()
+{
+ if (mysql_library_init(0, NULL, NULL)) {
+ fprintf(stderr, "failed to initialize mysql client library\n");
+ return 1;
+ }
+
+ mysql_library_end();
+ return 0;
+}
+EOF
+
+echo "building..."
+gcc -o libmysqltest libmysqltest.c `/usr/bin/mysql_config --cflags --libs` -Wall -Werror
+echo "build: OK"
+[ -x libmysqltest ]
+./libmysqltest
+echo "run: OK"
--- /dev/null
+Tests: smoke upstream
+Depends: mysql-testsuite-5.6
+Restrictions: allow-stderr needs-root breaks-testbed
--- /dev/null
+#!/bin/sh
+set -ex
+
+# dep8 smoke test for mysql-server
+# Author: Robie Basak <robie.basak at canonical.com>
+#
+# This test should be declared in debian/tests/control with a dependency
+# on the package that provides a configured MySQL server (eg.
+# mysql-server-5.6).
+#
+# This test should be declared in debian/tests/control with the
+# following restrictions:
+#
+# needs-root (needed to reset the root mysql password)
+# breaks-testbed (because it resets the root mysql password)
+# allow-stderr
+#
+# This test:
+#
+# 1) Configures packaged mysql server root password with maintainer
+# scripts.
+#
+# 2) Creates a test database and test user as the root user.
+#
+# 3) Creates a test table and checks it appears to operate normally
+# using the test user and test database.
+
+debconf-set-selections <<EOT
+mysql-server-5.6 mysql-server/root_password password rootpassword
+mysql-server-5.6 mysql-server/root_password_again password rootpassword
+EOT
+
+DEBIAN_FRONTEND=noninteractive dpkg-reconfigure mysql-server-5.6
+
+mysql --user=root --password=rootpassword <<EOT
+CREATE DATABASE testdatabase;
+CREATE USER 'testuser'@'localhost' identified by 'testpassword';
+GRANT ALL ON testdatabase.* TO 'testuser'@'localhost';
+EOT
+
+mysql --user=testuser --password=testpassword testdatabase <<EOT
+CREATE TABLE foo (bar INTEGER);
+INSERT INTO foo (bar) VALUES (41);
+EOT
+
+result=`echo 'SELECT bar+1 FROM foo;'|mysql --batch --skip-column-names --user=testuser --password=testpassword testdatabase`
+if [ "$result" != "42" ]; then
+ echo "Unexpected result" >&2
+ exit 1
+fi
+
+mysql --user=testuser --password=testpassword testdatabase <<EOT
+DROP TABLE foo;
+EOT
+
+mysql --user=root --password=rootpassword <<EOT
+DROP DATABASE testdatabase;
+DROP USER 'testuser'@'localhost';
+EOT
--- /dev/null
+#!/bin/sh
+# autopkgtest check: Build and run the upstream test suite.
+# (C) 2012 Canonical Ltd.
+# Author: Daniel Kessel <d.kessel@gmx.de>
+
+# running the mysql testsuite as described in:
+# https://bugs.launchpad.net/ubuntu/+source/mysql-5.5/+bug/959683
+
+echo "running test 'testsuite'"
+set -e
+
+SKIP_TEST_LST="/tmp/skip-test.lst"
+WORKDIR=$(mktemp -d)
+trap "rm -rf $WORKDIRi $SKIP_TEST_LST" 0 INT QUIT ABRT PIPE TERM
+cd $WORKDIR
+
+mkdir var
+mkdir tmp
+
+echo "using vardir: $WORKDIR/var"
+echo "using tmpdir: $WORKDIR/tmp"
+
+echo "Setting up skip-tests-list"
+cat > $SKIP_TEST_LST << EOF
+binlog.binlog_server_start_options : Requires writable /usr
+main.ctype_uca : Requires writable /usr
+EOF
+
+cd /usr/lib/mysql-testsuite
+echo "starting mysql-test-tun.pl..."
+./mysql-test-run.pl --force --vardir=$WORKDIR/var --tmpdir=$WORKDIR/tmp \
+ --comment=normal --timer --skip-ndbcluster --report-features \
+ --skip-test-list=$SKIP_TEST_LST $@ 2>&1
+echo "run: OK"
--- /dev/null
+# See debian/README.source for more information about the DFSG repacking here.
+version=3
+http://mysql.mirrors.pair.com/Downloads/MySQL-5.6/mysql-([\d\.]+).tar.gz
#ifndef _lf_h
#define _lf_h
-#include <my_atomic.h>
+#include "my_global.h"
+#include "my_atomic.h"
+#include "my_sys.h"
+#include "hash.h"
C_MODE_START
typedef struct {
void * volatile pin[LF_PINBOX_PINS];
LF_PINBOX *pinbox;
- void **stack_ends_here;
void *purgatory;
uint32 purgatory_count;
uint32 volatile link;
/* we want sizeof(LF_PINS) to be 64 to avoid false sharing */
-#if SIZEOF_INT*2+SIZEOF_CHARP*(LF_PINBOX_PINS+3) != 64
- char pad[64-sizeof(uint32)*2-sizeof(void*)*(LF_PINBOX_PINS+3)];
+#if SIZEOF_INT*2+SIZEOF_CHARP*(LF_PINBOX_PINS+2) != 64
+ char pad[64-sizeof(uint32)*2-sizeof(void*)*(LF_PINBOX_PINS+2)];
#endif
} LF_PINS;
(pins),
&pins->pinbox->pinarray.lock)
-C_MODE_END
-
-/*
- extendible hash, lf_hash.c
-*/
-#include <hash.h>
-
-C_MODE_START
-
#define LF_HASH_UNIQUE 1
/* lf_hash overhead per element (that is, sizeof(LF_SLIST) */
extern const char *my_defaults_group_suffix;
extern const char *my_defaults_file;
extern my_bool my_getopt_use_args_separator;
+extern my_bool my_defaults_read_login_file;
/* Define the type of function to be passed to process_default_option_files */
typedef int (*Process_option_func)(void *ctx, const char *group_name,
my_bool init;
struct st_my_thread_var *next,**prev;
void *opt_info;
- void *stack_ends_here;
#ifndef DBUG_OFF
void *dbug;
char name[THREAD_NAME_SIZE+1];
extern size_t system_filename(char * to,const char *from);
extern size_t unpack_filename(char * to,const char *from);
extern char * intern_filename(char * to,const char *from);
-extern char * directory_file_name(char * dst, const char *src);
extern int pack_filename(char * to, const char *name, size_t max_length);
extern char * my_path(char * to,const char *progname,
const char *own_pathname_part);
# they are declared without DEFAULT clause.
sync_slave_with_master;
+ --replace_column 4 CURRENT_TIMESTAMP
select * from t9;
+ # Bug #22916743 RBR DOES NOT USE DEFAULT CURRENT_TIMESTAMP FOR EXTRA COLUMN
+ # ON SLAVE
+ --let $assert_text= The values of column 'd' should have non-zero timetsamp.
+ --let $assert_cond= [SELECT COUNT(*) AS Val FROM t9 WHERE d = "0000-00-00 00:00:00", Val, 1] = 0
+ --source include/assert.inc
+ # End of test for Bug #22916743
+
# todo: fix Bug #43992 slave sql thread can't tune own sql_mode ...
# and add/restore waiting for stop test
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
--list_files $MYSQLD_DATADIR/hotcopy_save
--replace_result $MASTER_MYSOCK MASTER_MYSOCK
---error 9,11,110,2304
+--error 9,11,110,2304,255
--exec $MYSQLHOTCOPY --quiet -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save
--replace_result $MASTER_MYSOCK MASTER_MYSOCK
--exec $MYSQLHOTCOPY --quiet --allowold -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save
validate_password plugin/password_validation VALIDATE_PASSWORD validate_password
mysql_no_login plugin/mysql_no_login MYSQL_NO_LOGIN mysql_no_login
test_udf_services plugin/udf_services TESTUDFSERVICES
+connection_control plugin/connection_control CONNECTION_CONTROL_PLUGIN connection_control
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
-DROP TABLE t1;
\ No newline at end of file
+DROP TABLE t1;
+
+--echo #
+--echo # Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+--echo # INDEX, EVEN THOUGH COST IS HIGHER
+--echo #
+
+CREATE TABLE `giant_table` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `one_id` int(11) NOT NULL,
+ `other_id` bigint(20) NOT NULL DEFAULT '0',
+ `some_other_id` int(11) DEFAULT 0 NOT NULL,
+ `something` double NOT NULL DEFAULT '0',
+ `comment` text COLLATE utf8_unicode_ci,
+ `flags` int(11) NOT NULL DEFAULT '0',
+ `time_created` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ KEY `time_created` (`time_created`),
+ KEY `some_other_id` (`some_other_id`),
+ KEY `one_other_idx` (`one_id`,`other_id`),
+ KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+ (82543190), (67538270), (77282760), (77908170),
+ (70923370), (68066360);
+DELIMITER $;
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+
+INSERT INTO giant_table(id,one_id)
+ SELECT c1 + @x, 0
+ FROM t1
+ WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+ 77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+
+UNTIL @x > 30 END REPEAT;
+END $
+DELIMITER ;$
+
+CALL p();
+SELECT count(*) FROM giant_table;
+
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+ (89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+ (61069031, 1, 1);
+
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+ some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+ 84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+ 89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+ 70923271, 68066180)
+ AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
# If you add a new suite, please check TEST_DIRS in Makefile.am.
#
-my $DEFAULT_SUITES= "main,sys_vars,binlog,federated,rpl,innodb,innodb_fts,innodb_zip,perfschema,funcs_1,opt_trace,parts,auth_sec";
+my $DEFAULT_SUITES= "main,sys_vars,binlog,federated,rpl,innodb,innodb_fts,innodb_zip,perfschema,funcs_1,opt_trace,parts,auth_sec,connection_control";
my $opt_suites;
our $opt_verbose= 0; # Verbose output, enable with --verbose
PRIMARY KEY (`fld1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
DROP TABLE t1;
+#
+# Bug#19635706
+# Verify that it is possible to add a unique key to a not-NULL POINT
+# column and that this key is promoted to primary key
+#
+CREATE TABLE t1(a INT NOT NULL, b POINT NOT NULL) ENGINE=INNODB;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) NOT NULL,
+ `b` point NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+ALTER TABLE t1 ADD UNIQUE INDEX (b);
+# Note that SHOW CREATE TABLE does not list b as a primary key,
+# even though it was promoted. This appears to be the case also
+# for other column types.
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) NOT NULL,
+ `b` point NOT NULL,
+ UNIQUE KEY `b` (`b`(25))
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+ALTER TABLE t1 ADD UNIQUE INDEX (a);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) NOT NULL,
+ `b` point NOT NULL,
+ UNIQUE KEY `b` (`b`(25)),
+ UNIQUE KEY `a` (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+# Verify that the expected indices have been created by Innodb
+SELECT T.NAME AS TABLE_NAME, I.NAME AS INDEX_NAME,
+CASE I.TYPE
+WHEN 0 THEN 'Secondary'
+WHEN 1 THEN 'Clustered'
+WHEN 2 THEN 'Unique'
+WHEN 3 THEN 'Primary'
+WHEN 32 THEN 'Full text'
+WHEN 64 THEN 'Spatial'
+ELSE 'Unknown'
+END AS INDEX_TYPE,
+F.NAME AS FIELD_NAME, F.POS AS FIELD_POS FROM
+INFORMATION_SCHEMA.INNODB_SYS_TABLES AS T JOIN
+INFORMATION_SCHEMA.INNODB_SYS_INDEXES AS I JOIN
+INFORMATION_SCHEMA.INNODB_SYS_FIELDS AS F
+ON I.INDEX_ID = F.INDEX_ID AND I.TABLE_ID = T.TABLE_ID
+WHERE T.NAME = 'test/t1';
+TABLE_NAME INDEX_NAME INDEX_TYPE FIELD_NAME FIELD_POS
+test/t1 b Primary b 0
+test/t1 a Unique a 0
+DROP TABLE t1;
drop database if exists events_test;
create database events_test;
use events_test;
-create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5;
+create event e_26 on schedule at '2038-01-19 03:14:07' disable do set @a = 5;
select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event;
db name body definer convert_tz(execute_at, 'UTC', 'SYSTEM') on_completion
-events_test e_26 set @a = 5 root@localhost 2017-01-01 00:00:00 DROP
+events_test e_26 set @a = 5 root@localhost 2038-01-19 03:14:07 DROP
drop event e_26;
create event e_26 on schedule at NULL disable do set @a = 5;
ERROR HY000: Incorrect AT value: 'NULL'
# Bug#11765139 58069: LOAD DATA INFILE: VALGRIND REPORTS INVALID MEMORY READS AND WRITES WITH U
#
CREATE TABLE t1(f1 INT);
-SELECT 0xE1C330 INTO OUTFILE 't1.dat';
+SELECT 0xE1BB30 INTO OUTFILE 't1.dat';
LOAD DATA INFILE 't1.dat' IGNORE INTO TABLE t1 CHARACTER SET utf8;
DROP TABLE t1;
#
Got one of the listed errors
SET @@sql_mode= @old_mode;
DROP TABLE t1;
-
-#
-# Bug#23080148 - Backport of Bug#20683959.
-# Bug#20683959 LOAD DATA INFILE IGNORES A SPECIFIC ROW SILENTLY
-# UNDER DB CHARSET IS UTF8.
-#
-CREATE DATABASE d1 CHARSET latin1;
-USE d1;
-CREATE TABLE t1 (val TEXT);
-LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1;
-SELECT COUNT(*) FROM t1;
-COUNT(*)
-1
-SELECT HEX(val) FROM t1;
-HEX(val)
-C38322525420406E696F757A656368756E3A20E98198E2889AF58081AEE7B99DE4B88AE383A3E7B99DE69690F58087B3E7B9A7EFBDA8E7B99DEFBDB3E7B99DE78999E880B3E7B8BAEFBDAAE7B9A7E89699E296A1E7B8BAE4BBA3EFBD8CE7B8BAEFBDA9E7B8B2E2889AE38184E7B99DEFBDB3E7B99DE4B88AE383A3E7B99DE69690F58087B3E7B9A7EFBDA8E7B99DEFBDB3E7B99DE5B3A8EFBD84E8ABA0EFBDA8E89C89F580948EE599AAE7B8BAEFBDAAE7B8BAE9A198EFBDA9EFBDB1E7B9A7E581B5E289A0E7B8BAEFBDBEE7B9A7E9A194EFBDA9E882B4EFBDA5EFBDB5E980A7F5808B96E28693E99EABE38287E58F99E7B8BAE58AB1E28691E7B8BAF5808B9AE7828AE98095EFBDB1E7B8BAEFBDAFE7B8B2E288ABE6A89FE89EB3E6BA98F58081ADE88EA0EFBDBAE98095E6BA98F58081AEE89D93EFBDBAE8AD9BEFBDACE980A7F5808B96E28693E7B8BAF580918EE288AAE7B8BAE4B88AEFBC9EE7B8BAE4B99DE28691E7B8BAF5808B96EFBCA0E88DB3E6A68AEFBDB9EFBDB3E981B2E5B3A8E296A1E7B8BAE7A4BCE7828AE88DB3E6A68AEFBDB0EFBDBDE7B8BAA0E7B8BAE88B93EFBDBEE5B899EFBC9E
-CREATE DATABASE d2 CHARSET utf8;
-USE d2;
-CREATE TABLE t1 (val TEXT);
-LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1;
-ERROR HY000: Invalid utf8 character string: 'Ã"RT @niouzechun: \9058\221A'
-DROP TABLE d1.t1, d2.t1;
-DROP DATABASE d1;
-DROP DATABASE d2;
# normally.
mysqld is alive
#### End of test ####
+#
+# Bug #24557925: MYSQL_CONFIG_EDITOR CAN MAKE SERVER UNBOOTABLE
+#
+# Restarting the server. Should work
+# Cleanup
+# End of 5.6 tests
# test.t1 have partitions in mysqltest2-directory!
# user root:
CREATE USER mysqltest_1@localhost;
+# Need FILE permission to use external datadir or indexdir.
+GRANT FILE ON *.* TO mysqltest_1@localhost;
CREATE DATABASE mysqltest2;
USE mysqltest2;
CREATE TABLE t1 (a INT) ENGINE = MyISAM;
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
1
DROP TABLE t1;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE `giant_table` (
+`id` int(11) NOT NULL AUTO_INCREMENT,
+`one_id` int(11) NOT NULL,
+`other_id` bigint(20) NOT NULL DEFAULT '0',
+`some_other_id` int(11) DEFAULT 0 NOT NULL,
+`something` double NOT NULL DEFAULT '0',
+`comment` text COLLATE utf8_unicode_ci,
+`flags` int(11) NOT NULL DEFAULT '0',
+`time_created` int(11) NOT NULL DEFAULT '0',
+PRIMARY KEY (`id`),
+KEY `time_created` (`time_created`),
+KEY `some_other_id` (`some_other_id`),
+KEY `one_other_idx` (`one_id`,`other_id`),
+KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+(82543190), (67538270), (77282760), (77908170),
+(70923370), (68066360);
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+INSERT INTO giant_table(id,one_id)
+SELECT c1 + @x, 0
+FROM t1
+WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+UNTIL @x > 30 END REPEAT;
+END $
+CALL p();
+SELECT count(*) FROM giant_table;
+count(*)
+270
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+(89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+(61069031, 1, 1);
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+70923271, 68066180)
+AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using index condition; Using where; Using MRR
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
set optimizer_switch=default;
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
1
DROP TABLE t1;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE `giant_table` (
+`id` int(11) NOT NULL AUTO_INCREMENT,
+`one_id` int(11) NOT NULL,
+`other_id` bigint(20) NOT NULL DEFAULT '0',
+`some_other_id` int(11) DEFAULT 0 NOT NULL,
+`something` double NOT NULL DEFAULT '0',
+`comment` text COLLATE utf8_unicode_ci,
+`flags` int(11) NOT NULL DEFAULT '0',
+`time_created` int(11) NOT NULL DEFAULT '0',
+PRIMARY KEY (`id`),
+KEY `time_created` (`time_created`),
+KEY `some_other_id` (`some_other_id`),
+KEY `one_other_idx` (`one_id`,`other_id`),
+KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+(82543190), (67538270), (77282760), (77908170),
+(70923370), (68066360);
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+INSERT INTO giant_table(id,one_id)
+SELECT c1 + @x, 0
+FROM t1
+WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+UNTIL @x > 30 END REPEAT;
+END $
+CALL p();
+SELECT count(*) FROM giant_table;
+count(*)
+270
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+(89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+(61069031, 1, 1);
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+70923271, 68066180)
+AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using index condition; Using where
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
set optimizer_switch=default;
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
1
DROP TABLE t1;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE `giant_table` (
+`id` int(11) NOT NULL AUTO_INCREMENT,
+`one_id` int(11) NOT NULL,
+`other_id` bigint(20) NOT NULL DEFAULT '0',
+`some_other_id` int(11) DEFAULT 0 NOT NULL,
+`something` double NOT NULL DEFAULT '0',
+`comment` text COLLATE utf8_unicode_ci,
+`flags` int(11) NOT NULL DEFAULT '0',
+`time_created` int(11) NOT NULL DEFAULT '0',
+PRIMARY KEY (`id`),
+KEY `time_created` (`time_created`),
+KEY `some_other_id` (`some_other_id`),
+KEY `one_other_idx` (`one_id`,`other_id`),
+KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+(82543190), (67538270), (77282760), (77908170),
+(70923370), (68066360);
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+INSERT INTO giant_table(id,one_id)
+SELECT c1 + @x, 0
+FROM t1
+WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+UNTIL @x > 30 END REPEAT;
+END $
+CALL p();
+SELECT count(*) FROM giant_table;
+count(*)
+270
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+(89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+(61069031, 1, 1);
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+70923271, 68066180)
+AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using index condition; Using where; Using MRR
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
set optimizer_switch=default;
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
1
DROP TABLE t1;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE `giant_table` (
+`id` int(11) NOT NULL AUTO_INCREMENT,
+`one_id` int(11) NOT NULL,
+`other_id` bigint(20) NOT NULL DEFAULT '0',
+`some_other_id` int(11) DEFAULT 0 NOT NULL,
+`something` double NOT NULL DEFAULT '0',
+`comment` text COLLATE utf8_unicode_ci,
+`flags` int(11) NOT NULL DEFAULT '0',
+`time_created` int(11) NOT NULL DEFAULT '0',
+PRIMARY KEY (`id`),
+KEY `time_created` (`time_created`),
+KEY `some_other_id` (`some_other_id`),
+KEY `one_other_idx` (`one_id`,`other_id`),
+KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+(82543190), (67538270), (77282760), (77908170),
+(70923370), (68066360);
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+INSERT INTO giant_table(id,one_id)
+SELECT c1 + @x, 0
+FROM t1
+WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+UNTIL @x > 30 END REPEAT;
+END $
+CALL p();
+SELECT count(*) FROM giant_table;
+count(*)
+270
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+(89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+(61069031, 1, 1);
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+70923271, 68066180)
+AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using where; Using MRR
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
set optimizer_switch=default;
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
1
DROP TABLE t1;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE `giant_table` (
+`id` int(11) NOT NULL AUTO_INCREMENT,
+`one_id` int(11) NOT NULL,
+`other_id` bigint(20) NOT NULL DEFAULT '0',
+`some_other_id` int(11) DEFAULT 0 NOT NULL,
+`something` double NOT NULL DEFAULT '0',
+`comment` text COLLATE utf8_unicode_ci,
+`flags` int(11) NOT NULL DEFAULT '0',
+`time_created` int(11) NOT NULL DEFAULT '0',
+PRIMARY KEY (`id`),
+KEY `time_created` (`time_created`),
+KEY `some_other_id` (`some_other_id`),
+KEY `one_other_idx` (`one_id`,`other_id`),
+KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+(82543190), (67538270), (77282760), (77908170),
+(70923370), (68066360);
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+INSERT INTO giant_table(id,one_id)
+SELECT c1 + @x, 0
+FROM t1
+WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+UNTIL @x > 30 END REPEAT;
+END $
+CALL p();
+SELECT count(*) FROM giant_table;
+count(*)
+270
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+(89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+(61069031, 1, 1);
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+70923271, 68066180)
+AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using where
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
set optimizer_switch=default;
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
1
DROP TABLE t1;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE `giant_table` (
+`id` int(11) NOT NULL AUTO_INCREMENT,
+`one_id` int(11) NOT NULL,
+`other_id` bigint(20) NOT NULL DEFAULT '0',
+`some_other_id` int(11) DEFAULT 0 NOT NULL,
+`something` double NOT NULL DEFAULT '0',
+`comment` text COLLATE utf8_unicode_ci,
+`flags` int(11) NOT NULL DEFAULT '0',
+`time_created` int(11) NOT NULL DEFAULT '0',
+PRIMARY KEY (`id`),
+KEY `time_created` (`time_created`),
+KEY `some_other_id` (`some_other_id`),
+KEY `one_other_idx` (`one_id`,`other_id`),
+KEY `other_id` (`other_id`,`time_created`)
+) ENGINE=InnoDB AUTO_INCREMENT=101651329
+DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
+(82543190), (67538270), (77282760), (77908170),
+(70923370), (68066360);
+CREATE PROCEDURE p()
+BEGIN
+SET @x = 1;
+REPEAT
+INSERT INTO giant_table(id,one_id)
+SELECT c1 + @x, 0
+FROM t1
+WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
+77282760, 77908170, 70923370, 68066360);
+SET @x = @x + 1;
+UNTIL @x > 30 END REPEAT;
+END $
+CALL p();
+SELECT count(*) FROM giant_table;
+count(*)
+270
+INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
+(89628210,1), (77869520,2);
+INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
+(61069031, 1, 1);
+EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
+some_other_id, flags
+FROM giant_table
+WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
+84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
+89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
+70923271, 68066180)
+AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using where
+DROP PROCEDURE p;
+DROP TABLE giant_table, t1;
set optimizer_switch=default;
+++ /dev/null
-Ã\83"RT @niouzechun: é\81\98â\88\9aõ\80\81®ç¹\9dä¸\8aã\83£ç¹\9dæ\96\90õ\80\87³ç¹§ï½¨ç¹\9dï½³ç¹\9dç\89\99è\80³ç¸ºï½ªç¹§è\96\99â\96¡ç¸ºä»£ï½\8c縺ゥ縲â\88\9aã\81\84ç¹\9dï½³ç¹\9dä¸\8aã\83£ç¹\9dæ\96\90õ\80\87³ç¹§ï½¨ç¹\9dï½³ç¹\9d峨ï½\84è« ï½¨è\9c\89õ\80\94\8eå\99ªç¸ºï½ªç¸ºé¡\98ゥア繧å\81µâ\89 縺セ繧é¡\94ゥè\82´ï½¥ï½µé\80§õ\80\8b\96â\86\93é\9e«ã\82\87å\8f\99縺å\8a±â\86\91縺õ\80\8b\9aç\82\8aé\80\95ア縺ッ縲â\88«æ¨\9fè\9e³æº\98õ\80\81è\8e コé\80\95æº\98õ\80\81®è\9d\93コè\9bャé\80§õ\80\8b\96â\86\93縺õ\80\91\8eâ\88ªç¸ºä¸\8aï¼\9e縺ä¹\9dâ\86\91縺õ\80\8b\96ï¼ è\8d³æ¦\8aケウé\81²å³¨â\96¡ç¸ºç¤¼ç\82\8aè\8d³æ¦\8aース縺 縺è\8b\93ï½¾å¸\99ï¼\9e
--- /dev/null
+# Following variables should be set:
+# $USER Name of the user
+# $PASSWORD Password to be supplied
+# $SUCCESS Whether a successful connection is expected or not
+# $DELAY_STATS Expected value of Connection_control_delay_generated
+# $USE_AUTH_PLUGIN Whether an authentication plugin is to be used or not
+# $CLIENT_AUTH_PLUGIN Authentication plugin
+
+connection default;
+disable_query_log;
+disable_result_log;
+
+if ($SUCCESS == 0)
+{
+ --echo # Connection attempt should fail.
+ if ($USE_AUTH_PLUGIN == 0)
+ {
+ --error 1
+ --exec $MYSQL -u$USER -p$PASSWORD -e "SELECT 1;" 2>&1
+ }
+ if ($USE_AUTH_PLUGIN == 1)
+ {
+ --error 1
+ --exec $MYSQL -u$USER $CLIENT_AUTH_PLUGIN -p$PASSWORD -e "SELECT 1;" 2>&1
+ }
+}
+
+if ($SUCCESS != 0)
+{
+ --echo # Connection attempt should succeed.
+ if ($USE_AUTH_PLUGIN == 0)
+ {
+ --exec $MYSQL -u$USER -p$PASSWORD -e "SELECT 1;" 2>&1
+ }
+ if ($USE_AUTH_PLUGIN == 1)
+ {
+ --exec $MYSQL -u$USER $CLIENT_AUTH_PLUGIN -p$PASSWORD -e "SELECT 1;" 2>&1
+ }
+}
+
+enable_result_log;
+--echo Connection_control_delay_generated should be $DELAY_STATS
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+enable_query_log;
--- /dev/null
+disable_query_log;
+disable_result_log;
+# Revoke proxy grants
+REVOKE PROXY ON proxied@localhost FROM u1@localhost, u2@localhost, u3@localhost;
+
+# Drop proxy users
+DROP USER u1@localhost, u2@localhost, u3@localhost;
+
+# Drop proxied user
+DROP USER proxied@localhost;
+
+# Uninstall test_plugin_server
+UNINSTALL PLUGIN test_plugin_server;
+
+# Remove plugin library
+let $auth_plugin_path= `SELECT SUBSTR('$PLUGIN_AUTH_OPT/$PLUGIN_AUTH', 14)`;
+let $connection_control_plugin_path= `SELECT SUBSTR('$CONNECTION_CONTROL_PLUGIN_OPT/$PLUGIN_AUTH', 14)`;
+
+if ($auth_plugin_path != $connection_control_plugin_path)
+{
+ --remove_file $connection_control_plugin_path
+}
+enable_result_log;
+enable_query_log;
--- /dev/null
+disable_query_log;
+#
+# Check if server has support for loading plugin
+#
+if (`SELECT @@have_dynamic_loading != 'YES'`) {
+ --skip The connection_control plugin requires dynamic loading
+}
+
+#
+# Check if the variable CONNECTION_CONTROL_PLUGIN is set
+#
+if (!$CONNECTION_CONTROL_PLUGIN) {
+ --skip The connection_control plugin requires the environment variable \$CONNECTION_CONTROL_PLUGIN to be set (normally done by mtr)
+}
+
+#
+# Check if --plugin-dir was setup for null_audit_db
+#
+if (`SELECT CONCAT('--plugin-dir=', REPLACE(@@plugin_dir, '\\\\', '/')) != '$CONNECTION_CONTROL_PLUGIN_OPT/'`) {
+ --skip The connection_control plugin requires that --plugin-dir is set to the connection_control plugin dir (either the .opt file does not contain \$CONNECTION_CONTROL_PLUGIN_OPT or another plugin is in use)
+}
+enable_query_log;
--- /dev/null
+disable_query_log;
+#
+# Check if server has support for loading plugin
+#
+if (`SELECT @@have_dynamic_loading != 'YES'`) {
+ --skip The connection_control plugin requires dynamic loading
+}
+
+#
+# Check if the variable PLUGIN_AUTH is set
+#
+if (!$PLUGIN_AUTH) {
+ --skip The connection_control plugin requires the environment variable \$PLUGIN_AUTH to be set (normally done by mtr)
+}
+
+enable_query_log;
--- /dev/null
+# Install connection_control plugin
+--replace_result $CONNECTION_CONTROL_PLUGIN CONNECTION_CONTROL_LIB
+eval INSTALL PLUGIN connection_control SONAME '$CONNECTION_CONTROL_PLUGIN';
+--replace_result $CONNECTION_CONTROL_PLUGIN CONNECTION_CONTROL_LIB
+eval INSTALL PLUGIN connection_control_failed_login_attempts SONAME '$CONNECTION_CONTROL_PLUGIN';
--- /dev/null
+# set after marker
+# $SERVER_RESPONSE_TIME [IN] Expected delay
+disable_query_log;
+disable_result_log;
+SET @after= TIMESTAMP(current_time());
+SET @server_response_time= TIMESTAMPDIFF(SECOND, @before, @after);
+enable_result_log;
+
+--eval SELECT @server_response_time >= $SERVER_RESPONSE_TIME
+enable_query_log;
--- /dev/null
+# Set before marker
+disable_query_log;
+disable_result_log;
+SET @before= TIMESTAMP(current_time());
+enable_result_log;
+enable_query_log;
--- /dev/null
+disable_query_log;
+disable_result_log;
+# Copy PLUGIN_AUTH library to CONNECTION_CONTROL_OPT location
+let $auth_plugin_path= `SELECT SUBSTR('$PLUGIN_AUTH_OPT/$PLUGIN_AUTH', 14)`;
+let $connection_control_plugin_path= `SELECT SUBSTR('$CONNECTION_CONTROL_PLUGIN_OPT/$PLUGIN_AUTH', 14)`;
+
+if ($auth_plugin_path != $connection_control_plugin_path)
+{
+ --error 0, 1
+ --remove_file $connection_control_plugin_path
+ --copy_file $auth_plugin_path $connection_control_plugin_path
+}
+# Install test_plugin_server
+eval INSTALL PLUGIN test_plugin_server SONAME '$PLUGIN_AUTH';
+
+# Create proxied@localhost
+CREATE USER proxied@localhost IDENTIFIED BY 'proxied_password';
+
+# Create u1@localhost, u2@localhost, u3@localhst
+CREATE USER u1@localhost IDENTIFIED WITH test_plugin_server AS 'proxied';
+CREATE USER u2@localhost IDENTIFIED WITH test_plugin_server AS 'proxied';
+CREATE USER u3@localhost IDENTIFIED WITH test_plugin_server AS 'proxied';
+
+GRANT PROXY ON proxied@localhost TO u1@localhost, u2@localhost, u3@localhost;
+enable_result_log;
+enable_query_log;
--- /dev/null
+# Uninstall connection_control plugin
+--disable_warnings
+UNINSTALL PLUGIN connection_control;
+--enable_warnings
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
--- /dev/null
+# Connection delay tests for valid user accounts
+
+# ----------------------------------------------------------------------
+
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Create anonymous user
+CREATE USER ''@'localhost';
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+# Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @global.connection_control_max_connection_delay = 1000;
+
+# ----------------------------------------------------------------------
+
+# Following attempts will not experience any delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+
+# Following attempts will experience delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 1
+Variable_name Value
+Connection_control_delay_generated 1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 2
+Variable_name Value
+Connection_control_delay_generated 2
+# Connection attempt should fail.
+Connection_control_delay_generated should be 3
+Variable_name Value
+Connection_control_delay_generated 3
+
+# ----------------------------------------------------------------------
+
+# Cleanup
+# Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+# Drop anonymous user
+DROP USER ''@'localhost';
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+
+# ----------------------------------------------------------------------
--- /dev/null
+# Connection delay tests for valid user accounts
+
+# ----------------------------------------------------------------------
+
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Create user accounts for testing
+CREATE USER u1@localhost IDENTIFIED BY 'abcd';
+CREATE USER u2@localhost IDENTIFIED BY 'abcd';
+CREATE USER u3@localhost IDENTIFIED BY 'abcd';
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+# Avoid triggering delay
+SET @@global.connection_control_failed_connections_threshold = 100;
+
+# ----------------------------------------------------------------------
+
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: NO)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+# connection_control_failed_login_attempts should contain entries
+# for u1@localhost and u2@localhost
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 2
+'u2'@'localhost' 1
+connect(localhost,u3,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: NO)
+connect(localhost,u2,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+# connection_control_failed_login_attempts should contain entries
+# for u1@localhost, u2@localhost and u3@localhost
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 2
+'u2'@'localhost' 2
+'u3'@'localhost' 2
+connect(localhost,u2,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: NO)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+# failed connection counts should have increased for all users
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 3
+'u2'@'localhost' 3
+'u3'@'localhost' 3
+connect(localhost,u2,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: NO)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+# Try various queries
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+WHERE USERHOST = '\'u1\'@\'localhost\'';
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 6
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+WHERE USERHOST LIKE '%u2%';
+USERHOST FAILED_ATTEMPTS
+'u2'@'localhost' 4
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+WHERE FAILED_ATTEMPTS > 4;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 6
+'u3'@'localhost' 5
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+WHERE FAILED_ATTEMPTS > 5;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 6
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+WHERE FAILED_ATTEMPTS < 5;
+USERHOST FAILED_ATTEMPTS
+'u2'@'localhost' 4
+# After successful login, corresponding entry should disappear
+# from the view
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u3'@'localhost' 5
+# A user without privileges should not be able to any information
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: NO)
+connect(localhost,u3,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+# Subsequent failed attempts should put entries back in the view
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 1
+'u2'@'localhost' 1
+'u3'@'localhost' 1
+connect(localhost,u4,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u4'@'localhost' (using password: NO)
+connect(localhost,u5,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u5'@'localhost' (using password: YES)
+connect(localhost,u6,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u6'@'localhost' (using password: YES)
+# Attempts by invalid accounts should be shown in the view
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 1
+'u2'@'localhost' 1
+'u3'@'localhost' 1
+'u4'@'localhost' 1
+'u5'@'localhost' 1
+'u6'@'localhost' 1
+
+# ----------------------------------------------------------------------
+
+# Cleanup
+# Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+# Remove user accounts created for the test
+DROP USER u1@localhost;
+DROP USER u2@localhost;
+DROP USER u3@localhost;
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+
+# ----------------------------------------------------------------------
--- /dev/null
+# Connection delay tests for valid user accounts
+
+# ----------------------------------------------------------------------
+
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+# Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 1000;
+
+# ----------------------------------------------------------------------
+
+# Following attempts will not experience any delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+# Connection attempt should fail.
+Connection_control_delay_generated should be 0
+Variable_name Value
+Connection_control_delay_generated 0
+
+# Following attempts will experience delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 1
+Variable_name Value
+Connection_control_delay_generated 1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 2
+Variable_name Value
+Connection_control_delay_generated 2
+# Connection attempt should fail.
+Connection_control_delay_generated should be 3
+Variable_name Value
+Connection_control_delay_generated 3
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 4
+'u2'@'localhost' 4
+'u3'@'localhost' 4
+# Connection attempt should fail.
+Connection_control_delay_generated should be 4
+Variable_name Value
+Connection_control_delay_generated 4
+# Connection attempt should fail.
+Connection_control_delay_generated should be 5
+Variable_name Value
+Connection_control_delay_generated 5
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+USERHOST FAILED_ATTEMPTS
+'u1'@'localhost' 5
+'u2'@'localhost' 5
+'u3'@'localhost' 5
+
+# ----------------------------------------------------------------------
+
+# Cleanup
+# Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+
+# ----------------------------------------------------------------------
--- /dev/null
+# Connection delay tests for valid user accounts
+
+# ----------------------------------------------------------------------
+
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Create user account for testing
+CREATE USER u1 IDENTIFIED BY 'abcd';
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+SET @saved_min_delay = @@global.connection_control_min_connection_delay;
+# Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 4000;
+SET @@global.connection_control_min_connection_delay = 2000;
+
+# ----------------------------------------------------------------------
+
+# Make enough failed attempts to trigger delays
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+
+# Following attempts will experience delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 1
+Variable_name Value
+Connection_control_delay_generated 1
+@server_response_time >= 2
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 2
+Variable_name Value
+Connection_control_delay_generated 2
+@server_response_time >= 2
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 3
+Variable_name Value
+Connection_control_delay_generated 3
+@server_response_time >= 3
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 4
+Variable_name Value
+Connection_control_delay_generated 4
+@server_response_time >= 4
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 5
+Variable_name Value
+Connection_control_delay_generated 5
+@server_response_time >= 4
+1
+
+# Following attempts will not experience any delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 5
+Variable_name Value
+Connection_control_delay_generated 5
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 5
+Variable_name Value
+Connection_control_delay_generated 5
+
+# ----------------------------------------------------------------------
+
+# Cleanup
+# Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_min_delay;
+# Remove user account created for the test
+DROP USER u1;
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+
+# ----------------------------------------------------------------------
--- /dev/null
+#-----------------------------------------------------------------------
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Do proxy setup
+CALL mtr.add_suppression("Plugin test_plugin_server reported: 'Wrong password supplied for proxied'");
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+SET @saved_min_delay= @@global.connection_control_min_connection_delay;
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 4000;
+SET @@global.connection_control_min_connection_delay = 2000;
+#-----------------------------------------------------------------------
+# Case 1 : Testss for valid user accounts
+# Make enough failed attempts to trigger delays
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u3,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u2,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u2,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+# Following attempts will experience delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 1
+Variable_name Value
+Connection_control_delay_generated 1
+@server_response_time >= 2
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 2
+Variable_name Value
+Connection_control_delay_generated 2
+@server_response_time >= 2
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 3
+Variable_name Value
+Connection_control_delay_generated 3
+@server_response_time >= 2
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 4
+Variable_name Value
+Connection_control_delay_generated 4
+@server_response_time >= 2
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 5
+Variable_name Value
+Connection_control_delay_generated 5
+@server_response_time >= 2
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+@server_response_time >= 2
+1
+# Following attempts will not experience any delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+#-----------------------------------------------------------------------
+# Cleanup
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_min_delay;
+# Remove user accounts
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+#-----------------------------------------------------------------------
--- /dev/null
+# Connection delay tests for valid user accounts
+
+# ----------------------------------------------------------------------
+
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Create user accounts for testing
+CREATE USER u1 IDENTIFIED BY 'abcd';
+CREATE USER u2 IDENTIFIED BY 'abcd';
+CREATE USER u3 IDENTIFIED BY 'abcd';
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+SET @saved_min_delay= @@global.connection_control_min_connection_delay;
+# Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 4000;
+SET @@global.connection_control_min_connection_delay = 2000;
+
+# ----------------------------------------------------------------------
+
+# Make enough failed attempts to trigger delays
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u3,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u2,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u2,,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+
+# Following attempts will experience delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 1
+Variable_name Value
+Connection_control_delay_generated 1
+@server_response_time >= 2
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 2
+Variable_name Value
+Connection_control_delay_generated 2
+@server_response_time >= 2
+1
+# Connection attempt should fail.
+Connection_control_delay_generated should be 3
+Variable_name Value
+Connection_control_delay_generated 3
+@server_response_time >= 2
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 4
+Variable_name Value
+Connection_control_delay_generated 4
+@server_response_time >= 2
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 5
+Variable_name Value
+Connection_control_delay_generated 5
+@server_response_time >= 2
+1
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+@server_response_time >= 2
+1
+
+# Following attempts will not experience any delay in server respose
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should fail.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+# Connection attempt should succeed.
+Connection_control_delay_generated should be 6
+Variable_name Value
+Connection_control_delay_generated 6
+
+# ----------------------------------------------------------------------
+
+# Cleanup
+# Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_min_delay;
+# Remove user accounts created for the test
+DROP USER u1;
+DROP USER u2;
+DROP USER u3;
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+
+# ----------------------------------------------------------------------
--- /dev/null
+# Connection delay tests for valid user accounts
+
+# ----------------------------------------------------------------------
+
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+# Create user accounts for testing
+CREATE USER u1@localhost IDENTIFIED BY 'abcd';
+CREATE USER u2@localhost IDENTIFIED BY 'abcd';
+CREATE USER u3@localhost IDENTIFIED BY 'abcd';
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay= @@global.connection_control_max_connection_delay;
+# Set small threshold
+SET @@global.connection_control_failed_connections_threshold = 1;
+# Set small max delay
+SET @@global.connection_control_max_connection_delay = 1000;
+
+# ----------------------------------------------------------------------
+
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: NO)
+connect(localhost,u3,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+# Check Connection_control_delay_generated - Should be 0
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+Variable_name Value
+Connection_control_delay_generated 0
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: NO)
+connect(localhost,u2,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u2,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: NO)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+# Check Connection_control_delay_generated - Should be 6
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+Variable_name Value
+Connection_control_delay_generated 6
+connect(localhost,u2,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: NO)
+connect(localhost,u1,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: YES)
+connect(localhost,u3,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+# Check Connection_control_delay_generated - Should be 9
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+Variable_name Value
+Connection_control_delay_generated 9
+# Successful connection but delays would still be generated
+# Should be 12
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+Variable_name Value
+Connection_control_delay_generated 12
+# Setting failed connection threshold should reset delay statistics
+SET @@global.connection_control_failed_connections_threshold = 1;
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: NO)
+connect(localhost,u3,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+connect(localhost,u1,,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u1'@'localhost' (using password: NO)
+connect(localhost,u3,haha,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u3'@'localhost' (using password: YES)
+connect(localhost,u2,efgh,test,MASTER_PORT,MASTER_SOCKET);
+ERROR 28000: Access denied for user 'u2'@'localhost' (using password: YES)
+# Check Connection_control_delay_generated - Should be 3
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+Variable_name Value
+Connection_control_delay_generated 3
+
+# ----------------------------------------------------------------------
+
+# Cleanup
+# Restore original values of conenction_control variables
+SET @@global.connection_control_max_connection_delay= @saved_max_delay;
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+# Remove user accounts created for the test
+DROP USER u1@localhost;
+DROP USER u2@localhost;
+DROP USER u3@localhost;
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+
+# ----------------------------------------------------------------------
--- /dev/null
+#-----------------------------------------------------------------------
+# Setup
+# Install connection_control plugin
+INSTALL PLUGIN connection_control SONAME 'CONNECTION_CONTROL_LIB';
+INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'CONNECTION_CONTROL_LIB';
+CREATE USER no_privs@localhost IDENTIFIED BY 'abcd';
+#-----------------------------------------------------------------------
+# Case 1 : connection_control_failed_connections_threshold
+SHOW GRANTS;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+SET @saved_value = @@global.connection_control_failed_connections_threshold;
+SELECT @saved_value;
+@saved_value
+3
+SET @@global.connection_control_failed_connections_threshold = @saved_value;
+# 1.1 : Setting connection_control_failed_connections_threshold to valid
+# value
+SET @@global.connection_control_failed_connections_threshold = 20;
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+20
+SET @@global.connection_control_failed_connections_threshold = 2000;
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+2000
+SET @@global.connection_control_failed_connections_threshold = 2147483647;
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+2147483647
+SET @@global.connection_control_failed_connections_threshold = DEFAULT;
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+# 1.2 : Setting connection_control_failed_connections_threshold to
+# invalid value
+SET @@global.connection_control_failed_connections_threshold = NULL;
+ERROR 42000: Incorrect argument type to variable 'connection_control_failed_connections_threshold'
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = `SELECT * FROM mysql.user`;
+ERROR 42000: Incorrect argument type to variable 'connection_control_failed_connections_threshold'
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = -20;
+ERROR 42000: Variable 'connection_control_failed_connections_threshold' can't be set to the value of '-20'
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = 9223372036854775808;
+ERROR 42000: Variable 'connection_control_failed_connections_threshold' can't be set to the value of '9223372036854775808'
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = -9223372036854775808;
+ERROR 42000: Variable 'connection_control_failed_connections_threshold' can't be set to the value of '-9223372036854775808'
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+# Switch to conn_no_privs
+# 1.3 : Use no_privs@localhost to set
+# connection_control_failed_connections_threshold to valid value
+SET @@global.connection_control_failed_connections_threshold = 2147483647;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = DEFAULT;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+# 1.4 : Use no_privs@localhost to set
+# connection_control_failed_connections_threshold to invalid value
+SET @@global.connection_control_failed_connections_threshold = NULL;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = 9223372036854775808;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+SET @@global.connection_control_failed_connections_threshold = @saved_value;
+SELECT @@global.connection_control_failed_connections_threshold;
+@@global.connection_control_failed_connections_threshold
+3
+#-----------------------------------------------------------------------
+# Case 2 : connection_control_min_connection_delay
+SET @saved_value= @@global.connection_control_min_connection_delay;
+SELECT @saved_value;
+@saved_value
+1000
+# 2.1 : Setting connection_control_min_connection_delay to valid
+# value
+SET @@global.connection_control_min_connection_delay = 20000;
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+20000
+SET @@global.connection_control_min_connection_delay = 2000;
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+2000
+SET @@global.connection_control_min_connection_delay = 2147483647;
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+2147483647
+SET @@global.connection_control_min_connection_delay = DEFAULT;
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+# 2.2 : Setting connection_control_min_connection_delay to
+# invalid value
+SET @@global.connection_control_min_connection_delay = NULL;
+ERROR 42000: Incorrect argument type to variable 'connection_control_min_connection_delay'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_min_connection_delay = `SELECT * FROM mysql.user`;
+ERROR 42000: Incorrect argument type to variable 'connection_control_min_connection_delay'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_min_connection_delay = -20;
+ERROR 42000: Variable 'connection_control_min_connection_delay' can't be set to the value of '-20'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_min_connection_delay = 9223372036854775808;
+ERROR 42000: Variable 'connection_control_min_connection_delay' can't be set to the value of '9223372036854775808'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_min_connection_delay = -9223372036854775808;
+ERROR 42000: Variable 'connection_control_min_connection_delay' can't be set to the value of '-9223372036854775808'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET@@global.connection_control_min_connection_delay = 20;
+ERROR 42000: Variable 'connection_control_min_connection_delay' can't be set to the value of '20'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+# Switch to conn_no_privs
+# 2.3 : Use no_privs@localhost to set
+# connection_control_min_connection_delay to valid value
+SET @@global.connection_control_min_connection_delay = 2147483647;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_min_connection_delay = DEFAULT;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+# 2.4 : Use no_privs@localhost to set
+# connection_control_min_connection_delay to invalid value
+SET @@global.connection_control_min_connection_delay = NULL;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_min_connection_delay = 9223372036854775808;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+# Switch to default connection
+# 2.5 : Setting connection_control_min_connection_delay to a value
+# greater than connection_control_max_connection_delay
+SET @saved_max_delay= @@global.connection_control_max_connection_delay;
+SET @@global.connection_control_max_connection_delay= 10000;
+SET @@global.connection_control_min_connection_delay= 11000;
+ERROR 42000: Variable 'connection_control_min_connection_delay' can't be set to the value of '11000'
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+SET @@global.connection_control_max_connection_delay= @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_value;
+SELECT @@global.connection_control_min_connection_delay;
+@@global.connection_control_min_connection_delay
+1000
+#-----------------------------------------------------------------------
+# Case 3 : connection_control_max_connection_delay
+SET @saved_value= @@global.connection_control_max_connection_delay;
+SELECT @saved_value;
+@saved_value
+2147483647
+# 3.1 : Setting connection_control_max_connection_delay to valid
+# value
+SET @@global.connection_control_max_connection_delay = 20000;
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+20000
+SET @@global.connection_control_max_connection_delay = 2000;
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2000
+SET @@global.connection_control_max_connection_delay = 2147483647;
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = DEFAULT;
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+# 3.2 : Setting connection_control_max_connection_delay to
+# invalid value
+SET @@global.connection_control_max_connection_delay = NULL;
+ERROR 42000: Incorrect argument type to variable 'connection_control_max_connection_delay'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = `SELECT * FROM mysql.user`;
+ERROR 42000: Incorrect argument type to variable 'connection_control_max_connection_delay'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = -20;
+ERROR 42000: Variable 'connection_control_max_connection_delay' can't be set to the value of '-20'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = 9223372036854775808;
+ERROR 42000: Variable 'connection_control_max_connection_delay' can't be set to the value of '9223372036854775808'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = -9223372036854775808;
+ERROR 42000: Variable 'connection_control_max_connection_delay' can't be set to the value of '-9223372036854775808'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = 20;
+ERROR 42000: Variable 'connection_control_max_connection_delay' can't be set to the value of '20'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+# Switch to conn_no_privs
+# 3.3 : Use no_privs@localhost to set
+# connection_control_max_connection_delay to valid value
+SET @@global.connection_control_max_connection_delay = 2147483647;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = DEFAULT;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+# 3.4 : Use no_privs@localhost to set
+# connection_control_max_connection_delay to invalid value
+SET @@global.connection_control_max_connection_delay = NULL;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_max_connection_delay = 9223372036854775808;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+# Switch to default connection
+# 3.5 : Setting connection_control_min_connection_delay to a value
+# greater than connection_control_max_connection_delay
+SET @saved_min_delay= @@global.connection_control_min_connection_delay;
+SET @@global.connection_control_min_connection_delay= 11000;
+SET @@global.connection_control_max_connection_delay= 10000;
+ERROR 42000: Variable 'connection_control_max_connection_delay' can't be set to the value of '10000'
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+SET @@global.connection_control_min_connection_delay= @saved_min_delay;
+SET @@global.connection_control_max_connection_delay = @saved_value;
+SELECT @@global.connection_control_max_connection_delay;
+@@global.connection_control_max_connection_delay
+2147483647
+#-----------------------------------------------------------------------
+# Cleanup
+DROP USER no_privs@localhost;
+# Uninstall connection_control plugin
+UNINSTALL PLUGIN connection_control;
+UNINSTALL PLUGIN connection_control_failed_login_attempts;
+#-----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo # Connection delay tests for valid user accounts
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+
+--echo # Create anonymous user
+CREATE USER ''@'localhost';
+
+--echo # Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+
+-- echo # Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @global.connection_control_max_connection_delay = 1000;
+
+# We don't need to use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 0;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Following attempts will not experience any delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+--echo
+--echo # Following attempts will experience delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=1;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=2;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=3;
+--source ../inc/check_connection_delay.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Cleanup
+
+connection default;
+
+--echo # Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+
+--echo # Drop anonymous user
+DROP USER ''@'localhost';
+
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo # Connection delay tests for valid user accounts
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+
+--echo # Create user accounts for testing
+CREATE USER u1@localhost IDENTIFIED BY 'abcd';
+CREATE USER u2@localhost IDENTIFIED BY 'abcd';
+CREATE USER u3@localhost IDENTIFIED BY 'abcd';
+
+--echo # Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+
+-- echo # Avoid triggering delay
+SET @@global.connection_control_failed_connections_threshold = 100;
+
+# We don't need to use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 0;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,efgh,,,,);
+
+-- echo # connection_control_failed_login_attempts should contain entries
+-- echo # for u1@localhost and u2@localhost
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+-- echo # connection_control_failed_login_attempts should contain entries
+-- echo # for u1@localhost, u2@localhost and u3@localhost
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+-- echo # failed connection counts should have increased for all users
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+
+-- echo # Try various queries
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+ WHERE USERHOST = '\'u1\'@\'localhost\'';
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+ WHERE USERHOST LIKE '%u2%';
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+ WHERE FAILED_ATTEMPTS > 4;
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+ WHERE FAILED_ATTEMPTS > 5;
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts
+ WHERE FAILED_ATTEMPTS < 5;
+
+connect(con_u1, localhost, u1,abcd,,,,);
+connect(con_u2, localhost, u2,abcd,,,,);
+connection default;
+--disconnect con_u1
+--disconnect con_u2
+
+--echo # After successful login, corresponding entry should disappear
+--echo # from the view
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+connect(con_u3, localhost, u3,abcd,,,,);
+--echo # A user without privileges should not be able to any information
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+connection default;
+--disconnect con_u3
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,efgh,,,,);
+
+-- echo # Subsequent failed attempts should put entries back in the view
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u4, localhost, u4,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u5, localhost, u5,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u6, localhost, u6,efgh,,,,);
+
+-- echo # Attempts by invalid accounts should be shown in the view
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Cleanup
+
+connection default;
+
+--echo # Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+
+--echo # Remove user accounts created for the test
+DROP USER u1@localhost;
+DROP USER u2@localhost;
+DROP USER u3@localhost;
+
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo # Connection delay tests for valid user accounts
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+
+--echo # Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+
+-- echo # Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 1000;
+
+# We don't need to use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 0;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Following attempts will not experience any delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=0;
+--source ../inc/check_connection_delay.inc
+
+--echo
+--echo # Following attempts will experience delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=1;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=2;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=3;
+--source ../inc/check_connection_delay.inc
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+let $USER=u3;
+let $PASSWORD=abcd;
+let $SUCCESS=0;
+let $DELAY_STATS=4;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=abcd;
+let $SUCCESS=0;
+let $DELAY_STATS=5;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u1;
+let $PASSWORD=abcd;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+--sorted_result
+SELECT * FROM INFORMATION_SCHEMA.connection_control_failed_login_attempts;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Cleanup
+
+connection default;
+
+--echo # Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+
+
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo # Connection delay tests for valid user accounts
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+
+--echo # Create user account for testing
+CREATE USER u1 IDENTIFIED BY 'abcd';
+
+--echo # Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+SET @saved_min_delay = @@global.connection_control_min_connection_delay;
+
+-- echo # Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 4000;
+SET @@global.connection_control_min_connection_delay = 2000;
+
+# We don't need to use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 0;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Make enough failed attempts to trigger delays
+
+disable_result_log;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+
+enable_result_log;
+
+--echo
+--echo # Following attempts will experience delay in server respose
+
+# Trying with invalid passwords
+
+--source ../inc/set_before_marker.inc
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=1;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=2;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=3;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 3;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=4;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 4;
+--source ../inc/set_after_marker.inc
+
+# Trying with valid passwords
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=5;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 4;
+--source ../inc/set_after_marker.inc
+
+--echo
+--echo # Following attempts will not experience any delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=5;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u1;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=5;
+--source ../inc/check_connection_delay.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Cleanup
+
+connection default;
+
+--echo # Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_min_delay;
+
+--echo # Remove user account created for the test
+DROP USER u1;
+
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+--source ../inc/have_test_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo #-----------------------------------------------------------------------
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+--echo # Do proxy setup
+--source ../inc/setup_proxy_accounts.inc
+
+# Suppress error messages from test_plugin_server
+CALL mtr.add_suppression("Plugin test_plugin_server reported: 'Wrong password supplied for proxied'");
+
+# Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+SET @saved_min_delay= @@global.connection_control_min_connection_delay;
+
+# Set a small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 4000;
+SET @@global.connection_control_min_connection_delay = 2000;
+
+# We will use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 1;
+let $CLIENT_AUTH_PLUGIN= $PLUGIN_AUTH_OPT;
+
+--echo #-----------------------------------------------------------------------
+
+--echo # Case 1 : Testss for valid user accounts
+
+--echo # Make enough failed attempts to trigger delays
+
+disable_result_log;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u2,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u3,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u2,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u3,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u2,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u3,efgh,,,,);
+
+enable_result_log;
+
+--echo # Following attempts will experience delay in server respose
+
+# Trying with invalid passwords
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=1;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=2;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=3;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+# Trying with valid passwords
+# Even though u1@localhost, u2@localhost and u3@localhost are
+# proxying same user - proxied@localhost, we will not use
+# proxied@localohst for recording failed attempts information.
+# Rather, we will rely on proxying users' data.
+# So each of the following should get delay of 2 seconds
+# or more.
+
+--source ../inc/set_before_marker.inc
+let $USER=u3;
+let $PASSWORD=proxied;
+let $SUCCESS=1;
+let $DELAY_STATS=4;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u2;
+let $PASSWORD=proxied;
+let $SUCCESS=1;
+let $DELAY_STATS=5;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=proxied;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+
+--echo # Following attempts will not experience any delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u1;
+let $PASSWORD=proxied;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=proxied;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=proxied;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+--echo #-----------------------------------------------------------------------
+--echo # Cleanup
+
+connection default;
+
+# Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_min_delay;
+
+--echo # Remove user accounts
+--source ../inc/cleanup_proxy_accounts.inc
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+--echo #-----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo # Connection delay tests for valid user accounts
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+
+--echo # Create user accounts for testing
+CREATE USER u1 IDENTIFIED BY 'abcd';
+CREATE USER u2 IDENTIFIED BY 'abcd';
+CREATE USER u3 IDENTIFIED BY 'abcd';
+
+--echo # Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay = @@global.connection_control_max_connection_delay;
+SET @saved_min_delay= @@global.connection_control_min_connection_delay;
+
+-- echo # Set small values for connection_control variables
+SET @@global.connection_control_failed_connections_threshold = 3;
+SET @@global.connection_control_max_connection_delay = 4000;
+SET @@global.connection_control_min_connection_delay = 2000;
+
+# We don't need to use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 0;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Make enough failed attempts to trigger delays
+
+disable_result_log;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+enable_result_log;
+
+--echo
+--echo # Following attempts will experience delay in server respose
+
+# Trying with invalid passwords
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=1;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=2;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=3;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+# Trying with valid passwords
+
+--source ../inc/set_before_marker.inc
+let $USER=u3;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=4;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u2;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=5;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--source ../inc/set_before_marker.inc
+let $USER=u1;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $SERVER_RESPONSE_TIME= 2;
+--source ../inc/set_after_marker.inc
+
+--echo
+--echo # Following attempts will not experience any delay in server respose
+
+let $USER=u1;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=hoho;
+let $SUCCESS=0;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u1;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u2;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+let $USER=u3;
+let $PASSWORD=abcd;
+let $SUCCESS=1;
+let $DELAY_STATS=6;
+--source ../inc/check_connection_delay.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Cleanup
+
+connection default;
+
+--echo # Restore original values of conenction_control variables
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+SET @@global.connection_control_max_connection_delay = @saved_max_delay;
+SET @@global.connection_control_min_connection_delay = @saved_min_delay;
+
+--echo # Remove user accounts created for the test
+DROP USER u1;
+DROP USER u2;
+DROP USER u3;
+
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo # Connection delay tests for valid user accounts
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+
+--echo # Create user accounts for testing
+CREATE USER u1@localhost IDENTIFIED BY 'abcd';
+CREATE USER u2@localhost IDENTIFIED BY 'abcd';
+CREATE USER u3@localhost IDENTIFIED BY 'abcd';
+
+--echo # Save original values of connection_control variables
+SET @saved_connections_threshold = @@global.connection_control_failed_connections_threshold;
+SET @saved_max_delay= @@global.connection_control_max_connection_delay;
+
+-- echo # Set small threshold
+SET @@global.connection_control_failed_connections_threshold = 1;
+-- echo # Set small max delay
+SET @@global.connection_control_max_connection_delay = 1000;
+
+# We don't need to use client side authentication plugin for this test.
+let $USE_AUTH_PLUGIN= 0;
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,efgh,,,,);
+
+--echo # Check Connection_control_delay_generated - Should be 0
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+--echo # Check Connection_control_delay_generated - Should be 6
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,efgh,,,,);
+
+--echo # Check Connection_control_delay_generated - Should be 9
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+
+connect(con_u1, localhost, u1,abcd,,,,);
+connect(con_u2, localhost, u2,abcd,,,,);
+connect(con_u3, localhost, u3,abcd,,,,);
+connection default;
+--disconnect con_u1
+--disconnect con_u2
+--disconnect con_u3
+
+--echo # Successful connection but delays would still be generated
+--echo # Should be 12
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+
+--echo # Setting failed connection threshold should reset delay statistics
+SET @@global.connection_control_failed_connections_threshold = 1;
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,efgh,,,,);
+
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u1, localhost, u1,,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u3, localhost, u3,haha,,,,);
+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
+--error ER_ACCESS_DENIED_ERROR
+connect(fail_con_u2, localhost, u2,efgh,,,,);
+
+--echo # Check Connection_control_delay_generated - Should be 3
+SHOW STATUS LIKE 'Connection_control_delay_generated';
+
+--echo
+--echo # ----------------------------------------------------------------------
+--echo
+
+--echo # Cleanup
+
+connection default;
+
+--echo # Restore original values of conenction_control variables
+SET @@global.connection_control_max_connection_delay= @saved_max_delay;
+SET @@global.connection_control_failed_connections_threshold = @saved_connections_threshold;
+
+--echo # Remove user accounts created for the test
+DROP USER u1@localhost;
+DROP USER u2@localhost;
+DROP USER u3@localhost;
+
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+
+--echo
+--echo # ----------------------------------------------------------------------
--- /dev/null
+$CONNECTION_CONTROL_PLUGIN_OPT
--- /dev/null
+# Skip the test in embedded mode
+--source include/not_embedded.inc
+
+# Make sure that connection_control plugin can be loaded
+--source ../inc/have_connection_control_plugin.inc
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+--echo #-----------------------------------------------------------------------
+--echo # Setup
+--echo # Install connection_control plugin
+--source ../inc/install_connection_control_plugin.inc
+CREATE USER no_privs@localhost IDENTIFIED BY 'abcd';
+connect(conn_no_privs, localhost, no_privs, abcd,,,,);
+connection default;
+--echo #-----------------------------------------------------------------------
+
+--echo # Case 1 : connection_control_failed_connections_threshold
+SHOW GRANTS;
+SET @saved_value = @@global.connection_control_failed_connections_threshold;
+SELECT @saved_value;
+SET @@global.connection_control_failed_connections_threshold = @saved_value;
+
+--echo # 1.1 : Setting connection_control_failed_connections_threshold to valid
+--echo # value
+
+SET @@global.connection_control_failed_connections_threshold = 20;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+SET @@global.connection_control_failed_connections_threshold = 2000;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+SET @@global.connection_control_failed_connections_threshold = 2147483647;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+SET @@global.connection_control_failed_connections_threshold = DEFAULT;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--echo # 1.2 : Setting connection_control_failed_connections_threshold to
+--echo # invalid value
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@global.connection_control_failed_connections_threshold = NULL;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@global.connection_control_failed_connections_threshold = `SELECT * FROM mysql.user`;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_failed_connections_threshold = -20;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_failed_connections_threshold = 9223372036854775808;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_failed_connections_threshold = -9223372036854775808;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--echo # Switch to conn_no_privs
+connection conn_no_privs;
+--echo # 1.3 : Use no_privs@localhost to set
+--echo # connection_control_failed_connections_threshold to valid value
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_failed_connections_threshold = 2147483647;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_failed_connections_threshold = DEFAULT;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--echo # 1.4 : Use no_privs@localhost to set
+--echo # connection_control_failed_connections_threshold to invalid value
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_failed_connections_threshold = NULL;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_failed_connections_threshold = 9223372036854775808;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+connection default;
+
+SET @@global.connection_control_failed_connections_threshold = @saved_value;
+SELECT @@global.connection_control_failed_connections_threshold;
+
+--echo #-----------------------------------------------------------------------
+
+--echo # Case 2 : connection_control_min_connection_delay
+
+SET @saved_value= @@global.connection_control_min_connection_delay;
+SELECT @saved_value;
+
+--echo # 2.1 : Setting connection_control_min_connection_delay to valid
+--echo # value
+
+SET @@global.connection_control_min_connection_delay = 20000;
+SELECT @@global.connection_control_min_connection_delay;
+
+SET @@global.connection_control_min_connection_delay = 2000;
+SELECT @@global.connection_control_min_connection_delay;
+
+SET @@global.connection_control_min_connection_delay = 2147483647;
+SELECT @@global.connection_control_min_connection_delay;
+
+SET @@global.connection_control_min_connection_delay = DEFAULT;
+SELECT @@global.connection_control_min_connection_delay;
+
+--echo # 2.2 : Setting connection_control_min_connection_delay to
+--echo # invalid value
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@global.connection_control_min_connection_delay = NULL;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@global.connection_control_min_connection_delay = `SELECT * FROM mysql.user`;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_min_connection_delay = -20;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_min_connection_delay = 9223372036854775808;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_min_connection_delay = -9223372036854775808;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET@@global.connection_control_min_connection_delay = 20;
+SELECT @@global.connection_control_min_connection_delay;
+
+--echo # Switch to conn_no_privs
+connection conn_no_privs;
+--echo # 2.3 : Use no_privs@localhost to set
+--echo # connection_control_min_connection_delay to valid value
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_min_connection_delay = 2147483647;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_min_connection_delay = DEFAULT;
+SELECT @@global.connection_control_min_connection_delay;
+
+--echo # 2.4 : Use no_privs@localhost to set
+--echo # connection_control_min_connection_delay to invalid value
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_min_connection_delay = NULL;
+SELECT @@global.connection_control_min_connection_delay;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_min_connection_delay = 9223372036854775808;
+SELECT @@global.connection_control_min_connection_delay;
+
+--echo # Switch to default connection
+connection default;
+--echo # 2.5 : Setting connection_control_min_connection_delay to a value
+--echo # greater than connection_control_max_connection_delay
+
+SET @saved_max_delay= @@global.connection_control_max_connection_delay;
+SET @@global.connection_control_max_connection_delay= 10000;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_min_connection_delay= 11000;
+SELECT @@global.connection_control_min_connection_delay;
+
+SET @@global.connection_control_max_connection_delay= @saved_max_delay;
+
+SET @@global.connection_control_min_connection_delay = @saved_value;
+SELECT @@global.connection_control_min_connection_delay;
+
+--echo #-----------------------------------------------------------------------
+
+--echo # Case 3 : connection_control_max_connection_delay
+
+SET @saved_value= @@global.connection_control_max_connection_delay;
+SELECT @saved_value;
+
+--echo # 3.1 : Setting connection_control_max_connection_delay to valid
+--echo # value
+
+SET @@global.connection_control_max_connection_delay = 20000;
+SELECT @@global.connection_control_max_connection_delay;
+
+SET @@global.connection_control_max_connection_delay = 2000;
+SELECT @@global.connection_control_max_connection_delay;
+
+SET @@global.connection_control_max_connection_delay = 2147483647;
+SELECT @@global.connection_control_max_connection_delay;
+
+SET @@global.connection_control_max_connection_delay = DEFAULT;
+SELECT @@global.connection_control_max_connection_delay;
+
+--echo # 3.2 : Setting connection_control_max_connection_delay to
+--echo # invalid value
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@global.connection_control_max_connection_delay = NULL;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@global.connection_control_max_connection_delay = `SELECT * FROM mysql.user`;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_max_connection_delay = -20;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_max_connection_delay = 9223372036854775808;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_max_connection_delay = -9223372036854775808;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_max_connection_delay = 20;
+SELECT @@global.connection_control_max_connection_delay;
+
+--echo # Switch to conn_no_privs
+connection conn_no_privs;
+--echo # 3.3 : Use no_privs@localhost to set
+--echo # connection_control_max_connection_delay to valid value
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_max_connection_delay = 2147483647;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_max_connection_delay = DEFAULT;
+SELECT @@global.connection_control_max_connection_delay;
+
+--echo # 3.4 : Use no_privs@localhost to set
+--echo # connection_control_max_connection_delay to invalid value
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_max_connection_delay = NULL;
+SELECT @@global.connection_control_max_connection_delay;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET @@global.connection_control_max_connection_delay = 9223372036854775808;
+SELECT @@global.connection_control_max_connection_delay;
+
+--echo # Switch to default connection
+connection default;
+--echo # 3.5 : Setting connection_control_min_connection_delay to a value
+--echo # greater than connection_control_max_connection_delay
+
+SET @saved_min_delay= @@global.connection_control_min_connection_delay;
+SET @@global.connection_control_min_connection_delay= 11000;
+
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.connection_control_max_connection_delay= 10000;
+SELECT @@global.connection_control_max_connection_delay;
+
+SET @@global.connection_control_min_connection_delay= @saved_min_delay;
+
+SET @@global.connection_control_max_connection_delay = @saved_value;
+SELECT @@global.connection_control_max_connection_delay;
+
+--echo #-----------------------------------------------------------------------
+--echo # Cleanup
+disconnect conn_no_privs;
+DROP USER no_privs@localhost;
+--echo # Uninstall connection_control plugin
+--source ../inc/uninstall_connection_control_plugin.inc
+
+# Wait till all disconnects are completed.
+--source include/wait_until_count_sessions.inc
+--echo #-----------------------------------------------------------------------
--- /dev/null
+CREATE TABLE t1 (f1 CHAR(10), f0 integer) ENGINE=InnoDB;
+FLUSH TABLE t1 FOR EXPORT;
+UNLOCK TABLES;
+ALTER TABLE t1 DROP COLUMN f1;
+SET SESSION wsrep_osu_method='RSU';
+ALTER TABLE t1 ADD COLUMN f1 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f1;
+ALTER TABLE t1 ADD COLUMN f2 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f2;
+ALTER TABLE t1 ADD COLUMN f3 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f3;
+ALTER TABLE t1 ADD COLUMN f4 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f4;
+ALTER TABLE t1 ADD COLUMN f5 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f5;
+ALTER TABLE t1 ADD COLUMN f6 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f6;
+ALTER TABLE t1 ADD COLUMN f7 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f7;
+ALTER TABLE t1 ADD COLUMN f8 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f8;
+ALTER TABLE t1 ADD COLUMN f9 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f9;
+ALTER TABLE t1 ADD COLUMN f10 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f10;
+ALTER TABLE t1 ADD COLUMN f11 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f11;
+ALTER TABLE t1 ADD COLUMN f12 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f12;
+ALTER TABLE t1 ADD COLUMN f13 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f13;
+ALTER TABLE t1 ADD COLUMN f14 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f14;
+ALTER TABLE t1 ADD COLUMN f15 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f15;
+ALTER TABLE t1 ADD COLUMN f16 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f16;
+SET SESSION wsrep_osu_method='TOI';
+DROP TABLE t1;
--- /dev/null
+CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB;
+INSERT INTO t1 (f1) VALUES (1);
+CREATE TABLE t2 (f1 CHAR(20)) ENGINE=InnoDB;
+CREATE PROCEDURE proc_update ()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
+SET SESSION wsrep_sync_wait = 0;
+WHILE 1 DO
+UPDATE t1 SET f2 = LEFT(MD5(RAND()), 4);
+END WHILE;
+END|
+CALL proc_update();;
+SET SESSION wsrep_retry_autocommit = 0;
+have_successes
+1
+have_deadlocks
+1
+Got one of the listed errors
+DROP PROCEDURE proc_update;
+DROP TABLE t1, t2;
+CALL mtr.add_suppression("conflict state 3 after post commit");
--- /dev/null
+CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB;
+INSERT INTO t1 (f1) VALUES (1);
+CREATE TABLE t2 (f1 CHAR(20)) ENGINE=InnoDB;
+CREATE PROCEDURE proc_update ()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
+SET SESSION wsrep_sync_wait = 0;
+WHILE 1 DO
+UPDATE t1 SET f2 = LEFT(MD5(RAND()), 4);
+END WHILE;
+END|
+CALL proc_update();;
+SET SESSION wsrep_retry_autocommit = 0;
+Got one of the listed errors
+DROP PROCEDURE proc_update;
+DROP TABLE t1, t2;
+CALL mtr.add_suppression("conflict state 3 after post commit");
--- /dev/null
+CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB;
+INSERT INTO t1 (f1) VALUES (1);
+CREATE TABLE t2 (f1 CHAR(20)) ENGINE=InnoDB;
+CREATE PROCEDURE proc_update ()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
+SET SESSION wsrep_sync_wait = 0;
+WHILE 1 DO
+UPDATE t1 SET f2 = LEFT(MD5(RAND()), 4);
+END WHILE;
+END|
+CALL proc_update();;
+SET SESSION wsrep_retry_autocommit = 10000;
+Got one of the listed errors
+DROP PROCEDURE proc_update;
+DROP TABLE t1, t2;
+CALL mtr.add_suppression("conflict state 3 after post commit");
--- /dev/null
+CREATE TABLE t1 (i INT) ENGINE = InnoDB;
+INSERT INTO t1 (i) VALUES(1);
+CREATE TABLE t2 (i INT) ENGINE = InnoDB;
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+SELECT * FROM t1 WHERE i = 1 LOCK IN SHARE MODE;
+i
+1
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT IGNORE INTO t2 SELECT * FROM t1 WHERE i = 1 FOR UPDATE;;
+DELETE FROM t1 WHERE i = 1;
+COMMIT;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+DROP TABLE t1, t2;
--- /dev/null
+create table t1 (i int primary key, j int) engine=innodb;
+create table t2 (i int primary key, j int) engine=innodb;
+insert into t1 values (1,0);
+insert into t2 values (2,0);
+set autocommit=off;
+start transaction;
+update t1 set j=1 where i=1;
+set autocommit=off;
+start transaction;
+begin;
+update t2 set j=1 where i=2;
+insert into t1 select * from t2;;
+insert into t2 select * from t1;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+DROP TABLE t1, t2;
--- /dev/null
+CREATE TABLE t1 (f1 INTEGER, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB;
+INSERT INTO t1 (f1) VALUES (1),(65535);
+FLUSH STATUS;
+SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays';
+VARIABLE_VALUE = 0
+1
+CREATE PROCEDURE proc_insert ()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
+SET SESSION wsrep_sync_wait = 0;
+WHILE 1 DO
+INSERT INTO t1 (f1) VALUES (FLOOR( 1 + RAND( ) * 65535 ));
+END WHILE;
+END|
+CALL proc_insert();;
+SELECT VARIABLE_VALUE > 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays';
+VARIABLE_VALUE > 0
+1
+DROP PROCEDURE proc_insert;
+DROP TABLE t1;
--- /dev/null
+RESET MASTER;
+RESET MASTER;
+RESET MASTER;
+START SLAVE USER='root';
+Warnings:
+Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
+CREATE SCHEMA test1;
+CREATE SCHEMA test2;
+USE test1;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY,f2 CHAR(5) DEFAULT 'abc') ENGINE=InnoDB;
+USE test2;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY,f2 CHAR(5) DEFAULT 'abc') ENGINE=InnoDB;
+INSERT INTO test1.t1 (f1) VALUES (1);
+INSERT INTO test2.t1 (f1) VALUES (1);
+INSERT INTO test1.t1 (f1) VALUES (2);
+INSERT INTO test2.t1 (f1) VALUES (2);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+UPDATE test2.t1 SET test2.t1.f2 = 'cde';
+UPDATE test1.t1, test2.t1 SET test1.t1.f2 = 'klm', test2.t1.f2 = 'xyz';
+DELETE test1.t1, test2.t1 FROM test1.t1 INNER JOIN test2.t1 WHERE test1.t1.f1 = test2.t1.f1 AND test1.t1.f1 = 3;
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO test2.t1 (f1) VALUES (999);
+INSERT INTO test2.t1 (f1) VALUES (9999);
+COMMIT;
+START TRANSACTION;
+INSERT INTO test1.t1 (f1) VALUES (111);
+INSERT INTO test1.t1 (f1) VALUES (222);
+COMMIT;
+START TRANSACTION;
+INSERT INTO test1.t1 (f1) VALUES (333);
+INSERT INTO test2.t1 (f1) VALUES (99999);
+COMMIT;
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+Log_name Pos Event_type Server_id End_log_pos Info
+mysqld-bin.000001 120 Previous_gtids 2 151
+mysqld-bin.000001 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
+mysqld-bin.000001 199 Query 1 294 CREATE SCHEMA test1
+mysqld-bin.000001 294 Gtid 1 342 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
+mysqld-bin.000001 342 Query 1 415 BEGIN
+mysqld-bin.000001 415 Query 1 489 COMMIT
+mysqld-bin.000001 489 Gtid 1 537 SET @@SESSION.GTID_NEXT= '<effective_uuid>:3'
+mysqld-bin.000001 537 Query 1 692 use `test1`; CREATE TABLE t1 (f1 INTEGER PRIMARY KEY,f2 CHAR(5) DEFAULT 'abc') ENGINE=InnoDB
+mysqld-bin.000001 692 Gtid 1 740 SET @@SESSION.GTID_NEXT= '<effective_uuid>:4'
+mysqld-bin.000001 740 Query 1 813 BEGIN
+mysqld-bin.000001 813 Query 1 887 COMMIT
+mysqld-bin.000001 887 Gtid 1 935 SET @@SESSION.GTID_NEXT= '<effective_uuid>:5'
+mysqld-bin.000001 935 Query 1 998 BEGIN
+mysqld-bin.000001 998 Table_map 1 1047 table_id: ### (test1.t1)
+mysqld-bin.000001 1047 Write_rows 1 1091 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1091 Xid 1 1122 COMMIT /* xid=### */
+mysqld-bin.000001 1122 Gtid 1 1170 SET @@SESSION.GTID_NEXT= '<effective_uuid>:6'
+mysqld-bin.000001 1170 Query 1 1233 BEGIN
+mysqld-bin.000001 1233 Query 1 1297 COMMIT
+mysqld-bin.000001 1297 Gtid 1 1345 SET @@SESSION.GTID_NEXT= '<effective_uuid>:7'
+mysqld-bin.000001 1345 Query 1 1408 BEGIN
+mysqld-bin.000001 1408 Table_map 1 1457 table_id: ### (test1.t1)
+mysqld-bin.000001 1457 Write_rows 1 1501 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1501 Xid 1 1532 COMMIT /* xid=### */
+mysqld-bin.000001 1532 Gtid 1 1580 SET @@SESSION.GTID_NEXT= '<effective_uuid>:8'
+mysqld-bin.000001 1580 Query 1 1643 BEGIN
+mysqld-bin.000001 1643 Query 1 1707 COMMIT
+mysqld-bin.000001 1707 Gtid 1 1755 SET @@SESSION.GTID_NEXT= '<effective_uuid>:9'
+mysqld-bin.000001 1755 Query 1 1818 BEGIN
+mysqld-bin.000001 1818 Table_map 1 1867 table_id: ### (test1.t1)
+mysqld-bin.000001 1867 Write_rows 1 1911 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1911 Xid 1 1942 COMMIT /* xid=### */
+mysqld-bin.000001 1942 Gtid 1 1990 SET @@SESSION.GTID_NEXT= '<effective_uuid>:10'
+mysqld-bin.000001 1990 Query 1 2053 BEGIN
+mysqld-bin.000001 2053 Query 1 2117 COMMIT
+mysqld-bin.000001 2117 Gtid 1 2165 SET @@SESSION.GTID_NEXT= '<effective_uuid>:11'
+mysqld-bin.000001 2165 Query 1 2228 BEGIN
+mysqld-bin.000001 2228 Query 1 2292 COMMIT
+mysqld-bin.000001 2292 Gtid 1 2340 SET @@SESSION.GTID_NEXT= '<effective_uuid>:12'
+mysqld-bin.000001 2340 Query 1 2403 BEGIN
+mysqld-bin.000001 2403 Table_map 1 2452 table_id: ### (test1.t1)
+mysqld-bin.000001 2452 Update_rows 1 2542 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2542 Xid 1 2573 COMMIT /* xid=### */
+mysqld-bin.000001 2573 Gtid 1 2621 SET @@SESSION.GTID_NEXT= '<effective_uuid>:13'
+mysqld-bin.000001 2621 Query 1 2684 BEGIN
+mysqld-bin.000001 2684 Table_map 1 2733 table_id: ### (test1.t1)
+mysqld-bin.000001 2733 Delete_rows 1 2777 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2777 Xid 1 2808 COMMIT /* xid=### */
+mysqld-bin.000001 2808 Gtid 1 2856 SET @@SESSION.GTID_NEXT= '<effective_uuid>:14'
+mysqld-bin.000001 2856 Query 1 2919 BEGIN
+mysqld-bin.000001 2919 Query 1 2983 COMMIT
+mysqld-bin.000001 2983 Gtid 1 3031 SET @@SESSION.GTID_NEXT= '<effective_uuid>:15'
+mysqld-bin.000001 3031 Query 1 3094 BEGIN
+mysqld-bin.000001 3094 Table_map 1 3143 table_id: ### (test1.t1)
+mysqld-bin.000001 3143 Write_rows 1 3187 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3187 Table_map 1 3236 table_id: ### (test1.t1)
+mysqld-bin.000001 3236 Write_rows 1 3280 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3280 Xid 1 3311 COMMIT /* xid=### */
+mysqld-bin.000001 3311 Gtid 1 3359 SET @@SESSION.GTID_NEXT= '<effective_uuid>:16'
+mysqld-bin.000001 3359 Query 1 3422 BEGIN
+mysqld-bin.000001 3422 Table_map 1 3471 table_id: ### (test1.t1)
+mysqld-bin.000001 3471 Write_rows 1 3515 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3515 Xid 1 3546 COMMIT /* xid=### */
+gtid_executed_equal
+1
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+Log_name Pos Event_type Server_id End_log_pos Info
+mysqld-bin.000001 120 Previous_gtids 3 151
+mysqld-bin.000001 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
+mysqld-bin.000001 199 Query 1 294 CREATE SCHEMA test1
+mysqld-bin.000001 294 Gtid 1 342 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
+mysqld-bin.000001 342 Query 1 415 BEGIN
+mysqld-bin.000001 415 Query 1 489 COMMIT
+mysqld-bin.000001 489 Gtid 1 537 SET @@SESSION.GTID_NEXT= '<effective_uuid>:3'
+mysqld-bin.000001 537 Query 1 692 use `test1`; CREATE TABLE t1 (f1 INTEGER PRIMARY KEY,f2 CHAR(5) DEFAULT 'abc') ENGINE=InnoDB
+mysqld-bin.000001 692 Gtid 1 740 SET @@SESSION.GTID_NEXT= '<effective_uuid>:4'
+mysqld-bin.000001 740 Query 1 813 BEGIN
+mysqld-bin.000001 813 Query 1 887 COMMIT
+mysqld-bin.000001 887 Gtid 1 935 SET @@SESSION.GTID_NEXT= '<effective_uuid>:5'
+mysqld-bin.000001 935 Query 1 998 BEGIN
+mysqld-bin.000001 998 Table_map 1 1047 table_id: ### (test1.t1)
+mysqld-bin.000001 1047 Write_rows 1 1091 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1091 Xid 1 1122 COMMIT /* xid=### */
+mysqld-bin.000001 1122 Gtid 1 1170 SET @@SESSION.GTID_NEXT= '<effective_uuid>:6'
+mysqld-bin.000001 1170 Query 1 1233 BEGIN
+mysqld-bin.000001 1233 Query 1 1297 COMMIT
+mysqld-bin.000001 1297 Gtid 1 1345 SET @@SESSION.GTID_NEXT= '<effective_uuid>:7'
+mysqld-bin.000001 1345 Query 1 1408 BEGIN
+mysqld-bin.000001 1408 Table_map 1 1457 table_id: ### (test1.t1)
+mysqld-bin.000001 1457 Write_rows 1 1501 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1501 Xid 1 1532 COMMIT /* xid=### */
+mysqld-bin.000001 1532 Gtid 1 1580 SET @@SESSION.GTID_NEXT= '<effective_uuid>:8'
+mysqld-bin.000001 1580 Query 1 1643 BEGIN
+mysqld-bin.000001 1643 Query 1 1707 COMMIT
+mysqld-bin.000001 1707 Gtid 1 1755 SET @@SESSION.GTID_NEXT= '<effective_uuid>:9'
+mysqld-bin.000001 1755 Query 1 1818 BEGIN
+mysqld-bin.000001 1818 Table_map 1 1867 table_id: ### (test1.t1)
+mysqld-bin.000001 1867 Write_rows 1 1911 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1911 Xid 1 1942 COMMIT /* xid=### */
+mysqld-bin.000001 1942 Gtid 1 1990 SET @@SESSION.GTID_NEXT= '<effective_uuid>:10'
+mysqld-bin.000001 1990 Query 1 2053 BEGIN
+mysqld-bin.000001 2053 Query 1 2117 COMMIT
+mysqld-bin.000001 2117 Gtid 1 2165 SET @@SESSION.GTID_NEXT= '<effective_uuid>:11'
+mysqld-bin.000001 2165 Query 1 2228 BEGIN
+mysqld-bin.000001 2228 Query 1 2292 COMMIT
+mysqld-bin.000001 2292 Gtid 1 2340 SET @@SESSION.GTID_NEXT= '<effective_uuid>:12'
+mysqld-bin.000001 2340 Query 1 2403 BEGIN
+mysqld-bin.000001 2403 Table_map 1 2452 table_id: ### (test1.t1)
+mysqld-bin.000001 2452 Update_rows 1 2542 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2542 Xid 1 2573 COMMIT /* xid=### */
+mysqld-bin.000001 2573 Gtid 1 2621 SET @@SESSION.GTID_NEXT= '<effective_uuid>:13'
+mysqld-bin.000001 2621 Query 1 2684 BEGIN
+mysqld-bin.000001 2684 Table_map 1 2733 table_id: ### (test1.t1)
+mysqld-bin.000001 2733 Delete_rows 1 2777 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2777 Xid 1 2808 COMMIT /* xid=### */
+mysqld-bin.000001 2808 Gtid 1 2856 SET @@SESSION.GTID_NEXT= '<effective_uuid>:14'
+mysqld-bin.000001 2856 Query 1 2919 BEGIN
+mysqld-bin.000001 2919 Query 1 2983 COMMIT
+mysqld-bin.000001 2983 Gtid 1 3031 SET @@SESSION.GTID_NEXT= '<effective_uuid>:15'
+mysqld-bin.000001 3031 Query 1 3094 BEGIN
+mysqld-bin.000001 3094 Table_map 1 3143 table_id: ### (test1.t1)
+mysqld-bin.000001 3143 Write_rows 1 3187 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3187 Table_map 1 3236 table_id: ### (test1.t1)
+mysqld-bin.000001 3236 Write_rows 1 3280 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3280 Xid 1 3311 COMMIT /* xid=### */
+mysqld-bin.000001 3311 Gtid 1 3359 SET @@SESSION.GTID_NEXT= '<effective_uuid>:16'
+mysqld-bin.000001 3359 Query 1 3422 BEGIN
+mysqld-bin.000001 3422 Table_map 1 3471 table_id: ### (test1.t1)
+mysqld-bin.000001 3471 Write_rows 1 3515 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3515 Xid 1 3546 COMMIT /* xid=### */
+include/diff_servers.inc [servers=2 3]
+SELECT COUNT(*) = 2 FROM test1.t1 WHERE f1 IN (1,2);
+COUNT(*) = 2
+1
+SELECT COUNT(*) = 3 FROM test1.t1 WHERE f1 IN (111,222,333);
+COUNT(*) = 3
+1
+SELECT COUNT(*) = 2 FROM test1.t1 WHERE f2 = 'klm';
+COUNT(*) = 2
+1
+USE test2;
+ERROR 42000: Unknown database 'test2'
+DROP SCHEMA test1;
+DROP SCHEMA test2;
+STOP SLAVE;
+RESET SLAVE ALL;
--- /dev/null
+RESET MASTER;
+RESET MASTER;
+RESET MASTER;
+START SLAVE USER='root';
+Warnings:
+Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
+CREATE SCHEMA test1;
+CREATE SCHEMA test2;
+USE test1;
+CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
+USE test2;
+CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
+INSERT INTO test1.t1 (f1) VALUES (1);
+INSERT INTO test2.t1 (f1) VALUES (1);
+INSERT INTO test1.t1 (f1) VALUES (2);
+INSERT INTO test2.t1 (f1) VALUES (2);
+SET GLOBAL wsrep_provider_options='gmcast.isolate=1';
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+SET SESSION wsrep_on=OFF;
+include/wait_for_slave_sql_error.inc [errno=1047]
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+SET GLOBAL wsrep_provider_options='gmcast.isolate=0';
+INSERT INTO test1.t1 (f1) VALUES (5);
+INSERT INTO test2.t1 (f1) VALUES (5);
+SET SESSION wsrep_on=ON;
+INSERT INTO test1.t1 (f1) VALUES (6);
+INSERT INTO test2.t1 (f1) VALUES (6);
+START SLAVE;
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+Log_name Pos Event_type Server_id End_log_pos Info
+mysqld-bin.000001 120 Previous_gtids 2 151
+mysqld-bin.000001 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
+mysqld-bin.000001 199 Query 1 294 CREATE SCHEMA test1
+mysqld-bin.000001 294 Gtid 1 342 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
+mysqld-bin.000001 342 Query 1 415 BEGIN
+mysqld-bin.000001 415 Query 1 489 COMMIT
+mysqld-bin.000001 489 Gtid 1 537 SET @@SESSION.GTID_NEXT= '<effective_uuid>:3'
+mysqld-bin.000001 537 Query 1 655 use `test1`; CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB
+mysqld-bin.000001 655 Gtid 1 703 SET @@SESSION.GTID_NEXT= '<effective_uuid>:4'
+mysqld-bin.000001 703 Query 1 776 BEGIN
+mysqld-bin.000001 776 Query 1 850 COMMIT
+mysqld-bin.000001 850 Gtid 1 898 SET @@SESSION.GTID_NEXT= '<effective_uuid>:5'
+mysqld-bin.000001 898 Query 1 961 BEGIN
+mysqld-bin.000001 961 Table_map 1 1007 table_id: ### (test1.t1)
+mysqld-bin.000001 1007 Write_rows 1 1047 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1047 Xid 1 1078 COMMIT /* xid=### */
+mysqld-bin.000001 1078 Gtid 1 1126 SET @@SESSION.GTID_NEXT= '<effective_uuid>:6'
+mysqld-bin.000001 1126 Query 1 1189 BEGIN
+mysqld-bin.000001 1189 Query 1 1253 COMMIT
+mysqld-bin.000001 1253 Gtid 1 1301 SET @@SESSION.GTID_NEXT= '<effective_uuid>:7'
+mysqld-bin.000001 1301 Query 1 1364 BEGIN
+mysqld-bin.000001 1364 Table_map 1 1410 table_id: ### (test1.t1)
+mysqld-bin.000001 1410 Write_rows 1 1450 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1450 Xid 1 1481 COMMIT /* xid=### */
+mysqld-bin.000001 1481 Gtid 1 1529 SET @@SESSION.GTID_NEXT= '<effective_uuid>:8'
+mysqld-bin.000001 1529 Query 1 1592 BEGIN
+mysqld-bin.000001 1592 Query 1 1656 COMMIT
+mysqld-bin.000001 1656 Gtid 1 1704 SET @@SESSION.GTID_NEXT= '<effective_uuid>:9'
+mysqld-bin.000001 1704 Query 1 1767 BEGIN
+mysqld-bin.000001 1767 Table_map 1 1813 table_id: ### (test1.t1)
+mysqld-bin.000001 1813 Write_rows 1 1853 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1853 Xid 1 1884 COMMIT /* xid=### */
+mysqld-bin.000001 1884 Gtid 1 1932 SET @@SESSION.GTID_NEXT= '<effective_uuid>:10'
+mysqld-bin.000001 1932 Query 1 1995 BEGIN
+mysqld-bin.000001 1995 Query 1 2059 COMMIT
+mysqld-bin.000001 2059 Gtid 1 2107 SET @@SESSION.GTID_NEXT= '<effective_uuid>:11'
+mysqld-bin.000001 2107 Query 1 2170 BEGIN
+mysqld-bin.000001 2170 Table_map 1 2216 table_id: ### (test1.t1)
+mysqld-bin.000001 2216 Write_rows 1 2256 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2256 Xid 1 2287 COMMIT /* xid=### */
+mysqld-bin.000001 2287 Gtid 1 2335 SET @@SESSION.GTID_NEXT= '<effective_uuid>:12'
+mysqld-bin.000001 2335 Query 1 2398 BEGIN
+mysqld-bin.000001 2398 Query 1 2462 COMMIT
+mysqld-bin.000001 2462 Gtid 1 2510 SET @@SESSION.GTID_NEXT= '<effective_uuid>:13'
+mysqld-bin.000001 2510 Query 1 2573 BEGIN
+mysqld-bin.000001 2573 Table_map 1 2619 table_id: ### (test1.t1)
+mysqld-bin.000001 2619 Write_rows 1 2659 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2659 Xid 1 2690 COMMIT /* xid=### */
+mysqld-bin.000001 2690 Gtid 1 2738 SET @@SESSION.GTID_NEXT= '<effective_uuid>:14'
+mysqld-bin.000001 2738 Query 1 2801 BEGIN
+mysqld-bin.000001 2801 Query 1 2865 COMMIT
+mysqld-bin.000001 2865 Gtid 1 2913 SET @@SESSION.GTID_NEXT= '<effective_uuid>:15'
+mysqld-bin.000001 2913 Query 1 2976 BEGIN
+mysqld-bin.000001 2976 Table_map 1 3022 table_id: ### (test1.t1)
+mysqld-bin.000001 3022 Write_rows 1 3062 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3062 Xid 1 3093 COMMIT /* xid=### */
+mysqld-bin.000001 3093 Gtid 1 3141 SET @@SESSION.GTID_NEXT= '<effective_uuid>:16'
+mysqld-bin.000001 3141 Query 1 3204 BEGIN
+mysqld-bin.000001 3204 Query 1 3268 COMMIT
+mysqld-bin.000001 3268 Gtid 1 3316 SET @@SESSION.GTID_NEXT= '<effective_uuid>:17'
+mysqld-bin.000001 3316 Query 1 3379 BEGIN
+mysqld-bin.000001 3379 Table_map 1 3425 table_id: ### (test1.t1)
+mysqld-bin.000001 3425 Write_rows 1 3465 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3465 Xid 1 3496 COMMIT /* xid=### */
+mysqld-bin.000001 3496 Gtid 1 3544 SET @@SESSION.GTID_NEXT= '<effective_uuid>:18'
+mysqld-bin.000001 3544 Query 1 3607 BEGIN
+mysqld-bin.000001 3607 Query 1 3671 COMMIT
+mysqld-bin.000001 3671 Gtid 1 3719 SET @@SESSION.GTID_NEXT= '<effective_uuid>:19'
+mysqld-bin.000001 3719 Query 1 3782 BEGIN
+mysqld-bin.000001 3782 Table_map 1 3828 table_id: ### (test1.t1)
+mysqld-bin.000001 3828 Write_rows 1 3868 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3868 Xid 1 3899 COMMIT /* xid=### */
+mysqld-bin.000001 3899 Gtid 1 3947 SET @@SESSION.GTID_NEXT= '<effective_uuid>:20'
+mysqld-bin.000001 3947 Query 1 4010 BEGIN
+mysqld-bin.000001 4010 Query 1 4074 COMMIT
+mysqld-bin.000001 4074 Gtid 1 4122 SET @@SESSION.GTID_NEXT= '<effective_uuid>:21'
+mysqld-bin.000001 4122 Query 1 4185 BEGIN
+mysqld-bin.000001 4185 Table_map 1 4231 table_id: ### (test1.t1)
+mysqld-bin.000001 4231 Write_rows 1 4271 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 4271 Xid 1 4302 COMMIT /* xid=### */
+mysqld-bin.000001 4302 Gtid 1 4350 SET @@SESSION.GTID_NEXT= '<effective_uuid>:22'
+mysqld-bin.000001 4350 Query 1 4413 BEGIN
+mysqld-bin.000001 4413 Query 1 4477 COMMIT
+mysqld-bin.000001 4477 Gtid 1 4525 SET @@SESSION.GTID_NEXT= '<effective_uuid>:23'
+mysqld-bin.000001 4525 Query 1 4588 BEGIN
+mysqld-bin.000001 4588 Table_map 1 4634 table_id: ### (test1.t1)
+mysqld-bin.000001 4634 Write_rows 1 4674 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 4674 Xid 1 4705 COMMIT /* xid=### */
+mysqld-bin.000001 4705 Gtid 1 4753 SET @@SESSION.GTID_NEXT= '<effective_uuid>:24'
+mysqld-bin.000001 4753 Query 1 4816 BEGIN
+mysqld-bin.000001 4816 Query 1 4880 COMMIT
+mysqld-bin.000001 4880 Gtid 1 4928 SET @@SESSION.GTID_NEXT= '<effective_uuid>:25'
+mysqld-bin.000001 4928 Query 1 4991 BEGIN
+mysqld-bin.000001 4991 Table_map 1 5037 table_id: ### (test1.t1)
+mysqld-bin.000001 5037 Write_rows 1 5077 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 5077 Xid 1 5108 COMMIT /* xid=### */
+mysqld-bin.000001 5108 Gtid 1 5156 SET @@SESSION.GTID_NEXT= '<effective_uuid>:26'
+mysqld-bin.000001 5156 Query 1 5219 BEGIN
+mysqld-bin.000001 5219 Query 1 5283 COMMIT
+mysqld-bin.000001 5283 Gtid 1 5331 SET @@SESSION.GTID_NEXT= '<effective_uuid>:27'
+mysqld-bin.000001 5331 Query 1 5394 BEGIN
+mysqld-bin.000001 5394 Table_map 1 5440 table_id: ### (test1.t1)
+mysqld-bin.000001 5440 Write_rows 1 5480 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 5480 Xid 1 5511 COMMIT /* xid=### */
+mysqld-bin.000001 5511 Gtid 1 5559 SET @@SESSION.GTID_NEXT= '<effective_uuid>:28'
+mysqld-bin.000001 5559 Query 1 5622 BEGIN
+mysqld-bin.000001 5622 Query 1 5686 COMMIT
+mysqld-bin.000001 5686 Gtid 1 5734 SET @@SESSION.GTID_NEXT= '<effective_uuid>:29'
+mysqld-bin.000001 5734 Query 1 5797 BEGIN
+mysqld-bin.000001 5797 Table_map 1 5843 table_id: ### (test1.t1)
+mysqld-bin.000001 5843 Write_rows 1 5883 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 5883 Xid 1 5914 COMMIT /* xid=### */
+mysqld-bin.000001 5914 Gtid 1 5962 SET @@SESSION.GTID_NEXT= '<effective_uuid>:30'
+mysqld-bin.000001 5962 Query 1 6025 BEGIN
+mysqld-bin.000001 6025 Query 1 6089 COMMIT
+USE test2;
+ERROR 42000: Unknown database 'test2'
+gtid_executed_equal
+1
+USE test2;
+ERROR 42000: Unknown database 'test2'
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+Log_name Pos Event_type Server_id End_log_pos Info
+mysqld-bin.000001 120 Previous_gtids 4 151
+mysqld-bin.000001 151 Gtid 1 199 SET @@SESSION.GTID_NEXT= '<effective_uuid>:1'
+mysqld-bin.000001 199 Query 1 294 CREATE SCHEMA test1
+mysqld-bin.000001 294 Gtid 1 342 SET @@SESSION.GTID_NEXT= '<effective_uuid>:2'
+mysqld-bin.000001 342 Query 1 415 BEGIN
+mysqld-bin.000001 415 Query 1 489 COMMIT
+mysqld-bin.000001 489 Gtid 1 537 SET @@SESSION.GTID_NEXT= '<effective_uuid>:3'
+mysqld-bin.000001 537 Query 1 655 use `test1`; CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB
+mysqld-bin.000001 655 Gtid 1 703 SET @@SESSION.GTID_NEXT= '<effective_uuid>:4'
+mysqld-bin.000001 703 Query 1 776 BEGIN
+mysqld-bin.000001 776 Query 1 850 COMMIT
+mysqld-bin.000001 850 Gtid 1 898 SET @@SESSION.GTID_NEXT= '<effective_uuid>:5'
+mysqld-bin.000001 898 Query 1 961 BEGIN
+mysqld-bin.000001 961 Table_map 1 1007 table_id: ### (test1.t1)
+mysqld-bin.000001 1007 Write_rows 1 1047 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1047 Xid 1 1078 COMMIT /* xid=### */
+mysqld-bin.000001 1078 Gtid 1 1126 SET @@SESSION.GTID_NEXT= '<effective_uuid>:6'
+mysqld-bin.000001 1126 Query 1 1189 BEGIN
+mysqld-bin.000001 1189 Query 1 1253 COMMIT
+mysqld-bin.000001 1253 Gtid 1 1301 SET @@SESSION.GTID_NEXT= '<effective_uuid>:7'
+mysqld-bin.000001 1301 Query 1 1364 BEGIN
+mysqld-bin.000001 1364 Table_map 1 1410 table_id: ### (test1.t1)
+mysqld-bin.000001 1410 Write_rows 1 1450 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1450 Xid 1 1481 COMMIT /* xid=### */
+mysqld-bin.000001 1481 Gtid 1 1529 SET @@SESSION.GTID_NEXT= '<effective_uuid>:8'
+mysqld-bin.000001 1529 Query 1 1592 BEGIN
+mysqld-bin.000001 1592 Query 1 1656 COMMIT
+mysqld-bin.000001 1656 Gtid 1 1704 SET @@SESSION.GTID_NEXT= '<effective_uuid>:9'
+mysqld-bin.000001 1704 Query 1 1767 BEGIN
+mysqld-bin.000001 1767 Table_map 1 1813 table_id: ### (test1.t1)
+mysqld-bin.000001 1813 Write_rows 1 1853 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 1853 Xid 1 1884 COMMIT /* xid=### */
+mysqld-bin.000001 1884 Gtid 1 1932 SET @@SESSION.GTID_NEXT= '<effective_uuid>:10'
+mysqld-bin.000001 1932 Query 1 1995 BEGIN
+mysqld-bin.000001 1995 Query 1 2059 COMMIT
+mysqld-bin.000001 2059 Gtid 1 2107 SET @@SESSION.GTID_NEXT= '<effective_uuid>:11'
+mysqld-bin.000001 2107 Query 1 2170 BEGIN
+mysqld-bin.000001 2170 Table_map 1 2216 table_id: ### (test1.t1)
+mysqld-bin.000001 2216 Write_rows 1 2256 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2256 Xid 1 2287 COMMIT /* xid=### */
+mysqld-bin.000001 2287 Gtid 1 2335 SET @@SESSION.GTID_NEXT= '<effective_uuid>:12'
+mysqld-bin.000001 2335 Query 1 2398 BEGIN
+mysqld-bin.000001 2398 Query 1 2462 COMMIT
+mysqld-bin.000001 2462 Gtid 1 2510 SET @@SESSION.GTID_NEXT= '<effective_uuid>:13'
+mysqld-bin.000001 2510 Query 1 2573 BEGIN
+mysqld-bin.000001 2573 Table_map 1 2619 table_id: ### (test1.t1)
+mysqld-bin.000001 2619 Write_rows 1 2659 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 2659 Xid 1 2690 COMMIT /* xid=### */
+mysqld-bin.000001 2690 Gtid 1 2738 SET @@SESSION.GTID_NEXT= '<effective_uuid>:14'
+mysqld-bin.000001 2738 Query 1 2801 BEGIN
+mysqld-bin.000001 2801 Query 1 2865 COMMIT
+mysqld-bin.000001 2865 Gtid 1 2913 SET @@SESSION.GTID_NEXT= '<effective_uuid>:15'
+mysqld-bin.000001 2913 Query 1 2976 BEGIN
+mysqld-bin.000001 2976 Table_map 1 3022 table_id: ### (test1.t1)
+mysqld-bin.000001 3022 Write_rows 1 3062 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3062 Xid 1 3093 COMMIT /* xid=### */
+mysqld-bin.000001 3093 Gtid 1 3141 SET @@SESSION.GTID_NEXT= '<effective_uuid>:16'
+mysqld-bin.000001 3141 Query 1 3204 BEGIN
+mysqld-bin.000001 3204 Query 1 3268 COMMIT
+mysqld-bin.000001 3268 Gtid 1 3316 SET @@SESSION.GTID_NEXT= '<effective_uuid>:17'
+mysqld-bin.000001 3316 Query 1 3379 BEGIN
+mysqld-bin.000001 3379 Table_map 1 3425 table_id: ### (test1.t1)
+mysqld-bin.000001 3425 Write_rows 1 3465 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3465 Xid 1 3496 COMMIT /* xid=### */
+mysqld-bin.000001 3496 Gtid 1 3544 SET @@SESSION.GTID_NEXT= '<effective_uuid>:18'
+mysqld-bin.000001 3544 Query 1 3607 BEGIN
+mysqld-bin.000001 3607 Query 1 3671 COMMIT
+mysqld-bin.000001 3671 Gtid 1 3719 SET @@SESSION.GTID_NEXT= '<effective_uuid>:19'
+mysqld-bin.000001 3719 Query 1 3782 BEGIN
+mysqld-bin.000001 3782 Table_map 1 3828 table_id: ### (test1.t1)
+mysqld-bin.000001 3828 Write_rows 1 3868 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 3868 Xid 1 3899 COMMIT /* xid=### */
+mysqld-bin.000001 3899 Gtid 1 3947 SET @@SESSION.GTID_NEXT= '<effective_uuid>:20'
+mysqld-bin.000001 3947 Query 1 4010 BEGIN
+mysqld-bin.000001 4010 Query 1 4074 COMMIT
+mysqld-bin.000001 4074 Gtid 1 4122 SET @@SESSION.GTID_NEXT= '<effective_uuid>:21'
+mysqld-bin.000001 4122 Query 1 4185 BEGIN
+mysqld-bin.000001 4185 Table_map 1 4231 table_id: ### (test1.t1)
+mysqld-bin.000001 4231 Write_rows 1 4271 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 4271 Xid 1 4302 COMMIT /* xid=### */
+mysqld-bin.000001 4302 Gtid 1 4350 SET @@SESSION.GTID_NEXT= '<effective_uuid>:22'
+mysqld-bin.000001 4350 Query 1 4413 BEGIN
+mysqld-bin.000001 4413 Query 1 4477 COMMIT
+mysqld-bin.000001 4477 Gtid 1 4525 SET @@SESSION.GTID_NEXT= '<effective_uuid>:23'
+mysqld-bin.000001 4525 Query 1 4588 BEGIN
+mysqld-bin.000001 4588 Table_map 1 4634 table_id: ### (test1.t1)
+mysqld-bin.000001 4634 Write_rows 1 4674 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 4674 Xid 1 4705 COMMIT /* xid=### */
+mysqld-bin.000001 4705 Gtid 1 4753 SET @@SESSION.GTID_NEXT= '<effective_uuid>:24'
+mysqld-bin.000001 4753 Query 1 4816 BEGIN
+mysqld-bin.000001 4816 Query 1 4880 COMMIT
+mysqld-bin.000001 4880 Gtid 1 4928 SET @@SESSION.GTID_NEXT= '<effective_uuid>:25'
+mysqld-bin.000001 4928 Query 1 4991 BEGIN
+mysqld-bin.000001 4991 Table_map 1 5037 table_id: ### (test1.t1)
+mysqld-bin.000001 5037 Write_rows 1 5077 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 5077 Xid 1 5108 COMMIT /* xid=### */
+mysqld-bin.000001 5108 Gtid 1 5156 SET @@SESSION.GTID_NEXT= '<effective_uuid>:26'
+mysqld-bin.000001 5156 Query 1 5219 BEGIN
+mysqld-bin.000001 5219 Query 1 5283 COMMIT
+mysqld-bin.000001 5283 Gtid 1 5331 SET @@SESSION.GTID_NEXT= '<effective_uuid>:27'
+mysqld-bin.000001 5331 Query 1 5394 BEGIN
+mysqld-bin.000001 5394 Table_map 1 5440 table_id: ### (test1.t1)
+mysqld-bin.000001 5440 Write_rows 1 5480 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 5480 Xid 1 5511 COMMIT /* xid=### */
+mysqld-bin.000001 5511 Gtid 1 5559 SET @@SESSION.GTID_NEXT= '<effective_uuid>:28'
+mysqld-bin.000001 5559 Query 1 5622 BEGIN
+mysqld-bin.000001 5622 Query 1 5686 COMMIT
+mysqld-bin.000001 5686 Gtid 1 5734 SET @@SESSION.GTID_NEXT= '<effective_uuid>:29'
+mysqld-bin.000001 5734 Query 1 5797 BEGIN
+mysqld-bin.000001 5797 Table_map 1 5843 table_id: ### (test1.t1)
+mysqld-bin.000001 5843 Write_rows 1 5883 table_id: ### flags: STMT_END_F
+mysqld-bin.000001 5883 Xid 1 5914 COMMIT /* xid=### */
+mysqld-bin.000001 5914 Gtid 1 5962 SET @@SESSION.GTID_NEXT= '<effective_uuid>:30'
+mysqld-bin.000001 5962 Query 1 6025 BEGIN
+mysqld-bin.000001 6025 Query 1 6089 COMMIT
+DROP SCHEMA test1;
+DROP SCHEMA test2;
+STOP SLAVE;
+RESET SLAVE ALL;
+CALL mtr.add_suppression("GTID replication failed");
+CALL mtr.add_suppression("Slave SQL: Error in Xid_log_event: Commit could not be completed");
+CALL mtr.add_suppression("Slave SQL: Node has dropped from cluster, Error_code: 1047");
+CALL mtr.add_suppression("TO isolation failed for");
+CALL mtr.add_suppression("Slave SQL: Error 'Deadlock found when trying to get lock; try restarting transaction' on query");
+CALL mtr.add_suppression("Slave SQL: Error 'WSREP has not yet prepared node for application use' on query");
+CALL mtr.add_suppression("Slave: WSREP has not yet prepared node for application use Error_code: 1047");
--- /dev/null
+CREATE TABLE t1 (
+f1 VARCHAR(255) PRIMARY KEY
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+INSERT INTO t1 VALUES ('abc');
+SELECT f1 = 'abc' FROM t1;
+f1 = 'abc'
+1
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+UPDATE t1 SET f1 = 'klm';
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+UPDATE t1 SET f1 = 'xyz';
+COMMIT;
+COMMIT;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+include/assert_grep.inc [cluster conflict due to high priority abort for threads]
+DROP TABLE t1;
--- /dev/null
+--source include/galera_cluster.inc
+
+--connection node_1
+CREATE TABLE t1 (f1 CHAR(10), f0 integer) ENGINE=InnoDB;
+
+FLUSH TABLE t1 FOR EXPORT;
+UNLOCK TABLES;
+
+ALTER TABLE t1 DROP COLUMN f1;
+
+SET SESSION wsrep_osu_method='RSU';
+ALTER TABLE t1 ADD COLUMN f1 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f1;
+ALTER TABLE t1 ADD COLUMN f2 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f2;
+ALTER TABLE t1 ADD COLUMN f3 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f3;
+ALTER TABLE t1 ADD COLUMN f4 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f4;
+ALTER TABLE t1 ADD COLUMN f5 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f5;
+ALTER TABLE t1 ADD COLUMN f6 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f6;
+ALTER TABLE t1 ADD COLUMN f7 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f7;
+ALTER TABLE t1 ADD COLUMN f8 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f8;
+ALTER TABLE t1 ADD COLUMN f9 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f9;
+ALTER TABLE t1 ADD COLUMN f10 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f10;
+ALTER TABLE t1 ADD COLUMN f11 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f11;
+ALTER TABLE t1 ADD COLUMN f12 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f12;
+ALTER TABLE t1 ADD COLUMN f13 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f13;
+ALTER TABLE t1 ADD COLUMN f14 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f14;
+ALTER TABLE t1 ADD COLUMN f15 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f15;
+ALTER TABLE t1 ADD COLUMN f16 CHAR(10);
+ALTER TABLE t1 DROP COLUMN f16;
+
+SET SESSION wsrep_osu_method='TOI';
+DROP TABLE t1;
--- /dev/null
+#
+# Cleanup for MW-328 tests
+#
+
+--connection node_1
+--disable_query_log
+--eval KILL CONNECTION $sp_connection_id
+--enable_query_log
+
+--connection node_1X
+--error 2013,1317
+--reap
+
+--connection node_1
+DROP PROCEDURE proc_update;
+DROP TABLE t1, t2;
+
+CALL mtr.add_suppression("conflict state 3 after post commit");
--- /dev/null
+#
+# Initialization for MW-328 tests
+#
+
+CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB;
+INSERT INTO t1 (f1) VALUES (1);
+
+CREATE TABLE t2 (f1 CHAR(20)) ENGINE=InnoDB;
+
+#
+# Have some random updates going on against t1
+#
+
+DELIMITER |;
+CREATE PROCEDURE proc_update ()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
+ SET SESSION wsrep_sync_wait = 0;
+ WHILE 1 DO
+ UPDATE t1 SET f2 = LEFT(MD5(RAND()), 4);
+ END WHILE;
+END|
+
+DELIMITER ;|
+
+--connect node_1X, 127.0.0.1, root, , test, $NODE_MYPORT_1
+--connection node_1X
+--let $sp_connection_id = `SELECT CONNECTION_ID()`
+--send CALL proc_update();
--- /dev/null
+#
+# MW-328 Fix unnecessary/silent BF aborts
+#
+
+#
+# Attempt to insert into t2 and check if insert actually inserted rows if
+# a success was reported.
+#
+
+--source include/galera_cluster.inc
+--source suite/galera/t/MW-328-header.inc
+
+--connection node_2
+--let $count = 100
+--let $successes = 0
+--let $deadlocks = 0
+
+SET SESSION wsrep_retry_autocommit = 0;
+
+--disable_query_log
+
+while ($count)
+{
+ TRUNCATE TABLE t2;
+
+ --error 0,1213
+ INSERT IGNORE INTO t2 SELECT f2 FROM t1;
+ if ($mysql_errno != 1213) {
+ --inc $successes
+ if (`SELECT COUNT(*) = 0 FROM t2`) {
+ --die No rows arrived in table t2
+ }
+ }
+
+ if ($mysql_errno == 1213) {
+ --inc $deadlocks
+
+ }
+
+ --dec $count
+}
+
+--enable_query_log
+
+#
+# Check that the test produced both deadlocks and successes
+#
+
+--disable_query_log
+--eval SELECT $successes > 0 AS have_successes
+--eval SELECT $deadlocks > 0 AS have_deadlocks
+--enable_query_log
+
+
+--source suite/galera/t/MW-328-footer.inc
--- /dev/null
+#
+# MW-328 Fix unnecessary/silent BF aborts
+#
+
+#
+# Make sure an unrelated SELECT following a BF-aborted query never
+# gets the deadlock error
+#
+
+--source include/galera_cluster.inc
+--source suite/galera/t/MW-328-header.inc
+
+--connection node_2
+--let $count = 100
+
+SET SESSION wsrep_retry_autocommit = 0;
+
+--disable_query_log
+
+while ($count)
+{
+ --error 0,1213
+ INSERT IGNORE INTO t2 SELECT f2 FROM t1;
+
+ --disable_result_log
+ --error 0
+ SELECT 1 FROM DUAL;
+ --enable_result_log
+
+ --dec $count
+}
+
+--enable_query_log
+
+--source suite/galera/t/MW-328-footer.inc
--- /dev/null
+#
+# MW-328 Fix unnecessary/silent BF aborts
+#
+
+#
+# Make sure that a high value of wsrep_retry_autocommit
+# masks all deadlock errors
+#
+
+--source include/galera_cluster.inc
+--source suite/galera/t/MW-328-header.inc
+
+--connection node_2
+--let $count = 100
+
+SET SESSION wsrep_retry_autocommit = 10000;
+
+--disable_query_log
+
+while ($count)
+{
+ --error 0
+ INSERT IGNORE INTO t2 SELECT f2 FROM t1;
+
+ --disable_result_log
+ --error 0
+ SELECT 1 FROM DUAL;
+ --enable_result_log
+
+ --dec $count
+}
+
+--enable_query_log
+
+--source suite/galera/t/MW-328-footer.inc
--- /dev/null
+#
+# MW-328 Fix unnecessary/silent BF aborts
+#
+
+#
+# Test that non-Galera deadlock error still behaves as expected
+#
+
+--source include/galera_cluster.inc
+
+CREATE TABLE t1 (i INT) ENGINE = InnoDB;
+INSERT INTO t1 (i) VALUES(1);
+
+CREATE TABLE t2 (i INT) ENGINE = InnoDB;
+
+# Create a deadlock situation
+
+--connection node_1
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+SELECT * FROM t1 WHERE i = 1 LOCK IN SHARE MODE;
+
+--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
+--connection node_1a
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+--send INSERT IGNORE INTO t2 SELECT * FROM t1 WHERE i = 1 FOR UPDATE;
+
+--connection node_1
+--sleep 2
+DELETE FROM t1 WHERE i = 1;
+COMMIT;
+
+# We expect that ER_LOCK_DEADLOCK will be delivered even though it was a INSERT INGORE statement
+--connection node_1a
+--error ER_LOCK_DEADLOCK
+--reap
+
+DROP TABLE t1, t2;
--- /dev/null
+#
+# MW-328 Fix unnecessary/silent BF aborts
+#
+
+#
+# Test that non-Galera deadlock error still behaves as expected (case #2)
+#
+
+--source include/galera_cluster.inc
+
+create table t1 (i int primary key, j int) engine=innodb;
+create table t2 (i int primary key, j int) engine=innodb;
+
+insert into t1 values (1,0);
+insert into t2 values (2,0);
+
+set autocommit=off;
+start transaction;
+update t1 set j=1 where i=1;
+
+--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
+--connection node_1a
+set autocommit=off;
+start transaction;
+begin;
+update t2 set j=1 where i=2;
+
+--connection node_1
+# Hang expected here
+--send insert into t1 select * from t2;
+
+--sleep 2
+--connection node_1a
+--error ER_LOCK_DEADLOCK
+insert into t2 select * from t1;
+
+--connection node_1
+--reap
+
+DROP TABLE t1, t2;
--- /dev/null
+--wsrep-retry-autocommit=0
--- /dev/null
+#
+# #MW-329 Fix incorrect affected rows count after replay
+#
+
+--source include/galera_cluster.inc
+
+CREATE TABLE t1 (f1 INTEGER, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB;
+
+# We start with a populated table
+INSERT INTO t1 (f1) VALUES (1),(65535);
+
+# Clear the wsrep_local_replays counter
+
+FLUSH STATUS;
+SELECT VARIABLE_VALUE = 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays';
+
+#
+# Run concurrent INSERTs
+#
+
+DELIMITER |;
+CREATE PROCEDURE proc_insert ()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
+ SET SESSION wsrep_sync_wait = 0;
+ WHILE 1 DO
+ INSERT INTO t1 (f1) VALUES (FLOOR( 1 + RAND( ) * 65535 ));
+ END WHILE;
+END|
+DELIMITER ;|
+
+--connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1
+--connection node_1b
+--let $connection_id = `SELECT CONNECTION_ID()`
+--send CALL proc_insert();
+
+#
+# Run concurrent UPDATEs. We expect that each UPDATE will report that
+# some rows were matched and updated
+#
+
+--connection node_2
+--let $count = 10
+while ($count)
+{
+ --let $signature = `SELECT LEFT(MD5(RAND()), 10)`
+ --disable_query_log
+ --error 0,ER_LOCK_DEADLOCK
+ --eval UPDATE t1 SET f2 = '$signature'
+ --enable_query_log
+ --let $row_count = `SELECT ROW_COUNT()`
+ if (`SELECT @@error_count = 0`) {
+ if (`SELECT $row_count = 0`) {
+ --die ROW_COUNT() = 0
+ }
+ }
+ --dec $count
+}
+
+#
+# Confirm that some transaction replays occurred
+#
+
+SELECT VARIABLE_VALUE > 0 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays';
+
+#
+# Terminate the stored procedure
+#
+
+--connection node_1
+--disable_query_log
+--eval KILL CONNECTION $connection_id
+--enable_query_log
+
+--connection node_1b
+--error 0,2013,1317
+--reap
+
+--connection node_1
+DROP PROCEDURE proc_insert;
+DROP TABLE t1;
--- /dev/null
+!include ../galera_2nodes_as_slave.cnf
+
+[mysqld]
+gtid-mode=ON
+log-bin=mysqld-bin
+log-slave-updates
+enforce-gtid-consistency
+binlog-format=ROW
+
+[mysqld.2]
+replicate-do-db=test1
+replicate-wild-do-table=test1.%
+
+[mysqld.3]
+replicate-do-db=test1
+replicate-wild-do-table=test1.%
--- /dev/null
+#
+# Test Galera as a slave to a MySQL master using GTIDs
+#
+
+--source include/have_innodb.inc
+--source include/have_log_bin.inc
+
+# As node #1 is not a Galera node, we connect to node #2 in order to run include/galera_cluster.inc
+--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
+--source include/galera_cluster.inc
+
+--connection node_1
+RESET MASTER;
+
+--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
+--connection node_3
+RESET MASTER;
+
+--connection node_2
+RESET MASTER;
+--disable_query_log
+--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
+--enable_query_log
+START SLAVE USER='root';
+
+--connection node_1
+CREATE SCHEMA test1;
+CREATE SCHEMA test2;
+USE test1;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY,f2 CHAR(5) DEFAULT 'abc') ENGINE=InnoDB;
+USE test2;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY,f2 CHAR(5) DEFAULT 'abc') ENGINE=InnoDB;
+
+#
+# First , some autocommit stuff
+#
+
+# Simple inserts
+
+INSERT INTO test1.t1 (f1) VALUES (1);
+INSERT INTO test2.t1 (f1) VALUES (1);
+
+INSERT INTO test1.t1 (f1) VALUES (2);
+INSERT INTO test2.t1 (f1) VALUES (2);
+
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+
+# Update that only covers test2.t1
+
+UPDATE test2.t1 SET test2.t1.f2 = 'cde';
+
+# Multi-table UPDATE
+
+UPDATE test1.t1, test2.t1 SET test1.t1.f2 = 'klm', test2.t1.f2 = 'xyz';
+
+# Multi-table DELETE
+
+DELETE test1.t1, test2.t1 FROM test1.t1 INNER JOIN test2.t1 WHERE test1.t1.f1 = test2.t1.f1 AND test1.t1.f1 = 3;
+
+#
+# Multi-statement transactions
+#
+
+# Transaction which is not replicated at all
+
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO test2.t1 (f1) VALUES (999);
+INSERT INTO test2.t1 (f1) VALUES (9999);
+COMMIT;
+
+# Transaction that is completely replicated
+START TRANSACTION;
+INSERT INTO test1.t1 (f1) VALUES (111);
+INSERT INTO test1.t1 (f1) VALUES (222);
+COMMIT;
+
+# Transaction that is partially replicated
+
+START TRANSACTION;
+INSERT INTO test1.t1 (f1) VALUES (333);
+INSERT INTO test2.t1 (f1) VALUES (99999);
+COMMIT;
+
+#
+# Make sure binary logs and gtid_executed strings are equal
+#
+
+--sleep 2
+--connection node_2
+--let $effective_uuid = `SELECT LEFT(@@global.gtid_executed, 36)`
+--let $gtid_executed_node2 = `SELECT @@global.gtid_executed;`
+
+--replace_result $effective_uuid <effective_uuid>
+--replace_regex /xid=[0-9]+/xid=###/ /table_id: [0-9]+/table_id: ###/
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+
+--connection node_3
+
+--disable_query_log
+--eval SELECT '$gtid_executed_node2' = @@global.gtid_executed AS gtid_executed_equal;
+--enable_query_log
+
+--replace_result $effective_uuid <effective_uuid>
+--replace_regex /xid=[0-9]+/xid=###/ /table_id: [0-9]+/table_id: ###/
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+
+#
+# Final consistency checks
+#
+
+--let $diff_servers = 2 3
+--source include/diff_servers.inc
+
+--connection node_3
+--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--source include/wait_condition.inc
+
+SELECT COUNT(*) = 2 FROM test1.t1 WHERE f1 IN (1,2);
+SELECT COUNT(*) = 3 FROM test1.t1 WHERE f1 IN (111,222,333);
+SELECT COUNT(*) = 2 FROM test1.t1 WHERE f2 = 'klm';
+
+--error 1049
+USE test2;
+
+#
+# Cleanup
+#
+
+--connection node_1
+DROP SCHEMA test1;
+DROP SCHEMA test2;
+
+--sleep 1
+
+--connection node_3
+--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--source include/wait_condition.inc
+
+--connection node_2
+--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--source include/wait_condition.inc
+
+STOP SLAVE;
+RESET SLAVE ALL;
--- /dev/null
+!include ../galera_3nodes_as_slave.cnf
+
+[mysqld]
+gtid-mode=ON
+log-bin=mysqld-bin
+log-slave-updates
+enforce-gtid-consistency
+binlog-format=ROW
+
+[mysqld.2]
+replicate-do-db=test1
+replicate-wild-do-table=test1.%
+
+[mysqld.3]
+replicate-do-db=test1
+replicate-wild-do-table=test1.%
--- /dev/null
+#
+# Test the case where a Galera slave to async replication goes non-prim while
+# a stream of replication events including filtered events is arriving
+#
+
+--source include/have_innodb.inc
+--source include/have_log_bin.inc
+--source include/big_test.inc
+
+# As node #1 is not a Galera node, we connect to node #2 in order to run include/galera_cluster.inc
+--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
+--source include/galera_cluster.inc
+
+--connection node_1
+RESET MASTER;
+
+--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
+--connection node_3
+RESET MASTER;
+
+--connection node_2
+RESET MASTER;
+
+--connection node_2
+--disable_query_log
+--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$NODE_MYPORT_1;
+--enable_query_log
+START SLAVE USER='root';
+
+--connection node_1
+CREATE SCHEMA test1;
+CREATE SCHEMA test2;
+USE test1;
+CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
+USE test2;
+CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
+
+INSERT INTO test1.t1 (f1) VALUES (1);
+INSERT INTO test2.t1 (f1) VALUES (1);
+
+INSERT INTO test1.t1 (f1) VALUES (2);
+INSERT INTO test2.t1 (f1) VALUES (2);
+
+--connection node_2
+--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--source include/wait_condition.inc
+
+--let $wait_condition = SELECT COUNT(*) = 2 FROM test1.t1;
+--source include/wait_condition.inc
+
+SET GLOBAL wsrep_provider_options='gmcast.isolate=1';
+
+--connection node_1
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+INSERT INTO test1.t1 (f1) VALUES (3);
+INSERT INTO test2.t1 (f1) VALUES (3);
+
+--connection node_2
+SET SESSION wsrep_on=OFF;
+--let $wait_condition = SELECT VARIABLE_VALUE = 'non-Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE variable_name = 'wsrep_cluster_status';
+--source include/wait_condition.inc
+
+--let $slave_sql_errno = 1047
+--source include/wait_for_slave_sql_error.inc
+
+--connection node_1
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+INSERT INTO test1.t1 (f1) VALUES (4);
+INSERT INTO test2.t1 (f1) VALUES (4);
+
+--connection node_2
+SET GLOBAL wsrep_provider_options='gmcast.isolate=0';
+
+--connection node_1
+INSERT INTO test1.t1 (f1) VALUES (5);
+INSERT INTO test2.t1 (f1) VALUES (5);
+
+--connection node_2
+--let $wait_condition = SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE variable_name = 'wsrep_cluster_status';
+--source include/wait_condition.inc
+SET SESSION wsrep_on=ON;
+--let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE variable_name = 'wsrep_ready';
+--source include/wait_condition.inc
+
+--connection node_1
+INSERT INTO test1.t1 (f1) VALUES (6);
+INSERT INTO test2.t1 (f1) VALUES (6);
+
+--connection node_2
+START SLAVE;
+
+#
+# Consistency checks
+#
+
+--sleep 2
+--connection node_2
+--let $wait_condition = SELECT COUNT(DISTINCT f1) = 6 FROM test1.t1;
+--source include/wait_condition.inc
+
+--connection node_3
+--let $wait_condition = SELECT COUNT(DISTINCT f1) = 6 FROM test1.t1;
+--source include/wait_condition.inc
+
+--connection node_2
+--let $gtid_executed_node2 = `SELECT @@global.gtid_executed;`
+--let $effective_uuid = `SELECT LEFT(@@global.gtid_executed, 36)`
+
+--replace_result $effective_uuid <effective_uuid>
+--replace_regex /xid=[0-9]+/xid=###/ /table_id: [0-9]+/table_id: ###/
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+
+--error 1049
+USE test2;
+
+--connection node_3
+
+--disable_query_log
+--eval SELECT '$gtid_executed_node2' = @@global.gtid_executed AS gtid_executed_equal;
+--enable_query_log
+
+--error 1049
+USE test2;
+
+--replace_result $effective_uuid <effective_uuid>
+--replace_regex /xid=[0-9]+/xid=###/ /table_id: [0-9]+/table_id: ###/
+SHOW BINLOG EVENTS IN 'mysqld-bin.000001' FROM 120;
+
+#
+# Cleanup
+#
+
+--connection node_1
+DROP SCHEMA test1;
+DROP SCHEMA test2;
+
+--sleep 1
+
+--connection node_3
+--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--source include/wait_condition.inc
+
+--connection node_2
+--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--source include/wait_condition.inc
+
+--connection node_2
+STOP SLAVE;
+RESET SLAVE ALL;
+CALL mtr.add_suppression("GTID replication failed");
+CALL mtr.add_suppression("Slave SQL: Error in Xid_log_event: Commit could not be completed");
+CALL mtr.add_suppression("Slave SQL: Node has dropped from cluster, Error_code: 1047");
+CALL mtr.add_suppression("TO isolation failed for");
+CALL mtr.add_suppression("Slave SQL: Error 'Deadlock found when trying to get lock; try restarting transaction' on query");
+CALL mtr.add_suppression("Slave SQL: Error 'WSREP has not yet prepared node for application use' on query");
+CALL mtr.add_suppression("Slave: WSREP has not yet prepared node for application use Error_code: 1047");
!include ../galera_2nodes.cnf
[mysqld]
-#wsrep_sst_method=xtrabackup-v2
-#wsrep_sst_auth="root:"
-#wsrep_debug=ON
+wsrep_sst_method=xtrabackup-v2
+wsrep_sst_auth="root:"
[mysqld.1]
wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcache.size=1;pc.ignore_sb=true'
#
# Test that autoincrement works correctly while the cluster membership
-# is changing and IST takes place.
+# is changing and SST takes place.
#
--source include/big_test.inc
--- /dev/null
+--wsrep_log_conflicts=ON
--- /dev/null
+#
+# Test --wsrep_log_conflicts=ON
+#
+
+--source include/galera_cluster.inc
+--source include/have_innodb.inc
+
+CREATE TABLE t1 (
+ f1 VARCHAR(255) PRIMARY KEY
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO t1 VALUES ('abc');
+
+--connection node_2
+SELECT f1 = 'abc' FROM t1;
+
+#
+# Provoke a conflict
+#
+
+--connection node_1
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+UPDATE t1 SET f1 = 'klm';
+
+--connection node_2
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+UPDATE t1 SET f1 = 'xyz';
+
+--connection node_1
+COMMIT;
+
+--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
+--connection node_2a
+--let $wait_condition = SELECT f1 = 'klm' FROM t1;
+--source include/wait_condition.inc
+
+--connection node_2
+--error ER_LOCK_DEADLOCK
+COMMIT;
+
+#
+# We can not really check the log output very much because it is quite variable
+#
+
+--let $assert_file = $MYSQLTEST_VARDIR/log/mysqld.2.err
+--let $assert_only_after = CURRENT_TEST
+
+--let $assert_text = cluster conflict due to high priority abort for threads
+--let $assert_select = cluster conflict due to high priority abort for threads
+--let $assert_match = cluster conflict due to high priority abort for threads
+--source include/assert_grep.inc
+
+DROP TABLE t1;
--- /dev/null
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
+# SETTING auto_increment_increment IN CONNECTION DEFAULT
+SET AUTO_INCREMENT_INCREMENT = 1;
+INSERT INTO t1 VALUES(NULL);
+SELECT * FROM t1;
+id
+1
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
+# SETTING auto_increment_increment IN CONNECTION1
+SET AUTO_INCREMENT_INCREMENT = 2;
+SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
+INSERT INTO t1 VALUES(NULL);
+SET AUTO_INCREMENT_INCREMENT = 2;
+SET DEBUG_SYNC= 'now WAIT_FOR opened';
+SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
+insert into t1 values(NULL);
+# CONNECTION default
+SELECT * FROM t1;
+id
+1
+3
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
+SET DEBUG_SYNC= 'now SIGNAL opened1';
+# CONNECTION con1
+SELECT * FROM t1;
+id
+1
+3
+5
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
+# SETTING auto_increment_increment IN CONNECTION DEFAULT
+SET AUTO_INCREMENT_INCREMENT = 1;
+INSERT INTO t1 VALUES(NULL);
+SELECT * FROM t1;
+id
+1
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
+SET DEBUG_SYNC = 'now SIGNAL flushed';
+# SETTING auto_increment_increment in connection1
+SET AUTO_INCREMENT_INCREMENT = 2;
+SET DEBUG_SYNC= 'now WAIT_FOR flushed';
+SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
+INSERT INTO t1 values(NULL);
+# CONNECTION DEFAULT
+SET DEBUG_SYNC= 'now WAIT_FOR opened';
+SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
+INSERT INTO t1 VALUES(NULL);
+# CONNECTION con1
+SELECT * FROM t1;
+id
+1
+3
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
+SET DEBUG_SYNC= 'now SIGNAL opened1';
+# CONNECTION default
+SELECT * FROM t1;
+id
+1
+3
+5
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
+DROP TABLE t1;
21 aaaaaaaaaaaaaaaaaaaa
22 bbbbbbbbbbbbbbbbbbbb
23 cccccccccccccccccccc
+SET GLOBAL innodb_fast_shutdown=0;
"test misc 1"
"test misc 2"
"test misc 3"
3784744,4180925,4559596,3963734,3856391,4494153)
AND f5 = 'abcdefghijklmnopwrst' AND f2 = 1221457 AND f4 = 0 ;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref PRIMARY,idx1,idx2 idx1 60 const,const,const 18 Using index condition
+1 SIMPLE t1 range PRIMARY,idx1,idx2 idx1 64 NULL 18 Using index condition
DROP TABLE t1;
#
# Bug#51431 Wrong sort order after import of dump file
--- /dev/null
+#
+# Bug 23333990 PERSISTENT INDEX STATISTICS UPDATE BEFORE
+# TRANSACTION IS COMMITTED
+#
+"Test 1:- Uncommited delete test"
+CREATE TABLE t1 (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+val INT UNSIGNED NOT NULL,
+INDEX (val)) ENGINE=INNODB
+STATS_PERSISTENT=1,STATS_AUTO_RECALC=1;
+INSERT INTO t1 (val) VALUES (4);
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+262144
+Connection 1
+START TRANSACTION;
+DELETE FROM t1;
+SELECT COUNT(*) FROM t1;
+Connection Default
+analyze table t1;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+Test correctly estimates the number of rows as > 20000
+even when in other uncommmited transaction
+all rows have been deleted.
+Connection 1
+COUNT(*)
+0
+commit;
+Connection deafult
+Test 2:- Insert and rollback test
+CREATE TABLE t2 (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+val INT UNSIGNED NOT NULL,
+INDEX (val)) ENGINE=INNODB
+STATS_PERSISTENT=1,STATS_AUTO_RECALC=1;
+Connection 1
+START TRANSACTION;
+INSERT INTO t2 (val) VALUES (4);
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+SELECT COUNT(*) FROM t2;
+Connection default
+select count(*) from t2;
+count(*)
+0
+analyze table t2;
+Table Op Msg_type Msg_text
+test.t2 analyze status OK
+Test correctly estimates the number of rows as > 20000
+even when in other uncommited transaction
+many rows are inserted.
+Connection 1
+COUNT(*)
+262144
+Rollback the insert
+rollback;
+Connection default
+Test correctly estimates the number of rows as 1
+after rollback.
+DROP TABLE t1,t2;
--- /dev/null
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source include/have_debug.inc
+
+# Two parallel connection with autoinc column after restart.
+
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
+
+--echo # SETTING auto_increment_increment IN CONNECTION DEFAULT
+SET AUTO_INCREMENT_INCREMENT = 1;
+INSERT INTO t1 VALUES(NULL);
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+
+--source include/restart_mysqld.inc
+
+--echo # SETTING auto_increment_increment IN CONNECTION1
+SET AUTO_INCREMENT_INCREMENT = 2;
+
+SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
+
+SEND INSERT INTO t1 VALUES(NULL);
+
+connect(con1, localhost, root,,);
+SET AUTO_INCREMENT_INCREMENT = 2;
+SET DEBUG_SYNC= 'now WAIT_FOR opened';
+SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
+send insert into t1 values(NULL);
+
+--echo # CONNECTION default
+connection default;
+reap;
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+SET DEBUG_SYNC= 'now SIGNAL opened1';
+
+--echo # CONNECTION con1
+connection con1;
+reap;
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+connection default;
+disconnect con1;
+
+DROP TABLE t1;
+
+# Two parallel connection with autoinc column without restart.
+
+CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
+
+--echo # SETTING auto_increment_increment IN CONNECTION DEFAULT
+SET AUTO_INCREMENT_INCREMENT = 1;
+INSERT INTO t1 VALUES(NULL);
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+SET DEBUG_SYNC = 'now SIGNAL flushed';
+
+connect(con1, localhost, root,,);
+
+--echo # SETTING auto_increment_increment in connection1
+SET AUTO_INCREMENT_INCREMENT = 2;
+
+SET DEBUG_SYNC= 'now WAIT_FOR flushed';
+SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
+
+send INSERT INTO t1 values(NULL);
+
+--echo # CONNECTION DEFAULT
+connection default;
+
+SET DEBUG_SYNC= 'now WAIT_FOR opened';
+SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
+
+send INSERT INTO t1 VALUES(NULL);
+
+--echo # CONNECTION con1
+connection con1;
+reap;
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+SET DEBUG_SYNC= 'now SIGNAL opened1';
+
+--echo # CONNECTION default
+connection default;
+reap;
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+disconnect con1;
+DROP TABLE t1;
innodb-index-debug : Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT
flush-hang : Unsafe statement written to the binary log
innodb_deadlock_with_autoinc : Test uses autoinc_lock_mode = 0
+innodb_stats_del_mark : Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.
let $args=--loose-console > $SEARCH_FILE 2>&1;
# Stop the server
+SET GLOBAL innodb_fast_shutdown=0;
let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect;
--exec echo "wait" > $restart_file
--shutdown_server
--- /dev/null
+--innodb_stats_include_delete_marked=on
--- /dev/null
+--echo #
+--echo # Bug 23333990 PERSISTENT INDEX STATISTICS UPDATE BEFORE
+--echo # TRANSACTION IS COMMITTED
+--echo #
+
+--source include/big_test.inc
+--source include/not_valgrind.inc
+
+--echo "Test 1:- Uncommited delete test"
+CREATE TABLE t1 (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ val INT UNSIGNED NOT NULL,
+ INDEX (val)) ENGINE=INNODB
+ STATS_PERSISTENT=1,STATS_AUTO_RECALC=1;
+
+
+INSERT INTO t1 (val) VALUES (4);
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+INSERT INTO t1 (val) SELECT VAL from t1;
+SELECT COUNT(*) FROM t1;
+
+connect(con1, localhost, root,,);
+
+--echo Connection 1
+connection con1;
+START TRANSACTION;
+DELETE FROM t1;
+send SELECT COUNT(*) FROM t1;
+
+--echo Connection Default
+connection default;
+# To make test determinstic in case stats calculation is not
+# triggered we will call analyze table
+analyze table t1;
+let $row_count= query_get_value(EXPLAIN SELECT * FROM t1 WHERE val=4, rows,1);
+if ($row_count > 20000)
+{
+--echo Test correctly estimates the number of rows as > 20000
+--echo even when in other uncommmited transaction
+--echo all rows have been deleted.
+}
+
+if ($row_count < 20000)
+{
+--echo FAIL row count is $row_count
+}
+--echo Connection 1
+connection con1;
+reap;
+commit;
+
+--echo Connection deafult
+connection default;
+
+--echo Test 2:- Insert and rollback test
+CREATE TABLE t2 (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ val INT UNSIGNED NOT NULL,
+ INDEX (val)) ENGINE=INNODB
+ STATS_PERSISTENT=1,STATS_AUTO_RECALC=1;
+
+--echo Connection 1
+connection con1;
+
+START TRANSACTION;
+
+INSERT INTO t2 (val) VALUES (4);
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+INSERT INTO t2 (val) SELECT VAL from t2;
+send SELECT COUNT(*) FROM t2;
+
+--echo Connection default
+connection default;
+select count(*) from t2;
+analyze table t2;
+let $row_count= query_get_value(EXPLAIN SELECT * FROM t2 WHERE val=4, rows,1);
+if ($row_count > 20000)
+{
+--echo Test correctly estimates the number of rows as > 20000
+--echo even when in other uncommited transaction
+--echo many rows are inserted.
+}
+
+--echo Connection 1
+connection con1;
+reap;
+--echo Rollback the insert
+rollback;
+
+--echo Connection default
+connection default;
+let $row_count= query_get_value(EXPLAIN SELECT * FROM t2 WHERE val=4, rows,1);
+if ($row_count <= 1)
+{
+--echo Test correctly estimates the number of rows as $row_count
+--echo after rollback.
+}
+
+disconnect con1;
+DROP TABLE t1,t2;
HAVING alias1.col_int_nokey IN ( SELECT 2 FROM DUAL ) ;
DROP TABLE t1,t2,t3,t4;
+
+--echo #
+--echo # Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+--echo # INDEX, EVEN THOUGH COST IS HIGHER
+--echo #
+
+CREATE TABLE t1 (
+ a TINYTEXT NOT NULL,
+ b TINYINT(3) UNSIGNED NOT NULL,
+ PRIMARY KEY (a(32),b),
+ KEY b_idx(b)
+) ENGINE=INNODB;
+INSERT INTO t1 VALUES ('a',1),('a',2),('a',3),('b',1),('b',4),('c',1),('d',1),
+ ('c',4),('d',3),('e',2),('f',2);
+
+SET @optimizer_switch_saved=@@session.optimizer_switch;
+SET @@session.optimizer_switch=default;
+SET optimizer_trace="enabled=on";
+
+# Uses "access_type_changed" to use range over ref despite b_idx not being the
+# cheapest in range.
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2;
+
+# Output of table_scan in test_quick_select is not always reliable.
+--replace_regex /"rows": 12/"rows": 11/ /"cost": 5.5/"cost": 5.3/
+SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
+
+SET optimizer_trace="enabled=off";
+SET @@session.optimizer_switch=@optimizer_switch_saved;
+
+DROP TABLE t1;
HAVING alias1.col_int_nokey IN ( SELECT 2 FROM DUAL ) ;
field1 field2 field3 field4
DROP TABLE t1,t2,t3,t4;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE t1 (
+a TINYTEXT NOT NULL,
+b TINYINT(3) UNSIGNED NOT NULL,
+PRIMARY KEY (a(32),b),
+KEY b_idx(b)
+) ENGINE=INNODB;
+INSERT INTO t1 VALUES ('a',1),('a',2),('a',3),('b',1),('b',4),('c',1),('d',1),
+('c',4),('d',3),('e',2),('f',2);
+SET @optimizer_switch_saved=@@session.optimizer_switch;
+SET @@session.optimizer_switch=default;
+SET optimizer_trace="enabled=on";
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY,b_idx b_idx 35 NULL 2 Using index condition; Using where
+SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
+QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2 {
+ "steps": [
+ {
+ "join_preparation": {
+ "select#": 1,
+ "steps": [
+ {
+ "expanded_query": "/* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where ((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))"
+ }
+ ] /* steps */
+ } /* join_preparation */
+ },
+ {
+ "join_optimization": {
+ "select#": 1,
+ "steps": [
+ {
+ "condition_processing": {
+ "condition": "WHERE",
+ "original_condition": "((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))",
+ "steps": [
+ {
+ "transformation": "equality_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "constant_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "trivial_condition_removal",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ }
+ ] /* steps */
+ } /* condition_processing */
+ },
+ {
+ "table_dependencies": [
+ {
+ "table": "`t1`",
+ "row_may_be_null": false,
+ "map_bit": 0,
+ "depends_on_map_bits": [
+ ] /* depends_on_map_bits */
+ }
+ ] /* table_dependencies */
+ },
+ {
+ "ref_optimizer_key_uses": [
+ {
+ "table": "`t1`",
+ "field": "b",
+ "equals": "2",
+ "null_rejecting": false
+ }
+ ] /* ref_optimizer_key_uses */
+ },
+ {
+ "rows_estimation": [
+ {
+ "table": "`t1`",
+ "range_analysis": {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": true,
+ "key_parts": [
+ "a",
+ "b"
+ ] /* key_parts */
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "PRIMARY",
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": true,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 2.41,
+ "chosen": true
+ },
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": false,
+ "cause": "cost"
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "PRIMARY",
+ "rows": 2,
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 2.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ }
+ ] /* rows_estimation */
+ },
+ {
+ "considered_execution_plans": [
+ {
+ "plan_prefix": [
+ ] /* plan_prefix */,
+ "table": "`t1`",
+ "best_access_path": {
+ "considered_access_paths": [
+ {
+ "access_type": "ref",
+ "index": "b_idx",
+ "rows": 2,
+ "cost": 2.4,
+ "chosen": true
+ },
+ {
+ "access_type": "range",
+ "rows": 2,
+ "cost": 2.81,
+ "chosen": false
+ }
+ ] /* considered_access_paths */
+ } /* best_access_path */,
+ "cost_for_plan": 2.4,
+ "rows_for_plan": 2,
+ "chosen": true
+ }
+ ] /* considered_execution_plans */
+ },
+ {
+ "attaching_conditions_to_tables": {
+ "original_condition": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))",
+ "attached_conditions_computation": [
+ {
+ "rerunning_range_optimizer_for_single_index": [
+ {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": false,
+ "cause": "not_applicable"
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": true
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "b_idx",
+ "rows": 2,
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 4.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ ] /* rerunning_range_optimizer_for_single_index */
+ },
+ {
+ "access_type_changed": {
+ "table": "`t1`",
+ "index": "b_idx",
+ "old_type": "ref",
+ "new_type": "range",
+ "cause": "uses_more_keyparts"
+ } /* access_type_changed */
+ }
+ ] /* attached_conditions_computation */,
+ "attached_conditions_summary": [
+ {
+ "table": "`t1`",
+ "attached": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))"
+ }
+ ] /* attached_conditions_summary */
+ } /* attaching_conditions_to_tables */
+ },
+ {
+ "refine_plan": [
+ {
+ "table": "`t1`",
+ "pushed_index_condition": "(`t1`.`b` = 2)",
+ "table_condition_attached": "(`t1`.`a` in ('a','b'))",
+ "access_type": "range"
+ }
+ ] /* refine_plan */
+ }
+ ] /* steps */
+ } /* join_optimization */
+ },
+ {
+ "join_explain": {
+ "select#": 1,
+ "steps": [
+ ] /* steps */
+ } /* join_explain */
+ }
+ ] /* steps */
+} 0 0
+SET optimizer_trace="enabled=off";
+SET @@session.optimizer_switch=@optimizer_switch_saved;
+DROP TABLE t1;
HAVING alias1.col_int_nokey IN ( SELECT 2 FROM DUAL ) ;
field1 field2 field3 field4
DROP TABLE t1,t2,t3,t4;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE t1 (
+a TINYTEXT NOT NULL,
+b TINYINT(3) UNSIGNED NOT NULL,
+PRIMARY KEY (a(32),b),
+KEY b_idx(b)
+) ENGINE=INNODB;
+INSERT INTO t1 VALUES ('a',1),('a',2),('a',3),('b',1),('b',4),('c',1),('d',1),
+('c',4),('d',3),('e',2),('f',2);
+SET @optimizer_switch_saved=@@session.optimizer_switch;
+SET @@session.optimizer_switch=default;
+SET optimizer_trace="enabled=on";
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY,b_idx b_idx 35 NULL 2 Using index condition; Using where
+SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
+QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2 {
+ "steps": [
+ {
+ "join_preparation": {
+ "select#": 1,
+ "steps": [
+ {
+ "expanded_query": "/* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where ((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))"
+ }
+ ] /* steps */
+ } /* join_preparation */
+ },
+ {
+ "join_optimization": {
+ "select#": 1,
+ "steps": [
+ {
+ "condition_processing": {
+ "condition": "WHERE",
+ "original_condition": "((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))",
+ "steps": [
+ {
+ "transformation": "equality_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "constant_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "trivial_condition_removal",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ }
+ ] /* steps */
+ } /* condition_processing */
+ },
+ {
+ "table_dependencies": [
+ {
+ "table": "`t1`",
+ "row_may_be_null": false,
+ "map_bit": 0,
+ "depends_on_map_bits": [
+ ] /* depends_on_map_bits */
+ }
+ ] /* table_dependencies */
+ },
+ {
+ "ref_optimizer_key_uses": [
+ {
+ "table": "`t1`",
+ "field": "b",
+ "equals": "2",
+ "null_rejecting": false
+ }
+ ] /* ref_optimizer_key_uses */
+ },
+ {
+ "rows_estimation": [
+ {
+ "table": "`t1`",
+ "range_analysis": {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": true,
+ "key_parts": [
+ "a",
+ "b"
+ ] /* key_parts */
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "PRIMARY",
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": true,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 2.41,
+ "chosen": true
+ },
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": false,
+ "cause": "cost"
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "PRIMARY",
+ "rows": 2,
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 2.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ }
+ ] /* rows_estimation */
+ },
+ {
+ "considered_execution_plans": [
+ {
+ "plan_prefix": [
+ ] /* plan_prefix */,
+ "table": "`t1`",
+ "best_access_path": {
+ "considered_access_paths": [
+ {
+ "access_type": "ref",
+ "index": "b_idx",
+ "rows": 2,
+ "cost": 2.4,
+ "chosen": true
+ },
+ {
+ "access_type": "range",
+ "rows": 2,
+ "cost": 2.81,
+ "chosen": false
+ }
+ ] /* considered_access_paths */
+ } /* best_access_path */,
+ "cost_for_plan": 2.4,
+ "rows_for_plan": 2,
+ "chosen": true
+ }
+ ] /* considered_execution_plans */
+ },
+ {
+ "attaching_conditions_to_tables": {
+ "original_condition": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))",
+ "attached_conditions_computation": [
+ {
+ "rerunning_range_optimizer_for_single_index": [
+ {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": false,
+ "cause": "not_applicable"
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": true
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "b_idx",
+ "rows": 2,
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 4.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ ] /* rerunning_range_optimizer_for_single_index */
+ },
+ {
+ "access_type_changed": {
+ "table": "`t1`",
+ "index": "b_idx",
+ "old_type": "ref",
+ "new_type": "range",
+ "cause": "uses_more_keyparts"
+ } /* access_type_changed */
+ }
+ ] /* attached_conditions_computation */,
+ "attached_conditions_summary": [
+ {
+ "table": "`t1`",
+ "attached": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))"
+ }
+ ] /* attached_conditions_summary */
+ } /* attaching_conditions_to_tables */
+ },
+ {
+ "refine_plan": [
+ {
+ "table": "`t1`",
+ "pushed_index_condition": "(`t1`.`b` = 2)",
+ "table_condition_attached": "(`t1`.`a` in ('a','b'))",
+ "access_type": "range"
+ }
+ ] /* refine_plan */
+ }
+ ] /* steps */
+ } /* join_optimization */
+ },
+ {
+ "join_explain": {
+ "select#": 1,
+ "steps": [
+ ] /* steps */
+ } /* join_explain */
+ }
+ ] /* steps */
+} 0 0
+SET optimizer_trace="enabled=off";
+SET @@session.optimizer_switch=@optimizer_switch_saved;
+DROP TABLE t1;
HAVING alias1.col_int_nokey IN ( SELECT 2 FROM DUAL ) ;
field1 field2 field3 field4
DROP TABLE t1,t2,t3,t4;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE t1 (
+a TINYTEXT NOT NULL,
+b TINYINT(3) UNSIGNED NOT NULL,
+PRIMARY KEY (a(32),b),
+KEY b_idx(b)
+) ENGINE=INNODB;
+INSERT INTO t1 VALUES ('a',1),('a',2),('a',3),('b',1),('b',4),('c',1),('d',1),
+('c',4),('d',3),('e',2),('f',2);
+SET @optimizer_switch_saved=@@session.optimizer_switch;
+SET @@session.optimizer_switch=default;
+SET optimizer_trace="enabled=on";
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY,b_idx b_idx 35 NULL 2 Using index condition; Using where
+SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
+QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2 {
+ "steps": [
+ {
+ "join_preparation": {
+ "select#": 1,
+ "steps": [
+ {
+ "expanded_query": "/* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where ((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))"
+ }
+ ] /* steps */
+ } /* join_preparation */
+ },
+ {
+ "join_optimization": {
+ "select#": 1,
+ "steps": [
+ {
+ "condition_processing": {
+ "condition": "WHERE",
+ "original_condition": "((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))",
+ "steps": [
+ {
+ "transformation": "equality_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "constant_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "trivial_condition_removal",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ }
+ ] /* steps */
+ } /* condition_processing */
+ },
+ {
+ "table_dependencies": [
+ {
+ "table": "`t1`",
+ "row_may_be_null": false,
+ "map_bit": 0,
+ "depends_on_map_bits": [
+ ] /* depends_on_map_bits */
+ }
+ ] /* table_dependencies */
+ },
+ {
+ "ref_optimizer_key_uses": [
+ {
+ "table": "`t1`",
+ "field": "b",
+ "equals": "2",
+ "null_rejecting": false
+ }
+ ] /* ref_optimizer_key_uses */
+ },
+ {
+ "rows_estimation": [
+ {
+ "table": "`t1`",
+ "range_analysis": {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": true,
+ "key_parts": [
+ "a",
+ "b"
+ ] /* key_parts */
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "PRIMARY",
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": true,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 2.41,
+ "chosen": true
+ },
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": false,
+ "cause": "cost"
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "PRIMARY",
+ "rows": 2,
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 2.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ }
+ ] /* rows_estimation */
+ },
+ {
+ "considered_execution_plans": [
+ {
+ "plan_prefix": [
+ ] /* plan_prefix */,
+ "table": "`t1`",
+ "best_access_path": {
+ "considered_access_paths": [
+ {
+ "access_type": "ref",
+ "index": "b_idx",
+ "rows": 2,
+ "cost": 2.4,
+ "chosen": true
+ },
+ {
+ "access_type": "range",
+ "rows": 2,
+ "cost": 2.81,
+ "chosen": false
+ }
+ ] /* considered_access_paths */
+ } /* best_access_path */,
+ "cost_for_plan": 2.4,
+ "rows_for_plan": 2,
+ "chosen": true
+ }
+ ] /* considered_execution_plans */
+ },
+ {
+ "attaching_conditions_to_tables": {
+ "original_condition": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))",
+ "attached_conditions_computation": [
+ {
+ "rerunning_range_optimizer_for_single_index": [
+ {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": false,
+ "cause": "not_applicable"
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": true
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "b_idx",
+ "rows": 2,
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 4.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ ] /* rerunning_range_optimizer_for_single_index */
+ },
+ {
+ "access_type_changed": {
+ "table": "`t1`",
+ "index": "b_idx",
+ "old_type": "ref",
+ "new_type": "range",
+ "cause": "uses_more_keyparts"
+ } /* access_type_changed */
+ }
+ ] /* attached_conditions_computation */,
+ "attached_conditions_summary": [
+ {
+ "table": "`t1`",
+ "attached": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))"
+ }
+ ] /* attached_conditions_summary */
+ } /* attaching_conditions_to_tables */
+ },
+ {
+ "refine_plan": [
+ {
+ "table": "`t1`",
+ "pushed_index_condition": "(`t1`.`b` = 2)",
+ "table_condition_attached": "(`t1`.`a` in ('a','b'))",
+ "access_type": "range"
+ }
+ ] /* refine_plan */
+ }
+ ] /* steps */
+ } /* join_optimization */
+ },
+ {
+ "join_explain": {
+ "select#": 1,
+ "steps": [
+ ] /* steps */
+ } /* join_explain */
+ }
+ ] /* steps */
+} 0 0
+SET optimizer_trace="enabled=off";
+SET @@session.optimizer_switch=@optimizer_switch_saved;
+DROP TABLE t1;
HAVING alias1.col_int_nokey IN ( SELECT 2 FROM DUAL ) ;
field1 field2 field3 field4
DROP TABLE t1,t2,t3,t4;
+#
+# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
+# INDEX, EVEN THOUGH COST IS HIGHER
+#
+CREATE TABLE t1 (
+a TINYTEXT NOT NULL,
+b TINYINT(3) UNSIGNED NOT NULL,
+PRIMARY KEY (a(32),b),
+KEY b_idx(b)
+) ENGINE=INNODB;
+INSERT INTO t1 VALUES ('a',1),('a',2),('a',3),('b',1),('b',4),('c',1),('d',1),
+('c',4),('d',3),('e',2),('f',2);
+SET @optimizer_switch_saved=@@session.optimizer_switch;
+SET @@session.optimizer_switch=default;
+SET optimizer_trace="enabled=on";
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY,b_idx b_idx 35 NULL 2 Using index condition; Using where
+SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
+QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a IN ('a', 'b') AND b = 2 {
+ "steps": [
+ {
+ "join_preparation": {
+ "select#": 1,
+ "steps": [
+ {
+ "expanded_query": "/* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where ((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))"
+ }
+ ] /* steps */
+ } /* join_preparation */
+ },
+ {
+ "join_optimization": {
+ "select#": 1,
+ "steps": [
+ {
+ "condition_processing": {
+ "condition": "WHERE",
+ "original_condition": "((`t1`.`a` in ('a','b')) and (`t1`.`b` = 2))",
+ "steps": [
+ {
+ "transformation": "equality_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "constant_propagation",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ },
+ {
+ "transformation": "trivial_condition_removal",
+ "resulting_condition": "((`t1`.`a` in ('a','b')) and multiple equal(2, `t1`.`b`))"
+ }
+ ] /* steps */
+ } /* condition_processing */
+ },
+ {
+ "table_dependencies": [
+ {
+ "table": "`t1`",
+ "row_may_be_null": false,
+ "map_bit": 0,
+ "depends_on_map_bits": [
+ ] /* depends_on_map_bits */
+ }
+ ] /* table_dependencies */
+ },
+ {
+ "ref_optimizer_key_uses": [
+ {
+ "table": "`t1`",
+ "field": "b",
+ "equals": "2",
+ "null_rejecting": false
+ }
+ ] /* ref_optimizer_key_uses */
+ },
+ {
+ "rows_estimation": [
+ {
+ "table": "`t1`",
+ "range_analysis": {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": true,
+ "key_parts": [
+ "a",
+ "b"
+ ] /* key_parts */
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "PRIMARY",
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": true,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 2.41,
+ "chosen": true
+ },
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": false,
+ "cause": "cost"
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "PRIMARY",
+ "rows": 2,
+ "ranges": [
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2",
+ "unprintable_blob_value <= a <= unprintable_blob_value AND 2 <= b <= 2"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 2.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ }
+ ] /* rows_estimation */
+ },
+ {
+ "considered_execution_plans": [
+ {
+ "plan_prefix": [
+ ] /* plan_prefix */,
+ "table": "`t1`",
+ "best_access_path": {
+ "considered_access_paths": [
+ {
+ "access_type": "ref",
+ "index": "b_idx",
+ "rows": 2,
+ "cost": 2.4,
+ "chosen": true
+ },
+ {
+ "access_type": "range",
+ "rows": 2,
+ "cost": 2.81,
+ "chosen": false
+ }
+ ] /* considered_access_paths */
+ } /* best_access_path */,
+ "cost_for_plan": 2.4,
+ "rows_for_plan": 2,
+ "chosen": true
+ }
+ ] /* considered_execution_plans */
+ },
+ {
+ "attaching_conditions_to_tables": {
+ "original_condition": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))",
+ "attached_conditions_computation": [
+ {
+ "rerunning_range_optimizer_for_single_index": [
+ {
+ "table_scan": {
+ "rows": 11,
+ "cost": 5.3
+ } /* table_scan */,
+ "potential_range_indices": [
+ {
+ "index": "PRIMARY",
+ "usable": false,
+ "cause": "not_applicable"
+ },
+ {
+ "index": "b_idx",
+ "usable": true,
+ "key_parts": [
+ "b",
+ "a"
+ ] /* key_parts */
+ }
+ ] /* potential_range_indices */,
+ "setup_range_conditions": [
+ ] /* setup_range_conditions */,
+ "group_index_range": {
+ "chosen": false,
+ "cause": "not_group_by_or_distinct"
+ } /* group_index_range */,
+ "analyzing_range_alternatives": {
+ "range_scan_alternatives": [
+ {
+ "index": "b_idx",
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */,
+ "index_dives_for_eq_ranges": true,
+ "rowid_ordered": false,
+ "using_mrr": false,
+ "index_only": false,
+ "rows": 2,
+ "cost": 4.41,
+ "chosen": true
+ }
+ ] /* range_scan_alternatives */,
+ "analyzing_roworder_intersect": {
+ "usable": false,
+ "cause": "too_few_roworder_scans"
+ } /* analyzing_roworder_intersect */
+ } /* analyzing_range_alternatives */,
+ "chosen_range_access_summary": {
+ "range_access_plan": {
+ "type": "range_scan",
+ "index": "b_idx",
+ "rows": 2,
+ "ranges": [
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value",
+ "2 <= b <= 2 AND unprintable_blob_value <= a <= unprintable_blob_value"
+ ] /* ranges */
+ } /* range_access_plan */,
+ "rows_for_plan": 2,
+ "cost_for_plan": 4.41,
+ "chosen": true
+ } /* chosen_range_access_summary */
+ } /* range_analysis */
+ ] /* rerunning_range_optimizer_for_single_index */
+ },
+ {
+ "access_type_changed": {
+ "table": "`t1`",
+ "index": "b_idx",
+ "old_type": "ref",
+ "new_type": "range",
+ "cause": "uses_more_keyparts"
+ } /* access_type_changed */
+ }
+ ] /* attached_conditions_computation */,
+ "attached_conditions_summary": [
+ {
+ "table": "`t1`",
+ "attached": "((`t1`.`b` = 2) and (`t1`.`a` in ('a','b')))"
+ }
+ ] /* attached_conditions_summary */
+ } /* attaching_conditions_to_tables */
+ },
+ {
+ "refine_plan": [
+ {
+ "table": "`t1`",
+ "pushed_index_condition": "(`t1`.`b` = 2)",
+ "table_condition_attached": "(`t1`.`a` in ('a','b'))",
+ "access_type": "range"
+ }
+ ] /* refine_plan */
+ }
+ ] /* steps */
+ } /* join_optimization */
+ },
+ {
+ "join_explain": {
+ "select#": 1,
+ "steps": [
+ ] /* steps */
+ } /* join_explain */
+ }
+ ] /* steps */
+} 0 0
+SET optimizer_trace="enabled=off";
+SET @@session.optimizer_switch=@optimizer_switch_saved;
+DROP TABLE t1;
GRANT ALL PRIVILEGES ON test.* TO test_user_1 IDENTIFIED BY 'testpw';
GRANT ALL PRIVILEGES ON test.* TO test_user_2 IDENTIFIED BY 'testpw';
+--echo # FILE is needed to use the DATA DIRECTORY or INDEX DIRECTORY option
+GRANT FILE ON *.* TO test_user_2 IDENTIFIED BY 'testpw';
+
connect (session1, localhost, test_user_1,'testpw',test);
--sorted_result
SELECT * FROM t1 PARTITION (`p0-29`);
INSERT INTO t1 PARTITION (`p3000-299999`, subp14) VALUES (299999, '(p3000-299999-)subp14');
GRANT ALL PRIVILEGES ON test.* TO test_user_1 IDENTIFIED BY 'testpw';
GRANT ALL PRIVILEGES ON test.* TO test_user_2 IDENTIFIED BY 'testpw';
+# FILE is needed to use the DATA DIRECTORY or INDEX DIRECTORY option
+GRANT FILE ON *.* TO test_user_2 IDENTIFIED BY 'testpw';
connect session1, localhost, test_user_1,'testpw',test;
SELECT * FROM t1 PARTITION (`p0-29`);
a b
INSERT INTO t1 PARTITION (`p3000-299999`, subp14) VALUES (299999, '(p3000-299999-)subp14');
GRANT ALL PRIVILEGES ON test.* TO test_user_1 IDENTIFIED BY 'testpw';
GRANT ALL PRIVILEGES ON test.* TO test_user_2 IDENTIFIED BY 'testpw';
+# FILE is needed to use the DATA DIRECTORY or INDEX DIRECTORY option
+GRANT FILE ON *.* TO test_user_2 IDENTIFIED BY 'testpw';
connect session1, localhost, test_user_1,'testpw',test;
SELECT * FROM t1 PARTITION (`p0-29`);
a b
INSERT INTO t9 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA');
select * from t9;
a b c d e f g h i
-1 b1b1b1b1b1b1b1b1 Kyle 0000-00-00 00:00:00 0 NULL NULL
-2 b1b1b1b1b1b1b1b1 JOE 0000-00-00 00:00:00 0 NULL NULL
-3 b1b1b1b1b1b1b1b1 QA 0000-00-00 00:00:00 0 NULL NULL
+1 b1b1b1b1b1b1b1b1 Kyle CURRENT_TIMESTAMP 0 NULL NULL
+2 b1b1b1b1b1b1b1b1 JOE CURRENT_TIMESTAMP 0 NULL NULL
+3 b1b1b1b1b1b1b1b1 QA CURRENT_TIMESTAMP 0 NULL NULL
+include/assert.inc [The values of column 'd' should have non-zero timetsamp.]
DROP TABLE t9;
*** Create t10 on slave ***
STOP SLAVE;
INSERT INTO t9 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA');
select * from t9;
a b c d e f g h i
-1 b1b1b1b1b1b1b1b1 Kyle 0000-00-00 00:00:00 0 NULL NULL
-2 b1b1b1b1b1b1b1b1 JOE 0000-00-00 00:00:00 0 NULL NULL
-3 b1b1b1b1b1b1b1b1 QA 0000-00-00 00:00:00 0 NULL NULL
+1 b1b1b1b1b1b1b1b1 Kyle CURRENT_TIMESTAMP 0 NULL NULL
+2 b1b1b1b1b1b1b1b1 JOE CURRENT_TIMESTAMP 0 NULL NULL
+3 b1b1b1b1b1b1b1b1 QA CURRENT_TIMESTAMP 0 NULL NULL
+include/assert.inc [The values of column 'd' should have non-zero timetsamp.]
DROP TABLE t9;
*** Create t10 on slave ***
STOP SLAVE;
--- /dev/null
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+0
+SET GLOBAL innodb_stats_include_delete_marked=1;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+1
+SET SESSION innodb_stats_include_delete_marked=1;
+ERROR HY000: Variable 'innodb_stats_include_delete_marked' is a GLOBAL variable and should be set with SET GLOBAL
+SET GLOBAL innodb_stats_include_delete_marked=100;
+ERROR 42000: Variable 'innodb_stats_include_delete_marked' can't be set to the value of '100'
+SET GLOBAL innodb_stats_include_delete_marked=foo;
+ERROR 42000: Variable 'innodb_stats_include_delete_marked' can't be set to the value of 'foo'
+SET GLOBAL innodb_stats_include_delete_marked=OFF ;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+0
+SET GLOBAL innodb_stats_include_delete_marked=ON ;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+1
+SET GLOBAL innodb_stats_include_delete_marked=Default ;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+0
--- /dev/null
+ ###############################################################################
+# #
+# Variable Name: innodb_stats_include_delete_marked #
+# Scope: Global #
+# Access Type: Dynamic #
+# Data Type: numeric #
+# #
+# #
+# Creation Date: 2016-08-29 #
+# Author : Aditya #
+# #
+# #
+# Description: #
+# * Value check #
+# * Scope check #
+# #
+###############################################################################
+
+--source include/have_innodb.inc
+
+####################################################################
+# Display default value #
+####################################################################
+SELECT @@innodb_stats_include_delete_marked;
+
+SET GLOBAL innodb_stats_include_delete_marked=1;
+
+SELECT @@innodb_stats_include_delete_marked;
+
+# check error
+--error ER_GLOBAL_VARIABLE
+SET SESSION innodb_stats_include_delete_marked=1;
+
+# check error
+--error ER_WRONG_VALUE_FOR_VAR
+SET GLOBAL innodb_stats_include_delete_marked=100;
+
+# check error
+--error ER_WRONG_VALUE_FOR_VAR
+SET GLOBAL innodb_stats_include_delete_marked=foo;
+
+SET GLOBAL innodb_stats_include_delete_marked=OFF ;
+
+SELECT @@innodb_stats_include_delete_marked;
+
+SET GLOBAL innodb_stats_include_delete_marked=ON ;
+
+SELECT @@innodb_stats_include_delete_marked;
+
+# Check with default setting
+SET GLOBAL innodb_stats_include_delete_marked=Default ;
+
+SELECT @@innodb_stats_include_delete_marked;
--list_files $test_dir `#sql-*.frm`
+DROP TABLE t1;
+
+
+--echo #
+--echo # Bug#19635706
+--echo # Verify that it is possible to add a unique key to a not-NULL POINT
+--echo # column and that this key is promoted to primary key
+--echo #
+
+CREATE TABLE t1(a INT NOT NULL, b POINT NOT NULL) ENGINE=INNODB;
+SHOW CREATE TABLE t1;
+ALTER TABLE t1 ADD UNIQUE INDEX (b);
+
+--echo # Note that SHOW CREATE TABLE does not list b as a primary key,
+--echo # even though it was promoted. This appears to be the case also
+--echo # for other column types.
+SHOW CREATE TABLE t1;
+
+ALTER TABLE t1 ADD UNIQUE INDEX (a);
+SHOW CREATE TABLE t1;
+
+--echo # Verify that the expected indices have been created by Innodb
+SELECT T.NAME AS TABLE_NAME, I.NAME AS INDEX_NAME,
+ CASE I.TYPE
+ WHEN 0 THEN 'Secondary'
+ WHEN 1 THEN 'Clustered'
+ WHEN 2 THEN 'Unique'
+ WHEN 3 THEN 'Primary'
+ WHEN 32 THEN 'Full text'
+ WHEN 64 THEN 'Spatial'
+ ELSE 'Unknown'
+ END AS INDEX_TYPE,
+ F.NAME AS FIELD_NAME, F.POS AS FIELD_POS FROM
+ INFORMATION_SCHEMA.INNODB_SYS_TABLES AS T JOIN
+ INFORMATION_SCHEMA.INNODB_SYS_INDEXES AS I JOIN
+ INFORMATION_SCHEMA.INNODB_SYS_FIELDS AS F
+ ON I.INDEX_ID = F.INDEX_ID AND I.TABLE_ID = T.TABLE_ID
+ WHERE T.NAME = 'test/t1';
+
DROP TABLE t1;
# mysql.event intact checking end
#
-create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5;
+create event e_26 on schedule at '2038-01-19 03:14:07' disable do set @a = 5;
select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event;
drop event e_26;
--error ER_WRONG_VALUE
--echo #
CREATE TABLE t1(f1 INT);
-EVAL SELECT 0xE1C330 INTO OUTFILE 't1.dat';
+EVAL SELECT 0xE1BB30 INTO OUTFILE 't1.dat';
--disable_warnings
LOAD DATA INFILE 't1.dat' IGNORE INTO TABLE t1 CHARACTER SET utf8;
--enable_warnings
--remove_file $MYSQLTEST_VARDIR/mysql
DROP TABLE t1;
---echo
---echo #
---echo # Bug#23080148 - Backport of Bug#20683959.
---echo # Bug#20683959 LOAD DATA INFILE IGNORES A SPECIFIC ROW SILENTLY
---echo # UNDER DB CHARSET IS UTF8.
---echo #
-
-CREATE DATABASE d1 CHARSET latin1;
-USE d1;
-CREATE TABLE t1 (val TEXT);
-LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1;
-SELECT COUNT(*) FROM t1;
-SELECT HEX(val) FROM t1;
-
-CREATE DATABASE d2 CHARSET utf8;
-USE d2;
-CREATE TABLE t1 (val TEXT);
---error ER_INVALID_CHARACTER_STRING
-LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1;
-
-DROP TABLE d1.t1, d2.t1;
-DROP DATABASE d1;
-DROP DATABASE d2;
--echo #### End of test ####
+
+--echo #
+--echo # Bug #24557925: MYSQL_CONFIG_EDITOR CAN MAKE SERVER UNBOOTABLE
+--echo #
+
+
+--exec $MYSQL_CONFIG_EDITOR set --login-path=mysqld --host=test_user5
+
+--echo # Restarting the server. Should work
+--source include/restart_mysqld.inc
+
+--echo # Cleanup
+--exec $MYSQL_CONFIG_EDITOR remove --login-path=mysqld
+--remove_file $MYSQL_TEST_LOGIN_FILE
+
+--echo # End of 5.6 tests
-- echo # test.t1 have partitions in mysqltest2-directory!
-- echo # user root:
CREATE USER mysqltest_1@localhost;
+-- echo # Need FILE permission to use external datadir or indexdir.
+ GRANT FILE ON *.* TO mysqltest_1@localhost;
CREATE DATABASE mysqltest2;
USE mysqltest2;
CREATE TABLE t1 (a INT) ENGINE = MyISAM;
fun:my_b_flush_io_cache
fun:_my_b_write
fun:_Z*10write_keysP10Sort_paramP13Filesort_infojP11st_io_cacheS4_
- fun:_Z*13find_all_keysP10Sort_paramP10SQL_SELECTP13Filesort_infoP11st_io_cacheS6_P13Bounded_queueIhhEPy
+ ...
fun:_Z8filesortP3THDP5TABLEP8FilesortbPyS5_
}
-# Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES})
TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY}
- ${LIBNSL} ${LIBM} ${LIBRT})
+ ${LIBNSL} ${LIBM} ${LIBRT} ${LIBEXECINFO})
DTRACE_INSTRUMENT(mysys)
# Need explicit pthread for gcc -fsanitize=address
/* QQ: TODO multi-pinbox */
-/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
as necessary, old are pushed in the stack for reuse. ABA is solved by
versioning a pointer - because we use an array, a pointer to pins is 16 bit,
upper 16 bits are used for a version.
-
- It is assumed that pins belong to a THD and are not transferable
- between THD's (LF_PINS::stack_ends_here being a primary reason
- for this limitation).
*/
-#include <my_global.h>
-#include <my_sys.h>
-#include <lf.h>
+
+#include "lf.h"
+#include "mysys_priv.h" /* key_memory_lf_node */
#define LF_PINBOX_MAX_PINS 65536
DESCRIPTION
get a new LF_PINS structure from a stack of unused pins,
or allocate a new one out of dynarray.
-
- NOTE
- It is assumed that pins belong to a thread and are not transferable
- between threads.
*/
LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox)
{
- struct st_my_thread_var *var;
uint32 pins, next, top_ver;
LF_PINS *el;
/*
pinstack_top_ver is 32 bits; 16 low bits are the index in the
array, to the first element of the list. 16 high bits are a version
(every time the 16 low bits are updated, the 16 high bits are
- incremented). Versioniong prevents the ABA problem.
+ incremented). Versioning prevents the ABA problem.
*/
top_ver= pinbox->pinstack_top_ver;
do
el->link= pins;
el->purgatory_count= 0;
el->pinbox= pinbox;
- var= my_thread_var;
- /*
- Threads that do not call my_thread_init() should still be
- able to use the LF_HASH.
- */
- el->stack_ends_here= (var ? & var->stack_ends_here : NULL);
return el;
}
} while (!my_atomic_cas32((int32 volatile*) &pinbox->pinstack_top_ver,
(int32*) &top_ver,
top_ver-pins->link+nr+LF_PINBOX_MAX_PINS));
- return;
}
-static int ptr_cmp(void **a, void **b)
+/*
+ Get the next pointer in the purgatory list.
+ Note that next_node is not used to avoid the extra volatile.
+*/
+#define pnext_node(P, X) (*((void **)(((char *)(X)) + (P)->free_ptr_offset)))
+
+static inline void add_to_purgatory(LF_PINS *pins, void *addr)
{
- return *a < *b ? -1 : *a == *b ? 0 : 1;
+ pnext_node(pins->pinbox, addr)= pins->purgatory;
+ pins->purgatory= addr;
+ pins->purgatory_count++;
}
-#define add_to_purgatory(PINS, ADDR) \
- do \
- { \
- *(void **)((char *)(ADDR)+(PINS)->pinbox->free_ptr_offset)= \
- (PINS)->purgatory; \
- (PINS)->purgatory= (ADDR); \
- (PINS)->purgatory_count++; \
- } while (0)
/*
Free an object allocated via pinbox allocator
void _lf_pinbox_free(LF_PINS *pins, void *addr)
{
add_to_purgatory(pins, addr);
- if (pins->purgatory_count % LF_PURGATORY_SIZE)
+ if (pins->purgatory_count % LF_PURGATORY_SIZE == 0)
_lf_pinbox_real_free(pins);
}
-struct st_harvester {
- void **granary;
- int npins;
+struct st_match_and_save_arg {
+ LF_PINS *pins;
+ LF_PINBOX *pinbox;
+ void *old_purgatory;
};
/*
- callback for _lf_dynarray_iterate:
- scan all pins of all threads and accumulate all pins
+ Callback for lf_dynarray_iterate:
+ Scan all pins of all threads, for each active (non-null) pin,
+ scan the current thread's purgatory. If present there, move it
+ to a new purgatory. At the end, the old purgatory will contain
+ pointers not pinned by any thread.
*/
-static int harvest_pins(LF_PINS *el, struct st_harvester *hv)
+static int match_and_save(LF_PINS *el, struct st_match_and_save_arg *arg)
{
int i;
- LF_PINS *el_end= el + MY_MIN(hv->npins, LF_DYNARRAY_LEVEL_LENGTH);
+ LF_PINS *el_end= el + LF_DYNARRAY_LEVEL_LENGTH;
for (; el < el_end; el++)
{
for (i= 0; i < LF_PINBOX_PINS; i++)
{
void *p= el->pin[i];
if (p)
- *hv->granary++= p;
+ {
+ void *cur= arg->old_purgatory;
+ void **list_prev= &arg->old_purgatory;
+ while (cur)
+ {
+ void *next= pnext_node(arg->pinbox, cur);
+
+ if (p == cur)
+ {
+ /* pinned - keeping */
+ add_to_purgatory(arg->pins, cur);
+ /* unlink from old purgatory */
+ *list_prev= next;
+ }
+ else
+ list_prev= (void **)((char *)cur+arg->pinbox->free_ptr_offset);
+ cur= next;
+ }
+ if (!arg->old_purgatory)
+ return 1;
+ }
}
}
- /*
- hv->npins may become negative below, but it means that
- we're on the last dynarray page and harvest_pins() won't be
- called again. We don't bother to make hv->npins() correct
- (that is 0) in this case.
- */
- hv->npins-= LF_DYNARRAY_LEVEL_LENGTH;
return 0;
}
-/*
- callback for _lf_dynarray_iterate:
- scan all pins of all threads and see if addr is present there
-*/
-static int match_pins(LF_PINS *el, void *addr)
-{
- int i;
- LF_PINS *el_end= el+LF_DYNARRAY_LEVEL_LENGTH;
- for (; el < el_end; el++)
- for (i= 0; i < LF_PINBOX_PINS; i++)
- if (el->pin[i] == addr)
- return 1;
- return 0;
-}
-
-#if STACK_DIRECTION < 0
-#define available_stack_size(CUR,END) (long) ((char*)(CUR) - (char*)(END))
-#else
-#define available_stack_size(CUR,END) (long) ((char*)(END) - (char*)(CUR))
-#endif
-
-#define next_node(P, X) (*((uchar * volatile *)(((uchar *)(X)) + (P)->free_ptr_offset)))
-#define anext_node(X) next_node(&allocator->pinbox, (X))
-
/*
Scan the purgatory and free everything that can be freed
*/
static void _lf_pinbox_real_free(LF_PINS *pins)
{
- int npins;
- void *list;
- void **addr= NULL;
- void *first= NULL, *last= NULL;
LF_PINBOX *pinbox= pins->pinbox;
- npins= pinbox->pins_in_array+1;
+ /* Store info about current purgatory. */
+ struct st_match_and_save_arg arg = {pins, pinbox, pins->purgatory};
+ /* Reset purgatory. */
+ pins->purgatory= NULL;
+ pins->purgatory_count= 0;
-#ifdef HAVE_ALLOCA
- if (pins->stack_ends_here != NULL)
- {
- int alloca_size= sizeof(void *)*LF_PINBOX_PINS*npins;
- /* create a sorted list of pinned addresses, to speed up searches */
- if (available_stack_size(&pinbox, *pins->stack_ends_here) > alloca_size)
- {
- struct st_harvester hv;
- addr= (void **) alloca(alloca_size);
- hv.granary= addr;
- hv.npins= npins;
- /* scan the dynarray and accumulate all pinned addresses */
- _lf_dynarray_iterate(&pinbox->pinarray,
- (lf_dynarray_func)harvest_pins, &hv);
-
- npins= hv.granary-addr;
- /* and sort them */
- if (npins)
- qsort(addr, npins, sizeof(void *), (qsort_cmp)ptr_cmp);
- }
- }
-#endif
+ lf_dynarray_iterate(&pinbox->pinarray,
+ (lf_dynarray_func)match_and_save, &arg);
- list= pins->purgatory;
- pins->purgatory= 0;
- pins->purgatory_count= 0;
- while (list)
+ if (arg.old_purgatory)
{
- void *cur= list;
- list= *(void **)((char *)cur+pinbox->free_ptr_offset);
- if (npins)
- {
- if (addr) /* use binary search */
- {
- void **a, **b, **c;
- for (a= addr, b= addr+npins-1, c= a+(b-a)/2; (b-a) > 1; c= a+(b-a)/2)
- if (cur == *c)
- a= b= c;
- else if (cur > *c)
- a= c;
- else
- b= c;
- if (cur == *a || cur == *b)
- goto found;
- }
- else /* no alloca - no cookie. linear search here */
- {
- if (_lf_dynarray_iterate(&pinbox->pinarray,
- (lf_dynarray_func)match_pins, cur))
- goto found;
- }
- }
- /* not pinned - freeing */
- if (last)
- last= next_node(pinbox, last)= (uchar *)cur;
- else
- first= last= (uchar *)cur;
- continue;
-found:
- /* pinned - keeping */
- add_to_purgatory(pins, cur);
+ /* Some objects in the old purgatory were not pinned, free them. */
+ void *last= arg.old_purgatory;
+ while (pnext_node(pinbox, last))
+ last= pnext_node(pinbox, last);
+ pinbox->free_func(arg.old_purgatory, last, pinbox->free_func_arg);
}
- if (last)
- pinbox->free_func(first, last, pinbox->free_func_arg);
}
+#define next_node(P, X) (*((uchar * volatile *)(((uchar *)(X)) + (P)->free_ptr_offset)))
+#define anext_node(X) next_node(&allocator->pinbox, (X))
+
/* lock-free memory allocator for fixed-size objects */
LF_REQUIRE_PINS(1)
#include <m_string.h>
#include <my_dir.h> /* Structs used by my_dir,includes sys/types */
#include "mysys_err.h"
-#if defined(HAVE_DIRENT_H)
+#if !defined(_WIN32)
# include <dirent.h>
-# define NAMLEN(dirent) strlen((dirent)->d_name)
-#else
-# define dirent direct
-# define NAMLEN(dirent) (dirent)->d_namlen
-# if defined(HAVE_SYS_NDIR_H)
-# include <sys/ndir.h>
-# endif
-# if defined(HAVE_SYS_DIR_H)
-# include <sys/dir.h>
-# endif
-# if defined(HAVE_NDIR_H)
-# include <ndir.h>
# endif
-# if defined(_WIN32)
-# ifdef __BORLANDC__
-# include <dir.h>
-# endif
-# endif
-#endif
-#if defined(HAVE_READDIR_R)
-#define READDIR(A,B,C) ((errno=readdir_r(A,B,&C)) != 0 || !C)
-#else
-#define READDIR(A,B,C) (!(C=readdir(A)))
-#endif
/*
We are assuming that directory we are reading is either has less than
#if !defined(_WIN32)
+static char* directory_file_name(char *dst, const char *src);
+
MY_DIR *my_dir(const char *path, myf MyFlags)
{
char *buffer;
DYNAMIC_ARRAY *dir_entries_storage;
MEM_ROOT *names_storage;
DIR *dirp;
- struct dirent *dp;
char tmp_path[FN_REFLEN + 2], *tmp_file;
- char dirent_tmp[sizeof(struct dirent)+_POSIX_PATH_MAX+1];
+ const struct dirent *dp;
DBUG_ENTER("my_dir");
DBUG_PRINT("my",("path: '%s' MyFlags: %d",path,MyFlags));
-#if !defined(HAVE_READDIR_R)
- mysql_mutex_lock(&THR_LOCK_open);
-#endif
-
dirp = opendir(directory_file_name(tmp_path,(char *) path));
#if defined(__amiga__)
if ((dirp->dd_fd) < 0) /* Directory doesn't exists */
tmp_file=strend(tmp_path);
- dp= (struct dirent*) dirent_tmp;
-
- while (!(READDIR(dirp,(struct dirent*) dirent_tmp,dp)))
+ for (dp= readdir(dirp) ; dp; dp= readdir(dirp))
{
if (!(finfo.name= strdup_root(names_storage, dp->d_name)))
goto error;
}
(void) closedir(dirp);
-#if !defined(HAVE_READDIR_R)
- mysql_mutex_unlock(&THR_LOCK_open);
-#endif
+
result->dir_entry= (FILEINFO *)dir_entries_storage->buffer;
result->number_off_files= dir_entries_storage->elements;
DBUG_RETURN(result);
error:
-#if !defined(HAVE_READDIR_R)
- mysql_mutex_unlock(&THR_LOCK_open);
-#endif
my_errno=errno;
if (dirp)
(void) closedir(dirp);
* Returns pointer to dst;
*/
-char * directory_file_name (char * dst, const char *src)
+static char* directory_file_name(char *dst, const char *src)
{
/* Process as Unix format: just remove test the final slash. */
char *end;
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
mysql_mutex_init(key_my_thread_var_mutex, &tmp->mutex, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_my_thread_var_suspend, &tmp->suspend, NULL);
- tmp->stack_ends_here= (char*)&tmp +
- STACK_DIRECTION * (long)my_thread_stack_size;
-
mysql_mutex_lock(&THR_LOCK_threads);
tmp->id= ++thread_id;
++THR_thread_count;
#include <execinfo.h>
#endif
+#ifdef __linux__
+/* __bss_start doesn't seem to work on FreeBSD and doesn't exist on OSX/Solaris. */
#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)
-
static char *heap_start;
-
-#ifdef HAVE_BSS_START
extern char *__bss_start;
-#endif
+#else
+#define PTR_SANE(p) (p)
+#endif /* __linux */
void my_init_stacktrace()
{
-#ifdef HAVE_BSS_START
+#ifdef __linux__
heap_start = (char*) &__bss_start;
-#endif
+#endif /* __linux__ */
}
#ifdef __linux__
void my_safe_print_str(const char* val, int max_len)
{
+#ifdef __linux__
+/* Only needed by the linux version of PTR_SANE */
char *heap_end;
-#ifdef __linux__
if (!safe_print_str(val, max_len))
return;
-#endif
heap_end= (char*) sbrk(0);
+#endif
if (!PTR_SANE(val))
{
return my_load_defaults(conf_file, groups, argc, argv, &default_directories);
}
+/** A global to turn off or on reading the mylogin file. On by default */
+my_bool my_defaults_read_login_file= TRUE;
/*
Read options from configurations files
DBUG_RETURN(error);
}
- /* Read options from login group. */
- if (my_default_get_login_file(my_login_file, sizeof(my_login_file)) &&
- (error= my_search_option_files(my_login_file,argc, argv, &args_used,
+ if (my_defaults_read_login_file)
+ {
+ /* Read options from login group. */
+ if (my_default_get_login_file(my_login_file, sizeof(my_login_file)) &&
+ (error= my_search_option_files(my_login_file, argc, argv, &args_used,
handle_default_option, (void *) &ctx,
dirs, true, found_no_defaults)))
- {
- free_root(&alloc,MYF(0));
- DBUG_RETURN(error);
+ {
+ free_root(&alloc, MYF(0));
+ DBUG_RETURN(error);
+ }
}
-
/*
Here error contains <> 0 only if we have a fully specified conf_file
or a forced default file
debian/extra/semisync_slave-plugin
debian/extra/test_udf_services-plugin
debian/extra/validate_password-plugin
+debian/extra/connection_control-plugin
debian/extra/semisync_slave-plugin
debian/extra/test_udf_services-plugin
debian/extra/validate_password-plugin
+debian/extra/connection_control-plugin
debian/extra/semisync_slave-plugin
debian/extra/test_udf_services-plugin
debian/extra/validate_password-plugin
+debian/extra/connection_control-plugin
debian/extra/semisync_slave-plugin
debian/extra/test_udf_services-plugin
debian/extra/validate_password-plugin
+debian/extra/connection_control-plugin
%dir %{_libdir}/mysql/plugin
%attr(755, root, root) %{_libdir}/mysql/plugin/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/mypluglib.so
%dir %attr(750, mysql, mysql) /var/lib/mysql-files
%changelog
+* Tue Sep 13 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.34-1
+- Add connection_control.so to server subpackage
+
* Mon Mar 14 2016 Georgi Kodinov <georgi.kodinov@oracle.com> - 5.6.31-1
- Add test_udf_services.so plugin
datadir=$(get_option mysqld datadir "/var/lib/mysql")
# Restore log, dir, perms and SELinux contexts
- [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
+ if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
+ fi
log=/var/log/mysqld.log
[ -e $log ] || touch $log
chmod 0640 $log
%dir %{_libdir}/mysql/plugin
%attr(755, root, root) %{_libdir}/mysql/plugin/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/mypluglib.so
%dir %{_libdir}/mysql/plugin/debug
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/debug/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/mypluglib.so
%attr(755, root, root) %{_libdir}/mysql/libmysqld.so
%changelog
+* Mon Oct 31 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.35-1
+- Add connection_control.so to server subpackage
+
* Mon Sep 26 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.34-1
- Include mysql-files directory
#
# Wrapper script for mysql_config to support multilib
#
-# Only works on OEL6/RHEL6 and similar
#
-# This command respects setarch
+# This command respects setarch, works on OL6/RHEL6 and later
bits=$(rpm --eval %__isa_bits)
case $bits in
- 32|64) status=known ;;
- *) status=unknown ;;
+ 32|64) ;;
+ *) bits=unknown ;;
esac
-if [ "$status" = "unknown" ] ; then
- echo "$0: error: command 'rpm --eval %__isa_bits' returned unknown value: $bits"
- exit 1
+# Try mapping by uname if rpm command failed
+if [ "$bits" = "unknown" ] ; then
+ arch=$(uname -m)
+ case $arch in
+ x86_64|ppc64|ppc64le|aarch64|s390x|sparc64) bits=64 ;;
+ i386|i486|i586|i686|pentium3|pentium4|athlon|ppc|s390|sparc) bits=32 ;;
+ *) bits=unknown ;;
+ esac
fi
+if [ "$bits" == "unknown" ] ; then
+ echo "$0: error: failed to determine isa bits on your arch."
+ exit 1
+fi
if [ -x /usr/bin/mysql_config-$bits ] ; then
/usr/bin/mysql_config-$bits "$@"
echo "$0: error: needed binary: /usr/bin/mysql_config-$bits is missing. Please check your MySQL installation."
exit 1
fi
-
ExecStartPre=/usr/bin/mysql-systemd-start pre
# Start main service
-ExecStart=/usr/bin/mysqld_safe
+ExecStart=/usr/bin/mysqld_safe --basedir=/usr
# Don't signal startup success before a ping works
ExecStartPost=/usr/bin/mysql-systemd-start post
datadir=$(get_option mysqld datadir "/var/lib/mysql")
# Restore log, dir, perms and SELinux contexts
- [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
+ if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
+ fi
log=/var/log/mysqld.log
[ -e $log ] || touch $log
chmod 0640 $log
ret=0
else
# prepare for start
- touch "$errlogfile"
- chown mysql:mysql "$errlogfile"
- chmod 0640 "$errlogfile"
+ if [ ! -e "$errlogfile" -a ! -h "$errlogfile" -a "x$(dirname "$errlogfile")" = "x/var/log" ]; then
+ install /dev/null -m0640 -omysql -gmysql "$errlogfile"
+ fi
[ -x /sbin/restorecon ] && /sbin/restorecon "$errlogfile"
if [ ! -d "$datadir/mysql" ] ; then
# First, make sure $datadir is there with correct permissions
- if [ ! -e "$datadir" -a ! -h "$datadir" ]
- then
- mkdir -p "$datadir" || exit 1
+ if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ install -d -m0755 -omysql -gmysql "$datadir" || exit 1
+ fi
+ if [ ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ chown mysql:mysql "$datadir"
+ chmod 0755 "$datadir"
fi
- chown mysql:mysql "$datadir"
- chmod 0755 "$datadir"
if [ -x /sbin/restorecon ]; then
/sbin/restorecon "$datadir"
for dir in /var/lib/mysql-files ; do
# Now create the database
action $"Initializing MySQL database: " /usr/bin/mysql_install_db --rpm --datadir="$datadir" --user=mysql
ret=$?
- chown -R mysql:mysql "$datadir"
if [ $ret -ne 0 ] ; then
return $ret
fi
fi
- chown mysql:mysql "$datadir"
- chmod 0755 "$datadir"
+ if [ ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ chown mysql:mysql "$datadir"
+ chmod 0755 "$datadir"
+ fi
# Pass all the options determined above, to ensure consistent behavior.
# In many cases mysqld_safe would arrive at the same conclusions anyway
# but we need to be sure. (An exception is that we don't force the
%dir %{_libdir}/mysql/plugin
%attr(755, root, root) %{_libdir}/mysql/plugin/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/mypluglib.so
%dir %{_libdir}/mysql/plugin/debug
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/debug/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/mypluglib.so
%endif
%changelog
+* Mon Oct 31 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.35-1
+- Add connection_control.so to server subpackage
+
* Mon Sep 26 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.34-1
- Include mysql-files directory
#
# This command respects setarch, works on OL6/RHEL6 and later
-isa_bits=$(rpm --eval %__isa_bits)
+bits=$(rpm --eval %__isa_bits)
case $bits in
- 32|64) bits=$isa_bits ;;
+ 32|64) ;;
*) bits=unknown ;;
esac
if [ "$bits" = "unknown" ] ; then
arch=$(uname -m)
case $arch in
- x86_64|ppc64) bits=64 ;;
- i386|i486|i586|i686|pentium3|pentium4|athlon|ppc) bits=32 ;;
+ x86_64|ppc64|ppc64le|aarch64|s390x|sparc64) bits=64 ;;
+ i386|i486|i586|i686|pentium3|pentium4|athlon|ppc|s390|sparc) bits=32 ;;
*) bits=unknown ;;
esac
fi
echo "$0: error: needed binary: /usr/bin/mysql_config-$bits is missing. Please check your MySQL installation."
exit 1
fi
-
ExecStartPre=/usr/bin/mysql-systemd-start pre
# Start main service
-ExecStart=/usr/bin/mysqld_safe
+ExecStart=/usr/bin/mysqld_safe --basedir=/usr
# Don't signal startup success before a ping works
ExecStartPost=/usr/bin/mysql-systemd-start post
datadir=$(/usr/bin/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p' | tail -n 1)
# Restore log, dir, perms and SELinux contexts
- [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
+ if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
+ fi
log=/var/log/mysql/mysqld.log
[ -e $log ] || touch $log
chmod 0640 $log
datadir=$(get_option mysqld datadir "/var/lib/mysql")
socket=$(get_option mysqld socket "$datadir/mysql.sock")
pidfile=$(get_option mysqld_safe pid-file "/var/run/mysql/mysqld.pid")
-logfile=$(get_option mysqld_safe log-error "/var/log/mysql/mysqld.log")
install_db () {
# Note: something different than datadir=/var/lib/mysql requires
logfile=$(get_option mysqld_safe log-error "/var/log/mysql/mysqld.log")
# Restore log, dir, perms and SELinux contexts
- [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || return 1
+ if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then
+ install -d -m 0755 -omysql -gmysql "$datadir" || return 1
+ fi
- [ -e $logfile ] || touch $logfile || return 1
- chmod 0640 $logfile
- chown mysql:mysql $logfile || return 1
+ if [ ! -e "$logfile" -a ! -h "$logfile" -a "x$(dirname "$logfile")" = "x/var/log/mysql" ]; then
+ install /dev/null -omysql -gmysql "$logfile" || return 1
+ fi
if [ -x /usr/sbin/restorecon ]; then
/usr/sbin/restorecon "$datadir"
- /usr/sbin/restorecon $logfile
+ /usr/sbin/restorecon "$logfile"
fi
# If special mysql dir is in place, skip db install
%dir %{_libdir}/mysql/plugin
%attr(755, root, root) %{_libdir}/mysql/plugin/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/mypluglib.so
%dir %{_libdir}/mysql/plugin/debug
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/adt_null.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/auth_socket.so
+%attr(755, root, root) %{_libdir}/mysql/plugin/debug/connection_control.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/innodb_engine.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/libmemcached.so
%attr(755, root, root) %{_libdir}/mysql/plugin/debug/mypluglib.so
%attr(755, root, root) %{_libdir}/mysql/libmysqld.so
%changelog
+* Mon Oct 31 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.35-1
+- Add connection_control.so to server subpackage
+
* Mon Sep 26 2016 Balasubramanian Kandasamy <balasubramanian.kandasamy@oracle.com> - 5.6.34-1
- Include mysql-files directory
ExecStartPre=/usr/bin/mysql-systemd-start pre
# Start main service
-ExecStart=/usr/bin/mysqld_safe
+ExecStart=/usr/bin/mysqld_safe --basedir=/usr
# Don't signal startup success before a ping works
ExecStartPost=/usr/bin/mysql-systemd-start post
--- /dev/null
+# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+MYSQL_ADD_PLUGIN(connection_control
+ connection_control_coordinator.cc
+ connection_control.cc
+ security_context_wrapper.cc
+ connection_delay.cc
+ MODULE_ONLY)
+
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <mysql/plugin_audit.h> /* mysql_event_connection */
+#include "mysql_version.h"
+#include <my_global.h>
+#include <my_pthread.h>
+#include <my_atomic.h>
+
+#include "connection_control.h"
+#include "connection_delay_api.h" /* connection_delay apis */
+#include "connection_control_coordinator.h" /* g_connection_event_coordinator */
+
+namespace connection_control
+{
+ class Connection_control_error_handler : public Error_handler
+ {
+ public:
+ Connection_control_error_handler(MYSQL_PLUGIN plugin_info)
+ : m_plugin_info(plugin_info)
+ {}
+
+ void handle_error(const char * error_message)
+ {
+ my_plugin_log_message(&m_plugin_info,
+ MY_ERROR_LEVEL,
+ error_message);
+ }
+ private:
+ MYSQL_PLUGIN m_plugin_info;
+ };
+}
+
+using connection_control::Connection_event_coordinator;
+using connection_control::Connection_event_coordinator_services;
+using connection_control::Connection_control_statistics;
+using connection_control::Connection_control_variables;
+using connection_control::Error_handler;
+using connection_control::Connection_control_error_handler;
+
+typedef unsigned int mysql_event_class_t;
+
+Connection_control_statistics g_statistics;
+Connection_control_variables g_variables;
+
+Connection_event_coordinator *g_connection_event_coordinator= 0;
+MYSQL_PLUGIN connection_control_plugin_info= 0;
+
+
+/**
+ event_notify() implementation for connection_control
+
+ For connection event, notify Connection_event_coordinator
+ which in turn will notify subscribers.
+
+ @param thd [in] Handle to THD
+ @param event_class [in] Event class.
+ We are interested in MYSQL_AUDIT_CONNECTION_CLASS.
+ @param event [in] mysql_event_connection handle
+*/
+
+static void
+connection_control_notify(MYSQL_THD thd,
+ mysql_event_class_t event_class,
+ const void *event)
+{
+ DBUG_ENTER("connection_control_notify");
+ try
+ {
+ if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
+ {
+ const struct mysql_event_connection *connection_event=
+ (const struct mysql_event_connection *) event;
+ Connection_control_error_handler error_handler(connection_control_plugin_info);
+ /** Notify event coordinator */
+ g_connection_event_coordinator->notify_event(thd, &error_handler,
+ connection_event);
+ }
+ }
+ catch (...)
+ {
+ /* Happily ignore any bad behavior */
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Plugin initialization function
+
+ @param plugin_info [in] MYSQL_PLUGIN information
+
+ @returns initialization status
+ @retval 0 Success
+ @retval 1 Failure
+*/
+
+static int
+connection_control_init(MYSQL_PLUGIN plugin_info)
+{
+ connection_control_plugin_info= plugin_info;
+ Connection_control_error_handler error_handler(connection_control_plugin_info);
+ g_connection_event_coordinator= new Connection_event_coordinator();
+ if (!g_connection_event_coordinator)
+ {
+ error_handler.handle_error("Failed to initialize Connection_event_coordinator");
+ return 1;
+ }
+
+ if (init_connection_delay_event((Connection_event_coordinator_services *)
+ g_connection_event_coordinator,
+ &error_handler))
+ {
+ delete g_connection_event_coordinator;
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ Plugin deinitialization
+
+ @param arg Unused
+
+ @returns success
+*/
+
+static int
+connection_control_deinit(void *arg MY_ATTRIBUTE((unused)))
+{
+ delete g_connection_event_coordinator;
+ g_connection_event_coordinator= 0;
+ connection_control::deinit_connection_delay_event();
+ connection_control_plugin_info= 0;
+ return 0;
+}
+
+
+/** Connection_control plugin descriptor */
+static struct st_mysql_audit connection_control_descriptor=
+{
+ MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */\r
+ NULL, /* release_thd() */\r
+ connection_control_notify, /* event_notify() */\r
+ {(ulong)MYSQL_AUDIT_CONNECTION_CLASSMASK} /* class mask */
+};
+
+
+/**
+ check() function for connection_control_failed_connections_threshold
+
+ Check whether new value is within valid bounds or not.
+
+ @param thd Not used.
+ @param var Not used.
+ @param save Not used.
+ @param value New value for the option.
+
+ @returns whether new value is within valid bounds or not.
+ @retval 0 Value is ok
+ @retval 1 Value is not within valid bounds
+*/
+
+static int
+check_failed_connections_threshold(MYSQL_THD thd MY_ATTRIBUTE((unused)),
+ struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
+ void *save MY_ATTRIBUTE((unused)),
+ struct st_mysql_value *value)
+{
+ longlong new_value;
+ if (value->val_int(value, &new_value))
+ return 1; /* NULL value */
+
+ if (new_value >= connection_control::MIN_THRESHOLD &&
+ new_value <= connection_control::MAX_THRESHOLD)
+ {
+ *(reinterpret_cast<longlong *>(save))= new_value;
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ update() function for connection_control_failed_connections_threshold
+
+ Updates g_connection_event_coordinator with new value.
+ Also notifies observers about the update.
+
+ @param thd Not used.
+ @param var Not used.
+ @param var_ptr Variable information
+ @param save New value for connection_control_failed_connections_threshold
+*/
+
+static void
+update_failed_connections_threshold(MYSQL_THD thd MY_ATTRIBUTE((unused)),
+ struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
+ void *var_ptr, const void *save)
+{
+ /*
+ This won't result in overflow because we have already checked that this is
+ within valid bounds.
+ */
+ longlong new_value= *(reinterpret_cast<const longlong *>(save));
+ g_variables.failed_connections_threshold= (int64)new_value;
+ Connection_control_error_handler error_handler(connection_control_plugin_info);
+ g_connection_event_coordinator->notify_sys_var(&error_handler,
+ OPT_FAILED_CONNECTIONS_THRESHOLD,
+ &new_value);
+ return;
+}
+
+
+/** Declaration of connection_control_failed_connections_threshold */
+static MYSQL_SYSVAR_LONGLONG(
+ failed_connections_threshold,
+ g_variables.failed_connections_threshold,
+ PLUGIN_VAR_RQCMDARG,
+ "Failed connection threshold to trigger delay. Default is 3.",
+ check_failed_connections_threshold,
+ update_failed_connections_threshold,
+ connection_control::DEFAULT_THRESHOLD,
+ connection_control::MIN_THRESHOLD,
+ connection_control::MAX_THRESHOLD,
+ 1
+);
+
+
+/**
+ check() function for connection_control_min_connection_delay
+
+ Check whether new value is within valid bounds or not.
+
+ @param thd Not used.
+ @param var Not used.
+ @param save Not used.
+ @param value New value for the option.
+
+ @returns whether new value is within valid bounds or not.
+ @retval 0 Value is ok
+ @retval 1 Value is not within valid bounds
+*/
+
+static int
+check_min_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
+ struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
+ void *save MY_ATTRIBUTE((unused)),
+ struct st_mysql_value *value)
+{
+ long long new_value;
+ int64 existing_value= g_variables.max_connection_delay;
+ if (value->val_int(value, &new_value))
+ return 1; /* NULL value */
+
+ if (new_value >= connection_control::MIN_DELAY &&
+ new_value <= connection_control::MAX_DELAY &&
+ new_value <= existing_value)
+ {
+ *(reinterpret_cast<longlong *>(save))= new_value;
+ return 0;
+ }
+ return 1;
+}
+
+
+/**
+ update() function for connection_control_min_connection_delay
+
+ Updates g_connection_event_coordinator with new value.
+ Also notifies observers about the update.
+
+ @param thd Not used.
+ @param var Not used.
+ @param var_ptr Variable information
+ @param save New value for connection_control_min_connection_delay
+*/
+
+static void
+update_min_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
+ struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
+ void *var_ptr, const void *save)
+{
+ longlong new_value= *(reinterpret_cast<const longlong *>(save));
+ g_variables.min_connection_delay= (int64)new_value;
+ Connection_control_error_handler error_handler(connection_control_plugin_info);
+ g_connection_event_coordinator->notify_sys_var(&error_handler,
+ OPT_MIN_CONNECTION_DELAY,
+ &new_value);
+ return;
+}
+
+
+/** Declaration of connection_control_max_connection_delay */
+static MYSQL_SYSVAR_LONGLONG(
+ min_connection_delay,
+ g_variables.min_connection_delay,
+ PLUGIN_VAR_RQCMDARG,
+ "Maximum delay to be introduced. Default is 1000.",
+ check_min_connection_delay,
+ update_min_connection_delay,
+ connection_control::DEFAULT_MIN_DELAY,
+ connection_control::MIN_DELAY,
+ connection_control::MAX_DELAY,
+ 1
+);
+
+
+/**
+ check() function for connection_control_max_connection_delay
+
+ Check whether new value is within valid bounds or not.
+
+ @param thd Not used.
+ @param var Not used.
+ @param save Not used.
+ @param value New value for the option.
+
+ @returns whether new value is within valid bounds or not.
+ @retval 0 Value is ok
+ @retval 1 Value is not within valid bounds
+*/
+
+static int
+check_max_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
+ struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
+ void *save MY_ATTRIBUTE((unused)),
+ struct st_mysql_value *value)
+{
+ long long new_value;
+ int64 existing_value= my_atomic_load64(&g_variables.min_connection_delay);
+ if (value->val_int(value, &new_value))
+ return 1; /* NULL value */
+
+ if (new_value >= connection_control::MIN_DELAY &&
+ new_value <= connection_control::MAX_DELAY &&
+ new_value >= existing_value)
+ {
+ *(reinterpret_cast<longlong *>(save))= new_value;
+ return 0;
+ }
+ return 1;
+}
+
+
+/**
+ update() function for connection_control_max_connection_delay
+
+ Updates g_connection_event_coordinator with new value.
+ Also notifies observers about the update.
+
+ @param thd Not used.
+ @param var Not used.
+ @param var_ptr Variable information
+ @param save New value for connection_control_max_connection_delay
+*/
+
+static void
+update_max_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
+ struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
+ void *var_ptr, const void *save)
+{
+ longlong new_value= *(reinterpret_cast<const longlong *>(save));
+ my_atomic_store64(&g_variables.max_connection_delay, (int64)new_value);
+ Connection_control_error_handler error_handler(connection_control_plugin_info);
+ g_connection_event_coordinator->notify_sys_var(&error_handler,
+ OPT_MAX_CONNECTION_DELAY,
+ &new_value);
+ return;
+}
+
+
+/** Declaration of connection_control_max_connection_delay */
+static MYSQL_SYSVAR_LONGLONG(
+ max_connection_delay,
+ g_variables.max_connection_delay,
+ PLUGIN_VAR_RQCMDARG,
+ "Maximum delay to be introduced. Default is 2147483647.",
+ check_max_connection_delay,
+ update_max_connection_delay,
+ connection_control::DEFAULT_MAX_DELAY,
+ connection_control::MIN_DELAY,
+ connection_control::MAX_DELAY,
+ 1
+);
+
+
+/** Array of system variables. Used in plugin declaration. */
+struct st_mysql_sys_var *
+connection_control_system_variables[OPT_LAST + 1]=
+{
+ MYSQL_SYSVAR(failed_connections_threshold),
+ MYSQL_SYSVAR(min_connection_delay),
+ MYSQL_SYSVAR(max_connection_delay),
+ NULL
+};
+
+
+/**
+ Function to display value for status variable : Connection_control_delay_generated
+
+ @param thd MYSQL_THD handle. Unused.
+ @param var Status variable structure
+ @param buff Value buffer.
+
+ @returns Always returns success.
+*/
+
+static int show_delay_generated(MYSQL_THD,
+ struct st_mysql_show_var *var,
+ char *buff)
+{
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ longlong *value= reinterpret_cast<longlong *>(buff);
+ int64 current_val= my_atomic_load64(&g_statistics.stats_array[STAT_CONNECTION_DELAY_TRIGGERED]);
+ *value= static_cast<longlong>(current_val);
+ return 0;
+}
+
+
+/** Array of status variables. Used in plugin declaration. */
+struct st_mysql_show_var
+connection_control_status_variables[STAT_LAST + 1]={
+ {
+ "Connection_control_delay_generated",
+ (char *)&show_delay_generated,
+ SHOW_FUNC
+ },
+ {0, 0, enum_mysql_show_type(0)}
+};
+
+
+mysql_declare_plugin(audit_log)
+{
+ MYSQL_AUDIT_PLUGIN, /* plugin type */
+ &connection_control_descriptor, /* type specific descriptor */
+ "CONNECTION_CONTROL", /* plugin name */
+ "Oracle Inc", /* author */
+ "Connection event processing", /* description */
+ PLUGIN_LICENSE_GPL , /* license */
+ connection_control_init, /* plugin initializer */
+ connection_control_deinit, /* plugin deinitializer */
+ 0x0100, /* version */
+ connection_control_status_variables, /* status variables */
+ connection_control_system_variables, /* system variables */
+ NULL, /* reserverd */
+ 0 /* flags */
+},
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &connection_control_failed_attempts_view,
+ "CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS",
+ "Oracle Inc",
+ "I_S table providing a view into failed attempts statistics",
+ PLUGIN_LICENSE_GPL,
+ connection_control_failed_attempts_view_init,
+ NULL,
+ 0x0100,
+ NULL,
+ NULL,
+ NULL,
+ 0
+}
+mysql_declare_plugin_end;
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_CONTROL_H
+#define CONNECTION_CONTROL_H
+
+#include <mysql/psi/mysql_thread.h> /* mysql_rwlock_t */
+
+#include "connection_control_data.h"
+
+namespace connection_control
+{
+
+ /** Helper class : Wrapper on READ lock */
+
+ class RD_lock
+ {
+ public:
+ explicit RD_lock(mysql_rwlock_t *lock) : m_lock(lock)
+ {
+ if (m_lock)
+ mysql_rwlock_rdlock(m_lock);
+ }
+ ~RD_lock()
+ {
+ if (m_lock)
+ mysql_rwlock_unlock(m_lock);
+ }
+ void lock()
+ {
+ mysql_rwlock_rdlock(m_lock);
+ }
+ void unlock()
+ {
+ mysql_rwlock_unlock(m_lock);
+ }
+ private:
+ mysql_rwlock_t *m_lock;
+
+ RD_lock(const RD_lock&); /* Not copyable. */
+ void operator=(const RD_lock&); /* Not assignable. */
+ };
+
+
+ /** Helper class : Wrapper on write lock */
+
+ class WR_lock
+ {
+ public:
+ explicit WR_lock(mysql_rwlock_t *lock) : m_lock(lock)
+ {
+ if (m_lock)
+ mysql_rwlock_wrlock(m_lock);
+ }
+ ~WR_lock()
+ {
+ if (m_lock)
+ mysql_rwlock_unlock(m_lock);
+ }
+ void lock()
+ {
+ mysql_rwlock_wrlock(m_lock);
+ }
+ void unlock()
+ {
+ mysql_rwlock_unlock(m_lock);
+ }
+ private:
+ mysql_rwlock_t *m_lock;
+
+ WR_lock(const WR_lock&); /* Not copyable. */
+ void operator=(const WR_lock&); /* Not assignable. */
+ };
+}
+#endif /* !CONNECTION_CONTROL_H */
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <my_global.h>
+#include <my_pthread.h>
+#include <my_atomic.h>
+
+#include "connection_control_coordinator.h"
+#include "connection_control.h"
+
+namespace connection_control
+{
+ /**
+ Reset Connection_event_coordinator information
+ */
+
+ void
+ Connection_event_coordinator::reset()
+ {
+ m_subscribers.clear();
+ for (uint i= (uint)STAT_CONNECTION_DELAY_TRIGGERED;
+ i < (uint)STAT_LAST; ++i)
+ m_status_vars_subscription[i]= 0;
+ }
+
+
+ /**
+ Register an event subscriber.
+
+ A subscriber can provide following preferences:
+ 1. Set of events for which subscriber is interested
+ 2. Set of variables for which subscriber would like to receive update
+ 3. Set of stats for which subscriber would like to send update
+
+ @param subscriber [in] Handle to Connection_event_observers
+ @param events [in] Event mask supplied by subscriber
+ @param sys_vars [in] opt_connection_control vector
+ @param status_vars [in] stats_connection_control vector
+
+ @returns subscription status
+ @retval false Subscription successful
+ @retval true Failure in subscription for given combination of prefernece.
+ Most probably, other subscriber has already subscribed for
+ status var update.
+ */
+
+ bool
+ Connection_event_coordinator::register_event_subscriber(
+ Connection_event_observer **subscriber,
+ std::vector<opt_connection_control> *sys_vars,
+ std::vector<stats_connection_control> *status_vars)
+ {
+ DBUG_ENTER("Connection_event_coordinator::register_event_subscriber");
+ bool error= false;
+ std::vector<opt_connection_control>::iterator sys_vars_it;
+ std::vector<stats_connection_control>::iterator status_vars_it;
+
+ DBUG_ASSERT(subscriber != 0);
+
+ if (status_vars)
+ {
+ for (status_vars_it= status_vars->begin();
+ status_vars_it != status_vars->end();
+ ++status_vars_it)
+ {
+ if (*status_vars_it >= STAT_LAST ||
+ m_status_vars_subscription[*status_vars_it] != 0)
+ {
+ /*
+ Either an invalid status variable is specified or
+ someone has already subscribed for status variable
+ */
+ error= true;
+ break;
+ }
+ }
+ }
+
+ if (!error && sys_vars)
+ {
+ for (sys_vars_it= sys_vars->begin();
+ sys_vars_it != sys_vars->end();
+ ++sys_vars_it)
+ {
+ if (*sys_vars_it >= OPT_LAST)
+ error= true;
+ break;
+ }
+ }
+
+ if (!error)
+ {
+ /*
+ Create Connection_event_subscriber object and
+ initialize it with required details.
+ */
+ Connection_event_subscriber subscriber_info;
+ subscriber_info.m_subscriber= *subscriber;
+
+ /* Reset the list first */
+ for (uint i= (uint)OPT_FAILED_CONNECTIONS_THRESHOLD;
+ i < (uint)OPT_LAST;
+ ++i)
+ subscriber_info.m_sys_vars[i]= false;
+
+ /* Now set the bits which are requested by subscriber */
+ for (sys_vars_it= sys_vars->begin();
+ sys_vars_it != sys_vars->end();
+ ++sys_vars_it)
+ subscriber_info.m_sys_vars[*sys_vars_it]= true;
+
+ /* Insert new entry in m_subscribers */
+ try
+ {
+ m_subscribers.push_back(subscriber_info);
+ }
+ catch (...)
+ {
+ /* Something went wrong. Mostly likely OOM. */
+ error= true;
+ }
+
+ /*
+ Update m_status_vars_subscription only if subscriber information
+ has been inserted in m_subscribers successfully.
+ */
+ if (!error)
+ {
+ for (status_vars_it= status_vars->begin();
+ status_vars_it != status_vars->end();
+ ++status_vars_it)
+ m_status_vars_subscription[*status_vars_it]= *subscriber;
+ }
+ }
+ DBUG_RETURN(error);
+ }
+
+
+ /**
+ Handle connection event.
+ When a notification from server is received, perform following:
+ 1. Iterate through list of subscribers
+ - If a subscriber has shown interest in received event,
+ call notify() for the subscriber
+ 2. Interate through list of status variables
+ - If subscriber has show interest in any status variable,
+ call notify_status_var() for the subscriber
+ - If subscriber suggests an action on status variable,
+ perform the action
+
+ Note : If we receive error from a subscriber, we log it and move on.
+
+ @param thd [in] THD handle
+ @param error_handler [in] Error handler class
+ @param connection_event [in] Event information
+ */
+
+ void
+ Connection_event_coordinator::notify_event(
+ MYSQL_THD thd,
+ Error_handler *error_handler,
+ const mysql_event_connection *connection_event)
+ {
+ DBUG_ENTER("Connection_event_coordinator::notify_event");
+ std::vector<Connection_event_subscriber>::iterator it= m_subscribers.begin();
+
+ while (it != m_subscribers.end())
+ {
+ Connection_event_subscriber event_subscriber= *it;
+ (void)event_subscriber.m_subscriber->notify_event(thd, this,
+ connection_event,
+ error_handler);
+
+ ++it;
+ }
+
+ DBUG_VOID_RETURN;
+ }
+
+
+ /**
+ Process change in sys_var value
+
+ Iterate through all subscribers
+ - If a subscriber has shown interest in getting notification for given
+ system variable, call notify_sys_var.
+
+ Note : If we receive error from a subscriber, we log it and move on.
+
+ @param error_hanlder [in] Error handler class
+ @param opt_connection_control Variable information
+ @param new_value [in] New value for variable
+ */
+
+ void
+ Connection_event_coordinator::notify_sys_var(Error_handler *error_handler,
+ opt_connection_control variable,
+ void *new_value)
+ {
+ DBUG_ENTER("Connection_event_coordinator::notify_sys_var");
+ std::vector<Connection_event_subscriber>::iterator it= m_subscribers.begin();
+
+ while (it != m_subscribers.end())
+ {
+ Connection_event_subscriber event_subscriber= *it;
+ if (event_subscriber.m_sys_vars[variable])
+ {
+ (void) event_subscriber.m_subscriber->notify_sys_var(this,
+ variable,
+ new_value,
+ error_handler);
+ }
+ ++it;
+ }
+
+ DBUG_VOID_RETURN;
+ }
+
+
+ /**
+ Update a status variable
+
+ @param observer [in] Requestor
+ @param status_var [in] Status variable to be updated
+ @param action [in] Operation to be performed on status variable
+
+ @returns status of the operation
+ @retval false Success
+ @retval true Error in processing
+ */
+
+ bool
+ Connection_event_coordinator::notify_status_var(Connection_event_observer **observer,
+ stats_connection_control status_var,
+ status_var_action action)
+ {
+ DBUG_ENTER("Connection_event_coordinator::notify_status_var");
+ bool error= false;
+
+ if (m_status_vars_subscription[status_var] == *observer)
+ {
+ if (status_var < STAT_LAST)
+ {
+ switch (action)
+ {
+ case ACTION_INC:
+ {
+ my_atomic_add64(&g_statistics.stats_array[status_var], 1);
+ break;
+ }
+ case ACTION_RESET:
+ {
+ my_atomic_store64(&g_statistics.stats_array[status_var], 0);
+ break;
+ }
+ default:
+ {
+ error= true;
+ DBUG_ASSERT(FALSE);
+ break;
+ }
+ }
+ }
+ }
+
+ DBUG_RETURN(error);
+ }
+}
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_CONTROL_COORDINATOR_H
+#define CONNECTION_CONTROL_COORDINATOR_H
+
+#include "connection_control_interfaces.h" /* Connection_event_coordinator_services */
+#include "connection_control_memory.h" /* Connection_control_alloc */
+
+namespace connection_control
+{
+ /**
+ Connection event coordinator.
+ This class will keep list of subscribers for different subevents
+ and notify them based on their preference.
+ */
+
+ class Connection_event_coordinator : public Connection_event_coordinator_services,
+ public Connection_control_alloc
+ {
+ public:
+ Connection_event_coordinator()
+ {
+ reset();
+ }
+ ~Connection_event_coordinator()
+ {
+ reset();
+ }
+
+ /* Functions to receive notification from server */
+ void notify_event(MYSQL_THD thd,
+ Error_handler * error_handler,
+ const mysql_event_connection *connection_event);
+ void notify_sys_var(Error_handler * error_handler,
+ opt_connection_control variable,
+ void *new_value);
+
+ /* Services provided to observers */
+ bool register_event_subscriber(Connection_event_observer **subscriber,
+ std::vector<opt_connection_control> *sys_vars,
+ std::vector<stats_connection_control> *status_vars);
+
+ bool notify_status_var(Connection_event_observer **observer,
+ stats_connection_control status_var,
+ status_var_action action);
+
+ private:
+ void reset();
+ class Connection_event_subscriber
+ {
+ public:
+ Connection_event_observer * m_subscriber;
+ bool m_sys_vars[OPT_LAST];
+ };
+
+ std::vector<Connection_event_subscriber> m_subscribers;
+ Connection_event_observer *m_status_vars_subscription[STAT_LAST];
+ };
+}
+#endif // !CONNECTION_CONTROL_COORDINATOR_H
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_CONTROL_DATA_H
+#define CONNECTION_CONTROL_DATA_H
+
+#include <my_global.h>
+
+/**
+ Enum for system variables : Must be in sync with
+ members of Connection_control_variables.
+*/
+typedef enum opt_connection_control
+{
+ OPT_FAILED_CONNECTIONS_THRESHOLD= 0,
+ OPT_MIN_CONNECTION_DELAY,
+ OPT_MAX_CONNECTION_DELAY,
+ OPT_LAST /* Must be last */
+}opt_connection_control;
+
+/**
+ Enum for status variables : Must be in sync with
+ memebers of Connection_control_statistics.
+*/
+typedef enum stats_connection_control
+{
+ STAT_CONNECTION_DELAY_TRIGGERED= 0,
+ STAT_LAST /* Must be last */
+}stats_connection_control;
+
+namespace connection_control
+{
+ /** Structure to maintain system variables */
+ class Connection_control_variables
+ {
+ public:
+ /* Various global variables */
+ int64 failed_connections_threshold;
+ int64 min_connection_delay;
+ int64 max_connection_delay;
+ };
+
+ /** Structure to maintain statistics */
+ class Connection_control_statistics
+ {
+ public:
+ Connection_control_statistics()
+ {}
+ /* Various statistics to be collected */
+ volatile int64 stats_array[STAT_LAST];
+ };
+}
+
+extern connection_control::Connection_control_statistics g_statistics;
+extern connection_control::Connection_control_variables g_variables;
+#endif // !CONNECTION_CONTROL_DATA_H
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_CONTROL_INTERFACES_H
+#define CONNECTION_CONTROL_INTERFACES_H
+
+#include <mysql/plugin_audit.h> /* mysql_event_connection */
+
+#include "connection_control_data.h" /* Variables and Status */
+
+#include <vector> /* std::vector */
+#include <string> /* std::string */
+
+namespace connection_control
+{
+ /* Typedefs for convenience */
+ typedef std::string Sql_string;
+
+ /* Forward declarations */
+ class Connection_event_coordinator_services;
+
+ class Error_handler
+ {
+ public:
+ virtual void handle_error(const char * error_message)= 0;
+ virtual ~Error_handler() {}
+ };
+
+ /**
+ Interface for recording connection events
+ */
+
+ class Connection_event_records
+ {
+ public:
+ virtual bool create_or_update_entry(const Sql_string &s)= 0;
+ virtual bool remove_entry(const Sql_string &s)= 0;
+ virtual bool match_entry(const Sql_string &s, void *value)= 0;
+ virtual void reset_all()= 0;
+ virtual ~Connection_event_records()
+ { /* Emptiness! */ }
+ };
+
+
+ /**
+ Interface for defining action on connection events
+ */
+ class Connection_event_observer
+ {
+ public:
+ virtual bool notify_event(MYSQL_THD thd,
+ Connection_event_coordinator_services *coordinator,
+ const mysql_event_connection *connection_event,
+ Error_handler *error_handler)= 0;
+ virtual bool notify_sys_var(Connection_event_coordinator_services *coordinator,
+ opt_connection_control variable,
+ void *new_value,
+ Error_handler *error_handler)= 0;
+ virtual ~Connection_event_observer()
+ { /* Nothing to see here! */ }
+ };
+
+
+ /* Status variable action enum */
+ typedef enum status_var_action
+ {
+ ACTION_NONE=0,
+ ACTION_INC,
+ ACTION_RESET,
+ ACTION_LAST /* Must be at the end */
+ }status_var_action;
+
+
+ /**
+ Interface to provide service to observers
+ */
+
+ class Connection_event_coordinator_services
+ {
+ public:
+ virtual bool notify_status_var(Connection_event_observer **observer,
+ stats_connection_control status_var,
+ status_var_action action)= 0;
+ virtual bool register_event_subscriber(Connection_event_observer **subscriber,
+ std::vector<opt_connection_control> *sys_vars,
+ std::vector<stats_connection_control> *status_vars)= 0;
+ virtual ~Connection_event_coordinator_services()
+ { /* go away */ }
+ };
+}
+#endif // !CONNECTION_CONTROL_INTERFACES_H
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_CONTROL_MEMORY_H
+#define CONNECTION_CONTROL_MEMORY_H
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <limits>
+#include <memory>
+
+namespace connection_control
+{
+ template <class T>
+ T Connection_control_malloc(size_t size)
+ {
+ void *allocated_memory= my_malloc(size, MYF(MY_WME));
+ return allocated_memory ? reinterpret_cast<T>(allocated_memory) : NULL;
+ }
+
+ class Connection_control_alloc
+ {
+ public:
+ static void *operator new(size_t size) throw ()
+ {
+ return Connection_control_malloc<void*>(size);
+ }
+ static void *operator new[](size_t size) throw ()
+ {
+ return Connection_control_malloc<void*>(size);
+ }
+ static void operator delete(void* ptr, std::size_t sz)
+ {
+ my_free(ptr);
+ }
+ static void operator delete[](void* ptr, std::size_t sz)
+ {
+ my_free(ptr);
+ }
+ };
+}
+
+#endif //CONNECTION_CONTROL_MEMORY_H
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#define MYSQL_SERVER "We need security context"
+#include <m_ctype.h> /* my_charset_bin */
+#include <sql_class.h> /* THD, Security context */
+#include <item_cmpfunc.h>
+#include <mysql/psi/mysql_thread.h>
+
+#include "connection_delay.h"
+#include "connection_control.h"
+#include "security_context_wrapper.h"
+
+
+
+/* Forward declaration */
+bool schema_table_store_record(THD *thd, TABLE *table);
+
+struct st_mysql_information_schema
+ connection_control_failed_attempts_view={MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION};
+
+/** Fields of information_schema.connection_control_failed_attempts */
+static ST_FIELD_INFO failed_attempts_view_fields[]=
+{
+ {"USERHOST", USERNAME_LENGTH + MAX_HOSTNAME + 5, MYSQL_TYPE_STRING, 0, MY_I_S_UNSIGNED, 0, 0},
+ {"FAILED_ATTEMPTS", 16, MYSQL_TYPE_LONG, 0, MY_I_S_UNSIGNED, 0, 0},
+ {0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
+};
+
+
+namespace connection_control
+{
+
+ /** constants/variables declared in connection_delay_interfaces.h */
+
+ int64 DEFAULT_THRESHOLD= 3;
+ int64 MIN_THRESHOLD= 0;
+ int64 DISABLE_THRESHOLD= 0;
+ int64 MAX_THRESHOLD= INT_MAX32;
+
+ int64 DEFAULT_MAX_DELAY= INT_MAX32;
+ int64 DEFAULT_MIN_DELAY= 1000;
+ int64 MIN_DELAY= 1000;
+ int64 MAX_DELAY= INT_MAX32;
+
+ /** variables used by connection_delay.cc */
+ static mysql_rwlock_t connection_event_delay_lock;
+ static PSI_rwlock_key key_connection_event_delay_lock;
+ static PSI_rwlock_info all_rwlocks[]=
+ {
+ {&key_connection_event_delay_lock, "connection_event_delay_lock", 0}
+ };
+
+ static opt_connection_control opt_enums[]=
+ {
+ OPT_FAILED_CONNECTIONS_THRESHOLD,
+ OPT_MIN_CONNECTION_DELAY,
+ OPT_MAX_CONNECTION_DELAY
+ };
+ size_t opt_enums_size= 3;
+
+ static stats_connection_control status_vars_enums[]=
+ {
+ STAT_CONNECTION_DELAY_TRIGGERED
+ };
+ size_t status_vars_enums_size= 1;
+
+ static Connection_delay_action *g_max_failed_connection_handler= 0;
+
+ Sql_string I_S_CONNECTION_CONTROL_FAILED_ATTEMPTS_USERHOST
+ ("information_schema.connection_control_failed_login_attempts.userhost");
+
+ /**
+ Helper function for Connection_delay_event::reset_all
+
+ @param ptr [in] Pointer to an entry in hash
+
+ @returns 1 to indicate that entry is a match
+ */
+ int match_all_entries(const uchar *ptr)
+ {
+ return 1;
+ }
+
+
+ /**
+ Callback function for LF hash to get key information
+
+ Returns a pointer to a buffer which represent a key in the hash.
+ The function does NOT calculate a hash.
+ The function is called during lf_hash_insert(). The buffer is
+ fed to an internal calc_hash() which use the defined charset to
+ calculate a hash from the key buffer (in most cases a murmur)
+
+ @param el [in] Pointer to an element in the hash
+ @param length [out] The length of the key belonging to the element
+
+ @returns Pointer to key buffer
+ */
+
+ uchar *connection_delay_event_hash_key(const uchar *el, size_t *length, my_bool huh)
+ {
+ const Connection_event_record * const *entry;
+ const Connection_event_record *entry_info;
+ entry= reinterpret_cast<const Connection_event_record* const *>(el);
+ DBUG_ASSERT(entry != NULL);
+ entry_info= *entry;
+ *length= entry_info->get_length();
+ return (const_cast<uchar *>(entry_info->get_userhost()));
+ }
+
+
+ /**
+ Constructor for Connection_delay_event
+
+ Initialize LF hash.
+ */
+
+ Connection_delay_event::Connection_delay_event()
+ {
+ lf_hash_init(&m_entries, sizeof(Connection_event_record**), LF_HASH_UNIQUE,
+ 0, /* key offset */
+ 0, /* key length not used */
+ connection_delay_event_hash_key,
+ &my_charset_bin);
+ }
+
+
+ /**
+ Creates or updates an entry in hash
+
+ @param s [in] User information in '<user'@'<host>' format
+
+ @returns status of insertion/update
+ @retval false Insertion/Update successful
+ @retval true Failed to insert/update an entry
+ */
+
+ bool
+ Connection_delay_event::create_or_update_entry(const Sql_string &s)
+ {
+ Connection_event_record **searched_entry= NULL;
+ Connection_event_record *searched_entry_info= NULL;
+ Connection_event_record *new_entry= NULL;
+ int insert_status;
+ DBUG_ENTER("Connection_delay_event::create_or_update_entry");
+
+ LF_PINS *pins= lf_hash_get_pins(&m_entries);
+ if (unlikely(pins == NULL))
+ DBUG_RETURN(true);
+
+ searched_entry= reinterpret_cast<Connection_event_record **>
+ (lf_hash_search(&m_entries, pins, s.c_str(), s.length()));
+
+ if (searched_entry && (searched_entry != MY_ERRPTR))
+ {
+ /* We found an entry, so increment the count */
+ searched_entry_info= *searched_entry;
+ DBUG_ASSERT(searched_entry_info != NULL);
+ searched_entry_info->inc_count();
+ lf_hash_search_unpin(pins);
+ lf_hash_put_pins(pins);
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ /* No entry found, so try to add new entry */
+ lf_hash_search_unpin(pins);
+ new_entry= new Connection_event_record(s);
+
+ insert_status= lf_hash_insert(&m_entries, pins, &new_entry);
+
+ if (likely(insert_status == 0))
+ {
+ lf_hash_put_pins(pins);
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ /*
+ OOM. We are likely in bigger trouble than just
+ failing to insert an entry in hash.
+ */
+ lf_hash_put_pins(pins);
+ delete new_entry;
+ new_entry= NULL;
+ DBUG_RETURN(true);
+ }
+ }
+ }
+
+
+ /**
+ Resets count stored against given user entry
+
+ @param s [in] User information in '<user'@'<host>' format
+
+ @returns status of reset operation
+ @retval false Reset successful
+ @retval true Failed to find given entry
+ */
+
+ bool
+ Connection_delay_event::remove_entry(const Sql_string &s)
+ {
+ Connection_event_record **searched_entry= NULL;
+ Connection_event_record *searched_entry_info= NULL;
+ DBUG_ENTER("Connection_delay_event::reset_entry");
+
+ LF_PINS *pins= lf_hash_get_pins(&m_entries);
+
+ searched_entry= reinterpret_cast<Connection_event_record **>
+ (lf_hash_search(&m_entries, pins, s.c_str(), s.length()));
+
+ if (searched_entry && searched_entry != MY_ERRPTR)
+ {
+ searched_entry_info= *searched_entry;
+ DBUG_ASSERT(searched_entry_info != NULL);
+ int rc= lf_hash_delete(&m_entries, pins, s.c_str(), s.length());
+ lf_hash_search_unpin(pins);
+ lf_hash_put_pins(pins);
+ if (rc == 0)
+ {
+ /* free memory upon successful deletion */
+ delete searched_entry_info;
+ }
+ DBUG_RETURN(rc != 0);
+ }
+ else
+ {
+ /* No entry found. */
+ lf_hash_search_unpin(pins);
+ lf_hash_put_pins(pins);
+ DBUG_RETURN(true);
+ }
+ }
+
+
+ /**
+ Retrieve stored value for given user entry
+
+ @param s [in] User information in '<user'@'<host>' format
+ @param value [out] Buffer to hold value stored against given user
+
+ @returns whether given entry is present in hash or not
+ @retval false Entry found. Corresponding value is copied in value buffer.
+ @retval true No matching entry found. value buffer should not be used.
+ */
+
+ bool
+ Connection_delay_event::match_entry(const Sql_string &s, void *value)
+ {
+ Connection_event_record **searched_entry= NULL;
+ Connection_event_record *searched_entry_info= NULL;
+ int64 count= DISABLE_THRESHOLD;
+ bool error= true;
+ DBUG_ENTER("Connection_delay_event::match_entry");
+
+ LF_PINS *pins= lf_hash_get_pins(&m_entries);
+
+ searched_entry= reinterpret_cast<Connection_event_record **>
+ (lf_hash_search(&m_entries, pins, s.c_str(), s.length()));
+
+ if (searched_entry && searched_entry != MY_ERRPTR)
+ {
+ searched_entry_info= *searched_entry;
+ count= searched_entry_info->get_count();
+ error= false;
+ }
+
+ lf_hash_search_unpin(pins);
+ lf_hash_put_pins(pins);
+ *(reinterpret_cast<int64 *>(value))= count;
+
+ DBUG_RETURN(error);
+ }
+
+
+ /**
+ Delete all entries from hash and free memory
+
+ @returns Reset was successful or not
+ @retval true All entries removed.
+ @retval false Failed to remove some entries
+ */
+
+ void
+ Connection_delay_event::reset_all()
+ {
+ Connection_event_record **searched_entry= NULL;
+ DBUG_ENTER("Connection_delay_event::reset_all");
+ LF_PINS *pins= lf_hash_get_pins(&m_entries);
+
+ do
+ {
+ /* match anything */
+ searched_entry= reinterpret_cast<Connection_event_record**>
+ (lf_hash_random_match(&m_entries, pins, match_all_entries, 0));
+
+ if (searched_entry != NULL && searched_entry != MY_ERRPTR
+ && (*searched_entry)
+ && !lf_hash_delete(&m_entries, pins,
+ (*searched_entry)->get_userhost(),
+ (*searched_entry)->get_length()))
+ {
+ delete (*searched_entry);
+ *searched_entry= NULL;
+ }
+ else
+ {
+ /* Failed to delete didn't remove any pins */
+ lf_hash_search_unpin(pins);
+ }
+ } while (searched_entry != 0);
+
+ lf_hash_put_pins(pins);
+ DBUG_VOID_RETURN;
+ }
+
+
+ /** This works because connction_delay_IS_table is protected by wrlock */
+ static TABLE * connection_delay_IS_table;
+ void set_connection_delay_IS_table(TABLE *t)
+ {
+ connection_delay_IS_table= t;
+ }
+
+
+ /**
+ Function to populate information_schema view.
+
+ @param ptr [in] Entry from LF hash
+
+ @returns status of row insertion
+ @retval 0 Success
+ @retval 1 Error
+ */
+
+ int connection_delay_IS_table_writer(const uchar *ptr)
+ {
+ /* Always return "no match" so that we go through all entries */
+ THD *thd= current_thd;
+ const Connection_event_record * const * entry;
+ const Connection_event_record *entry_info;
+ entry= reinterpret_cast<const Connection_event_record * const *>(ptr);
+ entry_info= *entry;
+ connection_delay_IS_table->field[0]->store((char *)entry_info->get_userhost(),
+ entry_info->get_length(),
+ system_charset_info);
+ connection_delay_IS_table->field[1]->store(entry_info->get_count(), true);
+ if (schema_table_store_record(thd, connection_delay_IS_table))
+ return 1;
+ /* Always return "no match" so that we go over all entries in the hash */
+ return 0;
+ }
+
+
+ /**
+ Function to dump LF hash data to IS table.
+
+ @param thd [in] THD handle
+ @param tables [in] Handle to
+ information_schema.connection_control_failed_attempts
+ */
+
+ void
+ Connection_delay_event::fill_IS_table(THD *thd,
+ TABLE_LIST *tables)
+ {
+ DBUG_ENTER("Connection_delay_event::fill_IS_table");
+ TABLE *table= tables->table;
+ set_connection_delay_IS_table(table);
+ LF_PINS *pins= lf_hash_get_pins(&m_entries);
+ void *key= 0;
+
+ do
+ {
+ key= lf_hash_random_match(&m_entries,
+ pins,
+ /* Functor: match anything and store the fields */
+ connection_delay_IS_table_writer,
+ 0);
+ /* Always unpin after lf_hash_random_match() */
+ lf_hash_search_unpin(pins);
+ } while (key != 0);
+
+ lf_hash_put_pins(pins);
+ DBUG_VOID_RETURN;
+ }
+
+
+ /**
+ Connection_delay_action Constructor.
+
+ @param [in] threshold Defines a threshold after which wait is triggered
+ @param [in] min_delay Lower cap on wait
+ @param [in] max_delay Upper cap on wait
+ @param [in] sys_vars System variables
+ @param [in] sys_vars_size Size of sys_vars array
+ @param [in] status_vars Status variables
+ @param [in] status_vars_size Size of status_vars array
+ @param [in] lock RW lock handle
+ */
+
+ Connection_delay_action::Connection_delay_action(int64 threshold,
+ int64 min_delay,
+ int64 max_delay,
+ opt_connection_control *sys_vars,
+ size_t sys_vars_size,
+ stats_connection_control *status_vars,
+ size_t status_vars_size,
+ mysql_rwlock_t *lock)
+ : m_threshold(threshold),
+ m_min_delay(min_delay),
+ m_max_delay(max_delay),
+ m_lock(lock)
+ {
+ if (sys_vars_size)
+ {
+ for (uint i=0; i < sys_vars_size; ++i)
+ m_sys_vars.push_back(sys_vars[i]);
+ }
+
+ if (status_vars_size)
+ {
+ for (uint i=0; i < status_vars_size; ++i)
+ m_stats_vars.push_back(status_vars[i]);
+ }
+ }
+
+
+ /**
+ Create hash key of the format '<user>'@'<host>'.
+ Policy:
+ 1. Use proxy_user information if available. Else if,
+ 2. Use priv_user/priv_host if either of them is not empty. Else,
+ 3. Use user/host
+
+ @param thd [in] THD pointer for getting security context
+ @param s [out] Hash key is stored here
+ */
+
+ void
+ Connection_delay_action::make_hash_key(MYSQL_THD thd, Sql_string &s)
+ {
+ /* Our key for hash will be of format : '<user>'@'<host>' */
+
+ /* If proxy_user is set then use it directly for lookup */
+ Security_context_wrapper sctx_wrapper(thd);
+ const char * proxy_user= sctx_wrapper.get_proxy_user();
+ if (proxy_user && *proxy_user)
+ {
+ s.append(proxy_user);
+ } /* else if priv_user and/or priv_host is set, then use them */
+ else
+ {
+ const char * priv_user= sctx_wrapper.get_priv_user();
+ const char * priv_host= sctx_wrapper.get_priv_host();
+ if ((priv_user && *priv_user) || (priv_host && *priv_host))
+ {
+ s.append("'");
+
+ if (*priv_user)
+ s.append(priv_user);
+
+ s.append("'@'");
+
+ if (*priv_host)
+ s.append(priv_host);
+
+ s.append("'");
+ }
+ else
+ {
+ const char *user= sctx_wrapper.get_user();
+ const char *host= sctx_wrapper.get_host();
+ const char *ip= sctx_wrapper.get_ip();
+
+ s.append("'");
+
+ if (user && *user)
+ s.append(user);
+
+ s.append("'@'");
+
+ if (host && *host)
+ s.append(host);
+ else if (ip && *ip)
+ s.append(ip);
+
+ s.append("'");
+ }
+ }
+ }
+
+
+ /**
+ Wait till the wait_time expires or thread is killed
+
+ @param thd [in] Handle to MYSQL_THD object
+ @param wait_time [in] Maximum time to wait
+ */
+
+ void
+ Connection_delay_action::conditional_wait(MYSQL_THD thd,
+ ulonglong wait_time)
+ {
+ DBUG_ENTER("Connection_delay_action::conditional_wait");
+ const char * category= "connection_delay";
+
+ /** mysql_cond_timedwait requires wait time in timespec format */
+ struct timespec abstime;
+ /** Since we get wait_time in milliseconds, convert it to nanoseconds */
+ set_timespec_nsec(abstime, wait_time * 1000000ULL);
+
+ /** PSI_stage_info for thd_enter_cond/thd_exit_cond */
+ PSI_stage_info old_stage;
+ PSI_stage_info stage_waiting_in_connection_control_plugin=
+ {0, "Waiting in connection_control plugin", 0};
+
+ /** Initialize mutex required for mysql_cond_timedwait */
+ mysql_mutex_t connection_delay_mutex;
+ PSI_mutex_key key_connection_delay_mutex;
+ PSI_mutex_info connection_delay_mutex_info[]=
+ {
+ {&key_connection_delay_mutex, "connection_delay_mutex", PSI_FLAG_GLOBAL}
+ };
+ int count_mutex= array_elements(connection_delay_mutex_info);
+ PSI_server->register_mutex(category, connection_delay_mutex_info, count_mutex);
+ mysql_mutex_init(key_connection_delay_mutex, &connection_delay_mutex,
+ MY_MUTEX_INIT_FAST);
+
+ /* Initialize condition to wait for */
+ mysql_cond_t connection_delay_wait_condition;
+ PSI_cond_key key_connection_delay_wait;
+ PSI_cond_info connection_delay_wait_info[]=
+ {
+ {&key_connection_delay_wait, "connection_delay_wait_condition", 0}
+ };
+ int count_cond= array_elements(connection_delay_wait_info);
+ PSI_server->register_cond(category, connection_delay_wait_info, count_cond);
+ mysql_cond_init(key_connection_delay_wait, &connection_delay_wait_condition, NULL);
+
+ /** Register wait condition with THD */
+ mysql_mutex_lock(&connection_delay_mutex);
+ thd_enter_cond(thd, &connection_delay_wait_condition, &connection_delay_mutex,
+ &stage_waiting_in_connection_control_plugin, &old_stage,
+ __func__, __FILE__, __LINE__);
+
+ /*
+ At this point, thread is essentially going to sleep till
+ timeout. If admin issues KILL statement for this THD,
+ there is no point keeping this thread in sleep mode only
+ to wake up to be terminated. Hence, in case of KILL,
+ we will return control to server without worring about
+ wait_time.
+ */
+ mysql_cond_timedwait(&connection_delay_wait_condition, &connection_delay_mutex, &abstime);
+
+ /* Finish waiting and deregister wait condition */
+ thd_exit_cond(thd, &stage_waiting_in_connection_control_plugin,
+ __func__, __FILE__, __LINE__);
+
+ /* Cleanup */
+ mysql_mutex_destroy(&connection_delay_mutex);
+ mysql_cond_destroy(&connection_delay_wait_condition);
+ DBUG_VOID_RETURN;
+ }
+
+
+ /**
+ @brief Handle a connection event and if requried,
+ wait for random amount of time before returning.
+
+ We only care about CONNECT and CHANGE_USER sub events.
+
+ @param thd [in] THD pointer
+ @param coordinator [in] Connection_event_coordinator
+ @param connection_event [in] Connection event to be handled
+ @param error_handler [in] Error handler object
+
+ @returns status of connection event handling
+ @retval false Successfully handled an event.
+ @retval true Something went wrong.
+ error_buffer may contain details.
+ */
+
+ bool
+ Connection_delay_action::notify_event(MYSQL_THD thd,
+ Connection_event_coordinator_services *coordinator,
+ const mysql_event_connection *connection_event,
+ Error_handler *error_handler)
+ {
+ DBUG_ENTER("Connection_delay_action::notify");
+ bool error= false;
+ unsigned int subclass= connection_event->event_subclass;
+ Connection_event_observer *self= this;
+
+ if (subclass != MYSQL_AUDIT_CONNECTION_CONNECT &&
+ subclass != MYSQL_AUDIT_CONNECTION_CHANGE_USER)
+ DBUG_RETURN(error);
+
+ RD_lock rd_lock(m_lock);
+
+ int64 threshold= this->get_threshold();
+
+ /* If feature was disabled, return */
+ if (threshold <= DISABLE_THRESHOLD)
+ DBUG_RETURN(error);
+
+ int64 current_count= 0;
+ bool user_present= false;
+ Sql_string userhost;
+
+ make_hash_key(thd, userhost);
+
+ DBUG_PRINT("info", ("Connection control : Connection event lookup for: %s",
+ userhost.c_str()));
+
+ /* Cache current failure count */
+ user_present=
+ m_userhost_hash.match_entry(userhost, (void *)¤t_count) ? false : true;
+
+ if (current_count >= threshold || current_count < 0)
+ {
+ /*
+ If threshold is crosed, regardless of connection success
+ or failure, wait for (current_count + 1) - threshold seconds
+ Note that current_count is not yet updated in hash. So we
+ have to consider current connection as well - Hence the usage
+ of current_count + 1.
+ */
+ ulonglong wait_time= get_wait_time((current_count + 1) - threshold);
+
+ if ((error= coordinator->notify_status_var(
+ &self, STAT_CONNECTION_DELAY_TRIGGERED, ACTION_INC)))
+ {
+ error_handler->handle_error("Failed to update connection delay triggered stats");
+ }
+ /*
+ Invoking sleep while holding read lock on Connection_delay_action
+ would block access to cache data through IS table.
+ */
+ rd_lock.unlock();
+ conditional_wait(thd, wait_time);
+ rd_lock.lock();
+ }
+
+ if (connection_event->status)
+ {
+ /*
+ Connection failure.
+ Add new entry to hash or increment failed connection count
+ for an existing entry
+ */
+ if (m_userhost_hash.create_or_update_entry(userhost))
+ {
+ char error_buffer[512];
+ memset(error_buffer, 0, sizeof(error_buffer));
+ my_snprintf(error_buffer, sizeof(error_buffer)-1,
+ "Failed to update connection delay hash for account : %s",
+ userhost.c_str());
+ error_handler->handle_error(error_buffer);
+ error= true;
+ }
+ }
+ else
+ {
+ /*
+ Successful connection.
+ delete entry for given account from the hash
+ */
+ if (user_present && m_userhost_hash.remove_entry(userhost))
+ {
+ char error_buffer[512];
+ memset(error_buffer, 0, sizeof(error_buffer));
+ my_snprintf(error_buffer, sizeof(error_buffer) - 1,
+ "Failed to delete connection delay hash entry for acount : %s."
+ " It might have been deleted already.",
+ userhost.c_str());
+ error_handler->handle_error(error_buffer);
+ error= true;
+ }
+ }
+
+ DBUG_RETURN(error);
+ }
+
+
+ /**
+ Notification of a change in system variable value
+
+ @param coordinator [in] Handle to coordinator
+ @param variable [in] Enum of variable
+ @param new_value [in] New value for variable
+ @param error_buffer [out] Buffer to log error message if any
+ @param error_buffer_size [in] Size of error buffer
+
+ @returns processing status
+ @retval false Change in variable value processed successfully
+ @retval true Error processing new value.
+ error_buffer may contain more details.
+ */
+
+ bool
+ Connection_delay_action::notify_sys_var(Connection_event_coordinator_services *coordinator,
+ opt_connection_control variable,
+ void *new_value,
+ Error_handler * error_handler)
+ {
+ DBUG_ENTER("Connection_delay_action::notify_sys_var");
+ bool error= true;
+ Connection_event_observer *self= this;
+
+ WR_lock wr_lock(m_lock);
+
+ switch (variable)
+ {
+ case OPT_FAILED_CONNECTIONS_THRESHOLD:
+ {
+ int64 new_threshold= *(static_cast<int64 *>(new_value));
+ DBUG_ASSERT(new_threshold >= DISABLE_THRESHOLD);
+ set_threshold(new_threshold);
+
+ if ((error= coordinator->notify_status_var(&self,
+ STAT_CONNECTION_DELAY_TRIGGERED,
+ ACTION_RESET)))
+ {
+ error_handler->handle_error("Failed to reset connection delay triggered stats");
+ }
+ break;
+ }
+ case OPT_MIN_CONNECTION_DELAY:
+ case OPT_MAX_CONNECTION_DELAY:
+ {
+ int64 new_delay= *(static_cast<int64 *>(new_value));
+ if ((error= set_delay(new_delay,
+ (variable == OPT_MIN_CONNECTION_DELAY))))
+ {
+ char error_buffer[512];
+ memset(error_buffer, 0, sizeof(error_buffer));
+ my_snprintf(error_buffer, sizeof(error_buffer) - 1,
+ "Could not set %s delay for connection delay.",
+ (variable == OPT_MIN_CONNECTION_DELAY) ? "min" : "max");
+ error_handler->handle_error(error_buffer);
+ }
+ break;
+ }
+ default:
+ /* Should never reach here. */
+ DBUG_ASSERT(FALSE);
+ error_handler->handle_error("Unexpected option type for connection delay.");
+ };
+ DBUG_RETURN(error);
+ }
+
+
+ /**
+ Subscribe with coordinator for connection events
+
+ @param coordinator [in] Handle to Connection_event_coordinator_services
+ for registration
+ */
+ void
+ Connection_delay_action::init(Connection_event_coordinator_services *coordinator)
+ {
+ DBUG_ENTER("Connection_delay_action::init");
+ DBUG_ASSERT(coordinator);
+ bool retval;
+ Connection_event_observer *subscriber= this;
+ WR_lock wr_lock(m_lock);
+ retval= coordinator->register_event_subscriber(&subscriber,
+ &m_sys_vars,
+ &m_stats_vars);
+ DBUG_ASSERT(!retval);
+ if (retval)
+ retval= false; /* Make compiler happy */
+ DBUG_VOID_RETURN;
+ }
+
+
+ /**
+ Clear data from Connection_delay_action
+ */
+
+ void
+ Connection_delay_action::deinit()
+ {
+ mysql_rwlock_wrlock(m_lock);
+ m_userhost_hash.reset_all();
+ m_sys_vars.clear();
+ m_stats_vars.clear();
+ m_threshold= DISABLE_THRESHOLD;
+ mysql_rwlock_unlock(m_lock);
+ m_lock= 0;
+ }
+
+
+
+ /**
+ Get user information from "where userhost = <value>"
+
+ @param cond [in] Equality condition structure
+ @param eq_arg [out] Sql_string handle to store user information
+ @param field_name [in] userhost field
+
+ @returns whether a value was found or not
+ @retval false Found a value. Check eq_arg
+ @retval true Error.
+ */
+
+ static
+ bool get_equal_condition_argument(Item *cond, Sql_string *eq_arg,
+ const Sql_string &field_name)
+ {
+ if (cond != 0 && cond->type() == Item::FUNC_ITEM)
+ {
+ Item_func *func= static_cast<Item_func *>(cond);
+ if (func != NULL && func->functype() == Item_func::EQ_FUNC)
+ {
+ Item_func_eq* eq_func= static_cast<Item_func_eq*>(func);
+ if (eq_func->arguments()[0]->type() == Item::FIELD_ITEM &&
+ my_strcasecmp(system_charset_info,
+ eq_func->arguments()[0]->full_name(),
+ field_name.c_str()) == 0)
+ {
+ char buff[1024];
+ String *res;
+ String filter(buff, sizeof(buff), system_charset_info);
+ if (eq_func->arguments()[1] != NULL &&
+ (res= eq_func->arguments()[1]->val_str(&filter)))
+ {
+ eq_arg->append(res->c_ptr_safe(), res->length());
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ Function to fill information_schema.connection_control_failed_attempts.
+
+ Handles query with equality condition.
+ For full scan, calls Connection_delay_event::fill_IS_table()
+
+ Permission : SUPER_ACL is required.
+
+ @param thd [in] THD handle.
+ @param tables [in] Handle to
+ information_schema.connection_control_failed_attempts.
+ @param cond [in] Condition if any.
+ */
+
+ void
+ Connection_delay_action::fill_IS_table(THD *thd,
+ TABLE_LIST *tables,
+ Item *cond)
+ {
+ DBUG_ENTER("Connection_delay_action::fill_IS_table");
+ Security_context_wrapper sctx_wrapper(thd);
+ if (!sctx_wrapper.is_super_user())
+ DBUG_VOID_RETURN;
+ WR_lock wr_lock(m_lock);
+ Sql_string eq_arg;
+ if (cond != 0 &&
+ !get_equal_condition_argument(cond,
+ &eq_arg,
+ I_S_CONNECTION_CONTROL_FAILED_ATTEMPTS_USERHOST))
+ {
+ int64 current_count= 0;
+ if (m_userhost_hash.match_entry(eq_arg, (void *)¤t_count))
+ {
+ /* There are no matches given the condition */
+ DBUG_VOID_RETURN;
+ }
+ else
+ {
+ /* There is exactly one matching userhost entry */
+ TABLE *table= tables->table;
+ table->field[0]->store(eq_arg.c_str(), eq_arg.length(),
+ system_charset_info);
+ table->field[1]->store(current_count, true);
+ schema_table_store_record(thd, table);
+ }
+ }
+ else
+ m_userhost_hash.fill_IS_table(thd, tables);
+
+ DBUG_VOID_RETURN;
+ }
+
+
+ /**
+ Initializes required objects for handling connection events.
+
+ @param coordinator [in] Connection_event_coordinator_services handle.
+ */
+
+ bool init_connection_delay_event(Connection_event_coordinator_services *coordinator,
+ Error_handler *error_handler)
+ {
+ /*
+ 1. Initialize lock(s)
+ */
+ const char *category= "conn_control";
+ int count= array_elements(all_rwlocks);
+ mysql_rwlock_register(category, all_rwlocks, count);
+ mysql_rwlock_init(key_connection_event_delay_lock,
+ &connection_event_delay_lock);
+ g_max_failed_connection_handler= new Connection_delay_action(g_variables.failed_connections_threshold,
+ g_variables.min_connection_delay,
+ g_variables.max_connection_delay,
+ opt_enums, opt_enums_size,
+ status_vars_enums, status_vars_enums_size,
+ &connection_event_delay_lock);
+ if (!g_max_failed_connection_handler)
+ {
+ error_handler->handle_error("Failed to initialization Connection_delay_action");
+ return true;
+ }
+ g_max_failed_connection_handler->init(coordinator);
+
+ return false;
+ }
+
+
+ /**
+ Deinitializes objects and frees associated memory.
+ */
+
+ void deinit_connection_delay_event()
+ {
+ if (g_max_failed_connection_handler)
+ delete g_max_failed_connection_handler;
+ g_max_failed_connection_handler= 0;
+ mysql_rwlock_destroy(&connection_event_delay_lock);
+ return;
+ }
+}
+
+
+/**
+ Function to fill information_schema.connection_control_failed_attempts.
+
+ @param thd [in] THD handle.
+ @param tables [in] Handle to
+ information_schema.connection_control_failed_attempts.
+ @param cond [in] Condition if any.
+
+ @returns Always returns FALSE.
+*/
+
+int fill_failed_attempts_view(THD *thd,
+ TABLE_LIST *tables,
+ Item *cond)
+{
+ if (connection_control::g_max_failed_connection_handler)
+ connection_control::g_max_failed_connection_handler->fill_IS_table(thd, tables, cond);
+ return FALSE;
+}
+
+
+/**
+ View init function
+
+ @param ptr [in] Handle to
+ information_schema.connection_control_failed_attempts.
+
+ @returns Always returns 0.
+*/
+
+int connection_control_failed_attempts_view_init(void *ptr)
+{
+ ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)ptr;
+
+ schema_table->fields_info= failed_attempts_view_fields;
+ schema_table->fill_table= fill_failed_attempts_view;
+ schema_table->idx_field1= 0;
+ schema_table->idx_field2= 1;
+ return 0;
+}
+
+
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_DELAY_H
+#define CONNECTION_DELAY_H
+
+#include <lf.h> /* LF Hash */
+#include <my_global.h>
+#include <my_atomic.h> /* my_atomic_* */
+#include <mysql_com.h> /* USERNAME_LENGTH */
+#include "table.h" /* TABLE_LIST */
+
+#include "connection_control_interfaces.h" /* Observer interface */
+#include "connection_delay_api.h" /* Constants */
+#include "connection_control_data.h" /* variables and status */
+#include "connection_control_memory.h" /* Connection_control_alloc */
+
+namespace connection_control
+{
+ /**
+ Class to store failed attempts information for a given user.
+ */
+
+ class Connection_event_record : public Connection_control_alloc
+ {
+ public:
+
+ /**
+ Constructor for Connection_event_record. Always initializes failed login count to 1.
+ */
+ Connection_event_record(const Sql_string &s)
+ : m_count(1)
+ {
+ memset((void *)m_userhost, 0, sizeof(m_userhost));
+ memcpy((void *)m_userhost, s.c_str(), s.length());
+ m_length= s.length();
+ m_count= 1;
+ }
+
+ /**
+ Retrives failed login count for given user entry
+
+ @returns Failed login count
+ */
+ int64 get_count() const
+ {
+ int64 result= my_atomic_load64((volatile int64*)&m_count);
+ return result;
+ }
+
+ /** Increment failed login count for given user entry by 1 */
+ void inc_count()
+ {
+ my_atomic_add64((volatile int64*)&m_count, 1);
+ }
+
+ /** Reset failed login count for given user entry */
+ void reset_count()
+ {
+ my_atomic_store64(&m_count, 0);
+ }
+
+ /** Get user information */
+ uchar * get_userhost() const
+ {
+ return const_cast<uchar *> (m_userhost);
+ }
+
+ /** Get length information */
+ size_t get_length() const
+ {
+ return m_length;
+ }
+
+ /** Destructor */
+ ~Connection_event_record()
+ {
+ m_count= DISABLE_THRESHOLD;
+ }
+
+ private:
+ /* '<user>'@'<host>' */
+ uchar m_userhost[1 + USERNAME_LENGTH + 3 + HOSTNAME_LENGTH + 1 + 1];
+ /* Length of m_userhost */
+ size_t m_length;
+ /* connection event count */
+ volatile int64 m_count;
+ };
+
+
+ /**
+ Hash for a connection event.
+ Stores information in Connection_event_record object for each user.
+ */
+
+ class Connection_delay_event : public Connection_event_records
+ {
+ public:
+
+ /** Constructor. Also initializes the hash */
+ Connection_delay_event();
+
+ /** Destructor. Removes all entries from hash before destroying hash */
+ ~Connection_delay_event()
+ {
+ reset_all();
+ lf_hash_destroy(&m_entries);
+ }
+
+ void fill_IS_table(THD *thd, TABLE_LIST *tables);
+
+ /* Overridden function */
+ bool create_or_update_entry(const Sql_string &s);
+ bool remove_entry(const Sql_string &s);
+ bool match_entry(const Sql_string &s, void * value);
+ void reset_all();
+
+ private:
+ /** Hash for storing Connection_event_record per user */
+ LF_HASH m_entries;
+ };
+
+
+ /**
+ Connection event action to enforce max failed login constraint
+ */
+
+ class Connection_delay_action : public Connection_event_observer,
+ public Connection_control_alloc
+ {
+ public:
+
+ Connection_delay_action(int64 threshold,
+ int64 min_delay,
+ int64 max_delay,
+ opt_connection_control *sys_vars,
+ size_t sys_vars_size,
+ stats_connection_control *status_vars,
+ size_t status_vars_size,
+ mysql_rwlock_t *lock);
+
+ /** Destructor */
+ ~Connection_delay_action()
+ {
+ deinit();
+ m_lock= 0;
+ }
+
+ void init(Connection_event_coordinator_services *coordinator);
+
+ /**
+ Set threshold value.
+
+ @param threshold [in] New threshold value
+
+ @returns whether threshold value was changed successfully or not
+ @retval true Success
+ @retval false Failure. Invalid threshold value specified.
+ */
+
+ void set_threshold(int64 threshold)
+ {
+ my_atomic_store64(&m_threshold, threshold);
+ /* Clear the hash */
+ m_userhost_hash.reset_all();
+ }
+
+ /** Get threshold value */
+ int64 get_threshold()
+ {
+ int64 result= my_atomic_load64(&m_threshold);
+ return result;
+ }
+
+ /**
+ Set min/max delay
+
+ @param new_value [in] New m_min_delay/m_max_delay value
+ @param min [in] true for m_min_delay. false otherwise.
+
+ @returns whether m_min_delay/m_max_delay value was changed successfully or not
+ @retval false Success
+ @retval true Failure. Invalid value specified.
+ */
+
+ bool set_delay(int64 new_value, bool min)
+ {
+ int64 current_max= get_max_delay();
+ int64 current_min= get_min_delay();
+
+ if (new_value < MIN_DELAY)
+ return true;
+
+ if ((min && new_value > current_max) ||
+ (!min && new_value < current_min))
+ return true;
+
+ else
+ min ? my_atomic_store64(&m_min_delay, new_value) :
+ my_atomic_store64(&m_max_delay, new_value);
+ return false;
+ }
+
+ /** Get max value */
+ int64 get_max_delay()
+ {
+ int64 result= my_atomic_load64(&m_max_delay);
+ return result;
+ }
+
+ /** Get min value */
+ int64 get_min_delay()
+ {
+ int64 result= my_atomic_load64(&m_min_delay);
+ return result;
+ }
+
+ void fill_IS_table(THD *thd,
+ TABLE_LIST *tables,
+ Item *cond);
+
+ /** Overridden functions */
+ bool notify_event(MYSQL_THD thd,
+ Connection_event_coordinator_services *coordinator,
+ const mysql_event_connection *connection_event,
+ Error_handler *error_handler);
+ bool notify_sys_var(Connection_event_coordinator_services *coordinator,
+ opt_connection_control variable,
+ void *new_value,
+ Error_handler *error_handler);
+
+ private:
+ void deinit();
+ void make_hash_key(MYSQL_THD thd, Sql_string &s);
+ /**
+ Generates wait time
+
+ @param count [in] Proposed delay
+
+ @returns wait time
+ */
+
+ inline ulonglong get_wait_time(int64 count)
+ {
+ int64 max_delay= get_max_delay();
+ int64 min_delay= get_min_delay();
+ int64 count_mili= count*1000;
+
+ /*
+ if count < 0 (can happen in edge cases
+ we return max_delay.
+ Otherwise, following equation will be used:
+ wait_time = MIN(MIN(count, min_delay),
+ max_delay)
+ */
+ return (static_cast<ulonglong>((count_mili >= MIN_DELAY && count_mili < max_delay) ?
+ (count_mili < min_delay ? min_delay : count_mili) :
+ max_delay));
+ }
+ void conditional_wait(THD *thd,
+ ulonglong wait_time);
+
+ private:
+ /** Threshold value which triggers wait */
+ volatile int64 m_threshold;
+ /** Lower cap on delay to be generated */
+ volatile int64 m_min_delay;
+ /** Upper cap on delay to be generated */
+ volatile int64 m_max_delay;
+ /** System variables */
+ std::vector<opt_connection_control> m_sys_vars;
+ /** Status variables */
+ std::vector<stats_connection_control> m_stats_vars;
+ /** Hash to store failed attempts for each user entry */
+ Connection_delay_event m_userhost_hash;
+ /** RW lock */
+ mysql_rwlock_t *m_lock;
+ };
+}
+#endif /* !CONNECTION_DELAY_H */
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CONNECTION_DELAY_API_H
+#define CONNECTION_DELAY_API_H
+
+/* Structures/Functions requried for information schema table */
+
+extern struct st_mysql_information_schema connection_control_failed_attempts_view;
+int connection_control_failed_attempts_view_init(void *ptr);
+
+namespace connection_control
+{
+
+ /* constants/variables defined in connection_delay.cc */
+
+ extern int64 DEFAULT_THRESHOLD;
+ extern int64 MIN_THRESHOLD;
+ extern int64 DISABLE_THRESHOLD;
+ extern int64 MAX_THRESHOLD;
+
+ extern int64 DEFAULT_MAX_DELAY;
+ extern int64 DEFAULT_MIN_DELAY;
+ extern int64 MIN_DELAY;
+ extern int64 MAX_DELAY;
+
+ /** Functions being used by connection_control.cc */
+
+ class Connection_event_coordinator_services;
+ class Error_handler;
+
+ bool init_connection_delay_event(Connection_event_coordinator_services *coordinator,
+ Error_handler *error_handler);
+ void deinit_connection_delay_event();
+
+}
+#endif // !CONNECTION_DELAY_API_H
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#define MYSQL_SERVER "We need security context"
+
+#include <sql_class.h> /* THD, Security context */
+#include <sql_acl.h> /* SUPER_ACL */
+
+#include "security_context_wrapper.h"
+
+namespace connection_control
+{
+ /**
+ Security_context_wrapper constructor.
+
+ @param thd [in] Handle to THD
+
+ Get security context from thd.
+ */
+ Security_context_wrapper::Security_context_wrapper(MYSQL_THD thd)
+ {
+ m_valid= thd->security_ctx ? true : false;
+ m_thd= thd;
+ }
+
+
+ /**
+ Get value for given property from security context
+
+ @param property [in] Property to be checked
+ @param value [out] Value of the property
+
+ @returns status of property check
+ @retval true Error fetching property value
+ @retval false value contains valid value for given property
+ */
+
+ bool
+ Security_context_wrapper::get_property(const char *property, LEX_CSTRING *value)
+ {
+ value->length=0;
+ value->str= 0;
+
+ if (!m_valid || !property)
+ return true;
+ else
+ {
+ if (!strcmp(property, "priv_user"))\r
+ {\r
+ if (m_thd->security_ctx->priv_user)\r
+ {\r
+ value->str= m_thd->security_ctx->priv_user;\r
+ value->length= strlen(value->str);\r
+ }\r
+ }
+ else if (!strcmp(property, "priv_host"))
+ {
+ if (m_thd->security_ctx->priv_host)
+ {
+ value->str= m_thd->security_ctx->priv_host;
+ value->length= strlen(value->str);
+ }
+ }
+ else if (!strcmp(property, "user"))
+ {
+ if (m_thd->security_ctx->user)
+ {
+ value->str= m_thd->security_ctx->user;
+ value->length= strlen(value->str);
+ }
+ }
+ else if (!strcmp(property, "proxy_user"))\r
+ {\r
+ if (m_thd->security_ctx->proxy_user)\r
+ {\r
+ value->str= m_thd->security_ctx->proxy_user;\r
+ value->length= strlen(value->str);\r
+ }\r
+ }
+ else if (!strcmp(property, "host"))
+ {
+ if (m_thd->security_ctx->get_host()->length())
+ {
+ value->str= m_thd->security_ctx->get_host()->c_ptr();
+ value->length= strlen(value->str);
+ }
+ }
+ else if (!strcmp(property, "ip"))
+ {
+ if (m_thd->security_ctx->get_ip()->length())
+ {
+ value->str= m_thd->security_ctx->get_ip()->c_ptr();
+ value->length= strlen(value->str);
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /** Get proxy user information from security context */
+
+ const char *
+ Security_context_wrapper::get_proxy_user()
+ {
+ LEX_CSTRING proxy_user;
+ if (get_property("proxy_user", &proxy_user))
+ return 0;
+ return proxy_user.str;
+ }
+
+
+ /** Get priv user information from security context */
+
+ const char *
+ Security_context_wrapper::get_priv_user()
+ {
+ LEX_CSTRING priv_user;
+ if (get_property("priv_user", &priv_user))
+ return 0;
+ return priv_user.str;
+ }
+
+
+ /** Get priv host information from security context */
+
+ const char *
+ Security_context_wrapper::get_priv_host()
+ {
+ LEX_CSTRING priv_host;
+ if (get_property("priv_host", &priv_host))
+ return 0;
+ return priv_host.str;
+ }
+
+
+ /** Get connected user information from security context */
+
+ const char *
+ Security_context_wrapper::get_user()
+ {
+ LEX_CSTRING user;
+ if (get_property("user", &user))
+ return 0;
+ return user.str;
+ }
+
+
+ /** Get connected host information from security context */
+
+ const char *
+ Security_context_wrapper::get_host()
+ {
+ /*
+ We can't use thd->security_ctx->priv_host_name()
+ because it returns "%" if hostname is empty.
+ However, thd->security_ctx->proxy_user won't have
+ "%" if hostname was empty.
+
+ To be consistent, we will always use
+ 'user'@'host'/''@'host'/''@'' type of representation.
+ */
+ LEX_CSTRING host;
+ if (get_property("host", &host))
+ return 0;
+ return host.str;
+ }
+
+
+ /** Get connected ip information from security context */
+
+ const char *
+ Security_context_wrapper::get_ip()
+ {
+ LEX_CSTRING ip;
+ if (get_property("ip", &ip))
+ return 0;
+ return ip.str;
+ }
+
+
+ /** Check if valid security context exists for give THD or not */
+
+ bool
+ Security_context_wrapper::security_context_exists()
+ {
+ return m_valid;
+ }
+
+
+ /** Check whether user has requried privilege or not */
+
+ bool
+ Security_context_wrapper::is_super_user()
+ {
+ if (!m_valid)
+ return false;
+
+ bool has_super= ((m_thd->security_ctx->master_access & SUPER_ACL) == SUPER_ACL);
+
+ return has_super;
+ }
+}
--- /dev/null
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef HAVE_SECURITY_CONTEXT_WRAPPER_H
+#define HAVE_SECURITY_CONTEXT_WRAPPER_H
+
+#include <mysql/plugin.h>
+
+namespace connection_control
+{
+ class Security_context_wrapper
+ {
+ public:
+ Security_context_wrapper(MYSQL_THD thd);
+ ~Security_context_wrapper()
+ {}
+ const char * get_proxy_user();
+ const char * get_priv_user();
+ const char * get_priv_host();
+ const char * get_user();
+ const char * get_host();
+ const char * get_ip();
+ bool security_context_exists();
+ bool is_super_user();
+
+ private:
+ bool get_property(const char *property, LEX_CSTRING *value);
+ MYSQL_THD m_thd;
+ bool m_valid;
+ };
+}
+#endif // !HAVE_SECURITY_CONTEXT_WRAPPER_H
TranxNode* entry= NULL;
mysql_cond_t* thd_cond= NULL;
+ bool is_semi_sync_trans= true;
if (active_tranxs_ != NULL && trx_wait_binlog_name)
{
entry=
break;
}
}
+ /*
+ When code reaches here an Entry object may not be present in the
+ following scenario.
+
+ Semi sync was not enabled when transaction entered into ordered_commit
+ process. During flush stage, semi sync was not enabled and there was no
+ 'Entry' object created for the transaction being committed and at a
+ later stage it was enabled. In this case trx_wait_binlog_name and
+ trx_wait_binlog_pos are set but the 'Entry' object is not present. Hence
+ dump thread will not wait for reply from slave and it will not update
+ reply_file_name. In such case the committing transaction should not wait
+ for an ack from slave and it should be considered as an async
+ transaction.
+ */
+ if (!entry)
+ {
+ is_semi_sync_trans= false;
+ goto l_end;
+ }
/* Let us update the info about the minimum binlog position of waiting
* threads.
l_end:
/* Update the status counter. */
- if (is_on())
+ if (is_on() && is_semi_sync_trans)
rpl_semi_sync_master_yes_transactions++;
else
rpl_semi_sync_master_no_transactions++;
-# Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
STRING(REPLACE "-pipe" "" CXXFLAGS "${CXXFLAGS}")
IF(UNIX)
- # FIND_PROC and CHECK_PID are used by mysqld_safe
+ # SHELL_PATH, FIND_PROC, CHECK_PID are used by mysqld_safe
+IF(CMAKE_SYSTEM_NAME MATCHES "SunOS")
+ SET (SHELL_PATH "/bin/bash")
+ELSE()
+ SET (SHELL_PATH "/bin/sh")
+ENDIF()
+
IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
SET (FIND_PROC
"ps wwwp $PID | grep -v mysqld_safe | grep -- $MYSQLD > /dev/null")
-#!/bin/sh
+#!@SHELL_PATH@
# Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
# This file is public domain and comes with NO WARRANTY of any kind
#
echo "$msg"
case $logging in
init) ;; # Just echo the message, don't save it anywhere
- file) echo "$msg" >> "$err_log" ;;
+ file)
+ if [ -w / -o "$USER" = "root" ]; then
+ true
+ else
+ echo "$msg" >> "$err_log"
+ fi
+ ;;
syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;;
*)
echo "Internal program error (non-fatal):" \
eval_log_error () {
local cmd="$1"
case $logging in
- file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;;
+ file)
+ if [ -w / -o "$USER" = "root" ]; then
+ cmd="$cmd > /dev/null 2>&1"
+ else
+ cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1"
+ fi
+ ;;
syslog)
# mysqld often prefixes its messages with a timestamp, which is
# redundant when logging to syslog (which adds its own timestamp)
# mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])!
--core-file-size=*) core_file_size="$val" ;;
- --ledir=*) ledir="$val" ;;
+ --ledir=*)
+ if [ -z "$pick_args" ]; then
+ log_error "--ledir option can only be used as command line option, found in config file"
+ exit 1
+ fi
+ ledir="$val"
+ ;;
--malloc-lib=*) set_malloc_lib "$val" ;;
--mysqld=*)
if [ -z "$pick_args" ]; then
relpkgdata='@pkgdatadir@'
fi
-MY_PWD=`pwd`
+case "$0" in
+ /*)
+ MY_PWD='@prefix@'
+ ;;
+ *)
+ MY_PWD=`dirname $0`
+ MY_PWD=`dirname $MY_PWD`
+ ;;
+esac
# Check for the directories we would expect from a binary release install
if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION"
then
fi
# Log to err_log file
- log_notice "Logging to '$err_log'."
logging=file
-
- if [ ! -f "$err_log" -a ! -h "$err_log" ]; then # if error log already exists,
- touch "$err_log" # we just append. otherwise,
- chmod "$fmode" "$err_log" # fix the permissions here!
- fi
-
else
if [ -n "$syslog_tag" ]
then
logging=syslog
fi
+logdir=`dirname "$err_log"`
+# Change the err log to the right user, if possible and it is in use
+if [ $logging = "file" -o $logging = "both" ]; then
+ if [ ! -f "$err_log" -a ! -h "$err_log" ]; then
+ if test -w / -o "$USER" = "root"; then
+ case $logdir in
+ /var/log)
+ (
+ umask 0137
+ set -o noclobber
+ > "$err_log" && chown $user "$err_log"
+ ) ;;
+ *) ;;
+ esac
+ else
+ (
+ umask 0137
+ set -o noclobber
+ > "$err_log"
+ )
+ fi
+ fi
+
+ if [ -f "$err_log" ]; then # Log to err_log file
+ log_notice "Logging to '$err_log'."
+ elif [ "x$user" = "xroot" ]; then # running as root, mysqld can create log file; continue
+ echo "Logging to '$err_log'." >&2
+ else
+ case $logdir in
+ # We can't create $err_log, however mysqld can; continue
+ /tmp|/var/tmp|/var/log/mysql|$DATADIR)
+ echo "Logging to '$err_log'." >&2
+ ;;
+ # We can't create $err_log and don't know if mysqld can; error out
+ *)
+ log_error "error: log-error set to '$err_log', however file don't exists. Create writable for user '$user'."
+ exit 1
+ ;;
+ esac
+ fi
+fi
+
USER_OPTION=""
if test -w / -o "$USER" = "root"
then
then
USER_OPTION="--user=$user"
fi
- # Change the err log to the right user, if it is in use
- if [ $want_syslog -eq 0 -a ! -h "$err_log" ]; then
- touch "$err_log"
- chown $user "$err_log"
- fi
if test -n "$open_files"
then
ulimit -n $open_files
fi
safe_mysql_unix_port=${mysql_unix_port:-${MYSQL_UNIX_PORT:-@MYSQL_UNIX_ADDR@}}
-# Make sure that directory for $safe_mysql_unix_port exists
+# Check that directory for $safe_mysql_unix_port exists
mysql_unix_port_dir=`dirname $safe_mysql_unix_port`
if [ ! -d $mysql_unix_port_dir ]
then
- if [ ! -h $mysql_unix_port_dir ]; then
- mkdir $mysql_unix_port_dir
- chown $user $mysql_unix_port_dir
- chmod 755 $mysql_unix_port_dir
- fi
+ log_error "Directory '$mysql_unix_port_dir' for UNIX socket file don't exists."
+ exit 1
fi
# If the user doesn't specify a binary, we assume name "mysqld"
eval_log_error "$cmd $wsrep_start_position_opt --wsrep_cluster_address=$url $nohup_redir"
fi
+ # hypothetical: log was renamed but not
+ # flushed yet. we'd recreate it with
+ # wrong owner next time we log, so set
+ # it up correctly while we can!
+
if [ $want_syslog -eq 0 -a ! -f "$err_log" -a ! -h "$err_log" ]; then
- touch "$err_log" # hypothetical: log was renamed but not
- chown $user "$err_log" # flushed yet. we'd recreate it with
- chmod "$fmode" "$err_log" # wrong owner next time we log, so set
- fi # it up correctly while we can!
+ if test -w / -o "$USER" = "root"; then
+ logdir=`dirname "$err_log"`
+ case $logdir in
+ /var/log)
+ (
+ umask 0137
+ set -o noclobber
+ > "$err_log" && chown $user "$err_log"
+ ) ;;
+ *) ;;
+ esac
+ else
+ (
+ umask 0137
+ set -o noclobber
+ > "$err_log"
+ )
+ fi
+ fi
end_time=`date +%M%S`
-/* Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@param tm[OUT] The value to set.
@param time_type Timestasmp type
*/
-inline void set_zero_time(MYSQL_TIME *tm,
- enum enum_mysql_timestamp_type time_type)
+void set_zero_time(MYSQL_TIME *tm,
+ enum enum_mysql_timestamp_type time_type)
{
memset(tm, 0, sizeof(*tm));
tm->time_type= time_type;
Set hour, minute and second of a MYSQL_TIME variable to maximum time value.
Unlike set_max_time(), does not touch the other structure members.
*/
-inline void set_max_hhmmss(MYSQL_TIME *tm)
+void set_max_hhmmss(MYSQL_TIME *tm)
{
tm->hour= TIME_MAX_HOUR;
tm->minute= TIME_MAX_MINUTE;
@retval TRUE if the value is fatally bad.
@retval FALSE if the value is Ok.
*/
-inline my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
+my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
{
return ltime->minute >= 60 || ltime->second >= 60 ||
ltime->second_part > 999999;
@retval FALSE if value is Ok.
@retval TRUE if value is out of range.
*/
-inline my_bool check_time_range_quick(const MYSQL_TIME *ltime)
+my_bool check_time_range_quick(const MYSQL_TIME *ltime)
{
longlong hour= (longlong) ltime->hour + 24LL * ltime->day;
/* The input value should not be fatally bad */
gtid_before_write_cache(thd, cache_data))
goto err;
+#ifdef WITH_WSREP
+ if (WSREP_ON && thd->slave_thread) thd->wsrep_replicate_GTID= true;
+#endif /* WITH_WSREP */
ret= mysql_bin_log.commit(thd, true);
err:
{
DBUG_ENTER("binlog_rollback");
int error= 0;
+#ifdef WITH_WSREP
+ if (thd->lex->sql_command == SQLCOM_ROLLBACK_TO_SAVEPOINT &&
+ thd->wsrep_conflict_state != ABORTING)
+#else
if (thd->lex->sql_command == SQLCOM_ROLLBACK_TO_SAVEPOINT)
+#endif
error= mysql_bin_log.rollback(thd, all);
DBUG_RETURN(error);
}
the caches since this function is called as part of the engine
rollback.
*/
+#ifdef WITH_WSREP
+ if (thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT ||
+ thd->wsrep_conflict_state == ABORTING)
+#else
if (thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT)
+#endif /* WITH_WSREP */
if ((error= ha_rollback_low(thd, all)))
goto end;
*/
if (stuff_logged)
{
+#ifdef WITH_WSREP
+ if (WSREP_ON && thd->wsrep_replicate_GTID &&
+ wsrep_replicate_GTID(thd))
+ {
+ /* GTID replication failed */
+ DBUG_RETURN(RESULT_ABORTED);
+ }
+#endif /* WITH_WSREP */
if (ordered_commit(thd, all))
DBUG_RETURN(RESULT_INCONSISTENT);
}
/* Pre-conditions */
#ifdef WITH_WSREP
DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- (WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open()));
+ (WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open()));
#else
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
#endif /* WITH_WSREP */
binlog by filtering rules.
*/
#ifdef WITH_WSREP
- if ((WSREP_EMULATE_BINLOG(this) ||
+ if ((WSREP_EMULATE_BINLOG_NNULL(this) ||
(mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG))) &&
!(WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT &&
- !binlog_filter->db_ok(db)))
+ !binlog_filter->db_ok(db)))
#else
if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
!(variables.binlog_format == BINLOG_FORMAT_STMT &&
uint non_replicated_tables_count= 0;
#ifndef DBUG_OFF
{
- static const char *prelocked_mode_name[] = {
- "NON_PRELOCKED",
- "PRELOCKED",
- "PRELOCKED_UNDER_LOCK_TABLES",
- };
DBUG_PRINT("debug", ("prelocked_mode: %s",
- prelocked_mode_name[locked_tables_mode]));
+ get_locked_tables_mode_name(locked_tables_mode)));
}
#endif
limited to row-logging when binlog_format = STATEMENT
*/
#ifdef WITH_WSREP
- if (!WSREP(this) || wsrep_exec_mode == LOCAL_STATE)
+ if (!WSREP_NNULL(this) || wsrep_exec_mode == LOCAL_STATE)
{
#endif /* WITH_WSREP */
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
{
#ifdef WITH_WSREP
DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- ((WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open())));
+ ((WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open())));
#else
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
#endif /* WITH_WSREP */
{
#ifdef WITH_WSREP
DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- ((WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open())));
+ ((WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open())));
#else
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
#endif /* WITH_WSREP */
{
#ifdef WITH_WSREP
DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- ((WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open())));
+ ((WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open())));
#else
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
#endif /* WITH_WSREP */
flushing anything (e.g., if we have explicitly locked tables).
*/
#ifdef WITH_WSREP
- if (!(WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open()))
+ if (!(WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open()))
#else
if (!mysql_bin_log.is_open())
#endif /* WITH_WSREP */
DBUG_PRINT("enter", ("qtype: %s query: '%s'",
show_query_type(qtype), query_arg));
#ifdef WITH_WSREP
- DBUG_ASSERT(query_arg && (WSREP_EMULATE_BINLOG(this)
- || mysql_bin_log.is_open()));
+ DBUG_ASSERT(query_arg && (WSREP_EMULATE_BINLOG_NNULL(this)
+ || mysql_bin_log.is_open()));
#else
DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
#endif /* WITH_WSREP */
int diff;
if ((diff= (int) (field_length - value->length())) > 0)
{
- bmove_upp((uchar*) value->ptr()+field_length,
- (uchar*) value->ptr()+value->length(),
- value->length());
- memset(const_cast<char*>(value->ptr()), '0', diff);
- value->length(field_length);
- (void) value->c_ptr_quick(); // Avoid warnings in purify
+ const bool error= value->realloc(field_length);
+ if (!error)
+ {
+ bmove_upp((uchar*) value->ptr()+field_length,
+ (uchar*) value->ptr()+value->length(),
+ value->length());
+ memset(const_cast<char*>(value->ptr()), '0', diff);
+ value->length(field_length);
+ }
}
}
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
insert_sql_functions();
}
+struct hash_map_cleanup
+{
+ hash_map_cleanup(char **buffer, int *sz)
+ : m_buffer(buffer), m_sz(sz)
+ {}
+ ~hash_map_cleanup()
+ {
+ free(*m_buffer);
+ *m_buffer= NULL;
+ *m_sz= 0;
+ }
+ char **m_buffer;
+ int *m_sz;
+};
+
char *hash_map= 0;
int size_hash_map= 0;
void print_find_structs()
{
- add_structs_to_map(root_by_len,max_len);
- set_links(root_by_len,max_len);
- print_hash_map("sql_functions_map");
-
- hash_map= 0;
- size_hash_map= 0;
+ {
+ hash_map_cleanup cleanup(&hash_map, &size_hash_map);
+ add_structs_to_map(root_by_len,max_len);
+ set_links(root_by_len,max_len);
+ print_hash_map("sql_functions_map");
+ }
printf("\n");
- add_structs_to_map(root_by_len2,max_len2);
- set_links(root_by_len2,max_len2);
- print_hash_map("symbols_map");
+ {
+ hash_map_cleanup cleanup(&hash_map, &size_hash_map);
+ add_structs_to_map(root_by_len2,max_len2);
+ set_links(root_by_len2,max_len2);
+ print_hash_map("symbols_map");
+ }
}
int check_dup_symbols(SYMBOL *s1, SYMBOL *s2)
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error= 1;
+ WSREP_WARN("handlerton rollback failed, thd %lu %lld conf %d SQL %s",
+ thd->thread_id, thd->query_id, thd->wsrep_conflict_state,
+ thd->query());
}
status_var_increment(thd->status_var.ha_rollback_count);
ha_info_next= ha_info->next();
orig_argc= argc;
orig_argv= argv;
my_getopt_use_args_separator= TRUE;
+ my_defaults_read_login_file= FALSE;
if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv))
return 1;
my_getopt_use_args_separator= FALSE;
DBUG_PRINT("info", ("parser: %s", str));
DBUG_VOID_RETURN;
}
+
+bool has_external_data_or_index_dir(partition_info &pi)
+{
+ List_iterator<partition_element> part_it(pi.partitions);
+ for (partition_element *part= part_it++; part; part= part_it++)
+ {
+ if (part->data_file_name != NULL || part->index_file_name != NULL)
+ {
+ return true;
+ }
+ List_iterator<partition_element> subpart_it(part->subpartitions);
+ for (const partition_element *subpart= subpart_it++;
+ subpart;
+ subpart= subpart_it++)
+ {
+ if (subpart->data_file_name != NULL || subpart->index_file_name != NULL)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
#else /* WITH_PARTITION_STORAGE_ENGINE */
/*
For builds without partitioning we need to define these functions
#ifndef PARTITION_INFO_INCLUDED
#define PARTITION_INFO_INCLUDED
-/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
part_iter->get_next= get_next_partition_id_range;
}
+/**
+ Predicate which returns true if any partition or subpartition uses
+ an external data directory or external index directory.
+
+ @param pi partitioning information
+ @retval true if any partition or subpartition has an external
+ data directory or external index directory.
+ @retval false otherwise
+ */
+bool has_external_data_or_index_dir(partition_info &pi);
+
#endif /* PARTITION_INFO_INCLUDED */
-/* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
DBUG_PRINT_BITSET("debug", "cols: %s", cols);
+ /**
+ Save a reference to the original write set bitmaps.
+ We will need this to restore the bitmaps at the end.
+ */
+ MY_BITMAP *old_write_set= table->write_set;
+ /**
+ Just to be sure that tmp_set is currently not in use as
+ the read_set already.
+ */
+ DBUG_ASSERT(table->write_set != &table->tmp_set);
+ /* set the temporary write_set */
+ table->column_bitmaps_set_no_signal(table->read_set,
+ &table->tmp_set);
+ /**
+ Set table->write_set bits for all the columns as they
+ will be checked in set_default() function.
+ */
+ bitmap_set_all(table->write_set);
+
for (Field **field_ptr= table->field; *field_ptr; ++field_ptr)
{
- if ((uint) (field_ptr - table->field) >= cols->n_bits ||
- !bitmap_is_set(cols, field_ptr - table->field))
- {
+ uint field_index= (uint) (field_ptr - table->field);
+ if (field_index >= cols->n_bits || !bitmap_is_set(cols, field_index))
+ {
Field *const f= *field_ptr;
if ((f->flags & NO_DEFAULT_VALUE_FLAG) &&
(f->real_type() != MYSQL_TYPE_ENUM))
ER(ER_NO_DEFAULT_FOR_FIELD),
f->field_name);
}
+ else if (f->has_insert_default_function())
+ {
+ f->set_default();
+ }
}
}
+ /* set the write_set back to original*/
+ table->column_bitmaps_set_no_signal(table->read_set,
+ old_write_set);
+
DBUG_RETURN(0);
}
extern "C"
void thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, mysql_mutex_t *mutex,
- const PSI_stage_info *stage, PSI_stage_info *old_stage)
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line)
{
if (!thd)
thd= current_thd;
- return thd->ENTER_COND(cond, mutex, stage, old_stage);
+ return thd->enter_cond(cond, mutex, stage, old_stage,
+ src_function, src_file, src_line);
}
extern "C"
-void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage)
+void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line)
{
if (!thd)
thd= current_thd;
- thd->EXIT_COND(stage);
+ thd->exit_cond(stage, src_function, src_file, src_line);
return;
}
return &thd->wsrep_ws_handle;
}
-extern "C"void wsrep_thd_LOCK(THD *thd)
+extern "C" void wsrep_thd_LOCK(THD *thd)
{
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
}
-extern "C"void wsrep_thd_UNLOCK(THD *thd)
+extern "C" void wsrep_thd_UNLOCK(THD *thd)
{
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
wsrep_TOI_pre_query_len = 0;
wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
wsrep_affected_rows = 0;
+ wsrep_replicate_GTID = false;
#endif
/* Call to init() below requires fully initialized Open_tables_state. */
reset_open_tables_state();
thd->lex->current_select == 0 if lex structure is not inited
(not query command (COM_QUERY))
*/
+#ifdef WITH_WSREP
+ if (lex->current_select &&
+ lex->current_select->no_error && !is_fatal_error &&
+ (!(wsrep_conflict_state == ABORTED ||
+ wsrep_conflict_state == MUST_ABORT)))
+#else
if (lex->current_select &&
lex->current_select->no_error && !is_fatal_error)
+#endif
{
DBUG_PRINT("error",
("Error converted to warning: current_select: no_error %d "
wsrep_TOI_pre_query_len = 0;
wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
wsrep_affected_rows = 0;
+ wsrep_replicate_GTID = false;
#endif
binlog_row_event_extra_data= 0;
if (!thd_table->needs_reopen())
#ifdef WITH_WSREP
{
- signalled|= mysql_lock_abort_for_thread(this, thd_table);
- if (this && WSREP(this) && wsrep_thd_is_BF((void *)this, FALSE))
- {
- WSREP_DEBUG("remove_table_from_cache: %llu",
- (unsigned long long) this->real_id);
- wsrep_abort_thd((void *)this, (void *)in_use, FALSE);
- }
+ signalled|= mysql_lock_abort_for_thread(this, thd_table);
+ if (WSREP_NNULL(this) && wsrep_thd_is_BF((void *)this, FALSE))
+ {
+ WSREP_DEBUG("remove_table_from_cache: %llu",
+ (unsigned long long) this->real_id);
+ wsrep_abort_thd((void *)this, (void *)in_use, FALSE);
+ }
}
#else
signalled|= mysql_lock_abort_for_thread(this, thd_table);
const char *calling_func,
const char *calling_file,
const unsigned int calling_line);
-
+extern "C"
+void thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, mysql_mutex_t *mutex,
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line);
+
+extern "C"
+void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line);
+
#define THD_STAGE_INFO(thd, stage) \
(thd)->enter_stage(& stage, NULL, __func__, __FILE__, __LINE__)
char *db;
size_t db_length;
struct timeval user_time;
+ longlong row_count_func;
};
#endif
class Reprepare_observer;
Type of locked tables mode.
See comment for THD::locked_tables_mode for complete description.
While adding new enum values add them to the getter method for this enum
- declared below and defined in sql_class.cc as well.
+ declared below and defined in binlog.cc as well.
*/
enum enum_locked_tables_mode
bool wsrep_apply_toi; /* applier processing in TOI */
wsrep_gtid_t wsrep_sync_wait_gtid;
ulong wsrep_affected_rows;
+ bool wsrep_replicate_GTID;
#endif /* WITH_WSREP */
/**
Internal parser state.
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
global_system_variables.character_set_client->csname);
return true;
- }
+ }
thd->variables.character_set_client=
global_system_variables.character_set_client;
thd->variables.collation_connection=
my_net_set_write_timeout(net, connect_timeout);
error= check_connection(thd);
+ MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
thd->protocol->end_statement();
if (error)
bool rc;
lex_start(thd);
rc= login_connection(thd);
- MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
if (rc)
return rc;
set_if_bigger(length,line_start.length());
stack=stack_pos=(int*) sql_alloc(sizeof(int)*length);
- if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(MY_WME))))
- error= true; /* purecov: inspected */
+ if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(0))))
+ error=1; /* purecov: inspected */
else
{
end_of_buff=buffer+buff_length;
}
}
#ifdef USE_MB
- uint ml= my_mbcharlen(read_charset, chr);
- if (ml == 0)
- {
- *to= '\0';
- my_error(ER_INVALID_CHARACTER_STRING, MYF(0),
- read_charset->csname, buffer);
- error= true;
- return 1;
- }
+ if (my_mbcharlen(read_charset, chr) > 1 &&
+ to + my_mbcharlen(read_charset, chr) <= end_of_buff)
+ {
+ uchar* p= to;
+ int ml, i;
+ *to++ = chr;
- if (ml > 1 &&
- to + ml <= end_of_buff)
- {
- uchar* p= to;
- *to++ = chr;
+ ml= my_mbcharlen(read_charset, chr);
- for (uint i= 1; i < ml; i++)
+ for (i= 1; i < ml; i++)
+ {
+ chr= GET;
+ if (chr == my_b_EOF)
{
- chr= GET;
- if (chr == my_b_EOF)
- {
- /*
- Need to back up the bytes already ready from illformed
- multi-byte char
- */
- to-= i;
- goto found_eof;
- }
- *to++ = chr;
+ /*
+ Need to back up the bytes already ready from illformed
+ multi-byte char
+ */
+ to-= i;
+ goto found_eof;
}
- if (my_ismbchar(read_charset,
+ *to++ = chr;
+ }
+ if (my_ismbchar(read_charset,
(const char *)p,
(const char *)to))
- continue;
- for (uint i= 0; i < ml; i++)
- PUSH(*--to);
- chr= GET;
- }
- else if (ml > 1)
- {
- // Buffer is too small, exit while loop, and reallocate.
- PUSH(chr);
- break;
- }
+ continue;
+ for (i= 0; i < ml; i++)
+ PUSH(*--to);
+ chr= GET;
+ }
#endif
*to++ = (uchar) chr;
}
for (chr= GET; my_tospace(chr) != delim && chr != my_b_EOF;)
{
#ifdef USE_MB
- uint ml= my_mbcharlen(read_charset, chr);
- if (ml == 0)
- {
- chr= my_b_EOF;
- val->length(0);
- return chr;
- }
-
- if (ml > 1)
+ if (my_mbcharlen(read_charset, chr) > 1)
{
DBUG_PRINT("read_xml",("multi byte"));
int i, ml= my_mbcharlen(read_charset, chr);
table_map *cached_eq_ref_tables, table_map
*eq_ref_tables);
+static bool can_switch_from_ref_to_range(THD *thd, JOIN_TAB *tab);
/**
global select optimisation.
COND_EQUAL cond_equal;
cond_equal.upper_levels= inherited;
+ if (check_stack_overrun(thd, STACK_MIN_SIZE, NULL))
+ return cond;
+
if (cond->type() == Item::COND_ITEM)
{
List<Item> eq_list;
bool use_quick_range=0;
Item *tmp;
- /*
- Heuristic: Switch from 'ref' to 'range' access if 'range'
- access can utilize more keyparts than 'ref' access. Conditions
- for doing switching:
-
- 1) Current decision is to use 'ref' access
- 2) 'ref' access depends on a constant, not a value read from a
- table earlier in the join sequence.
-
- Rationale: if 'ref' depends on a value from another table,
- the join condition is not used to limit the rows read by
- 'range' access (that would require dynamic range - 'Range
- checked for each record'). In other words, if 'ref' depends
- on a value from another table, we have a query with
- conditions of the form
-
- this_table.idx_col1 = other_table.col AND <<- used by 'ref'
- this_table.idx_col1 OP <const> AND <<- used by 'range'
- this_table.idx_col2 OP <const> AND ... <<- used by 'range'
-
- and an index on (idx_col1,idx_col2,...). But the fact that
- 'range' access uses more keyparts does not mean that it is
- more selective than 'ref' access because these access types
- utilize different parts of the query condition. We
- therefore trust the cost based choice made by
- best_access_path() instead of forcing a heuristic choice
- here.
- 3) Range access is possible, and it is less costly than
- table/index scan
- 4) 'ref' access and 'range' access uses the same index
- 5) 'range' access uses more keyparts than 'ref' access
-
- @todo: This decision should rather be made in best_access_path()
- */
- if (tab->type == JT_REF && // 1)
- !tab->ref.depend_map && // 2)
- tab->quick && // 3)
- (uint) tab->ref.key == tab->quick->index && // 4)
- tab->ref.key_length < tab->quick->max_used_key_length) // 5)
+ /// See if you need to switch to range access
+ if (tab->type == JT_REF && can_switch_from_ref_to_range(thd, tab))
{
Opt_trace_object wrapper(trace);
Opt_trace_object (trace, "access_type_changed").
return true;
}
+/**
+ Heuristic: Switch from 'ref' to 'range' access if 'range' access can utilize
+ more keyparts than 'ref' access. Conditions for doing switching:
+
+ 1) 'ref' access depends on a constant, not a value read from a table earlier
+ in the join sequence.
+
+ Rationale: if 'ref' depends on a value from another table, the join condition
+ is not used to limit the rows read by 'range' access (that would require
+ dynamic range - 'Range checked for each record'). In other words, if 'ref'
+ depends on a value from another table, we have a query with conditions of
+ the form
+ this_table.idx_col1 = other_table.col AND <<- used by 'ref'
+ this_table.idx_col1 OP <const> AND <<- used by 'range'
+ this_table.idx_col2 OP <const> AND ... <<- used by 'range'
+
+ and an index on (idx_col1,idx_col2,...). But the fact that 'range' access
+ uses more keyparts does not mean that it is more selective than 'ref' access
+ because these access types utilize different parts of the query condition. We
+ therefore trust the cost based choice made by best_access_path() instead of
+ forcing a heuristic choice here.
+
+ 2) Range access is possible, and it is less costly than table/index scan.
+
+ 3a) 'ref' access and 'range' access uses the same index.
+ 3b) 'range' access uses more keyparts than 'ref' access
+
+ OR
+
+ 4) Ref has borrowed the index estimate from range and created a cost
+ estimate (See Optimize_table_order::find_best_ref). This will be a
+ problem if range built it's row estimate using a larger number of key
+ parts than ref. In such a case, shift to range access over the same
+ index. So run the range optimizer with that index as the only choice.
+ (Condition 5 is not relevant here since it has been tested in
+ find_best_ref.)
+
+ @param thd THD To re-run range optimizer.
+ @param tab JOIN_TAB To check the above conditions.
+
+ @return true Range is better than ref
+ @return false Ref is better or switch isn't possible
+
+ @todo: This decision should rather be made in best_access_path()
+*/
+static bool can_switch_from_ref_to_range(THD *thd, JOIN_TAB *tab)
+{
+ if (!tab->ref.depend_map && // 1)
+ tab->quick) // 2)
+ {
+ if ((uint) tab->ref.key == tab->quick->index && // 3a)
+ tab->ref.key_length < tab->quick->max_used_key_length) // 3b)
+ return true;
+ else if (tab->dodgy_ref_cost) // 4)
+ {
+ int error;
+ SQL_SELECT *select;
+ JOIN *join= tab->join;
+ select= make_select(tab->table, join->found_const_table_map,
+ join->found_const_table_map,
+ *tab->on_expr_ref ? *tab->on_expr_ref : join->conds,
+ 1, &error);
+
+ if (select)
+ {
+ Opt_trace_context * const trace= &thd->opt_trace;
+ Opt_trace_object trace_wrapper(trace);
+ Opt_trace_array
+ trace_setup_cond(trace,
+ "rerunning_range_optimizer_for_single_index");
+
+ key_map new_ref_key_map;
+ new_ref_key_map.set_bit(tab->position->key->key);
+ bool retcode= false;
+ if (select->test_quick_select(thd, new_ref_key_map, 0,
+ (join->select_options &
+ OPTION_FOUND_ROWS ? HA_POS_ERROR :
+ join->unit->select_limit_cnt),
+ false, // don't force quick range
+ ORDER::ORDER_NOT_RELEVANT) > 0)
+ {
+ delete tab->quick;
+ tab->quick= select->quick;
+ retcode= true;
+ }
+ select->quick= 0;
+ delete select;
+ return retcode;
+ }
+ }
+ }
+ return false;
+}
/**
Check if an expression in ORDER BY or GROUP BY is a duplicate of a
#include "sql_insert.h" // mysql_insert
#include "sql_update.h" // mysql_update, mysql_multi_update
#include "sql_partition.h" // struct partition_info
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "partition_info.h" // has_external_data_or_index_dir
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
#include "sql_db.h" // mysql_change_db, mysql_create_db,
// mysql_rm_db, mysql_upgrade_db,
// mysql_alter_db,
thd->enable_slow_log= TRUE;
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
thd->set_time();
- if (!thd->is_valid_time())
+ if (thd->is_valid_time() == false)
{
/*
- If the time has got past 2038 we need to shut this server down
- We do this by making sure every command is a shutdown and we
- have enough privileges to shut the server down
-
- TODO: remove this when we have full 64 bit my_time_t support
+ If the time has gone past 2038 we need to shutdown the server. But
+ there is possibility of getting invalid time value on some platforms.
+ For example, gettimeofday() might return incorrect value on solaris
+ platform. Hence validating the current time with 5 iterations before
+ initiating the normal server shutdown process because of time getting
+ past 2038.
*/
- thd->security_ctx->master_access|= SHUTDOWN_ACL;
- command= COM_SHUTDOWN;
+ const int max_tries= 5;
+ sql_print_warning("Current time has got past year 2038. Validating current "
+ "time with %d iterations before initiating the normal "
+ "server shutdown process.", max_tries);
+
+ int tries= 0;
+ while (++tries <= max_tries)
+ {
+ thd->set_time();
+ if (thd->is_valid_time() == true)
+ {
+ sql_print_warning("Iteration %d: Obtained valid current time from "
+ "system", tries);
+ break;
+ }
+ sql_print_warning("Iteration %d: Current time obtained from system is "
+ "greater than 2038", tries);
+ }
+ if (tries > max_tries)
+ {
+ /*
+ If the time has got past 2038 we need to shut this server down.
+ We do this by making sure every command is a shutdown and we
+ have enough privileges to shut the server down
+
+ TODO: remove this when we have full 64 bit my_time_t support
+ */
+ sql_print_error("This MySQL server doesn't support dates later than 2038");
+ thd->security_ctx->master_access|= SHUTDOWN_ACL;
+ command= COM_SHUTDOWN;
+ }
}
thd->set_query_id(next_query_id());
inc_thread_running();
thd->reset_query();
thd->set_command(COM_SLEEP);
+ thd->proc_info= 0;
/* Performance Schema Interface instrumentation, end */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
copy.
*/
Alter_info alter_info(lex->alter_info, thd->mem_root);
-
if (thd->is_fatal_error)
{
/* If out of memory when creating a copy of alter_info. */
goto end_with_restore_list;
}
+ if (((lex->create_info.used_fields & HA_CREATE_USED_DATADIR) != 0 ||
+ (lex->create_info.used_fields & HA_CREATE_USED_INDEXDIR) != 0) &&
+ check_access(thd, FILE_ACL, NULL, NULL, NULL, FALSE, FALSE))
+ {
+ res= 1;
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "FILE");
+ goto end_with_restore_list;
+ }
+
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
partition_info *part_info= thd->lex->part_info;
+ if (part_info != NULL && has_external_data_or_index_dir(*part_info) &&
+ check_access(thd, FILE_ACL, NULL, NULL, NULL, FALSE, FALSE))
+ {
+ res= -1;
+ goto end_with_restore_list;
+ }
if (part_info && !(part_info= thd->lex->part_info->get_clone(true)))
{
res= -1;
key_part_map const_part= 0;
/* The or-null keypart in ref-or-null access: */
key_part_map ref_or_null_part= 0;
+ /// Set dodgy_ref_cost only if that index is chosen for ref access.
+ bool is_dodgy= false;
/* Calculate how many key segments of the current key we can use */
Key_use *const start_key= keyuse;
if (!found_ref && table->quick_keys.is_set(key) && // (1)
table->quick_key_parts[key] > max_key_part && // (2)
records < (double)table->quick_rows[key]) // (3)
+ {
records= (double)table->quick_rows[key];
+ is_dodgy= true;
+ }
tmp= records;
}
}
}
done_with_index:
- trace_access_idx.add("chosen", best_key == start_key);
+ bool chosen= (best_key == start_key);
+ trace_access_idx.add("chosen", chosen);
+ if (chosen)
+ s->dodgy_ref_cost= is_dodgy;
} /* for each key */
records= best_records;
}
#ifndef SQL_SELECT_INCLUDED
#define SQL_SELECT_INCLUDED
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/** TRUE <=> remove duplicates on this table. */
bool distinct;
+ /**
+ Setting this flag means ref is using lesser number of key parts than range
+ and it borrows range's row estimate.
+ */
+ bool dodgy_ref_cost;
+
/** Clean up associated table after query execution, including resources */
void cleanup();
inline bool is_using_loose_index_scan()
ref_array(NULL),
send_records(0),
having(NULL),
- distinct(false)
+ distinct(false),
+ dodgy_ref_cost(false)
{
/**
@todo Add constructor to READ_RECORD.
HA_CREATE_INFO *create_info);
static int
-mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
+mysql_prepare_create_table(THD *thd, const char *error_schema_name,
+ const char *error_table_name,
+ HA_CREATE_INFO *create_info,
Alter_info *alter_info,
bool tmp_table,
uint *db_options,
strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
if (flags & WFRM_WRITE_SHADOW)
{
- if (mysql_prepare_create_table(lpt->thd, lpt->create_info,
+ if (mysql_prepare_create_table(lpt->thd, lpt->db,
+ lpt->table_name,
+ lpt->create_info,
lpt->alter_info,
/*tmp_table*/ 1,
&lpt->db_options,
/**
Check if there is a duplicate key. Report a warning for every duplicate key.
- @param thd Thread context.
- @param key Key to be checked.
- @param key_info Key meta-data info.
- @param alter_info List of columns and indexes to create.
+ @param thd Thread context.
+ @param error_schema_name Schema name of the table used for error reporting.
+ @param error_table_name Table name used for error reporting.
+ @param key Key to be checked.
+ @param key_info Key meta-data info.
+ @param alter_info List of columns and indexes to create.
*/
-static void check_duplicate_key(THD *thd,
+static void check_duplicate_key(THD *thd, const char *error_schema_name,
+ const char *error_table_name,
Key *key, KEY *key_info,
Alter_info *alter_info)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DUP_INDEX, ER(ER_DUP_INDEX),
key_info->name,
- thd->lex->query_tables->db,
- thd->lex->query_tables->table_name);
+ error_schema_name,
+ error_table_name);
break;
}
}
SYNOPSIS
mysql_prepare_create_table()
thd Thread object.
+ error_schema_name Schema name of the table to create/alter,only
+ used for error reporting.
+ error_table_name Name of table to create/alter, only used for
+ error reporting.
create_info Create information (like MAX_ROWS).
alter_info List of columns and indexes to create
tmp_table If a temporary table is to be created.
*/
static int
-mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
+mysql_prepare_create_table(THD *thd, const char *error_schema_name,
+ const char *error_table_name,
+ HA_CREATE_INFO *create_info,
Alter_info *alter_info,
bool tmp_table,
uint *db_options,
else
key_info->flags|= HA_PACK_KEY;
}
- /* Check if the key segment is partial, set the key flag accordingly */
- if (key_part_length != sql_field->key_length)
- key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;
+ /*
+ Check if the key segment is partial, set the key flag
+ accordingly. The key segment for a POINT column is NOT considered
+ partial if key_length==MAX_LEN_GEOM_POINT_FIELD.
+ */
+ if (key_part_length != sql_field->key_length &&
+ !(sql_field->sql_type == MYSQL_TYPE_GEOMETRY &&
+ sql_field->geom_type == Field::GEOM_POINT &&
+ key_part_length == MAX_LEN_GEOM_POINT_FIELD))
+ {
+ key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;
+ }
key_length+= key_part_length;
key_part_info++;
}
// Check if a duplicate index is defined.
- check_duplicate_key(thd, key, key_info, alter_info);
+ check_duplicate_key(thd, error_schema_name, error_table_name,
+ key, key_info, alter_info);
key_info++;
}
@param thd Thread object
@param db Database
@param table_name Table name
+ @param error_table_name The real table name in case table_name is a temporary
+ table (ALTER). Only used for error messages.
@param path Path to table (i.e. to its .FRM file without
the extension).
@param create_info Create information (like MAX_ROWS)
static
bool create_table_impl(THD *thd,
const char *db, const char *table_name,
+ const char *error_table_name,
const char *path,
HA_CREATE_INFO *create_info,
Alter_info *alter_info,
}
#endif
- if (mysql_prepare_create_table(thd, create_info, alter_info,
+ if (mysql_prepare_create_table(thd, db, error_table_name,
+ create_info, alter_info,
internal_tmp_table,
&db_options, file,
key_info, key_count,
}
}
- return create_table_impl(thd, db, table_name, path, create_info, alter_info,
- false, select_field_count, false, is_trans,
- ¬_used_1, ¬_used_2);
+ return create_table_impl(thd, db, table_name, table_name, path, create_info,
+ alter_info, false, select_field_count, false,
+ is_trans, ¬_used_1, ¬_used_2);
}
KEY *key_info_buffer= NULL;
/* Create the prepared information. */
- if (mysql_prepare_create_table(thd, create_info,
- &tmp_alter_info,
+ if (mysql_prepare_create_table(thd, "", "",
+ create_info, &tmp_alter_info,
(table->s->tmp_table != NO_TMP_TABLE),
&db_options,
table->file, &key_info_buffer,
tmp_disable_binlog(thd);
error= create_table_impl(thd, alter_ctx.new_db, alter_ctx.tmp_name,
+ alter_ctx.table_name,
alter_ctx.get_tmp_path(),
create_info, alter_info,
true, 0, true, NULL,
{
partition_info *part_info= Lex->part_info;
partition_element *p_elem= part_info->curr_part_elem;
+ if (check_string_char_length(&$1, "", NAME_CHAR_LEN,
+ system_charset_info, true))
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
p_elem->partition_name= $1.str;
}
;
sub_name:
ident_or_text
- { Lex->part_info->curr_part_elem->partition_name= $1.str; }
+ {
+ if (check_string_char_length(&$1, "", NAME_CHAR_LEN,
+ system_charset_info, true))
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ Lex->part_info->curr_part_elem->partition_name= $1.str;
+ }
;
opt_part_options:
/* Fix key->name and key_part->field */
if (key_parts)
{
- uint primary_key=(uint) (find_type(primary_key_name, &share->keynames,
- FIND_TYPE_NO_PREFIX) - 1);
+ const int pk_off= find_type(primary_key_name, &share->keynames,
+ FIND_TYPE_NO_PREFIX);
+ uint primary_key= (pk_off > 0 ? pk_off-1 : MAX_KEY);
+
longlong ha_option= handler_file->ha_table_flags();
keyinfo= share->key_info;
key_part= keyinfo->key_part;
#ifdef WITH_WSREP
bool mdl_can_safely_rollback_to_savepoint=
(!((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
- && thd->variables.sql_log_bin) ||
+ && thd->variables.sql_log_bin) ||
ha_rollback_to_savepoint_can_release_mdl(thd));
#else
bool mdl_can_safely_rollback_to_savepoint=
int replay_round= 0;
if (thd->get_stmt_da()->is_error()) {
- WSREP_ERROR("commit issue, error: %d %s",
+ WSREP_DEBUG("commit issue, error: %d %s",
thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
}
set to NO_CONFLICT and commit proceeds as usual.
*/
if (thd->wsrep_conflict_state == MUST_ABORT)
- thd->wsrep_conflict_state= NO_CONFLICT;
+ {
+ thd->killed= THD::NOT_KILLED;
+ thd->wsrep_conflict_state= NO_CONFLICT;
+ }
if (thd->wsrep_conflict_state != NO_CONFLICT)
{
DBUG_RETURN(WSREP_TRX_OK);
}
+bool wsrep_replicate_GTID(THD *thd)
+{
+ if (thd->slave_thread)
+ {
+ WSREP_DEBUG("GTID replication");
+ DBUG_ASSERT (WSREP_UNDEFINED_TRX_ID == thd->wsrep_ws_handle.trx_id);
+ (void)wsrep_ws_handle_for_trx(&thd->wsrep_ws_handle, thd->query_id);
+ DBUG_ASSERT (WSREP_UNDEFINED_TRX_ID != thd->wsrep_ws_handle.trx_id);
+ WSREP_DEBUG("slave trx using query ID %lu for replication GTID",
+ thd->wsrep_ws_handle.trx_id);
+ enum wsrep_trx_status rcode= wsrep_run_wsrep_commit(thd, wsrep_hton, true);
+ if (rcode)
+ {
+ /*
+ TODO: should error here cause stopping of MySQL slave?
+ Slave applying was totally filtered out, and fauílure in replicating
+ GTID event, would cause a hole in GTID history in other cluster nodes
+
+ */
+ WSREP_INFO("GTID replication failed: %d", rcode);
+ wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle);
+ thd->wsrep_replicate_GTID= false;
+ my_message(ER_ERROR_DURING_COMMIT,
+ "WSREP GTID replication was interrupted", MYF(0));
+ return true;
+ }
+ wsrep_post_commit(thd, true);
+ }
+ thd->wsrep_replicate_GTID= false;
+
+ return false;
+}
static int wsrep_hton_init(void *p)
{
if (buf) my_free(buf);
wsrep_keys_free(&key_arr);
WSREP_DEBUG("TO BEGIN: %lld, %d",(long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode);
+ thd->wsrep_exec_mode);
}
else if (key_arr.keys_len > 0) {
/* jump to error handler in mysql_execute_command() */
"connection state and retry the query.",
ret, (thd->db ? thd->db : "(null)"), WSREP_QUERY(thd));
my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check "
- "your wsrep connection state and retry the query.");
+ "your wsrep connection state and retry the query.");
if (buf) my_free(buf);
wsrep_keys_free(&key_arr);
return -1;
else {
/* non replicated DDL, affecting temporary tables only */
WSREP_DEBUG("TO isolation skipped for: %d, sql: %s."
- "Only temporary tables affected.",
- ret, WSREP_QUERY(thd));
+ "Only temporary tables affected.",
+ ret, WSREP_QUERY(thd));
return 1;
}
return 0;
wsrep_provider && \
strcmp(wsrep_provider, WSREP_NONE))
+/* use xxxxxx_NNULL macros when thd pointer is guaranteed to be non-null to
+ * avoid compiler warnings (GCC 6 and later) */
+#define WSREP_NNULL(thd) \
+ (WSREP_ON && wsrep && thd->variables.wsrep_on)
+
#define WSREP(thd) \
- (WSREP_ON && wsrep && (thd && thd->variables.wsrep_on))
+ (thd && WSREP_NNULL(thd))
#define WSREP_CLIENT(thd) \
- (WSREP(thd) && thd->wsrep_client_thread)
+ (WSREP(thd) && thd->wsrep_client_thread)
+
+#define WSREP_EMULATE_BINLOG_NNULL(thd) \
+ (WSREP_NNULL(thd) && wsrep_emulate_bin_log)
#define WSREP_EMULATE_BINLOG(thd) \
(WSREP(thd) && wsrep_emulate_bin_log)
void wsrep_init_sidno(const wsrep_uuid_t&);
bool wsrep_node_is_donor();
bool wsrep_node_is_synced();
+bool wsrep_replicate_GTID(THD* thd);
+
#endif /* WSREP_MYSQLD_H */
"%s",
host, port, mysqld_port, mysqld_unix_port,
wsrep_defaults_file, uuid_str,
- (long long)seqno, bypass ? " "WSREP_SST_OPT_BYPASS : "");
+ (long long)seqno, bypass ? " " WSREP_SST_OPT_BYPASS : "");
if (ret < 0 || ret >= cmd_len)
{
wsrep_defaults_file, wsrep_defaults_group_suffix,
binlog_opt, binlog_opt_val,
uuid, (long long) seqno,
- bypass ? " "WSREP_SST_OPT_BYPASS : "");
+ bypass ? " " WSREP_SST_OPT_BYPASS : "");
my_free(binlog_opt_val);
if (ret < 0 || ret >= cmd_len)
/* must have (&thd->LOCK_wsrep_thd) */
void wsrep_client_rollback(THD *thd)
{
- WSREP_DEBUG("client rollback due to BF abort for (%ld), query: %s",
- thd->thread_id, WSREP_QUERY(thd));
+ WSREP_DEBUG("client rollback due to BF abort for (%ld %lld), query: %s",
+ thd->thread_id, thd->query_id, WSREP_QUERY(thd));
my_atomic_add64(&wsrep_bf_aborts_counter, 1);
thd->wsrep_conflict_state= ABORTING;
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
- trans_rollback(thd);
+ if (trans_rollback(thd))
+ {
+ WSREP_WARN("client rollback failed for: %lu %lld, conf: %d",
+ thd->thread_id, thd->query_id, thd->wsrep_conflict_state);
+ }
if (thd->locked_tables_mode && thd->lock)
{
new Format_description_log_event(BINLOG_VERSION));
return (rli);
-
-#ifdef REMOVED
- Rpl_info_handler* handler_src= NULL;
- Rpl_info_handler* handler_dest= NULL;
- ulong *key_info_idx= NULL;
- const char *msg= "Failed to allocate memory for the relay log info "
- "structure";
-
- DBUG_ENTER("Rpl_info_factory::create_rli");
-
- if (!(rli= new Relay_log_info(false
-#ifdef HAVE_PSI_INTERFACE
- ,&key_relay_log_info_run_lock,
- &key_relay_log_info_data_lock,
- &key_relay_log_info_sleep_lock,
- &key_relay_log_info_data_cond,
- &key_relay_log_info_start_cond,
- &key_relay_log_info_stop_cond,
- &key_relay_log_info_sleep_cond
-#endif /* HAVE_PSI_INTERFACE */
- )))
- goto err;
-
- if (!(key_info_idx= new ulong[NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR]))
- goto err;
- key_info_idx[0]= server_id;
- rli->set_idx_info(key_info_idx, NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR);
-
- if(Rpl_info_factory::init_rli_repositories(rli, rli_option, &handler_src,
- &handler_dest, &msg))
- goto err;
-
- if (Rpl_info_factory::decide_repository(rli, rli_option, &handler_src,
- &handler_dest, &msg))
- goto err;
-
- DBUG_RETURN(rli);
-err:
- delete handler_src;
- delete handler_dest;
- delete []key_info_idx;
- if (rli)
- {
- /*
- The handler was previously deleted so we need to remove
- any reference to it.
- */
- rli->set_idx_info(NULL, 0);
- rli->set_rpl_info_handler(NULL);
- rli->set_rpl_info_type(INVALID_INFO_REPOSITORY);
- delete rli;
- }
- WSREP_ERROR("Error creating relay log info: %s.", msg);
- DBUG_RETURN(NULL);
-#endif /* REMOVED */
}
static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
thd->reset_db(NULL, 0);
shadow->user_time = thd->user_time;
+ shadow->row_count_func= thd->get_row_count_func();
}
static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
thd->variables.tx_isolation = shadow->tx_isolation;
thd->reset_db(shadow->db, shadow->db_length);
thd->user_time = shadow->user_time;
+ thd->set_row_count_func(shadow->row_count_func);
}
void wsrep_replay_transaction(THD *thd)
WSREP_WARN("dangling observer in replay transaction: (thr %lu %lld %s)",
thd->thread_id, thd->query_id, thd->query());
}
+
+ struct da_shadow
+ {
+ enum Diagnostics_area::enum_diagnostics_status status;
+ ulonglong affected_rows;
+ ulonglong last_insert_id;
+ char message[MYSQL_ERRMSG_SIZE];
+ };
+ struct da_shadow da_status;
+ da_status.status= thd->get_stmt_da()->status();
+ if (da_status.status == Diagnostics_area::DA_OK)
+ {
+ da_status.affected_rows= thd->get_stmt_da()->affected_rows();
+ da_status.last_insert_id= thd->get_stmt_da()->last_insert_id();
+ strmake(da_status.message,
+ thd->get_stmt_da()->message(),
+ sizeof(da_status.message)-1);
+ }
+
thd->get_stmt_da()->reset_diagnostics_area();
thd->wsrep_conflict_state= REPLAYING;
}
else
{
- my_ok(thd);
+ if (da_status.status == Diagnostics_area::DA_OK)
+ {
+ my_ok(thd,
+ da_status.affected_rows,
+ da_status.last_insert_id,
+ da_status.message);
+ }
+ else
+ {
+ my_ok(thd);
+ }
}
break;
case WSREP_TRX_FAIL:
aborting->store_globals();
mysql_mutex_lock(&aborting->LOCK_wsrep_thd);
+
+ /* prepare THD for rollback processing */
+ mysql_reset_thd_for_next_command(aborting);
+ aborting->lex->sql_command= SQLCOM_ROLLBACK;
+
wsrep_client_rollback(aborting);
WSREP_DEBUG("WSREP rollbacker aborted thd: (%lu %llu)",
aborting->thread_id, (long long)aborting->real_id);
{
THD* thd = (THD*)thd_ptr;
if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
-
+
status = ((thd->wsrep_exec_mode == REPL_RECV) ||
- (thd->wsrep_exec_mode == TOTAL_ORDER));
+ (thd->wsrep_exec_mode == TOTAL_ORDER));
if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
return status;
{
THD* thd = (THD*)thd_ptr;
if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
-
+
status = ((thd->wsrep_exec_mode == REPL_RECV) ||
- (thd->wsrep_exec_mode == TOTAL_ORDER) ||
- (thd->wsrep_exec_mode == LOCAL_COMMIT));
+ (thd->wsrep_exec_mode == TOTAL_ORDER) ||
+ (thd->wsrep_exec_mode == LOCAL_COMMIT));
if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
return status;
/*****************************************************************************
-Copyright (c) 2009, 2015, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2009, 2016, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
them away) which brings non-determinism. We skip only
leaf-level delete marks because delete marks on
non-leaf level do not make sense. */
- if (level == 0 &&
+
+ if (level == 0 && srv_stats_include_delete_marked? 0:
rec_get_deleted_flag(
rec,
page_is_comp(btr_pcur_get_page(&pcur)))) {
the given page and count the number of
distinct ones, also ignore delete marked
records */
- QUIT_ON_FIRST_NON_BORING/* quit when the first record that differs
+ QUIT_ON_FIRST_NON_BORING,/* quit when the first record that differs
from its right neighbor is found */
+ COUNT_ALL_NON_BORING_INCLUDE_DEL_MARKED/* scan all records on
+ the given page and count the number of
+ distinct ones, include delete marked
+ records */
};
/* @} */
offsets_rec = dict_stats_scan_page(
&rec, offsets1, offsets2, index, page, n_prefix,
+ srv_stats_include_delete_marked ?
+ COUNT_ALL_NON_BORING_INCLUDE_DEL_MARKED:
COUNT_ALL_NON_BORING_AND_SKIP_DEL_MARKED, n_diff,
n_external_pages);
fts_zip_t* zip, /*!< in: Zip state + data */
fts_string_t* word) /*!< out: uncompressed word */
{
-#ifdef UNIV_DEBUG
- ulint i;
-#endif
short len = 0;
void* null = NULL;
byte* ptr = word->f_str;
}
}
-#ifdef UNIV_DEBUG
/* All blocks must be freed at end of inflate. */
if (zip->status != Z_OK) {
- for (i = 0; i < ib_vector_size(zip->blocks); ++i) {
+ for (ulint i = 0; i < ib_vector_size(zip->blocks); ++i) {
if (ib_vector_getp(zip->blocks, i)) {
ut_free(ib_vector_getp(zip->blocks, i));
ib_vector_set(zip->blocks, i, &null);
if (ptr != NULL) {
ut_ad(word->f_len == strlen((char*) ptr));
}
-#endif /* UNIV_DEBUG */
return(zip->status == Z_OK || zip->status == Z_STREAM_END ? ptr : NULL);
}
ulonglong col_max_value = innobase_get_int_col_max_value(
table->next_number_field);
+ /** The following logic is needed to avoid duplicate key error
+ for autoincrement column.
+
+ (1) InnoDB gives the current autoincrement value with respect
+ to increment and offset value.
+
+ (2) Basically it does compute_next_insert_id() logic inside InnoDB
+ to avoid the current auto increment value changed by handler layer.
+
+ (3) It is restricted only for insert operations. */
+
+ if (increment > 1 && thd_sql_command(user_thd) != SQLCOM_ALTER_TABLE
+ && autoinc < col_max_value) {
+
+ ulonglong prev_auto_inc = autoinc;
+
+ autoinc = ((autoinc - 1) + increment - offset)/ increment;
+
+ autoinc = autoinc * increment + offset;
+
+ /* If autoinc exceeds the col_max_value then reset
+ to old autoinc value. Because in case of non-strict
+ sql mode, boundary value is not considered as error. */
+
+ if (autoinc >= col_max_value) {
+ autoinc = prev_auto_inc;
+ }
+
+ ut_ad(autoinc > 0);
+ }
+
/* Called for the first time ? */
if (trx->n_autoinc_rows == 0) {
"Disable with --skip-innodb-doublewrite.",
NULL, NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(stats_include_delete_marked,
+ srv_stats_include_delete_marked,
+ PLUGIN_VAR_OPCMDARG,
+ "Scan delete marked records for persistent stat",
+ NULL, NULL, FALSE);
+
static MYSQL_SYSVAR_ULONG(io_capacity, srv_io_capacity,
PLUGIN_VAR_RQCMDARG,
"Number of IOPs the server can do. Tunes the background IO rate",
MYSQL_SYSVAR(data_file_path),
MYSQL_SYSVAR(data_home_dir),
MYSQL_SYSVAR(doublewrite),
+ MYSQL_SYSVAR(stats_include_delete_marked),
MYSQL_SYSVAR(api_enable_binlog),
MYSQL_SYSVAR(api_enable_mdl),
MYSQL_SYSVAR(api_disable_rowlock),
extern "C" void wsrep_thd_set_trx_to_replay(THD *thd, uint64 trx_id);
-extern "C"void wsrep_thd_LOCK(THD *thd);
-extern "C"void wsrep_thd_UNLOCK(THD *thd);
+extern "C" void wsrep_thd_LOCK(THD *thd);
+extern "C" void wsrep_thd_UNLOCK(THD *thd);
extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd);
extern "C" time_t wsrep_thd_query_start(THD *thd);
extern "C" my_thread_id wsrep_thd_thread_id(THD *thd);
return(FTS_NOT_EXIST_DOC_ID_INDEX);
}
+
/*******************************************************************//**
Create an index table where indexes are ordered as follows:
(only prefix/part of the column is indexed), MySQL will treat the
index as a PRIMARY KEY unless the table already has one. */
- if (n_add > 0 && !new_primary && got_default_clust
- && (key_info[*add].flags & HA_NOSAME)
- && !(key_info[*add].flags & HA_KEY_HAS_PART_KEY_SEG)) {
- uint key_part = key_info[*add].user_defined_key_parts;
-
- new_primary = true;
+ ut_ad(altered_table->s->primary_key == 0
+ || altered_table->s->primary_key == MAX_KEY);
- while (key_part--) {
- const uint maybe_null
- = key_info[*add].key_part[key_part].key_type
- & FIELDFLAG_MAYBE_NULL;
- DBUG_ASSERT(!maybe_null
- == !key_info[*add].key_part[key_part].
- field->real_maybe_null());
-
- if (maybe_null) {
- new_primary = false;
- break;
- }
- }
+ if (got_default_clust && !new_primary) {
+ new_primary = (altered_table->s->primary_key != MAX_KEY);
}
const bool rebuild = new_primary || add_fts_doc_id
ulint primary_key_number;
if (new_primary) {
- DBUG_ASSERT(n_add > 0);
- primary_key_number = *add;
+ if (n_add == 0) {
+ DBUG_ASSERT(got_default_clust);
+ DBUG_ASSERT(altered_table->s->primary_key
+ == 0);
+ primary_key_number = 0;
+ } else {
+ primary_key_number = *add;
+ }
} else if (got_default_clust) {
/* Create the GEN_CLUST_INDEX */
index_def_t* index = indexdef++;
ctx->add_cols = add_cols;
} else {
DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info));
+ DBUG_ASSERT(old_table->s->primary_key
+ == altered_table->s->primary_key);
if (!ctx->new_table->fts
&& innobase_fulltext_exist(altered_table)) {
add_fts_doc_id_idx));
}
+/** Get the name of an erroneous key.
+@param[in] error_key_num InnoDB number of the erroneus key
+@param[in] ha_alter_info changes that were being performed
+@param[in] table InnoDB table
+@return the name of the erroneous key */
+static
+const char*
+get_error_key_name(
+ ulint error_key_num,
+ const Alter_inplace_info* ha_alter_info,
+ const dict_table_t* table)
+{
+ if (error_key_num == ULINT_UNDEFINED) {
+ return(FTS_DOC_ID_INDEX_NAME);
+ } else if (ha_alter_info->key_count == 0) {
+ return(dict_table_get_first_index(table)->name);
+ } else {
+ return(ha_alter_info->key_info_buffer[error_key_num].name);
+ }
+}
+
/** Alter the table structure in-place with operations
specified using Alter_inplace_info.
The level of concurrency allowed during this operation depends
case DB_ONLINE_LOG_TOO_BIG:
DBUG_ASSERT(ctx->online);
my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
- (prebuilt->trx->error_key_num == ULINT_UNDEFINED)
- ? FTS_DOC_ID_INDEX_NAME
- : ha_alter_info->key_info_buffer[
- prebuilt->trx->error_key_num].name);
+ get_error_key_name(prebuilt->trx->error_key_num,
+ ha_alter_info, prebuilt->table));
break;
case DB_INDEX_CORRUPT:
my_error(ER_INDEX_CORRUPT, MYF(0),
- (prebuilt->trx->error_key_num == ULINT_UNDEFINED)
- ? FTS_DOC_ID_INDEX_NAME
- : ha_alter_info->key_info_buffer[
- prebuilt->trx->error_key_num].name);
+ get_error_key_name(prebuilt->trx->error_key_num,
+ ha_alter_info, prebuilt->table));
break;
default:
my_error_innodb(error,
"Foreign key constraints for table '%s'"
" are loaded with charset check off",
user_table->name);
-
}
}
DBUG_RETURN(true);
case DB_ONLINE_LOG_TOO_BIG:
my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
- ha_alter_info->key_info_buffer[0].name);
+ get_error_key_name(err_key, ha_alter_info,
+ rebuilt_table));
DBUG_RETURN(true);
case DB_INDEX_CORRUPT:
my_error(ER_INDEX_CORRUPT, MYF(0),
- (err_key == ULINT_UNDEFINED)
- ? FTS_DOC_ID_INDEX_NAME
- : ha_alter_info->key_info_buffer[err_key]
- .name);
+ get_error_key_name(err_key, ha_alter_info,
+ rebuilt_table));
DBUG_RETURN(true);
default:
my_error_innodb(error, table_name, user_table->flags);
os_thread_id_t* thread_id); /*!< out: id of the created
thread, or NULL */
+/** Waits until the specified thread completes and joins it.
+Its return value is ignored.
+@param[in,out] thread thread to join */
+UNIV_INTERN
+void
+os_thread_join(
+ os_thread_t thread);
+
/*****************************************************************//**
Exits the current thread. */
UNIV_INTERN
void
os_thread_exit(
/*===========*/
- void* exit_value) /*!< in: exit value; in Windows this void*
+ void* exit_value, /*!< in: exit value; in Windows this void*
is cast as a DWORD */
+ bool detach = true) /*!< in: if true, the thread will be detached
+ right before exiting. If false, another thread
+ is responsible for joining this thread. */
UNIV_COLD MY_ATTRIBUTE((noreturn));
/*****************************************************************//**
Returns the thread identifier of current thread.
extern my_bool srv_stats_persistent;
extern unsigned long long srv_stats_persistent_sample_pages;
extern my_bool srv_stats_auto_recalc;
+extern my_bool srv_stats_include_delete_marked;
extern ibool srv_use_doublewrite_buf;
extern ulong srv_doublewrite_batch_size;
or 0 to use the default max length */
MY_ATTRIBUTE((nonnull));
+#ifdef WITH_WSREP
+/**********************************************************************//**
+Prints info about a transaction.
+Transaction information may be retrieved without having trx_sys->mutex acquired
+so it may not be completely accurate. The caller must own lock_sys->mutex
+and the trx must have some locks to make sure that it does not escape
+without locking lock_sys->mutex. */
+UNIV_INTERN
+void
+wsrep_trx_print_locking(
+/*==============*/
+ FILE* f, /*!< in: output stream */
+ const trx_t* trx, /*!< in: transaction */
+ ulint max_query_len) /*!< in: max query length to print,
+ or 0 to use the default max length */
+ MY_ATTRIBUTE((nonnull));
+#endif /* WITH_WSREP */
/**********************************************************************//**
Prints info about a transaction.
Acquires and releases lock_sys->mutex and trx_sys->mutex. */
is in the queue*/
} else if (lock->trx != trx) {
if (wsrep_log_conflicts) {
- mutex_enter(&trx_sys->mutex);
if (bf_this)
fputs("\n*** Priority TRANSACTION:\n",
stderr);
else
fputs("\n*** Victim TRANSACTION:\n",
stderr);
- trx_print_latched(stderr, trx, 3000);
+ wsrep_trx_print_locking(stderr, trx, 3000);
if (bf_other)
fputs("\n*** Priority TRANSACTION:\n",
else
fputs("\n*** Victim TRANSACTION:\n",
stderr);
- trx_print_latched(stderr, lock->trx, 3000);
+ wsrep_trx_print_locking(stderr, lock->trx, 3000);
- mutex_exit(&trx_sys->mutex);
fputs("*** WAITING FOR THIS LOCK TO BE GRANTED:\n",
stderr);
/*****************************************************************************
-Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
if (flag < 0x80UL) {
*val = flag;
return(ptr + 1);
+ }
+
+ /* Workaround GCC bug
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77673:
+ the compiler moves mach_read_from_4 right to the beginning of the
+ function, causing and out-of-bounds read if we are reading a short
+ integer close to the end of buffer. */
+#if defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__clang__)
+#define DEPLOY_FENCE
+#endif
+
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
- } else if (flag < 0xC0UL) {
+ if (flag < 0xC0UL) {
if (end_ptr < ptr + 2) {
return(NULL);
}
*val = mach_read_from_2(ptr) & 0x7FFFUL;
return(ptr + 2);
+ }
+
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
- } else if (flag < 0xE0UL) {
+ if (flag < 0xE0UL) {
if (end_ptr < ptr + 3) {
return(NULL);
}
*val = mach_read_from_3(ptr) & 0x3FFFFFUL;
return(ptr + 3);
- } else if (flag < 0xF0UL) {
+ }
+
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
+
+ if (flag < 0xF0UL) {
if (end_ptr < ptr + 4) {
return(NULL);
}
*val = mach_read_from_4(ptr) & 0x1FFFFFFFUL;
return(ptr + 4);
- } else {
- ut_ad(flag == 0xF0UL);
+ }
- if (end_ptr < ptr + 5) {
- return(NULL);
- }
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
+
+#undef DEPLOY_FENCE
+
+ ut_ad(flag == 0xF0UL);
- *val = mach_read_from_4(ptr + 1);
- return(ptr + 5);
+ if (end_ptr < ptr + 5) {
+ return(NULL);
}
+
+ *val = mach_read_from_4(ptr + 1);
+ return(ptr + 5);
}
/*****************************************************************************
-Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
#endif
}
+/** Waits until the specified thread completes and joins it.
+Its return value is ignored.
+@param[in,out] thread thread to join */
+UNIV_INTERN
+void
+os_thread_join(
+ os_thread_t thread)
+{
+#ifdef __WIN__
+ /* Do nothing. */
+#else
+#ifdef UNIV_DEBUG
+ const int ret =
+#endif /* UNIV_DEBUG */
+ pthread_join(thread, NULL);
+
+ /* Waiting on already-quit threads is allowed. */
+ ut_ad(ret == 0 || ret == ESRCH);
+#endif /* __WIN__ */
+}
+
/*****************************************************************//**
Exits the current thread. */
UNIV_INTERN
void
os_thread_exit(
/*===========*/
- void* exit_value) /*!< in: exit value; in Windows this void*
+ void* exit_value, /*!< in: exit value; in Windows this void*
is cast as a DWORD */
+ bool detach) /*!< in: if true, the thread will be detached
+ right before exiting. If false, another thread
+ is responsible for joining this thread. */
{
#ifdef UNIV_DEBUG_THREAD_CREATION
fprintf(stderr, "Thread exits, id %lu\n",
#ifdef __WIN__
ExitThread((DWORD) exit_value);
#else
- pthread_detach(pthread_self());
+ if (detach) {
+ pthread_detach(pthread_self());
+ }
pthread_exit(exit_value);
#endif
}
CloseHandle(psort_info->thread_hdl);
#endif /*__WIN__ */
- os_thread_exit(NULL);
+ os_thread_exit(NULL, false);
OS_THREAD_DUMMY_RETURN;
}
" exited when creating FTS"
" index '%s'",
indexes[i]->name);
+ } else {
+ for (j = 0; j < FTS_NUM_AUX_INDEX;
+ j++) {
+
+ os_thread_join(merge_info[j]
+ .thread_hdl);
+ }
}
} else {
/* This cannot report duplicates; an
row_ins_step(thr);
+ DEBUG_SYNC_C("ib_after_row_insert_step");
+
err = trx->error_state;
if (err != DB_SUCCESS) {
table/index are not found in the innodb database */
UNIV_INTERN unsigned long long srv_stats_transient_sample_pages = 8;
UNIV_INTERN my_bool srv_stats_persistent = TRUE;
+UNIV_INTERN my_bool srv_stats_include_delete_marked = FALSE;
UNIV_INTERN unsigned long long srv_stats_persistent_sample_pages = 20;
UNIV_INTERN my_bool srv_stats_auto_recalc = TRUE;
mem_heap_get_size(trx->lock.lock_heap));
}
+#ifdef WITH_WSREP
+/**********************************************************************//**
+Prints info about a transaction.
+Transaction information may be retrieved without having trx_sys->mutex acquired
+so it may not be completely accurate. The caller must own lock_sys->mutex
+and the trx must have some locks to make sure that it does not escape
+without locking lock_sys->mutex. */
+UNIV_INTERN
+void
+wsrep_trx_print_locking(
+/*==========*/
+ FILE* f,
+ /*!< in: output stream */
+ const trx_t* trx,
+ /*!< in: transaction */
+ ulint max_query_len)
+ /*!< in: max query length to print,
+ or 0 to use the default max length */
+{
+ ibool newline;
+ const char* op_info;
+
+ ut_ad(lock_mutex_own());
+ ut_ad(trx->lock.trx_locks.count > 0);
+
+ fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id);
+
+ /* trx->state may change since trx_sys->mutex is not required */
+ switch (trx->state) {
+ case TRX_STATE_NOT_STARTED:
+ fputs(", not started", f);
+ goto state_ok;
+ case TRX_STATE_ACTIVE:
+ fprintf(f, ", ACTIVE %lu sec",
+ (ulong) difftime(time(NULL), trx->start_time));
+ goto state_ok;
+ case TRX_STATE_PREPARED:
+ fprintf(f, ", ACTIVE (PREPARED) %lu sec",
+ (ulong) difftime(time(NULL), trx->start_time));
+ goto state_ok;
+ case TRX_STATE_COMMITTED_IN_MEMORY:
+ fputs(", COMMITTED IN MEMORY", f);
+ goto state_ok;
+ }
+ fprintf(f, ", state %lu", (ulong) trx->state);
+ ut_ad(0);
+state_ok:
+
+ /* prevent a race condition */
+ op_info = trx->op_info;
+
+ if (*op_info) {
+ putc(' ', f);
+ fputs(op_info, f);
+ }
+
+ if (trx->is_recovered) {
+ fputs(" recovered trx", f);
+ }
+
+ if (trx->declared_to_be_inside_innodb) {
+ fprintf(f, ", thread declared inside InnoDB %lu",
+ (ulong) trx->n_tickets_to_enter_innodb);
+ }
+
+ putc('\n', f);
+
+ if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) {
+ fprintf(f, "mysql tables in use %lu, locked %lu\n",
+ (ulong) trx->n_mysql_tables_in_use,
+ (ulong) trx->mysql_n_tables_locked);
+ }
+
+ newline = TRUE;
+
+ /* trx->lock.que_state of an ACTIVE transaction may change
+ while we are not holding trx->mutex. We perform a dirty read
+ for performance reasons. */
+
+ switch (trx->lock.que_state) {
+ case TRX_QUE_RUNNING:
+ newline = FALSE; break;
+ case TRX_QUE_LOCK_WAIT:
+ fputs("LOCK WAIT ", f); break;
+ case TRX_QUE_ROLLING_BACK:
+ fputs("ROLLING BACK ", f); break;
+ case TRX_QUE_COMMITTING:
+ fputs("COMMITTING ", f); break;
+ default:
+ fprintf(f, "que state %lu ", (ulong) trx->lock.que_state);
+ }
+
+ if (trx->has_search_latch) {
+ newline = TRUE;
+ fputs(", holds adaptive hash latch", f);
+ }
+
+ if (trx->undo_no != 0) {
+ newline = TRUE;
+ fprintf(f, ", undo log entries " TRX_ID_FMT, trx->undo_no);
+ }
+
+ if (newline) {
+ putc('\n', f);
+ }
+
+ if (trx->mysql_thd != NULL) {
+ innobase_mysql_print_thd(
+ f, trx->mysql_thd, static_cast<uint>(max_query_len));
+ }
+}
+#endif /* WITH_WSREP */
/**********************************************************************//**
Prints info about a transaction.
Acquires and releases lock_sys->mutex and trx_sys->mutex. */
-/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
if (! pfs_table->m_io_enabled)
return NULL;
- PFS_thread *pfs_thread= pfs_table->m_thread_owner;
-
- DBUG_ASSERT(pfs_thread ==
- my_pthread_getspecific_ptr(PFS_thread*, THR_PFS));
+ PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS);
register uint flags;
ulonglong timer_start= 0;
if (! pfs_table->m_lock_enabled)
return NULL;
- PFS_thread *pfs_thread= pfs_table->m_thread_owner;
+ PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS);
PFS_TL_LOCK_TYPE lock_type;
if (flag_thread_instrumentation)
{
- PFS_thread *pfs_thread= pfs_socket->m_thread_owner;
+ /*
+ Do not use pfs_socket->m_thread_owner here,
+ as different threads may use concurrently the same socket,
+ for example during a KILL.
+ */
+ PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS);
if (unlikely(pfs_thread == NULL))
return NULL;
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
}
if (flag_events_waits_history_long)
insert_events_waits_history_long(wait);
thread->m_events_waits_current--;
+
+ DBUG_ASSERT(wait == thread->m_events_waits_current);
}
}
-/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Current index in Stat array where new record is to be inserted.
index 0 is reserved for "all else" case when entire array is full.
*/
-volatile uint32 digest_index;
+volatile uint32 PFS_ALIGNED digest_monotonic_index;
bool digest_full= false;
LF_HASH digest_hash;
*/
digest_max= param->m_digest_sizing;
digest_lost= 0;
- digest_index= 1;
+ PFS_atomic::store_u32(& digest_monotonic_index, 1);
digest_full= false;
if (digest_max == 0)
+ index * pfs_max_digest_length, pfs_max_digest_length);
}
+ /* Set record[0] as allocated. */
+ statements_digest_stat_array[0].m_lock.set_allocated();
+
return 0;
}
memcpy(hash_key.m_schema_name, schema_name, schema_name_length);
int res;
- ulong safe_index;
uint retry_count= 0;
const uint retry_max= 3;
+ size_t safe_index;
+ size_t attempts= 0;
PFS_statements_digest_stat **entry;
PFS_statements_digest_stat *pfs= NULL;
return & pfs->m_stat;
}
- safe_index= PFS_atomic::add_u32(& digest_index, 1);
- if (safe_index >= digest_max)
+ while (++attempts <= digest_max)
{
- /* The digest array is now full. */
- digest_full= true;
- pfs= &statements_digest_stat_array[0];
-
- if (pfs->m_first_seen == 0)
- pfs->m_first_seen= now;
- pfs->m_last_seen= now;
- return & pfs->m_stat;
- }
-
- /* Add a new record in digest stat array. */
- pfs= &statements_digest_stat_array[safe_index];
-
- /* Copy digest hash/LF Hash search key. */
- memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key));
-
- /*
- Copy digest storage to statement_digest_stat_array so that it could be
- used later to generate digest text.
- */
- pfs->m_digest_storage.copy(digest_storage);
-
- pfs->m_first_seen= now;
- pfs->m_last_seen= now;
+ safe_index= PFS_atomic::add_u32(& digest_monotonic_index, 1) % digest_max;
+ if (safe_index == 0)
+ {
+ /* Record [0] is reserved. */
+ safe_index= 1;
+ }
- res= lf_hash_insert(&digest_hash, pins, &pfs);
- if (likely(res == 0))
- {
- return & pfs->m_stat;
- }
+ /* Add a new record in digest stat array. */
+ pfs= &statements_digest_stat_array[safe_index];
- if (res > 0)
- {
- /* Duplicate insert by another thread */
- if (++retry_count > retry_max)
+ if (pfs->m_lock.is_free())
{
- /* Avoid infinite loops */
- digest_lost++;
- return NULL;
+ if (pfs->m_lock.free_to_dirty())
+ {
+ /* Copy digest hash/LF Hash search key. */
+ memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key));
+
+ /*
+ Copy digest storage to statement_digest_stat_array so that it could be
+ used later to generate digest text.
+ */
+ pfs->m_digest_storage.copy(digest_storage);
+
+ pfs->m_first_seen= now;
+ pfs->m_last_seen= now;
+
+ res= lf_hash_insert(&digest_hash, pins, &pfs);
+ if (likely(res == 0))
+ {
+ pfs->m_lock.dirty_to_allocated();
+ return & pfs->m_stat;
+ }
+
+ pfs->m_lock.dirty_to_free();
+
+ if (res > 0)
+ {
+ /* Duplicate insert by another thread */
+ if (++retry_count > retry_max)
+ {
+ /* Avoid infinite loops */
+ digest_lost++;
+ return NULL;
+ }
+ goto search;
+ }
+
+ /* OOM in lf_hash_insert */
+ digest_lost++;
+ return NULL;
+ }
}
- goto search;
}
- /* OOM in lf_hash_insert */
- digest_lost++;
- return NULL;
+ /* The digest array is now full. */
+ digest_full= true;
+ pfs= &statements_digest_stat_array[0];
+
+ if (pfs->m_first_seen == 0)
+ pfs->m_first_seen= now;
+ pfs->m_last_seen= now;
+ return & pfs->m_stat;
}
void purge_digest(PFS_thread* thread, PFS_digest_key *hash_key)
void PFS_statements_digest_stat::reset_data(unsigned char *token_array, uint length)
{
+ m_lock.set_dirty();
m_digest_storage.reset(token_array, length);
m_stat.reset();
m_first_seen= 0;
m_last_seen= 0;
+ m_lock.dirty_to_free();
}
void PFS_statements_digest_stat::reset_index(PFS_thread *thread)
statements_digest_stat_array[index].reset_data(statements_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length);
}
+ /* Mark record[0] as allocated again. */
+ statements_digest_stat_array[0].m_lock.set_allocated();
+
/*
Reset index which indicates where the next calculated digest information
to be inserted in statements_digest_stat_array.
*/
- digest_index= 1;
+ PFS_atomic::store_u32(& digest_monotonic_index, 1);
digest_full= false;
}
-/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/** A statement digest stat record. */
struct PFS_ALIGNED PFS_statements_digest_stat
{
+ /** Internal lock. */
+ pfs_lock m_lock;
+
/** Digest Schema + MD5 Hash. */
PFS_digest_key m_digest_key;
-/* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
PFS_atomic::store_u32(&m_version_state, new_val);
}
+ /**
+ Initialize a lock to dirty.
+ */
+ void set_dirty(void)
+ {
+ /* Do not set the version to 0, read the previous value. */
+ uint32 copy= PFS_atomic::load_u32(&m_version_state);
+ /* Increment the version, set the DIRTY state */
+ uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_DIRTY;
+ PFS_atomic::store_u32(&m_version_state, new_val);
+ }
+
/**
Execute a dirty to free transition.
This transition should be executed by the writer that owns the record.
-/* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
m_pos.next())
{
digest_stat= &statements_digest_stat_array[m_pos.m_index];
- if (digest_stat->m_first_seen != 0)
+ if (digest_stat->m_lock.is_populated())
{
- make_row(digest_stat);
- m_next_pos.set_after(&m_pos);
- return 0;
+ if (digest_stat->m_first_seen != 0)
+ {
+ make_row(digest_stat);
+ m_next_pos.set_after(&m_pos);
+ return 0;
+ }
}
}
set_position(pos);
digest_stat= &statements_digest_stat_array[m_pos.m_index];
- if (digest_stat->m_first_seen != 0)
+ if (digest_stat->m_lock.is_populated())
{
- make_row(digest_stat);
- return 0;
+ if (digest_stat->m_first_seen != 0)
+ {
+ make_row(digest_stat);
+ return 0;
+ }
}
return HA_ERR_RECORD_DELETED;
-# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-# We want gmock-1.6.0.zip in order to build these unit tests.
+# We want release-1.8.0.zip in order to build these unit tests.
# If you have already downloaded it,
-# invoke cmake with -DWITH_GMOCK=/path/to/gmock-1.6.0.zip
+# invoke cmake with -DWITH_GMOCK=/path/to/release-1.8.0.zip
# or -DWITH_GMOCK=/path/to
#
# Alternatively, set an environment variable
-# export WITH_GMOCK=/path/to/gmock-1.6.0.zip
+# export WITH_GMOCK=/path/to/release-1.8.0.zip
#
# You can also do cmake -DENABLE_DOWNLOADS=1
-# and we will download it from http://googlemock.googlecode.com
+# and we will download it from https://github.com/google/googletest/archive/
#
# Either way: we will unpack the zip, compile gmock-all.cc and gtest-all.cc
# and link them into the executables.
-# Where to download and build gmock/gtest.
+# Disable googletest for Solaris Studio < 12.4 since it doesn't compile.
+IF(CMAKE_C_COMPILER_ID MATCHES "SunPro")
+ IF(${CC_MINOR_VERSION} LESS 13)
+ MESSAGE(WARNING "Googletest requires Solaris Studio 12.4 or newer.")
+ UNSET(GMOCK_FOUND)
+ UNSET(GMOCK_FOUND CACHE)
+ RETURN()
+ ENDIF()
+ENDIF()
+
+# Default location for where to download and build gmock/gtest.
IF(NOT DOWNLOAD_ROOT)
SET(DOWNLOAD_ROOT ${CMAKE_SOURCE_DIR}/source_downloads)
ENDIF()
-IF(NOT EXISTS DOWNLOAD_ROOT)
- MAKE_DIRECTORY(${DOWNLOAD_ROOT})
-ENDIF()
-# We want googlemock version 1.6, which also contains googletest.
-SET(GMOCK_PACKAGE_NAME "gmock-1.6.0")
-SET(GMOCK_SOURCE_DIR ${DOWNLOAD_ROOT}/${GMOCK_PACKAGE_NAME})
-SET(GTEST_SOURCE_DIR ${DOWNLOAD_ROOT}/${GMOCK_PACKAGE_NAME}/gtest)
+# We want googletest version 1.8, which also contains googlemock.
+SET(GMOCK_PACKAGE_NAME "release-1.8.0")
IF (DEFINED ENV{WITH_GMOCK} AND NOT DEFINED WITH_GMOCK)
- SET(WITH_GMOCK "$ENV{WITH_GMOCK}")
+ FILE(TO_CMAKE_PATH "$ENV{WITH_GMOCK}" WITH_GMOCK)
+ENDIF()
+
+IF(LOCAL_GMOCK_ZIP
+ AND NOT ${LOCAL_GMOCK_ZIP} MATCHES ".*${GMOCK_PACKAGE_NAME}\\.zip")
+ SET(LOCAL_GMOCK_ZIP 0)
ENDIF()
IF (WITH_GMOCK)
PATHS ${GMOCK_DIR}
NO_DEFAULT_PATH
)
+ ELSE()
+ ## Did we get a path name to the directory of the .zip file?
+ ## Check for both release-x.y.z.zip and googletest-release-x.y.z.zip
+ FIND_FILE(LOCAL_GMOCK_ZIP
+ NAMES "${GMOCK_PACKAGE_NAME}.zip" "googletest-${GMOCK_PACKAGE_NAME}.zip"
+ PATHS ${WITH_GMOCK}
+ NO_DEFAULT_PATH
+ )
+ ## If WITH_GMOCK is a directory, use it for download.
+ SET(DOWNLOAD_ROOT ${WITH_GMOCK})
ENDIF()
- ## Did we get a path name to the directory of the .zip file?
- FIND_FILE(LOCAL_GMOCK_ZIP
- NAMES "${GMOCK_PACKAGE_NAME}.zip"
- PATHS ${WITH_GMOCK}
- NO_DEFAULT_PATH
- )
MESSAGE(STATUS "Local gmock zip ${LOCAL_GMOCK_ZIP}")
ENDIF()
+IF(NOT EXISTS DOWNLOAD_ROOT)
+ MAKE_DIRECTORY(${DOWNLOAD_ROOT})
+ENDIF()
+SET(GMOCK_SOURCE_DIR ${DOWNLOAD_ROOT}/googletest-${GMOCK_PACKAGE_NAME}/googlemock)
+SET(GTEST_SOURCE_DIR ${DOWNLOAD_ROOT}/googletest-${GMOCK_PACKAGE_NAME}/googletest)
+
# We may have downloaded gmock/gtest already, building in a different directory.
-IF(EXISTS ${GMOCK_SOURCE_DIR})
+IF(EXISTS ${GMOCK_SOURCE_DIR} OR EXISTS ${LOCAL_GMOCK_ZIP})
MESSAGE(STATUS "GMOCK_SOURCE_DIR:${GMOCK_SOURCE_DIR}")
SET(GMOCK_DOWNLOADED 1 CACHE INTERNAL "")
SET(GMOCK_FOUND 1 CACHE INTERNAL "")
+# If source dir does not exist, reset dependent variables (might be set from before).
+ELSE()
+ SET(LOCAL_GMOCK_ZIP 0 CACHE INTERNAL "")
+ SET(GMOCK_DOWNLOADED 0 CACHE INTERNAL "")
+ SET(GMOCK_FOUND 0 CACHE INTERNAL "")
+ SET(GMOCK_INCLUDE_DIRS 0 CACHE INTERNAL "")
ENDIF()
OPTION(ENABLE_DOWNLOADS
- "Download and build 3rd party source code components, e.g. google mock"
+ "Download and build 3rd party source code components, e.g. googletest"
OFF)
# While experimenting, use local URL rather than google.
-SET(GMOCK_TARBALL "${GMOCK_PACKAGE_NAME}.zip")
+SET(GMOCK_TARBALL "googletest-${GMOCK_PACKAGE_NAME}.zip")
SET(GMOCK_DOWNLOAD_URL
- "http://googlemock.googlecode.com/files/${GMOCK_TARBALL}"
+ "https://github.com/google/googletest/archive/${GMOCK_PACKAGE_NAME}.zip"
)
MACRO(HTTP_PROXY_HINT)
MESSAGE(STATUS
- "If you are inside a firewall, you may need to use an http proxy: "
- "export http_proxy=http://example.com:80")
+ "If you are inside a firewall, you may need to use an https proxy: "
+ "export https_proxy=http://example.com:80")
ENDMACRO()
IF(NOT GMOCK_FOUND)
IF(NOT ENABLE_DOWNLOADS)
- # Give one-time warning
- IF(NOT ONETIME_GTEST_WARNING)
- MESSAGE(STATUS
- "Googlemock was not found. gtest-based unit tests will be disabled. "
- "You can run cmake . -DENABLE_DOWNLOADS=1 to automatically download "
- "and build required components from source.")
- HTTP_PROXY_HINT()
- SET(ONETIME_GTEST_WARNING 1 CACHE INTERNAL "")
- ENDIF()
+ # Give warning
+ MESSAGE(STATUS
+ "Googletest was not found. gtest-based unit tests will be disabled. "
+ "You can run cmake . -DENABLE_DOWNLOADS=1 to automatically download "
+ "and build required components from source.")
+ HTTP_PROXY_HINT()
RETURN()
ENDIF()
- # Download gmock source
+ # Download googletest source
IF(NOT EXISTS ${GMOCK_SOURCE_DIR})
IF(NOT EXISTS ${DOWNLOAD_ROOT}/${GMOCK_TARBALL})
# Download the tarball
"Successfully downloaded ${GMOCK_DOWNLOAD_URL} to ${DOWNLOAD_ROOT}")
ELSE()
MESSAGE(STATUS
- "To enable google test, please download ${GMOCK_DOWNLOAD_URL} "
+ "To enable googletest, please download ${GMOCK_DOWNLOAD_URL} "
"to the directory ${DOWNLOAD_ROOT}")
HTTP_PROXY_HINT()
RETURN()