Merge lp:~percona-dev/percona-server/release-5.1.47-11-debian into lp:percona-server/release-5.1.47-11

Proposed by Aleksandr Kuzminsky
Status: Merged
Merged at revision: 21
Proposed branch: lp:~percona-dev/percona-server/release-5.1.47-11-debian
Merge into: lp:percona-server/release-5.1.47-11
Diff against target: 26356 lines (+25772/-0)
116 files modified
build/debian/README.Maintainer (+116/-0)
build/debian/additions/Docs__Images__Makefile.in (+6/-0)
build/debian/additions/Docs__Makefile.in (+6/-0)
build/debian/additions/debian-start (+31/-0)
build/debian/additions/debian-start.inc.sh (+72/-0)
build/debian/additions/echo_stderr (+2/-0)
build/debian/additions/innotop/InnoDBParser.pm (+1089/-0)
build/debian/additions/innotop/changelog.innotop (+318/-0)
build/debian/additions/innotop/innotop (+9485/-0)
build/debian/additions/innotop/innotop.1 (+2086/-0)
build/debian/additions/msql2mysql.1 (+16/-0)
build/debian/additions/my.cnf (+129/-0)
build/debian/additions/my_print_defaults.1 (+16/-0)
build/debian/additions/myisam_ftdump.1 (+16/-0)
build/debian/additions/myisamchk.1 (+17/-0)
build/debian/additions/myisamlog.1 (+16/-0)
build/debian/additions/myisampack.1 (+19/-0)
build/debian/additions/mysql-server.lintian-overrides (+2/-0)
build/debian/additions/mysql_config.1 (+17/-0)
build/debian/additions/mysql_convert_table_format.1 (+17/-0)
build/debian/additions/mysql_find_rows.1 (+18/-0)
build/debian/additions/mysql_fix_extensions.1 (+18/-0)
build/debian/additions/mysql_install_db.1 (+16/-0)
build/debian/additions/mysql_secure_installation.1 (+17/-0)
build/debian/additions/mysql_setpermission.1 (+23/-0)
build/debian/additions/mysql_tableinfo.1 (+322/-0)
build/debian/additions/mysql_waitpid.1 (+20/-0)
build/debian/additions/mysqlbinlog.1 (+17/-0)
build/debian/additions/mysqlbug.1 (+14/-0)
build/debian/additions/mysqlcheck.1 (+28/-0)
build/debian/additions/mysqld_safe_syslog.cnf (+2/-0)
build/debian/additions/mysqldumpslow.1 (+50/-0)
build/debian/additions/mysqlimport.1 (+20/-0)
build/debian/additions/mysqlmanager.1 (+49/-0)
build/debian/additions/mysqlreport (+1298/-0)
build/debian/additions/mysqlreport.1 (+180/-0)
build/debian/additions/mysqltest.1 (+16/-0)
build/debian/additions/pack_isam.1 (+19/-0)
build/debian/additions/resolve_stack_dump.1 (+16/-0)
build/debian/additions/resolveip.1 (+16/-0)
build/debian/changelog (+5/-0)
build/debian/compat (+1/-0)
build/debian/control (+120/-0)
build/debian/copyright (+169/-0)
build/debian/libmysqlclient-dev.README.Maintainer (+4/-0)
build/debian/libmysqlclient-dev.dirs (+2/-0)
build/debian/libmysqlclient-dev.docs (+1/-0)
build/debian/libmysqlclient-dev.examples (+1/-0)
build/debian/libmysqlclient-dev.files (+7/-0)
build/debian/libmysqlclient-dev.links (+2/-0)
build/debian/libmysqlclient16.dirs (+1/-0)
build/debian/libmysqlclient16.docs (+1/-0)
build/debian/libmysqlclient16.files (+1/-0)
build/debian/libmysqlclient16.postinst (+12/-0)
build/debian/patches/00list (+6/-0)
build/debian/patches/01_MAKEFILES__Docs_Images_Makefile.in.dpatch (+776/-0)
build/debian/patches/01_MAKEFILES__Docs_Makefile.in.dpatch (+776/-0)
build/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch (+29/-0)
build/debian/patches/38_scripts__mysqld_safe.sh__signals.dpatch (+43/-0)
build/debian/patches/41_scripts__mysql_install_db.sh__no_test.dpatch (+20/-0)
build/debian/patches/44_scripts__mysql_config__libs.dpatch (+24/-0)
build/debian/patches/50_mysql-test__db_test.dpatch (+23/-0)
build/debian/patches/60_percona_support.dpatch (+16/-0)
build/debian/percona-server-client-5.1.README.Debian (+4/-0)
build/debian/percona-server-client-5.1.dirs (+3/-0)
build/debian/percona-server-client-5.1.docs (+3/-0)
build/debian/percona-server-client-5.1.files (+39/-0)
build/debian/percona-server-client-5.1.links (+3/-0)
build/debian/percona-server-client-5.1.lintian-overrides (+3/-0)
build/debian/percona-server-client-5.1.menu (+3/-0)
build/debian/percona-server-common.dirs (+1/-0)
build/debian/percona-server-common.files (+2/-0)
build/debian/percona-server-common.lintian-overrides (+2/-0)
build/debian/percona-server-common.postrm (+7/-0)
build/debian/percona-server-server-5.1.NEWS (+34/-0)
build/debian/percona-server-server-5.1.README.Debian (+109/-0)
build/debian/percona-server-server-5.1.config (+46/-0)
build/debian/percona-server-server-5.1.dirs (+9/-0)
build/debian/percona-server-server-5.1.docs (+1/-0)
build/debian/percona-server-server-5.1.files (+53/-0)
build/debian/percona-server-server-5.1.links (+2/-0)
build/debian/percona-server-server-5.1.lintian-overrides (+4/-0)
build/debian/percona-server-server-5.1.logcheck.ignore.paranoid (+9/-0)
build/debian/percona-server-server-5.1.logcheck.ignore.server (+32/-0)
build/debian/percona-server-server-5.1.logcheck.ignore.workstation (+32/-0)
build/debian/percona-server-server-5.1.mysql.init (+182/-0)
build/debian/percona-server-server-5.1.percona-xtradb-server.logrotate (+27/-0)
build/debian/percona-server-server-5.1.postinst (+277/-0)
build/debian/percona-server-server-5.1.postrm (+83/-0)
build/debian/percona-server-server-5.1.preinst (+186/-0)
build/debian/percona-server-server-5.1.prerm (+8/-0)
build/debian/percona-server-server-5.1.templates (+90/-0)
build/debian/po/POTFILES.in (+1/-0)
build/debian/po/ar.po (+271/-0)
build/debian/po/ca.po (+342/-0)
build/debian/po/cs.po (+365/-0)
build/debian/po/da.po (+401/-0)
build/debian/po/de.po (+281/-0)
build/debian/po/es.po (+409/-0)
build/debian/po/eu.po (+295/-0)
build/debian/po/fr.po (+278/-0)
build/debian/po/gl.po (+268/-0)
build/debian/po/it.po (+270/-0)
build/debian/po/ja.po (+394/-0)
build/debian/po/nb.po (+297/-0)
build/debian/po/nl.po (+302/-0)
build/debian/po/pt.po (+326/-0)
build/debian/po/pt_BR.po (+462/-0)
build/debian/po/ro.po (+319/-0)
build/debian/po/ru.po (+305/-0)
build/debian/po/sv.po (+400/-0)
build/debian/po/templates.pot (+187/-0)
build/debian/po/tr.po (+342/-0)
build/debian/rules (+323/-0)
build/debian/source.lintian-overrides (+2/-0)
build/debian/watch (+3/-0)
To merge this branch: bzr merge lp:~percona-dev/percona-server/release-5.1.47-11-debian
Reviewer Review Type Date Requested Status
Yasufumi Kinoshita (community) Approve
Review via email: mp+26439@code.launchpad.net

Description of the change

Added Debian build options

