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