Merge lp:~maria-captains/ourdelta/ourdelta-mariadb-5.2-montyprogram into lp:ourdelta

Proposed by Kristian Nielsen
Status: Needs review
Proposed branch: lp:~maria-captains/ourdelta/ourdelta-mariadb-5.2-montyprogram
Merge into: lp:ourdelta
Diff against target: 27515 lines (+26842/-1)
131 files modified
bakery/autobake51-centos.sh (+1/-1)
bakery/debian-5.2/README.Maintainer (+114/-0)
bakery/debian-5.2/additions/Docs__Images__Makefile.in (+6/-0)
bakery/debian-5.2/additions/Docs__Makefile.in (+6/-0)
bakery/debian-5.2/additions/debian-start (+35/-0)
bakery/debian-5.2/additions/debian-start.inc.sh (+72/-0)
bakery/debian-5.2/additions/echo_stderr (+2/-0)
bakery/debian-5.2/additions/innotop/changelog.innotop (+357/-0)
bakery/debian-5.2/additions/innotop/innotop (+10946/-0)
bakery/debian-5.2/additions/innotop/innotop.1 (+2084/-0)
bakery/debian-5.2/additions/msql2mysql.1 (+16/-0)
bakery/debian-5.2/additions/my.cnf (+193/-0)
bakery/debian-5.2/additions/my_print_defaults.1 (+16/-0)
bakery/debian-5.2/additions/myisam_ftdump.1 (+16/-0)
bakery/debian-5.2/additions/myisamchk.1 (+17/-0)
bakery/debian-5.2/additions/myisamlog.1 (+16/-0)
bakery/debian-5.2/additions/myisampack.1 (+19/-0)
bakery/debian-5.2/additions/mysql-server.lintian-overrides (+2/-0)
bakery/debian-5.2/additions/mysql_config.1 (+17/-0)
bakery/debian-5.2/additions/mysql_convert_table_format.1 (+17/-0)
bakery/debian-5.2/additions/mysql_find_rows.1 (+18/-0)
bakery/debian-5.2/additions/mysql_fix_extensions.1 (+18/-0)
bakery/debian-5.2/additions/mysql_install_db.1 (+16/-0)
bakery/debian-5.2/additions/mysql_secure_installation.1 (+17/-0)
bakery/debian-5.2/additions/mysql_setpermission.1 (+23/-0)
bakery/debian-5.2/additions/mysql_tableinfo.1 (+322/-0)
bakery/debian-5.2/additions/mysql_waitpid.1 (+20/-0)
bakery/debian-5.2/additions/mysqlbinlog.1 (+17/-0)
bakery/debian-5.2/additions/mysqlbug.1 (+14/-0)
bakery/debian-5.2/additions/mysqlcheck.1 (+28/-0)
bakery/debian-5.2/additions/mysqld_safe_syslog.cnf (+2/-0)
bakery/debian-5.2/additions/mysqldumpslow.1 (+50/-0)
bakery/debian-5.2/additions/mysqlimport.1 (+20/-0)
bakery/debian-5.2/additions/mysqlreport (+1298/-0)
bakery/debian-5.2/additions/mysqlreport.1 (+180/-0)
bakery/debian-5.2/additions/mysqltest.1 (+16/-0)
bakery/debian-5.2/additions/pack_isam.1 (+19/-0)
bakery/debian-5.2/additions/resolve_stack_dump.1 (+16/-0)
bakery/debian-5.2/additions/resolveip.1 (+16/-0)
bakery/debian-5.2/changelog (+11/-0)
bakery/debian-5.2/compat (+1/-0)
bakery/debian-5.2/copyright (+164/-0)
bakery/debian-5.2/dist/Debian/control (+169/-0)
bakery/debian-5.2/dist/Debian/mariadb-server-5.2.README.Debian (+109/-0)
bakery/debian-5.2/dist/Debian/mariadb-server-5.2.dirs (+10/-0)
bakery/debian-5.2/dist/Debian/mariadb-server-5.2.files (+90/-0)
bakery/debian-5.2/dist/Debian/mariadb-server-5.2.postinst (+267/-0)
bakery/debian-5.2/dist/Debian/mariadb-server-5.2.postrm (+83/-0)
bakery/debian-5.2/dist/Debian/rules (+319/-0)
bakery/debian-5.2/dist/Ubuntu/apparmor-profile (+36/-0)
bakery/debian-5.2/dist/Ubuntu/control (+183/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-5.2.README.Debian (+119/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-5.2.dirs (+11/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-5.2.files (+66/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-5.2.postinst (+274/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-5.2.postrm (+86/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-5.2.py (+53/-0)
bakery/debian-5.2/dist/Ubuntu/mariadb-server-core-5.2.files (+26/-0)
bakery/debian-5.2/dist/Ubuntu/rules (+325/-0)
bakery/debian-5.2/libmariadbclient-dev.README.Maintainer (+4/-0)
bakery/debian-5.2/libmariadbclient-dev.dirs (+2/-0)
bakery/debian-5.2/libmariadbclient-dev.docs (+1/-0)
bakery/debian-5.2/libmariadbclient-dev.examples (+1/-0)
bakery/debian-5.2/libmariadbclient-dev.files (+9/-0)
bakery/debian-5.2/libmariadbclient-dev.links (+2/-0)
bakery/debian-5.2/libmariadbclient16.dirs (+1/-0)
bakery/debian-5.2/libmariadbclient16.docs (+1/-0)
bakery/debian-5.2/libmariadbclient16.files (+1/-0)
bakery/debian-5.2/libmariadbclient16.postinst (+12/-0)
bakery/debian-5.2/libmariadbd-dev.files (+2/-0)
bakery/debian-5.2/mariadb-client-5.2.README.Debian (+4/-0)
bakery/debian-5.2/mariadb-client-5.2.dirs (+3/-0)
bakery/debian-5.2/mariadb-client-5.2.docs (+3/-0)
bakery/debian-5.2/mariadb-client-5.2.files (+36/-0)
bakery/debian-5.2/mariadb-client-5.2.links (+3/-0)
bakery/debian-5.2/mariadb-client-5.2.lintian-overrides (+3/-0)
bakery/debian-5.2/mariadb-client-5.2.menu (+3/-0)
bakery/debian-5.2/mariadb-server-5.2.NEWS (+34/-0)
bakery/debian-5.2/mariadb-server-5.2.config (+46/-0)
bakery/debian-5.2/mariadb-server-5.2.docs (+1/-0)
bakery/debian-5.2/mariadb-server-5.2.lintian-overrides (+5/-0)
bakery/debian-5.2/mariadb-server-5.2.logcheck.ignore.paranoid (+9/-0)
bakery/debian-5.2/mariadb-server-5.2.logcheck.ignore.server (+32/-0)
bakery/debian-5.2/mariadb-server-5.2.logcheck.ignore.workstation (+32/-0)
bakery/debian-5.2/mariadb-server-5.2.mysql-server.logrotate (+27/-0)
bakery/debian-5.2/mariadb-server-5.2.mysql.init (+187/-0)
bakery/debian-5.2/mariadb-server-5.2.preinst (+182/-0)
bakery/debian-5.2/mariadb-server-5.2.prerm (+8/-0)
bakery/debian-5.2/mariadb-server-5.2.templates (+89/-0)
bakery/debian-5.2/mariadb-test-5.2.dirs (+94/-0)
bakery/debian-5.2/mariadb-test-5.2.files (+9/-0)
bakery/debian-5.2/mariadb-test-5.2.links (+2/-0)
bakery/debian-5.2/mysql-common.dirs (+1/-0)
bakery/debian-5.2/mysql-common.files (+3/-0)
bakery/debian-5.2/mysql-common.lintian-overrides (+2/-0)
bakery/debian-5.2/mysql-common.postrm (+7/-0)
bakery/debian-5.2/patches/00list (+10/-0)
bakery/debian-5.2/patches/01_MAKEFILES__Docs_Images_Makefile.in.dpatch (+776/-0)
bakery/debian-5.2/patches/01_MAKEFILES__Docs_Makefile.in.dpatch (+776/-0)
bakery/debian-5.2/patches/02_no_builtin_ndbcluster_plugin.dpatch (+18/-0)
bakery/debian-5.2/patches/21_init__openquery_configtest.dpatch (+33/-0)
bakery/debian-5.2/patches/33_scripts__mysql_create_system_tables__no_test.dpatch (+29/-0)
bakery/debian-5.2/patches/38_scripts__mysqld_safe.sh__signals.dpatch (+43/-0)
bakery/debian-5.2/patches/41_scripts__mysql_install_db.sh__no_test.dpatch (+20/-0)
bakery/debian-5.2/patches/44_scripts__mysql_config__libs.dpatch (+24/-0)
bakery/debian-5.2/patches/50_mysql-test__db_test.dpatch (+23/-0)
bakery/debian-5.2/patches/60_zlib_innodb_workaround.dpatch (+31/-0)
bakery/debian-5.2/po/POTFILES.in (+1/-0)
bakery/debian-5.2/po/ar.po (+236/-0)
bakery/debian-5.2/po/ca.po (+341/-0)
bakery/debian-5.2/po/cs.po (+330/-0)
bakery/debian-5.2/po/da.po (+366/-0)
bakery/debian-5.2/po/de.po (+229/-0)
bakery/debian-5.2/po/es.po (+374/-0)
bakery/debian-5.2/po/eu.po (+236/-0)
bakery/debian-5.2/po/fr.po (+241/-0)
bakery/debian-5.2/po/gl.po (+233/-0)
bakery/debian-5.2/po/it.po (+213/-0)
bakery/debian-5.2/po/ja.po (+218/-0)
bakery/debian-5.2/po/nb.po (+296/-0)
bakery/debian-5.2/po/nl.po (+301/-0)
bakery/debian-5.2/po/pt.po (+291/-0)
bakery/debian-5.2/po/pt_BR.po (+427/-0)
bakery/debian-5.2/po/ro.po (+318/-0)
bakery/debian-5.2/po/ru.po (+229/-0)
bakery/debian-5.2/po/sv.po (+230/-0)
bakery/debian-5.2/po/templates.pot (+186/-0)
bakery/debian-5.2/po/tr.po (+341/-0)
bakery/debian-5.2/source.lintian-overrides (+2/-0)
bakery/debian-5.2/watch (+3/-0)
bakery/tarbake52.sh (+92/-0)
To merge this branch: bzr merge lp:~maria-captains/ourdelta/ourdelta-mariadb-5.2-montyprogram
Reviewer Review Type Date Requested Status
OurDelta-core Pending
Review via email: mp+21697@code.launchpad.net

Description of the change

I went through all the stuff in bakery/debian-5.2 and updated as seemed appropriate.
I also checked the centos .spec for .rpm.

Things now look ok on all platforms in Buildbot, so should be ready for merge.

To post a comment you must log in.

Unmerged revisions

75. By Kristian Nielsen

Add new vcol testsuite.

74. By Kristian Nielsen

Don't include -alpha suffix from version like mariadb-5.2.0-alpha, rpmbuild doesn't like it.

73. By Kristian Nielsen

MWL#88: Update packaging scripts for MariaDB 5.2.

Update all ourdelta bakery files, renaming 5.1->5.2 where appropriate.
Attempt to add necessary extra dependencies.

72. By Arjen Lentz

Adjusted build scripts for 5.2 (some symlinked same as 5.1) - work in progress

71. By Arjen Lentz

Cloned bakery/debian-5.1 to -5.2 (before other changes)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bakery/autobake51-centos.sh'
--- bakery/autobake51-centos.sh 2010-02-06 00:30:21 +0000
+++ bakery/autobake51-centos.sh 2010-03-19 00:47:20 +0000
@@ -36,7 +36,7 @@
36TARGET="ourdelta"36TARGET="ourdelta"
37#TARGET="$(echo ${TARBALL} | cut -d- -f 4- | cut -d. -f1)"37#TARGET="$(echo ${TARBALL} | cut -d- -f 4- | cut -d. -f1)"
38RPMTARGET="$(echo ${TARGET} | tr -d -)"38RPMTARGET="$(echo ${TARGET} | tr -d -)"
39BASEVERSION="$(echo ${TARBALL%.tar.gz} | cut -d- -f 2-)"39BASEVERSION="$(echo ${TARBALL%.tar.gz} | cut -d- -f 2)"
40# no additional patches right now40# no additional patches right now
41PATCHLEVEL="0"41PATCHLEVEL="0"
42#PATCHLEVEL="$(echo ${TARBALL} | cut -d- -f 3)"42#PATCHLEVEL="$(echo ${TARBALL} | cut -d- -f 3)"
4343
=== added symlink 'bakery/autobake52-bintar.sh'
=== target is u'autobake51-bintar.sh'
=== added symlink 'bakery/autobake52-deb.sh'
=== target is u'autobake51-deb.sh'
=== added directory 'bakery/debian-5.2'
=== added file 'bakery/debian-5.2/README.Maintainer'
--- bakery/debian-5.2/README.Maintainer 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/README.Maintainer 2010-03-19 00:47:20 +0000
@@ -0,0 +1,114 @@
1
2###########################
3## FIXME for 5.1 ##
4###########################
5
6* put this trigger-recreation thing into the init scripts -- what?!
7
8###########################################################################
9# Here are some information that are only of interest for the current and #
10# following Debian maintainers of MySQL. #
11###########################################################################
12
13The debian/ directory is under SVN control, see debian/control for URL.
14
15#
16# Preparing a new version
17#
18The new orig.tar.gz (without non-free documentation) is created in /tmp/ when
19running this command:
20
21debian/rules get-orig-source
22
23#
24# mysqlreport
25#
26The authors e-mail address is <public@codenode.com>.
27
28#
29# Remarks to dependencies
30#
31libwrap0-dev (>= 7.6-8.3)
32 According to bug report 114582 where where build problems on
33 IA-64/sid with at least two prior versions.
34psmisc
35 /usr/bin/killall in the initscript
36
37zlib1g in libmysqlclient-dev:
38 "mysql_config --libs" ads "-lz"
39
40Build-Dep:
41
42debhelper (>=4.1.16):
43 See po-debconf(7).
44
45autoconf (>= 2.13-20), automake1.7
46 Try to get rid of them.
47
48doxygen, tetex-bin, tetex-extra, gs
49 for ndb/docs/*tex
50
51#
52# Remarks to the start scripts
53#
54
55## initscripts rely on mysqladmin from a different package
56We have the problem that "/etc/init.d/mysql stop" relies on mysqladmin which
57is in another package (mysql-client) and a passwordless access that's maybe
58only available if the user configured his /root/.my.cnf. Can this be a problem?
59* normal mode: not because the user is required to have it. Else:
60* purge/remove: not, same as normal mode
61* upgrade: not, same as normal mode
62* first install: not, it depends on mysql-client which at least is unpacked
63 so mysqladmin is there (to ping). It is not yet configured
64 passwordles but if there's a server running then there's a
65 /root/.my.cnf. Anyways, we simply kill anything that's mysqld.
66
67## Passwordless access for the maintainer scripts
68Another issue is that the scripts needs passwordless access. To ensure this
69a debian-sys-maint user is configured which has process and shutdown privs.
70The file with the randomly (that's important!) generated password must be
71present as long as the databases remain installed because else a new install
72would have no access. This file should be used like:
73 mysqladmin --defaults-file=/etc/mysql/debian.cnf restart
74to avoid providing the password in plaintext on a commandline where it would
75be visible to any user via the "ps" command.
76
77## When to start the daemon?
78We aim to give the admin full control on when MySQL is running.
79Issues to be faced here:
80OLD:
81 1. Debconf asks whether MySQL should be started on boot so update-rc.d is
82 only run if the answer has been yes. The admin is likely to forget
83 this decision but update-rc.d checks for an existing line in
84 /etc/runlevel.conf and leaves it intact.
85 2. On initial install, if the answer is yes, the daemon has to be started.
86 3. On upgrades it should only be started if it was already running, everything
87 else is confusing. Especiall relying on an debconf decision made month ago
88 is considered suboptimal. See bug #274264
89 Implementation so far:
90 prerm (called on upgrade before stopping the server):
91 check for a running server and set flag if necessary
92 preinst (called on initial install and before unpacking when upgrading):
93 check for the debconf variable and set flag if necessary
94 postinst (called on initial install and after each upgrade after unpacking):
95 call update-rc.d if debconf says yes
96 call invoce-rc.d if the flag has been set
97 Problems remaining:
98 dpkg-reconfigure and setting mysql start on boot to yes did not start mysql
99 (ok "start on boot" literally does not mean "start now" so that might have been ok)
100NEW:
101 1. --- no debconf anymore for the sake of simplicity. We have runlevel.conf,
102 the admin should use it
103 2. On initial install the server is started.
104 3. On upgrades the server is started exactly if it was running before so the
105 runlevel configuration is irrelevant. It will be preserved by the mean of
106 update-rc.d's builtin check.
107 Implementation:
108 prerm (called on upgrade before stopping the server):
109 check for a running server and set flag if necessary
110 preinst (called on initial install and before unpacking when upgrading):
111 check for $1 beeing (initial) "install" and set flag
112 postinst (called on initial install and after each upgrade after unpacking):
113 call update-rc.d
114 call invoce-rc.d if the flag has been set
0115
=== added directory 'bakery/debian-5.2/additions'
=== added file 'bakery/debian-5.2/additions/Docs__Images__Makefile.in'
--- bakery/debian-5.2/additions/Docs__Images__Makefile.in 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/Docs__Images__Makefile.in 2010-03-19 00:47:20 +0000
@@ -0,0 +1,6 @@
1all:
2
3distclean:
4 -rm -f Makefile
5
6.PHONY: all distclean clean install check
07
=== added file 'bakery/debian-5.2/additions/Docs__Makefile.in'
--- bakery/debian-5.2/additions/Docs__Makefile.in 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/Docs__Makefile.in 2010-03-19 00:47:20 +0000
@@ -0,0 +1,6 @@
1all:
2
3distclean:
4 -rm -f Makefile
5
6.PHONY: all distclean clean install check
07
=== added file 'bakery/debian-5.2/additions/debian-start'
--- bakery/debian-5.2/additions/debian-start 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/debian-start 2010-03-19 00:47:20 +0000
@@ -0,0 +1,35 @@
1#!/bin/bash
2#
3# This script is executed by "/etc/init.d/mysql" on every (re)start.
4#
5# Changes to this file will be preserved when updating the Debian package.
6#
7
8source /usr/share/mysql/debian-start.inc.sh
9
10MYSQL="/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf"
11MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
12MYUPGRADE="/usr/bin/mysql_upgrade --defaults-extra-file=/etc/mysql/debian.cnf"
13MYCHECK="/usr/bin/mysqlcheck --defaults-file=/etc/mysql/debian.cnf"
14MYCHECK_SUBJECT="WARNING: mysqlcheck has found corrupt tables"
15MYCHECK_PARAMS="--all-databases --fast --silent"
16MYCHECK_RCPT="root"
17
18# The following commands should be run when the server is up but in background
19# where they do not block the server start and in one shell instance so that
20# they run sequentially. They are supposed not to echo anything to stdout.
21# If you want to disable the check for crashed tables comment
22# "check_for_crashed_tables" out.
23# (There may be no output to stdout inside the background process!)
24echo "Checking for corrupt, not cleanly closed and upgrade needing tables."
25
26# Need to ignore SIGHUP, as otherwise a SIGHUP can sometimes abort the upgrade
27# process in the middle.
28trap "" SIGHUP
29(
30 upgrade_system_tables_if_necessary;
31 check_root_accounts;
32 check_for_crashed_tables;
33) >&2 &
34
35exit 0
036
=== added file 'bakery/debian-5.2/additions/debian-start.inc.sh'
--- bakery/debian-5.2/additions/debian-start.inc.sh 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/debian-start.inc.sh 2010-03-19 00:47:20 +0000
@@ -0,0 +1,72 @@
1#!/bin/bash
2#
3# This file is included by /etc/mysql/debian-start
4#
5
6## Check all unclosed tables.
7# - Requires the server to be up.
8# - Is supposed to run silently in background.
9function check_for_crashed_tables() {
10 set -e
11 set -u
12
13 # But do it in the background to not stall the boot process.
14 logger -p daemon.info -i -t$0 "Triggering myisam-recover for all MyISAM tables"
15
16 # Checking for $? is unreliable so the size of the output is checked.
17 # Some table handlers like HEAP do not support CHECK TABLE.
18 tempfile=`tempfile`
19 # We have to use xargs in this case, because a for loop barfs on the
20 # spaces in the thing to be looped over.
21 LC_ALL=C $MYSQL --skip-column-names --batch -e '
22 select concat('\''select count(*) into @discard from `'\'',
23 TABLE_SCHEMA, '\''`.`'\'', TABLE_NAME, '\''`'\'')
24 from information_schema.TABLES where ENGINE='\''MyISAM'\' | \
25 xargs -i $MYSQL --skip-column-names --silent --batch \
26 --force -e "{}" >$tempfile
27 if [ -s $tempfile ]; then
28 (
29 /bin/echo -e "\n" \
30 "Improperly closed tables are also reported if clients are accessing\n" \
31 "the tables *now*. A list of current connections is below.\n";
32 $MYADMIN processlist status
33 ) >> $tempfile
34 # Check for presence as a dependency on mailx would require an MTA.
35 if [ -x /usr/bin/mailx ]; then
36 mailx -e -s"$MYCHECK_SUBJECT" $MYCHECK_RCPT < $tempfile
37 fi
38 (echo "$MYCHECK_SUBJECT"; cat $tempfile) | logger -p daemon.warn -i -t$0
39 fi
40 rm $tempfile
41}
42
43## Check for tables needing an upgrade.
44# - Requires the server to be up.
45# - Is supposed to run silently in background.
46function upgrade_system_tables_if_necessary() {
47 set -e
48 set -u
49
50 logger -p daemon.info -i -t$0 "Upgrading MySQL tables if necessary."
51
52 # Filter all "duplicate column", "duplicate key" and "unknown column"
53 # errors as the script is designed to be idempotent.
54 LC_ALL=C $MYUPGRADE \
55 2>&1 \
56 | egrep -v '^(1|@had|ERROR (1054|1060|1061))' \
57 | logger -p daemon.warn -i -t$0
58}
59
60## Check for the presence of both, root accounts with and without password.
61# This might have been caused by a bug related to mysql_install_db (#418672).
62function check_root_accounts() {
63 set -e
64 set -u
65
66 logger -p daemon.info -i -t$0 "Checking for insecure root accounts."
67
68 ret=$( echo "SELECT count(*) FROM mysql.user WHERE user='root' and password='';" | $MYSQL --skip-column-names )
69 if [ "$ret" -ne "0" ]; then
70 logger -p daemon.warn -i -t$0 "WARNING: mysql.user contains $ret root accounts without password!"
71 fi
72}
073
=== added file 'bakery/debian-5.2/additions/echo_stderr'
--- bakery/debian-5.2/additions/echo_stderr 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/echo_stderr 2010-03-19 00:47:20 +0000
@@ -0,0 +1,2 @@
1#!/bin/bash
2echo "$*" 1>&2
03
=== added directory 'bakery/debian-5.2/additions/innotop'
=== added file 'bakery/debian-5.2/additions/innotop/changelog.innotop'
--- bakery/debian-5.2/additions/innotop/changelog.innotop 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/innotop/changelog.innotop 2010-03-19 00:47:20 +0000
@@ -0,0 +1,357 @@
1Changelog for innotop:
2
32009-03-09: version 1.7.1
4
5 Changes:
6 * Don't display the CXN column if only one connection is active in
7 the current view
8
9 Bugs fixed:
10 * fixed bug where trying to aggregate the time column would result
11 in a crash if the time column had an undef value in it, which is
12 the case when a thread is in the 'Connect' state
13 * updated innotop.spec file to reflect current version
14
152009-02-23: version 1.7.0
16
17 Changes:
18 * supports a central config (/etc/innotop/innotop.conf)
19 * changed the default home directory config to ~/.innotop/innotop.conf
20 (away from .ini)
21 * embedded InnoDBParser.pm into innotop so it can be run with no
22 installation
23 * no longer writes a new config file by default
24 * added --skipcentral (skip reading central config) and --write (write
25 a config if none were loaded at start-up)
26 * if no config file is loaded, connect to a MySQL database on
27 localhost using mysql_read_default_group=client
28 * embedded maatkit's DSNParser.pm and added support for --user,
29 --password, --host, --port
30 * changed default mode from T (InnoDB Transactions) to Q (Query List)
31 * in addition to connected threads, now displays running and cached
32 threads in statusbar
33 * don't load connections from a config file if any DSN information or
34 a username or password is specified on the command-line
35
36 Bugs fixed:
37 * fixed bug preventing utilization of command-line options that
38 override default config settings if no config file was loaded
39 * fixed a bug where migrating from an old version of the config will
40 delete ~/innotop.ini, if it exists. Now uses File::Temp::tempfile().
41
422007-11-09: version 1.6.0
43
44 * S mode crashed on non-numeric values.
45 * New user-defined columns crashed upon restart.
46 * Added --color option to control terminal coloring.
47
482007-09-18: version 1.5.2
49
50 * Added the ability to monitor InnoDB status from a file.
51 * Changed W mode to L mode; it monitors all locks, not just lock waits.
52
532007-09-16: version 1.5.1
54
55 * Added C (Command Summary) mode.
56 * Fixed a bug in the 'avg' aggregate function.
57
582007-09-10: version 1.5.0
59
60 Changes:
61 * Added plugin functionality.
62 * Added group-by functionality.
63 * Moved the configuration file to a directory.
64 * Enhanced filtering and sorting on pivoted tables.
65 * Many small bug fixes.
66
672007-07-16: version 1.4.3
68
69 Changes:
70 * Added standard --version command-line option
71 * Changed colors to cyan instead of blue; more visible on dark terminals.
72 * Added information to the filter-choosing dialog.
73 * Added column auto-completion when entering a filter expression.
74 * Changed Term::ReadKey from optional to mandatory.
75 * Clarified username in password prompting.
76 * Ten thousand words of documentation!
77
78 Bugs fixed:
79 * innotop crashed in W mode when InnoDB status data was truncated.
80 * innotop didn't display errors in tables if debug was enabled.
81 * The colored() subroutine wasn't being created in non-interactive mode.
82 * Don't prompt to save password except the first time.
83
842007-05-03: version 1.4.2
85
86 This version contains all changes to the trunk until revision 239; some
87 changes in revisions 240:250 are included.
88
89 MAJOR CHANGES:
90
91 * Quick-filters to easily filter any column in any display
92 * Compatibility with MySQL 3.23 through 6.0
93 * Improved error handling when a server is down, permissions denied, etc
94 * Use additional SHOW INNODB STATUS information in 5.1.x
95 * Make all modes use tables consistently, so they can all be edited,
96 filtered, colored and sorted consistently
97 * Combine V, G and S modes into S mode, with v, g, and s hot-keys
98 * Let DBD driver read MySQL option files; permit connections without
99 user/pass/etc
100 * Compile SQL-like expressions into Perl subroutines; eliminate need to
101 know Perl
102 * Do not save all config data to config file, only save user's customizations
103 * Rewritten and improved command-line option handling
104 * Added --count, --delay, and other command-line options to support
105 run-and-exit operation
106 * Improve built-in variable sets
107 * Improve help screen with three-part balanced-column layout
108 * Simplify table-editor and improve hotkey support
109 * Require Perl to have high-resolution time support (Time::HiRes)
110 * Help the user choose a query to analyze or kill
111 * Enable EXPLAIN, show-full-query in T mode just like Q mode
112 * Let data-extraction access current, previous and incremental data sets
113 all at once
114
115 MINOR CHANGES:
116
117 * Column stabilizing for Q mode
118 * New color rules for T, Q, W modes
119 * Apply slave I/O filter to Q mode
120 * Improve detection of server version and other meta-data
121 * Make connection timeout a config variable
122 * Improve cross-version-compatible SQL syntax
123 * Get some information from the DBD driver instead of asking MySQL for it
124 * Improved error messages
125 * Improve server group creation/editing
126 * Improve connection/thread killing
127 * Fix broken key bindings and restore previously mapped hot-keys for
128 choosing columns
129 * Some documentation updates (but not nearly enough)
130 * Allow the user to specify graphing char in S mode (formerly G mode)
131 * Allow easy switching between variable sets in S mode
132 * Bind 'n' key globally to choose the 'next' server connection
133 * Bind '%' key globally to filter displayed tables
134 * Allow aligning columns on the decimal place for easy readability
135 * Add hide_hdr config variable to hide column headers in tables
136 * Add a feature to smartly run PURGE MASTER LOGS in Replication mode
137 * Enable debug mode as a globally configurable variable
138 * Improve error messages when an expression or filter doesn't compile or has
139 a run-time error; die on error when debug is enabled
140 * Allow user-configurable delays after executing SQL (to let the server
141 settle down before taking another measurement)
142 * Add an expression to show how long until a transaction is finished
143 * Add skip_innodb as a global config variable
144 * Add '%' after percentages to help disambiguate (user-configurable)
145 * Add column to M mode to help see how fast slave is catching up to master
146
147 BUG FIXES:
148
149 * T and W modes had wrong value for wait_status column
150 * Error tracking on connections didn't reset when the connection recovered
151 * wait_timeout on connections couldn't be set before MySQL 4.0.3
152 * There was a crash on 3.23 when wiping deadlocks
153 * Lettercase changes in some result sets (SHOW MASTER/SLAVE STATUS) between
154 MySQL versions crashed innotop
155 * Inactive connections crashed innotop upon access to DBD driver
156 * set_precision did not respect user defaults for number of digits
157 * --inc command-line option could not be negated
158 * InnoDB status parsing was not always parsing all needed information
159 * S mode (formerly G mode) could crash trying to divide non-numeric data
160 * M table didn't show Slave_open_temp_tables variable; incorrect lettercase
161 * DBD drivers with broken AutoCommit would crash innotop
162 * Some key bindings had incorrect labels
163 * Some config-file loading routines could load data for things that didn't
164 exist
165 * Headers printed too often in S mode
166 * High-resolution time was not used even when the user had it
167 * Non-interactive mode printed blank lines sometimes
168 * Q-mode header and statusbar showed different QPS numbers
169 * Formulas for key-cache and query-cache hit ratios were wrong
170 * Mac OS "Darwin" machines were mis-identified as Microsoft Windows
171 * Some multiplications crashed when given undefined input
172 * The commify transformation did not check its input and could crash
173 * Specifying an invalid mode on the command line or config file could crash
174 innotop
175
1762007-03-29: version 1.4.1
177
178 * More tweaks to display of connection errors.
179 * Fixed a problem with skip-innodb in MySQL 5.1.
180 * Fix a bug with dead connections in single-connection mode.
181 * Fix a regex to allow parsing more data from truncated deadlocks.
182 * Don't load active cxns from the config file if the cxn isn't defined.
183
1842007-03-03: version 1.4.0
185
186 * Further tweak error handling and display of connection errors
187 * More centralization of querying
188 * Fix forking so it doesn't kill all database connections
189 * Allow user to run innotop without permissions for GLOBAL variables and status
190
1912007-02-11: version 1.3.6
192
193 * Handle some connection failures so innotop doesn't crash because of one server.
194 * Enable incremental display in more modes.
195 * Tweaks to colorizing, color editor, and default color rules.
196 * Tweaks to default sorting rules.
197 * Use prepared statements for efficiency.
198 * Bug fixes and code cleanups.
199 * Data storage is keyed on clock ticks now.
200
2012007-02-03: version 1.3.5
202
203 * Bug fixes.
204 * More tools for editing configuration from within innotop.
205 * Filters and transformations are constrained to valid values.
206 * Support for colorizing rows.
207 * Sorting by multiple columns.
208 * Compress headers when display is very wide.
209 * Stabilize and limit column widths.
210 * Check config file formats when upgrading so upgrades go smoothly.
211 * Make D mode handle many connections at once.
212 * Extract simple expressions from data sets in column src property.
213 This makes innotop more awk-ish.
214
2152007-01-16: version 1.3
216
217 * Readline support.
218 * Can be used unattended, or in a pipe-and-filter mode
219 where it outputs tab-separated data to standard output.
220 * You can specify a config file on the command line.
221 Config files can be marked read-only.
222 * Monitor multiple servers simultaneously.
223 * Server groups to help manage many servers conveniently.
224 * Monitor master/slave status, and control slaves.
225 * Columns can have user-defined expressions as their data sources.
226 * Better configuration tools.
227 * InnoDB status information is merged into SHOW VARIABLES and
228 SHOW STATUS information, so you can access it all together.
229 * High-precision time support in more places.
230 * Lots of tweaks to make things display more readably and compactly.
231 * Column transformations and filters.
232
2332007-01-16: version 1.0.1
234 * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS.
235 The new project homepage is http://sourceforge.net/projects/innotop/
236 * Tweak default T/Q mode sort columns to match what people expect.
237 * Fix broken InnoDBParser.pm documentation (and hence man page).
238
2392007-01-06: version 1.0
240 * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS.
241 The new project homepage is http://sourceforge.net/projects/innotop/
242 * Prevent control characters from freaking terminal out.
243 * Set timeout to keep busy servers from closing connection.
244 * There is only one InnoDB insert buffer.
245 * Make licenses clear and consistent.
246
2472006-11-14: innotop 0.1.160, InnoDBParser version 1.69
248 * Support for ANSI color on Microsoft Windows (more readable, compact
249 display; thanks Gisbert W. Selke).
250 * Better handling of $ENV{HOME} on Windows.
251 * Added a LICENSE file to the package as per Gentoo bug:
252 http://bugs.gentoo.org/show_bug.cgi?id=147600
253
2542006-11-11: innotop 0.1.157, InnoDBParser version 1.69
255 * Add Microsoft Windows support.
256
2572006-10-19: innotop 0.1.154, InnoDBParser version 1.69
258 * Add O (Open Tables) mode
259 * Add some more checks to handle incomplete InnoDB status information
260
2612006-09-30: innotop 0.1.152, InnoDBParser version 1.69
262 * Figured out what was wrong with package $VERSION variable: it wasn't
263 after the package declaration!
264
2652006-09-28: innotop 0.1.152, InnoDBParser version 1.67
266 * Make more efforts towards crash-resistance and tolerance of completely
267 messed-up inputs. If innotop itself is broken, it is now much harder to
268 tell, because it just keeps on running without complaining.
269 * Fix a small bug parsing out some information and displaying it.
270
2712006-09-05: innotop 0.1.149, InnoDBParser version 1.64
272 * Try to find and eliminate any parsing code that assumes pattern matches
273 will succeed.
274
2752006-09-05: innotop 0.1.149, InnoDBParser version 1.62
276 * Make innotop crash-resistant, so I can declare it STABLE finally.
277 * Instead of using SQL conditional comments, detect MySQL version.
278
2792006-08-22: innotop 0.1.147, InnoDBParser version 1.60
280 * Fix some innotop bugs with undefined values, bad formatting etc.
281
2822006-08-19: innotop 0.1.146, InnoDBParser version 1.60
283 * Make innotop handle some unexpected NULL values in Q mode.
284 * Add OS wait information to W mode, so it is now "everything that waits."
285 * Center section captions better.
286 * Make R mode more readable and compact.
287 * Make InnoDBParser parse lock waits even when they've been waiting 0 secs.
288
2892006-08-12: innotop 0.1.139, InnoDBParser version 1.59
290 * Add more documentation
291 * Tweak V mode to show more info in less space.
292 * Fix a bug in G mode.
293
2942006-08-10: innotop 0.1.132, InnoDBParser version 1.58
295 * Handle yet more types of FK error... it will never end!
296 * Handle some special cases when DEADLOCK info truncated
297 * Add a bit more FK info to F mode in innotop
298 * More tests added to the test suite
299
3002006-08-07: innotop 0.1.131, InnoDBParser version 1.55
301 * Fix another issue with configuration
302 * Handle another type of FK error
303
3042006-08-03: innotop 0.1.130, InnoDBParser version 1.54
305 * Fix an issue loading config file
306 * Add heap_no to 'D' (InnoDB Deadlock) mode to ease deadlock debugging.
307
3082006-08-02: innotop 0.1.128, InnoDBParser version 1.54
309 * Parse lock wait information from the TRANSACTION section.
310 * Even more OS-specific parsing... pain in the butt...
311 * Add 'W' (InnoDB Lock Wait) mode.
312 * Fix some minor display issues with statusbar.
313
3142006-08-02: innotop 0.1.125, InnoDBParser version 1.50
315 * Don't try to get references to Perl built-in functions like time()
316 * Handle more OS-specific variations of InnoDB status text
317 * Add some more information to various places in innotop
318
3192006-08-01: innotop 0.1.123, InnoDBParser version 1.47
320
321 * Enhance S and G modes: clear screen and re-print headers
322 * Don't crash when deadlock data is truncated
323 * Make Analyze mode say how to get back to whatever you came from
324 * Display 'nothing to display' when there is nothing
325 * Add ability to read InnoDB status text from a file (mostly helps test)
326 * Add table of Wait Array Information in Row Op/Semaphore mode
327 * Add table of lock information in InnoDB deadlock mode
328 * Ensure new features in upgrades don't get masked by existing config files
329 * Tweak default column choices for T mode
330 * Enhance foreign key parsing
331 * Enhance physical record and data tuple parsing
332 * Enhance lock parsing (handle old-style and new-style formats)
333
3342006-07-24: innotop 0.1.112, InnoDBParser version 1.36
335
336 * InnoDBParser enhancements for FK error messages.
337 * A fix to innotop to prevent it from crashing while trying to display a FK
338 error message.
339 * Some minor cosmetic changes to number formatting in innotop.
340
3412006-07-22: innotop 0.1.106, InnoDBParser version 1.35
342
343 * InnoDBParser is much more complete and accurate.
344 * Tons of bug fixes.
345 * Add partitions to EXPLAIN mode.
346 * Enhance Q mode header, add T mode header.
347 * Share some configuration variables across modes.
348 * Add formatted time columns to Q, T modes.
349 * Add command-line argument parsing.
350 * Turn off echo when asking for password.
351 * Add option to specify port when connecting.
352 * Let display-optimized-query display multiple notes.
353 * Lots of small improvements, such as showing more info in statusbar.
354
3552006-07-02: innotop 0.1.74, InnoDBParser version 1.24
356
357 * Initial release for public consumption.
0358
=== added file 'bakery/debian-5.2/additions/innotop/innotop'
--- bakery/debian-5.2/additions/innotop/innotop 1970-01-01 00:00:00 +0000
+++ bakery/debian-5.2/additions/innotop/innotop 2010-03-19 00:47:20 +0000
@@ -0,0 +1,10946 @@
1#!/usr/bin/perl
2
3# vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3
4
5# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com.
6# Feedback and improvements are gratefully received.
7#
8# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
9# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
10# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11#
12# This program is free software; you can redistribute it and/or modify it under
13# the terms of the GNU General Public License as published by the Free Software
14# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
15# systems, you can issue `man perlgpl' or `man perlartistic' to read these
16
17# You should have received a copy of the GNU General Public License along with
18# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19# Place, Suite 330, Boston, MA 02111-1307 USA
20
21use strict;
22use warnings FATAL => 'all';
23
24our $VERSION = '1.7.1';
25
26# Find the home directory; it's different on different OSes.
27our $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';
28
29# Configuration files
30our $default_home_conf = "$homepath/.innotop/innotop.conf";
31our $default_central_conf = "/etc/innotop/innotop.conf";
32our $conf_file = "";
33
34## Begin packages ##
35
36package DSNParser;
37
38use DBI;
39use Data::Dumper;
40$Data::Dumper::Indent = 0;
41$Data::Dumper::Quotekeys = 0;
42use English qw(-no_match_vars);
43
44use constant MKDEBUG => $ENV{MKDEBUG};
45
46# Defaults are built-in, but you can add/replace items by passing them as
47# hashrefs of {key, desc, copy, dsn}. The desc and dsn items are optional.
48# You can set properties with the prop() sub. Don't set the 'opts' property.
49sub new {
50 my ( $class, @opts ) = @_;
51 my $self = {
52 opts => {
53 A => {
54 desc => 'Default character set',
55 dsn => 'charset',
56 copy => 1,
57 },
58 D => {
59 desc => 'Database to use',
60 dsn => 'database',
61 copy => 1,
62 },
63 F => {
64 desc => 'Only read default options from the given file',
65 dsn => 'mysql_read_default_file',
66 copy => 1,
67 },
68 h => {
69 desc => 'Connect to host',
70 dsn => 'host',
71 copy => 1,
72 },
73 p => {
74 desc => 'Password to use when connecting',
75 dsn => 'password',
76 copy => 1,
77 },
78 P => {
79 desc => 'Port number to use for connection',
80 dsn => 'port',
81 copy => 1,
82 },
83 S => {
84 desc => 'Socket file to use for connection',
85 dsn => 'mysql_socket',
86 copy => 1,
87 },
88 u => {
89 desc => 'User for login if not current user',
90 dsn => 'user',
91 copy => 1,
92 },
93 },
94 };
95 foreach my $opt ( @opts ) {
96 MKDEBUG && _d('Adding extra property ' . $opt->{key});
97 $self->{opts}->{$opt->{key}} = { desc => $opt->{desc}, copy => $opt->{copy} };
98 }
99 return bless $self, $class;
100}
101
102# Recognized properties:
103# * autokey: which key to treat a bareword as (typically h=host).
104# * dbidriver: which DBI driver to use; assumes mysql, supports Pg.
105# * required: which parts are required (hashref).
106# * setvars: a list of variables to set after connecting
107sub prop {
108 my ( $self, $prop, $value ) = @_;
109 if ( @_ > 2 ) {
110 MKDEBUG && _d("Setting $prop property");
111 $self->{$prop} = $value;
112 }
113 return $self->{$prop};
114}
115
116sub parse {
117 my ( $self, $dsn, $prev, $defaults ) = @_;
118 if ( !$dsn ) {
119 MKDEBUG && _d('No DSN to parse');
120 return;
121 }
122 MKDEBUG && _d("Parsing $dsn");
123 $prev ||= {};
124 $defaults ||= {};
125 my %given_props;
126 my %final_props;
127 my %opts = %{$self->{opts}};
128 my $prop_autokey = $self->prop('autokey');
129
130 # Parse given props
131 foreach my $dsn_part ( split(/,/, $dsn) ) {
132 if ( my ($prop_key, $prop_val) = $dsn_part =~ m/^(.)=(.*)$/ ) {
133 # Handle the typical DSN parts like h=host, P=3306, etc.
134 $given_props{$prop_key} = $prop_val;
135 }
136 elsif ( $prop_autokey ) {
137 # Handle barewords
138 MKDEBUG && _d("Interpreting $dsn_part as $prop_autokey=$dsn_part");
139 $given_props{$prop_autokey} = $dsn_part;
140 }
141 else {
142 MKDEBUG && _d("Bad DSN part: $dsn_part");
143 }
144 }
145
146 # Fill in final props from given, previous, and/or default props
147 foreach my $key ( keys %opts ) {
148 MKDEBUG && _d("Finding value for $key");
149 $final_props{$key} = $given_props{$key};
150 if ( !defined $final_props{$key}
151 && defined $prev->{$key} && $opts{$key}->{copy} )
152 {
153 $final_props{$key} = $prev->{$key};
154 MKDEBUG && _d("Copying value for $key from previous DSN");
155 }
156 if ( !defined $final_props{$key} ) {
157 $final_props{$key} = $defaults->{$key};
158 MKDEBUG && _d("Copying value for $key from defaults");
159 }
160 }
161
162 # Sanity check props
163 foreach my $key ( keys %given_props ) {
164 die "Unrecognized DSN part '$key' in '$dsn'\n"
165 unless exists $opts{$key};
166 }
167 if ( (my $required = $self->prop('required')) ) {
168 foreach my $key ( keys %$required ) {
169 die "Missing DSN part '$key' in '$dsn'\n" unless $final_props{$key};
170 }
171 }
172
173 return \%final_props;
174}
175
176sub as_string {
177 my ( $self, $dsn ) = @_;
178 return $dsn unless ref $dsn;
179 return join(',',
180 map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
181 grep { defined $dsn->{$_} && $self->{opts}->{$_} }
182 sort keys %$dsn );
183}
184
185sub usage {
186 my ( $self ) = @_;
187 my $usage
188 = "DSN syntax is key=value[,key=value...] Allowable DSN keys:\n"
189 . " KEY COPY MEANING\n"
190 . " === ==== =============================================\n";
191 my %opts = %{$self->{opts}};
192 foreach my $key ( sort keys %opts ) {
193 $usage .= " $key "
194 . ($opts{$key}->{copy} ? 'yes ' : 'no ')
195 . ($opts{$key}->{desc} || '[No description]')
196 . "\n";
197 }
198 if ( (my $key = $self->prop('autokey')) ) {
199 $usage .= " If the DSN is a bareword, the word is treated as the '$key' key.\n";
200 }
201 return $usage;
202}
203
204# Supports PostgreSQL via the dbidriver element of $info, but assumes MySQL by
205# default.
206sub get_cxn_params {
207 my ( $self, $info ) = @_;
208 my $dsn;
209 my %opts = %{$self->{opts}};
210 my $driver = $self->prop('dbidriver') || '';
211 if ( $driver eq 'Pg' ) {
212 $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';'
213 . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" }
214 grep { defined $info->{$_} }
215 qw(h P));
216 }
217 else {
218 $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';'
219 . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" }
220 grep { defined $info->{$_} }
221 qw(F h P S A))
222 . ';mysql_read_default_group=client';
223 }
224 MKDEBUG && _d($dsn);
225 return ($dsn, $info->{u}, $info->{p});
226}
227
228
229# Fills in missing info from a DSN after successfully connecting to the server.
230sub fill_in_dsn {
231 my ( $self, $dbh, $dsn ) = @_;
232 my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name');
233 my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()');
234 $user =~ s/@.*//;
235 $dsn->{h} ||= $vars->{hostname}->{Value};
236 $dsn->{S} ||= $vars->{'socket'}->{Value};
237 $dsn->{P} ||= $vars->{port}->{Value};
238 $dsn->{u} ||= $user;
239 $dsn->{D} ||= $db;
240}
241
242sub get_dbh {
243 my ( $self, $cxn_string, $user, $pass, $opts ) = @_;
244 $opts ||= {};
245 my $defaults = {
246 AutoCommit => 0,
247 RaiseError => 1,
248 PrintError => 0,
249 mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0),
250 };
251 @{$defaults}{ keys %$opts } = values %$opts;
252 my $dbh;
253 my $tries = 2;
254 while ( !$dbh && $tries-- ) {
255 eval {
256 MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {',
257 join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}');
258 $dbh = DBI->connect($cxn_string, $user, $pass, $defaults);
259 # Immediately set character set and binmode on STDOUT.
260 if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
261 my $sql = "/*!40101 SET NAMES $charset*/";
262 MKDEBUG && _d("$dbh: $sql");
263 $dbh->do($sql);
264 MKDEBUG && _d('Enabling charset for STDOUT');
265 if ( $charset eq 'utf8' ) {
266 binmode(STDOUT, ':utf8')
267 or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
268 }
269 else {
270 binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR";
271 }
272 }
273 };
274 if ( !$dbh && $EVAL_ERROR ) {
275 MKDEBUG && _d($EVAL_ERROR);
276 if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) {
277 MKDEBUG && _d("Going to try again without utf8 support");
278 delete $defaults->{mysql_enable_utf8};
279 }
280 if ( !$tries ) {
281 die $EVAL_ERROR;
282 }
283 }
284 }
285 # If setvars exists and it's MySQL connection, set them
286 my $setvars = $self->prop('setvars');
287 if ( $cxn_string =~ m/mysql/i && $setvars ) {
288 my $sql = "SET $setvars";
289 MKDEBUG && _d("$dbh: $sql");
290 eval {
291 $dbh->do($sql);
292 };
293 if ( $EVAL_ERROR ) {
294 MKDEBUG && _d($EVAL_ERROR);
295 }
296 }
297 MKDEBUG && _d('DBH info: ',
298 $dbh,
299 Dumper($dbh->selectrow_hashref(
300 'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')),
301 ' Connection info: ', ($dbh->{mysql_hostinfo} || 'undef'),
302 ' Character set info: ',
303 Dumper($dbh->selectall_arrayref(
304 'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})),
305 ' $DBD::mysql::VERSION: ', $DBD::mysql::VERSION,
306 ' $DBI::VERSION: ', $DBI::VERSION,
307 );
308 return $dbh;
309}
310
311# Tries to figure out a hostname for the connection.
312sub get_hostname {
313 my ( $self, $dbh ) = @_;
314 if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) {
315 return $host;
316 }
317 my ( $hostname, $one ) = $dbh->selectrow_array(
318 'SELECT /*!50038 @@hostname, */ 1');
319 return $hostname;
320}
321
322# Disconnects a database handle, but complains verbosely if there are any active
323# children. These are usually $sth handles that haven't been finish()ed.
324sub disconnect {
325 my ( $self, $dbh ) = @_;
326 MKDEBUG && $self->print_active_handles($dbh);
327 $dbh->disconnect;
328}
329
330sub print_active_handles {
331 my ( $self, $thing, $level ) = @_;
332 $level ||= 0;
333 printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level,
334 $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : ''))
335 or die "Cannot print: $OS_ERROR";
336 foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) {
337 $self->print_active_handles( $handle, $level + 1 );
338 }
339}
340
341sub _d {
342 my ($package, undef, $line) = caller 0;
343 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
344 map { defined $_ ? $_ : 'undef' }
345 @_;
346 # Use $$ instead of $PID in case the package
347 # does not use English.
348 print "# $package:$line $$ ", @_, "\n";
349}
350
3511;
352
353package InnoDBParser;
354
355use Data::Dumper;
356$Data::Dumper::Sortkeys = 1;
357use English qw(-no_match_vars);
358use List::Util qw(max);
359
360# Some common patterns
361my $d = qr/(\d+)/; # Digit
362my $f = qr/(\d+\.\d+)/; # Float
363my $t = qr/(\d+ \d+)/; # Transaction ID
364my $i = qr/((?:\d{1,3}\.){3}\d+)/; # IP address
365my $n = qr/([^`\s]+)/; # MySQL object name
366my $w = qr/(\w+)/; # Words
367my $fl = qr/([\w\.\/]+) line $d/; # Filename and line number
368my $h = qr/((?:0x)?[0-9a-f]*)/; # Hex
369my $s = qr/(\d{6} .\d:\d\d:\d\d)/; # InnoDB timestamp
370
371# If you update this variable, also update the SYNOPSIS in the pod.
372my %innodb_section_headers = (
373 "TRANSACTIONS" => "tx",
374 "BUFFER POOL AND MEMORY" => "bp",
375 "SEMAPHORES" => "sm",
376 "LOG" => "lg",
377 "ROW OPERATIONS" => "ro",
378 "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib",
379 "FILE I/O" => "io",
380 "LATEST DETECTED DEADLOCK" => "dl",
381 "LATEST FOREIGN KEY ERROR" => "fk",
382);
383
384my %parser_for = (
385 tx => \&parse_tx_section,
386 bp => \&parse_bp_section,
387 sm => \&parse_sm_section,
388 lg => \&parse_lg_section,
389 ro => \&parse_ro_section,
390 ib => \&parse_ib_section,
391 io => \&parse_io_section,
392 dl => \&parse_dl_section,
393 fk => \&parse_fk_section,
394);
395
396my %fk_parser_for = (
397 Transaction => \&parse_fk_transaction_error,
398 Error => \&parse_fk_bad_constraint_error,
399 Cannot => \&parse_fk_cant_drop_parent_error,
400);
401
402# A thread's proc_info can be at least 98 different things I've found in the
403# source. Fortunately, most of them begin with a gerunded verb. These are
404# the ones that don't.
405my %is_proc_info = (
406 'After create' => 1,
407 'Execution of init_command' => 1,
408 'FULLTEXT initialization' => 1,
409 'Reopen tables' => 1,
410 'Repair done' => 1,
411 'Repair with keycache' => 1,
412 'System lock' => 1,
413 'Table lock' => 1,
414 'Thread initialized' => 1,
415 'User lock' => 1,
416 'copy to tmp table' => 1,
417 'discard_or_import_tablespace' => 1,
418 'end' => 1,
419 'got handler lock' => 1,
420 'got old table' => 1,
421 'init' => 1,
422 'key cache' => 1,
423 'locks' => 1,
424 'malloc' => 1,
425 'query end' => 1,
426 'rename result table' => 1,
427 'rename' => 1,
428 'setup' => 1,
429 'statistics' => 1,
430 'status' => 1,
431 'table cache' => 1,
432 'update' => 1,
433);
434
435sub new {
436 bless {}, shift;
437}
438
439# Parse the status and return it.
440# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c
441# Pass in the text to parse, whether to be in debugging mode, which sections
442# to parse (hashref; if empty, parse all), and whether to parse full info from
443# locks and such (probably shouldn't unless you need to).
444sub parse_status_text {
445 my ( $self, $fulltext, $debug, $sections, $full ) = @_;
446
447 die "I can't parse undef" unless defined $fulltext;
448 $fulltext =~ s/[\r\n]+/\n/g;
449
450 $sections ||= {};
451 die '$sections must be a hashref' unless ref($sections) eq 'HASH';
452
453 my %innodb_data = (
454 got_all => 0, # Whether I was able to get the whole thing
455 ts => '', # Timestamp the server put on it
456 last_secs => 0, # Num seconds the averages are over
457 sections => {}, # Parsed values from each section
458 );
459
460 if ( $debug ) {
461 $innodb_data{'fulltext'} = $fulltext;
462 }
463
464 # Get the most basic info about the status: beginning and end, and whether
465 # I got the whole thing (if there has been a big deadlock and there are
466 # too many locks to print, the output might be truncated)
467 my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m;
468 $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ];
469 $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'});
470 ( $innodb_data{'last_secs'} ) = $fulltext
471 =~ m/Per second averages calculated from the last $d seconds/;
472
473 ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/;
474 $innodb_data{'got_all'} = $got_all || 0;
475
476 # Split it into sections. Each section begins with
477 # -----
478 # LABEL
479 # -----
480 my %innodb_sections;
481 my @matches = $fulltext
482 =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs;
483 while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) {
484 $innodb_sections{$name} = [ $text, $end ? 1 : 0 ];
485 }
486 # The Row Operations section is a special case, because instead of ending
487 # with the beginning of another section, it ends with the end of the file.
488 # So this section is complete if the entire file is complete.
489 $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'};
490
491 # Just for sanity's sake, make sure I understand what to do with each
492 # section
493 eval {
494 foreach my $section ( keys %innodb_sections ) {
495 my $header = $innodb_section_headers{$section};
496 die "Unknown section $section in $fulltext\n"
497 unless $header;
498 $innodb_data{'sections'}->{ $header }
499 ->{'fulltext'} = $innodb_sections{$section}->[0];
500 $innodb_data{'sections'}->{ $header }
501 ->{'complete'} = $innodb_sections{$section}->[1];
502 }
503 };
504 if ( $EVAL_ERROR ) {
505 _debug( $debug, $EVAL_ERROR);
506 }
507
508 # ################################################################
509 # Parse the detailed data out of the sections.
510 # ################################################################
511 eval {
512 foreach my $section ( keys %parser_for ) {
513 if ( defined $innodb_data{'sections'}->{$section}
514 && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) {
515 $parser_for{$section}->(
516 $innodb_data{'sections'}->{$section},
517 $innodb_data{'sections'}->{$section}->{'complete'},
518 $debug,
519 $full )
520 or delete $innodb_data{'sections'}->{$section};
521 }
522 else {
523 delete $innodb_data{'sections'}->{$section};
524 }
525 }
526 };
527 if ( $EVAL_ERROR ) {
528 _debug( $debug, $EVAL_ERROR);
529 }
530
531 return \%innodb_data;
532}
533
534# Parses the status text and returns it flattened out as a single hash.
535sub get_status_hash {
536 my ( $self, $fulltext, $debug, $sections, $full ) = @_;
537
538 # Parse the status text...
539 my $innodb_status
540 = $self->parse_status_text($fulltext, $debug, $sections, $full );
541
542 # Flatten the hierarchical structure into a single list by grabbing desired
543 # sections from it.
544 return
545 (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)),
546 (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} }
547 qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads
548 awe_mem_alloc pages_modified writes_pending_lru page_creates_sec
549 reads_pending pages_total buf_pool_hits writes_pending_single_page
550 page_writes_sec pages_read pages_written page_reads_sec
551 writes_pending_flush_list buf_pool_size add_pool_alloc
552 dict_mem_alloc pages_created buf_free complete )),
553 (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} }
554 qw( num_lock_structs history_list_len purge_done_for transactions
555 purge_undo_for is_truncated trx_id_counter complete )),
556 (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} }
557 qw( hash_table_size hash_searches_s non_hash_searches_s
558 bufs_in_node_heap used_cells size free_list_len seg_size inserts
559 merged_recs merges complete )),
560 (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} }
561 qw( log_ios_done pending_chkp_writes last_chkp log_ios_s
562 log_flushed_to log_seq_no pending_log_writes complete )),
563 (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} }
564 qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits
565 mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits
566 waits signal_count reservation_count complete )),
567 (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} }
568 qw( queries_in_queue n_reserved_extents main_thread_state
569 main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec
570 read_views_open num_rows_upd num_rows_ins num_rows_read
571 queries_inside num_rows_del complete )),
572 (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} }
573 qw( trigger parent_table child_index parent_index attempted_op
574 child_db timestring fk_name records col_name reason txn parent_db
575 type child_table parent_col complete )),
576 (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} }
577 qw( pending_buffer_pool_flushes pending_pwrites pending_preads
578 pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios
579 reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s
580 threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs
581 pending_log_flushes complete )),
582 (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} }
583 qw( timestring rolled_back txns complete ));
584
585}
586
587sub ts_to_string {
588 my $parts = shift;
589 return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts);
590}
591
592sub parse_innodb_timestamp {
593 my $text = shift;
594 my ( $y, $m, $d, $h, $i, $s )
595 = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/;
596 die("Can't get timestamp from $text\n") unless $y;
597 $y += 2000;
598 return ( $y, $m, $d, $h, $i, $s );
599}
600
601sub parse_fk_section {
602 my ( $section, $complete, $debug, $full ) = @_;
603 my $fulltext = $section->{'fulltext'};
604
605 return 0 unless $fulltext;
606
607 my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m;
608 $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
609 $section->{'timestring'} = ts_to_string($section->{'ts'});
610 $section->{'type'} = $type;
611
612 # Decide which type of FK error happened, and dispatch to the right parser.
613 if ( $type && $fk_parser_for{$type} ) {
614 $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full );
615 }
616
617 delete $section->{'fulltext'} unless $debug;
618
619 return 1;
620}
621
622sub parse_fk_cant_drop_parent_error {
623 my ( $section, $complete, $debug, $fulltext, $full ) = @_;
624
625 # Parse the parent/child table info out
626 @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext
627 =~ m{Cannot $w table `(.*)/(.*)`}m;
628 @{$section}{ qw(child_db child_table) } = $fulltext
629 =~ m{because it is referenced by `(.*)/(.*)`}m;
630
631 ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s;
632 $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm
633 if $section->{'reason'};
634
635 # Certain data may not be present. Make them '' if not present.
636 map { $section->{$_} ||= "" }
637 qw(child_index fk_name col_name parent_col);
638}
639
640# See dict/dict0dict.c, function dict_foreign_error_report
641# I don't care much about these. There are lots of different messages, and
642# they come from someone trying to create a foreign key, or similar
643# statements. They aren't indicative of some transaction trying to insert,
644# delete or update data. Sometimes it is possible to parse out a lot of
645# information about the tables and indexes involved, but often the message
646# contains the DDL string the user entered, which is way too much for this
647# module to try to handle.
648sub parse_fk_bad_constraint_error {
649 my ( $section, $complete, $debug, $fulltext, $full ) = @_;
650
651 # Parse the parent/child table and index info out
652 @{$section}{ qw(child_db child_table) } = $fulltext
653 =~ m{Error in foreign key constraint of table (.*)/(.*):$}m;
654 $section->{'attempted_op'} = 'DDL';
655
656 # FK name, parent info... if possible.
657 @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
658 = $fulltext
659 =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/;
660
661 if ( !defined($section->{'fk_name'}) ) {
662 # Try to parse SQL a user might have typed in a CREATE statement or such
663 @{$section}{ qw(col_name parent_db parent_table parent_col) }
664 = $fulltext
665 =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i;
666 }
667 $section->{'parent_db'} ||= $section->{'child_db'};
668
669 # Name of the child index (index in the same table where the FK is, see
670 # definition of dict_foreign_struct in include/dict0mem.h, where it is
671 # called foreign_index, as opposed to referenced_index which is in the
672 # parent table. This may not be possible to find.
673 @{$section}{ qw(child_index) } = $fulltext
674 =~ m/^The index in the foreign key in table is $n$/m;
675
676 @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms;
677 $section->{'reason'} =~ s/\s+/ /g
678 if $section->{'reason'};
679
680 # Certain data may not be present. Make them '' if not present.
681 map { $section->{$_} ||= "" }
682 qw(child_index fk_name col_name parent_table parent_col);
683}
684
685# see source file row/row0ins.c
686sub parse_fk_transaction_error {
687 my ( $section, $complete, $debug, $fulltext, $full ) = @_;
688
689 # Parse the txn info out
690 my ( $txn ) = $fulltext
691 =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s;
692 if ( $txn ) {
693 $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full );
694 }
695
696 # Parse the parent/child table and index info out. There are two types: an
697 # update or a delete of a parent record leaves a child orphaned
698 # (row_ins_foreign_report_err), and an insert or update of a child record has
699 # no matching parent record (row_ins_foreign_report_add_err).
700
701 @{$section}{ qw(reason child_db child_table) }
702 = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m;
703
704 @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
705 = $fulltext
706 =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/;
707 $section->{'parent_db'} ||= $section->{'child_db'};
708
709 # Special case, which I don't know how to trigger, but see
710 # innobase/row/row0ins.c row_ins_check_foreign_constraint
711 if ( $fulltext =~ m/ibd file does not currently exist!/ ) {
712 my ( $attempted_op, $index, $records )
713 = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm;
714 $section->{'child_index'} = $index;
715 $section->{'attempted_op'} = $attempted_op || '';
716 if ( $records && $full ) {
717 ( $section->{'records'} )
718 = parse_innodb_record_dump( $records, $complete, $debug );
719 }
720 @{$section}{qw(parent_db parent_table)}
721 =~ m/^But the parent table `$n`\.`$n`$/m;
722 }
723 else {
724 my ( $attempted_op, $which, $index )
725 = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m;
726 if ( $which ) {
727 $section->{$which . '_index'} = $index;
728 $section->{'attempted_op'} = $attempted_op || '';
729
730 # Parse out the related records in the other table.
731 my ( $search_index, $records );
732 if ( $which eq 'child' ) {
733 ( $search_index, $records ) = $fulltext
734 =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms;
735 $section->{'parent_index'} = $search_index;
736 }
737 else {
738 ( $search_index, $records ) = $fulltext
739 =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms;
740 $section->{'child_index'} = $search_index;
741 }
742 if ( $records && $full ) {
743 $section->{'records'}
744 = parse_innodb_record_dump( $records, $complete, $debug );
745 }
746 else {
747 $section->{'records'} = '';
748 }
749 }
750 }
751
752 # Parse out the tuple trying to be updated, deleted or inserted.
753 my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m;
754 if ( $trigger ) {
755 $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug );
756 }
757
758 # Certain data may not be present. Make them '' if not present.
759 map { $section->{$_} ||= "" }
760 qw(child_index fk_name col_name parent_table parent_col);
761}
762
763# There are new-style and old-style record formats. See rem/rem0rec.c
764# TODO: write some tests for this
765sub parse_innodb_record_dump {
766 my ( $dump, $complete, $debug ) = @_;
767 return undef unless $dump;
768
769 my $result = {};
770
771 if ( $dump =~ m/PHYSICAL RECORD/ ) {
772 my $style = $dump =~ m/compact format/ ? 'new' : 'old';
773 $result->{'style'} = $style;
774
775 # This is a new-style record.
776 if ( $style eq 'new' ) {
777 @{$result}{qw( heap_no type num_fields info_bits )}
778 = $dump
779 =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m;
780 }
781
782 # OK, it's old-style. Unfortunately there are variations here too.
783 elsif ( $dump =~ m/-byte offs / ) {
784 # Older-old style.
785 @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
786 = $dump
787 =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m;
788 if ( $dump !~ m/-byte offs TRUE/ ) {
789 $result->{'byte_offset'} = 0;
790 }
791 }
792 else {
793 # Newer-old style.
794 @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
795 = $dump
796 =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m;
797 }
798
799 }
800 else {
801 $result->{'style'} = 'tuple';
802 @{$result}{qw( type num_fields )}
803 = $dump =~ m/^(DATA TUPLE): $d fields;$/m;
804 }
805
806 # Fill in default values for things that couldn't be parsed.
807 map { $result->{$_} ||= 0 }
808 qw(heap_no num_fields byte_offset info_bits);
809 map { $result->{$_} ||= '' }
810 qw(style type );
811
812 my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm;
813 $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ];
814
815 return $result;
816}
817
818# New/old-style applies here. See rem/rem0rec.c
819# $text should not include the leading space or the second trailing semicolon.
820sub parse_field {
821 my ( $text, $complete, $debug ) = @_;
822
823 # Sample fields:
824 # '4: SQL NULL, size 4 '
825 # '1: len 6; hex 000000005601; asc V ;'
826 # '6: SQL NULL'
827 # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)'
828 my ( $id, $nullsize, $len, $hex, $asc, $truncated );
829 ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/;
830 if ( !defined($id) ) {
831 ( $id ) = $text =~ m/^$d: SQL NULL$/;
832 }
833 if ( !defined($id) ) {
834 ( $id, $len, $hex, $asc, $truncated )
835 = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/;
836 }
837
838 die "Could not parse this field: '$text'" unless defined $id;
839 return {
840 id => $id,
841 len => defined($len) ? $len : defined($nullsize) ? $nullsize : 0,
842 'hex' => defined($hex) ? $hex : '',
843 asc => defined($asc) ? $asc : '',
844 trunc => $truncated ? 1 : 0,
845 };
846
847}
848
849sub parse_dl_section {
850 my ( $dl, $complete, $debug, $full ) = @_;
851 return unless $dl;
852 my $fulltext = $dl->{'fulltext'};
853 return 0 unless $fulltext;
854
855 my ( $ts ) = $fulltext =~ m/^$s$/m;
856 return 0 unless $ts;
857
858 $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
859 $dl->{'timestring'} = ts_to_string($dl->{'ts'});
860 $dl->{'txns'} = {};
861
862 my @sections
863 = $fulltext
864 =~ m{
865 ^\*{3}\s([^\n]*) # *** (1) WAITING FOR THIS...
866 (.*?) # Followed by anything, non-greedy
867 (?=(?:^\*{3})|\z) # Followed by another three stars or EOF
868 }gmsx;
869
870
871 # Loop through each section. There are no assumptions about how many
872 # there are, who holds and wants what locks, and who gets rolled back.
873 while ( my ($header, $body) = splice(@sections, 0, 2) ) {
874 my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/;
875 next unless $txn_id;
876 $dl->{'txns'}->{$txn_id} ||= {};
877 my $txn = $dl->{'txns'}->{$txn_id};
878
879 if ( $what eq 'TRANSACTION' ) {
880 $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full );
881 }
882 else {
883 push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full );
884 }
885 }
886
887 @{ $dl }{ qw(rolled_back) }
888 = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m;
889
890 # Make sure certain values aren't undef
891 map { $dl->{$_} ||= '' } qw(rolled_back);
892
893 delete $dl->{'fulltext'} unless $debug;
894 return 1;
895}
896
897sub parse_innodb_record_locks {
898 my ( $text, $complete, $debug, $full ) = @_;
899 my @result;
900
901 foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) {
902 my $hash = {};
903 @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) }
904 = $lock
905 =~ 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;
906 ( $hash->{'special'} )
907 = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m;
908 $hash->{'insert_intention'}
909 = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0;
910 $hash->{'waiting'}
911 = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0;
912
913 # Some things may not be in the text, so make sure they are not
914 # undef.
915 map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id);
916 map { $hash->{$_} ||= "" } qw(index special);
917 push @result, $hash;
918 }
919
920 return @result;
921}
922
923sub parse_tx_text {
924 my ( $txn, $complete, $debug, $full ) = @_;
925
926 my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id )
927 = $txn
928 =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m;
929 my ( $thread_status, $thread_decl_inside )
930 = $txn
931 =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m;
932
933 # Parsing the line that begins 'MySQL thread id' is complicated. The only
934 # thing always in the line is the thread and query id. See function
935 # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc.
936 my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m;
937 my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status );
938
939 if ( $thread_line ) {
940 # These parts can always be gotten.
941 ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m;
942
943 # If it's a master/slave thread, "Has (read|sent) all" may be the thread's
944 # proc_info. In these cases, there won't be any host/ip/user info
945 ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m;
946 if ( defined($query_status) ) {
947 $user = 'system user';
948 }
949
950 # It may be the case that the query id is the last thing in the line.
951 elsif ( $thread_line =~ m/query id \d+ / ) {
952 # The IP address is the only non-word thing left, so it's the most
953 # useful marker for where I have to start guessing.
954 ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m;
955 if ( defined $ip ) {
956 ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/;
957 }
958 else { # OK, there wasn't an IP address.
959 # There might not be ANYTHING except the query status.
960 ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/;
961 if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) {
962 # The remaining tokens are, in order: hostname, user, query_status.
963 # It's basically impossible to know which is which.
964 ( $hostname, $user, $query_status ) = $thread_line
965 =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m;
966 }
967 else {
968 $user = 'system user';
969 }
970 }
971 }
972 }
973
974 my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries )
975 = $txn
976 =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m;
977 my ( $lock_wait_time )
978 = $txn
979 =~ m/^------- TRX HAS BEEN WAITING $d SEC/m;
980
981 my $locks;
982 # If the transaction has locks, grab the locks.
983 if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) {
984 $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)];
985 }
986
987 my ( $tables_in_use, $tables_locked )
988 = $txn
989 =~ m/^mysql tables in use $d, locked $d$/m;
990 my ( $txn_doesnt_see_ge, $txn_sees_lt )
991 = $txn
992 =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m;
993 my $has_read_view = defined($txn_doesnt_see_ge);
994 # Only a certain number of bytes of the query text are included here, at least
995 # under some circumstances. Some versions include 300, some 600.
996 my ( $query_text )
997 = $txn
998 =~ m{
999 ^MySQL\sthread\sid\s[^\n]+\n # This comes before the query text
1000 (.*?) # The query text
1001 (?= # Followed by any of...
1002 ^Trx\sread\sview
1003 |^-------\sTRX\sHAS\sBEEN\sWAITING
1004 |^TABLE\sLOCK
1005 |^RECORD\sLOCKS\sspace\sid
1006 |^(?:---)?TRANSACTION
1007 |^\*\*\*\s\(\d\)
1008 |\Z
1009 )
1010 }xms;
1011 if ( $query_text ) {
1012 $query_text =~ s/\s+$//;
1013 }
1014 else {
1015 $query_text = '';
1016 }
1017
1018 my %stuff = (
1019 active_secs => $active_secs,
1020 has_read_view => $has_read_view,
1021 heap_size => $heap_size,
1022 hostname => $hostname,
1023 ip => $ip,
1024 lock_structs => $lock_structs,
1025 lock_wait_status => $lock_wait_status,
1026 lock_wait_time => $lock_wait_time,
1027 mysql_thread_id => $mysql_thread_id,
1028 os_thread_id => $os_thread_id,
1029 proc_no => $proc_no,
1030 query_id => $query_id,
1031 query_status => $query_status,
1032 query_text => $query_text,
1033 row_locks => $row_locks,
1034 tables_in_use => $tables_in_use,
1035 tables_locked => $tables_locked,
1036 thread_decl_inside => $thread_decl_inside,
1037 thread_status => $thread_status,
1038 txn_doesnt_see_ge => $txn_doesnt_see_ge,
1039 txn_id => $txn_id,
1040 txn_sees_lt => $txn_sees_lt,
1041 txn_status => $txn_status,
1042 undo_log_entries => $undo_log_entries,
1043 user => $user,
1044 );
1045 $stuff{'fulltext'} = $txn if $debug;
1046 $stuff{'locks'} = $locks if $locks;
1047
1048 # Some things may not be in the txn text, so make sure they are not
1049 # undef.
1050 map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs
1051 tables_in_use undo_log_entries tables_locked has_read_view
1052 thread_decl_inside lock_wait_time proc_no row_locks);
1053 map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge
1054 txn_sees_lt query_status ip query_text lock_wait_status user);
1055 $stuff{'hostname'} ||= $stuff{'ip'};
1056
1057 return \%stuff;
1058}
1059
1060sub parse_tx_section {
1061 my ( $section, $complete, $debug, $full ) = @_;
1062 return unless $section && $section->{'fulltext'};
1063 my $fulltext = $section->{'fulltext'};
1064 $section->{'transactions'} = [];
1065
1066 # Handle the individual transactions
1067 my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs;
1068 foreach my $txn ( @transactions ) {
1069 my $stuff = parse_tx_text( $txn, $complete, $debug, $full );
1070 delete $stuff->{'fulltext'} unless $debug;
1071 push @{$section->{'transactions'}}, $stuff;
1072 }
1073
1074 # Handle the general info
1075 @{$section}{ 'trx_id_counter' }
1076 = $fulltext =~ m/^Trx id counter $t$/m;
1077 @{$section}{ 'purge_done_for', 'purge_undo_for' }
1078 = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m;
1079 @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions
1080 = $fulltext =~ m/^History list length $d$/m;
1081 @{$section}{ 'num_lock_structs' }
1082 = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m;
1083 @{$section}{ 'is_truncated' }
1084 = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0;
1085
1086 # Fill in things that might not be present
1087 foreach ( qw(history_list_len) ) {
1088 $section->{$_} ||= 0;
1089 }
1090
1091 delete $section->{'fulltext'} unless $debug;
1092 return 1;
1093}
1094
1095# I've read the source for this section.
1096sub parse_ro_section {
1097 my ( $section, $complete, $debug, $full ) = @_;
1098 return unless $section && $section->{'fulltext'};
1099 my $fulltext = $section->{'fulltext'};
1100
1101 # Grab the info
1102 @{$section}{ 'queries_inside', 'queries_in_queue' }
1103 = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m;
1104 ( $section->{ 'read_views_open' } )
1105 = $fulltext =~ m/^$d read views open inside InnoDB$/m;
1106 ( $section->{ 'n_reserved_extents' } )
1107 = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m;
1108 @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' }
1109 = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m;
1110 @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' }
1111 = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m;
1112 @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' }
1113 = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m;
1114 $section->{'main_thread_proc_no'} ||= 0;
1115
1116 map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents);
1117 delete $section->{'fulltext'} unless $debug;
1118 return 1;
1119}
1120
1121sub parse_lg_section {
1122 my ( $section, $complete, $debug, $full ) = @_;
1123 return unless $section;
1124 my $fulltext = $section->{'fulltext'};
1125
1126 # Grab the info
1127 ( $section->{ 'log_seq_no' } )
1128 = $fulltext =~ m/Log sequence number \s*(\d.*)$/m;
1129 ( $section->{ 'log_flushed_to' } )
1130 = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m;
1131 ( $section->{ 'last_chkp' } )
1132 = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m;
1133 @{$section}{ 'pending_log_writes', 'pending_chkp_writes' }
1134 = $fulltext =~ m/$d pending log writes, $d pending chkp writes/;
1135 @{$section}{ 'log_ios_done', 'log_ios_s' }
1136 = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#;
1137
1138 delete $section->{'fulltext'} unless $debug;
1139 return 1;
1140}
1141
1142sub parse_ib_section {
1143 my ( $section, $complete, $debug, $full ) = @_;
1144 return unless $section && $section->{'fulltext'};
1145 my $fulltext = $section->{'fulltext'};
1146
1147 # Some servers will output ibuf information for tablespace 0, as though there
1148 # might be many tablespaces with insert buffers. (In practice I believe
1149 # the source code shows there will only ever be one). I have to parse both
1150 # cases here, but I assume there will only be one.
1151 @{$section}{ 'size', 'free_list_len', 'seg_size' }
1152 = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m;
1153 @{$section}{ 'inserts', 'merged_recs', 'merges' }
1154 = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m;
1155
1156 @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' }
1157 = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m;
1158 @{$section}{ 'hash_searches_s', 'non_hash_searches_s' }
1159 = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m;
1160
1161 delete $section->{'fulltext'} unless $debug;
1162 return 1;
1163}
1164
1165sub parse_wait_array {
1166 my ( $text, $complete, $debug, $full ) = @_;
1167 my %result;
1168
1169 @result{ qw(thread waited_at_filename waited_at_line waited_secs) }
1170 = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m;
1171
1172 # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED,
1173 # there will be different text output
1174 if ( $text =~ m/^Mutex at/m ) {
1175 $result{'request_type'} = 'M';
1176 @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) }
1177 = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m;
1178 @result{ qw( waiters_flag )}
1179 = $text =~ m/^waiters flag $d$/m;
1180 }
1181 else {
1182 @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) }
1183 = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m;
1184 @result{ qw( writer_thread writer_lock_mode ) }
1185 = $text =~ m/^a writer \(thread id $d\) has reserved it in mode (.*)$/m;
1186 @result{ qw( num_readers waiters_flag )}
1187 = $text =~ m/^number of readers $d, waiters flag $d$/m;
1188 @result{ qw(last_s_file_name last_s_line ) }
1189 = $text =~ m/Last time read locked in file $fl$/m;
1190 @result{ qw(last_x_file_name last_x_line ) }
1191 = $text =~ m/Last time write locked in file $fl$/m;
1192 }
1193
1194 $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1;
1195 $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0;
1196
1197 # Because there are two code paths, some things won't get set.
1198 map { $result{$_} ||= '' }
1199 qw(last_s_file_name last_x_file_name writer_lock_mode);
1200 map { $result{$_} ||= 0 }
1201 qw(num_readers lock_var last_s_line last_x_line writer_thread);
1202
1203 return \%result;
1204}
1205
1206sub parse_sm_section {
1207 my ( $section, $complete, $debug, $full ) = @_;
1208 return 0 unless $section && $section->{'fulltext'};
1209 my $fulltext = $section->{'fulltext'};
1210
1211 # Grab the info
1212 @{$section}{ 'reservation_count', 'signal_count' }
1213 = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m;
1214 @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' }
1215 = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m;
1216 @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' }
1217 = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m;
1218
1219 # Look for info on waits.
1220 my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms;
1221 $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ];
1222 $section->{'wait_array_size'} = scalar(@waits);
1223
1224 delete $section->{'fulltext'} unless $debug;
1225 return 1;
1226}
1227
1228# I've read the source for this section.
1229sub parse_bp_section {
1230 my ( $section, $complete, $debug, $full ) = @_;
1231 return unless $section && $section->{'fulltext'};
1232 my $fulltext = $section->{'fulltext'};
1233
1234 # Grab the info
1235 @{$section}{ 'total_mem_alloc', 'add_pool_alloc' }
1236 = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m;
1237 @{$section}{'dict_mem_alloc'} = $fulltext =~ m/Dictionary memory allocated $d/;
1238 @{$section}{'awe_mem_alloc'} = $fulltext =~ m/$d MB of AWE memory/;
1239 @{$section}{'buf_pool_size'} = $fulltext =~ m/^Buffer pool size\s*$d$/m;
1240 @{$section}{'buf_free'} = $fulltext =~ m/^Free buffers\s*$d$/m;
1241 @{$section}{'pages_total'} = $fulltext =~ m/^Database pages\s*$d$/m;
1242 @{$section}{'pages_modified'} = $fulltext =~ m/^Modified db pages\s*$d$/m;
1243 @{$section}{'pages_read', 'pages_created', 'pages_written'}
1244 = $fulltext =~ m/^Pages read $d, created $d, written $d$/m;
1245 @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'}
1246 = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m;
1247 @{$section}{'buf_pool_hits', 'buf_pool_reads'}
1248 = $fulltext =~ m{Buffer pool hit rate $d / $d$}m;
1249 if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) {
1250 @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0);
1251 @{$section}{'buf_pool_hit_rate'} = '--';
1252 }
1253 else {
1254 @{$section}{'buf_pool_hit_rate'}
1255 = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m;
1256 }
1257 @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m;
1258 @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' }
1259 = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m;
1260
1261 map { $section->{$_} ||= 0 }
1262 qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page
1263 awe_mem_alloc dict_mem_alloc);
1264 @{$section}{'writes_pending'} = List::Util::sum(
1265 @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) });
1266
1267 delete $section->{'fulltext'} unless $debug;
1268 return 1;
1269}
1270
1271# I've read the source for this.
1272sub parse_io_section {
1273 my ( $section, $complete, $debug, $full ) = @_;
1274 return unless $section && $section->{'fulltext'};
1275 my $fulltext = $section->{'fulltext'};
1276 $section->{'threads'} = {};
1277
1278 # Grab the I/O thread info
1279 my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm;
1280 foreach my $thread (@threads) {
1281 my ( $tid, $state, $purpose, $event_set )
1282 = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m;
1283 if ( defined $tid ) {
1284 $section->{'threads'}->{$tid} = {
1285 thread => $tid,
1286 state => $state,
1287 purpose => $purpose,
1288 event_set => $event_set ? 1 : 0,
1289 };
1290 }
1291 }
1292
1293 # Grab the reads/writes/flushes info
1294 @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' }
1295 = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m;
1296 @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' }
1297 = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m;
1298 @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' }
1299 = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m;
1300 @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' }
1301 = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m;
1302 @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' }
1303 = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m;
1304 @{$section}{ 'pending_preads', 'pending_pwrites' }
1305 = $fulltext =~ m/$d pending preads, $d pending pwrites$/m;
1306 @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0)
1307 unless defined($section->{'pending_preads'});
1308
1309 delete $section->{'fulltext'} unless $debug;
1310 return 1;
1311}
1312
1313sub _debug {
1314 my ( $debug, $msg ) = @_;
1315 if ( $debug ) {
1316 die $msg;
1317 }
1318 else {
1319 warn $msg;
1320 }
1321 return 1;
1322}
1323
13241;
1325
1326# end_of_package InnoDBParser
1327
1328package main;
1329
1330use sigtrap qw(handler finish untrapped normal-signals);
1331
1332use Data::Dumper;
1333use DBI;
1334use English qw(-no_match_vars);
1335use File::Basename qw(dirname);
1336use File::Temp;
1337use Getopt::Long;
1338use List::Util qw(max min maxstr sum);
1339use POSIX qw(ceil);
1340use Time::HiRes qw(time sleep);
1341use Term::ReadKey qw(ReadMode ReadKey);
1342
1343# License and warranty information. {{{1
1344# ###########################################################################
1345
1346my $innotop_license = <<"LICENSE";
1347
1348This is innotop version $VERSION, a MySQL and InnoDB monitor.
1349
1350This program is copyright (c) 2006 Baron Schwartz.
1351Feedback and improvements are welcome.
1352
1353THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
1354WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1355MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1356
1357This program is free software; you can redistribute it and/or modify it under
1358the terms of the GNU General Public License as published by the Free Software
1359Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
1360systems, you can issue `man perlgpl' or `man perlartistic' to read these
1361licenses.
1362
1363You should have received a copy of the GNU General Public License along with
1364this program; if not, write to the Free Software Foundation, Inc., 59 Temple
1365Place, Suite 330, Boston, MA 02111-1307 USA.
1366LICENSE
1367
1368# Configuration information and global setup {{{1
1369# ###########################################################################
1370
1371# Really, really, super-global variables.
1372my @config_versions = (
1373 "000-000-000", "001-003-000", # config file was one big name-value hash.
1374 "001-003-000", "001-004-002", # config file contained non-user-defined stuff.
1375);
1376
1377my $clear_screen_sub;
1378my $dsn_parser = new DSNParser();
1379
1380# This defines expected properties and defaults for the column definitions that
1381# eventually end up in tbl_meta.
1382my %col_props = (
1383 hdr => '',
1384 just => '-',
1385 dec => 0, # Whether to align the column on the decimal point
1386 num => 0,
1387 label => '',
1388 user => 0,
1389 src => '',
1390 tbl => '', # Helps when writing/reading custom columns in config files
1391 minw => 0,
1392 maxw => 0,
1393 trans => [],
1394 agg => 'first', # Aggregate function
1395 aggonly => 0, # Whether to show only when tbl_meta->{aggregate} is true
1396);
1397
1398# Actual DBI connections to MySQL servers.
1399my %dbhs;
1400
1401# Command-line parameters {{{2
1402# ###########################################################################
1403
1404my @opt_spec = (
1405 { s => 'help', d => 'Show this help message' },
1406 { s => 'color|C!', d => 'Use terminal coloring (default)', c => 'color' },
1407 { s => 'config|c=s', d => 'Config file to read' },
1408 { s => 'nonint|n', d => 'Non-interactive, output tab-separated fields' },
1409 { s => 'count=i', d => 'Number of updates before exiting' },
1410 { s => 'delay|d=f', d => 'Delay between updates in seconds', c => 'interval' },
1411 { s => 'mode|m=s', d => 'Operating mode to start in', c => 'mode' },
1412 { s => 'inc|i!', d => 'Measure incremental differences', c => 'status_inc' },
1413 { s => 'write|w', d => 'Write running configuration into home directory if no config files were loaded' },
1414 { s => 'skipcentral|s', d => 'Skip reading the central configuration file' },
1415 { s => 'version', d => 'Output version information and exit' },
1416 { s => 'user|u=s', d => 'User for login if not current user' },
1417 { s => 'password|p=s', d => 'Password to use for connection' },
1418 { s => 'host|h=s', d => 'Connect to host' },
1419 { s => 'port|P=i', d => 'Port number to use for connection' },
1420);
1421
1422# This is the container for the command-line options' values to be stored in
1423# after processing. Initial values are defaults.
1424my %opts = (
1425 n => !( -t STDIN && -t STDOUT ), # If in/out aren't to terminals, we're interactive
1426);
1427# Post-process...
1428my %opt_seen;
1429foreach my $spec ( @opt_spec ) {
1430 my ( $long, $short ) = $spec->{s} =~ m/^(\w+)(?:\|([^!+=]*))?/;
1431 $spec->{k} = $short || $long;
1432 $spec->{l} = $long;
1433 $spec->{t} = $short;
1434 $spec->{n} = $spec->{s} =~ m/!/;
1435 $opts{$spec->{k}} = undef unless defined $opts{$spec->{k}};
1436 die "Duplicate option $spec->{k}" if $opt_seen{$spec->{k}}++;
1437}
1438
1439Getopt::Long::Configure('no_ignore_case', 'bundling');
1440GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1;
1441
1442if ( $opts{version} ) {
1443 print "innotop Ver $VERSION\n";
1444 exit(0);
1445}
1446
1447if ( $opts{c} and ! -f $opts{c} ) {
1448 print $opts{c} . " doesn't exist. Exiting.\n";
1449 exit(1);
1450}
1451if ( $opts{'help'} ) {
1452 print "Usage: innotop <options> <innodb-status-file>\n\n";
1453 my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec);
1454 foreach my $spec ( sort { $a->{l} cmp $b->{l} } @opt_spec ) {
1455 my $long = $spec->{n} ? "[no]$spec->{l}" : $spec->{l};
1456 my $short = $spec->{t} ? "-$spec->{t}" : '';
1457 printf(" --%-${maxw}s %-4s %s\n", $long, $short, $spec->{d});
1458 }
1459 print <<USAGE;
1460
1461innotop is a MySQL and InnoDB transaction/status monitor, like 'top' for
1462MySQL. It displays queries, InnoDB transactions, lock waits, deadlocks,
1463foreign key errors, open tables, replication status, buffer information,
1464row operations, logs, I/O operations, load graph, and more. You can
1465monitor many servers at once with innotop.
1466
1467USAGE
1468 exit(1);
1469}
1470
1471# Meta-data (table definitions etc) {{{2
1472# ###########################################################################
1473
1474# Expressions {{{3
1475# Convenience so I can copy/paste these in several places...
1476# ###########################################################################
1477my %exprs = (
1478 Host => q{my $host = host || hostname || ''; ($host) = $host =~ m/^((?:[\d.]+(?=:))|(?:[a-zA-Z]\w+))/; return $host || ''},
1479 Port => q{my ($p) = host =~ m/:(.*)$/; return $p || 0},
1480 OldVersions => q{dulint_to_int(IB_tx_trx_id_counter) - dulint_to_int(IB_tx_purge_done_for)},
1481 MaxTxnTime => q/max(map{ $_->{active_secs} } @{ IB_tx_transactions }) || 0/,
1482 NumTxns => q{scalar @{ IB_tx_transactions } },
1483 DirtyBufs => q{ $cur->{IB_bp_pages_modified} / ($cur->{IB_bp_buf_pool_size} || 1) },
1484 BufPoolFill => q{ $cur->{IB_bp_pages_total} / ($cur->{IB_bp_buf_pool_size} || 1) },
1485 ServerLoad => q{ $cur->{Threads_connected}/(Questions||1)/Uptime_hires },
1486 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},
1487 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',
1488 QcacheHitRatio => q{(Qcache_hits||0)/(((Com_select||0)+(Qcache_hits||0))||1)},
1489);
1490
1491# ###########################################################################
1492# Column definitions {{{3
1493# Defines every column in every table. A named column has the following
1494# properties:
1495# * hdr Column header/title
1496# * label Documentation for humans.
1497# * num Whether it's numeric (for sorting).
1498# * just Alignment; generated from num, user-overridable in tbl_meta
1499# * minw, maxw Auto-generated, user-overridable.
1500# Values from this hash are just copied to tbl_meta, which is where everything
1501# else in the program should read from.
1502# ###########################################################################
1503
1504my %columns = (
1505 active_secs => { hdr => 'SecsActive', num => 1, label => 'Seconds transaction has been active', },
1506 add_pool_alloc => { hdr => 'Add\'l Pool', num => 1, label => 'Additonal pool allocated' },
1507 attempted_op => { hdr => 'Action', num => 0, label => 'The action that caused the error' },
1508 awe_mem_alloc => { hdr => 'AWE Memory', num => 1, label => '[Windows] AWE memory allocated' },
1509 binlog_cache_overflow => { hdr => 'Binlog Cache', num => 1, label => 'Transactions too big for binlog cache that went to disk' },
1510 binlog_do_db => { hdr => 'Binlog Do DB', num => 0, label => 'binlog-do-db setting' },
1511 binlog_ignore_db => { hdr => 'Binlog Ignore DB', num => 0, label => 'binlog-ignore-db setting' },
1512 bps_in => { hdr => 'BpsIn', num => 1, label => 'Bytes per second received by the server', },
1513 bps_out => { hdr => 'BpsOut', num => 1, label => 'Bytes per second sent by the server', },
1514 buf_free => { hdr => 'Free Bufs', num => 1, label => 'Buffers free in the buffer pool' },
1515 buf_pool_hit_rate => { hdr => 'Hit Rate', num => 0, label => 'Buffer pool hit rate' },
1516 buf_pool_hits => { hdr => 'Hits', num => 1, label => 'Buffer pool hits' },
1517 buf_pool_reads => { hdr => 'Reads', num => 1, label => 'Buffer pool reads' },
1518 buf_pool_size => { hdr => 'Size', num => 1, label => 'Buffer pool size' },
1519 bufs_in_node_heap => { hdr => 'Node Heap Bufs', num => 1, label => 'Buffers in buffer pool node heap' },
1520 bytes_behind_master => { hdr => 'ByteLag', num => 1, label => 'Bytes the slave lags the master in binlog' },
1521 cell_event_set => { hdr => 'Ending?', num => 1, label => 'Whether the cell event is set' },
1522 cell_waiting => { hdr => 'Waiting?', num => 1, label => 'Whether the cell is waiting' },
1523 child_db => { hdr => 'Child DB', num => 0, label => 'The database of the child table' },
1524 child_index => { hdr => 'Child Index', num => 0, label => 'The index in the child table' },
1525 child_table => { hdr => 'Child Table', num => 0, label => 'The child table' },
1526 cmd => { hdr => 'Cmd', num => 0, label => 'Type of command being executed', },
1527 cnt => { hdr => 'Cnt', num => 0, label => 'Count', agg => 'count', aggonly => 1 },
1528 connect_retry => { hdr => 'Connect Retry', num => 1, label => 'Slave connect-retry timeout' },
1529 cxn => { hdr => 'CXN', num => 0, label => 'Connection from which the data came', },
1530 db => { hdr => 'DB', num => 0, label => 'Current database', },
1531 dict_mem_alloc => { hdr => 'Dict Mem', num => 1, label => 'Dictionary memory allocated' },
1532 dirty_bufs => { hdr => 'Dirty Buf', num => 1, label => 'Dirty buffer pool pages' },
1533 dl_txn_num => { hdr => 'Num', num => 0, label => 'Deadlocked transaction number', },
1534 event_set => { hdr => 'Evt Set?', num => 1, label => '[Win32] if a wait event is set', },
1535 exec_master_log_pos => { hdr => 'Exec Master Log Pos', num => 1, label => 'Exec Master Log Position' },
1536 fk_name => { hdr => 'Constraint', num => 0, label => 'The name of the FK constraint' },
1537 free_list_len => { hdr => 'Free List Len', num => 1, label => 'Length of the free list' },
1538 has_read_view => { hdr => 'Rd View', num => 1, label => 'Whether the transaction has a read view' },
1539 hash_searches_s => { hdr => 'Hash/Sec', num => 1, label => 'Number of hash searches/sec' },
1540 hash_table_size => { hdr => 'Size', num => 1, label => 'Number of non-hash searches/sec' },
1541 heap_no => { hdr => 'Heap', num => 1, label => 'Heap number' },
1542 heap_size => { hdr => 'Heap', num => 1, label => 'Heap size' },
1543 history_list_len => { hdr => 'History', num => 1, label => 'History list length' },
1544 host_and_domain => { hdr => 'Host', num => 0, label => 'Hostname/IP and domain' },
1545 host_and_port => { hdr => 'Host/IP', num => 0, label => 'Hostname or IP address, and port number', },
1546 hostname => { hdr => 'Host', num => 0, label => 'Hostname' },
1547 index => { hdr => 'Index', num => 0, label => 'The index involved' },
1548 index_ref => { hdr => 'Index Ref', num => 0, label => 'Index referenced' },
1549 info => { hdr => 'Query', num => 0, label => 'Info or the current query', },
1550 insert_intention => { hdr => 'Ins Intent', num => 1, label => 'Whether the thread was trying to insert' },
1551 inserts => { hdr => 'Inserts', num => 1, label => 'Inserts' },
1552 io_bytes_s => { hdr => 'Bytes/Sec', num => 1, label => 'Average I/O bytes/sec' },
1553 io_flush_type => { hdr => 'Flush Type', num => 0, label => 'I/O Flush Type' },
1554 io_fsyncs_s => { hdr => 'fsyncs/sec', num => 1, label => 'I/O fsyncs/sec' },
1555 io_reads_s => { hdr => 'Reads/Sec', num => 1, label => 'Average I/O reads/sec' },
1556 io_writes_s => { hdr => 'Writes/Sec', num => 1, label => 'Average I/O writes/sec' },
1557 ip => { hdr => 'IP', num => 0, label => 'IP address' },
1558 is_name_locked => { hdr => 'Locked', num => 1, label => 'Whether table is name locked', },
1559 key_buffer_hit => { hdr => 'KCacheHit', num => 1, label => 'Key cache hit ratio', },
1560 key_len => { hdr => 'Key Length', num => 1, label => 'Number of bytes used in the key' },
1561 last_chkp => { hdr => 'Last Checkpoint', num => 0, label => 'Last log checkpoint' },
1562 last_errno => { hdr => 'Last Errno', num => 1, label => 'Last error number' },
1563 last_error => { hdr => 'Last Error', num => 0, label => 'Last error' },
1564 last_s_file_name => { hdr => 'S-File', num => 0, label => 'Filename where last read locked' },
1565 last_s_line => { hdr => 'S-Line', num => 1, label => 'Line where last read locked' },
1566 last_x_file_name => { hdr => 'X-File', num => 0, label => 'Filename where last write locked' },
1567 last_x_line => { hdr => 'X-Line', num => 1, label => 'Line where last write locked' },
1568 last_pct => { hdr => 'Pct', num => 1, label => 'Last Percentage' },
1569 last_total => { hdr => 'Last Total', num => 1, label => 'Last Total' },
1570 last_value => { hdr => 'Last Incr', num => 1, label => 'Last Value' },
1571 load => { hdr => 'Load', num => 1, label => 'Server load' },
1572 lock_cfile_name => { hdr => 'Crtd File', num => 0, label => 'Filename where lock created' },
1573 lock_cline => { hdr => 'Crtd Line', num => 1, label => 'Line where lock created' },
1574 lock_mem_addr => { hdr => 'Addr', num => 0, label => 'The lock memory address' },
1575 lock_mode => { hdr => 'Mode', num => 0, label => 'The lock mode' },
1576 lock_structs => { hdr => 'LStrcts', num => 1, label => 'Number of lock structs' },
1577 lock_type => { hdr => 'Type', num => 0, label => 'The lock type' },
1578 lock_var => { hdr => 'Lck Var', num => 1, label => 'The lock variable' },
1579 lock_wait_time => { hdr => 'Wait', num => 1, label => 'How long txn has waited for a lock' },
1580 log_flushed_to => { hdr => 'Flushed To', num => 0, label => 'Log position flushed to' },
1581 log_ios_done => { hdr => 'IO Done', num => 1, label => 'Log I/Os done' },
1582 log_ios_s => { hdr => 'IO/Sec', num => 1, label => 'Average log I/Os per sec' },
1583 log_seq_no => { hdr => 'Sequence No.', num => 0, label => 'Log sequence number' },
1584 main_thread_id => { hdr => 'Main Thread ID', num => 1, label => 'Main thread ID' },
1585 main_thread_proc_no => { hdr => 'Main Thread Proc', num => 1, label => 'Main thread process number' },
1586 main_thread_state => { hdr => 'Main Thread State', num => 0, label => 'Main thread state' },
1587 master_file => { hdr => 'File', num => 0, label => 'Master file' },
1588 master_host => { hdr => 'Master', num => 0, label => 'Master server hostname' },
1589 master_log_file => { hdr => 'Master Log File', num => 0, label => 'Master log file' },
1590 master_port => { hdr => 'Master Port', num => 1, label => 'Master port' },
1591 master_pos => { hdr => 'Position', num => 1, label => 'Master position' },
1592 master_ssl_allowed => { hdr => 'Master SSL Allowed', num => 0, label => 'Master SSL Allowed' },
1593 master_ssl_ca_file => { hdr => 'Master SSL CA File', num => 0, label => 'Master SSL Cert Auth File' },
1594 master_ssl_ca_path => { hdr => 'Master SSL CA Path', num => 0, label => 'Master SSL Cert Auth Path' },
1595 master_ssl_cert => { hdr => 'Master SSL Cert', num => 0, label => 'Master SSL Cert' },
1596 master_ssl_cipher => { hdr => 'Master SSL Cipher', num => 0, label => 'Master SSL Cipher' },
1597 master_ssl_key => { hdr => 'Master SSL Key', num => 0, label => 'Master SSL Key' },
1598 master_user => { hdr => 'Master User', num => 0, label => 'Master username' },
1599 max_txn => { hdr => 'MaxTxnTime', num => 1, label => 'MaxTxn' },
1600 merged_recs => { hdr => 'Merged Recs', num => 1, label => 'Merged records' },
1601 merges => { hdr => 'Merges', num => 1, label => 'Merges' },
1602 mutex_os_waits => { hdr => 'Waits', num => 1, label => 'Mutex OS Waits' },
1603 mutex_spin_rounds => { hdr => 'Rounds', num => 1, label => 'Mutex Spin Rounds' },
1604 mutex_spin_waits => { hdr => 'Spins', num => 1, label => 'Mutex Spin Waits' },
1605 mysql_thread_id => { hdr => 'ID', num => 1, label => 'MySQL connection (thread) ID', },
1606 name => { hdr => 'Name', num => 0, label => 'Variable Name' },
1607 n_bits => { hdr => '# Bits', num => 1, label => 'Number of bits' },
1608 non_hash_searches_s => { hdr => 'Non-Hash/Sec', num => 1, label => 'Non-hash searches/sec' },
1609 num_deletes => { hdr => 'Del', num => 1, label => 'Number of deletes' },
1610 num_deletes_sec => { hdr => 'Del/Sec', num => 1, label => 'Number of deletes' },
1611 num_inserts => { hdr => 'Ins', num => 1, label => 'Number of inserts' },
1612 num_inserts_sec => { hdr => 'Ins/Sec', num => 1, label => 'Number of inserts' },
1613 num_readers => { hdr => 'Readers', num => 1, label => 'Number of readers' },
1614 num_reads => { hdr => 'Read', num => 1, label => 'Number of reads' },
1615 num_reads_sec => { hdr => 'Read/Sec', num => 1, label => 'Number of reads' },
1616 num_res_ext => { hdr => 'BTree Extents', num => 1, label => 'Number of extents reserved for B-Tree' },
1617 num_rows => { hdr => 'Row Count', num => 1, label => 'Number of rows estimated to examine' },
1618 num_times_open => { hdr => 'In Use', num => 1, label => '# times table is opened', },
1619 num_txns => { hdr => 'Txns', num => 1, label => 'Number of transactions' },
1620 num_updates => { hdr => 'Upd', num => 1, label => 'Number of updates' },
1621 num_updates_sec => { hdr => 'Upd/Sec', num => 1, label => 'Number of updates' },
1622 os_file_reads => { hdr => 'OS Reads', num => 1, label => 'OS file reads' },
1623 os_file_writes => { hdr => 'OS Writes', num => 1, label => 'OS file writes' },
1624 os_fsyncs => { hdr => 'OS fsyncs', num => 1, label => 'OS fsyncs' },
1625 os_thread_id => { hdr => 'OS Thread', num => 1, label => 'The operating system thread ID' },
1626 p_aio_writes => { hdr => 'Async Wrt', num => 1, label => 'Pending asynchronous I/O writes' },
1627 p_buf_pool_flushes => { hdr => 'Buffer Pool Flushes', num => 1, label => 'Pending buffer pool flushes' },
1628 p_ibuf_aio_reads => { hdr => 'IBuf Async Rds', num => 1, label => 'Pending insert buffer asynch I/O reads' },
1629 p_log_flushes => { hdr => 'Log Flushes', num => 1, label => 'Pending log flushes' },
1630 p_log_ios => { hdr => 'Log I/Os', num => 1, label => 'Pending log I/O operations' },
1631 p_normal_aio_reads => { hdr => 'Async Rds', num => 1, label => 'Pending asynchronous I/O reads' },
1632 p_preads => { hdr => 'preads', num => 1, label => 'Pending p-reads' },
1633 p_pwrites => { hdr => 'pwrites', num => 1, label => 'Pending p-writes' },
1634 p_sync_ios => { hdr => 'Sync I/Os', num => 1, label => 'Pending synchronous I/O operations' },
1635 page_creates_sec => { hdr => 'Creates/Sec', num => 1, label => 'Page creates/sec' },
1636 page_no => { hdr => 'Page', num => 1, label => 'Page number' },
1637 page_reads_sec => { hdr => 'Reads/Sec', num => 1, label => 'Page reads per second' },
1638 page_writes_sec => { hdr => 'Writes/Sec', num => 1, label => 'Page writes per second' },
1639 pages_created => { hdr => 'Created', num => 1, label => 'Pages created' },
1640 pages_modified => { hdr => 'Dirty Pages', num => 1, label => 'Pages modified (dirty)' },
1641 pages_read => { hdr => 'Reads', num => 1, label => 'Pages read' },
1642 pages_total => { hdr => 'Pages', num => 1, label => 'Pages total' },
1643 pages_written => { hdr => 'Writes', num => 1, label => 'Pages written' },
1644 parent_col => { hdr => 'Parent Column', num => 0, label => 'The referred column in the parent table', },
1645 parent_db => { hdr => 'Parent DB', num => 0, label => 'The database of the parent table' },
1646 parent_index => { hdr => 'Parent Index', num => 0, label => 'The referred index in the parent table' },
1647 parent_table => { hdr => 'Parent Table', num => 0, label => 'The parent table' },
1648 part_id => { hdr => 'Part ID', num => 1, label => 'Sub-part ID of the query' },
1649 partitions => { hdr => 'Partitions', num => 0, label => 'Query partitions used' },
1650 pct => { hdr => 'Pct', num => 1, label => 'Percentage' },
1651 pending_chkp_writes => { hdr => 'Chkpt Writes', num => 1, label => 'Pending log checkpoint writes' },
1652 pending_log_writes => { hdr => 'Log Writes', num => 1, label => 'Pending log writes' },
1653 port => { hdr => 'Port', num => 1, label => 'Client port number', },
1654 possible_keys => { hdr => 'Poss. Keys', num => 0, label => 'Possible keys' },
1655 proc_no => { hdr => 'Proc', num => 1, label => 'Process number' },
1656 q_cache_hit => { hdr => 'QCacheHit', num => 1, label => 'Query cache hit ratio', },
1657 qps => { hdr => 'QPS', num => 1, label => 'How many queries/sec', },
1658 queries_in_queue => { hdr => 'Queries Queued', num => 1, label => 'Queries in queue' },
1659 queries_inside => { hdr => 'Queries Inside', num => 1, label => 'Queries inside InnoDB' },
1660 query_id => { hdr => 'Query ID', num => 1, label => 'Query ID' },
1661 query_status => { hdr => 'Query Status', num => 0, label => 'The query status' },
1662 query_text => { hdr => 'Query Text', num => 0, label => 'The query text' },
1663 questions => { hdr => 'Questions', num => 1, label => 'How many queries the server has gotten', },
1664 read_master_log_pos => { hdr => 'Read Master Pos', num => 1, label => 'Read master log position' },
1665 read_views_open => { hdr => 'Rd Views', num => 1, label => 'Number of read views open' },
1666 reads_pending => { hdr => 'Pending Reads', num => 1, label => 'Reads pending' },
1667 relay_log_file => { hdr => 'Relay File', num => 0, label => 'Relay log file' },
1668 relay_log_pos => { hdr => 'Relay Pos', num => 1, label => 'Relay log position' },
1669 relay_log_size => { hdr => 'Relay Size', num => 1, label => 'Relay log size' },
1670 relay_master_log_file => { hdr => 'Relay Master File', num => 0, label => 'Relay master log file' },
1671 replicate_do_db => { hdr => 'Do DB', num => 0, label => 'Replicate-do-db setting' },
1672 replicate_do_table => { hdr => 'Do Table', num => 0, label => 'Replicate-do-table setting' },
1673 replicate_ignore_db => { hdr => 'Ignore DB', num => 0, label => 'Replicate-ignore-db setting' },
1674 replicate_ignore_table => { hdr => 'Ignore Table', num => 0, label => 'Replicate-do-table setting' },
1675 replicate_wild_do_table => { hdr => 'Wild Do Table', num => 0, label => 'Replicate-wild-do-table setting' },
1676 replicate_wild_ignore_table => { hdr => 'Wild Ignore Table', num => 0, label => 'Replicate-wild-ignore-table setting' },
1677 request_type => { hdr => 'Type', num => 0, label => 'Type of lock the thread waits for' },
1678 reservation_count => { hdr => 'ResCnt', num => 1, label => 'Reservation Count' },
1679 row_locks => { hdr => 'RLocks', num => 1, label => 'Number of row locks' },
1680 rw_excl_os_waits => { hdr => 'RW Waits', num => 1, label => 'R/W Excl. OS Waits' },
1681 rw_excl_spins => { hdr => 'RW Spins', num => 1, label => 'R/W Excl. Spins' },
1682 rw_shared_os_waits => { hdr => 'Sh Waits', num => 1, label => 'R/W Shared OS Waits' },
1683 rw_shared_spins => { hdr => 'Sh Spins', num => 1, label => 'R/W Shared Spins' },
1684 scan_type => { hdr => 'Type', num => 0, label => 'Scan type in chosen' },
1685 seg_size => { hdr => 'Seg. Size', num => 1, label => 'Segment size' },
1686 select_type => { hdr => 'Select Type', num => 0, label => 'Type of select used' },
1687 signal_count => { hdr => 'Signals', num => 1, label => 'Signal Count' },
1688 size => { hdr => 'Size', num => 1, label => 'Size of the tablespace' },
1689 skip_counter => { hdr => 'Skip Counter', num => 1, label => 'Skip counter' },
1690 slave_catchup_rate => { hdr => 'Catchup', num => 1, label => 'How fast the slave is catching up in the binlog' },
1691 slave_io_running => { hdr => 'Slave-IO', num => 0, label => 'Whether the slave I/O thread is running' },
1692 slave_io_state => { hdr => 'Slave IO State', num => 0, label => 'Slave I/O thread state' },
1693 slave_open_temp_tables => { hdr => 'Temp', num => 1, label => 'Slave open temp tables' },
1694 slave_sql_running => { hdr => 'Slave-SQL', num => 0, label => 'Whether the slave SQL thread is running' },
1695 slow => { hdr => 'Slow', num => 1, label => 'How many slow queries', },
1696 space_id => { hdr => 'Space', num => 1, label => 'Tablespace ID' },
1697 special => { hdr => 'Special', num => 0, label => 'Special/Other info' },
1698 state => { hdr => 'State', num => 0, label => 'Connection state', maxw => 18, },
1699 tables_in_use => { hdr => 'Tbl Used', num => 1, label => 'Number of tables in use' },
1700 tables_locked => { hdr => 'Tbl Lck', num => 1, label => 'Number of tables locked' },
1701 tbl => { hdr => 'Table', num => 0, label => 'Table', },
1702 thread => { hdr => 'Thread', num => 1, label => 'Thread number' },
1703 thread_decl_inside => { hdr => 'Thread Inside', num => 0, label => 'What the thread is declared inside' },
1704 thread_purpose => { hdr => 'Purpose', num => 0, label => "The thread's purpose" },
1705 thread_status => { hdr => 'Thread Status', num => 0, label => 'The thread status' },
1706 time => { hdr => 'Time', num => 1, label => 'Time since the last event', },
1707 time_behind_master => { hdr => 'TimeLag', num => 1, label => 'Time slave lags master' },
1708 timestring => { hdr => 'Timestring', num => 0, label => 'Time the event occurred' },
1709 total => { hdr => 'Total', num => 1, label => 'Total' },
1710 total_mem_alloc => { hdr => 'Memory', num => 1, label => 'Total memory allocated' },
1711 truncates => { hdr => 'Trunc', num => 0, label => 'Whether the deadlock is truncating InnoDB status' },
1712 txn_doesnt_see_ge => { hdr => "Txn Won't See", num => 0, label => 'Where txn read view is limited' },
1713 txn_id => { hdr => 'ID', num => 0, label => 'Transaction ID' },
1714 txn_sees_lt => { hdr => 'Txn Sees', num => 1, label => 'Where txn read view is limited' },
1715 txn_status => { hdr => 'Txn Status', num => 0, label => 'Transaction status' },
1716 txn_time_remain => { hdr => 'Remaining', num => 1, label => 'Time until txn rollback/commit completes' },
1717 undo_log_entries => { hdr => 'Undo', num => 1, label => 'Number of undo log entries' },
1718 undo_for => { hdr => 'Undo', num => 0, label => 'Undo for' },
1719 until_condition => { hdr => 'Until Condition', num => 0, label => 'Slave until condition' },
1720 until_log_file => { hdr => 'Until Log File', num => 0, label => 'Slave until log file' },
1721 until_log_pos => { hdr => 'Until Log Pos', num => 1, label => 'Slave until log position' },
1722 used_cells => { hdr => 'Cells Used', num => 1, label => 'Number of cells used' },
1723 used_bufs => { hdr => 'Used Bufs', num => 1, label => 'Number of buffer pool pages used' },
1724 user => { hdr => 'User', num => 0, label => 'Database username', },
1725 value => { hdr => 'Value', num => 1, label => 'Value' },
1726 versions => { hdr => 'Versions', num => 1, label => 'Number of InnoDB MVCC versions unpurged' },
1727 victim => { hdr => 'Victim', num => 0, label => 'Whether this txn was the deadlock victim' },
1728 wait_array_size => { hdr => 'Wait Array Size', num => 1, label => 'Wait Array Size' },
1729 wait_status => { hdr => 'Lock Status', num => 0, label => 'Status of txn locks' },
1730 waited_at_filename => { hdr => 'File', num => 0, label => 'Filename at which thread waits' },
1731 waited_at_line => { hdr => 'Line', num => 1, label => 'Line at which thread waits' },
1732 waiters_flag => { hdr => 'Waiters', num => 1, label => 'Waiters Flag' },
1733 waiting => { hdr => 'Waiting', num => 1, label => 'Whether lock is being waited for' },
1734 when => { hdr => 'When', num => 0, label => 'Time scale' },
1735 writer_lock_mode => { hdr => 'Wrtr Lck Mode', num => 0, label => 'Writer lock mode' },
1736 writer_thread => { hdr => 'Wrtr Thread', num => 1, label => 'Writer thread ID' },
1737 writes_pending => { hdr => 'Writes', num => 1, label => 'Number of writes pending' },
1738 writes_pending_flush_list => { hdr => 'Flush List Writes', num => 1, label => 'Number of flush list writes pending' },
1739 writes_pending_lru => { hdr => 'LRU Writes', num => 1, label => 'Number of LRU writes pending' },
1740 writes_pending_single_page => { hdr => '1-Page Writes', num => 1, label => 'Number of 1-page writes pending' },
1741);
1742
1743# Apply a default property or three. By default, columns are not width-constrained,
1744# aligned left, and sorted alphabetically, not numerically.
1745foreach my $col ( values %columns ) {
1746 map { $col->{$_} ||= 0 } qw(num minw maxw);
1747 $col->{just} = $col->{num} ? '' : '-';
1748}
1749
1750# Filters {{{3
1751# This hash defines every filter that can be applied to a table. These
1752# become part of tbl_meta as well. Each filter is just an expression that
1753# returns true or false.
1754# Properties of each entry:
1755# * func: the subroutine
1756# * name: the name, repeated
1757# * user: whether it's a user-defined filter (saved in config)
1758# * text: text of the subroutine
1759# * note: explanation
1760my %filters = ();
1761
1762# These are pre-processed to live in %filters above, by compiling them.
1763my %builtin_filters = (
1764 hide_self => {
1765 text => <<' END',
1766 return ( !$set->{info} || $set->{info} ne 'SHOW FULL PROCESSLIST' )
1767 && ( !$set->{query_text} || $set->{query_text} !~ m/INNODB STATUS$/ );
1768 END
1769 note => 'Removes the innotop processes from the list',
1770 tbls => [qw(innodb_transactions processlist)],
1771 },
1772 hide_inactive => {
1773 text => <<' END',
1774 return ( !defined($set->{txn_status}) || $set->{txn_status} ne 'not started' )
1775 && ( !defined($set->{cmd}) || $set->{cmd} !~ m/Sleep|Binlog Dump/ )
1776 && ( !defined($set->{info}) || $set->{info} =~ m/\S/ );
1777 END
1778 note => 'Removes processes which are not doing anything',
1779 tbls => [qw(innodb_transactions processlist)],
1780 },
1781 hide_slave_io => {
1782 text => <<' END',
1783 return !$set->{state} || $set->{state} !~ m/^(?:Waiting for master|Has read all relay)/;
1784 END
1785 note => 'Removes slave I/O threads from the list',
1786 tbls => [qw(processlist slave_io_status)],
1787 },
1788 table_is_open => {
1789 text => <<' END',
1790 return $set->{num_times_open} + $set->{is_name_locked};
1791 END
1792 note => 'Removes tables that are not in use or locked',
1793 tbls => [qw(open_tables)],
1794 },
1795 cxn_is_master => {
1796 text => <<' END',
1797 return $set->{master_file} ? 1 : 0;
1798 END
1799 note => 'Removes servers that are not masters',
1800 tbls => [qw(master_status)],
1801 },
1802 cxn_is_slave => {
1803 text => <<' END',
1804 return $set->{master_host} ? 1 : 0;
1805 END
1806 note => 'Removes servers that are not slaves',
1807 tbls => [qw(slave_io_status slave_sql_status)],
1808 },
1809 thd_is_not_waiting => {
1810 text => <<' END',
1811 return $set->{thread_status} !~ m#waiting for i/o request#;
1812 END
1813 note => 'Removes idle I/O threads',
1814 tbls => [qw(io_threads)],
1815 },
1816);
1817foreach my $key ( keys %builtin_filters ) {
1818 my ( $sub, $err ) = compile_filter($builtin_filters{$key}->{text});
1819 $filters{$key} = {
1820 func => $sub,
1821 text => $builtin_filters{$key}->{text},
1822 user => 0,
1823 name => $key, # useful for later
1824 note => $builtin_filters{$key}->{note},
1825 tbls => $builtin_filters{$key}->{tbls},
1826 }
1827}
1828
1829# Variable sets {{{3
1830# Sets (arrayrefs) of variables that are used in S mode. They are read/written to
1831# the config file.
1832my %var_sets = (
1833 general => {
1834 text => join(
1835 ', ',
1836 'set_precision(Questions/Uptime_hires) as QPS',
1837 'set_precision(Com_commit/Uptime_hires) as Commit_PS',
1838 'set_precision((Com_rollback||0)/(Com_commit||1)) as Rollback_Commit',
1839 'set_precision(('
1840 . join('+', map { "($_||0)" }
1841 qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
1842 Com_replace_select Com_select Com_update Com_update_multi))
1843 . ')/(Com_commit||1)) as Write_Commit',
1844 'set_precision((Com_select+(Qcache_hits||0))/(('
1845 . join('+', map { "($_||0)" }
1846 qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
1847 Com_replace_select Com_select Com_update Com_update_multi))
1848 . ')||1)) as R_W_Ratio',
1849 'set_precision(Opened_tables/Uptime_hires) as Opens_PS',
1850 'percent($cur->{Open_tables}/($cur->{table_cache})) as Table_Cache_Used',
1851 'set_precision(Threads_created/Uptime_hires) as Threads_PS',
1852 'percent($cur->{Threads_cached}/($cur->{thread_cache_size}||1)) as Thread_Cache_Used',
1853 'percent($cur->{Max_used_connections}/($cur->{max_connections}||1)) as CXN_Used_Ever',
1854 'percent($cur->{Threads_connected}/($cur->{max_connections}||1)) as CXN_Used_Now',
1855 ),
1856 },
1857 commands => {
1858 text => join(
1859 ', ',
1860 qw(Uptime Questions Com_delete Com_delete_multi Com_insert
1861 Com_insert_select Com_replace Com_replace_select Com_select Com_update
1862 Com_update_multi)
1863 ),
1864 },
1865 query_status => {
1866 text => join(
1867 ',',
1868 qw( Uptime Select_full_join Select_full_range_join Select_range
1869 Select_range_check Select_scan Slow_queries Sort_merge_passes
1870 Sort_range Sort_rows Sort_scan)
1871 ),
1872 },
1873 innodb => {
1874 text => join(
1875 ',',
1876 qw( Uptime Innodb_row_lock_current_waits Innodb_row_lock_time
1877 Innodb_row_lock_time_avg Innodb_row_lock_time_max Innodb_row_lock_waits
1878 Innodb_rows_deleted Innodb_rows_inserted Innodb_rows_read
1879 Innodb_rows_updated)
1880 ),
1881 },
1882 txn => {
1883 text => join(
1884 ',',
1885 qw( Uptime Com_begin Com_commit Com_rollback Com_savepoint
1886 Com_xa_commit Com_xa_end Com_xa_prepare Com_xa_recover Com_xa_rollback
1887 Com_xa_start)
1888 ),
1889 },
1890 key_cache => {
1891 text => join(
1892 ',',
1893 qw( Uptime Key_blocks_not_flushed Key_blocks_unused Key_blocks_used
1894 Key_read_requests Key_reads Key_write_requests Key_writes )
1895 ),
1896 },
1897 query_cache => {
1898 text => join(
1899 ',',
1900 "percent($exprs{QcacheHitRatio}) as Hit_Pct",
1901 'set_precision((Qcache_hits||0)/(Qcache_inserts||1)) as Hit_Ins',
1902 'set_precision((Qcache_lowmem_prunes||0)/Uptime_hires) as Lowmem_Prunes_sec',
1903 'percent(1-((Qcache_free_blocks||0)/(Qcache_total_blocks||1))) as Blocks_used',
1904 qw( Qcache_free_blocks Qcache_free_memory Qcache_not_cached Qcache_queries_in_cache)
1905 ),
1906 },
1907 handler => {
1908 text => join(
1909 ',',
1910 qw( Uptime Handler_read_key Handler_read_first Handler_read_next
1911 Handler_read_prev Handler_read_rnd Handler_read_rnd_next Handler_delete
1912 Handler_update Handler_write)
1913 ),
1914 },
1915 cxns_files_threads => {
1916 text => join(
1917 ',',
1918 qw( Uptime Aborted_clients Aborted_connects Bytes_received Bytes_sent
1919 Compression Connections Created_tmp_disk_tables Created_tmp_files
1920 Created_tmp_tables Max_used_connections Open_files Open_streams
1921 Open_tables Opened_tables Table_locks_immediate Table_locks_waited
1922 Threads_cached Threads_connected Threads_created Threads_running)
1923 ),
1924 },
1925 prep_stmt => {
1926 text => join(
1927 ',',
1928 qw( Uptime Com_dealloc_sql Com_execute_sql Com_prepare_sql Com_reset
1929 Com_stmt_close Com_stmt_execute Com_stmt_fetch Com_stmt_prepare
1930 Com_stmt_reset Com_stmt_send_long_data )
1931 ),
1932 },
1933 innodb_health => {
1934 text => join(
1935 ',',
1936 "$exprs{OldVersions} as OldVersions",
1937 qw(IB_sm_mutex_spin_waits IB_sm_mutex_spin_rounds IB_sm_mutex_os_waits),
1938 "$exprs{NumTxns} as NumTxns",
1939 "$exprs{MaxTxnTime} as MaxTxnTime",
1940 qw(IB_ro_queries_inside IB_ro_queries_in_queue),
1941 "set_precision($exprs{DirtyBufs} * 100) as dirty_bufs",
1942 "set_precision($exprs{BufPoolFill} * 100) as buf_fill",
1943 qw(IB_bp_pages_total IB_bp_pages_read IB_bp_pages_written IB_bp_pages_created)
1944 ),
1945 },
1946 innodb_health2 => {
1947 text => join(
1948 ', ',
1949 'percent(1-((Innodb_buffer_pool_pages_free||0)/($cur->{Innodb_buffer_pool_pages_total}||1))) as BP_page_cache_usage',
1950 'percent(1-((Innodb_buffer_pool_reads||0)/(Innodb_buffer_pool_read_requests||1))) as BP_cache_hit_ratio',
1951 'Innodb_buffer_pool_wait_free',
1952 'Innodb_log_waits',
1953 ),
1954 },
1955 slow_queries => {
1956 text => join(
1957 ', ',
1958 'set_precision(Slow_queries/Uptime_hires) as Slow_PS',
1959 'set_precision(Select_full_join/Uptime_hires) as Full_Join_PS',
1960 'percent(Select_full_join/(Com_select||1)) as Full_Join_Ratio',
1961 ),
1962 },
1963);
1964
1965# Server sets {{{3
1966# Defines sets of servers between which the user can quickly switch.
1967my %server_groups;
1968
1969# Connections {{{3
1970# This hash defines server connections. Each connection is a string that can be passed to
1971# the DBI connection. These are saved in the connections section in the config file.
1972my %connections;
1973# Defines the parts of connections.
1974my @conn_parts = qw(user have_user pass have_pass dsn savepass dl_table);
1975
1976# Graph widths {{{3
1977# This hash defines the max values seen for various status/variable values, for graphing.
1978# These are stored in their own section in the config file. These are just initial values:
1979my %mvs = (
1980 Com_select => 50,
1981 Com_insert => 50,
1982 Com_update => 50,
1983 Com_delete => 50,
1984 Questions => 100,
1985);
1986
1987# ###########################################################################
1988# Valid Term::ANSIColor color strings.
1989# ###########################################################################
1990my %ansicolors = map { $_ => 1 }
1991 qw( black blink blue bold clear concealed cyan dark green magenta on_black
1992 on_blue on_cyan on_green on_magenta on_red on_white on_yellow red reset
1993 reverse underline underscore white yellow);
1994
1995# ###########################################################################
1996# Valid comparison operators for color rules
1997# ###########################################################################
1998my %comp_ops = (
1999 '==' => 'Numeric equality',
2000 '>' => 'Numeric greater-than',
2001 '<' => 'Numeric less-than',
2002 '>=' => 'Numeric greater-than/equal',
2003 '<=' => 'Numeric less-than/equal',
2004 '!=' => 'Numeric not-equal',
2005 'eq' => 'String equality',
2006 'gt' => 'String greater-than',
2007 'lt' => 'String less-than',
2008 'ge' => 'String greater-than/equal',
2009 'le' => 'String less-than/equal',
2010 'ne' => 'String not-equal',
2011 '=~' => 'Pattern match',
2012 '!~' => 'Negated pattern match',
2013);
2014
2015# ###########################################################################
2016# Valid aggregate functions.
2017# ###########################################################################
2018my %agg_funcs = (
2019 first => sub {
2020 return $_[0]
2021 },
2022 count => sub {
2023 return 0 + @_;
2024 },
2025 avg => sub {
2026 my @args = grep { defined $_ } @_;
2027 return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1);
2028 },
2029 sum => sub {
2030 my @args = grep { defined $_ } @_;
2031 return sum(@args);
2032 }
2033);
2034
2035# ###########################################################################
2036# Valid functions for transformations.
2037# ###########################################################################
2038my %trans_funcs = (
2039 shorten => \&shorten,
2040 secs_to_time => \&secs_to_time,
2041 no_ctrl_char => \&no_ctrl_char,
2042 percent => \&percent,
2043 commify => \&commify,
2044 dulint_to_int => \&dulint_to_int,
2045 set_precision => \&set_precision,
2046);
2047
2048# Table definitions {{{3
2049# This hash defines every table that can get displayed in every mode. Each
2050# table specifies columns and column data sources. The column is
2051# defined by the %columns hash.
2052#
2053# Example: foo => { src => 'bar' } means the foo column (look at
2054# $columns{foo} for its definition) gets its data from the 'bar' element of
2055# the current data set, whatever that is.
2056#
2057# These columns are post-processed after being defined, because they get stuff
2058# from %columns. After all the config is loaded for columns, there's more
2059# post-processing too; the subroutines compiled from src get added to
2060# the hash elements for extract_values to use.
2061# ###########################################################################
2062
2063my %tbl_meta = (
2064 adaptive_hash_index => {
2065 capt => 'Adaptive Hash Index',
2066 cust => {},
2067 cols => {
2068 cxn => { src => 'cxn' },
2069 hash_table_size => { src => 'IB_ib_hash_table_size', trans => [qw(shorten)], },
2070 used_cells => { src => 'IB_ib_used_cells' },
2071 bufs_in_node_heap => { src => 'IB_ib_bufs_in_node_heap' },
2072 hash_searches_s => { src => 'IB_ib_hash_searches_s' },
2073 non_hash_searches_s => { src => 'IB_ib_non_hash_searches_s' },
2074 },
2075 visible => [ qw(cxn hash_table_size used_cells bufs_in_node_heap hash_searches_s non_hash_searches_s) ],
2076 filters => [],
2077 sort_cols => 'cxn',
2078 sort_dir => '1',
2079 innodb => 'ib',
2080 group_by => [],
2081 aggregate => 0,
2082 },
2083 buffer_pool => {
2084 capt => 'Buffer Pool',
2085 cust => {},
2086 cols => {
2087 cxn => { src => 'cxn' },
2088 total_mem_alloc => { src => 'IB_bp_total_mem_alloc', trans => [qw(shorten)], },
2089 awe_mem_alloc => { src => 'IB_bp_awe_mem_alloc', trans => [qw(shorten)], },
2090 add_pool_alloc => { src => 'IB_bp_add_pool_alloc', trans => [qw(shorten)], },
2091 buf_pool_size => { src => 'IB_bp_buf_pool_size', trans => [qw(shorten)], },
2092 buf_free => { src => 'IB_bp_buf_free' },
2093 buf_pool_hit_rate => { src => 'IB_bp_buf_pool_hit_rate' },
2094 buf_pool_reads => { src => 'IB_bp_buf_pool_reads' },
2095 buf_pool_hits => { src => 'IB_bp_buf_pool_hits' },
2096 dict_mem_alloc => { src => 'IB_bp_dict_mem_alloc' },
2097 pages_total => { src => 'IB_bp_pages_total' },
2098 pages_modified => { src => 'IB_bp_pages_modified' },
2099 reads_pending => { src => 'IB_bp_reads_pending' },
2100 writes_pending => { src => 'IB_bp_writes_pending' },
2101 writes_pending_lru => { src => 'IB_bp_writes_pending_lru' },
2102 writes_pending_flush_list => { src => 'IB_bp_writes_pending_flush_list' },
2103 writes_pending_single_page => { src => 'IB_bp_writes_pending_single_page' },
2104 page_creates_sec => { src => 'IB_bp_page_creates_sec' },
2105 page_reads_sec => { src => 'IB_bp_page_reads_sec' },
2106 page_writes_sec => { src => 'IB_bp_page_writes_sec' },
2107 pages_created => { src => 'IB_bp_pages_created' },
2108 pages_read => { src => 'IB_bp_pages_read' },
2109 pages_written => { src => 'IB_bp_pages_written' },
2110 },
2111 visible => [ qw(cxn buf_pool_size buf_free pages_total pages_modified buf_pool_hit_rate total_mem_alloc add_pool_alloc)],
2112 filters => [],
2113 sort_cols => 'cxn',
2114 sort_dir => '1',
2115 innodb => 'bp',
2116 group_by => [],
2117 aggregate => 0,
2118 },
2119 # TODO: a new step in set_to_tbl: join result to itself, grouped?
2120 # TODO: this would also enable pulling Q and T data together.
2121 # TODO: using a SQL-ish language would also allow pivots to be easier -- treat the pivoted data as a view and SELECT from it.
2122 cmd_summary => {
2123 capt => 'Command Summary',
2124 cust => {},
2125 cols => {
2126 name => { src => 'name' },
2127 total => { src => 'total' },
2128 value => { src => 'value', agg => 'sum'},
2129 pct => { src => 'value/total', trans => [qw(percent)] },
2130 last_total => { src => 'last_total' },
2131 last_value => { src => 'last_value', agg => 'sum'},
2132 last_pct => { src => 'last_value/last_total', trans => [qw(percent)] },
2133 },
2134 visible => [qw(name value pct last_value last_pct)],
2135 filters => [qw()],
2136 sort_cols => '-value',
2137 sort_dir => '1',
2138 innodb => '',
2139 group_by => [qw(name)],
2140 aggregate => 1,
2141 },
2142 deadlock_locks => {
2143 capt => 'Deadlock Locks',
2144 cust => {},
2145 cols => {
2146 cxn => { src => 'cxn' },
2147 mysql_thread_id => { src => 'mysql_thread_id' },
2148 dl_txn_num => { src => 'dl_txn_num' },
2149 lock_type => { src => 'lock_type' },
2150 space_id => { src => 'space_id' },
2151 page_no => { src => 'page_no' },
2152 heap_no => { src => 'heap_no' },
2153 n_bits => { src => 'n_bits' },
2154 index => { src => 'index' },
2155 db => { src => 'db' },
2156 tbl => { src => 'table' },
2157 lock_mode => { src => 'lock_mode' },
2158 special => { src => 'special' },
2159 insert_intention => { src => 'insert_intention' },
2160 waiting => { src => 'waiting' },
2161 },
2162 visible => [ qw(cxn mysql_thread_id waiting lock_mode db tbl index special insert_intention)],
2163 filters => [],
2164 sort_cols => 'cxn mysql_thread_id',
2165 sort_dir => '1',
2166 innodb => 'dl',
2167 group_by => [],
2168 aggregate => 0,
2169 },
2170 deadlock_transactions => {
2171 capt => 'Deadlock Transactions',
2172 cust => {},
2173 cols => {
2174 cxn => { src => 'cxn' },
2175 active_secs => { src => 'active_secs' },
2176 dl_txn_num => { src => 'dl_txn_num' },
2177 has_read_view => { src => 'has_read_view' },
2178 heap_size => { src => 'heap_size' },
2179 host_and_domain => { src => 'hostname' },
2180 hostname => { src => $exprs{Host} },
2181 ip => { src => 'ip' },
2182 lock_structs => { src => 'lock_structs' },
2183 lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
2184 mysql_thread_id => { src => 'mysql_thread_id' },
2185 os_thread_id => { src => 'os_thread_id' },
2186 proc_no => { src => 'proc_no' },
2187 query_id => { src => 'query_id' },
2188 query_status => { src => 'query_status' },
2189 query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
2190 row_locks => { src => 'row_locks' },
2191 tables_in_use => { src => 'tables_in_use' },
2192 tables_locked => { src => 'tables_locked' },
2193 thread_decl_inside => { src => 'thread_decl_inside' },
2194 thread_status => { src => 'thread_status' },
2195 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ] },
2196 timestring => { src => 'timestring' },
2197 txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' },
2198 txn_id => { src => 'txn_id' },
2199 txn_sees_lt => { src => 'txn_sees_lt' },
2200 txn_status => { src => 'txn_status' },
2201 truncates => { src => 'truncates' },
2202 undo_log_entries => { src => 'undo_log_entries' },
2203 user => { src => 'user' },
2204 victim => { src => 'victim' },
2205 wait_status => { src => 'lock_wait_status' },
2206 },
2207 visible => [ qw(cxn mysql_thread_id timestring user hostname victim time undo_log_entries lock_structs query_text)],
2208 filters => [],
2209 sort_cols => 'cxn mysql_thread_id',
2210 sort_dir => '1',
2211 innodb => 'dl',
2212 group_by => [],
2213 aggregate => 0,
2214 },
2215 explain => {
2216 capt => 'EXPLAIN Results',
2217 cust => {},
2218 cols => {
2219 part_id => { src => 'id' },
2220 select_type => { src => 'select_type' },
2221 tbl => { src => 'table' },
2222 partitions => { src => 'partitions' },
2223 scan_type => { src => 'type' },
2224 possible_keys => { src => 'possible_keys' },
2225 index => { src => 'key' },
2226 key_len => { src => 'key_len' },
2227 index_ref => { src => 'ref' },
2228 num_rows => { src => 'rows' },
2229 special => { src => 'extra' },
2230 },
2231 visible => [ qw(select_type tbl partitions scan_type possible_keys index key_len index_ref num_rows special)],
2232 filters => [],
2233 sort_cols => '',
2234 sort_dir => '1',
2235 innodb => '',
2236 group_by => [],
2237 aggregate => 0,
2238 },
2239 file_io_misc => {
2240 capt => 'File I/O Misc',
2241 cust => {},
2242 cols => {
2243 cxn => { src => 'cxn' },
2244 io_bytes_s => { src => 'IB_io_avg_bytes_s' },
2245 io_flush_type => { src => 'IB_io_flush_type' },
2246 io_fsyncs_s => { src => 'IB_io_fsyncs_s' },
2247 io_reads_s => { src => 'IB_io_reads_s' },
2248 io_writes_s => { src => 'IB_io_writes_s' },
2249 os_file_reads => { src => 'IB_io_os_file_reads' },
2250 os_file_writes => { src => 'IB_io_os_file_writes' },
2251 os_fsyncs => { src => 'IB_io_os_fsyncs' },
2252 },
2253 visible => [ qw(cxn os_file_reads os_file_writes os_fsyncs io_reads_s io_writes_s io_bytes_s)],
2254 filters => [],
2255 sort_cols => 'cxn',
2256 sort_dir => '1',
2257 innodb => 'io',
2258 group_by => [],
2259 aggregate => 0,
2260 },
2261 fk_error => {
2262 capt => 'Foreign Key Error Info',
2263 cust => {},
2264 cols => {
2265 timestring => { src => 'IB_fk_timestring' },
2266 child_db => { src => 'IB_fk_child_db' },
2267 child_table => { src => 'IB_fk_child_table' },
2268 child_index => { src => 'IB_fk_child_index' },
2269 fk_name => { src => 'IB_fk_fk_name' },
2270 parent_db => { src => 'IB_fk_parent_db' },
2271 parent_table => { src => 'IB_fk_parent_table' },
2272 parent_col => { src => 'IB_fk_parent_col' },
2273 parent_index => { src => 'IB_fk_parent_index' },
2274 attempted_op => { src => 'IB_fk_attempted_op' },
2275 },
2276 visible => [ qw(timestring child_db child_table child_index parent_db parent_table parent_col parent_index fk_name attempted_op)],
2277 filters => [],
2278 sort_cols => '',
2279 sort_dir => '1',
2280 innodb => 'fk',
2281 group_by => [],
2282 aggregate => 0,
2283 },
2284 insert_buffers => {
2285 capt => 'Insert Buffers',
2286 cust => {},
2287 cols => {
2288 cxn => { src => 'cxn' },
2289 inserts => { src => 'IB_ib_inserts' },
2290 merged_recs => { src => 'IB_ib_merged_recs' },
2291 merges => { src => 'IB_ib_merges' },
2292 size => { src => 'IB_ib_size' },
2293 free_list_len => { src => 'IB_ib_free_list_len' },
2294 seg_size => { src => 'IB_ib_seg_size' },
2295 },
2296 visible => [ qw(cxn inserts merged_recs merges size free_list_len seg_size)],
2297 filters => [],
2298 sort_cols => 'cxn',
2299 sort_dir => '1',
2300 innodb => 'ib',
2301 group_by => [],
2302 aggregate => 0,
2303 },
2304 innodb_locks => {
2305 capt => 'InnoDB Locks',
2306 cust => {},
2307 cols => {
2308 cxn => { src => 'cxn' },
2309 db => { src => 'db' },
2310 index => { src => 'index' },
2311 insert_intention => { src => 'insert_intention' },
2312 lock_mode => { src => 'lock_mode' },
2313 lock_type => { src => 'lock_type' },
2314 lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
2315 mysql_thread_id => { src => 'mysql_thread_id' },
2316 n_bits => { src => 'n_bits' },
2317 page_no => { src => 'page_no' },
2318 space_id => { src => 'space_id' },
2319 special => { src => 'special' },
2320 tbl => { src => 'table' },
2321 'time' => { src => 'active_secs', hdr => 'Active', trans => [ qw(secs_to_time) ] },
2322 txn_id => { src => 'txn_id' },
2323 waiting => { src => 'waiting' },
2324 },
2325 visible => [ qw(cxn mysql_thread_id lock_type waiting lock_wait_time time lock_mode db tbl index insert_intention special)],
2326 filters => [],
2327 sort_cols => 'cxn -lock_wait_time',
2328 sort_dir => '1',
2329 innodb => 'tx',
2330 colors => [
2331 { col => 'lock_wait_time', op => '>', arg => 60, color => 'red' },
2332 { col => 'lock_wait_time', op => '>', arg => 30, color => 'yellow' },
2333 { col => 'lock_wait_time', op => '>', arg => 10, color => 'green' },
2334 ],
2335 group_by => [],
2336 aggregate => 0,
2337 },
2338 innodb_transactions => {
2339 capt => 'InnoDB Transactions',
2340 cust => {},
2341 cols => {
2342 cxn => { src => 'cxn' },
2343 active_secs => { src => 'active_secs' },
2344 has_read_view => { src => 'has_read_view' },
2345 heap_size => { src => 'heap_size' },
2346 hostname => { src => $exprs{Host} },
2347 ip => { src => 'ip' },
2348 wait_status => { src => 'lock_wait_status' },
2349 lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
2350 lock_structs => { src => 'lock_structs' },
2351 mysql_thread_id => { src => 'mysql_thread_id' },
2352 os_thread_id => { src => 'os_thread_id' },
2353 proc_no => { src => 'proc_no' },
2354 query_id => { src => 'query_id' },
2355 query_status => { src => 'query_status' },
2356 query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
2357 txn_time_remain => { src => $exprs{TxnTimeRemain}, trans => [ qw(secs_to_time) ] },
2358 row_locks => { src => 'row_locks' },
2359 tables_in_use => { src => 'tables_in_use' },
2360 tables_locked => { src => 'tables_locked' },
2361 thread_decl_inside => { src => 'thread_decl_inside' },
2362 thread_status => { src => 'thread_status' },
2363 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ], agg => 'sum' },
2364 txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' },
2365 txn_id => { src => 'txn_id' },
2366 txn_sees_lt => { src => 'txn_sees_lt' },
2367 txn_status => { src => 'txn_status', minw => 10, maxw => 10 },
2368 undo_log_entries => { src => 'undo_log_entries' },
2369 user => { src => 'user', maxw => 10 },
2370 cnt => { src => 'mysql_thread_id', minw => 0 },
2371 },
2372 visible => [ qw(cxn cnt mysql_thread_id user hostname txn_status time undo_log_entries query_text)],
2373 filters => [ qw( hide_self hide_inactive ) ],
2374 sort_cols => '-active_secs txn_status cxn mysql_thread_id',
2375 sort_dir => '1',
2376 innodb => 'tx',
2377 hide_caption => 1,
2378 colors => [
2379 { col => 'wait_status', op => 'eq', arg => 'LOCK WAIT', color => 'black on_red' },
2380 { col => 'time', op => '>', arg => 600, color => 'red' },
2381 { col => 'time', op => '>', arg => 300, color => 'yellow' },
2382 { col => 'time', op => '>', arg => 60, color => 'green' },
2383 { col => 'time', op => '>', arg => 30, color => 'cyan' },
2384 { col => 'txn_status', op => 'eq', arg => 'not started', color => 'white' },
2385 ],
2386 group_by => [ qw(cxn txn_status) ],
2387 aggregate => 0,
2388 },
2389 io_threads => {
2390 capt => 'I/O Threads',
2391 cust => {},
2392 cols => {
2393 cxn => { src => 'cxn' },
2394 thread => { src => 'thread' },
2395 thread_purpose => { src => 'purpose' },
2396 event_set => { src => 'event_set' },
2397 thread_status => { src => 'state' },
2398 },
2399 visible => [ qw(cxn thread thread_purpose thread_status)],
2400 filters => [ qw() ],
2401 sort_cols => 'cxn thread',
2402 sort_dir => '1',
2403 innodb => 'io',
2404 group_by => [],
2405 aggregate => 0,
2406 },
2407 log_statistics => {
2408 capt => 'Log Statistics',
2409 cust => {},
2410 cols => {
2411 cxn => { src => 'cxn' },
2412 last_chkp => { src => 'IB_lg_last_chkp' },
2413 log_flushed_to => { src => 'IB_lg_log_flushed_to' },
2414 log_ios_done => { src => 'IB_lg_log_ios_done' },
2415 log_ios_s => { src => 'IB_lg_log_ios_s' },
2416 log_seq_no => { src => 'IB_lg_log_seq_no' },
2417 pending_chkp_writes => { src => 'IB_lg_pending_chkp_writes' },
2418 pending_log_writes => { src => 'IB_lg_pending_log_writes' },
2419 },
2420 visible => [ qw(cxn log_seq_no log_flushed_to last_chkp log_ios_done log_ios_s)],
2421 filters => [],
2422 sort_cols => 'cxn',
2423 sort_dir => '1',
2424 innodb => 'lg',
2425 group_by => [],
2426 aggregate => 0,
2427 },
2428 master_status => {
2429 capt => 'Master Status',
2430 cust => {},
2431 cols => {
2432 cxn => { src => 'cxn' },
2433 binlog_do_db => { src => 'binlog_do_db' },
2434 binlog_ignore_db => { src => 'binlog_ignore_db' },
2435 master_file => { src => 'file' },
2436 master_pos => { src => 'position' },
2437 binlog_cache_overflow => { src => '(Binlog_cache_disk_use||0)/(Binlog_cache_use||1)', trans => [ qw(percent) ] },
2438 },
2439 visible => [ qw(cxn master_file master_pos binlog_cache_overflow)],
2440 filters => [ qw(cxn_is_master) ],
2441 sort_cols => 'cxn',
2442 sort_dir => '1',
2443 innodb => '',
2444 group_by => [],
2445 aggregate => 0,
2446 },
2447 pending_io => {
2448 capt => 'Pending I/O',
2449 cust => {},
2450 cols => {
2451 cxn => { src => 'cxn' },
2452 p_normal_aio_reads => { src => 'IB_io_pending_normal_aio_reads' },
2453 p_aio_writes => { src => 'IB_io_pending_aio_writes' },
2454 p_ibuf_aio_reads => { src => 'IB_io_pending_ibuf_aio_reads' },
2455 p_sync_ios => { src => 'IB_io_pending_sync_ios' },
2456 p_buf_pool_flushes => { src => 'IB_io_pending_buffer_pool_flushes' },
2457 p_log_flushes => { src => 'IB_io_pending_log_flushes' },
2458 p_log_ios => { src => 'IB_io_pending_log_ios' },
2459 p_preads => { src => 'IB_io_pending_preads' },
2460 p_pwrites => { src => 'IB_io_pending_pwrites' },
2461 },
2462 visible => [ qw(cxn p_normal_aio_reads p_aio_writes p_ibuf_aio_reads p_sync_ios p_log_flushes p_log_ios)],
2463 filters => [],
2464 sort_cols => 'cxn',
2465 sort_dir => '1',
2466 innodb => 'io',
2467 group_by => [],
2468 aggregate => 0,
2469 },
2470 open_tables => {
2471 capt => 'Open Tables',
2472 cust => {},
2473 cols => {
2474 cxn => { src => 'cxn' },
2475 db => { src => 'database' },
2476 tbl => { src => 'table' },
2477 num_times_open => { src => 'in_use' },
2478 is_name_locked => { src => 'name_locked' },
2479 },
2480 visible => [ qw(cxn db tbl num_times_open is_name_locked)],
2481 filters => [ qw(table_is_open) ],
2482 sort_cols => '-num_times_open cxn db tbl',
2483 sort_dir => '1',
2484 innodb => '',
2485 group_by => [],
2486 aggregate => 0,
2487 },
2488 page_statistics => {
2489 capt => 'Page Statistics',
2490 cust => {},
2491 cols => {
2492 cxn => { src => 'cxn' },
2493 pages_read => { src => 'IB_bp_pages_read' },
2494 pages_written => { src => 'IB_bp_pages_written' },
2495 pages_created => { src => 'IB_bp_pages_created' },
2496 page_reads_sec => { src => 'IB_bp_page_reads_sec' },
2497 page_writes_sec => { src => 'IB_bp_page_writes_sec' },
2498 page_creates_sec => { src => 'IB_bp_page_creates_sec' },
2499 },
2500 visible => [ qw(cxn pages_read pages_written pages_created page_reads_sec page_writes_sec page_creates_sec)],
2501 filters => [],
2502 sort_cols => 'cxn',
2503 sort_dir => '1',
2504 innodb => 'bp',
2505 group_by => [],
2506 aggregate => 0,
2507 },
2508 processlist => {
2509 capt => 'MySQL Process List',
2510 cust => {},
2511 cols => {
2512 cxn => { src => 'cxn', minw => 6, maxw => 10 },
2513 mysql_thread_id => { src => 'id', minw => 6, maxw => 0 },
2514 user => { src => 'user', minw => 5, maxw => 8 },
2515 hostname => { src => $exprs{Host}, minw => 13, maxw => 8, },
2516 port => { src => $exprs{Port}, minw => 0, maxw => 0, },
2517 host_and_port => { src => 'host', minw => 0, maxw => 0 },
2518 db => { src => 'db', minw => 6, maxw => 12 },
2519 cmd => { src => 'command', minw => 5, maxw => 0 },
2520 time => { src => 'time', minw => 5, maxw => 0, trans => [ qw(secs_to_time) ], agg => 'sum' },
2521 state => { src => 'state', minw => 0, maxw => 0 },
2522 info => { src => 'info', minw => 0, maxw => 0, trans => [ qw(no_ctrl_char) ] },
2523 cnt => { src => 'id', minw => 0, maxw => 0 },
2524 },
2525 visible => [ qw(cxn cmd cnt mysql_thread_id state user hostname db time info)],
2526 filters => [ qw(hide_self hide_inactive hide_slave_io) ],
2527 sort_cols => '-time cxn hostname mysql_thread_id',
2528 sort_dir => '1',
2529 innodb => '',
2530 hide_caption => 1,
2531 colors => [
2532 { col => 'state', op => 'eq', arg => 'Locked', color => 'black on_red' },
2533 { col => 'cmd', op => 'eq', arg => 'Sleep', color => 'white' },
2534 { col => 'user', op => 'eq', arg => 'system user', color => 'white' },
2535 { col => 'cmd', op => 'eq', arg => 'Connect', color => 'white' },
2536 { col => 'cmd', op => 'eq', arg => 'Binlog Dump', color => 'white' },
2537 { col => 'time', op => '>', arg => 600, color => 'red' },
2538 { col => 'time', op => '>', arg => 120, color => 'yellow' },
2539 { col => 'time', op => '>', arg => 60, color => 'green' },
2540 { col => 'time', op => '>', arg => 30, color => 'cyan' },
2541 ],
2542 group_by => [qw(cxn cmd)],
2543 aggregate => 0,
2544 },
2545
2546 # TODO: some more columns:
2547 # 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'
2548 # 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'
2549 # 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'
2550
2551 q_header => {
2552 capt => 'Q-mode Header',
2553 cust => {},
2554 cols => {
2555 cxn => { src => 'cxn' },
2556 questions => { src => 'Questions' },
2557 qps => { src => 'Questions/Uptime_hires', dec => 1, trans => [qw(shorten)] },
2558 load => { src => $exprs{ServerLoad}, dec => 1, trans => [qw(shorten)] },
2559 slow => { src => 'Slow_queries', dec => 1, trans => [qw(shorten)] },
2560 q_cache_hit => { src => $exprs{QcacheHitRatio}, dec => 1, trans => [qw(percent)] },
2561 key_buffer_hit => { src => '1-(Key_reads/(Key_read_requests||1))', dec => 1, trans => [qw(percent)] },
2562 bps_in => { src => 'Bytes_received/Uptime_hires', dec => 1, trans => [qw(shorten)] },
2563 bps_out => { src => 'Bytes_sent/Uptime_hires', dec => 1, trans => [qw(shorten)] },
2564 when => { src => 'when' },
2565 },
2566 visible => [ qw(cxn when load qps slow q_cache_hit key_buffer_hit bps_in bps_out)],
2567 filters => [],
2568 sort_cols => 'when cxn',
2569 sort_dir => '1',
2570 innodb => '',
2571 hide_caption => 1,
2572 group_by => [],
2573 aggregate => 0,
2574 },
2575 row_operations => {
2576 capt => 'InnoDB Row Operations',
2577 cust => {},
2578 cols => {
2579 cxn => { src => 'cxn' },
2580 num_inserts => { src => 'IB_ro_num_rows_ins' },
2581 num_updates => { src => 'IB_ro_num_rows_upd' },
2582 num_reads => { src => 'IB_ro_num_rows_read' },
2583 num_deletes => { src => 'IB_ro_num_rows_del' },
2584 num_inserts_sec => { src => 'IB_ro_ins_sec' },
2585 num_updates_sec => { src => 'IB_ro_upd_sec' },
2586 num_reads_sec => { src => 'IB_ro_read_sec' },
2587 num_deletes_sec => { src => 'IB_ro_del_sec' },
2588 },
2589 visible => [ qw(cxn num_inserts num_updates num_reads num_deletes num_inserts_sec
2590 num_updates_sec num_reads_sec num_deletes_sec)],
2591 filters => [],
2592 sort_cols => 'cxn',
2593 sort_dir => '1',
2594 innodb => 'ro',
2595 group_by => [],
2596 aggregate => 0,
2597 },
2598 row_operation_misc => {
2599 capt => 'Row Operation Misc',
2600 cust => {},
2601 cols => {
2602 cxn => { src => 'cxn' },
2603 queries_in_queue => { src => 'IB_ro_queries_in_queue' },
2604 queries_inside => { src => 'IB_ro_queries_inside' },
2605 read_views_open => { src => 'IB_ro_read_views_open' },
2606 main_thread_id => { src => 'IB_ro_main_thread_id' },
2607 main_thread_proc_no => { src => 'IB_ro_main_thread_proc_no' },
2608 main_thread_state => { src => 'IB_ro_main_thread_state' },
2609 num_res_ext => { src => 'IB_ro_n_reserved_extents' },
2610 },
2611 visible => [ qw(cxn queries_in_queue queries_inside read_views_open main_thread_state)],
2612 filters => [],
2613 sort_cols => 'cxn',
2614 sort_dir => '1',
2615 innodb => 'ro',
2616 group_by => [],
2617 aggregate => 0,
2618 },
2619 semaphores => {
2620 capt => 'InnoDB Semaphores',
2621 cust => {},
2622 cols => {
2623 cxn => { src => 'cxn' },
2624 mutex_os_waits => { src => 'IB_sm_mutex_os_waits' },
2625 mutex_spin_rounds => { src => 'IB_sm_mutex_spin_rounds' },
2626 mutex_spin_waits => { src => 'IB_sm_mutex_spin_waits' },
2627 reservation_count => { src => 'IB_sm_reservation_count' },
2628 rw_excl_os_waits => { src => 'IB_sm_rw_excl_os_waits' },
2629 rw_excl_spins => { src => 'IB_sm_rw_excl_spins' },
2630 rw_shared_os_waits => { src => 'IB_sm_rw_shared_os_waits' },
2631 rw_shared_spins => { src => 'IB_sm_rw_shared_spins' },
2632 signal_count => { src => 'IB_sm_signal_count' },
2633 wait_array_size => { src => 'IB_sm_wait_array_size' },
2634 },
2635 visible => [ qw(cxn mutex_os_waits mutex_spin_waits mutex_spin_rounds
2636 rw_excl_os_waits rw_excl_spins rw_shared_os_waits rw_shared_spins
2637 signal_count reservation_count )],
2638 filters => [],
2639 sort_cols => 'cxn',
2640 sort_dir => '1',
2641 innodb => 'sm',
2642 group_by => [],
2643 aggregate => 0,
2644 },
2645 slave_io_status => {
2646 capt => 'Slave I/O Status',
2647 cust => {},
2648 cols => {
2649 cxn => { src => 'cxn' },
2650 connect_retry => { src => 'connect_retry' },
2651 master_host => { src => 'master_host', hdr => 'Master'},
2652 master_log_file => { src => 'master_log_file', hdr => 'File' },
2653 master_port => { src => 'master_port' },
2654 master_ssl_allowed => { src => 'master_ssl_allowed' },
2655 master_ssl_ca_file => { src => 'master_ssl_ca_file' },
2656 master_ssl_ca_path => { src => 'master_ssl_ca_path' },
2657 master_ssl_cert => { src => 'master_ssl_cert' },
2658 master_ssl_cipher => { src => 'master_ssl_cipher' },
2659 master_ssl_key => { src => 'master_ssl_key' },
2660 master_user => { src => 'master_user' },
2661 read_master_log_pos => { src => 'read_master_log_pos', hdr => 'Pos' },
2662 relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] },
2663 slave_io_running => { src => 'slave_io_running', hdr => 'On?' },
2664 slave_io_state => { src => 'slave_io_state', hdr => 'State' },
2665 },
2666 visible => [ qw(cxn master_host slave_io_running master_log_file relay_log_size read_master_log_pos slave_io_state)],
2667 filters => [ qw( cxn_is_slave ) ],
2668 sort_cols => 'slave_io_running cxn',
2669 colors => [
2670 { col => 'slave_io_running', op => 'ne', arg => 'Yes', color => 'black on_red' },
2671 ],
2672 sort_dir => '1',
2673 innodb => '',
2674 group_by => [],
2675 aggregate => 0,
2676 },
2677 slave_sql_status => {
2678 capt => 'Slave SQL Status',
2679 cust => {},
2680 cols => {
2681 cxn => { src => 'cxn' },
2682 exec_master_log_pos => { src => 'exec_master_log_pos', hdr => 'Master Pos' },
2683 last_errno => { src => 'last_errno' },
2684 last_error => { src => 'last_error' },
2685 master_host => { src => 'master_host', hdr => 'Master' },
2686 relay_log_file => { src => 'relay_log_file' },
2687 relay_log_pos => { src => 'relay_log_pos' },
2688 relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] },
2689 relay_master_log_file => { src => 'relay_master_log_file', hdr => 'Master File' },
2690 replicate_do_db => { src => 'replicate_do_db' },
2691 replicate_do_table => { src => 'replicate_do_table' },
2692 replicate_ignore_db => { src => 'replicate_ignore_db' },
2693 replicate_ignore_table => { src => 'replicate_ignore_table' },
2694 replicate_wild_do_table => { src => 'replicate_wild_do_table' },
2695 replicate_wild_ignore_table => { src => 'replicate_wild_ignore_table' },
2696 skip_counter => { src => 'skip_counter' },
2697 slave_sql_running => { src => 'slave_sql_running', hdr => 'On?' },
2698 until_condition => { src => 'until_condition' },
2699 until_log_file => { src => 'until_log_file' },
2700 until_log_pos => { src => 'until_log_pos' },
2701 time_behind_master => { src => 'seconds_behind_master', trans => [ qw(secs_to_time) ] },
2702 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)] },
2703 slave_catchup_rate => { src => $exprs{SlaveCatchupRate}, trans => [ qw(set_precision) ] },
2704 slave_open_temp_tables => { src => 'Slave_open_temp_tables' },
2705 },
2706 visible => [ qw(cxn master_host slave_sql_running time_behind_master slave_catchup_rate slave_open_temp_tables relay_log_pos last_error)],
2707 filters => [ qw( cxn_is_slave ) ],
2708 sort_cols => 'slave_sql_running cxn',
2709 sort_dir => '1',
2710 innodb => '',
2711 colors => [
2712 { col => 'slave_sql_running', op => 'ne', arg => 'Yes', color => 'black on_red' },
2713 { col => 'time_behind_master', op => '>', arg => 600, color => 'red' },
2714 { col => 'time_behind_master', op => '>', arg => 60, color => 'yellow' },
2715 { col => 'time_behind_master', op => '==', arg => 0, color => 'white' },
2716 ],
2717 group_by => [],
2718 aggregate => 0,
2719 },
2720 t_header => {
2721 capt => 'T-Mode Header',
2722 cust => {},
2723 cols => {
2724 cxn => { src => 'cxn' },
2725 dirty_bufs => { src => $exprs{DirtyBufs}, trans => [qw(percent)] },
2726 history_list_len => { src => 'IB_tx_history_list_len' },
2727 lock_structs => { src => 'IB_tx_num_lock_structs' },
2728 num_txns => { src => $exprs{NumTxns} },
2729 max_txn => { src => $exprs{MaxTxnTime}, trans => [qw(secs_to_time)] },
2730 undo_for => { src => 'IB_tx_purge_undo_for' },
2731 used_bufs => { src => $exprs{BufPoolFill}, trans => [qw(percent)]},
2732 versions => { src => $exprs{OldVersions} },
2733 },
2734 visible => [ qw(cxn history_list_len versions undo_for dirty_bufs used_bufs num_txns max_txn lock_structs)],
2735 filters => [ ],
2736 sort_cols => 'cxn',
2737 sort_dir => '1',
2738 innodb => '',
2739 colors => [],
2740 hide_caption => 1,
2741 group_by => [],
2742 aggregate => 0,
2743 },
2744 var_status => {
2745 capt => 'Variables & Status',
2746 cust => {},
2747 cols => {}, # Generated from current varset
2748 visible => [], # Generated from current varset
2749 filters => [],
2750 sort_cols => '',
2751 sort_dir => 1,
2752 innodb => '',
2753 temp => 1, # Do not persist to config file.
2754 hide_caption => 1,
2755 pivot => 0,
2756 group_by => [],
2757 aggregate => 0,
2758 },
2759 wait_array => {
2760 capt => 'InnoDB Wait Array',
2761 cust => {},
2762 cols => {
2763 cxn => { src => 'cxn' },
2764 thread => { src => 'thread' },
2765 waited_at_filename => { src => 'waited_at_filename' },
2766 waited_at_line => { src => 'waited_at_line' },
2767 'time' => { src => 'waited_secs', trans => [ qw(secs_to_time) ] },
2768 request_type => { src => 'request_type' },
2769 lock_mem_addr => { src => 'lock_mem_addr' },
2770 lock_cfile_name => { src => 'lock_cfile_name' },
2771 lock_cline => { src => 'lock_cline' },
2772 writer_thread => { src => 'writer_thread' },
2773 writer_lock_mode => { src => 'writer_lock_mode' },
2774 num_readers => { src => 'num_readers' },
2775 lock_var => { src => 'lock_var' },
2776 waiters_flag => { src => 'waiters_flag' },
2777 last_s_file_name => { src => 'last_s_file_name' },
2778 last_s_line => { src => 'last_s_line' },
2779 last_x_file_name => { src => 'last_x_file_name' },
2780 last_x_line => { src => 'last_x_line' },
2781 cell_waiting => { src => 'cell_waiting' },
2782 cell_event_set => { src => 'cell_event_set' },
2783 },
2784 visible => [ qw(cxn thread time waited_at_filename waited_at_line request_type num_readers lock_var waiters_flag cell_waiting cell_event_set)],
2785 filters => [],
2786 sort_cols => 'cxn -time',
2787 sort_dir => '1',
2788 innodb => 'sm',
2789 group_by => [],
2790 aggregate => 0,
2791 },
2792);
2793
2794# Initialize %tbl_meta from %columns and do some checks.
2795foreach my $table_name ( keys %tbl_meta ) {
2796 my $table = $tbl_meta{$table_name};