To post a comment you must log in.
Revision history for this message
Yasufumi Kinoshita (yasufumi-kinoshita) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'build/debian'
2=== added file 'build/debian/README.Maintainer'
3--- build/debian/README.Maintainer 1970-01-01 00:00:00 +0000
4+++ build/debian/README.Maintainer 2010-05-31 18:03:25 +0000
5@@ -0,0 +1,116 @@
6+
7+###########################
8+## FIXME for 5.1 ##
9+###########################
10+
11+* put this trigger-recreation thing into the init scripts -- what?!
12+* Let debian-i10n-english review all template changes before the translaters start.
13+* Mark debconf translations as obsolete with debconf-updatepo.
14+
15+###########################################################################
16+# Here are some information that are only of interest for the current and #
17+# following Debian maintainers of MySQL. #
18+###########################################################################
19+
20+The debian/ directory is under SVN control, see debian/control for URL.
21+
22+#
23+# Preparing a new version
24+#
25+The new orig.tar.gz (without non-free documentation) is created in /tmp/ when
26+running this command:
27+
28+debian/rules get-orig-source
29+
30+#
31+# mysqlreport
32+#
33+The authors e-mail address is <public@codenode.com>.
34+
35+#
36+# Remarks to dependencies
37+#
38+libwrap0-dev (>= 7.6-8.3)
39+ According to bug report 114582 where where build problems on
40+ IA-64/sid with at least two prior versions.
41+psmisc
42+ /usr/bin/killall in the initscript
43+
44+zlib1g in libmysqlclient-dev:
45+ "mysql_config --libs" ads "-lz"
46+
47+Build-Dep:
48+
49+debhelper (>=4.1.16):
50+ See po-debconf(7).
51+
52+autoconf (>= 2.13-20), automake1.7
53+ Try to get rid of them.
54+
55+doxygen, tetex-bin, tetex-extra, gs
56+ for ndb/docs/*tex
57+
58+#
59+# Remarks to the start scripts
60+#
61+
62+## initscripts rely on mysqladmin from a different package
63+We have the problem that "/etc/init.d/mysql stop" relies on mysqladmin which
64+is in another package (mysql-client) and a passwordless access that's maybe
65+only available if the user configured his /root/.my.cnf. Can this be a problem?
66+* normal mode: not because the user is required to have it. Else:
67+* purge/remove: not, same as normal mode
68+* upgrade: not, same as normal mode
69+* first install: not, it depends on mysql-client which at least is unpacked
70+ so mysqladmin is there (to ping). It is not yet configured
71+ passwordles but if there's a server running then there's a
72+ /root/.my.cnf. Anyways, we simply kill anything that's mysqld.
73+
74+## Passwordless access for the maintainer scripts
75+Another issue is that the scripts needs passwordless access. To ensure this
76+a debian-sys-maint user is configured which has process and shutdown privs.
77+The file with the randomly (that's important!) generated password must be
78+present as long as the databases remain installed because else a new install
79+would have no access. This file should be used like:
80+ mysqladmin --defaults-file=/etc/mysql/debian.cnf restart
81+to avoid providing the password in plaintext on a commandline where it would
82+be visible to any user via the "ps" command.
83+
84+## When to start the daemon?
85+We aim to give the admin full control on when MySQL is running.
86+Issues to be faced here:
87+OLD:
88+ 1. Debconf asks whether MySQL should be started on boot so update-rc.d is
89+ only run if the answer has been yes. The admin is likely to forget
90+ this decision but update-rc.d checks for an existing line in
91+ /etc/runlevel.conf and leaves it intact.
92+ 2. On initial install, if the answer is yes, the daemon has to be started.
93+ 3. On upgrades it should only be started if it was already running, everything
94+ else is confusing. Especiall relying on an debconf decision made month ago
95+ is considered suboptimal. See bug #274264
96+ Implementation so far:
97+ prerm (called on upgrade before stopping the server):
98+ check for a running server and set flag if necessary
99+ preinst (called on initial install and before unpacking when upgrading):
100+ check for the debconf variable and set flag if necessary
101+ postinst (called on initial install and after each upgrade after unpacking):
102+ call update-rc.d if debconf says yes
103+ call invoce-rc.d if the flag has been set
104+ Problems remaining:
105+ dpkg-reconfigure and setting mysql start on boot to yes did not start mysql
106+ (ok "start on boot" literally does not mean "start now" so that might have been ok)
107+NEW:
108+ 1. --- no debconf anymore for the sake of simplicity. We have runlevel.conf,
109+ the admin should use it
110+ 2. On initial install the server is started.
111+ 3. On upgrades the server is started exactly if it was running before so the
112+ runlevel configuration is irrelevant. It will be preserved by the mean of
113+ update-rc.d's builtin check.
114+ Implementation:
115+ prerm (called on upgrade before stopping the server):
116+ check for a running server and set flag if necessary
117+ preinst (called on initial install and before unpacking when upgrading):
118+ check for $1 beeing (initial) "install" and set flag
119+ postinst (called on initial install and after each upgrade after unpacking):
120+ call update-rc.d
121+ call invoce-rc.d if the flag has been set
122
123=== added directory 'build/debian/additions'
124=== added file 'build/debian/additions/Docs__Images__Makefile.in'
125--- build/debian/additions/Docs__Images__Makefile.in 1970-01-01 00:00:00 +0000
126+++ build/debian/additions/Docs__Images__Makefile.in 2010-05-31 18:03:25 +0000
127@@ -0,0 +1,6 @@
128+all:
129+
130+distclean:
131+ -rm -f Makefile
132+
133+.PHONY: all distclean clean install check
134
135=== added file 'build/debian/additions/Docs__Makefile.in'
136--- build/debian/additions/Docs__Makefile.in 1970-01-01 00:00:00 +0000
137+++ build/debian/additions/Docs__Makefile.in 2010-05-31 18:03:25 +0000
138@@ -0,0 +1,6 @@
139+all:
140+
141+distclean:
142+ -rm -f Makefile
143+
144+.PHONY: all distclean clean install check
145
146=== added file 'build/debian/additions/debian-start'
147--- build/debian/additions/debian-start 1970-01-01 00:00:00 +0000
148+++ build/debian/additions/debian-start 2010-05-31 18:03:25 +0000
149@@ -0,0 +1,31 @@
150+#!/bin/bash
151+#
152+# This script is executed by "/etc/init.d/mysql" on every (re)start.
153+#
154+# Changes to this file will be preserved when updating the Debian package.
155+#
156+
157+source /usr/share/mysql/debian-start.inc.sh
158+
159+MYSQL="/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf"
160+MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
161+MYUPGRADE="/usr/bin/mysql_upgrade --defaults-extra-file=/etc/mysql/debian.cnf"
162+MYCHECK="/usr/bin/mysqlcheck --defaults-file=/etc/mysql/debian.cnf"
163+MYCHECK_SUBJECT="WARNING: mysqlcheck has found corrupt tables"
164+MYCHECK_PARAMS="--all-databases --fast --silent"
165+MYCHECK_RCPT="root"
166+
167+# The following commands should be run when the server is up but in background
168+# where they do not block the server start and in one shell instance so that
169+# they run sequentially. They are supposed not to echo anything to stdout.
170+# If you want to disable the check for crashed tables comment
171+# "check_for_crashed_tables" out.
172+# (There may be no output to stdout inside the background process!)
173+echo "Checking for corrupt, not cleanly closed and upgrade needing tables."
174+(
175+ upgrade_system_tables_if_necessary;
176+ check_root_accounts;
177+ check_for_crashed_tables;
178+) >&2 &
179+
180+exit 0
181
182=== added file 'build/debian/additions/debian-start.inc.sh'
183--- build/debian/additions/debian-start.inc.sh 1970-01-01 00:00:00 +0000
184+++ build/debian/additions/debian-start.inc.sh 2010-05-31 18:03:25 +0000
185@@ -0,0 +1,72 @@
186+#!/bin/bash
187+#
188+# This file is included by /etc/mysql/debian-start
189+#
190+
191+## Check all unclosed tables.
192+# - Requires the server to be up.
193+# - Is supposed to run silently in background.
194+function check_for_crashed_tables() {
195+ set -e
196+ set -u
197+
198+ # But do it in the background to not stall the boot process.
199+ logger -p daemon.info -i -t$0 "Triggering myisam-recover for all MyISAM tables"
200+
201+ # Checking for $? is unreliable so the size of the output is checked.
202+ # Some table handlers like HEAP do not support CHECK TABLE.
203+ tempfile=`tempfile`
204+ # We have to use xargs in this case, because a for loop barfs on the
205+ # spaces in the thing to be looped over.
206+ LC_ALL=C $MYSQL --skip-column-names --batch -e '
207+ select concat("select count(*) into @discard from `",
208+ TABLE_SCHEMA, "`.`", TABLE_NAME, "`")
209+ from information_schema.TABLES where ENGINE="MyISAM"' | \
210+ xargs -i $MYSQL --skip-column-names --silent --batch \
211+ --force -e "{}" >$tempfile
212+ if [ -s $tempfile ]; then
213+ (
214+ /bin/echo -e "\n" \
215+ "Improperly closed tables are also reported if clients are accessing\n" \
216+ "the tables *now*. A list of current connections is below.\n";
217+ $MYADMIN processlist status
218+ ) >> $tempfile
219+ # Check for presence as a dependency on mailx would require an MTA.
220+ if [ -x /usr/bin/mailx ]; then
221+ mailx -e -s"$MYCHECK_SUBJECT" $MYCHECK_RCPT < $tempfile
222+ fi
223+ (echo "$MYCHECK_SUBJECT"; cat $tempfile) | logger -p daemon.warn -i -t$0
224+ fi
225+ rm $tempfile
226+}
227+
228+## Check for tables needing an upgrade.
229+# - Requires the server to be up.
230+# - Is supposed to run silently in background.
231+function upgrade_system_tables_if_necessary() {
232+ set -e
233+ set -u
234+
235+ logger -p daemon.info -i -t$0 "Upgrading MySQL tables if necessary."
236+
237+ # Filter all "duplicate column", "duplicate key" and "unknown column"
238+ # errors as the script is designed to be idempotent.
239+ LC_ALL=C $MYUPGRADE \
240+ 2>&1 \
241+ | egrep -v '^(1|@had|ERROR (1054|1060|1061))' \
242+ | logger -p daemon.warn -i -t$0
243+}
244+
245+## Check for the presence of both, root accounts with and without password.
246+# This might have been caused by a bug related to mysql_install_db (#418672).
247+function check_root_accounts() {
248+ set -e
249+ set -u
250+
251+ logger -p daemon.info -i -t$0 "Checking for insecure root accounts."
252+
253+ ret=$( echo "SELECT count(*) FROM mysql.user WHERE user='root' and password='';" | $MYSQL --skip-column-names )
254+ if [ "$ret" -ne "0" ]; then
255+ logger -p daemon.warn -i -t$0 "WARNING: mysql.user contains $ret root accounts without password!"
256+ fi
257+}
258
259=== added file 'build/debian/additions/echo_stderr'
260--- build/debian/additions/echo_stderr 1970-01-01 00:00:00 +0000
261+++ build/debian/additions/echo_stderr 2010-05-31 18:03:25 +0000
262@@ -0,0 +1,2 @@
263+#!/bin/bash
264+echo "$*" 1>&2
265
266=== added directory 'build/debian/additions/innotop'
267=== added file 'build/debian/additions/innotop/InnoDBParser.pm'
268--- build/debian/additions/innotop/InnoDBParser.pm 1970-01-01 00:00:00 +0000
269+++ build/debian/additions/innotop/InnoDBParser.pm 2010-05-31 18:03:25 +0000
270@@ -0,0 +1,1089 @@
271+use strict;
272+use warnings FATAL => 'all';
273+
274+package InnoDBParser;
275+
276+# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com.
277+# Feedback and improvements are gratefully received.
278+#
279+# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
280+# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
281+# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
282+#
283+# This program is free software; you can redistribute it and/or modify it under
284+# the terms of the GNU General Public License as published by the Free Software
285+# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
286+# systems, you can issue `man perlgpl' or `man perlartistic' to read these
287+
288+# You should have received a copy of the GNU General Public License along with
289+# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
290+# Place, Suite 330, Boston, MA 02111-1307 USA
291+
292+our $VERSION = '1.6.0';
293+
294+use Data::Dumper;
295+$Data::Dumper::Sortkeys = 1;
296+use English qw(-no_match_vars);
297+use List::Util qw(max);
298+
299+# Some common patterns
300+my $d = qr/(\d+)/; # Digit
301+my $f = qr/(\d+\.\d+)/; # Float
302+my $t = qr/(\d+ \d+)/; # Transaction ID
303+my $i = qr/((?:\d{1,3}\.){3}\d+)/; # IP address
304+my $n = qr/([^`\s]+)/; # MySQL object name
305+my $w = qr/(\w+)/; # Words
306+my $fl = qr/([\w\.\/]+) line $d/; # Filename and line number
307+my $h = qr/((?:0x)?[0-9a-f]*)/; # Hex
308+my $s = qr/(\d{6} .\d:\d\d:\d\d)/; # InnoDB timestamp
309+
310+# If you update this variable, also update the SYNOPSIS in the pod.
311+my %innodb_section_headers = (
312+ "TRANSACTIONS" => "tx",
313+ "BUFFER POOL AND MEMORY" => "bp",
314+ "SEMAPHORES" => "sm",
315+ "LOG" => "lg",
316+ "ROW OPERATIONS" => "ro",
317+ "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib",
318+ "FILE I/O" => "io",
319+ "LATEST DETECTED DEADLOCK" => "dl",
320+ "LATEST FOREIGN KEY ERROR" => "fk",
321+);
322+
323+my %parser_for = (
324+ tx => \&parse_tx_section,
325+ bp => \&parse_bp_section,
326+ sm => \&parse_sm_section,
327+ lg => \&parse_lg_section,
328+ ro => \&parse_ro_section,
329+ ib => \&parse_ib_section,
330+ io => \&parse_io_section,
331+ dl => \&parse_dl_section,
332+ fk => \&parse_fk_section,
333+);
334+
335+my %fk_parser_for = (
336+ Transaction => \&parse_fk_transaction_error,
337+ Error => \&parse_fk_bad_constraint_error,
338+ Cannot => \&parse_fk_cant_drop_parent_error,
339+);
340+
341+# A thread's proc_info can be at least 98 different things I've found in the
342+# source. Fortunately, most of them begin with a gerunded verb. These are
343+# the ones that don't.
344+my %is_proc_info = (
345+ 'After create' => 1,
346+ 'Execution of init_command' => 1,
347+ 'FULLTEXT initialization' => 1,
348+ 'Reopen tables' => 1,
349+ 'Repair done' => 1,
350+ 'Repair with keycache' => 1,
351+ 'System lock' => 1,
352+ 'Table lock' => 1,
353+ 'Thread initialized' => 1,
354+ 'User lock' => 1,
355+ 'copy to tmp table' => 1,
356+ 'discard_or_import_tablespace' => 1,
357+ 'end' => 1,
358+ 'got handler lock' => 1,
359+ 'got old table' => 1,
360+ 'init' => 1,
361+ 'key cache' => 1,
362+ 'locks' => 1,
363+ 'malloc' => 1,
364+ 'query end' => 1,
365+ 'rename result table' => 1,
366+ 'rename' => 1,
367+ 'setup' => 1,
368+ 'statistics' => 1,
369+ 'status' => 1,
370+ 'table cache' => 1,
371+ 'update' => 1,
372+);
373+
374+sub new {
375+ bless {}, shift;
376+}
377+
378+# Parse the status and return it.
379+# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c
380+# Pass in the text to parse, whether to be in debugging mode, which sections
381+# to parse (hashref; if empty, parse all), and whether to parse full info from
382+# locks and such (probably shouldn't unless you need to).
383+sub parse_status_text {
384+ my ( $self, $fulltext, $debug, $sections, $full ) = @_;
385+
386+ die "I can't parse undef" unless defined $fulltext;
387+ $fulltext =~ s/[\r\n]+/\n/g;
388+
389+ $sections ||= {};
390+ die '$sections must be a hashref' unless ref($sections) eq 'HASH';
391+
392+ my %innodb_data = (
393+ got_all => 0, # Whether I was able to get the whole thing
394+ ts => '', # Timestamp the server put on it
395+ last_secs => 0, # Num seconds the averages are over
396+ sections => {}, # Parsed values from each section
397+ );
398+
399+ if ( $debug ) {
400+ $innodb_data{'fulltext'} = $fulltext;
401+ }
402+
403+ # Get the most basic info about the status: beginning and end, and whether
404+ # I got the whole thing (if there has been a big deadlock and there are
405+ # too many locks to print, the output might be truncated)
406+ my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m;
407+ $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ];
408+ $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'});
409+ ( $innodb_data{'last_secs'} ) = $fulltext
410+ =~ m/Per second averages calculated from the last $d seconds/;
411+
412+ ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/;
413+ $innodb_data{'got_all'} = $got_all || 0;
414+
415+ # Split it into sections. Each section begins with
416+ # -----
417+ # LABEL
418+ # -----
419+ my %innodb_sections;
420+ my @matches = $fulltext
421+ =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs;
422+ while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) {
423+ $innodb_sections{$name} = [ $text, $end ? 1 : 0 ];
424+ }
425+ # The Row Operations section is a special case, because instead of ending
426+ # with the beginning of another section, it ends with the end of the file.
427+ # So this section is complete if the entire file is complete.
428+ $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'};
429+
430+ # Just for sanity's sake, make sure I understand what to do with each
431+ # section
432+ eval {
433+ foreach my $section ( keys %innodb_sections ) {
434+ my $header = $innodb_section_headers{$section};
435+ die "Unknown section $section in $fulltext\n"
436+ unless $header;
437+ $innodb_data{'sections'}->{ $header }
438+ ->{'fulltext'} = $innodb_sections{$section}->[0];
439+ $innodb_data{'sections'}->{ $header }
440+ ->{'complete'} = $innodb_sections{$section}->[1];
441+ }
442+ };
443+ if ( $EVAL_ERROR ) {
444+ _debug( $debug, $EVAL_ERROR);
445+ }
446+
447+ # ################################################################
448+ # Parse the detailed data out of the sections.
449+ # ################################################################
450+ eval {
451+ foreach my $section ( keys %parser_for ) {
452+ if ( defined $innodb_data{'sections'}->{$section}
453+ && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) {
454+ $parser_for{$section}->(
455+ $innodb_data{'sections'}->{$section},
456+ $innodb_data{'sections'}->{$section}->{'complete'},
457+ $debug,
458+ $full )
459+ or delete $innodb_data{'sections'}->{$section};
460+ }
461+ else {
462+ delete $innodb_data{'sections'}->{$section};
463+ }
464+ }
465+ };
466+ if ( $EVAL_ERROR ) {
467+ _debug( $debug, $EVAL_ERROR);
468+ }
469+
470+ return \%innodb_data;
471+}
472+
473+# Parses the status text and returns it flattened out as a single hash.
474+sub get_status_hash {
475+ my ( $self, $fulltext, $debug, $sections, $full ) = @_;
476+
477+ # Parse the status text...
478+ my $innodb_status
479+ = $self->parse_status_text($fulltext, $debug, $sections, $full );
480+
481+ # Flatten the hierarchical structure into a single list by grabbing desired
482+ # sections from it.
483+ return
484+ (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)),
485+ (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} }
486+ qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads
487+ awe_mem_alloc pages_modified writes_pending_lru page_creates_sec
488+ reads_pending pages_total buf_pool_hits writes_pending_single_page
489+ page_writes_sec pages_read pages_written page_reads_sec
490+ writes_pending_flush_list buf_pool_size add_pool_alloc
491+ dict_mem_alloc pages_created buf_free complete )),
492+ (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} }
493+ qw( num_lock_structs history_list_len purge_done_for transactions
494+ purge_undo_for is_truncated trx_id_counter complete )),
495+ (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} }
496+ qw( hash_table_size hash_searches_s non_hash_searches_s
497+ bufs_in_node_heap used_cells size free_list_len seg_size inserts
498+ merged_recs merges complete )),
499+ (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} }
500+ qw( log_ios_done pending_chkp_writes last_chkp log_ios_s
501+ log_flushed_to log_seq_no pending_log_writes complete )),
502+ (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} }
503+ qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits
504+ mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits
505+ waits signal_count reservation_count complete )),
506+ (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} }
507+ qw( queries_in_queue n_reserved_extents main_thread_state
508+ main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec
509+ read_views_open num_rows_upd num_rows_ins num_rows_read
510+ queries_inside num_rows_del complete )),
511+ (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} }
512+ qw( trigger parent_table child_index parent_index attempted_op
513+ child_db timestring fk_name records col_name reason txn parent_db
514+ type child_table parent_col complete )),
515+ (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} }
516+ qw( pending_buffer_pool_flushes pending_pwrites pending_preads
517+ pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios
518+ reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s
519+ threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs
520+ pending_log_flushes complete )),
521+ (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} }
522+ qw( timestring rolled_back txns complete ));
523+
524+}
525+
526+sub ts_to_string {
527+ my $parts = shift;
528+ return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts);
529+}
530+
531+sub parse_innodb_timestamp {
532+ my $text = shift;
533+ my ( $y, $m, $d, $h, $i, $s )
534+ = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/;
535+ die("Can't get timestamp from $text\n") unless $y;
536+ $y += 2000;
537+ return ( $y, $m, $d, $h, $i, $s );
538+}
539+
540+sub parse_fk_section {
541+ my ( $section, $complete, $debug, $full ) = @_;
542+ my $fulltext = $section->{'fulltext'};
543+
544+ return 0 unless $fulltext;
545+
546+ my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m;
547+ $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
548+ $section->{'timestring'} = ts_to_string($section->{'ts'});
549+ $section->{'type'} = $type;
550+
551+ # Decide which type of FK error happened, and dispatch to the right parser.
552+ if ( $type && $fk_parser_for{$type} ) {
553+ $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full );
554+ }
555+
556+ delete $section->{'fulltext'} unless $debug;
557+
558+ return 1;
559+}
560+
561+sub parse_fk_cant_drop_parent_error {
562+ my ( $section, $complete, $debug, $fulltext, $full ) = @_;
563+
564+ # Parse the parent/child table info out
565+ @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext
566+ =~ m{Cannot $w table `(.*)/(.*)`}m;
567+ @{$section}{ qw(child_db child_table) } = $fulltext
568+ =~ m{because it is referenced by `(.*)/(.*)`}m;
569+
570+ ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s;
571+ $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm
572+ if $section->{'reason'};
573+
574+ # Certain data may not be present. Make them '' if not present.
575+ map { $section->{$_} ||= "" }
576+ qw(child_index fk_name col_name parent_col);
577+}
578+
579+# See dict/dict0dict.c, function dict_foreign_error_report
580+# I don't care much about these. There are lots of different messages, and
581+# they come from someone trying to create a foreign key, or similar
582+# statements. They aren't indicative of some transaction trying to insert,
583+# delete or update data. Sometimes it is possible to parse out a lot of
584+# information about the tables and indexes involved, but often the message
585+# contains the DDL string the user entered, which is way too much for this
586+# module to try to handle.
587+sub parse_fk_bad_constraint_error {
588+ my ( $section, $complete, $debug, $fulltext, $full ) = @_;
589+
590+ # Parse the parent/child table and index info out
591+ @{$section}{ qw(child_db child_table) } = $fulltext
592+ =~ m{Error in foreign key constraint of table (.*)/(.*):$}m;
593+ $section->{'attempted_op'} = 'DDL';
594+
595+ # FK name, parent info... if possible.
596+ @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
597+ = $fulltext
598+ =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/;
599+
600+ if ( !defined($section->{'fk_name'}) ) {
601+ # Try to parse SQL a user might have typed in a CREATE statement or such
602+ @{$section}{ qw(col_name parent_db parent_table parent_col) }
603+ = $fulltext
604+ =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i;
605+ }
606+ $section->{'parent_db'} ||= $section->{'child_db'};
607+
608+ # Name of the child index (index in the same table where the FK is, see
609+ # definition of dict_foreign_struct in include/dict0mem.h, where it is
610+ # called foreign_index, as opposed to referenced_index which is in the
611+ # parent table. This may not be possible to find.
612+ @{$section}{ qw(child_index) } = $fulltext
613+ =~ m/^The index in the foreign key in table is $n$/m;
614+
615+ @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms;
616+ $section->{'reason'} =~ s/\s+/ /g
617+ if $section->{'reason'};
618+
619+ # Certain data may not be present. Make them '' if not present.
620+ map { $section->{$_} ||= "" }
621+ qw(child_index fk_name col_name parent_table parent_col);
622+}
623+
624+# see source file row/row0ins.c
625+sub parse_fk_transaction_error {
626+ my ( $section, $complete, $debug, $fulltext, $full ) = @_;
627+
628+ # Parse the txn info out
629+ my ( $txn ) = $fulltext
630+ =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s;
631+ if ( $txn ) {
632+ $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full );
633+ }
634+
635+ # Parse the parent/child table and index info out. There are two types: an
636+ # update or a delete of a parent record leaves a child orphaned
637+ # (row_ins_foreign_report_err), and an insert or update of a child record has
638+ # no matching parent record (row_ins_foreign_report_add_err).
639+
640+ @{$section}{ qw(reason child_db child_table) }
641+ = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m;
642+
643+ @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
644+ = $fulltext
645+ =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/;
646+ $section->{'parent_db'} ||= $section->{'child_db'};
647+
648+ # Special case, which I don't know how to trigger, but see
649+ # innobase/row/row0ins.c row_ins_check_foreign_constraint
650+ if ( $fulltext =~ m/ibd file does not currently exist!/ ) {
651+ my ( $attempted_op, $index, $records )
652+ = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm;
653+ $section->{'child_index'} = $index;
654+ $section->{'attempted_op'} = $attempted_op || '';
655+ if ( $records && $full ) {
656+ ( $section->{'records'} )
657+ = parse_innodb_record_dump( $records, $complete, $debug );
658+ }
659+ @{$section}{qw(parent_db parent_table)}
660+ =~ m/^But the parent table `$n`\.`$n`$/m;
661+ }
662+ else {
663+ my ( $attempted_op, $which, $index )
664+ = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m;
665+ if ( $which ) {
666+ $section->{$which . '_index'} = $index;
667+ $section->{'attempted_op'} = $attempted_op || '';
668+
669+ # Parse out the related records in the other table.
670+ my ( $search_index, $records );
671+ if ( $which eq 'child' ) {
672+ ( $search_index, $records ) = $fulltext
673+ =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms;
674+ $section->{'parent_index'} = $search_index;
675+ }
676+ else {
677+ ( $search_index, $records ) = $fulltext
678+ =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms;
679+ $section->{'child_index'} = $search_index;
680+ }
681+ if ( $records && $full ) {
682+ $section->{'records'}
683+ = parse_innodb_record_dump( $records, $complete, $debug );
684+ }
685+ else {
686+ $section->{'records'} = '';
687+ }
688+ }
689+ }
690+
691+ # Parse out the tuple trying to be updated, deleted or inserted.
692+ my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m;
693+ if ( $trigger ) {
694+ $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug );
695+ }
696+
697+ # Certain data may not be present. Make them '' if not present.
698+ map { $section->{$_} ||= "" }
699+ qw(child_index fk_name col_name parent_table parent_col);
700+}
701+
702+# There are new-style and old-style record formats. See rem/rem0rec.c
703+# TODO: write some tests for this
704+sub parse_innodb_record_dump {
705+ my ( $dump, $complete, $debug ) = @_;
706+ return undef unless $dump;
707+
708+ my $result = {};
709+
710+ if ( $dump =~ m/PHYSICAL RECORD/ ) {
711+ my $style = $dump =~ m/compact format/ ? 'new' : 'old';
712+ $result->{'style'} = $style;
713+
714+ # This is a new-style record.
715+ if ( $style eq 'new' ) {
716+ @{$result}{qw( heap_no type num_fields info_bits )}
717+ = $dump
718+ =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m;
719+ }
720+
721+ # OK, it's old-style. Unfortunately there are variations here too.
722+ elsif ( $dump =~ m/-byte offs / ) {
723+ # Older-old style.
724+ @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
725+ = $dump
726+ =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m;
727+ if ( $dump !~ m/-byte offs TRUE/ ) {
728+ $result->{'byte_offset'} = 0;
729+ }
730+ }
731+ else {
732+ # Newer-old style.
733+ @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
734+ = $dump
735+ =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m;
736+ }
737+
738+ }
739+ else {
740+ $result->{'style'} = 'tuple';
741+ @{$result}{qw( type num_fields )}
742+ = $dump =~ m/^(DATA TUPLE): $d fields;$/m;
743+ }
744+
745+ # Fill in default values for things that couldn't be parsed.
746+ map { $result->{$_} ||= 0 }
747+ qw(heap_no num_fields byte_offset info_bits);
748+ map { $result->{$_} ||= '' }
749+ qw(style type );
750+
751+ my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm;
752+ $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ];
753+
754+ return $result;
755+}
756+
757+# New/old-style applies here. See rem/rem0rec.c
758+# $text should not include the leading space or the second trailing semicolon.
759+sub parse_field {
760+ my ( $text, $complete, $debug ) = @_;
761+
762+ # Sample fields:
763+ # '4: SQL NULL, size 4 '
764+ # '1: len 6; hex 000000005601; asc V ;'
765+ # '6: SQL NULL'
766+ # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)'
767+ my ( $id, $nullsize, $len, $hex, $asc, $truncated );
768+ ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/;
769+ if ( !defined($id) ) {
770+ ( $id ) = $text =~ m/^$d: SQL NULL$/;
771+ }
772+ if ( !defined($id) ) {
773+ ( $id, $len, $hex, $asc, $truncated )
774+ = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/;
775+ }
776+
777+ die "Could not parse this field: '$text'" unless defined $id;
778+ return {
779+ id => $id,
780+ len => defined($len) ? $len : defined($nullsize) ? $nullsize : 0,
781+ 'hex' => defined($hex) ? $hex : '',
782+ asc => defined($asc) ? $asc : '',
783+ trunc => $truncated ? 1 : 0,
784+ };
785+
786+}
787+
788+sub parse_dl_section {
789+ my ( $dl, $complete, $debug, $full ) = @_;
790+ return unless $dl;
791+ my $fulltext = $dl->{'fulltext'};
792+ return 0 unless $fulltext;
793+
794+ my ( $ts ) = $fulltext =~ m/^$s$/m;
795+ return 0 unless $ts;
796+
797+ $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
798+ $dl->{'timestring'} = ts_to_string($dl->{'ts'});
799+ $dl->{'txns'} = {};
800+
801+ my @sections
802+ = $fulltext
803+ =~ m{
804+ ^\*{3}\s([^\n]*) # *** (1) WAITING FOR THIS...
805+ (.*?) # Followed by anything, non-greedy
806+ (?=(?:^\*{3})|\z) # Followed by another three stars or EOF
807+ }gmsx;
808+
809+
810+ # Loop through each section. There are no assumptions about how many
811+ # there are, who holds and wants what locks, and who gets rolled back.
812+ while ( my ($header, $body) = splice(@sections, 0, 2) ) {
813+ my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/;
814+ next unless $txn_id;
815+ $dl->{'txns'}->{$txn_id} ||= {};
816+ my $txn = $dl->{'txns'}->{$txn_id};
817+
818+ if ( $what eq 'TRANSACTION' ) {
819+ $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full );
820+ }
821+ else {
822+ push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full );
823+ }
824+ }
825+
826+ @{ $dl }{ qw(rolled_back) }
827+ = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m;
828+
829+ # Make sure certain values aren't undef
830+ map { $dl->{$_} ||= '' } qw(rolled_back);
831+
832+ delete $dl->{'fulltext'} unless $debug;
833+ return 1;
834+}
835+
836+sub parse_innodb_record_locks {
837+ my ( $text, $complete, $debug, $full ) = @_;
838+ my @result;
839+
840+ foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) {
841+ my $hash = {};
842+ @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) }
843+ = $lock
844+ =~ 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;
845+ ( $hash->{'special'} )
846+ = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m;
847+ $hash->{'insert_intention'}
848+ = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0;
849+ $hash->{'waiting'}
850+ = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0;
851+
852+ # Some things may not be in the text, so make sure they are not
853+ # undef.
854+ map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id);
855+ map { $hash->{$_} ||= "" } qw(index special);
856+ push @result, $hash;
857+ }
858+
859+ return @result;
860+}
861+
862+sub parse_tx_text {
863+ my ( $txn, $complete, $debug, $full ) = @_;
864+
865+ my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id )
866+ = $txn
867+ =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m;
868+ my ( $thread_status, $thread_decl_inside )
869+ = $txn
870+ =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m;
871+
872+ # Parsing the line that begins 'MySQL thread id' is complicated. The only
873+ # thing always in the line is the thread and query id. See function
874+ # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc.
875+ my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m;
876+ my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status );
877+
878+ if ( $thread_line ) {
879+ # These parts can always be gotten.
880+ ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m;
881+
882+ # If it's a master/slave thread, "Has (read|sent) all" may be the thread's
883+ # proc_info. In these cases, there won't be any host/ip/user info
884+ ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m;
885+ if ( defined($query_status) ) {
886+ $user = 'system user';
887+ }
888+
889+ # It may be the case that the query id is the last thing in the line.
890+ elsif ( $thread_line =~ m/query id \d+ / ) {
891+ # The IP address is the only non-word thing left, so it's the most
892+ # useful marker for where I have to start guessing.
893+ ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m;
894+ if ( defined $ip ) {
895+ ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/;
896+ }
897+ else { # OK, there wasn't an IP address.
898+ # There might not be ANYTHING except the query status.
899+ ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/;
900+ if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) {
901+ # The remaining tokens are, in order: hostname, user, query_status.
902+ # It's basically impossible to know which is which.
903+ ( $hostname, $user, $query_status ) = $thread_line
904+ =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m;
905+ }
906+ else {
907+ $user = 'system user';
908+ }
909+ }
910+ }
911+ }
912+
913+ my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries )
914+ = $txn
915+ =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m;
916+ my ( $lock_wait_time )
917+ = $txn
918+ =~ m/^------- TRX HAS BEEN WAITING $d SEC/m;
919+
920+ my $locks;
921+ # If the transaction has locks, grab the locks.
922+ if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) {
923+ $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)];
924+ }
925+
926+ my ( $tables_in_use, $tables_locked )
927+ = $txn
928+ =~ m/^mysql tables in use $d, locked $d$/m;
929+ my ( $txn_doesnt_see_ge, $txn_sees_lt )
930+ = $txn
931+ =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m;
932+ my $has_read_view = defined($txn_doesnt_see_ge);
933+ # Only a certain number of bytes of the query text are included here, at least
934+ # under some circumstances. Some versions include 300, some 600.
935+ my ( $query_text )
936+ = $txn
937+ =~ m{
938+ ^MySQL\sthread\sid\s[^\n]+\n # This comes before the query text
939+ (.*?) # The query text
940+ (?= # Followed by any of...
941+ ^Trx\sread\sview
942+ |^-------\sTRX\sHAS\sBEEN\sWAITING
943+ |^TABLE\sLOCK
944+ |^RECORD\sLOCKS\sspace\sid
945+ |^(?:---)?TRANSACTION
946+ |^\*\*\*\s\(\d\)
947+ |\Z
948+ )
949+ }xms;
950+ if ( $query_text ) {
951+ $query_text =~ s/\s+$//;
952+ }
953+ else {
954+ $query_text = '';
955+ }
956+
957+ my %stuff = (
958+ active_secs => $active_secs,
959+ has_read_view => $has_read_view,
960+ heap_size => $heap_size,
961+ hostname => $hostname,
962+ ip => $ip,
963+ lock_structs => $lock_structs,
964+ lock_wait_status => $lock_wait_status,
965+ lock_wait_time => $lock_wait_time,
966+ mysql_thread_id => $mysql_thread_id,
967+ os_thread_id => $os_thread_id,
968+ proc_no => $proc_no,
969+ query_id => $query_id,
970+ query_status => $query_status,
971+ query_text => $query_text,
972+ row_locks => $row_locks,
973+ tables_in_use => $tables_in_use,
974+ tables_locked => $tables_locked,
975+ thread_decl_inside => $thread_decl_inside,
976+ thread_status => $thread_status,
977+ txn_doesnt_see_ge => $txn_doesnt_see_ge,
978+ txn_id => $txn_id,
979+ txn_sees_lt => $txn_sees_lt,
980+ txn_status => $txn_status,
981+ undo_log_entries => $undo_log_entries,
982+ user => $user,
983+ );
984+ $stuff{'fulltext'} = $txn if $debug;
985+ $stuff{'locks'} = $locks if $locks;
986+
987+ # Some things may not be in the txn text, so make sure they are not
988+ # undef.
989+ map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs
990+ tables_in_use undo_log_entries tables_locked has_read_view
991+ thread_decl_inside lock_wait_time proc_no row_locks);
992+ map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge
993+ txn_sees_lt query_status ip query_text lock_wait_status user);
994+ $stuff{'hostname'} ||= $stuff{'ip'};
995+
996+ return \%stuff;
997+}
998+
999+sub parse_tx_section {
1000+ my ( $section, $complete, $debug, $full ) = @_;
1001+ return unless $section && $section->{'fulltext'};
1002+ my $fulltext = $section->{'fulltext'};
1003+ $section->{'transactions'} = [];
1004+
1005+ # Handle the individual transactions
1006+ my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs;
1007+ foreach my $txn ( @transactions ) {
1008+ my $stuff = parse_tx_text( $txn, $complete, $debug, $full );
1009+ delete $stuff->{'fulltext'} unless $debug;
1010+ push @{$section->{'transactions'}}, $stuff;
1011+ }
1012+
1013+ # Handle the general info
1014+ @{$section}{ 'trx_id_counter' }
1015+ = $fulltext =~ m/^Trx id counter $t$/m;
1016+ @{$section}{ 'purge_done_for', 'purge_undo_for' }
1017+ = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m;
1018+ @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions
1019+ = $fulltext =~ m/^History list length $d$/m;
1020+ @{$section}{ 'num_lock_structs' }
1021+ = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m;
1022+ @{$section}{ 'is_truncated' }
1023+ = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0;
1024+
1025+ # Fill in things that might not be present
1026+ foreach ( qw(history_list_len) ) {
1027+ $section->{$_} ||= 0;
1028+ }
1029+
1030+ delete $section->{'fulltext'} unless $debug;
1031+ return 1;
1032+}
1033+
1034+# I've read the source for this section.
1035+sub parse_ro_section {
1036+ my ( $section, $complete, $debug, $full ) = @_;
1037+ return unless $section && $section->{'fulltext'};
1038+ my $fulltext = $section->{'fulltext'};
1039+
1040+ # Grab the info
1041+ @{$section}{ 'queries_inside', 'queries_in_queue' }
1042+ = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m;
1043+ ( $section->{ 'read_views_open' } )
1044+ = $fulltext =~ m/^$d read views open inside InnoDB$/m;
1045+ ( $section->{ 'n_reserved_extents' } )
1046+ = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m;
1047+ @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' }
1048+ = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m;
1049+ @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' }
1050+ = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m;
1051+ @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' }
1052+ = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m;
1053+ $section->{'main_thread_proc_no'} ||= 0;
1054+
1055+ map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents);
1056+ delete $section->{'fulltext'} unless $debug;
1057+ return 1;
1058+}
1059+
1060+sub parse_lg_section {
1061+ my ( $section, $complete, $debug, $full ) = @_;
1062+ return unless $section;
1063+ my $fulltext = $section->{'fulltext'};
1064+
1065+ # Grab the info
1066+ ( $section->{ 'log_seq_no' } )
1067+ = $fulltext =~ m/Log sequence number \s*(\d.*)$/m;
1068+ ( $section->{ 'log_flushed_to' } )
1069+ = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m;
1070+ ( $section->{ 'last_chkp' } )
1071+ = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m;
1072+ @{$section}{ 'pending_log_writes', 'pending_chkp_writes' }
1073+ = $fulltext =~ m/$d pending log writes, $d pending chkp writes/;
1074+ @{$section}{ 'log_ios_done', 'log_ios_s' }
1075+ = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#;
1076+
1077+ delete $section->{'fulltext'} unless $debug;
1078+ return 1;
1079+}
1080+
1081+sub parse_ib_section {
1082+ my ( $section, $complete, $debug, $full ) = @_;
1083+ return unless $section && $section->{'fulltext'};
1084+ my $fulltext = $section->{'fulltext'};
1085+
1086+ # Some servers will output ibuf information for tablespace 0, as though there
1087+ # might be many tablespaces with insert buffers. (In practice I believe
1088+ # the source code shows there will only ever be one). I have to parse both
1089+ # cases here, but I assume there will only be one.
1090+ @{$section}{ 'size', 'free_list_len', 'seg_size' }
1091+ = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m;
1092+ @{$section}{ 'inserts', 'merged_recs', 'merges' }
1093+ = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m;
1094+
1095+ @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' }
1096+ = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m;
1097+ @{$section}{ 'hash_searches_s', 'non_hash_searches_s' }
1098+ = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m;
1099+
1100+ delete $section->{'fulltext'} unless $debug;
1101+ return 1;
1102+}
1103+
1104+sub parse_wait_array {
1105+ my ( $text, $complete, $debug, $full ) = @_;
1106+ my %result;
1107+
1108+ @result{ qw(thread waited_at_filename waited_at_line waited_secs) }
1109+ = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m;
1110+
1111+ # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED,
1112+ # there will be different text output
1113+ if ( $text =~ m/^Mutex at/m ) {
1114+ $result{'request_type'} = 'M';
1115+ @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) }
1116+ = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m;
1117+ @result{ qw( waiters_flag )}
1118+ = $text =~ m/^waiters flag $d$/m;
1119+ }
1120+ else {
1121+ @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) }
1122+ = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m;
1123+ @result{ qw( writer_thread writer_lock_mode ) }
1124+ = $text =~ m/^a writer \(thread id $d\) has reserved it in mode (.*)$/m;
1125+ @result{ qw( num_readers waiters_flag )}
1126+ = $text =~ m/^number of readers $d, waiters flag $d$/m;
1127+ @result{ qw(last_s_file_name last_s_line ) }
1128+ = $text =~ m/Last time read locked in file $fl$/m;
1129+ @result{ qw(last_x_file_name last_x_line ) }
1130+ = $text =~ m/Last time write locked in file $fl$/m;
1131+ }
1132+
1133+ $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1;
1134+ $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0;
1135+
1136+ # Because there are two code paths, some things won't get set.
1137+ map { $result{$_} ||= '' }
1138+ qw(last_s_file_name last_x_file_name writer_lock_mode);
1139+ map { $result{$_} ||= 0 }
1140+ qw(num_readers lock_var last_s_line last_x_line writer_thread);
1141+
1142+ return \%result;
1143+}
1144+
1145+sub parse_sm_section {
1146+ my ( $section, $complete, $debug, $full ) = @_;
1147+ return 0 unless $section && $section->{'fulltext'};
1148+ my $fulltext = $section->{'fulltext'};
1149+
1150+ # Grab the info
1151+ @{$section}{ 'reservation_count', 'signal_count' }
1152+ = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m;
1153+ @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' }
1154+ = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m;
1155+ @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' }
1156+ = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m;
1157+
1158+ # Look for info on waits.
1159+ my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms;
1160+ $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ];
1161+ $section->{'wait_array_size'} = scalar(@waits);
1162+
1163+ delete $section->{'fulltext'} unless $debug;
1164+ return 1;
1165+}
1166+
1167+# I've read the source for this section.
1168+sub parse_bp_section {
1169+ my ( $section, $complete, $debug, $full ) = @_;
1170+ return unless $section && $section->{'fulltext'};
1171+ my $fulltext = $section->{'fulltext'};
1172+
1173+ # Grab the info
1174+ @{$section}{ 'total_mem_alloc', 'add_pool_alloc' }
1175+ = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m;
1176+ @{$section}{'dict_mem_alloc'} = $fulltext =~ m/Dictionary memory allocated $d/;
1177+ @{$section}{'awe_mem_alloc'} = $fulltext =~ m/$d MB of AWE memory/;
1178+ @{$section}{'buf_pool_size'} = $fulltext =~ m/^Buffer pool size\s*$d$/m;
1179+ @{$section}{'buf_free'} = $fulltext =~ m/^Free buffers\s*$d$/m;
1180+ @{$section}{'pages_total'} = $fulltext =~ m/^Database pages\s*$d$/m;
1181+ @{$section}{'pages_modified'} = $fulltext =~ m/^Modified db pages\s*$d$/m;
1182+ @{$section}{'pages_read', 'pages_created', 'pages_written'}
1183+ = $fulltext =~ m/^Pages read $d, created $d, written $d$/m;
1184+ @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'}
1185+ = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m;
1186+ @{$section}{'buf_pool_hits', 'buf_pool_reads'}
1187+ = $fulltext =~ m{Buffer pool hit rate $d / $d$}m;
1188+ if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) {
1189+ @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0);
1190+ @{$section}{'buf_pool_hit_rate'} = '--';
1191+ }
1192+ else {
1193+ @{$section}{'buf_pool_hit_rate'}
1194+ = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m;
1195+ }
1196+ @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m;
1197+ @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' }
1198+ = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m;
1199+
1200+ map { $section->{$_} ||= 0 }
1201+ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page
1202+ awe_mem_alloc dict_mem_alloc);
1203+ @{$section}{'writes_pending'} = List::Util::sum(
1204+ @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) });
1205+
1206+ delete $section->{'fulltext'} unless $debug;
1207+ return 1;
1208+}
1209+
1210+# I've read the source for this.
1211+sub parse_io_section {
1212+ my ( $section, $complete, $debug, $full ) = @_;
1213+ return unless $section && $section->{'fulltext'};
1214+ my $fulltext = $section->{'fulltext'};
1215+ $section->{'threads'} = {};
1216+
1217+ # Grab the I/O thread info
1218+ my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm;
1219+ foreach my $thread (@threads) {
1220+ my ( $tid, $state, $purpose, $event_set )
1221+ = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m;
1222+ if ( defined $tid ) {
1223+ $section->{'threads'}->{$tid} = {
1224+ thread => $tid,
1225+ state => $state,
1226+ purpose => $purpose,
1227+ event_set => $event_set ? 1 : 0,
1228+ };
1229+ }
1230+ }
1231+
1232+ # Grab the reads/writes/flushes info
1233+ @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' }
1234+ = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m;
1235+ @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' }
1236+ = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m;
1237+ @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' }
1238+ = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m;
1239+ @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' }
1240+ = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m;
1241+ @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' }
1242+ = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m;
1243+ @{$section}{ 'pending_preads', 'pending_pwrites' }
1244+ = $fulltext =~ m/$d pending preads, $d pending pwrites$/m;
1245+ @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0)
1246+ unless defined($section->{'pending_preads'});
1247+
1248+ delete $section->{'fulltext'} unless $debug;
1249+ return 1;
1250+}
1251+
1252+sub _debug {
1253+ my ( $debug, $msg ) = @_;
1254+ if ( $debug ) {
1255+ die $msg;
1256+ }
1257+ else {
1258+ warn $msg;
1259+ }
1260+ return 1;
1261+}
1262+
1263+1;
1264+
1265+# end_of_package
1266+# ############################################################################
1267+# Perldoc section. I put this last as per the Dog book.
1268+# ############################################################################
1269+=pod
1270+
1271+=head1 NAME
1272+
1273+InnoDBParser - Parse InnoDB monitor text.
1274+
1275+=head1 DESCRIPTION
1276+
1277+InnoDBParser tries to parse the output of the InnoDB monitor. One way to get
1278+this output is to connect to a MySQL server and issue the command SHOW ENGINE
1279+INNODB STATUS (omit 'ENGINE' on earlier versions of MySQL). The goal is to
1280+turn text into data that something else (e.g. innotop) can use.
1281+
1282+The output comes from all over, but the place to start in the source is
1283+innobase/srv/srv0srv.c.
1284+
1285+=head1 SYNOPSIS
1286+
1287+ use InnoDBParser;
1288+ use DBI;
1289+
1290+ # Get the status text.
1291+ my $dbh = DBI->connect(
1292+ "DBI::mysql:test;host=localhost",
1293+ 'user',
1294+ 'password'
1295+ );
1296+ my $query = 'SHOW /*!5 ENGINE */ INNODB STATUS';
1297+ my $text = $dbh->selectcol_arrayref($query)->[0];
1298+
1299+ # 1 or 0
1300+ my $debug = 1;
1301+
1302+ # Choose sections of the monitor text you want. Possible values:
1303+ # TRANSACTIONS => tx
1304+ # BUFFER POOL AND MEMORY => bp
1305+ # SEMAPHORES => sm
1306+ # LOG => lg
1307+ # ROW OPERATIONS => ro
1308+ # INSERT BUFFER AND ADAPTIVE HASH INDEX => ib
1309+ # FILE I/O => io
1310+ # LATEST DETECTED DEADLOCK => dl
1311+ # LATEST FOREIGN KEY ERROR => fk
1312+
1313+ my $required_sections = {
1314+ tx => 1,
1315+ };
1316+
1317+ # Parse the status text.
1318+ my $parser = InnoDBParser->new;
1319+ $innodb_status = $parser->parse_status_text(
1320+ $text,
1321+ $debug,
1322+ # Omit the following parameter to get all sections.
1323+ $required_sections,
1324+ );
1325+
1326+=head1 COPYRIGHT, LICENSE AND WARRANTY
1327+
1328+This package is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com.
1329+Feedback and improvements are gratefully received.
1330+
1331+THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
1332+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1333+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1334+
1335+This program is free software; you can redistribute it and/or modify it under
1336+the terms of the GNU General Public License as published by the Free Software
1337+Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
1338+systems, you can issue `man perlgpl' or `man perlartistic' to read these
1339+licenses.
1340+
1341+You should have received a copy of the GNU General Public License along with
1342+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
1343+Place, Suite 330, Boston, MA 02111-1307 USA
1344+
1345+=head1 AUTHOR
1346+
1347+Baron Schwartz, baron at xaprb dot com.
1348+
1349+=head1 BUGS
1350+
1351+None known, but I bet there are some. The InnoDB monitor text wasn't really
1352+designed to be parsable.
1353+
1354+=head1 SEE ALSO
1355+
1356+innotop - a program that can format the parsed status information for humans
1357+to read and enjoy.
1358+
1359+=cut
1360
1361=== added file 'build/debian/additions/innotop/changelog.innotop'
1362--- build/debian/additions/innotop/changelog.innotop 1970-01-01 00:00:00 +0000
1363+++ build/debian/additions/innotop/changelog.innotop 2010-05-31 18:03:25 +0000
1364@@ -0,0 +1,318 @@
1365+Changelog for innotop and InnoDBParser:
1366+
1367+2007-11-09: version 1.6.0
1368+
1369+ * S mode crashed on non-numeric values.
1370+ * New user-defined columns crashed upon restart.
1371+ * Added --color option to control terminal coloring.
1372+
1373+2007-09-18: version 1.5.2
1374+
1375+ * Added the ability to monitor InnoDB status from a file.
1376+ * Changed W mode to L mode; it monitors all locks, not just lock waits.
1377+
1378+2007-09-16: version 1.5.1
1379+
1380+ * Added C (Command Summary) mode.
1381+ * Fixed a bug in the 'avg' aggregate function.
1382+
1383+2007-09-10: version 1.5.0
1384+
1385+ Changes:
1386+ * Added plugin functionality.
1387+ * Added group-by functionality.
1388+ * Moved the configuration file to a directory.
1389+ * Enhanced filtering and sorting on pivoted tables.
1390+ * Many small bug fixes.
1391+
1392+2007-07-16: version 1.4.3
1393+
1394+ Changes:
1395+ * Added standard --version command-line option
1396+ * Changed colors to cyan instead of blue; more visible on dark terminals.
1397+ * Added information to the filter-choosing dialog.
1398+ * Added column auto-completion when entering a filter expression.
1399+ * Changed Term::ReadKey from optional to mandatory.
1400+ * Clarified username in password prompting.
1401+ * Ten thousand words of documentation!
1402+
1403+ Bugs fixed:
1404+ * innotop crashed in W mode when InnoDB status data was truncated.
1405+ * innotop didn't display errors in tables if debug was enabled.
1406+ * The colored() subroutine wasn't being created in non-interactive mode.
1407+ * Don't prompt to save password except the first time.
1408+
1409+2007-05-03: version 1.4.2
1410+
1411+ This version contains all changes to the trunk until revision 239; some
1412+ changes in revisions 240:250 are included.
1413+
1414+ MAJOR CHANGES:
1415+
1416+ * Quick-filters to easily filter any column in any display
1417+ * Compatibility with MySQL 3.23 through 6.0
1418+ * Improved error handling when a server is down, permissions denied, etc
1419+ * Use additional SHOW INNODB STATUS information in 5.1.x
1420+ * Make all modes use tables consistently, so they can all be edited,
1421+ filtered, colored and sorted consistently
1422+ * Combine V, G and S modes into S mode, with v, g, and s hot-keys
1423+ * Let DBD driver read MySQL option files; permit connections without
1424+ user/pass/etc
1425+ * Compile SQL-like expressions into Perl subroutines; eliminate need to
1426+ know Perl
1427+ * Do not save all config data to config file, only save user's customizations
1428+ * Rewritten and improved command-line option handling
1429+ * Added --count, --delay, and other command-line options to support
1430+ run-and-exit operation
1431+ * Improve built-in variable sets
1432+ * Improve help screen with three-part balanced-column layout
1433+ * Simplify table-editor and improve hotkey support
1434+ * Require Perl to have high-resolution time support (Time::HiRes)
1435+ * Help the user choose a query to analyze or kill
1436+ * Enable EXPLAIN, show-full-query in T mode just like Q mode
1437+ * Let data-extraction access current, previous and incremental data sets
1438+ all at once
1439+
1440+ MINOR CHANGES:
1441+
1442+ * Column stabilizing for Q mode
1443+ * New color rules for T, Q, W modes
1444+ * Apply slave I/O filter to Q mode
1445+ * Improve detection of server version and other meta-data
1446+ * Make connection timeout a config variable
1447+ * Improve cross-version-compatible SQL syntax
1448+ * Get some information from the DBD driver instead of asking MySQL for it
1449+ * Improved error messages
1450+ * Improve server group creation/editing
1451+ * Improve connection/thread killing
1452+ * Fix broken key bindings and restore previously mapped hot-keys for
1453+ choosing columns
1454+ * Some documentation updates (but not nearly enough)
1455+ * Allow the user to specify graphing char in S mode (formerly G mode)
1456+ * Allow easy switching between variable sets in S mode
1457+ * Bind 'n' key globally to choose the 'next' server connection
1458+ * Bind '%' key globally to filter displayed tables
1459+ * Allow aligning columns on the decimal place for easy readability
1460+ * Add hide_hdr config variable to hide column headers in tables
1461+ * Add a feature to smartly run PURGE MASTER LOGS in Replication mode
1462+ * Enable debug mode as a globally configurable variable
1463+ * Improve error messages when an expression or filter doesn't compile or has
1464+ a run-time error; die on error when debug is enabled
1465+ * Allow user-configurable delays after executing SQL (to let the server
1466+ settle down before taking another measurement)
1467+ * Add an expression to show how long until a transaction is finished
1468+ * Add skip_innodb as a global config variable
1469+ * Add '%' after percentages to help disambiguate (user-configurable)
1470+ * Add column to M mode to help see how fast slave is catching up to master
1471+
1472+ BUG FIXES:
1473+
1474+ * T and W modes had wrong value for wait_status column
1475+ * Error tracking on connections didn't reset when the connection recovered
1476+ * wait_timeout on connections couldn't be set before MySQL 4.0.3
1477+ * There was a crash on 3.23 when wiping deadlocks
1478+ * Lettercase changes in some result sets (SHOW MASTER/SLAVE STATUS) between
1479+ MySQL versions crashed innotop
1480+ * Inactive connections crashed innotop upon access to DBD driver
1481+ * set_precision did not respect user defaults for number of digits
1482+ * --inc command-line option could not be negated
1483+ * InnoDB status parsing was not always parsing all needed information
1484+ * S mode (formerly G mode) could crash trying to divide non-numeric data
1485+ * M table didn't show Slave_open_temp_tables variable; incorrect lettercase
1486+ * DBD drivers with broken AutoCommit would crash innotop
1487+ * Some key bindings had incorrect labels
1488+ * Some config-file loading routines could load data for things that didn't
1489+ exist
1490+ * Headers printed too often in S mode
1491+ * High-resolution time was not used even when the user had it
1492+ * Non-interactive mode printed blank lines sometimes
1493+ * Q-mode header and statusbar showed different QPS numbers
1494+ * Formulas for key-cache and query-cache hit ratios were wrong
1495+ * Mac OS "Darwin" machines were mis-identified as Microsoft Windows
1496+ * Some multiplications crashed when given undefined input
1497+ * The commify transformation did not check its input and could crash
1498+ * Specifying an invalid mode on the command line or config file could crash
1499+ innotop
1500+
1501+2007-03-29: version 1.4.1
1502+
1503+ * More tweaks to display of connection errors.
1504+ * Fixed a problem with skip-innodb in MySQL 5.1.
1505+ * Fix a bug with dead connections in single-connection mode.
1506+ * Fix a regex to allow parsing more data from truncated deadlocks.
1507+ * Don't load active cxns from the config file if the cxn isn't defined.
1508+
1509+2007-03-03: version 1.4.0
1510+
1511+ * Further tweak error handling and display of connection errors
1512+ * More centralization of querying
1513+ * Fix forking so it doesn't kill all database connections
1514+ * Allow user to run innotop without permissions for GLOBAL variables and status
1515+
1516+2007-02-11: version 1.3.6
1517+
1518+ * Handle some connection failures so innotop doesn't crash because of one server.
1519+ * Enable incremental display in more modes.
1520+ * Tweaks to colorizing, color editor, and default color rules.
1521+ * Tweaks to default sorting rules.
1522+ * Use prepared statements for efficiency.
1523+ * Bug fixes and code cleanups.
1524+ * Data storage is keyed on clock ticks now.
1525+
1526+2007-02-03: version 1.3.5
1527+
1528+ * Bug fixes.
1529+ * More tools for editing configuration from within innotop.
1530+ * Filters and transformations are constrained to valid values.
1531+ * Support for colorizing rows.
1532+ * Sorting by multiple columns.
1533+ * Compress headers when display is very wide.
1534+ * Stabilize and limit column widths.
1535+ * Check config file formats when upgrading so upgrades go smoothly.
1536+ * Make D mode handle many connections at once.
1537+ * Extract simple expressions from data sets in column src property.
1538+ This makes innotop more awk-ish.
1539+
1540+2007-01-16: version 1.3
1541+
1542+ * Readline support.
1543+ * Can be used unattended, or in a pipe-and-filter mode
1544+ where it outputs tab-separated data to standard output.
1545+ * You can specify a config file on the command line.
1546+ Config files can be marked read-only.
1547+ * Monitor multiple servers simultaneously.
1548+ * Server groups to help manage many servers conveniently.
1549+ * Monitor master/slave status, and control slaves.
1550+ * Columns can have user-defined expressions as their data sources.
1551+ * Better configuration tools.
1552+ * InnoDB status information is merged into SHOW VARIABLES and
1553+ SHOW STATUS information, so you can access it all together.
1554+ * High-precision time support in more places.
1555+ * Lots of tweaks to make things display more readably and compactly.
1556+ * Column transformations and filters.
1557+
1558+2007-01-16: version 1.0.1
1559+ * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS.
1560+ The new project homepage is http://sourceforge.net/projects/innotop/
1561+ * Tweak default T/Q mode sort columns to match what people expect.
1562+ * Fix broken InnoDBParser.pm documentation (and hence man page).
1563+
1564+2007-01-06: version 1.0
1565+ * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS.
1566+ The new project homepage is http://sourceforge.net/projects/innotop/
1567+ * Prevent control characters from freaking terminal out.
1568+ * Set timeout to keep busy servers from closing connection.
1569+ * There is only one InnoDB insert buffer.
1570+ * Make licenses clear and consistent.
1571+
1572+2006-11-14: innotop 0.1.160, InnoDBParser version 1.69
1573+ * Support for ANSI color on Microsoft Windows (more readable, compact
1574+ display; thanks Gisbert W. Selke).
1575+ * Better handling of $ENV{HOME} on Windows.
1576+ * Added a LICENSE file to the package as per Gentoo bug:
1577+ http://bugs.gentoo.org/show_bug.cgi?id=147600
1578+
1579+2006-11-11: innotop 0.1.157, InnoDBParser version 1.69
1580+ * Add Microsoft Windows support.
1581+
1582+2006-10-19: innotop 0.1.154, InnoDBParser version 1.69
1583+ * Add O (Open Tables) mode
1584+ * Add some more checks to handle incomplete InnoDB status information
1585+
1586+2006-09-30: innotop 0.1.152, InnoDBParser version 1.69
1587+ * Figured out what was wrong with package $VERSION variable: it wasn't
1588+ after the package declaration!
1589+
1590+2006-09-28: innotop 0.1.152, InnoDBParser version 1.67
1591+ * Make more efforts towards crash-resistance and tolerance of completely
1592+ messed-up inputs. If innotop itself is broken, it is now much harder to
1593+ tell, because it just keeps on running without complaining.
1594+ * Fix a small bug parsing out some information and displaying it.
1595+
1596+2006-09-05: innotop 0.1.149, InnoDBParser version 1.64
1597+ * Try to find and eliminate any parsing code that assumes pattern matches
1598+ will succeed.
1599+
1600+2006-09-05: innotop 0.1.149, InnoDBParser version 1.62
1601+ * Make innotop crash-resistant, so I can declare it STABLE finally.
1602+ * Instead of using SQL conditional comments, detect MySQL version.
1603+
1604+2006-08-22: innotop 0.1.147, InnoDBParser version 1.60
1605+ * Fix some innotop bugs with undefined values, bad formatting etc.
1606+
1607+2006-08-19: innotop 0.1.146, InnoDBParser version 1.60
1608+ * Make innotop handle some unexpected NULL values in Q mode.
1609+ * Add OS wait information to W mode, so it is now "everything that waits."
1610+ * Center section captions better.
1611+ * Make R mode more readable and compact.
1612+ * Make InnoDBParser parse lock waits even when they've been waiting 0 secs.
1613+
1614+2006-08-12: innotop 0.1.139, InnoDBParser version 1.59
1615+ * Add more documentation
1616+ * Tweak V mode to show more info in less space.
1617+ * Fix a bug in G mode.
1618+
1619+2006-08-10: innotop 0.1.132, InnoDBParser version 1.58
1620+ * Handle yet more types of FK error... it will never end!
1621+ * Handle some special cases when DEADLOCK info truncated
1622+ * Add a bit more FK info to F mode in innotop
1623+ * More tests added to the test suite
1624+
1625+2006-08-07: innotop 0.1.131, InnoDBParser version 1.55
1626+ * Fix another issue with configuration
1627+ * Handle another type of FK error
1628+
1629+2006-08-03: innotop 0.1.130, InnoDBParser version 1.54
1630+ * Fix an issue loading config file
1631+ * Add heap_no to 'D' (InnoDB Deadlock) mode to ease deadlock debugging.
1632+
1633+2006-08-02: innotop 0.1.128, InnoDBParser version 1.54
1634+ * Parse lock wait information from the TRANSACTION section.
1635+ * Even more OS-specific parsing... pain in the butt...
1636+ * Add 'W' (InnoDB Lock Wait) mode.
1637+ * Fix some minor display issues with statusbar.
1638+
1639+2006-08-02: innotop 0.1.125, InnoDBParser version 1.50
1640+ * Don't try to get references to Perl built-in functions like time()
1641+ * Handle more OS-specific variations of InnoDB status text
1642+ * Add some more information to various places in innotop
1643+
1644+2006-08-01: innotop 0.1.123, InnoDBParser version 1.47
1645+
1646+ * Enhance S and G modes: clear screen and re-print headers
1647+ * Don't crash when deadlock data is truncated
1648+ * Make Analyze mode say how to get back to whatever you came from
1649+ * Display 'nothing to display' when there is nothing
1650+ * Add ability to read InnoDB status text from a file (mostly helps test)
1651+ * Add table of Wait Array Information in Row Op/Semaphore mode
1652+ * Add table of lock information in InnoDB deadlock mode
1653+ * Ensure new features in upgrades don't get masked by existing config files
1654+ * Tweak default column choices for T mode
1655+ * Enhance foreign key parsing
1656+ * Enhance physical record and data tuple parsing
1657+ * Enhance lock parsing (handle old-style and new-style formats)
1658+
1659+2006-07-24: innotop 0.1.112, InnoDBParser version 1.36
1660+
1661+ * InnoDBParser enhancements for FK error messages.
1662+ * A fix to innotop to prevent it from crashing while trying to display a FK
1663+ error message.
1664+ * Some minor cosmetic changes to number formatting in innotop.
1665+
1666+2006-07-22: innotop 0.1.106, InnoDBParser version 1.35
1667+
1668+ * InnoDBParser is much more complete and accurate.
1669+ * Tons of bug fixes.
1670+ * Add partitions to EXPLAIN mode.
1671+ * Enhance Q mode header, add T mode header.
1672+ * Share some configuration variables across modes.
1673+ * Add formatted time columns to Q, T modes.
1674+ * Add command-line argument parsing.
1675+ * Turn off echo when asking for password.
1676+ * Add option to specify port when connecting.
1677+ * Let display-optimized-query display multiple notes.
1678+ * Lots of small improvements, such as showing more info in statusbar.
1679+
1680+2006-07-02: innotop 0.1.74, InnoDBParser version 1.24
1681+
1682+ * Initial release for public consumption.
1683
1684=== added file 'build/debian/additions/innotop/innotop'
1685--- build/debian/additions/innotop/innotop 1970-01-01 00:00:00 +0000
1686+++ build/debian/additions/innotop/innotop 2010-05-31 18:03:25 +0000
1687@@ -0,0 +1,9485 @@
1688+#!/usr/bin/perl
1689+
1690+# vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3
1691+
1692+use strict;
1693+use warnings FATAL => 'all';
1694+use sigtrap qw(handler finish untrapped normal-signals);
1695+
1696+use Data::Dumper;
1697+use DBI;
1698+use English qw(-no_match_vars);
1699+use File::Basename qw(dirname);
1700+use Getopt::Long;
1701+use List::Util qw(max min maxstr sum);
1702+use InnoDBParser;
1703+use POSIX qw(ceil);
1704+use Time::HiRes qw(time sleep);
1705+use Term::ReadKey qw(ReadMode ReadKey);
1706+
1707+# Version, license and warranty information. {{{1
1708+# ###########################################################################
1709+our $VERSION = '1.6.0';
1710+our $SVN_REV = sprintf("%d", q$Revision: 383 $ =~ m/(\d+)/g);
1711+our $SVN_URL = sprintf("%s", q$URL: https://innotop.svn.sourceforge.net/svnroot/innotop/trunk/innotop $ =~ m$svnroot/innotop/(\S+)$g);
1712+
1713+my $innotop_license = <<"LICENSE";
1714+
1715+This is innotop version $VERSION, a MySQL and InnoDB monitor.
1716+
1717+This program is copyright (c) 2006 Baron Schwartz.
1718+Feedback and improvements are welcome.
1719+
1720+THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
1721+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1722+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1723+
1724+This program is free software; you can redistribute it and/or modify it under
1725+the terms of the GNU General Public License as published by the Free Software
1726+Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
1727+systems, you can issue `man perlgpl' or `man perlartistic' to read these
1728+licenses.
1729+
1730+You should have received a copy of the GNU General Public License along with
1731+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
1732+Place, Suite 330, Boston, MA 02111-1307 USA.
1733+LICENSE
1734+
1735+# Configuration information and global setup {{{1
1736+# ###########################################################################
1737+
1738+# Really, really, super-global variables.
1739+my @config_versions = (
1740+ "000-000-000", "001-003-000", # config file was one big name-value hash.
1741+ "001-003-000", "001-004-002", # config file contained non-user-defined stuff.
1742+);
1743+
1744+my $clear_screen_sub;
1745+
1746+# This defines expected properties and defaults for the column definitions that
1747+# eventually end up in tbl_meta.
1748+my %col_props = (
1749+ hdr => '',
1750+ just => '-',
1751+ dec => 0, # Whether to align the column on the decimal point
1752+ num => 0,
1753+ label => '',
1754+ user => 0,
1755+ src => '',
1756+ tbl => '', # Helps when writing/reading custom columns in config files
1757+ minw => 0,
1758+ maxw => 0,
1759+ trans => [],
1760+ agg => 'first', # Aggregate function
1761+ aggonly => 0, # Whether to show only when tbl_meta->{aggregate} is true
1762+);
1763+
1764+# Actual DBI connections to MySQL servers.
1765+my %dbhs;
1766+
1767+# Command-line parameters {{{2
1768+# ###########################################################################
1769+
1770+my @opt_spec = (
1771+ { s => 'help', d => 'Show this help message' },
1772+ { s => 'color|C!', d => 'Use terminal coloring (default)', c => 'color' },
1773+ { s => 'config|c=s', d => 'Config file to read' },
1774+ { s => 'nonint|n', d => 'Non-interactive, output tab-separated fields' },
1775+ { s => 'count=i', d => 'Number of updates before exiting' },
1776+ { s => 'delay|d=f', d => 'Delay between updates in seconds', c => 'interval' },
1777+ { s => 'mode|m=s', d => 'Operating mode to start in', c => 'mode' },
1778+ { s => 'inc|i!', d => 'Measure incremental differences', c => 'status_inc' },
1779+ { s => 'version', d => 'Output version information and exit' },
1780+);
1781+
1782+# This is the container for the command-line options' values to be stored in
1783+# after processing. Initial values are defaults.
1784+my %opts = (
1785+ n => !( -t STDIN && -t STDOUT ), # If in/out aren't to terminals, we're interactive
1786+);
1787+# Post-process...
1788+my %opt_seen;
1789+foreach my $spec ( @opt_spec ) {
1790+ my ( $long, $short ) = $spec->{s} =~ m/^(\w+)(?:\|([^!+=]*))?/;
1791+ $spec->{k} = $short || $long;
1792+ $spec->{l} = $long;
1793+ $spec->{t} = $short;
1794+ $spec->{n} = $spec->{s} =~ m/!/;
1795+ $opts{$spec->{k}} = undef unless defined $opts{$spec->{k}};
1796+ die "Duplicate option $spec->{k}" if $opt_seen{$spec->{k}}++;
1797+}
1798+
1799+Getopt::Long::Configure('no_ignore_case', 'bundling');
1800+GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1;
1801+
1802+if ( $opts{version} ) {
1803+ print "innotop Ver $VERSION Changeset $SVN_REV from $SVN_URL\n";
1804+ exit(0);
1805+}
1806+
1807+if ( $opts{'help'} ) {
1808+ print "Usage: innotop <options> <innodb-status-file>\n\n";
1809+ my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec);
1810+ foreach my $spec ( sort { $a->{l} cmp $b->{l} } @opt_spec ) {
1811+ my $long = $spec->{n} ? "[no]$spec->{l}" : $spec->{l};
1812+ my $short = $spec->{t} ? "-$spec->{t}" : '';
1813+ printf(" --%-${maxw}s %-4s %s\n", $long, $short, $spec->{d});
1814+ }
1815+ print <<USAGE;
1816+
1817+innotop is a MySQL and InnoDB transaction/status monitor, like 'top' for
1818+MySQL. It displays queries, InnoDB transactions, lock waits, deadlocks,
1819+foreign key errors, open tables, replication status, buffer information,
1820+row operations, logs, I/O operations, load graph, and more. You can
1821+monitor many servers at once with innotop.
1822+
1823+USAGE
1824+ exit(1);
1825+}
1826+
1827+# Meta-data (table definitions etc) {{{2
1828+# ###########################################################################
1829+
1830+# Expressions {{{3
1831+# Convenience so I can copy/paste these in several places...
1832+# ###########################################################################
1833+my %exprs = (
1834+ Host => q{my $host = host || hostname || ''; ($host) = $host =~ m/^((?:[\d.]+(?=:))|(?:[a-zA-Z]\w+))/; return $host || ''},
1835+ Port => q{my ($p) = host =~ m/:(.*)$/; return $p || 0},
1836+ OldVersions => q{dulint_to_int(IB_tx_trx_id_counter) - dulint_to_int(IB_tx_purge_done_for)},
1837+ MaxTxnTime => q/max(map{ $_->{active_secs} } @{ IB_tx_transactions }) || 0/,
1838+ NumTxns => q{scalar @{ IB_tx_transactions } },
1839+ DirtyBufs => q{ $cur->{IB_bp_pages_modified} / ($cur->{IB_bp_buf_pool_size} || 1) },
1840+ BufPoolFill => q{ $cur->{IB_bp_pages_total} / ($cur->{IB_bp_buf_pool_size} || 1) },
1841+ ServerLoad => q{ $cur->{Threads_connected}/(Questions||1)/Uptime_hires },
1842+ 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},
1843+ 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',
1844+ QcacheHitRatio => q{(Qcache_hits||0)/(((Com_select||0)+(Qcache_hits||0))||1)},
1845+);
1846+
1847+# ###########################################################################
1848+# Column definitions {{{3
1849+# Defines every column in every table. A named column has the following
1850+# properties:
1851+# * hdr Column header/title
1852+# * label Documentation for humans.
1853+# * num Whether it's numeric (for sorting).
1854+# * just Alignment; generated from num, user-overridable in tbl_meta
1855+# * minw, maxw Auto-generated, user-overridable.
1856+# Values from this hash are just copied to tbl_meta, which is where everything
1857+# else in the program should read from.
1858+# ###########################################################################
1859+
1860+my %columns = (
1861+ active_secs => { hdr => 'SecsActive', num => 1, label => 'Seconds transaction has been active', },
1862+ add_pool_alloc => { hdr => 'Add\'l Pool', num => 1, label => 'Additonal pool allocated' },
1863+ attempted_op => { hdr => 'Action', num => 0, label => 'The action that caused the error' },
1864+ awe_mem_alloc => { hdr => 'AWE Memory', num => 1, label => '[Windows] AWE memory allocated' },
1865+ binlog_cache_overflow => { hdr => 'Binlog Cache', num => 1, label => 'Transactions too big for binlog cache that went to disk' },
1866+ binlog_do_db => { hdr => 'Binlog Do DB', num => 0, label => 'binlog-do-db setting' },
1867+ binlog_ignore_db => { hdr => 'Binlog Ignore DB', num => 0, label => 'binlog-ignore-db setting' },
1868+ bps_in => { hdr => 'BpsIn', num => 1, label => 'Bytes per second received by the server', },
1869+ bps_out => { hdr => 'BpsOut', num => 1, label => 'Bytes per second sent by the server', },
1870+ buf_free => { hdr => 'Free Bufs', num => 1, label => 'Buffers free in the buffer pool' },
1871+ buf_pool_hit_rate => { hdr => 'Hit Rate', num => 0, label => 'Buffer pool hit rate' },
1872+ buf_pool_hits => { hdr => 'Hits', num => 1, label => 'Buffer pool hits' },
1873+ buf_pool_reads => { hdr => 'Reads', num => 1, label => 'Buffer pool reads' },
1874+ buf_pool_size => { hdr => 'Size', num => 1, label => 'Buffer pool size' },
1875+ bufs_in_node_heap => { hdr => 'Node Heap Bufs', num => 1, label => 'Buffers in buffer pool node heap' },
1876+ bytes_behind_master => { hdr => 'ByteLag', num => 1, label => 'Bytes the slave lags the master in binlog' },
1877+ cell_event_set => { hdr => 'Ending?', num => 1, label => 'Whether the cell event is set' },
1878+ cell_waiting => { hdr => 'Waiting?', num => 1, label => 'Whether the cell is waiting' },
1879+ child_db => { hdr => 'Child DB', num => 0, label => 'The database of the child table' },
1880+ child_index => { hdr => 'Child Index', num => 0, label => 'The index in the child table' },
1881+ child_table => { hdr => 'Child Table', num => 0, label => 'The child table' },
1882+ cmd => { hdr => 'Cmd', num => 0, label => 'Type of command being executed', },
1883+ cnt => { hdr => 'Cnt', num => 0, label => 'Count', agg => 'count', aggonly => 1 },
1884+ connect_retry => { hdr => 'Connect Retry', num => 1, label => 'Slave connect-retry timeout' },
1885+ cxn => { hdr => 'CXN', num => 0, label => 'Connection from which the data came', },
1886+ db => { hdr => 'DB', num => 0, label => 'Current database', },
1887+ dict_mem_alloc => { hdr => 'Dict Mem', num => 1, label => 'Dictionary memory allocated' },
1888+ dirty_bufs => { hdr => 'Dirty Buf', num => 1, label => 'Dirty buffer pool pages' },
1889+ dl_txn_num => { hdr => 'Num', num => 0, label => 'Deadlocked transaction number', },
1890+ event_set => { hdr => 'Evt Set?', num => 1, label => '[Win32] if a wait event is set', },
1891+ exec_master_log_pos => { hdr => 'Exec Master Log Pos', num => 1, label => 'Exec Master Log Position' },
1892+ fk_name => { hdr => 'Constraint', num => 0, label => 'The name of the FK constraint' },
1893+ free_list_len => { hdr => 'Free List Len', num => 1, label => 'Length of the free list' },
1894+ has_read_view => { hdr => 'Rd View', num => 1, label => 'Whether the transaction has a read view' },
1895+ hash_searches_s => { hdr => 'Hash/Sec', num => 1, label => 'Number of hash searches/sec' },
1896+ hash_table_size => { hdr => 'Size', num => 1, label => 'Number of non-hash searches/sec' },
1897+ heap_no => { hdr => 'Heap', num => 1, label => 'Heap number' },
1898+ heap_size => { hdr => 'Heap', num => 1, label => 'Heap size' },
1899+ history_list_len => { hdr => 'History', num => 1, label => 'History list length' },
1900+ host_and_domain => { hdr => 'Host', num => 0, label => 'Hostname/IP and domain' },
1901+ host_and_port => { hdr => 'Host/IP', num => 0, label => 'Hostname or IP address, and port number', },
1902+ hostname => { hdr => 'Host', num => 0, label => 'Hostname' },
1903+ index => { hdr => 'Index', num => 0, label => 'The index involved' },
1904+ index_ref => { hdr => 'Index Ref', num => 0, label => 'Index referenced' },
1905+ info => { hdr => 'Query', num => 0, label => 'Info or the current query', },
1906+ insert_intention => { hdr => 'Ins Intent', num => 1, label => 'Whether the thread was trying to insert' },
1907+ inserts => { hdr => 'Inserts', num => 1, label => 'Inserts' },
1908+ io_bytes_s => { hdr => 'Bytes/Sec', num => 1, label => 'Average I/O bytes/sec' },
1909+ io_flush_type => { hdr => 'Flush Type', num => 0, label => 'I/O Flush Type' },
1910+ io_fsyncs_s => { hdr => 'fsyncs/sec', num => 1, label => 'I/O fsyncs/sec' },
1911+ io_reads_s => { hdr => 'Reads/Sec', num => 1, label => 'Average I/O reads/sec' },
1912+ io_writes_s => { hdr => 'Writes/Sec', num => 1, label => 'Average I/O writes/sec' },
1913+ ip => { hdr => 'IP', num => 0, label => 'IP address' },
1914+ is_name_locked => { hdr => 'Locked', num => 1, label => 'Whether table is name locked', },
1915+ key_buffer_hit => { hdr => 'KCacheHit', num => 1, label => 'Key cache hit ratio', },
1916+ key_len => { hdr => 'Key Length', num => 1, label => 'Number of bytes used in the key' },
1917+ last_chkp => { hdr => 'Last Checkpoint', num => 0, label => 'Last log checkpoint' },
1918+ last_errno => { hdr => 'Last Errno', num => 1, label => 'Last error number' },
1919+ last_error => { hdr => 'Last Error', num => 0, label => 'Last error' },
1920+ last_s_file_name => { hdr => 'S-File', num => 0, label => 'Filename where last read locked' },
1921+ last_s_line => { hdr => 'S-Line', num => 1, label => 'Line where last read locked' },
1922+ last_x_file_name => { hdr => 'X-File', num => 0, label => 'Filename where last write locked' },
1923+ last_x_line => { hdr => 'X-Line', num => 1, label => 'Line where last write locked' },
1924+ last_pct => { hdr => 'Pct', num => 1, label => 'Last Percentage' },
1925+ last_total => { hdr => 'Last Total', num => 1, label => 'Last Total' },
1926+ last_value => { hdr => 'Last Incr', num => 1, label => 'Last Value' },
1927+ load => { hdr => 'Load', num => 1, label => 'Server load' },
1928+ lock_cfile_name => { hdr => 'Crtd File', num => 0, label => 'Filename where lock created' },
1929+ lock_cline => { hdr => 'Crtd Line', num => 1, label => 'Line where lock created' },
1930+ lock_mem_addr => { hdr => 'Addr', num => 0, label => 'The lock memory address' },
1931+ lock_mode => { hdr => 'Mode', num => 0, label => 'The lock mode' },
1932+ lock_structs => { hdr => 'LStrcts', num => 1, label => 'Number of lock structs' },
1933+ lock_type => { hdr => 'Type', num => 0, label => 'The lock type' },
1934+ lock_var => { hdr => 'Lck Var', num => 1, label => 'The lock variable' },
1935+ lock_wait_time => { hdr => 'Wait', num => 1, label => 'How long txn has waited for a lock' },
1936+ log_flushed_to => { hdr => 'Flushed To', num => 0, label => 'Log position flushed to' },
1937+ log_ios_done => { hdr => 'IO Done', num => 1, label => 'Log I/Os done' },
1938+ log_ios_s => { hdr => 'IO/Sec', num => 1, label => 'Average log I/Os per sec' },
1939+ log_seq_no => { hdr => 'Sequence No.', num => 0, label => 'Log sequence number' },
1940+ main_thread_id => { hdr => 'Main Thread ID', num => 1, label => 'Main thread ID' },
1941+ main_thread_proc_no => { hdr => 'Main Thread Proc', num => 1, label => 'Main thread process number' },
1942+ main_thread_state => { hdr => 'Main Thread State', num => 0, label => 'Main thread state' },
1943+ master_file => { hdr => 'File', num => 0, label => 'Master file' },
1944+ master_host => { hdr => 'Master', num => 0, label => 'Master server hostname' },
1945+ master_log_file => { hdr => 'Master Log File', num => 0, label => 'Master log file' },
1946+ master_port => { hdr => 'Master Port', num => 1, label => 'Master port' },
1947+ master_pos => { hdr => 'Position', num => 1, label => 'Master position' },
1948+ master_ssl_allowed => { hdr => 'Master SSL Allowed', num => 0, label => 'Master SSL Allowed' },
1949+ master_ssl_ca_file => { hdr => 'Master SSL CA File', num => 0, label => 'Master SSL Cert Auth File' },
1950+ master_ssl_ca_path => { hdr => 'Master SSL CA Path', num => 0, label => 'Master SSL Cert Auth Path' },
1951+ master_ssl_cert => { hdr => 'Master SSL Cert', num => 0, label => 'Master SSL Cert' },
1952+ master_ssl_cipher => { hdr => 'Master SSL Cipher', num => 0, label => 'Master SSL Cipher' },
1953+ master_ssl_key => { hdr => 'Master SSL Key', num => 0, label => 'Master SSL Key' },
1954+ master_user => { hdr => 'Master User', num => 0, label => 'Master username' },
1955+ max_txn => { hdr => 'MaxTxnTime', num => 1, label => 'MaxTxn' },
1956+ merged_recs => { hdr => 'Merged Recs', num => 1, label => 'Merged records' },
1957+ merges => { hdr => 'Merges', num => 1, label => 'Merges' },
1958+ mutex_os_waits => { hdr => 'Waits', num => 1, label => 'Mutex OS Waits' },
1959+ mutex_spin_rounds => { hdr => 'Rounds', num => 1, label => 'Mutex Spin Rounds' },
1960+ mutex_spin_waits => { hdr => 'Spins', num => 1, label => 'Mutex Spin Waits' },
1961+ mysql_thread_id => { hdr => 'ID', num => 1, label => 'MySQL connection (thread) ID', },
1962+ name => { hdr => 'Name', num => 0, label => 'Variable Name' },
1963+ n_bits => { hdr => '# Bits', num => 1, label => 'Number of bits' },
1964+ non_hash_searches_s => { hdr => 'Non-Hash/Sec', num => 1, label => 'Non-hash searches/sec' },
1965+ num_deletes => { hdr => 'Del', num => 1, label => 'Number of deletes' },
1966+ num_deletes_sec => { hdr => 'Del/Sec', num => 1, label => 'Number of deletes' },
1967+ num_inserts => { hdr => 'Ins', num => 1, label => 'Number of inserts' },
1968+ num_inserts_sec => { hdr => 'Ins/Sec', num => 1, label => 'Number of inserts' },
1969+ num_readers => { hdr => 'Readers', num => 1, label => 'Number of readers' },
1970+ num_reads => { hdr => 'Read', num => 1, label => 'Number of reads' },
1971+ num_reads_sec => { hdr => 'Read/Sec', num => 1, label => 'Number of reads' },
1972+ num_res_ext => { hdr => 'BTree Extents', num => 1, label => 'Number of extents reserved for B-Tree' },
1973+ num_rows => { hdr => 'Row Count', num => 1, label => 'Number of rows estimated to examine' },
1974+ num_times_open => { hdr => 'In Use', num => 1, label => '# times table is opened', },
1975+ num_txns => { hdr => 'Txns', num => 1, label => 'Number of transactions' },
1976+ num_updates => { hdr => 'Upd', num => 1, label => 'Number of updates' },
1977+ num_updates_sec => { hdr => 'Upd/Sec', num => 1, label => 'Number of updates' },
1978+ os_file_reads => { hdr => 'OS Reads', num => 1, label => 'OS file reads' },
1979+ os_file_writes => { hdr => 'OS Writes', num => 1, label => 'OS file writes' },
1980+ os_fsyncs => { hdr => 'OS fsyncs', num => 1, label => 'OS fsyncs' },
1981+ os_thread_id => { hdr => 'OS Thread', num => 1, label => 'The operating system thread ID' },
1982+ p_aio_writes => { hdr => 'Async Wrt', num => 1, label => 'Pending asynchronous I/O writes' },
1983+ p_buf_pool_flushes => { hdr => 'Buffer Pool Flushes', num => 1, label => 'Pending buffer pool flushes' },
1984+ p_ibuf_aio_reads => { hdr => 'IBuf Async Rds', num => 1, label => 'Pending insert buffer asynch I/O reads' },
1985+ p_log_flushes => { hdr => 'Log Flushes', num => 1, label => 'Pending log flushes' },
1986+ p_log_ios => { hdr => 'Log I/Os', num => 1, label => 'Pending log I/O operations' },
1987+ p_normal_aio_reads => { hdr => 'Async Rds', num => 1, label => 'Pending asynchronous I/O reads' },
1988+ p_preads => { hdr => 'preads', num => 1, label => 'Pending p-reads' },
1989+ p_pwrites => { hdr => 'pwrites', num => 1, label => 'Pending p-writes' },
1990+ p_sync_ios => { hdr => 'Sync I/Os', num => 1, label => 'Pending synchronous I/O operations' },
1991+ page_creates_sec => { hdr => 'Creates/Sec', num => 1, label => 'Page creates/sec' },
1992+ page_no => { hdr => 'Page', num => 1, label => 'Page number' },
1993+ page_reads_sec => { hdr => 'Reads/Sec', num => 1, label => 'Page reads per second' },
1994+ page_writes_sec => { hdr => 'Writes/Sec', num => 1, label => 'Page writes per second' },
1995+ pages_created => { hdr => 'Created', num => 1, label => 'Pages created' },
1996+ pages_modified => { hdr => 'Dirty Pages', num => 1, label => 'Pages modified (dirty)' },
1997+ pages_read => { hdr => 'Reads', num => 1, label => 'Pages read' },
1998+ pages_total => { hdr => 'Pages', num => 1, label => 'Pages total' },
1999+ pages_written => { hdr => 'Writes', num => 1, label => 'Pages written' },
2000+ parent_col => { hdr => 'Parent Column', num => 0, label => 'The referred column in the parent table', },
2001+ parent_db => { hdr => 'Parent DB', num => 0, label => 'The database of the parent table' },
2002+ parent_index => { hdr => 'Parent Index', num => 0, label => 'The referred index in the parent table' },
2003+ parent_table => { hdr => 'Parent Table', num => 0, label => 'The parent table' },
2004+ part_id => { hdr => 'Part ID', num => 1, label => 'Sub-part ID of the query' },
2005+ partitions => { hdr => 'Partitions', num => 0, label => 'Query partitions used' },
2006+ pct => { hdr => 'Pct', num => 1, label => 'Percentage' },
2007+ pending_chkp_writes => { hdr => 'Chkpt Writes', num => 1, label => 'Pending log checkpoint writes' },
2008+ pending_log_writes => { hdr => 'Log Writes', num => 1, label => 'Pending log writes' },
2009+ port => { hdr => 'Port', num => 1, label => 'Client port number', },
2010+ possible_keys => { hdr => 'Poss. Keys', num => 0, label => 'Possible keys' },
2011+ proc_no => { hdr => 'Proc', num => 1, label => 'Process number' },
2012+ q_cache_hit => { hdr => 'QCacheHit', num => 1, label => 'Query cache hit ratio', },
2013+ qps => { hdr => 'QPS', num => 1, label => 'How many queries/sec', },
2014+ queries_in_queue => { hdr => 'Queries Queued', num => 1, label => 'Queries in queue' },
2015+ queries_inside => { hdr => 'Queries Inside', num => 1, label => 'Queries inside InnoDB' },
2016+ query_id => { hdr => 'Query ID', num => 1, label => 'Query ID' },
2017+ query_status => { hdr => 'Query Status', num => 0, label => 'The query status' },
2018+ query_text => { hdr => 'Query Text', num => 0, label => 'The query text' },
2019+ questions => { hdr => 'Questions', num => 1, label => 'How many queries the server has gotten', },
2020+ read_master_log_pos => { hdr => 'Read Master Pos', num => 1, label => 'Read master log position' },
2021+ read_views_open => { hdr => 'Rd Views', num => 1, label => 'Number of read views open' },
2022+ reads_pending => { hdr => 'Pending Reads', num => 1, label => 'Reads pending' },
2023+ relay_log_file => { hdr => 'Relay File', num => 0, label => 'Relay log file' },
2024+ relay_log_pos => { hdr => 'Relay Pos', num => 1, label => 'Relay log position' },
2025+ relay_log_size => { hdr => 'Relay Size', num => 1, label => 'Relay log size' },
2026+ relay_master_log_file => { hdr => 'Relay Master File', num => 0, label => 'Relay master log file' },
2027+ replicate_do_db => { hdr => 'Do DB', num => 0, label => 'Replicate-do-db setting' },
2028+ replicate_do_table => { hdr => 'Do Table', num => 0, label => 'Replicate-do-table setting' },
2029+ replicate_ignore_db => { hdr => 'Ignore DB', num => 0, label => 'Replicate-ignore-db setting' },
2030+ replicate_ignore_table => { hdr => 'Ignore Table', num => 0, label => 'Replicate-do-table setting' },
2031+ replicate_wild_do_table => { hdr => 'Wild Do Table', num => 0, label => 'Replicate-wild-do-table setting' },
2032+ replicate_wild_ignore_table => { hdr => 'Wild Ignore Table', num => 0, label => 'Replicate-wild-ignore-table setting' },
2033+ request_type => { hdr => 'Type', num => 0, label => 'Type of lock the thread waits for' },
2034+ reservation_count => { hdr => 'ResCnt', num => 1, label => 'Reservation Count' },
2035+ row_locks => { hdr => 'RLocks', num => 1, label => 'Number of row locks' },
2036+ rw_excl_os_waits => { hdr => 'RW Waits', num => 1, label => 'R/W Excl. OS Waits' },
2037+ rw_excl_spins => { hdr => 'RW Spins', num => 1, label => 'R/W Excl. Spins' },
2038+ rw_shared_os_waits => { hdr => 'Sh Waits', num => 1, label => 'R/W Shared OS Waits' },
2039+ rw_shared_spins => { hdr => 'Sh Spins', num => 1, label => 'R/W Shared Spins' },
2040+ scan_type => { hdr => 'Type', num => 0, label => 'Scan type in chosen' },
2041+ seg_size => { hdr => 'Seg. Size', num => 1, label => 'Segment size' },
2042+ select_type => { hdr => 'Select Type', num => 0, label => 'Type of select used' },
2043+ signal_count => { hdr => 'Signals', num => 1, label => 'Signal Count' },
2044+ size => { hdr => 'Size', num => 1, label => 'Size of the tablespace' },
2045+ skip_counter => { hdr => 'Skip Counter', num => 1, label => 'Skip counter' },
2046+ slave_catchup_rate => { hdr => 'Catchup', num => 1, label => 'How fast the slave is catching up in the binlog' },
2047+ slave_io_running => { hdr => 'Slave-IO', num => 0, label => 'Whether the slave I/O thread is running' },
2048+ slave_io_state => { hdr => 'Slave IO State', num => 0, label => 'Slave I/O thread state' },
2049+ slave_open_temp_tables => { hdr => 'Temp', num => 1, label => 'Slave open temp tables' },
2050+ slave_sql_running => { hdr => 'Slave-SQL', num => 0, label => 'Whether the slave SQL thread is running' },
2051+ slow => { hdr => 'Slow', num => 1, label => 'How many slow queries', },
2052+ space_id => { hdr => 'Space', num => 1, label => 'Tablespace ID' },
2053+ special => { hdr => 'Special', num => 0, label => 'Special/Other info' },
2054+ state => { hdr => 'State', num => 0, label => 'Connection state', maxw => 18, },
2055+ tables_in_use => { hdr => 'Tbl Used', num => 1, label => 'Number of tables in use' },
2056+ tables_locked => { hdr => 'Tbl Lck', num => 1, label => 'Number of tables locked' },
2057+ tbl => { hdr => 'Table', num => 0, label => 'Table', },
2058+ thread => { hdr => 'Thread', num => 1, label => 'Thread number' },
2059+ thread_decl_inside => { hdr => 'Thread Inside', num => 0, label => 'What the thread is declared inside' },
2060+ thread_purpose => { hdr => 'Purpose', num => 0, label => "The thread's purpose" },
2061+ thread_status => { hdr => 'Thread Status', num => 0, label => 'The thread status' },
2062+ time => { hdr => 'Time', num => 1, label => 'Time since the last event', },
2063+ time_behind_master => { hdr => 'TimeLag', num => 1, label => 'Time slave lags master' },
2064+ timestring => { hdr => 'Timestring', num => 0, label => 'Time the event occurred' },
2065+ total => { hdr => 'Total', num => 1, label => 'Total' },
2066+ total_mem_alloc => { hdr => 'Memory', num => 1, label => 'Total memory allocated' },
2067+ truncates => { hdr => 'Trunc', num => 0, label => 'Whether the deadlock is truncating InnoDB status' },
2068+ txn_doesnt_see_ge => { hdr => "Txn Won't See", num => 0, label => 'Where txn read view is limited' },
2069+ txn_id => { hdr => 'ID', num => 0, label => 'Transaction ID' },
2070+ txn_sees_lt => { hdr => 'Txn Sees', num => 1, label => 'Where txn read view is limited' },
2071+ txn_status => { hdr => 'Txn Status', num => 0, label => 'Transaction status' },
2072+ txn_time_remain => { hdr => 'Remaining', num => 1, label => 'Time until txn rollback/commit completes' },
2073+ undo_log_entries => { hdr => 'Undo', num => 1, label => 'Number of undo log entries' },
2074+ undo_for => { hdr => 'Undo', num => 0, label => 'Undo for' },
2075+ until_condition => { hdr => 'Until Condition', num => 0, label => 'Slave until condition' },
2076+ until_log_file => { hdr => 'Until Log File', num => 0, label => 'Slave until log file' },
2077+ until_log_pos => { hdr => 'Until Log Pos', num => 1, label => 'Slave until log position' },
2078+ used_cells => { hdr => 'Cells Used', num => 1, label => 'Number of cells used' },
2079+ used_bufs => { hdr => 'Used Bufs', num => 1, label => 'Number of buffer pool pages used' },
2080+ user => { hdr => 'User', num => 0, label => 'Database username', },
2081+ value => { hdr => 'Value', num => 1, label => 'Value' },
2082+ versions => { hdr => 'Versions', num => 1, label => 'Number of InnoDB MVCC versions unpurged' },
2083+ victim => { hdr => 'Victim', num => 0, label => 'Whether this txn was the deadlock victim' },
2084+ wait_array_size => { hdr => 'Wait Array Size', num => 1, label => 'Wait Array Size' },
2085+ wait_status => { hdr => 'Lock Status', num => 0, label => 'Status of txn locks' },
2086+ waited_at_filename => { hdr => 'File', num => 0, label => 'Filename at which thread waits' },
2087+ waited_at_line => { hdr => 'Line', num => 1, label => 'Line at which thread waits' },
2088+ waiters_flag => { hdr => 'Waiters', num => 1, label => 'Waiters Flag' },
2089+ waiting => { hdr => 'Waiting', num => 1, label => 'Whether lock is being waited for' },
2090+ when => { hdr => 'When', num => 0, label => 'Time scale' },
2091+ writer_lock_mode => { hdr => 'Wrtr Lck Mode', num => 0, label => 'Writer lock mode' },
2092+ writer_thread => { hdr => 'Wrtr Thread', num => 1, label => 'Writer thread ID' },
2093+ writes_pending => { hdr => 'Writes', num => 1, label => 'Number of writes pending' },
2094+ writes_pending_flush_list => { hdr => 'Flush List Writes', num => 1, label => 'Number of flush list writes pending' },
2095+ writes_pending_lru => { hdr => 'LRU Writes', num => 1, label => 'Number of LRU writes pending' },
2096+ writes_pending_single_page => { hdr => '1-Page Writes', num => 1, label => 'Number of 1-page writes pending' },
2097+);
2098+
2099+# Apply a default property or three. By default, columns are not width-constrained,
2100+# aligned left, and sorted alphabetically, not numerically.
2101+foreach my $col ( values %columns ) {
2102+ map { $col->{$_} ||= 0 } qw(num minw maxw);
2103+ $col->{just} = $col->{num} ? '' : '-';
2104+}
2105+
2106+# Filters {{{3
2107+# This hash defines every filter that can be applied to a table. These
2108+# become part of tbl_meta as well. Each filter is just an expression that
2109+# returns true or false.
2110+# Properties of each entry:
2111+# * func: the subroutine
2112+# * name: the name, repeated
2113+# * user: whether it's a user-defined filter (saved in config)
2114+# * text: text of the subroutine
2115+# * note: explanation
2116+my %filters = ();
2117+
2118+# These are pre-processed to live in %filters above, by compiling them.
2119+my %builtin_filters = (
2120+ hide_self => {
2121+ text => <<' END',
2122+ return ( !$set->{info} || $set->{info} ne 'SHOW FULL PROCESSLIST' )
2123+ && ( !$set->{query_text} || $set->{query_text} !~ m/INNODB STATUS$/ );
2124+ END
2125+ note => 'Removes the innotop processes from the list',
2126+ tbls => [qw(innodb_transactions processlist)],
2127+ },
2128+ hide_inactive => {
2129+ text => <<' END',
2130+ return ( !defined($set->{txn_status}) || $set->{txn_status} ne 'not started' )
2131+ && ( !defined($set->{cmd}) || $set->{cmd} !~ m/Sleep|Binlog Dump/ )
2132+ && ( !defined($set->{info}) || $set->{info} =~ m/\S/ );
2133+ END
2134+ note => 'Removes processes which are not doing anything',
2135+ tbls => [qw(innodb_transactions processlist)],
2136+ },
2137+ hide_slave_io => {
2138+ text => <<' END',
2139+ return !$set->{state} || $set->{state} !~ m/^(?:Waiting for master|Has read all relay)/;
2140+ END
2141+ note => 'Removes slave I/O threads from the list',
2142+ tbls => [qw(processlist slave_io_status)],
2143+ },
2144+ table_is_open => {
2145+ text => <<' END',
2146+ return $set->{num_times_open} + $set->{is_name_locked};
2147+ END
2148+ note => 'Removes tables that are not in use or locked',
2149+ tbls => [qw(open_tables)],
2150+ },
2151+ cxn_is_master => {
2152+ text => <<' END',
2153+ return $set->{master_file} ? 1 : 0;
2154+ END
2155+ note => 'Removes servers that are not masters',
2156+ tbls => [qw(master_status)],
2157+ },
2158+ cxn_is_slave => {
2159+ text => <<' END',
2160+ return $set->{master_host} ? 1 : 0;
2161+ END
2162+ note => 'Removes servers that are not slaves',
2163+ tbls => [qw(slave_io_status slave_sql_status)],
2164+ },
2165+ thd_is_not_waiting => {
2166+ text => <<' END',
2167+ return $set->{thread_status} !~ m#waiting for i/o request#;
2168+ END
2169+ note => 'Removes idle I/O threads',
2170+ tbls => [qw(io_threads)],
2171+ },
2172+);
2173+foreach my $key ( keys %builtin_filters ) {
2174+ my ( $sub, $err ) = compile_filter($builtin_filters{$key}->{text});
2175+ $filters{$key} = {
2176+ func => $sub,
2177+ text => $builtin_filters{$key}->{text},
2178+ user => 0,
2179+ name => $key, # useful for later
2180+ note => $builtin_filters{$key}->{note},
2181+ tbls => $builtin_filters{$key}->{tbls},
2182+ }
2183+}
2184+
2185+# Variable sets {{{3
2186+# Sets (arrayrefs) of variables that are used in S mode. They are read/written to
2187+# the config file.
2188+my %var_sets = (
2189+ general => {
2190+ text => join(
2191+ ', ',
2192+ 'set_precision(Questions/Uptime_hires) as QPS',
2193+ 'set_precision(Com_commit/Uptime_hires) as Commit_PS',
2194+ 'set_precision((Com_rollback||0)/(Com_commit||1)) as Rollback_Commit',
2195+ 'set_precision(('
2196+ . join('+', map { "($_||0)" }
2197+ qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
2198+ Com_replace_select Com_select Com_update Com_update_multi))
2199+ . ')/(Com_commit||1)) as Write_Commit',
2200+ 'set_precision((Com_select+(Qcache_hits||0))/(('
2201+ . join('+', map { "($_||0)" }
2202+ qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
2203+ Com_replace_select Com_select Com_update Com_update_multi))
2204+ . ')||1)) as R_W_Ratio',
2205+ 'set_precision(Opened_tables/Uptime_hires) as Opens_PS',
2206+ 'percent($cur->{Open_tables}/($cur->{table_cache})) as Table_Cache_Used',
2207+ 'set_precision(Threads_created/Uptime_hires) as Threads_PS',
2208+ 'percent($cur->{Threads_cached}/($cur->{thread_cache_size}||1)) as Thread_Cache_Used',
2209+ 'percent($cur->{Max_used_connections}/($cur->{max_connections}||1)) as CXN_Used_Ever',
2210+ 'percent($cur->{Threads_connected}/($cur->{max_connections}||1)) as CXN_Used_Now',
2211+ ),
2212+ },
2213+ commands => {
2214+ text => join(
2215+ ', ',
2216+ qw(Uptime Questions Com_delete Com_delete_multi Com_insert
2217+ Com_insert_select Com_replace Com_replace_select Com_select Com_update
2218+ Com_update_multi)
2219+ ),
2220+ },
2221+ query_status => {
2222+ text => join(
2223+ ',',
2224+ qw( Uptime Select_full_join Select_full_range_join Select_range
2225+ Select_range_check Select_scan Slow_queries Sort_merge_passes
2226+ Sort_range Sort_rows Sort_scan)
2227+ ),
2228+ },
2229+ innodb => {
2230+ text => join(
2231+ ',',
2232+ qw( Uptime Innodb_row_lock_current_waits Innodb_row_lock_time
2233+ Innodb_row_lock_time_avg Innodb_row_lock_time_max Innodb_row_lock_waits
2234+ Innodb_rows_deleted Innodb_rows_inserted Innodb_rows_read
2235+ Innodb_rows_updated)
2236+ ),
2237+ },
2238+ txn => {
2239+ text => join(
2240+ ',',
2241+ qw( Uptime Com_begin Com_commit Com_rollback Com_savepoint
2242+ Com_xa_commit Com_xa_end Com_xa_prepare Com_xa_recover Com_xa_rollback
2243+ Com_xa_start)
2244+ ),
2245+ },
2246+ key_cache => {
2247+ text => join(
2248+ ',',
2249+ qw( Uptime Key_blocks_not_flushed Key_blocks_unused Key_blocks_used
2250+ Key_read_requests Key_reads Key_write_requests Key_writes )
2251+ ),
2252+ },
2253+ query_cache => {
2254+ text => join(
2255+ ',',
2256+ "percent($exprs{QcacheHitRatio}) as Hit_Pct",
2257+ 'set_precision((Qcache_hits||0)/(Qcache_inserts||1)) as Hit_Ins',
2258+ 'set_precision((Qcache_lowmem_prunes||0)/Uptime_hires) as Lowmem_Prunes_sec',
2259+ 'percent(1-((Qcache_free_blocks||0)/(Qcache_total_blocks||1))) as Blocks_used',
2260+ qw( Qcache_free_blocks Qcache_free_memory Qcache_not_cached Qcache_queries_in_cache)
2261+ ),
2262+ },
2263+ handler => {
2264+ text => join(
2265+ ',',
2266+ qw( Uptime Handler_read_key Handler_read_first Handler_read_next
2267+ Handler_read_prev Handler_read_rnd Handler_read_rnd_next Handler_delete
2268+ Handler_update Handler_write)
2269+ ),
2270+ },
2271+ cxns_files_threads => {
2272+ text => join(
2273+ ',',
2274+ qw( Uptime Aborted_clients Aborted_connects Bytes_received Bytes_sent
2275+ Compression Connections Created_tmp_disk_tables Created_tmp_files
2276+ Created_tmp_tables Max_used_connections Open_files Open_streams
2277+ Open_tables Opened_tables Table_locks_immediate Table_locks_waited
2278+ Threads_cached Threads_connected Threads_created Threads_running)
2279+ ),
2280+ },
2281+ prep_stmt => {
2282+ text => join(
2283+ ',',
2284+ qw( Uptime Com_dealloc_sql Com_execute_sql Com_prepare_sql Com_reset
2285+ Com_stmt_close Com_stmt_execute Com_stmt_fetch Com_stmt_prepare
2286+ Com_stmt_reset Com_stmt_send_long_data )
2287+ ),
2288+ },
2289+ innodb_health => {
2290+ text => join(
2291+ ',',
2292+ "$exprs{OldVersions} as OldVersions",
2293+ qw(IB_sm_mutex_spin_waits IB_sm_mutex_spin_rounds IB_sm_mutex_os_waits),
2294+ "$exprs{NumTxns} as NumTxns",
2295+ "$exprs{MaxTxnTime} as MaxTxnTime",
2296+ qw(IB_ro_queries_inside IB_ro_queries_in_queue),
2297+ "set_precision($exprs{DirtyBufs} * 100) as dirty_bufs",
2298+ "set_precision($exprs{BufPoolFill} * 100) as buf_fill",
2299+ qw(IB_bp_pages_total IB_bp_pages_read IB_bp_pages_written IB_bp_pages_created)
2300+ ),
2301+ },
2302+ innodb_health2 => {
2303+ text => join(
2304+ ', ',
2305+ 'percent(1-((Innodb_buffer_pool_pages_free||0)/($cur->{Innodb_buffer_pool_pages_total}||1))) as BP_page_cache_usage',
2306+ 'percent(1-((Innodb_buffer_pool_reads||0)/(Innodb_buffer_pool_read_requests||1))) as BP_cache_hit_ratio',
2307+ 'Innodb_buffer_pool_wait_free',
2308+ 'Innodb_log_waits',
2309+ ),
2310+ },
2311+ slow_queries => {
2312+ text => join(
2313+ ', ',
2314+ 'set_precision(Slow_queries/Uptime_hires) as Slow_PS',
2315+ 'set_precision(Select_full_join/Uptime_hires) as Full_Join_PS',
2316+ 'percent(Select_full_join/(Com_select||1)) as Full_Join_Ratio',
2317+ ),
2318+ },
2319+);
2320+
2321+# Server sets {{{3
2322+# Defines sets of servers between which the user can quickly switch.
2323+my %server_groups;
2324+
2325+# Connections {{{3
2326+# This hash defines server connections. Each connection is a string that can be passed to
2327+# the DBI connection. These are saved in the connections section in the config file.
2328+my %connections;
2329+# Defines the parts of connections.
2330+my @conn_parts = qw(user have_user pass have_pass dsn savepass dl_table);
2331+
2332+# Graph widths {{{3
2333+# This hash defines the max values seen for various status/variable values, for graphing.
2334+# These are stored in their own section in the config file. These are just initial values:
2335+my %mvs = (
2336+ Com_select => 50,
2337+ Com_insert => 50,
2338+ Com_update => 50,
2339+ Com_delete => 50,
2340+ Questions => 100,
2341+);
2342+
2343+# ###########################################################################
2344+# Valid Term::ANSIColor color strings.
2345+# ###########################################################################
2346+my %ansicolors = map { $_ => 1 }
2347+ qw( black blink blue bold clear concealed cyan dark green magenta on_black
2348+ on_blue on_cyan on_green on_magenta on_red on_white on_yellow red reset
2349+ reverse underline underscore white yellow);
2350+
2351+# ###########################################################################
2352+# Valid comparison operators for color rules
2353+# ###########################################################################
2354+my %comp_ops = (
2355+ '==' => 'Numeric equality',
2356+ '>' => 'Numeric greater-than',
2357+ '<' => 'Numeric less-than',
2358+ '>=' => 'Numeric greater-than/equal',
2359+ '<=' => 'Numeric less-than/equal',
2360+ '!=' => 'Numeric not-equal',
2361+ 'eq' => 'String equality',
2362+ 'gt' => 'String greater-than',
2363+ 'lt' => 'String less-than',
2364+ 'ge' => 'String greater-than/equal',
2365+ 'le' => 'String less-than/equal',
2366+ 'ne' => 'String not-equal',
2367+ '=~' => 'Pattern match',
2368+ '!~' => 'Negated pattern match',
2369+);
2370+
2371+# ###########################################################################
2372+# Valid aggregate functions.
2373+# ###########################################################################
2374+my %agg_funcs = (
2375+ first => sub {
2376+ return $_[0]
2377+ },
2378+ count => sub {
2379+ return 0 + @_;
2380+ },
2381+ avg => sub {
2382+ my @args = grep { defined $_ } @_;
2383+ return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1);
2384+ },
2385+ sum => \&sum,
2386+);
2387+
2388+# ###########################################################################
2389+# Valid functions for transformations.
2390+# ###########################################################################
2391+my %trans_funcs = (
2392+ shorten => \&shorten,
2393+ secs_to_time => \&secs_to_time,
2394+ no_ctrl_char => \&no_ctrl_char,
2395+ percent => \&percent,
2396+ commify => \&commify,
2397+ dulint_to_int => \&dulint_to_int,
2398+ set_precision => \&set_precision,
2399+);
2400+
2401+# Table definitions {{{3
2402+# This hash defines every table that can get displayed in every mode. Each
2403+# table specifies columns and column data sources. The column is
2404+# defined by the %columns hash.
2405+#
2406+# Example: foo => { src => 'bar' } means the foo column (look at
2407+# $columns{foo} for its definition) gets its data from the 'bar' element of
2408+# the current data set, whatever that is.
2409+#
2410+# These columns are post-processed after being defined, because they get stuff
2411+# from %columns. After all the config is loaded for columns, there's more
2412+# post-processing too; the subroutines compiled from src get added to
2413+# the hash elements for extract_values to use.
2414+# ###########################################################################
2415+
2416+my %tbl_meta = (
2417+ adaptive_hash_index => {
2418+ capt => 'Adaptive Hash Index',
2419+ cust => {},
2420+ cols => {
2421+ cxn => { src => 'cxn' },
2422+ hash_table_size => { src => 'IB_ib_hash_table_size', trans => [qw(shorten)], },
2423+ used_cells => { src => 'IB_ib_used_cells' },
2424+ bufs_in_node_heap => { src => 'IB_ib_bufs_in_node_heap' },
2425+ hash_searches_s => { src => 'IB_ib_hash_searches_s' },
2426+ non_hash_searches_s => { src => 'IB_ib_non_hash_searches_s' },
2427+ },
2428+ visible => [ qw(cxn hash_table_size used_cells bufs_in_node_heap hash_searches_s non_hash_searches_s) ],
2429+ filters => [],
2430+ sort_cols => 'cxn',
2431+ sort_dir => '1',
2432+ innodb => 'ib',
2433+ group_by => [],
2434+ aggregate => 0,
2435+ },
2436+ buffer_pool => {
2437+ capt => 'Buffer Pool',
2438+ cust => {},
2439+ cols => {
2440+ cxn => { src => 'cxn' },
2441+ total_mem_alloc => { src => 'IB_bp_total_mem_alloc', trans => [qw(shorten)], },
2442+ awe_mem_alloc => { src => 'IB_bp_awe_mem_alloc', trans => [qw(shorten)], },
2443+ add_pool_alloc => { src => 'IB_bp_add_pool_alloc', trans => [qw(shorten)], },
2444+ buf_pool_size => { src => 'IB_bp_buf_pool_size', trans => [qw(shorten)], },
2445+ buf_free => { src => 'IB_bp_buf_free' },
2446+ buf_pool_hit_rate => { src => 'IB_bp_buf_pool_hit_rate' },
2447+ buf_pool_reads => { src => 'IB_bp_buf_pool_reads' },
2448+ buf_pool_hits => { src => 'IB_bp_buf_pool_hits' },
2449+ dict_mem_alloc => { src => 'IB_bp_dict_mem_alloc' },
2450+ pages_total => { src => 'IB_bp_pages_total' },
2451+ pages_modified => { src => 'IB_bp_pages_modified' },
2452+ reads_pending => { src => 'IB_bp_reads_pending' },
2453+ writes_pending => { src => 'IB_bp_writes_pending' },
2454+ writes_pending_lru => { src => 'IB_bp_writes_pending_lru' },
2455+ writes_pending_flush_list => { src => 'IB_bp_writes_pending_flush_list' },
2456+ writes_pending_single_page => { src => 'IB_bp_writes_pending_single_page' },
2457+ page_creates_sec => { src => 'IB_bp_page_creates_sec' },
2458+ page_reads_sec => { src => 'IB_bp_page_reads_sec' },
2459+ page_writes_sec => { src => 'IB_bp_page_writes_sec' },
2460+ pages_created => { src => 'IB_bp_pages_created' },
2461+ pages_read => { src => 'IB_bp_pages_read' },
2462+ pages_written => { src => 'IB_bp_pages_written' },
2463+ },
2464+ visible => [ qw(cxn buf_pool_size buf_free pages_total pages_modified buf_pool_hit_rate total_mem_alloc add_pool_alloc)],
2465+ filters => [],
2466+ sort_cols => 'cxn',
2467+ sort_dir => '1',
2468+ innodb => 'bp',
2469+ group_by => [],
2470+ aggregate => 0,
2471+ },
2472+ # TODO: a new step in set_to_tbl: join result to itself, grouped?
2473+ # TODO: this would also enable pulling Q and T data together.
2474+ # TODO: using a SQL-ish language would also allow pivots to be easier -- treat the pivoted data as a view and SELECT from it.
2475+ cmd_summary => {
2476+ capt => 'Command Summary',
2477+ cust => {},
2478+ cols => {
2479+ name => { src => 'name' },
2480+ total => { src => 'total' },
2481+ value => { src => 'value', agg => 'sum'},
2482+ pct => { src => 'value/total', trans => [qw(percent)] },
2483+ last_total => { src => 'last_total' },
2484+ last_value => { src => 'last_value', agg => 'sum'},
2485+ last_pct => { src => 'last_value/last_total', trans => [qw(percent)] },
2486+ },
2487+ visible => [qw(name value pct last_value last_pct)],
2488+ filters => [qw()],
2489+ sort_cols => '-value',
2490+ sort_dir => '1',
2491+ innodb => '',
2492+ group_by => [qw(name)],
2493+ aggregate => 1,
2494+ },
2495+ deadlock_locks => {
2496+ capt => 'Deadlock Locks',
2497+ cust => {},
2498+ cols => {
2499+ cxn => { src => 'cxn' },
2500+ mysql_thread_id => { src => 'mysql_thread_id' },
2501+ dl_txn_num => { src => 'dl_txn_num' },
2502+ lock_type => { src => 'lock_type' },
2503+ space_id => { src => 'space_id' },
2504+ page_no => { src => 'page_no' },
2505+ heap_no => { src => 'heap_no' },
2506+ n_bits => { src => 'n_bits' },
2507+ index => { src => 'index' },
2508+ db => { src => 'db' },
2509+ tbl => { src => 'table' },
2510+ lock_mode => { src => 'lock_mode' },
2511+ special => { src => 'special' },
2512+ insert_intention => { src => 'insert_intention' },
2513+ waiting => { src => 'waiting' },
2514+ },
2515+ visible => [ qw(cxn mysql_thread_id waiting lock_mode db tbl index special insert_intention)],
2516+ filters => [],
2517+ sort_cols => 'cxn mysql_thread_id',
2518+ sort_dir => '1',
2519+ innodb => 'dl',
2520+ group_by => [],
2521+ aggregate => 0,
2522+ },
2523+ deadlock_transactions => {
2524+ capt => 'Deadlock Transactions',
2525+ cust => {},
2526+ cols => {
2527+ cxn => { src => 'cxn' },
2528+ active_secs => { src => 'active_secs' },
2529+ dl_txn_num => { src => 'dl_txn_num' },
2530+ has_read_view => { src => 'has_read_view' },
2531+ heap_size => { src => 'heap_size' },
2532+ host_and_domain => { src => 'hostname' },
2533+ hostname => { src => $exprs{Host} },
2534+ ip => { src => 'ip' },
2535+ lock_structs => { src => 'lock_structs' },
2536+ lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
2537+ mysql_thread_id => { src => 'mysql_thread_id' },
2538+ os_thread_id => { src => 'os_thread_id' },
2539+ proc_no => { src => 'proc_no' },
2540+ query_id => { src => 'query_id' },
2541+ query_status => { src => 'query_status' },
2542+ query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
2543+ row_locks => { src => 'row_locks' },
2544+ tables_in_use => { src => 'tables_in_use' },
2545+ tables_locked => { src => 'tables_locked' },
2546+ thread_decl_inside => { src => 'thread_decl_inside' },
2547+ thread_status => { src => 'thread_status' },
2548+ 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ] },
2549+ timestring => { src => 'timestring' },
2550+ txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' },
2551+ txn_id => { src => 'txn_id' },
2552+ txn_sees_lt => { src => 'txn_sees_lt' },
2553+ txn_status => { src => 'txn_status' },
2554+ truncates => { src => 'truncates' },
2555+ undo_log_entries => { src => 'undo_log_entries' },
2556+ user => { src => 'user' },
2557+ victim => { src => 'victim' },
2558+ wait_status => { src => 'lock_wait_status' },
2559+ },
2560+ visible => [ qw(cxn mysql_thread_id timestring user hostname victim time undo_log_entries lock_structs query_text)],
2561+ filters => [],
2562+ sort_cols => 'cxn mysql_thread_id',
2563+ sort_dir => '1',
2564+ innodb => 'dl',
2565+ group_by => [],
2566+ aggregate => 0,
2567+ },
2568+ explain => {
2569+ capt => 'EXPLAIN Results',
2570+ cust => {},
2571+ cols => {
2572+ part_id => { src => 'id' },
2573+ select_type => { src => 'select_type' },
2574+ tbl => { src => 'table' },
2575+ partitions => { src => 'partitions' },
2576+ scan_type => { src => 'type' },
2577+ possible_keys => { src => 'possible_keys' },
2578+ index => { src => 'key' },
2579+ key_len => { src => 'key_len' },
2580+ index_ref => { src => 'ref' },
2581+ num_rows => { src => 'rows' },
2582+ special => { src => 'extra' },
2583+ },
2584+ visible => [ qw(select_type tbl partitions scan_type possible_keys index key_len index_ref num_rows special)],
2585+ filters => [],
2586+ sort_cols => '',
2587+ sort_dir => '1',
2588+ innodb => '',
2589+ group_by => [],
2590+ aggregate => 0,
2591+ },
2592+ file_io_misc => {
2593+ capt => 'File I/O Misc',
2594+ cust => {},
2595+ cols => {
2596+ cxn => { src => 'cxn' },
2597+ io_bytes_s => { src => 'IB_io_avg_bytes_s' },
2598+ io_flush_type => { src => 'IB_io_flush_type' },
2599+ io_fsyncs_s => { src => 'IB_io_fsyncs_s' },
2600+ io_reads_s => { src => 'IB_io_reads_s' },
2601+ io_writes_s => { src => 'IB_io_writes_s' },
2602+ os_file_reads => { src => 'IB_io_os_file_reads' },
2603+ os_file_writes => { src => 'IB_io_os_file_writes' },
2604+ os_fsyncs => { src => 'IB_io_os_fsyncs' },
2605+ },
2606+ visible => [ qw(cxn os_file_reads os_file_writes os_fsyncs io_reads_s io_writes_s io_bytes_s)],
2607+ filters => [],
2608+ sort_cols => 'cxn',
2609+ sort_dir => '1',
2610+ innodb => 'io',
2611+ group_by => [],
2612+ aggregate => 0,
2613+ },
2614+ fk_error => {
2615+ capt => 'Foreign Key Error Info',
2616+ cust => {},
2617+ cols => {
2618+ timestring => { src => 'IB_fk_timestring' },
2619+ child_db => { src => 'IB_fk_child_db' },
2620+ child_table => { src => 'IB_fk_child_table' },
2621+ child_index => { src => 'IB_fk_child_index' },
2622+ fk_name => { src => 'IB_fk_fk_name' },
2623+ parent_db => { src => 'IB_fk_parent_db' },
2624+ parent_table => { src => 'IB_fk_parent_table' },
2625+ parent_col => { src => 'IB_fk_parent_col' },
2626+ parent_index => { src => 'IB_fk_parent_index' },
2627+ attempted_op => { src => 'IB_fk_attempted_op' },
2628+ },
2629+ visible => [ qw(timestring child_db child_table child_index parent_db parent_table parent_col parent_index fk_name attempted_op)],
2630+ filters => [],
2631+ sort_cols => '',
2632+ sort_dir => '1',
2633+ innodb => 'fk',
2634+ group_by => [],
2635+ aggregate => 0,
2636+ },
2637+ insert_buffers => {
2638+ capt => 'Insert Buffers',
2639+ cust => {},
2640+ cols => {
2641+ cxn => { src => 'cxn' },
2642+ inserts => { src => 'IB_ib_inserts' },
2643+ merged_recs => { src => 'IB_ib_merged_recs' },
2644+ merges => { src => 'IB_ib_merges' },
2645+ size => { src => 'IB_ib_size' },
2646+ free_list_len => { src => 'IB_ib_free_list_len' },
2647+ seg_size => { src => 'IB_ib_seg_size' },
2648+ },
2649+ visible => [ qw(cxn inserts merged_recs merges size free_list_len seg_size)],
2650+ filters => [],
2651+ sort_cols => 'cxn',
2652+ sort_dir => '1',
2653+ innodb => 'ib',
2654+ group_by => [],
2655+ aggregate => 0,
2656+ },
2657+ innodb_locks => {
2658+ capt => 'InnoDB Locks',
2659+ cust => {},
2660+ cols => {
2661+ cxn => { src => 'cxn' },
2662+ db => { src => 'db' },
2663+ index => { src => 'index' },
2664+ insert_intention => { src => 'insert_intention' },
2665+ lock_mode => { src => 'lock_mode' },
2666+ lock_type => { src => 'lock_type' },
2667+ lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
2668+ mysql_thread_id => { src => 'mysql_thread_id' },
2669+ n_bits => { src => 'n_bits' },
2670+ page_no => { src => 'page_no' },
2671+ space_id => { src => 'space_id' },
2672+ special => { src => 'special' },
2673+ tbl => { src => 'table' },
2674+ 'time' => { src => 'active_secs', hdr => 'Active', trans => [ qw(secs_to_time) ] },
2675+ txn_id => { src => 'txn_id' },
2676+ waiting => { src => 'waiting' },
2677+ },
2678+ visible => [ qw(cxn mysql_thread_id lock_type waiting lock_wait_time time lock_mode db tbl index insert_intention special)],
2679+ filters => [],
2680+ sort_cols => 'cxn -lock_wait_time',
2681+ sort_dir => '1',
2682+ innodb => 'tx',
2683+ colors => [
2684+ { col => 'lock_wait_time', op => '>', arg => 60, color => 'red' },
2685+ { col => 'lock_wait_time', op => '>', arg => 30, color => 'yellow' },
2686+ { col => 'lock_wait_time', op => '>', arg => 10, color => 'green' },
2687+ ],
2688+ group_by => [],
2689+ aggregate => 0,
2690+ },
2691+ innodb_transactions => {
2692+ capt => 'InnoDB Transactions',
2693+ cust => {},
2694+ cols => {
2695+ cxn => { src => 'cxn' },
2696+ active_secs => { src => 'active_secs' },
2697+ has_read_view => { src => 'has_read_view' },
2698+ heap_size => { src => 'heap_size' },
2699+ hostname => { src => $exprs{Host} },
2700+ ip => { src => 'ip' },
2701+ wait_status => { src => 'lock_wait_status' },
2702+ lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
2703+ lock_structs => { src => 'lock_structs' },
2704+ mysql_thread_id => { src => 'mysql_thread_id' },
2705+ os_thread_id => { src => 'os_thread_id' },
2706+ proc_no => { src => 'proc_no' },
2707+ query_id => { src => 'query_id' },
2708+ query_status => { src => 'query_status' },
2709+ query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
2710+ txn_time_remain => { src => $exprs{TxnTimeRemain}, trans => [ qw(secs_to_time) ] },
2711+ row_locks => { src => 'row_locks' },
2712+ tables_in_use => { src => 'tables_in_use' },
2713+ tables_locked => { src => 'tables_locked' },
2714+ thread_decl_inside => { src => 'thread_decl_inside' },
2715+ thread_status => { src => 'thread_status' },
2716+ 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ], agg => 'sum' },
2717+ txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' },
2718+ txn_id => { src => 'txn_id' },
2719+ txn_sees_lt => { src => 'txn_sees_lt' },
2720+ txn_status => { src => 'txn_status', minw => 10, maxw => 10 },
2721+ undo_log_entries => { src => 'undo_log_entries' },
2722+ user => { src => 'user', maxw => 10 },
2723+ cnt => { src => 'mysql_thread_id', minw => 0 },
2724+ },
2725+ visible => [ qw(cxn cnt mysql_thread_id user hostname txn_status time undo_log_entries query_text)],
2726+ filters => [ qw( hide_self hide_inactive ) ],
2727+ sort_cols => '-active_secs txn_status cxn mysql_thread_id',
2728+ sort_dir => '1',
2729+ innodb => 'tx',
2730+ hide_caption => 1,
2731+ colors => [
2732+ { col => 'wait_status', op => 'eq', arg => 'LOCK WAIT', color => 'black on_red' },
2733+ { col => 'time', op => '>', arg => 600, color => 'red' },
2734+ { col => 'time', op => '>', arg => 300, color => 'yellow' },
2735+ { col => 'time', op => '>', arg => 60, color => 'green' },
2736+ { col => 'time', op => '>', arg => 30, color => 'cyan' },
2737+ { col => 'txn_status', op => 'eq', arg => 'not started', color => 'white' },
2738+ ],
2739+ group_by => [ qw(cxn txn_status) ],
2740+ aggregate => 0,
2741+ },
2742+ io_threads => {
2743+ capt => 'I/O Threads',
2744+ cust => {},
2745+ cols => {
2746+ cxn => { src => 'cxn' },
2747+ thread => { src => 'thread' },
2748+ thread_purpose => { src => 'purpose' },
2749+ event_set => { src => 'event_set' },
2750+ thread_status => { src => 'state' },
2751+ },
2752+ visible => [ qw(cxn thread thread_purpose thread_status)],
2753+ filters => [ qw() ],
2754+ sort_cols => 'cxn thread',
2755+ sort_dir => '1',
2756+ innodb => 'io',
2757+ group_by => [],
2758+ aggregate => 0,
2759+ },
2760+ log_statistics => {
2761+ capt => 'Log Statistics',
2762+ cust => {},
2763+ cols => {
2764+ cxn => { src => 'cxn' },
2765+ last_chkp => { src => 'IB_lg_last_chkp' },
2766+ log_flushed_to => { src => 'IB_lg_log_flushed_to' },
2767+ log_ios_done => { src => 'IB_lg_log_ios_done' },
2768+ log_ios_s => { src => 'IB_lg_log_ios_s' },
2769+ log_seq_no => { src => 'IB_lg_log_seq_no' },
2770+ pending_chkp_writes => { src => 'IB_lg_pending_chkp_writes' },
2771+ pending_log_writes => { src => 'IB_lg_pending_log_writes' },
2772+ },
2773+ visible => [ qw(cxn log_seq_no log_flushed_to last_chkp log_ios_done log_ios_s)],
2774+ filters => [],
2775+ sort_cols => 'cxn',
2776+ sort_dir => '1',
2777+ innodb => 'lg',
2778+ group_by => [],
2779+ aggregate => 0,
2780+ },
2781+ master_status => {
2782+ capt => 'Master Status',
2783+ cust => {},
2784+ cols => {
2785+ cxn => { src => 'cxn' },
2786+ binlog_do_db => { src => 'binlog_do_db' },
2787+ binlog_ignore_db => { src => 'binlog_ignore_db' },
2788+ master_file => { src => 'file' },
2789+ master_pos => { src => 'position' },
2790+ binlog_cache_overflow => { src => '(Binlog_cache_disk_use||0)/(Binlog_cache_use||1)', trans => [ qw(percent) ] },
2791+ },
2792+ visible => [ qw(cxn master_file master_pos binlog_cache_overflow)],
2793+ filters => [ qw(cxn_is_master) ],
2794+ sort_cols => 'cxn',
2795+ sort_dir => '1',
2796+ innodb => '',
2797+ group_by => [],
2798+ aggregate => 0,
2799+ },
2800+ pending_io => {
2801+ capt => 'Pending I/O',
2802+ cust => {},
2803+ cols => {
2804+ cxn => { src => 'cxn' },
2805+ p_normal_aio_reads => { src => 'IB_io_pending_normal_aio_reads' },
2806+ p_aio_writes => { src => 'IB_io_pending_aio_writes' },
2807+ p_ibuf_aio_reads => { src => 'IB_io_pending_ibuf_aio_reads' },
2808+ p_sync_ios => { src => 'IB_io_pending_sync_ios' },
2809+ p_buf_pool_flushes => { src => 'IB_io_pending_buffer_pool_flushes' },
2810+ p_log_flushes => { src => 'IB_io_pending_log_flushes' },
2811+ p_log_ios => { src => 'IB_io_pending_log_ios' },
2812+ p_preads => { src => 'IB_io_pending_preads' },
2813+ p_pwrites => { src => 'IB_io_pending_pwrites' },
2814+ },
2815+ visible => [ qw(cxn p_normal_aio_reads p_aio_writes p_ibuf_aio_reads p_sync_ios p_log_flushes p_log_ios)],
2816+ filters => [],
2817+ sort_cols => 'cxn',
2818+ sort_dir => '1',
2819+ innodb => 'io',
2820+ group_by => [],
2821+ aggregate => 0,
2822+ },
2823+ open_tables => {
2824+ capt => 'Open Tables',
2825+ cust => {},
2826+ cols => {
2827+ cxn => { src => 'cxn' },
2828+ db => { src => 'database' },
2829+ tbl => { src => 'table' },
2830+ num_times_open => { src => 'in_use' },
2831+ is_name_locked => { src => 'name_locked' },
2832+ },
2833+ visible => [ qw(cxn db tbl num_times_open is_name_locked)],
2834+ filters => [ qw(table_is_open) ],
2835+ sort_cols => '-num_times_open cxn db tbl',
2836+ sort_dir => '1',
2837+ innodb => '',
2838+ group_by => [],
2839+ aggregate => 0,
2840+ },
2841+ page_statistics => {
2842+ capt => 'Page Statistics',
2843+ cust => {},
2844+ cols => {
2845+ cxn => { src => 'cxn' },
2846+ pages_read => { src => 'IB_bp_pages_read' },
2847+ pages_written => { src => 'IB_bp_pages_written' },
2848+ pages_created => { src => 'IB_bp_pages_created' },
2849+ page_reads_sec => { src => 'IB_bp_page_reads_sec' },
2850+ page_writes_sec => { src => 'IB_bp_page_writes_sec' },
2851+ page_creates_sec => { src => 'IB_bp_page_creates_sec' },
2852+ },
2853+ visible => [ qw(cxn pages_read pages_written pages_created page_reads_sec page_writes_sec page_creates_sec)],
2854+ filters => [],
2855+ sort_cols => 'cxn',
2856+ sort_dir => '1',
2857+ innodb => 'bp',
2858+ group_by => [],
2859+ aggregate => 0,
2860+ },
2861+ processlist => {
2862+ capt => 'MySQL Process List',
2863+ cust => {},
2864+ cols => {
2865+ cxn => { src => 'cxn', minw => 6, maxw => 10 },
2866+ mysql_thread_id => { src => 'id', minw => 6, maxw => 0 },
2867+ user => { src => 'user', minw => 5, maxw => 8 },
2868+ hostname => { src => $exprs{Host}, minw => 13, maxw => 8, },
2869+ port => { src => $exprs{Port}, minw => 0, maxw => 0, },
2870+ host_and_port => { src => 'host', minw => 0, maxw => 0 },
2871+ db => { src => 'db', minw => 6, maxw => 12 },
2872+ cmd => { src => 'command', minw => 5, maxw => 0 },
2873+ time => { src => 'time', minw => 5, maxw => 0, trans => [ qw(secs_to_time) ], agg => 'sum' },
2874+ state => { src => 'state', minw => 0, maxw => 0 },
2875+ info => { src => 'info', minw => 0, maxw => 0, trans => [ qw(no_ctrl_char) ] },
2876+ cnt => { src => 'id', minw => 0, maxw => 0 },
2877+ },
2878+ visible => [ qw(cxn cmd cnt mysql_thread_id user hostname db time info)],
2879+ filters => [ qw(hide_self hide_inactive hide_slave_io) ],
2880+ sort_cols => '-time cxn hostname mysql_thread_id',
2881+ sort_dir => '1',
2882+ innodb => '',
2883+ hide_caption => 1,
2884+ colors => [
2885+ { col => 'state', op => 'eq', arg => 'Locked', color => 'black on_red' },
2886+ { col => 'cmd', op => 'eq', arg => 'Sleep', color => 'white' },
2887+ { col => 'user', op => 'eq', arg => 'system user', color => 'white' },
2888+ { col => 'cmd', op => 'eq', arg => 'Connect', color => 'white' },
2889+ { col => 'cmd', op => 'eq', arg => 'Binlog Dump', color => 'white' },
2890+ { col => 'time', op => '>', arg => 600, color => 'red' },
2891+ { col => 'time', op => '>', arg => 120, color => 'yellow' },
2892+ { col => 'time', op => '>', arg => 60, color => 'green' },
2893+ { col => 'time', op => '>', arg => 30, color => 'cyan' },
2894+ ],
2895+ group_by => [qw(cxn cmd)],
2896+ aggregate => 0,
2897+ },
2898+
2899+ # TODO: some more columns:
2900+ # 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'
2901+ # 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'
2902+ # 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'
2903+
2904+ q_header => {
2905+ capt => 'Q-mode Header',
2906+ cust => {},
2907+ cols => {
2908+ cxn => { src => 'cxn' },
2909+ questions => { src => 'Questions' },
2910+ qps => { src => 'Questions/Uptime_hires', dec => 1, trans => [qw(shorten)] },
2911+ load => { src => $exprs{ServerLoad}, dec => 1, trans => [qw(shorten)] },
2912+ slow => { src => 'Slow_queries', dec => 1, trans => [qw(shorten)] },
2913+ q_cache_hit => { src => $exprs{QcacheHitRatio}, dec => 1, trans => [qw(percent)] },
2914+ key_buffer_hit => { src => '1-(Key_reads/(Key_read_requests||1))', dec => 1, trans => [qw(percent)] },
2915+ bps_in => { src => 'Bytes_received/Uptime_hires', dec => 1, trans => [qw(shorten)] },
2916+ bps_out => { src => 'Bytes_sent/Uptime_hires', dec => 1, trans => [qw(shorten)] },
2917+ when => { src => 'when' },
2918+ },
2919+ visible => [ qw(cxn when load qps slow q_cache_hit key_buffer_hit bps_in bps_out)],
2920+ filters => [],
2921+ sort_cols => 'when cxn',
2922+ sort_dir => '1',
2923+ innodb => '',
2924+ hide_caption => 1,
2925+ group_by => [],
2926+ aggregate => 0,
2927+ },
2928+ row_operations => {
2929+ capt => 'InnoDB Row Operations',
2930+ cust => {},
2931+ cols => {
2932+ cxn => { src => 'cxn' },
2933+ num_inserts => { src => 'IB_ro_num_rows_ins' },
2934+ num_updates => { src => 'IB_ro_num_rows_upd' },
2935+ num_reads => { src => 'IB_ro_num_rows_read' },
2936+ num_deletes => { src => 'IB_ro_num_rows_del' },
2937+ num_inserts_sec => { src => 'IB_ro_ins_sec' },
2938+ num_updates_sec => { src => 'IB_ro_upd_sec' },
2939+ num_reads_sec => { src => 'IB_ro_read_sec' },
2940+ num_deletes_sec => { src => 'IB_ro_del_sec' },
2941+ },
2942+ visible => [ qw(cxn num_inserts num_updates num_reads num_deletes num_inserts_sec
2943+ num_updates_sec num_reads_sec num_deletes_sec)],
2944+ filters => [],
2945+ sort_cols => 'cxn',
2946+ sort_dir => '1',
2947+ innodb => 'ro',
2948+ group_by => [],
2949+ aggregate => 0,
2950+ },
2951+ row_operation_misc => {
2952+ capt => 'Row Operation Misc',
2953+ cust => {},
2954+ cols => {
2955+ cxn => { src => 'cxn' },
2956+ queries_in_queue => { src => 'IB_ro_queries_in_queue' },
2957+ queries_inside => { src => 'IB_ro_queries_inside' },
2958+ read_views_open => { src => 'IB_ro_read_views_open' },
2959+ main_thread_id => { src => 'IB_ro_main_thread_id' },
2960+ main_thread_proc_no => { src => 'IB_ro_main_thread_proc_no' },
2961+ main_thread_state => { src => 'IB_ro_main_thread_state' },
2962+ num_res_ext => { src => 'IB_ro_n_reserved_extents' },
2963+ },
2964+ visible => [ qw(cxn queries_in_queue queries_inside read_views_open main_thread_state)],
2965+ filters => [],
2966+ sort_cols => 'cxn',
2967+ sort_dir => '1',
2968+ innodb => 'ro',
2969+ group_by => [],
2970+ aggregate => 0,
2971+ },
2972+ semaphores => {
2973+ capt => 'InnoDB Semaphores',
2974+ cust => {},
2975+ cols => {
2976+ cxn => { src => 'cxn' },
2977+ mutex_os_waits => { src => 'IB_sm_mutex_os_waits' },
2978+ mutex_spin_rounds => { src => 'IB_sm_mutex_spin_rounds' },
2979+ mutex_spin_waits => { src => 'IB_sm_mutex_spin_waits' },
2980+ reservation_count => { src => 'IB_sm_reservation_count' },
2981+ rw_excl_os_waits => { src => 'IB_sm_rw_excl_os_waits' },
2982+ rw_excl_spins => { src => 'IB_sm_rw_excl_spins' },
2983+ rw_shared_os_waits => { src => 'IB_sm_rw_shared_os_waits' },
2984+ rw_shared_spins => { src => 'IB_sm_rw_shared_spins' },
2985+ signal_count => { src => 'IB_sm_signal_count' },
2986+ wait_array_size => { src => 'IB_sm_wait_array_size' },
2987+ },
2988+ visible => [ qw(cxn mutex_os_waits mutex_spin_waits mutex_spin_rounds
2989+ rw_excl_os_waits rw_excl_spins rw_shared_os_waits rw_shared_spins
2990+ signal_count reservation_count )],
2991+ filters => [],
2992+ sort_cols => 'cxn',
2993+ sort_dir => '1',
2994+ innodb => 'sm',
2995+ group_by => [],
2996+ aggregate => 0,
2997+ },
2998+ slave_io_status => {
2999+ capt => 'Slave I/O Status',
3000+ cust => {},
3001+ cols => {
3002+ cxn => { src => 'cxn' },
3003+ connect_retry => { src => 'connect_retry' },
3004+ master_host => { src => 'master_host', hdr => 'Master'},
3005+ master_log_file => { src => 'master_log_file', hdr => 'File' },
3006+ master_port => { src => 'master_port' },
3007+ master_ssl_allowed => { src => 'master_ssl_allowed' },
3008+ master_ssl_ca_file => { src => 'master_ssl_ca_file' },
3009+ master_ssl_ca_path => { src => 'master_ssl_ca_path' },
3010+ master_ssl_cert => { src => 'master_ssl_cert' },
3011+ master_ssl_cipher => { src => 'master_ssl_cipher' },
3012+ master_ssl_key => { src => 'master_ssl_key' },
3013+ master_user => { src => 'master_user' },
3014+ read_master_log_pos => { src => 'read_master_log_pos', hdr => 'Pos' },
3015+ relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] },
3016+ slave_io_running => { src => 'slave_io_running', hdr => 'On?' },
3017+ slave_io_state => { src => 'slave_io_state', hdr => 'State' },
3018+ },
3019+ visible => [ qw(cxn master_host slave_io_running master_log_file relay_log_size read_master_log_pos slave_io_state)],
3020+ filters => [ qw( cxn_is_slave ) ],
3021+ sort_cols => 'slave_io_running cxn',
3022+ colors => [
3023+ { col => 'slave_io_running', op => 'ne', arg => 'Yes', color => 'black on_red' },
3024+ ],
3025+ sort_dir => '1',
3026+ innodb => '',
3027+ group_by => [],
3028+ aggregate => 0,
3029+ },
3030+ slave_sql_status => {
3031+ capt => 'Slave SQL Status',
3032+ cust => {},
3033+ cols => {
3034+ cxn => { src => 'cxn' },
3035+ exec_master_log_pos => { src => 'exec_master_log_pos', hdr => 'Master Pos' },
3036+ last_errno => { src => 'last_errno' },
3037+ last_error => { src => 'last_error' },
3038+ master_host => { src => 'master_host', hdr => 'Master' },
3039+ relay_log_file => { src => 'relay_log_file' },
3040+ relay_log_pos => { src => 'relay_log_pos' },
3041+ relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] },
3042+ relay_master_log_file => { src => 'relay_master_log_file', hdr => 'Master File' },
3043+ replicate_do_db => { src => 'replicate_do_db' },
3044+ replicate_do_table => { src => 'replicate_do_table' },
3045+ replicate_ignore_db => { src => 'replicate_ignore_db' },
3046+ replicate_ignore_table => { src => 'replicate_ignore_table' },
3047+ replicate_wild_do_table => { src => 'replicate_wild_do_table' },
3048+ replicate_wild_ignore_table => { src => 'replicate_wild_ignore_table' },
3049+ skip_counter => { src => 'skip_counter' },
3050+ slave_sql_running => { src => 'slave_sql_running', hdr => 'On?' },
3051+ until_condition => { src => 'until_condition' },
3052+ until_log_file => { src => 'until_log_file' },
3053+ until_log_pos => { src => 'until_log_pos' },
3054+ time_behind_master => { src => 'seconds_behind_master', trans => [ qw(secs_to_time) ] },
3055+ 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)] },
3056+ slave_catchup_rate => { src => $exprs{SlaveCatchupRate}, trans => [ qw(set_precision) ] },
3057+ slave_open_temp_tables => { src => 'Slave_open_temp_tables' },
3058+ },
3059+ visible => [ qw(cxn master_host slave_sql_running time_behind_master slave_catchup_rate slave_open_temp_tables relay_log_pos last_error)],
3060+ filters => [ qw( cxn_is_slave ) ],
3061+ sort_cols => 'slave_sql_running cxn',
3062+ sort_dir => '1',
3063+ innodb => '',
3064+ colors => [
3065+ { col => 'slave_sql_running', op => 'ne', arg => 'Yes', color => 'black on_red' },
3066+ { col => 'time_behind_master', op => '>', arg => 600, color => 'red' },
3067+ { col => 'time_behind_master', op => '>', arg => 60, color => 'yellow' },
3068+ { col => 'time_behind_master', op => '==', arg => 0, color => 'white' },
3069+ ],
3070+ group_by => [],
3071+ aggregate => 0,
3072+ },
3073+ t_header => {
3074+ capt => 'T-Mode Header',
3075+ cust => {},
3076+ cols => {
3077+ cxn => { src => 'cxn' },
3078+ dirty_bufs => { src => $exprs{DirtyBufs}, trans => [qw(percent)] },
3079+ history_list_len => { src => 'IB_tx_history_list_len' },
3080+ lock_structs => { src => 'IB_tx_num_lock_structs' },
3081+ num_txns => { src => $exprs{NumTxns} },
3082+ max_txn => { src => $exprs{MaxTxnTime}, trans => [qw(secs_to_time)] },
3083+ undo_for => { src => 'IB_tx_purge_undo_for' },
3084+ used_bufs => { src => $exprs{BufPoolFill}, trans => [qw(percent)]},
3085+ versions => { src => $exprs{OldVersions} },
3086+ },
3087+ visible => [ qw(cxn history_list_len versions undo_for dirty_bufs used_bufs num_txns max_txn lock_structs)],
3088+ filters => [ ],
3089+ sort_cols => 'cxn',
3090+ sort_dir => '1',
3091+ innodb => '',
3092+ colors => [],
3093+ hide_caption => 1,
3094+ group_by => [],
3095+ aggregate => 0,
3096+ },
3097+ var_status => {
3098+ capt => 'Variables & Status',
3099+ cust => {},
3100+ cols => {}, # Generated from current varset
3101+ visible => [], # Generated from current varset
3102+ filters => [],
3103+ sort_cols => '',
3104+ sort_dir => 1,
3105+ innodb => '',
3106+ temp => 1, # Do not persist to config file.
3107+ hide_caption => 1,
3108+ pivot => 0,
3109+ group_by => [],
3110+ aggregate => 0,
3111+ },
3112+ wait_array => {
3113+ capt => 'InnoDB Wait Array',
3114+ cust => {},
3115+ cols => {
3116+ cxn => { src => 'cxn' },
3117+ thread => { src => 'thread' },
3118+ waited_at_filename => { src => 'waited_at_filename' },
3119+ waited_at_line => { src => 'waited_at_line' },
3120+ 'time' => { src => 'waited_secs', trans => [ qw(secs_to_time) ] },
3121+ request_type => { src => 'request_type' },
3122+ lock_mem_addr => { src => 'lock_mem_addr' },
3123+ lock_cfile_name => { src => 'lock_cfile_name' },
3124+ lock_cline => { src => 'lock_cline' },
3125+ writer_thread => { src => 'writer_thread' },
3126+ writer_lock_mode => { src => 'writer_lock_mode' },
3127+ num_readers => { src => 'num_readers' },
3128+ lock_var => { src => 'lock_var' },
3129+ waiters_flag => { src => 'waiters_flag' },
3130+ last_s_file_name => { src => 'last_s_file_name' },
3131+ last_s_line => { src => 'last_s_line' },
3132+ last_x_file_name => { src => 'last_x_file_name' },
3133+ last_x_line => { src => 'last_x_line' },
3134+ cell_waiting => { src => 'cell_waiting' },
3135+ cell_event_set => { src => 'cell_event_set' },
3136+ },
3137+ visible => [ qw(cxn thread time waited_at_filename waited_at_line request_type num_readers lock_var waiters_flag cell_waiting cell_event_set)],
3138+ filters => [],
3139+ sort_cols => 'cxn -time',
3140+ sort_dir => '1',
3141+ innodb => 'sm',
3142+ group_by => [],
3143+ aggregate => 0,
3144+ },
3145+);
3146+
3147+# Initialize %tbl_meta from %columns and do some checks.
3148+foreach my $table_name ( keys %tbl_meta ) {
3149+ my $table = $tbl_meta{$table_name};
3150+ my $cols = $table->{cols};
3151+
3152+ foreach my $col_name ( keys %$cols ) {
3153+ my $col_def = $table->{cols}->{$col_name};
3154+ die "I can't find a column named '$col_name' for '$table_name'" unless $columns{$col_name};
3155+ $columns{$col_name}->{referenced} = 1;
3156+
3157+ foreach my $prop ( keys %col_props ) {
3158+ # Each column gets non-existing values set from %columns or defaults from %col_props.
3159+ if ( !$col_def->{$prop} ) {
3160+ $col_def->{$prop}
3161+ = defined($columns{$col_name}->{$prop})
3162+ ? $columns{$col_name}->{$prop}
3163+ : $col_props{$prop};
3164+ }
3165+ }
3166+
3167+ # Ensure transformations and aggregate functions are valid
3168+ die "Unknown aggregate function '$col_def->{agg}' "
3169+ . "for column '$col_name' in table '$table_name'"
3170+ unless exists $agg_funcs{$col_def->{agg}};
3171+ foreach my $trans ( @{$col_def->{trans}} ) {
3172+ die "Unknown transformation '$trans' "
3173+ . "for column '$col_name' in table '$table_name'"
3174+ unless exists $trans_funcs{$trans};
3175+ }
3176+ }
3177+
3178+ # Ensure each column in visible and group_by exists in cols
3179+ foreach my $place ( qw(visible group_by) ) {
3180+ foreach my $col_name ( @{$table->{$place}} ) {
3181+ if ( !exists $cols->{$col_name} ) {
3182+ die "Column '$col_name' is listed in '$place' for '$table_name', but doesn't exist";
3183+ }
3184+ }
3185+ }
3186+
3187+ # Compile sort and color subroutines
3188+ $table->{sort_func} = make_sort_func($table);
3189+ $table->{color_func} = make_color_func($table);
3190+}
3191+
3192+# This is for code cleanup:
3193+{
3194+ my @unused_cols = grep { !$columns{$_}->{referenced} } sort keys %columns;
3195+ if ( @unused_cols ) {
3196+ die "The following columns are not used: "
3197+ . join(' ', @unused_cols);
3198+ }
3199+}
3200+
3201+# ###########################################################################
3202+# Operating modes {{{3
3203+# ###########################################################################
3204+my %modes = (
3205+ B => {
3206+ hdr => 'InnoDB Buffers',
3207+ cust => {},
3208+ note => 'Shows buffer info from InnoDB',
3209+ action_for => {
3210+ i => {
3211+ action => sub { toggle_config('status_inc') },
3212+ label => 'Toggle incremental status display',
3213+ },
3214+ },
3215+ display_sub => \&display_B,
3216+ connections => [],
3217+ server_group => '',
3218+ one_connection => 0,
3219+ tables => [qw(buffer_pool page_statistics insert_buffers adaptive_hash_index)],
3220+ visible_tables => [qw(buffer_pool page_statistics insert_buffers adaptive_hash_index)],
3221+ },
3222+ C => {
3223+ hdr => 'Command Summary',
3224+ cust => {},
3225+ note => 'Shows relative magnitude of variables',
3226+ action_for => {
3227+ s => {
3228+ action => sub { get_config_interactive('cmd_filter') },
3229+ label => 'Choose variable prefix',
3230+ },
3231+ },
3232+ display_sub => \&display_C,
3233+ connections => [],
3234+ server_group => '',
3235+ one_connection => 0,
3236+ tables => [qw(cmd_summary)],
3237+ visible_tables => [qw(cmd_summary)],
3238+ },
3239+ D => {
3240+ hdr => 'InnoDB Deadlocks',
3241+ cust => {},
3242+ note => 'View InnoDB deadlock information',
3243+ action_for => {
3244+ c => {
3245+ action => sub { edit_table('deadlock_transactions') },
3246+ label => 'Choose visible columns',
3247+ },
3248+ w => {
3249+ action => \&create_deadlock,
3250+ label => 'Wipe deadlock status info by creating a deadlock',
3251+ },
3252+ },
3253+ display_sub => \&display_D,
3254+ connections => [],
3255+ server_group => '',
3256+ one_connection => 0,
3257+ tables => [qw(deadlock_transactions deadlock_locks)],
3258+ visible_tables => [qw(deadlock_transactions deadlock_locks)],
3259+ },
3260+ F => {
3261+ hdr => 'InnoDB FK Err',
3262+ cust => {},
3263+ note => 'View the latest InnoDB foreign key error',
3264+ action_for => {},
3265+ display_sub => \&display_F,
3266+ connections => [],
3267+ server_group => '',
3268+ one_connection => 1,
3269+ tables => [qw(fk_error)],
3270+ visible_tables => [qw(fk_error)],
3271+ },
3272+ I => {
3273+ hdr => 'InnoDB I/O Info',
3274+ cust => {},
3275+ note => 'Shows I/O info (i/o, log...) from InnoDB',
3276+ action_for => {
3277+ i => {
3278+ action => sub { toggle_config('status_inc') },
3279+ label => 'Toggle incremental status display',
3280+ },
3281+ },
3282+ display_sub => \&display_I,
3283+ connections => [],
3284+ server_group => '',
3285+ one_connection => 0,
3286+ tables => [qw(io_threads pending_io file_io_misc log_statistics)],
3287+ visible_tables => [qw(io_threads pending_io file_io_misc log_statistics)],
3288+ },
3289+ L => {
3290+ hdr => 'Locks',
3291+ cust => {},
3292+ note => 'Shows transaction locks',
3293+ action_for => {
3294+ a => {
3295+ action => sub { send_cmd_to_servers('CREATE TABLE IF NOT EXISTS test.innodb_lock_monitor(a int) ENGINE=InnoDB', 0, '', []); },
3296+ label => 'Start the InnoDB Lock Monitor',
3297+ },
3298+ o => {
3299+ action => sub { send_cmd_to_servers('DROP TABLE IF EXISTS test.innodb_lock_monitor', 0, '', []); },
3300+ label => 'Stop the InnoDB Lock Monitor',
3301+ },
3302+ },
3303+ display_sub => \&display_L,
3304+ connections => [],
3305+ server_group => '',
3306+ one_connection => 0,
3307+ tables => [qw(innodb_locks)],
3308+ visible_tables => [qw(innodb_locks)],
3309+ },
3310+ M => {
3311+ hdr => 'Replication Status',
3312+ cust => {},
3313+ note => 'Shows replication (master and slave) status',
3314+ action_for => {
3315+ a => {
3316+ action => sub { send_cmd_to_servers('START SLAVE', 0, 'START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE = ?, MASTER_LOG_POS = ?', []); },
3317+ label => 'Start slave(s)',
3318+ },
3319+ i => {
3320+ action => sub { toggle_config('status_inc') },
3321+ label => 'Toggle incremental status display',
3322+ },
3323+ o => {
3324+ action => sub { send_cmd_to_servers('STOP SLAVE', 0, '', []); },
3325+ label => 'Stop slave(s)',
3326+ },
3327+ b => {
3328+ action => sub { purge_master_logs() },
3329+ label => 'Purge unused master logs',
3330+ },
3331+ },
3332+ display_sub => \&display_M,
3333+ connections => [],
3334+ server_group => '',
3335+ one_connection => 0,
3336+ tables => [qw(slave_sql_status slave_io_status master_status)],
3337+ visible_tables => [qw(slave_sql_status slave_io_status master_status)],
3338+ },
3339+ O => {
3340+ hdr => 'Open Tables',
3341+ cust => {},
3342+ note => 'Shows open tables in MySQL',
3343+ action_for => {
3344+ r => {
3345+ action => sub { reverse_sort('open_tables'); },
3346+ label => 'Reverse sort order',
3347+ },
3348+ s => {
3349+ action => sub { choose_sort_cols('open_tables'); },
3350+ label => "Choose sort column",
3351+ },
3352+ },
3353+ display_sub => \&display_O,
3354+ connections => [],
3355+ server_group => '',
3356+ one_connection => 0,
3357+ tables => [qw(open_tables)],
3358+ visible_tables => [qw(open_tables)],
3359+ },
3360+ Q => {
3361+ hdr => 'Query List',
3362+ cust => {},
3363+ note => 'Shows queries from SHOW FULL PROCESSLIST',
3364+ action_for => {
3365+ a => {
3366+ action => sub { toggle_filter('processlist', 'hide_self') },
3367+ label => 'Toggle the innotop process',
3368+ },
3369+ c => {
3370+ action => sub { edit_table('processlist') },
3371+ label => 'Choose visible columns',
3372+ },
3373+ e => {
3374+ action => sub { analyze_query('e'); },
3375+ label => "Explain a thread's query",
3376+ },
3377+ f => {
3378+ action => sub { analyze_query('f'); },
3379+ label => "Show a thread's full query",
3380+ },
3381+ h => {
3382+ action => sub { toggle_visible_table('Q', 'q_header') },
3383+ label => 'Toggle the header on and off',
3384+ },
3385+ i => {
3386+ action => sub { toggle_filter('processlist', 'hide_inactive') },
3387+ label => 'Toggle idle processes',
3388+ },
3389+ k => {
3390+ action => sub { kill_query('CONNECTION') },
3391+ label => "Kill a query's connection",
3392+ },
3393+ r => {
3394+ action => sub { reverse_sort('processlist'); },
3395+ label => 'Reverse sort order',
3396+ },
3397+ s => {
3398+ action => sub { choose_sort_cols('processlist'); },
3399+ label => "Change the display's sort column",
3400+ },
3401+ x => {
3402+ action => sub { kill_query('QUERY') },
3403+ label => "Kill a query",
3404+ },
3405+ },
3406+ display_sub => \&display_Q,
3407+ connections => [],
3408+ server_group => '',
3409+ one_connection => 0,
3410+ tables => [qw(q_header processlist)],
3411+ visible_tables => [qw(q_header processlist)],
3412+ },
3413+ R => {
3414+ hdr => 'InnoDB Row Ops',
3415+ cust => {},
3416+ note => 'Shows InnoDB row operation and semaphore info',
3417+ action_for => {
3418+ i => {
3419+ action => sub { toggle_config('status_inc') },
3420+ label => 'Toggle incremental status display',
3421+ },
3422+ },
3423+ display_sub => \&display_R,
3424+ connections => [],
3425+ server_group => '',
3426+ one_connection => 0,
3427+ tables => [qw(row_operations row_operation_misc semaphores wait_array)],
3428+ visible_tables => [qw(row_operations row_operation_misc semaphores wait_array)],
3429+ },
3430+ S => {
3431+ hdr => 'Variables & Status',
3432+ cust => {},
3433+ note => 'Shows query load statistics a la vmstat',
3434+ action_for => {
3435+ '>' => {
3436+ action => sub { switch_var_set('S_set', 1) },
3437+ label => 'Switch to next variable set',
3438+ },
3439+ '<' => {
3440+ action => sub { switch_var_set('S_set', -1) },
3441+ label => 'Switch to prev variable set',
3442+ },
3443+ c => {
3444+ action => sub {
3445+ choose_var_set('S_set');
3446+ start_S_mode();
3447+ },
3448+ label => "Choose which set to display",
3449+ },
3450+ e => {
3451+ action => \&edit_current_var_set,
3452+ label => 'Edit the current set of variables',
3453+ },
3454+ i => {
3455+ action => sub { $clear_screen_sub->(); toggle_config('status_inc') },
3456+ label => 'Toggle incremental status display',
3457+ },
3458+ '-' => {
3459+ action => sub { set_display_precision(-1) },
3460+ label => 'Decrease fractional display precision',
3461+ },
3462+ '+' => {
3463+ action => sub { set_display_precision(1) },
3464+ label => 'Increase fractional display precision',
3465+ },
3466+ g => {
3467+ action => sub { set_s_mode('g') },
3468+ label => 'Switch to graph (tload) view',
3469+ },
3470+ s => {
3471+ action => sub { set_s_mode('s') },
3472+ label => 'Switch to standard (vmstat) view',
3473+ },
3474+ v => {
3475+ action => sub { set_s_mode('v') },
3476+ label => 'Switch to pivoted view',
3477+ },
3478+ },
3479+ display_sub => \&display_S,
3480+ no_clear_screen => 1,
3481+ connections => [],
3482+ server_group => '',
3483+ one_connection => 0,
3484+ tables => [qw(var_status)],
3485+ visible_tables => [qw(var_status)],
3486+ },
3487+ T => {
3488+ hdr => 'InnoDB Txns',
3489+ cust => {},
3490+ note => 'Shows InnoDB transactions in top-like format',
3491+ action_for => {
3492+ a => {
3493+ action => sub { toggle_filter('innodb_transactions', 'hide_self') },
3494+ label => 'Toggle the innotop process',
3495+ },
3496+ c => {
3497+ action => sub { edit_table('innodb_transactions') },
3498+ label => 'Choose visible columns',
3499+ },
3500+ e => {
3501+ action => sub { analyze_query('e'); },
3502+ label => "Explain a thread's query",
3503+ },
3504+ f => {
3505+ action => sub { analyze_query('f'); },
3506+ label => "Show a thread's full query",
3507+ },
3508+ h => {
3509+ action => sub { toggle_visible_table('T', 't_header') },
3510+ label => 'Toggle the header on and off',
3511+ },
3512+ i => {
3513+ action => sub { toggle_filter('innodb_transactions', 'hide_inactive') },
3514+ label => 'Toggle inactive transactions',
3515+ },
3516+ k => {
3517+ action => sub { kill_query('CONNECTION') },
3518+ label => "Kill a transaction's connection",
3519+ },
3520+ r => {
3521+ action => sub { reverse_sort('innodb_transactions'); },
3522+ label => 'Reverse sort order',
3523+ },
3524+ s => {
3525+ action => sub { choose_sort_cols('innodb_transactions'); },
3526+ label => "Change the display's sort column",
3527+ },
3528+ x => {
3529+ action => sub { kill_query('QUERY') },
3530+ label => "Kill a query",
3531+ },
3532+ },
3533+ display_sub => \&display_T,
3534+ connections => [],
3535+ server_group => '',
3536+ one_connection => 0,
3537+ tables => [qw(t_header innodb_transactions)],
3538+ visible_tables => [qw(t_header innodb_transactions)],
3539+ },
3540+);
3541+
3542+# ###########################################################################
3543+# Global key mappings {{{3
3544+# Keyed on a single character, which is read from the keyboard. Uppercase
3545+# letters switch modes. Lowercase letters access commands when in a mode.
3546+# These can be overridden by action_for in %modes.
3547+# ###########################################################################
3548+my %action_for = (
3549+ '$' => {
3550+ action => \&edit_configuration,
3551+ label => 'Edit configuration settings',
3552+ },
3553+ '?' => {
3554+ action => \&display_help,
3555+ label => 'Show help',
3556+ },
3557+ '!' => {
3558+ action => \&display_license,
3559+ label => 'Show license and warranty',
3560+ },
3561+ '^' => {
3562+ action => \&edit_table,
3563+ label => "Edit the displayed table(s)",
3564+ },
3565+ '#' => {
3566+ action => \&choose_server_groups,
3567+ label => 'Select/create server groups',
3568+ },
3569+ '@' => {
3570+ action => \&choose_servers,
3571+ label => 'Select/create server connections',
3572+ },
3573+ '/' => {
3574+ action => \&add_quick_filter,
3575+ label => 'Quickly filter what you see',
3576+ },
3577+ '\\' => {
3578+ action => \&clear_quick_filters,
3579+ label => 'Clear quick-filters',
3580+ },
3581+ '%' => {
3582+ action => \&choose_filters,
3583+ label => 'Choose and edit table filters',
3584+ },
3585+ "\t" => {
3586+ action => \&next_server_group,
3587+ label => 'Switch to the next server group',
3588+ key => 'TAB',
3589+ },
3590+ '=' => {
3591+ action => \&toggle_aggregate,
3592+ label => 'Toggle aggregation',
3593+ },
3594+ # TODO: can these be auto-generated from %modes?
3595+ B => {
3596+ action => sub { switch_mode('B') },
3597+ label => '',
3598+ },
3599+ C => {
3600+ action => sub { switch_mode('C') },
3601+ label => '',
3602+ },
3603+ D => {
3604+ action => sub { switch_mode('D') },
3605+ label => '',
3606+ },
3607+ F => {
3608+ action => sub { switch_mode('F') },
3609+ label => '',
3610+ },
3611+ I => {
3612+ action => sub { switch_mode('I') },
3613+ label => '',
3614+ },
3615+ L => {
3616+ action => sub { switch_mode('L') },
3617+ label => '',
3618+ },
3619+ M => {
3620+ action => sub { switch_mode('M') },
3621+ label => '',
3622+ },
3623+ O => {
3624+ action => sub { switch_mode('O') },
3625+ label => '',
3626+ },
3627+ Q => {
3628+ action => sub { switch_mode('Q') },
3629+ label => '',
3630+ },
3631+ R => {
3632+ action => sub { switch_mode('R') },
3633+ label => '',
3634+ },
3635+ S => {
3636+ action => \&start_S_mode,
3637+ label => '',
3638+ },
3639+ T => {
3640+ action => sub { switch_mode('T') },
3641+ label => '',
3642+ },
3643+ d => {
3644+ action => sub { get_config_interactive('interval') },
3645+ label => 'Change refresh interval',
3646+ },
3647+ n => { action => \&next_server, label => 'Switch to the next connection' },
3648+ p => { action => \&pause, label => 'Pause innotop', },
3649+ q => { action => \&finish, label => 'Quit innotop', },
3650+);
3651+
3652+# ###########################################################################
3653+# Sleep times after certain statements {{{3
3654+# ###########################################################################
3655+my %stmt_sleep_time_for = ();
3656+
3657+# ###########################################################################
3658+# Config editor key mappings {{{3
3659+# ###########################################################################
3660+my %cfg_editor_action = (
3661+ c => {
3662+ note => 'Edit columns, etc in the displayed table(s)',
3663+ func => \&edit_table,
3664+ },
3665+ g => {
3666+ note => 'Edit general configuration',
3667+ func => \&edit_configuration_variables,
3668+ },
3669+ k => {
3670+ note => 'Edit row-coloring rules',
3671+ func => \&edit_color_rules,
3672+ },
3673+ p => {
3674+ note => 'Manage plugins',
3675+ func => \&edit_plugins,
3676+ },
3677+ s => {
3678+ note => 'Edit server groups',
3679+ func => \&edit_server_groups,
3680+ },
3681+ S => {
3682+ note => 'Edit SQL statement sleep delays',
3683+ func => \&edit_stmt_sleep_times,
3684+ },
3685+ t => {
3686+ note => 'Choose which table(s) to display in this mode',
3687+ func => \&choose_mode_tables,
3688+ },
3689+);
3690+
3691+# ###########################################################################
3692+# Color editor key mappings {{{3
3693+# ###########################################################################
3694+my %color_editor_action = (
3695+ n => {
3696+ note => 'Create a new color rule',
3697+ func => sub {
3698+ my ( $tbl, $idx ) = @_;
3699+ my $meta = $tbl_meta{$tbl};
3700+
3701+ $clear_screen_sub->();
3702+ my $col;
3703+ do {
3704+ $col = prompt_list(
3705+ 'Choose the target column for the rule',
3706+ '',
3707+ sub { return keys %{$meta->{cols}} },
3708+ { map { $_ => $meta->{cols}->{$_}->{label} } keys %{$meta->{cols}} });
3709+ } while ( !$col );
3710+ ( $col ) = grep { $_ } split(/\W+/, $col);
3711+ return $idx unless $col && exists $meta->{cols}->{$col};
3712+
3713+ $clear_screen_sub->();
3714+ my $op;
3715+ do {
3716+ $op = prompt_list(
3717+ 'Choose the comparison operator for the rule',
3718+ '',
3719+ sub { return keys %comp_ops },
3720+ { map { $_ => $comp_ops{$_} } keys %comp_ops } );
3721+ } until ( $op );
3722+ $op =~ s/\s+//g;
3723+ return $idx unless $op && exists $comp_ops{$op};
3724+
3725+ my $arg;
3726+ do {
3727+ $arg = prompt('Specify an argument for the comparison');
3728+ } until defined $arg;
3729+
3730+ my $color;
3731+ do {
3732+ $color = prompt_list(
3733+ 'Choose the color(s) the row should be when the rule matches',
3734+ '',
3735+ sub { return keys %ansicolors },
3736+ { map { $_ => $_ } keys %ansicolors } );
3737+ } until defined $color;
3738+ $color = join(' ', unique(grep { exists $ansicolors{$_} } split(/\W+/, $color)));
3739+ return $idx unless $color;
3740+
3741+ push @{$tbl_meta{$tbl}->{colors}}, {
3742+ col => $col,
3743+ op => $op,
3744+ arg => $arg,
3745+ color => $color
3746+ };
3747+ $tbl_meta{$tbl}->{cust}->{colors} = 1;
3748+
3749+ return $idx;
3750+ },
3751+ },
3752+ d => {
3753+ note => 'Remove the selected rule',
3754+ func => sub {
3755+ my ( $tbl, $idx ) = @_;
3756+ my @rules = @{ $tbl_meta{$tbl}->{colors} };
3757+ return 0 unless @rules > 0 && $idx < @rules && $idx >= 0;
3758+ splice(@{$tbl_meta{$tbl}->{colors}}, $idx, 1);
3759+ $tbl_meta{$tbl}->{cust}->{colors} = 1;
3760+ return $idx == @rules ? $#rules : $idx;
3761+ },
3762+ },
3763+ j => {
3764+ note => 'Move highlight down one',
3765+ func => sub {
3766+ my ( $tbl, $idx ) = @_;
3767+ my $num_rules = scalar @{$tbl_meta{$tbl}->{colors}};
3768+ return ($idx + 1) % $num_rules;
3769+ },
3770+ },
3771+ k => {
3772+ note => 'Move highlight up one',
3773+ func => sub {
3774+ my ( $tbl, $idx ) = @_;
3775+ my $num_rules = scalar @{$tbl_meta{$tbl}->{colors}};
3776+ return ($idx - 1) % $num_rules;
3777+ },
3778+ },
3779+ '+' => {
3780+ note => 'Move selected rule up one',
3781+ func => sub {
3782+ my ( $tbl, $idx ) = @_;
3783+ my $meta = $tbl_meta{$tbl};
3784+ my $dest = $idx == 0 ? scalar(@{$meta->{colors}} - 1) : $idx - 1;
3785+ my $temp = $meta->{colors}->[$idx];
3786+ $meta->{colors}->[$idx] = $meta->{colors}->[$dest];
3787+ $meta->{colors}->[$dest] = $temp;
3788+ $meta->{cust}->{colors} = 1;
3789+ return $dest;
3790+ },
3791+ },
3792+ '-' => {
3793+ note => 'Move selected rule down one',
3794+ func => sub {
3795+ my ( $tbl, $idx ) = @_;
3796+ my $meta = $tbl_meta{$tbl};
3797+ my $dest = $idx == scalar(@{$meta->{colors}} - 1) ? 0 : $idx + 1;
3798+ my $temp = $meta->{colors}->[$idx];
3799+ $meta->{colors}->[$idx] = $meta->{colors}->[$dest];
3800+ $meta->{colors}->[$dest] = $temp;
3801+ $meta->{cust}->{colors} = 1;
3802+ return $dest;
3803+ },
3804+ },
3805+);
3806+
3807+# ###########################################################################
3808+# Plugin editor key mappings {{{3
3809+# ###########################################################################
3810+my %plugin_editor_action = (
3811+ '*' => {
3812+ note => 'Toggle selected plugin active/inactive',
3813+ func => sub {
3814+ my ( $plugins, $idx ) = @_;
3815+ my $plugin = $plugins->[$idx];
3816+ $plugin->{active} = $plugin->{active} ? 0 : 1;
3817+ return $idx;
3818+ },
3819+ },
3820+ j => {
3821+ note => 'Move highlight down one',
3822+ func => sub {
3823+ my ( $plugins, $idx ) = @_;
3824+ return ($idx + 1) % scalar(@$plugins);
3825+ },
3826+ },
3827+ k => {
3828+ note => 'Move highlight up one',
3829+ func => sub {
3830+ my ( $plugins, $idx ) = @_;
3831+ return $idx == 0 ? @$plugins - 1 : $idx - 1;
3832+ },
3833+ },
3834+);
3835+
3836+# ###########################################################################
3837+# Table editor key mappings {{{3
3838+# ###########################################################################
3839+my %tbl_editor_action = (
3840+ a => {
3841+ note => 'Add a column to the table',
3842+ func => sub {
3843+ my ( $tbl, $col ) = @_;
3844+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
3845+ my %all_cols = %{ $tbl_meta{$tbl}->{cols} };
3846+ delete @all_cols{@visible_cols};
3847+ my $choice = prompt_list(
3848+ 'Choose a column',
3849+ '',
3850+ sub { return keys %all_cols; },
3851+ { map { $_ => $all_cols{$_}->{label} || $all_cols{$_}->{hdr} } keys %all_cols });
3852+ if ( $all_cols{$choice} ) {
3853+ push @{$tbl_meta{$tbl}->{visible}}, $choice;
3854+ $tbl_meta{$tbl}->{cust}->{visible} = 1;
3855+ return $choice;
3856+ }
3857+ return $col;
3858+ },
3859+ },
3860+ n => {
3861+ note => 'Create a new column and add it to the table',
3862+ func => sub {
3863+ my ( $tbl, $col ) = @_;
3864+
3865+ $clear_screen_sub->();
3866+ print word_wrap("Choose a name for the column. This name is not displayed, and is used only "
3867+ . "for internal reference. It can contain only lowercase letters, numbers, "
3868+ . "and underscores.");
3869+ print "\n\n";
3870+ do {
3871+ $col = prompt("Enter column name");
3872+ $col = '' if $col =~ m/[^a-z0-9_]/;
3873+ } while ( !$col );
3874+
3875+ $clear_screen_sub->();
3876+ my $hdr;
3877+ do {
3878+ $hdr = prompt("Enter column header");
3879+ } while ( !$hdr );
3880+
3881+ $clear_screen_sub->();
3882+ print "Choose a source for the column's data\n\n";
3883+ my ( $src, $sub, $err );
3884+ do {
3885+ if ( $err ) {
3886+ print "Error: $err\n\n";
3887+ }
3888+ $src = prompt("Enter column source");
3889+ if ( $src ) {
3890+ ( $sub, $err ) = compile_expr($src);
3891+ }
3892+ } until ( !$err);
3893+
3894+ # TODO: this duplicates %col_props.
3895+ $tbl_meta{$tbl}->{cols}->{$col} = {
3896+ hdr => $hdr,
3897+ src => $src,
3898+ just => '-',
3899+ num => 0,
3900+ label => 'User-defined',
3901+ user => 1,
3902+ tbl => $tbl,
3903+ minw => 0,
3904+ maxw => 0,
3905+ trans => [],
3906+ func => $sub,
3907+ dec => 0,
3908+ agg => 0,
3909+ aggonly => 0,
3910+ };
3911+
3912+ $tbl_meta{$tbl}->{visible} = [ unique(@{$tbl_meta{$tbl}->{visible}}, $col) ];
3913+ $tbl_meta{$tbl}->{cust}->{visible} = 1;
3914+ return $col;
3915+ },
3916+ },
3917+ d => {
3918+ note => 'Remove selected column',
3919+ func => sub {
3920+ my ( $tbl, $col ) = @_;
3921+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
3922+ my $idx = 0;
3923+ return $col unless @visible_cols > 1;
3924+ while ( $visible_cols[$idx] ne $col ) {
3925+ $idx++;
3926+ }
3927+ $tbl_meta{$tbl}->{visible} = [ grep { $_ ne $col } @visible_cols ];
3928+ $tbl_meta{$tbl}->{cust}->{visible} = 1;
3929+ return $idx == $#visible_cols ? $visible_cols[$idx - 1] : $visible_cols[$idx + 1];
3930+ },
3931+ },
3932+ e => {
3933+ note => 'Edit selected column',
3934+ func => sub {
3935+ # TODO: make this editor hotkey-driven and give readline support.
3936+ my ( $tbl, $col ) = @_;
3937+ $clear_screen_sub->();
3938+ my $meta = $tbl_meta{$tbl}->{cols}->{$col};
3939+ my @prop = qw(hdr label src just num minw maxw trans agg); # TODO redundant
3940+
3941+ my $answer;
3942+ do {
3943+ # Do what the user asked...
3944+ if ( $answer && grep { $_ eq $answer } @prop ) {
3945+ # Some properties are arrays, others scalars.
3946+ my $ini = ref $col_props{$answer} ? join(' ', @{$meta->{$answer}}) : $meta->{$answer};
3947+ my $val = prompt("New value for $answer", undef, $ini);
3948+ $val = [ split(' ', $val) ] if ref($col_props{$answer});
3949+ if ( $answer eq 'trans' ) {
3950+ $val = [ unique(grep{ exists $trans_funcs{$_} } @$val) ];
3951+ }
3952+ @{$meta}{$answer, 'user', 'tbl' } = ( $val, 1, $tbl );
3953+ }
3954+
3955+ my @display_lines = (
3956+ '',
3957+ "You are editing column $tbl.$col.\n",
3958+ );
3959+
3960+ push @display_lines, create_table2(
3961+ \@prop,
3962+ { map { $_ => $_ } @prop },
3963+ { map { $_ => ref $meta->{$_} eq 'ARRAY' ? join(' ', @{$meta->{$_}})
3964+ : ref $meta->{$_} ? '[expression code]'
3965+ : $meta->{$_}
3966+ } @prop
3967+ },
3968+ { sep => ' ' });
3969+ draw_screen(\@display_lines, { raw => 1 });
3970+ print "\n\n"; # One to add space, one to clear readline artifacts
3971+ $answer = prompt('Edit what? (q to quit)');
3972+ } while ( $answer ne 'q' );
3973+
3974+ return $col;
3975+ },
3976+ },
3977+ j => {
3978+ note => 'Move highlight down one',
3979+ func => sub {
3980+ my ( $tbl, $col ) = @_;
3981+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
3982+ my $idx = 0;
3983+ while ( $visible_cols[$idx] ne $col ) {
3984+ $idx++;
3985+ }
3986+ return $visible_cols[ ($idx + 1) % @visible_cols ];
3987+ },
3988+ },
3989+ k => {
3990+ note => 'Move highlight up one',
3991+ func => sub {
3992+ my ( $tbl, $col ) = @_;
3993+ my @visible_cols = @{ $tbl_meta{$tbl}->{visible} };
3994+ my $idx = 0;
3995+ while ( $visible_cols[$idx] ne $col ) {
3996+ $idx++;
3997+ }
3998+ return $visible_cols[ $idx - 1 ];
3999+ },
4000+ },
4001+ '+' => {
4002+ note => 'Move selected column up one',
4003+ func => sub {
4004+ my ( $tbl, $col ) = @_;
4005+ my $meta = $tbl_meta{$tbl};
4006+ my @visible_cols = @{$meta->{visible}};
4007+ my $idx = 0;
4008+ while ( $visible_cols[$idx] ne $col ) {
4009+ $idx++;
4010+ }
4011+ if ( $idx ) {
4012+ $visible_cols[$idx] = $visible_cols[$idx - 1];
4013+ $visible_cols[$idx - 1] = $col;
4014+ $meta->{visible} = \@visible_cols;
4015+ }
4016+ else {
4017+ shift @{$meta->{visible}};
4018+ push @{$meta->{visible}}, $col;
4019+ }
4020+ $meta->{cust}->{visible} = 1;
4021+ return $col;
4022+ },
4023+ },
4024+ '-' => {
4025+ note => 'Move selected column down one',
4026+ func => sub {
4027+ my ( $tbl, $col ) = @_;
4028+ my $meta = $tbl_meta{$tbl};
4029+ my @visible_cols = @{$meta->{visible}};
4030+ my $idx = 0;
4031+ while ( $visible_cols[$idx] ne $col ) {
4032+ $idx++;
4033+ }
4034+ if ( $idx == $#visible_cols ) {
4035+ unshift @{$meta->{visible}}, $col;
4036+ pop @{$meta->{visible}};
4037+ }
4038+ else {
4039+ $visible_cols[$idx] = $visible_cols[$idx + 1];
4040+ $visible_cols[$idx + 1] = $col;
4041+ $meta->{visible} = \@visible_cols;
4042+ }
4043+ $meta->{cust}->{visible} = 1;
4044+ return $col;
4045+ },
4046+ },
4047+ f => {
4048+ note => 'Choose filters',
4049+ func => sub {
4050+ my ( $tbl, $col ) = @_;
4051+ choose_filters($tbl);
4052+ return $col;
4053+ },
4054+ },
4055+ o => {
4056+ note => 'Edit color rules',
4057+ func => sub {
4058+ my ( $tbl, $col ) = @_;
4059+ edit_color_rules($tbl);
4060+ return $col;
4061+ },
4062+ },
4063+ s => {
4064+ note => 'Choose sort columns',
4065+ func => sub {
4066+ my ( $tbl, $col ) = @_;
4067+ choose_sort_cols($tbl);
4068+ return $col;
4069+ },
4070+ },
4071+ g => {
4072+ note => 'Choose group-by (aggregate) columns',
4073+ func => sub {
4074+ my ( $tbl, $col ) = @_;
4075+ choose_group_cols($tbl);
4076+ return $col;
4077+ },
4078+ },
4079+);
4080+
4081+# ###########################################################################
4082+# Global variables and environment {{{2
4083+# ###########################################################################
4084+
4085+my @this_term_size; # w_chars, h_chars, w_pix, h_pix
4086+my @last_term_size; # w_chars, h_chars, w_pix, h_pix
4087+my $char;
4088+my $windows = $OSNAME =~ m/MSWin/;
4089+my $have_color = 0;
4090+my $MAX_ULONG = 4294967295; # 2^32-1
4091+my $num_regex = qr/^[+-]?(?=\d|\.)\d*(?:\.\d+)?(?:E[+-]?\d+|)$/i;
4092+my $int_regex = qr/^\d+$/;
4093+my $bool_regex = qr/^[01]$/;
4094+my $term = undef;
4095+my $file = undef; # File to watch for InnoDB monitor output
4096+my $file_mtime = undef; # Status of watched file
4097+my $file_data = undef; # Last chunk of text read from file
4098+my $innodb_parser = InnoDBParser->new;
4099+
4100+my $nonfatal_errs = join('|',
4101+ 'Access denied for user',
4102+ 'Unknown MySQL server host',
4103+ 'Unknown database',
4104+ 'Can\'t connect to local MySQL server through socket',
4105+ 'Can\'t connect to MySQL server on',
4106+ 'MySQL server has gone away',
4107+ 'Cannot call SHOW INNODB STATUS',
4108+ 'Access denied',
4109+ 'AutoCommit',
4110+);
4111+
4112+if ( !$opts{n} ) {
4113+ require Term::ReadLine;
4114+ $term = Term::ReadLine->new('innotop');
4115+}
4116+
4117+# Stores status, variables, innodb status, master/slave status etc.
4118+# Keyed on connection name. Each entry is a hashref of current and past data sets,
4119+# keyed on clock tick.
4120+my %vars;
4121+my %info_gotten = (); # Which things have been retrieved for the current clock tick.
4122+
4123+# Stores info on currently displayed queries: cxn, connection ID, query text.
4124+my @current_queries;
4125+
4126+my $lines_printed = 0;
4127+my $clock = 0; # Incremented with every wake-sleep cycle
4128+my $clearing_deadlocks = 0;
4129+
4130+# Find the home directory; it's different on different OSes.
4131+my $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';
4132+
4133+# If terminal coloring is available, use it. The only function I want from
4134+# the module is the colored() function.
4135+eval {
4136+ if ( !$opts{n} ) {
4137+ if ( $windows ) {
4138+ require Win32::Console::ANSI;
4139+ }
4140+ require Term::ANSIColor;
4141+ import Term::ANSIColor qw(colored);
4142+ $have_color = 1;
4143+ }
4144+};
4145+if ( $EVAL_ERROR || $opts{n} ) {
4146+ # If there was an error, manufacture my own colored() function that does no
4147+ # coloring.
4148+ *colored = sub { pop @_; @_; };
4149+}
4150+
4151+if ( $opts{n} ) {
4152+ $clear_screen_sub = sub {};
4153+}
4154+elsif ( $windows ) {
4155+ $clear_screen_sub = sub { $lines_printed = 0; system("cls") };
4156+}
4157+else {
4158+ my $clear = `clear`;
4159+ $clear_screen_sub = sub { $lines_printed = 0; print $clear };
4160+}
4161+
4162+# ###########################################################################
4163+# Config storage. {{{2
4164+# ###########################################################################
4165+my %config = (
4166+ color => {
4167+ val => $have_color,
4168+ note => 'Whether to use terminal coloring',
4169+ conf => 'ALL',
4170+ pat => $bool_regex,
4171+ },
4172+ cmd_filter => {
4173+ val => 'Com_',
4174+ note => 'Prefix for values in C mode',
4175+ conf => [qw(C)],
4176+ },
4177+ plugin_dir => {
4178+ val => "$homepath/.innotop/plugins",
4179+ note => 'Directory where plugins can be found',
4180+ conf => 'ALL',
4181+ },
4182+ show_percent => {
4183+ val => 1,
4184+ note => 'Show the % symbol after percentages',
4185+ conf => 'ALL',
4186+ pat => $bool_regex,
4187+ },
4188+ skip_innodb => {
4189+ val => 0,
4190+ note => 'Disable SHOW INNODB STATUS',
4191+ conf => 'ALL',
4192+ pat => $bool_regex,
4193+ },
4194+ S_func => {
4195+ val => 's',
4196+ note => 'What to display in S mode: graph, status, pivoted status',
4197+ conf => [qw(S)],
4198+ pat => qr/^[gsv]$/,
4199+ },
4200+ cxn_timeout => {
4201+ val => 28800,
4202+ note => 'Connection timeout for keeping unused connections alive',
4203+ conf => 'ALL',
4204+ pat => $int_regex,
4205+ },
4206+ graph_char => {
4207+ val => '*',
4208+ note => 'Character for drawing graphs',
4209+ conf => [ qw(S) ],
4210+ pat => qr/^.$/,
4211+ },
4212+ show_cxn_errors_in_tbl => {
4213+ val => 1,
4214+ note => 'Whether to display connection errors as rows in the table',
4215+ conf => 'ALL',
4216+ pat => $bool_regex,
4217+ },
4218+ hide_hdr => {
4219+ val => 0,
4220+ note => 'Whether to show column headers',
4221+ conf => 'ALL',
4222+ pat => $bool_regex,
4223+ },
4224+ show_cxn_errors => {
4225+ val => 1,
4226+ note => 'Whether to print connection errors to STDOUT',
4227+ conf => 'ALL',
4228+ pat => $bool_regex,
4229+ },
4230+ readonly => {
4231+ val => 0,
4232+ note => 'Whether the config file is read-only',
4233+ conf => [ qw() ],
4234+ pat => $bool_regex,
4235+ },
4236+ global => {
4237+ val => 1,
4238+ note => 'Whether to show GLOBAL variables and status',
4239+ conf => 'ALL',
4240+ pat => $bool_regex,
4241+ },
4242+ header_highlight => {
4243+ val => 'bold',
4244+ note => 'How to highlight table column headers',
4245+ conf => 'ALL',
4246+ pat => qr/^(?:bold|underline)$/,
4247+ },
4248+ display_table_captions => {
4249+ val => 1,
4250+ note => 'Whether to put captions on tables',
4251+ conf => 'ALL',
4252+ pat => $bool_regex,
4253+ },
4254+ charset => {
4255+ val => 'ascii',
4256+ note => 'What type of characters should be displayed in queries (ascii, unicode, none)',
4257+ conf => 'ALL',
4258+ pat => qr/^(?:ascii|unicode|none)$/,
4259+ },
4260+ auto_wipe_dl => {
4261+ val => 0,
4262+ note => 'Whether to auto-wipe InnoDB deadlocks',
4263+ conf => 'ALL',
4264+ pat => $bool_regex,
4265+ },
4266+ max_height => {
4267+ val => 30,
4268+ note => '[Win32] Max window height',
4269+ conf => 'ALL',
4270+ },
4271+ debug => {
4272+ val => 0,
4273+ pat => $bool_regex,
4274+ note => 'Debug mode (more verbose errors, uses more memory)',
4275+ conf => 'ALL',
4276+ },
4277+ num_digits => {
4278+ val => 2,
4279+ pat => $int_regex,
4280+ note => 'How many digits to show in fractional numbers and percents',
4281+ conf => 'ALL',
4282+ },
4283+ debugfile => {
4284+ val => "$homepath/.innotop/core_dump",
4285+ note => 'A debug file in case you are interested in error output',
4286+ },
4287+ show_statusbar => {
4288+ val => 1,
4289+ pat => $bool_regex,
4290+ note => 'Whether to show the status bar in the display',
4291+ conf => 'ALL',
4292+ },
4293+ mode => {
4294+ val => "T",
4295+ note => "Which mode to start in",
4296+ cmdline => 1,
4297+ },
4298+ status_inc => {
4299+ val => 0,
4300+ note => 'Whether to show raw or incremental values for status variables',
4301+ pat => $bool_regex,
4302+ },
4303+ interval => {
4304+ val => 10,
4305+ pat => qr/^(?:(?:\d*?[1-9]\d*(?:\.\d*)?)|(?:\d*\.\d*?[1-9]\d*))$/,
4306+ note => "The interval at which the display will be refreshed. Fractional values allowed.",
4307+ },
4308+ num_status_sets => {
4309+ val => 9,
4310+ pat => $int_regex,
4311+ note => 'How many sets of STATUS and VARIABLES values to show',
4312+ conf => [ qw(S) ],
4313+ },
4314+ S_set => {
4315+ val => 'general',
4316+ pat => qr/^\w+$/,
4317+ note => 'Which set of variables to display in S (Variables & Status) mode',
4318+ conf => [ qw(S) ],
4319+ },
4320+);
4321+
4322+# ###########################################################################
4323+# Config file sections {{{2
4324+# The configuration file is broken up into sections like a .ini file. This
4325+# variable defines those sections and the subroutines responsible for reading
4326+# and writing them.
4327+# ###########################################################################
4328+my %config_file_sections = (
4329+ plugins => {
4330+ reader => \&load_config_plugins,
4331+ writer => \&save_config_plugins,
4332+ },
4333+ group_by => {
4334+ reader => \&load_config_group_by,
4335+ writer => \&save_config_group_by,
4336+ },
4337+ filters => {
4338+ reader => \&load_config_filters,
4339+ writer => \&save_config_filters,
4340+ },
4341+ active_filters => {
4342+ reader => \&load_config_active_filters,
4343+ writer => \&save_config_active_filters,
4344+ },
4345+ visible_tables => {
4346+ reader => \&load_config_visible_tables,
4347+ writer => \&save_config_visible_tables,
4348+ },
4349+ sort_cols => {
4350+ reader => \&load_config_sort_cols,
4351+ writer => \&save_config_sort_cols,
4352+ },
4353+ active_columns => {
4354+ reader => \&load_config_active_columns,
4355+ writer => \&save_config_active_columns,
4356+ },
4357+ tbl_meta => {
4358+ reader => \&load_config_tbl_meta,
4359+ writer => \&save_config_tbl_meta,
4360+ },
4361+ general => {
4362+ reader => \&load_config_config,
4363+ writer => \&save_config_config,
4364+ },
4365+ connections => {
4366+ reader => \&load_config_connections,
4367+ writer => \&save_config_connections,
4368+ },
4369+ active_connections => {
4370+ reader => \&load_config_active_connections,
4371+ writer => \&save_config_active_connections,
4372+ },
4373+ server_groups => {
4374+ reader => \&load_config_server_groups,
4375+ writer => \&save_config_server_groups,
4376+ },
4377+ active_server_groups => {
4378+ reader => \&load_config_active_server_groups,
4379+ writer => \&save_config_active_server_groups,
4380+ },
4381+ max_values_seen => {
4382+ reader => \&load_config_mvs,
4383+ writer => \&save_config_mvs,
4384+ },
4385+ varsets => {
4386+ reader => \&load_config_varsets,
4387+ writer => \&save_config_varsets,
4388+ },
4389+ colors => {
4390+ reader => \&load_config_colors,
4391+ writer => \&save_config_colors,
4392+ },
4393+ stmt_sleep_times => {
4394+ reader => \&load_config_stmt_sleep_times,
4395+ writer => \&save_config_stmt_sleep_times,
4396+ },
4397+);
4398+
4399+# Config file sections have some dependencies, so they have to be read/written in order.
4400+my @ordered_config_file_sections = qw(general plugins filters active_filters tbl_meta
4401+ connections active_connections server_groups active_server_groups max_values_seen
4402+ active_columns sort_cols visible_tables varsets colors stmt_sleep_times
4403+ group_by);
4404+
4405+# All events for which plugins may register themselves. Entries are arrayrefs.
4406+my %event_listener_for = map { $_ => [] }
4407+ qw(
4408+ extract_values
4409+ set_to_tbl_pre_filter set_to_tbl_pre_sort set_to_tbl_pre_group
4410+ set_to_tbl_pre_colorize set_to_tbl_pre_transform set_to_tbl_pre_pivot
4411+ set_to_tbl_pre_create set_to_tbl_post_create
4412+ draw_screen
4413+ );
4414+
4415+# All variables to which plugins have access.
4416+my %pluggable_vars = (
4417+ action_for => \%action_for,
4418+ agg_funcs => \%agg_funcs,
4419+ config => \%config,
4420+ connections => \%connections,
4421+ dbhs => \%dbhs,
4422+ filters => \%filters,
4423+ modes => \%modes,
4424+ server_groups => \%server_groups,
4425+ tbl_meta => \%tbl_meta,
4426+ trans_funcs => \%trans_funcs,
4427+ var_sets => \%var_sets,
4428+);
4429+
4430+# ###########################################################################
4431+# Contains logic to generate prepared statements for a given function for a
4432+# given DB connection. Returns a $sth.
4433+# ###########################################################################
4434+my %stmt_maker_for = (
4435+ INNODB_STATUS => sub {
4436+ my ( $dbh ) = @_;
4437+ return $dbh->prepare(version_ge( $dbh, '5.0.0' )
4438+ ? 'SHOW ENGINE INNODB STATUS'
4439+ : 'SHOW INNODB STATUS');
4440+ },
4441+ SHOW_VARIABLES => sub {
4442+ my ( $dbh ) = @_;
4443+ return $dbh->prepare($config{global}->{val} && version_ge( $dbh, '4.0.3' )
4444+ ? 'SHOW GLOBAL VARIABLES'
4445+ : 'SHOW VARIABLES');
4446+ },
4447+ SHOW_STATUS => sub {
4448+ my ( $dbh ) = @_;
4449+ return $dbh->prepare($config{global}->{val} && version_ge( $dbh, '5.0.2' )
4450+ ? 'SHOW GLOBAL STATUS'
4451+ : 'SHOW STATUS');
4452+ },
4453+ KILL_QUERY => sub {
4454+ my ( $dbh ) = @_;
4455+ return $dbh->prepare(version_ge( $dbh, '5.0.0' )
4456+ ? 'KILL QUERY ?'
4457+ : 'KILL ?');
4458+ },
4459+ SHOW_MASTER_LOGS => sub {
4460+ my ( $dbh ) = @_;
4461+ return $dbh->prepare('SHOW MASTER LOGS');
4462+ },
4463+ SHOW_MASTER_STATUS => sub {
4464+ my ( $dbh ) = @_;
4465+ return $dbh->prepare('SHOW MASTER STATUS');
4466+ },
4467+ SHOW_SLAVE_STATUS => sub {
4468+ my ( $dbh ) = @_;
4469+ return $dbh->prepare('SHOW SLAVE STATUS');
4470+ },
4471+ KILL_CONNECTION => sub {
4472+ my ( $dbh ) = @_;
4473+ return $dbh->prepare(version_ge( $dbh, '5.0.0' )
4474+ ? 'KILL CONNECTION ?'
4475+ : 'KILL ?');
4476+ },
4477+ OPEN_TABLES => sub {
4478+ my ( $dbh ) = @_;
4479+ return version_ge($dbh, '4.0.0')
4480+ ? $dbh->prepare('SHOW OPEN TABLES')
4481+ : undef;
4482+ },
4483+ PROCESSLIST => sub {
4484+ my ( $dbh ) = @_;
4485+ return $dbh->prepare('SHOW FULL PROCESSLIST');
4486+ },
4487+);
4488+
4489+# Plugins!
4490+my %plugins = (
4491+);
4492+
4493+# ###########################################################################
4494+# Run the program {{{1
4495+# ###########################################################################
4496+
4497+# This config variable is only useful for MS Windows because its terminal
4498+# can't tell how tall it is.
4499+if ( !$windows ) {
4500+ delete $config{max_height};
4501+}
4502+
4503+# Try to lower my priority.
4504+eval { setpriority(0, 0, getpriority(0, 0) + 10); };
4505+
4506+# Print stuff to the screen immediately, don't wait for a newline.
4507+$OUTPUT_AUTOFLUSH = 1;
4508+
4509+# Clear the screen and load the configuration.
4510+$clear_screen_sub->();
4511+load_config();
4512+post_process_tbl_meta();
4513+
4514+# Make sure no changes are written to config file in non-interactive mode.
4515+if ( $opts{n} ) {
4516+ $config{readonly}->{val} = 1;
4517+}
4518+
4519+eval {
4520+
4521+ # Open the file for InnoDB status
4522+ if ( @ARGV ) {
4523+ my $filename = shift @ARGV;
4524+ open $file, "<", $filename
4525+ or die "Cannot open '$filename': $OS_ERROR";
4526+ }
4527+
4528+ # In certain modes we might have to collect data for two cycles
4529+ # before printing anything out, so we need to bump up the count one.
4530+ if ( $opts{n} && $opts{count} && $config{status_inc}->{val}
4531+ && $config{mode}->{val} =~ m/[S]/ )
4532+ {
4533+ $opts{count}++;
4534+ }
4535+
4536+ while (++$clock) {
4537+
4538+ my $mode = $config{mode}->{val} || 'T';
4539+ if ( !$modes{$mode} ) {
4540+ die "Mode '$mode' doesn't exist; try one of these:\n"
4541+ . join("\n", map { " $_ $modes{$_}->{hdr}" } sort keys %modes)
4542+ . "\n";
4543+ }
4544+
4545+ if ( !$opts{n} ) {
4546+ @last_term_size = @this_term_size;
4547+ @this_term_size = Term::ReadKey::GetTerminalSize(\*STDOUT);
4548+ if ( $windows ) {
4549+ $this_term_size[0]--;
4550+ $this_term_size[1]
4551+ = min($this_term_size[1], $config{max_height}->{val});
4552+ }
4553+ die("Can't read terminal size") unless @this_term_size;
4554+ }
4555+
4556+ # If there's no connection to a database server, we need to fix that...
4557+ if ( !%connections ) {
4558+ print "You have not defined any database connections.\n\n";
4559+ add_new_dsn();
4560+ }
4561+
4562+ # See whether there are any connections defined for this mode. If there's only one
4563+ # connection total, assume the user wants to just use innotop for a single server
4564+ # and don't ask which server to connect to. Also, if we're monitoring from a file,
4565+ # we just use the first connection.
4566+ if ( !get_connections() ) {
4567+ if ( $file || 1 == scalar keys %connections ) {
4568+ $modes{$config{mode}->{val}}->{connections} = [ keys %connections ];
4569+ }
4570+ else {
4571+ choose_connections();
4572+ }
4573+ }
4574+
4575+ # Term::ReadLine might have re-set $OUTPUT_AUTOFLUSH.
4576+ $OUTPUT_AUTOFLUSH = 1;
4577+
4578+ # Prune old data
4579+ my $sets = $config{num_status_sets}->{val};
4580+ foreach my $store ( values %vars ) {
4581+ delete @{$store}{ grep { $_ < $clock - $sets } keys %$store };
4582+ }
4583+ %info_gotten = ();
4584+
4585+ # Call the subroutine to display this mode.
4586+ $modes{$mode}->{display_sub}->();
4587+
4588+ # It may be time to quit now.
4589+ if ( $opts{count} && $clock >= $opts{count} ) {
4590+ finish();
4591+ }
4592+
4593+ # Wait for a bit.
4594+ if ( $opts{n} ) {
4595+ sleep($config{interval}->{val});
4596+ }
4597+ else {
4598+ ReadMode('cbreak');
4599+ $char = ReadKey($config{interval}->{val});
4600+ ReadMode('normal');
4601+ }
4602+
4603+ # Handle whatever action the key indicates.
4604+ do_key_action();
4605+
4606+ }
4607+};
4608+if ( $EVAL_ERROR ) {
4609+ core_dump( $EVAL_ERROR );
4610+}
4611+finish();
4612+
4613+# Subroutines {{{1
4614+# Mode functions{{{2
4615+# switch_mode {{{3
4616+sub switch_mode {
4617+ my $mode = shift;
4618+ $config{mode}->{val} = $mode;
4619+}
4620+
4621+# Prompting functions {{{2
4622+# prompt_list {{{3
4623+# Prompts the user for a value, given a question, initial value,
4624+# a completion function and a hashref of hints.
4625+sub prompt_list {
4626+ die "Can't call in non-interactive mode" if $opts{n};
4627+ my ( $question, $init, $completion, $hints ) = @_;
4628+ if ( $hints ) {
4629+ # Figure out how wide the table will be
4630+ my $max_name = max(map { length($_) } keys %$hints );
4631+ $max_name ||= 0;
4632+ $max_name += 3;
4633+ my @meta_rows = create_table2(
4634+ [ sort keys %$hints ],
4635+ { map { $_ => $_ } keys %$hints },
4636+ { map { $_ => trunc($hints->{$_}, $this_term_size[0] - $max_name) } keys %$hints },
4637+ { sep => ' ' });
4638+ if (@meta_rows > 10) {
4639+ # Try to split and stack the meta rows next to each other
4640+ my $split = int(@meta_rows / 2);
4641+ @meta_rows = stack_next(
4642+ [@meta_rows[0..$split - 1]],
4643+ [@meta_rows[$split..$#meta_rows]],
4644+ { pad => ' | '},
4645+ );
4646+ }
4647+ print join( "\n",
4648+ '',
4649+ map { ref $_ ? colored(@$_) : $_ } create_caption('Choose from', @meta_rows), ''),
4650+ "\n";
4651+ }
4652+ $term->Attribs->{completion_function} = $completion;
4653+ my $answer = $term->readline("$question: ", $init);
4654+ $OUTPUT_AUTOFLUSH = 1;
4655+ $answer = '' if !defined($answer);
4656+ $answer =~ s/\s+$//;
4657+ return $answer;
4658+}
4659+
4660+# prompt {{{3
4661+# Prints out a prompt and reads from the keyboard, then validates with the
4662+# validation regex until the input is correct.
4663+sub prompt {
4664+ die "Can't call in non-interactive mode" if $opts{n};
4665+ my ( $prompt, $regex, $init, $completion ) = @_;
4666+ my $response;
4667+ my $success = 0;
4668+ do {
4669+ if ( $completion ) {
4670+ $term->Attribs->{completion_function} = $completion;
4671+ }
4672+ $response = $term->readline("$prompt: ", $init);
4673+ if ( $regex && $response !~ m/$regex/ ) {
4674+ print "Invalid response.\n\n";
4675+ }
4676+ else {
4677+ $success = 1;
4678+ }
4679+ } while ( !$success );
4680+ $OUTPUT_AUTOFLUSH = 1;
4681+ $response =~ s/\s+$//;
4682+ return $response;
4683+}
4684+
4685+# prompt_noecho {{{3
4686+# Unfortunately, suppressing echo with Term::ReadLine isn't reliable; the user might not
4687+# have that library, or it might not support that feature.
4688+sub prompt_noecho {
4689+ my ( $prompt ) = @_;
4690+ print colored("$prompt: ", 'underline');
4691+ my $response;
4692+ ReadMode('noecho');
4693+ $response = <STDIN>;
4694+ chomp($response);
4695+ ReadMode('normal');
4696+ return $response;
4697+}
4698+
4699+# do_key_action {{{3
4700+# Depending on whether a key was read, do something. Keys have certain
4701+# actions defined in lookup tables. Each mode may have its own lookup table,
4702+# which trumps the global table -- so keys can be context-sensitive. The key
4703+# may be read and written in a subroutine, so it's a global.
4704+sub do_key_action {
4705+ if ( defined $char ) {
4706+ my $mode = $config{mode}->{val};
4707+ my $action
4708+ = defined($modes{$mode}->{action_for}->{$char})
4709+ ? $modes{$mode}->{action_for}->{$char}->{action}
4710+ : defined($action_for{$char})
4711+ ? $action_for{$char}->{action}
4712+ : sub{};
4713+ $action->();
4714+ }
4715+}
4716+
4717+# pause {{{3
4718+sub pause {
4719+ die "Can't call in non-interactive mode" if $opts{n};
4720+ my $msg = shift;
4721+ print defined($msg) ? "\n$msg" : "\nPress any key to continue";
4722+ ReadMode('cbreak');
4723+ my $char = ReadKey(0);
4724+ ReadMode('normal');
4725+ return $char;
4726+}
4727+
4728+# reverse_sort {{{3
4729+sub reverse_sort {
4730+ my $tbl = shift;
4731+ $tbl_meta{$tbl}->{sort_dir} *= -1;
4732+}
4733+
4734+# select_cxn {{{3
4735+# Selects connection(s). If the mode (or argument list) has only one, returns
4736+# it without prompt.
4737+sub select_cxn {
4738+ my ( $prompt, @cxns ) = @_;
4739+ if ( !@cxns ) {
4740+ @cxns = get_connections();
4741+ }
4742+ if ( @cxns == 1 ) {
4743+ return $cxns[0];
4744+ }
4745+ my $choices = prompt_list(
4746+ $prompt,
4747+ $cxns[0],
4748+ sub{ return @cxns },
4749+ { map { $_ => $connections{$_}->{dsn} } @cxns });
4750+ my @result = unique(grep { my $a = $_; grep { $_ eq $a } @cxns } split(/\s+/, $choices));
4751+ return @result;
4752+}
4753+
4754+# kill_query {{{3
4755+# Kills a connection, or on new versions, optionally a query but not connection.
4756+sub kill_query {
4757+ my ( $q_or_c ) = @_;
4758+
4759+ my $info = choose_thread(
4760+ sub { 1 },
4761+ 'Select a thread to kill the ' . $q_or_c,
4762+ );
4763+ return unless $info;
4764+ return unless pause("Kill $info->{id}?") =~ m/y/i;
4765+
4766+ eval {
4767+ do_stmt($info->{cxn}, $q_or_c eq 'QUERY' ? 'KILL_QUERY' : 'KILL_CONNECTION', $info->{id} );
4768+ };
4769+
4770+ if ( $EVAL_ERROR ) {
4771+ print "\nError: $EVAL_ERROR";
4772+ pause();
4773+ }
4774+}
4775+
4776+# set_display_precision {{{3
4777+sub set_display_precision {
4778+ my $dir = shift;
4779+ $config{num_digits}->{val} = min(9, max(0, $config{num_digits}->{val} + $dir));
4780+}
4781+
4782+sub toggle_visible_table {
4783+ my ( $mode, $table ) = @_;
4784+ my $visible = $modes{$mode}->{visible_tables};
4785+ if ( grep { $_ eq $table } @$visible ) {
4786+ $modes{$mode}->{visible_tables} = [ grep { $_ ne $table } @$visible ];
4787+ }
4788+ else {
4789+ unshift @$visible, $table;
4790+ }
4791+ $modes{$mode}->{cust}->{visible_tables} = 1;
4792+}
4793+
4794+# toggle_filter{{{3
4795+sub toggle_filter {
4796+ my ( $tbl, $filter ) = @_;
4797+ my $filters = $tbl_meta{$tbl}->{filters};
4798+ if ( grep { $_ eq $filter } @$filters ) {
4799+ $tbl_meta{$tbl}->{filters} = [ grep { $_ ne $filter } @$filters ];
4800+ }
4801+ else {
4802+ push @$filters, $filter;
4803+ }
4804+ $tbl_meta{$tbl}->{cust}->{filters} = 1;
4805+}
4806+
4807+# toggle_config {{{3
4808+sub toggle_config {
4809+ my ( $key ) = @_;
4810+ $config{$key}->{val} ^= 1;
4811+}
4812+
4813+# create_deadlock {{{3
4814+sub create_deadlock {
4815+ $clear_screen_sub->();
4816+
4817+ print "This function will deliberately cause a small deadlock, "
4818+ . "clearing deadlock information from the InnoDB monitor.\n\n";
4819+
4820+ my $answer = prompt("Are you sure you want to proceed? Say 'y' if you do");
4821+ return 0 unless $answer eq 'y';
4822+
4823+ my ( $cxn ) = select_cxn('Clear on which server? ');
4824+ return unless $cxn && exists($connections{$cxn});
4825+
4826+ clear_deadlock($cxn);
4827+}
4828+
4829+# deadlock_thread {{{3
4830+sub deadlock_thread {
4831+ my ( $id, $tbl, $cxn ) = @_;
4832+
4833+ eval {
4834+ my $dbh = get_new_db_connection($cxn, 1);
4835+ my @stmts = (
4836+ "set transaction isolation level serializable",
4837+ (version_ge($dbh, '4.0.11') ? "start transaction" : 'begin'),
4838+ "select * from $tbl where a = $id",
4839+ "update $tbl set a = $id where a <> $id",
4840+ );
4841+
4842+ foreach my $stmt (@stmts[0..2]) {
4843+ $dbh->do($stmt);
4844+ }
4845+ sleep(1 + $id);
4846+ $dbh->do($stmts[-1]);
4847+ };
4848+ if ( $EVAL_ERROR ) {
4849+ if ( $EVAL_ERROR !~ m/Deadlock found/ ) {
4850+ die $EVAL_ERROR;
4851+ }
4852+ }
4853+ exit(0);
4854+}
4855+
4856+# Purges unused binlogs on the master, up to but not including the latest log.
4857+# TODO: guess which connections are slaves of a given master.
4858+sub purge_master_logs {
4859+ my @cxns = get_connections();
4860+
4861+ get_master_slave_status(@cxns);
4862+
4863+ # Toss out the rows that don't have master/slave status...
4864+ my @vars =
4865+ grep { $_ && ($_->{file} || $_->{master_host}) }
4866+ map { $vars{$_}->{$clock} } @cxns;
4867+ @cxns = map { $_->{cxn} } @vars;
4868+
4869+ # Figure out which master to purge ons.
4870+ my @masters = map { $_->{cxn} } grep { $_->{file} } @vars;
4871+ my ( $master ) = select_cxn('Which master?', @masters );
4872+ return unless $master;
4873+ my ($master_status) = grep { $_->{cxn} eq $master } @vars;
4874+
4875+ # Figure out the result order (not lexical order) of master logs.
4876+ my @master_logs = get_master_logs($master);
4877+ my $i = 0;
4878+ my %master_logs = map { $_->{log_name} => $i++ } @master_logs;
4879+
4880+ # Ask which slave(s) are reading from this master.
4881+ my @slave_status = grep { $_->{master_host} } @vars;
4882+ my @slaves = map { $_->{cxn} } @slave_status;
4883+ @slaves = select_cxn("Which slaves are reading from $master?", @slaves);
4884+ @slave_status = grep { my $item = $_; grep { $item->{cxn} eq $_ } @slaves } @slave_status;
4885+ return unless @slave_status;
4886+
4887+ # Find the minimum binary log in use.
4888+ my $min_log = min(map { $master_logs{$_->{master_log_file}} } @slave_status);
4889+ my $log_name = $master_logs[$min_log]->{log_name};
4890+
4891+ my $stmt = "PURGE MASTER LOGS TO '$log_name'";
4892+ send_cmd_to_servers($stmt, 0, 'PURGE {MASTER | BINARY} LOGS {TO "log_name" | BEFORE "date"}', [$master]);
4893+}
4894+
4895+sub send_cmd_to_servers {
4896+ my ( $cmd, $all, $hint, $cxns ) = @_;
4897+ if ( $all ) {
4898+ @$cxns = get_connections();
4899+ }
4900+ elsif ( !@$cxns ) {
4901+ @$cxns = select_cxn('Which servers?', @$cxns);
4902+ }
4903+ if ( $hint ) {
4904+ print "\nHint: $hint\n";
4905+ }
4906+ $cmd = prompt('Command to send', undef, $cmd);
4907+ foreach my $cxn ( @$cxns ) {
4908+ eval {
4909+ my $sth = do_query($cxn, $cmd);
4910+ };
4911+ if ( $EVAL_ERROR ) {
4912+ print "Error from $cxn: $EVAL_ERROR\n";
4913+ }
4914+ else {
4915+ print "Success on $cxn\n";
4916+ }
4917+ }
4918+ pause();
4919+}
4920+
4921+# Display functions {{{2
4922+
4923+sub set_s_mode {
4924+ my ( $func ) = @_;
4925+ $clear_screen_sub->();
4926+ $config{S_func}->{val} = $func;
4927+}
4928+
4929+# start_S_mode {{{3
4930+sub start_S_mode {
4931+ $clear_screen_sub->();
4932+ switch_mode('S');
4933+}
4934+
4935+# display_B {{{3
4936+sub display_B {
4937+ my @display_lines;
4938+ my @cxns = get_connections();
4939+ get_innodb_status(\@cxns);
4940+
4941+ my @buffer_pool;
4942+ my @page_statistics;
4943+ my @insert_buffers;
4944+ my @adaptive_hash_index;
4945+ my %rows_for = (
4946+ buffer_pool => \@buffer_pool,
4947+ page_statistics => \@page_statistics,
4948+ insert_buffers => \@insert_buffers,
4949+ adaptive_hash_index => \@adaptive_hash_index,
4950+ );
4951+
4952+ my @visible = get_visible_tables();
4953+ my %wanted = map { $_ => 1 } @visible;
4954+
4955+ foreach my $cxn ( @cxns ) {
4956+ my $set = $vars{$cxn}->{$clock};
4957+ my $pre = $vars{$cxn}->{$clock-1} || $set;
4958+
4959+ if ( $set->{IB_bp_complete} ) {
4960+ if ( $wanted{buffer_pool} ) {
4961+ push @buffer_pool, extract_values($set, $set, $pre, 'buffer_pool');
4962+ }
4963+ if ( $wanted{page_statistics} ) {
4964+ push @page_statistics, extract_values($set, $set, $pre, 'page_statistics');
4965+ }
4966+ }
4967+ if ( $set->{IB_ib_complete} ) {
4968+ if ( $wanted{insert_buffers} ) {
4969+ push @insert_buffers, extract_values(
4970+ $config{status_inc}->{val} ? inc(0, $cxn) : $set, $set, $pre,
4971+ 'insert_buffers');
4972+ }
4973+ if ( $wanted{adaptive_hash_index} ) {
4974+ push @adaptive_hash_index, extract_values($set, $set, $pre, 'adaptive_hash_index');
4975+ }
4976+ }
4977+ }
4978+
4979+ my $first_table = 0;
4980+ foreach my $tbl ( @visible ) {
4981+ push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl);
4982+ push @display_lines, get_cxn_errors(@cxns)
4983+ if ( $config{debug}->{val} || !$first_table++ );
4984+ }
4985+
4986+ draw_screen(\@display_lines);
4987+}
4988+
4989+# display_C {{{3
4990+sub display_C {
4991+ my @display_lines;
4992+ my @cxns = get_connections();
4993+ get_status_info(@cxns);
4994+
4995+ my @cmd_summary;
4996+ my %rows_for = (
4997+ cmd_summary => \@cmd_summary,
4998+ );
4999+
5000+ my @visible = get_visible_tables();
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: