#!/usr/bin/perl

# vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3

# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com.
# Feedback and improvements are gratefully received.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 2; OR the Perl Artistic License.  On UNIX and similar
# systems, you can issue `man perlgpl' or `man perlartistic' to read these

# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA  02111-1307  USA

use strict;
use warnings FATAL => 'all';

our $VERSION = '1.7.1';

# Find the home directory; it's different on different OSes.
our $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';

# Configuration files
our $default_home_conf = "$homepath/.innotop/innotop.conf";
our $default_central_conf = "/etc/innotop/innotop.conf";
our $conf_file = "";

## Begin packages ##

package DSNParser;

use DBI;
use Data::Dumper;
$Data::Dumper::Indent    = 0;
$Data::Dumper::Quotekeys = 0;
use English qw(-no_match_vars);

use constant MKDEBUG => $ENV{MKDEBUG};

# Defaults are built-in, but you can add/replace items by passing them as
# hashrefs of {key, desc, copy, dsn}.  The desc and dsn items are optional.
# You can set properties with the prop() sub.  Don't set the 'opts' property.
sub new {
   my ( $class, @opts ) = @_;
   my $self = {
      opts => {
         A => {
            desc => 'Default character set',
            dsn  => 'charset',
            copy => 1,
         },
         D => {
            desc => 'Database to use',
            dsn  => 'database',
            copy => 1,
         },
         F => {
            desc => 'Only read default options from the given file',
            dsn  => 'mysql_read_default_file',
            copy => 1,
         },
         h => {
            desc => 'Connect to host',
            dsn  => 'host',
            copy => 1,
         },
         p => {
            desc => 'Password to use when connecting',
            dsn  => 'password',
            copy => 1,
         },
         P => {
            desc => 'Port number to use for connection',
            dsn  => 'port',
            copy => 1,
         },
         S => {
            desc => 'Socket file to use for connection',
            dsn  => 'mysql_socket',
            copy => 1,
         },
         u => {
            desc => 'User for login if not current user',
            dsn  => 'user',
            copy => 1,
         },
      },
   };
   foreach my $opt ( @opts ) {
      MKDEBUG && _d('Adding extra property ' . $opt->{key});
      $self->{opts}->{$opt->{key}} = { desc => $opt->{desc}, copy => $opt->{copy} };
   }
   return bless $self, $class;
}

# Recognized properties:
# * autokey:   which key to treat a bareword as (typically h=host).
# * dbidriver: which DBI driver to use; assumes mysql, supports Pg.
# * required:  which parts are required (hashref).
# * setvars:   a list of variables to set after connecting
sub prop {
   my ( $self, $prop, $value ) = @_;
   if ( @_ > 2 ) {
      MKDEBUG && _d("Setting $prop property");
      $self->{$prop} = $value;
   }
   return $self->{$prop};
}

sub parse {
   my ( $self, $dsn, $prev, $defaults ) = @_;
   if ( !$dsn ) {
      MKDEBUG && _d('No DSN to parse');
      return;
   }
   MKDEBUG && _d("Parsing $dsn");
   $prev     ||= {};
   $defaults ||= {};
   my %given_props;
   my %final_props;
   my %opts = %{$self->{opts}};
   my $prop_autokey = $self->prop('autokey');

   # Parse given props
   foreach my $dsn_part ( split(/,/, $dsn) ) {
      if ( my ($prop_key, $prop_val) = $dsn_part =~  m/^(.)=(.*)$/ ) {
         # Handle the typical DSN parts like h=host, P=3306, etc.
         $given_props{$prop_key} = $prop_val;
      }
      elsif ( $prop_autokey ) {
         # Handle barewords
         MKDEBUG && _d("Interpreting $dsn_part as $prop_autokey=$dsn_part");
         $given_props{$prop_autokey} = $dsn_part;
      }
      else {
         MKDEBUG && _d("Bad DSN part: $dsn_part");
      }
   }

   # Fill in final props from given, previous, and/or default props
   foreach my $key ( keys %opts ) {
      MKDEBUG && _d("Finding value for $key");
      $final_props{$key} = $given_props{$key};
      if (   !defined $final_props{$key}
           && defined $prev->{$key} && $opts{$key}->{copy} )
      {
         $final_props{$key} = $prev->{$key};
         MKDEBUG && _d("Copying value for $key from previous DSN");
      }
      if ( !defined $final_props{$key} ) {
         $final_props{$key} = $defaults->{$key};
         MKDEBUG && _d("Copying value for $key from defaults");
      }
   }

   # Sanity check props
   foreach my $key ( keys %given_props ) {
      die "Unrecognized DSN part '$key' in '$dsn'\n"
         unless exists $opts{$key};
   }
   if ( (my $required = $self->prop('required')) ) {
      foreach my $key ( keys %$required ) {
         die "Missing DSN part '$key' in '$dsn'\n" unless $final_props{$key};
      }
   }

   return \%final_props;
}

sub as_string {
   my ( $self, $dsn ) = @_;
   return $dsn unless ref $dsn;
   return join(',',
      map  { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
      grep { defined $dsn->{$_} && $self->{opts}->{$_} }
      sort keys %$dsn );
}

sub usage {
   my ( $self ) = @_;
   my $usage
      = "DSN syntax is key=value[,key=value...]  Allowable DSN keys:\n"
      . "  KEY  COPY  MEANING\n"
      . "  ===  ====  =============================================\n";
   my %opts = %{$self->{opts}};
   foreach my $key ( sort keys %opts ) {
      $usage .= "  $key    "
             .  ($opts{$key}->{copy} ? 'yes   ' : 'no    ')
             .  ($opts{$key}->{desc} || '[No description]')
             . "\n";
   }
   if ( (my $key = $self->prop('autokey')) ) {
      $usage .= "  If the DSN is a bareword, the word is treated as the '$key' key.\n";
   }
   return $usage;
}

# Supports PostgreSQL via the dbidriver element of $info, but assumes MySQL by
# default.
sub get_cxn_params {
   my ( $self, $info ) = @_;
   my $dsn;
   my %opts = %{$self->{opts}};
   my $driver = $self->prop('dbidriver') || '';
   if ( $driver eq 'Pg' ) {
      $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';'
         . join(';', map  { "$opts{$_}->{dsn}=$info->{$_}" }
                     grep { defined $info->{$_} }
                     qw(h P));
   }
   else {
      $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';'
         . join(';', map  { "$opts{$_}->{dsn}=$info->{$_}" }
                     grep { defined $info->{$_} }
                     qw(F h P S A))
         . ';mysql_read_default_group=client';
   }
   MKDEBUG && _d($dsn);
   return ($dsn, $info->{u}, $info->{p});
}


# Fills in missing info from a DSN after successfully connecting to the server.
sub fill_in_dsn {
   my ( $self, $dbh, $dsn ) = @_;
   my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name');
   my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()');
   $user =~ s/@.*//;
   $dsn->{h} ||= $vars->{hostname}->{Value};
   $dsn->{S} ||= $vars->{'socket'}->{Value};
   $dsn->{P} ||= $vars->{port}->{Value};
   $dsn->{u} ||= $user;
   $dsn->{D} ||= $db;
}

sub get_dbh {
   my ( $self, $cxn_string, $user, $pass, $opts ) = @_;
   $opts ||= {};
   my $defaults = {
      AutoCommit        => 0,
      RaiseError        => 1,
      PrintError        => 0,
      mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0),
   };
   @{$defaults}{ keys %$opts } = values %$opts;
   my $dbh;
   my $tries = 2;
   while ( !$dbh && $tries-- ) {
      eval {
         MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {',
            join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}');
         $dbh = DBI->connect($cxn_string, $user, $pass, $defaults);
         # Immediately set character set and binmode on STDOUT.
         if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
            my $sql = "/*!40101 SET NAMES $charset*/";
            MKDEBUG && _d("$dbh: $sql");
            $dbh->do($sql);
            MKDEBUG && _d('Enabling charset for STDOUT');
            if ( $charset eq 'utf8' ) {
               binmode(STDOUT, ':utf8')
                  or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
            }
            else {
               binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR";
            }
         }
      };
      if ( !$dbh && $EVAL_ERROR ) {
         MKDEBUG && _d($EVAL_ERROR);
         if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) {
            MKDEBUG && _d("Going to try again without utf8 support");
            delete $defaults->{mysql_enable_utf8};
         }
         if ( !$tries ) {
            die $EVAL_ERROR;
         }
      }
   }
   # If setvars exists and it's MySQL connection, set them
   my $setvars = $self->prop('setvars');
   if ( $cxn_string =~ m/mysql/i && $setvars ) {
      my $sql = "SET $setvars";
      MKDEBUG && _d("$dbh: $sql");
      eval {
         $dbh->do($sql);
      };
      if ( $EVAL_ERROR ) {
         MKDEBUG && _d($EVAL_ERROR);
      }
   }
   MKDEBUG && _d('DBH info: ',
      $dbh,
      Dumper($dbh->selectrow_hashref(
         'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')),
      ' Connection info: ', ($dbh->{mysql_hostinfo} || 'undef'),
      ' Character set info: ',
      Dumper($dbh->selectall_arrayref(
         'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})),
      ' $DBD::mysql::VERSION: ', $DBD::mysql::VERSION,
      ' $DBI::VERSION: ', $DBI::VERSION,
   );
   return $dbh;
}

# Tries to figure out a hostname for the connection.
sub get_hostname {
   my ( $self, $dbh ) = @_;
   if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) {
      return $host;
   }
   my ( $hostname, $one ) = $dbh->selectrow_array(
      'SELECT /*!50038 @@hostname, */ 1');
   return $hostname;
}

# Disconnects a database handle, but complains verbosely if there are any active
# children.  These are usually $sth handles that haven't been finish()ed.
sub disconnect {
   my ( $self, $dbh ) = @_;
   MKDEBUG && $self->print_active_handles($dbh);
   $dbh->disconnect;
}

sub print_active_handles {
   my ( $self, $thing, $level ) = @_;
   $level ||= 0;
   printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level,
      $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : ''))
      or die "Cannot print: $OS_ERROR";
   foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) {
      $self->print_active_handles( $handle, $level + 1 );
   }
}

sub _d {
   my ($package, undef, $line) = caller 0;
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
        map { defined $_ ? $_ : 'undef' }
        @_;
   # Use $$ instead of $PID in case the package
   # does not use English.
   print "# $package:$line $$ ", @_, "\n";
}

1;

package InnoDBParser;

use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
use English qw(-no_match_vars);
use List::Util qw(max);

# Some common patterns
my $d  = qr/(\d+)/;                    # Digit
my $f  = qr/(\d+\.\d+)/;               # Float
my $t  = qr/(\d+ \d+)/;                # Transaction ID
my $i  = qr/((?:\d{1,3}\.){3}\d+)/;    # IP address
my $n  = qr/([^`\s]+)/;                # MySQL object name
my $w  = qr/(\w+)/;                    # Words
my $fl = qr/([\w\.\/]+) line $d/;      # Filename and line number
my $h  = qr/((?:0x)?[0-9a-f]*)/;       # Hex
my $s  = qr/(\d{6} .\d:\d\d:\d\d)/;    # InnoDB timestamp

# If you update this variable, also update the SYNOPSIS in the pod.
my %innodb_section_headers = (
   "TRANSACTIONS"                          => "tx",
   "BUFFER POOL AND MEMORY"                => "bp",
   "SEMAPHORES"                            => "sm",
   "LOG"                                   => "lg",
   "ROW OPERATIONS"                        => "ro",
   "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib",
   "FILE I/O"                              => "io",
   "LATEST DETECTED DEADLOCK"              => "dl",
   "LATEST FOREIGN KEY ERROR"              => "fk",
);

my %parser_for = (
   tx => \&parse_tx_section,
   bp => \&parse_bp_section,
   sm => \&parse_sm_section,
   lg => \&parse_lg_section,
   ro => \&parse_ro_section,
   ib => \&parse_ib_section,
   io => \&parse_io_section,
   dl => \&parse_dl_section,
   fk => \&parse_fk_section,
);

my %fk_parser_for = (
   Transaction => \&parse_fk_transaction_error,
   Error       => \&parse_fk_bad_constraint_error,
   Cannot      => \&parse_fk_cant_drop_parent_error,
);

# A thread's proc_info can be at least 98 different things I've found in the
# source.  Fortunately, most of them begin with a gerunded verb.  These are
# the ones that don't.
my %is_proc_info = (
   'After create'                 => 1,
   'Execution of init_command'    => 1,
   'FULLTEXT initialization'      => 1,
   'Reopen tables'                => 1,
   'Repair done'                  => 1,
   'Repair with keycache'         => 1,
   'System lock'                  => 1,
   'Table lock'                   => 1,
   'Thread initialized'           => 1,
   'User lock'                    => 1,
   'copy to tmp table'            => 1,
   'discard_or_import_tablespace' => 1,
   'end'                          => 1,
   'got handler lock'             => 1,
   'got old table'                => 1,
   'init'                         => 1,
   'key cache'                    => 1,
   'locks'                        => 1,
   'malloc'                       => 1,
   'query end'                    => 1,
   'rename result table'          => 1,
   'rename'                       => 1,
   'setup'                        => 1,
   'statistics'                   => 1,
   'status'                       => 1,
   'table cache'                  => 1,
   'update'                       => 1,
);

sub new {
   bless {}, shift;
}

# Parse the status and return it.
# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c
# Pass in the text to parse, whether to be in debugging mode, which sections
# to parse (hashref; if empty, parse all), and whether to parse full info from
# locks and such (probably shouldn't unless you need to).
sub parse_status_text {
   my ( $self, $fulltext, $debug, $sections, $full ) = @_;

   die "I can't parse undef" unless defined $fulltext;
   $fulltext =~ s/[\r\n]+/\n/g;

   $sections ||= {};
   die '$sections must be a hashref' unless ref($sections) eq 'HASH';

   my %innodb_data = (
      got_all   => 0,         # Whether I was able to get the whole thing
      ts        => '',        # Timestamp the server put on it
      last_secs => 0,         # Num seconds the averages are over
      sections  => {},        # Parsed values from each section
   );

   if ( $debug ) {
      $innodb_data{'fulltext'} = $fulltext;
   }

   # Get the most basic info about the status: beginning and end, and whether
   # I got the whole thing (if there has been a big deadlock and there are
   # too many locks to print, the output might be truncated)
   my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m;
   $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ];
   $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'});
   ( $innodb_data{'last_secs'} ) = $fulltext
      =~ m/Per second averages calculated from the last $d seconds/;

   ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/;
   $innodb_data{'got_all'} = $got_all || 0;

   # Split it into sections.  Each section begins with
   # -----
   # LABEL
   # -----
   my %innodb_sections;
   my @matches = $fulltext
      =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs;
   while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) {
      $innodb_sections{$name} = [ $text, $end ? 1 : 0 ];
   }
   # The Row Operations section is a special case, because instead of ending
   # with the beginning of another section, it ends with the end of the file.
   # So this section is complete if the entire file is complete.
   $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'};

   # Just for sanity's sake, make sure I understand what to do with each
   # section
   eval {
      foreach my $section ( keys %innodb_sections ) {
         my $header = $innodb_section_headers{$section};
         die "Unknown section $section in $fulltext\n"
            unless $header;
         $innodb_data{'sections'}->{ $header }
            ->{'fulltext'} = $innodb_sections{$section}->[0];
         $innodb_data{'sections'}->{ $header }
            ->{'complete'} = $innodb_sections{$section}->[1];
      }
   };
   if ( $EVAL_ERROR ) {
      _debug( $debug, $EVAL_ERROR);
   }

   # ################################################################
   # Parse the detailed data out of the sections.
   # ################################################################
   eval {
      foreach my $section ( keys %parser_for ) {
         if ( defined $innodb_data{'sections'}->{$section}
               && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) {
            $parser_for{$section}->(
                  $innodb_data{'sections'}->{$section},
                  $innodb_data{'sections'}->{$section}->{'complete'},
                  $debug,
                  $full )
               or delete $innodb_data{'sections'}->{$section};
         }
         else {
            delete $innodb_data{'sections'}->{$section};
         }
      }
   };
   if ( $EVAL_ERROR ) {
      _debug( $debug, $EVAL_ERROR);
   }

   return \%innodb_data;
}

# Parses the status text and returns it flattened out as a single hash.
sub get_status_hash {
   my ( $self, $fulltext, $debug, $sections, $full ) = @_;

   # Parse the status text...
   my $innodb_status
      = $self->parse_status_text($fulltext, $debug, $sections, $full );

   # Flatten the hierarchical structure into a single list by grabbing desired
   # sections from it.
   return
      (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)),
      (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} }
         qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads
            awe_mem_alloc pages_modified writes_pending_lru page_creates_sec
            reads_pending pages_total buf_pool_hits writes_pending_single_page
            page_writes_sec pages_read pages_written page_reads_sec
            writes_pending_flush_list buf_pool_size add_pool_alloc
            dict_mem_alloc pages_created buf_free complete )),
      (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} }
         qw( num_lock_structs history_list_len purge_done_for transactions
            purge_undo_for is_truncated trx_id_counter complete )),
      (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} }
         qw( hash_table_size hash_searches_s non_hash_searches_s
            bufs_in_node_heap used_cells size free_list_len seg_size inserts
            merged_recs merges complete )),
      (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} }
         qw( log_ios_done pending_chkp_writes last_chkp log_ios_s
            log_flushed_to log_seq_no pending_log_writes complete )),
      (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} }
         qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits
            mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits
            waits signal_count reservation_count complete )),
      (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} }
         qw( queries_in_queue n_reserved_extents main_thread_state
         main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec
         read_views_open num_rows_upd num_rows_ins num_rows_read
         queries_inside num_rows_del complete )),
      (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} }
         qw( trigger parent_table child_index parent_index attempted_op
         child_db timestring fk_name records col_name reason txn parent_db
         type child_table parent_col complete )),
      (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} }
         qw( pending_buffer_pool_flushes pending_pwrites pending_preads
         pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios
         reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s
         threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs
         pending_log_flushes complete )),
      (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} }
         qw( timestring rolled_back txns complete ));

}

sub ts_to_string {
   my $parts = shift;
   return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts);
}

sub parse_innodb_timestamp {
   my $text = shift;
   my ( $y, $m, $d, $h, $i, $s )
      = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/;
   die("Can't get timestamp from $text\n") unless $y;
   $y += 2000;
   return ( $y, $m, $d, $h, $i, $s );
}

sub parse_fk_section {
   my ( $section, $complete, $debug, $full ) = @_;
   my $fulltext = $section->{'fulltext'};

   return 0 unless $fulltext;

   my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m;
   $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
   $section->{'timestring'} = ts_to_string($section->{'ts'});
   $section->{'type'} = $type;

   # Decide which type of FK error happened, and dispatch to the right parser.
   if ( $type && $fk_parser_for{$type} ) {
      $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full );
   }

   delete $section->{'fulltext'} unless $debug;

   return 1;
}

sub parse_fk_cant_drop_parent_error {
   my ( $section, $complete, $debug, $fulltext, $full ) = @_;

   # Parse the parent/child table info out
   @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext
      =~ m{Cannot $w table `(.*)/(.*)`}m;
   @{$section}{ qw(child_db child_table) } = $fulltext
      =~ m{because it is referenced by `(.*)/(.*)`}m;

   ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s;
   $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm
      if $section->{'reason'};

   # Certain data may not be present.  Make them '' if not present.
   map { $section->{$_} ||= "" }
      qw(child_index fk_name col_name parent_col);
}

# See dict/dict0dict.c, function dict_foreign_error_report
# I don't care much about these.  There are lots of different messages, and
# they come from someone trying to create a foreign key, or similar
# statements.  They aren't indicative of some transaction trying to insert,
# delete or update data.  Sometimes it is possible to parse out a lot of
# information about the tables and indexes involved, but often the message
# contains the DDL string the user entered, which is way too much for this
# module to try to handle.
sub parse_fk_bad_constraint_error {
   my ( $section, $complete, $debug, $fulltext, $full ) = @_;

   # Parse the parent/child table and index info out
   @{$section}{ qw(child_db child_table) } = $fulltext
      =~ m{Error in foreign key constraint of table (.*)/(.*):$}m;
   $section->{'attempted_op'} = 'DDL';

   # FK name, parent info... if possible.
   @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
      = $fulltext
      =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/;

   if ( !defined($section->{'fk_name'}) ) {
      # Try to parse SQL a user might have typed in a CREATE statement or such
      @{$section}{ qw(col_name parent_db parent_table parent_col) }
         = $fulltext
         =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i;
   }
   $section->{'parent_db'} ||= $section->{'child_db'};

   # Name of the child index (index in the same table where the FK is, see
   # definition of dict_foreign_struct in include/dict0mem.h, where it is
   # called foreign_index, as opposed to referenced_index which is in the
   # parent table.  This may not be possible to find.
   @{$section}{ qw(child_index) } = $fulltext
      =~ m/^The index in the foreign key in table is $n$/m;

   @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms;
   $section->{'reason'} =~ s/\s+/ /g
      if $section->{'reason'};
   
   # Certain data may not be present.  Make them '' if not present.
   map { $section->{$_} ||= "" }
      qw(child_index fk_name col_name parent_table parent_col);
}

# see source file row/row0ins.c
sub parse_fk_transaction_error {
   my ( $section, $complete, $debug, $fulltext, $full ) = @_;

   # Parse the txn info out
   my ( $txn ) = $fulltext
      =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s;
   if ( $txn ) {
      $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full );
   }

   # Parse the parent/child table and index info out.  There are two types: an
   # update or a delete of a parent record leaves a child orphaned
   # (row_ins_foreign_report_err), and an insert or update of a child record has
   # no matching parent record (row_ins_foreign_report_add_err).

   @{$section}{ qw(reason child_db child_table) }
      = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m;

   @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
      = $fulltext
      =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/;
   $section->{'parent_db'} ||= $section->{'child_db'};

   # Special case, which I don't know how to trigger, but see
   # innobase/row/row0ins.c row_ins_check_foreign_constraint
   if ( $fulltext =~ m/ibd file does not currently exist!/ ) {
      my ( $attempted_op, $index, $records )
         = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm;
      $section->{'child_index'} = $index;
      $section->{'attempted_op'} = $attempted_op || '';
      if ( $records && $full ) {
         ( $section->{'records'} )
            = parse_innodb_record_dump( $records, $complete, $debug );
      }
      @{$section}{qw(parent_db parent_table)}
         =~ m/^But the parent table `$n`\.`$n`$/m;
   }
   else {
      my ( $attempted_op, $which, $index )
         = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m;
      if ( $which ) {
         $section->{$which . '_index'} = $index;
         $section->{'attempted_op'} = $attempted_op || '';

         # Parse out the related records in the other table.
         my ( $search_index, $records );
         if ( $which eq 'child' ) {
            ( $search_index, $records ) = $fulltext
               =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms;
            $section->{'parent_index'} = $search_index;
         }
         else {
            ( $search_index, $records ) = $fulltext
               =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms;
            $section->{'child_index'} = $search_index;
         }
         if ( $records && $full ) {
            $section->{'records'}
               = parse_innodb_record_dump( $records, $complete, $debug );
         }
         else {
            $section->{'records'} = '';
         }
      }
   }

   # Parse out the tuple trying to be updated, deleted or inserted.
   my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m;
   if ( $trigger ) {
      $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug );
   }

   # Certain data may not be present.  Make them '' if not present.
   map { $section->{$_} ||= "" }
      qw(child_index fk_name col_name parent_table parent_col);
}

# There are new-style and old-style record formats.  See rem/rem0rec.c
# TODO: write some tests for this
sub parse_innodb_record_dump {
   my ( $dump, $complete, $debug ) = @_;
   return undef unless $dump;

   my $result = {};

   if ( $dump =~ m/PHYSICAL RECORD/ ) {
      my $style = $dump =~ m/compact format/ ? 'new' : 'old';
      $result->{'style'} = $style;

      # This is a new-style record.
      if ( $style eq 'new' ) {
         @{$result}{qw( heap_no type num_fields info_bits )}
            = $dump
            =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m;
      }

      # OK, it's old-style.  Unfortunately there are variations here too.
      elsif ( $dump =~ m/-byte offs / ) {
         # Older-old style.
         @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
            = $dump
            =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m;
            if ( $dump !~ m/-byte offs TRUE/ ) {
               $result->{'byte_offset'} = 0;
            }
      }
      else {
         # Newer-old style.
         @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
            = $dump
            =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m;
      }

   }
   else {
      $result->{'style'} = 'tuple';
      @{$result}{qw( type num_fields )}
         = $dump =~ m/^(DATA TUPLE): $d fields;$/m;
   }

   # Fill in default values for things that couldn't be parsed.
   map { $result->{$_} ||= 0 }
      qw(heap_no num_fields byte_offset info_bits);
   map { $result->{$_} ||= '' }
      qw(style type );

   my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm;
   $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ];

   return $result;
}

# New/old-style applies here.  See rem/rem0rec.c
# $text should not include the leading space or the second trailing semicolon.
sub parse_field {
   my ( $text, $complete, $debug ) = @_;

   # Sample fields:
   # '4: SQL NULL, size 4 '
   # '1: len 6; hex 000000005601; asc     V ;'
   # '6: SQL NULL'
   # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)'
   my ( $id, $nullsize, $len, $hex, $asc, $truncated );
   ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/;
   if ( !defined($id) ) {
      ( $id ) = $text =~ m/^$d: SQL NULL$/;
   }
   if ( !defined($id) ) {
      ( $id, $len, $hex, $asc, $truncated )
         = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/;
   }

   die "Could not parse this field: '$text'" unless defined $id;
   return {
      id    => $id,
      len   => defined($len) ? $len : defined($nullsize) ? $nullsize : 0,
      'hex' => defined($hex) ? $hex : '',
      asc   => defined($asc) ? $asc : '',
      trunc => $truncated ? 1 : 0,
   };

}

sub parse_dl_section {
   my ( $dl, $complete, $debug, $full ) = @_;
   return unless $dl;
   my $fulltext = $dl->{'fulltext'};
   return 0 unless $fulltext;

   my ( $ts ) = $fulltext =~ m/^$s$/m;
   return 0 unless $ts;

   $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
   $dl->{'timestring'} = ts_to_string($dl->{'ts'});
   $dl->{'txns'} = {};

   my @sections
      = $fulltext
      =~ m{
         ^\*{3}\s([^\n]*)  # *** (1) WAITING FOR THIS...
         (.*?)             # Followed by anything, non-greedy
         (?=(?:^\*{3})|\z) # Followed by another three stars or EOF
      }gmsx;


   # Loop through each section.  There are no assumptions about how many
   # there are, who holds and wants what locks, and who gets rolled back.
   while ( my ($header, $body) = splice(@sections, 0, 2) ) {
      my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/;
      next unless $txn_id;
      $dl->{'txns'}->{$txn_id} ||= {};
      my $txn = $dl->{'txns'}->{$txn_id};

      if ( $what eq 'TRANSACTION' ) {
         $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full );
      }
      else {
         push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full );
      }
   }

   @{ $dl }{ qw(rolled_back) }
      = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m;

   # Make sure certain values aren't undef
   map { $dl->{$_} ||= '' } qw(rolled_back);

   delete $dl->{'fulltext'} unless $debug;
   return 1;
}

sub parse_innodb_record_locks {
   my ( $text, $complete, $debug, $full ) = @_;
   my @result;

   foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) {
      my $hash = {};
      @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) }
         = $lock
         =~ m{^(RECORD|TABLE) LOCKS? (?:space id $d page no $d n bits $d index `?$n`? of )?table `$n(?:/|`\.`)$n` trx id $t lock.mode (\S+)}m;
      ( $hash->{'special'} )
         = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m;
      $hash->{'insert_intention'}
         = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0;
      $hash->{'waiting'}
         = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0;

      # Some things may not be in the text, so make sure they are not
      # undef.
      map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id);
      map { $hash->{$_} ||= "" } qw(index special);
      push @result, $hash;
   }

   return @result;
}

sub parse_tx_text {
   my ( $txn, $complete, $debug, $full ) = @_;

   my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id )
      = $txn
      =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m;
   my ( $thread_status, $thread_decl_inside )
      = $txn
      =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m;

   # Parsing the line that begins 'MySQL thread id' is complicated.  The only
   # thing always in the line is the thread and query id.  See function
   # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc.
   my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m;
   my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status );

   if ( $thread_line ) {
      # These parts can always be gotten.
      ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m;

      # If it's a master/slave thread, "Has (read|sent) all" may be the thread's
      # proc_info.  In these cases, there won't be any host/ip/user info
      ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m;
      if ( defined($query_status) ) {
         $user = 'system user';
      }

      # It may be the case that the query id is the last thing in the line.
      elsif ( $thread_line =~ m/query id \d+ / ) {
         # The IP address is the only non-word thing left, so it's the most
         # useful marker for where I have to start guessing.
         ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m;
         if ( defined $ip ) {
            ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/;
         }
         else { # OK, there wasn't an IP address.
            # There might not be ANYTHING except the query status.
            ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/;
            if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) {
               # The remaining tokens are, in order: hostname, user, query_status.
               # It's basically impossible to know which is which.
               ( $hostname, $user, $query_status ) = $thread_line
                  =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m;
            }
            else {
               $user = 'system user';
            }
         }
      }
   }

   my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries )
      = $txn
      =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m;
   my ( $lock_wait_time )
      = $txn
      =~ m/^------- TRX HAS BEEN WAITING $d SEC/m;

   my $locks;
   # If the transaction has locks, grab the locks.
   if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) {
      $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)];
   }
   
   my ( $tables_in_use, $tables_locked )
      = $txn
      =~ m/^mysql tables in use $d, locked $d$/m;
   my ( $txn_doesnt_see_ge, $txn_sees_lt )
      = $txn
      =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m;
   my $has_read_view = defined($txn_doesnt_see_ge);
   # Only a certain number of bytes of the query text are included here, at least
   # under some circumstances.  Some versions include 300, some 600.
   my ( $query_text )
      = $txn
      =~ m{
         ^MySQL\sthread\sid\s[^\n]+\n           # This comes before the query text
         (.*?)                                  # The query text
         (?=                                    # Followed by any of...
            ^Trx\sread\sview
            |^-------\sTRX\sHAS\sBEEN\sWAITING
            |^TABLE\sLOCK
            |^RECORD\sLOCKS\sspace\sid
            |^(?:---)?TRANSACTION
            |^\*\*\*\s\(\d\)
            |\Z
         )
      }xms;
   if ( $query_text ) {
      $query_text =~ s/\s+$//;
   }
   else {
      $query_text = '';
   }

   my %stuff = (
      active_secs        => $active_secs,
      has_read_view      => $has_read_view,
      heap_size          => $heap_size,
      hostname           => $hostname,
      ip                 => $ip,
      lock_structs       => $lock_structs,
      lock_wait_status   => $lock_wait_status,
      lock_wait_time     => $lock_wait_time,
      mysql_thread_id    => $mysql_thread_id,
      os_thread_id       => $os_thread_id,
      proc_no            => $proc_no,
      query_id           => $query_id,
      query_status       => $query_status,
      query_text         => $query_text,
      row_locks          => $row_locks,
      tables_in_use      => $tables_in_use,
      tables_locked      => $tables_locked,
      thread_decl_inside => $thread_decl_inside,
      thread_status      => $thread_status,
      txn_doesnt_see_ge  => $txn_doesnt_see_ge,
      txn_id             => $txn_id,
      txn_sees_lt        => $txn_sees_lt,
      txn_status         => $txn_status,
      undo_log_entries   => $undo_log_entries,
      user               => $user,
   );
   $stuff{'fulltext'} = $txn if $debug;
   $stuff{'locks'} = $locks if $locks;

   # Some things may not be in the txn text, so make sure they are not
   # undef.
   map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs
         tables_in_use undo_log_entries tables_locked has_read_view
         thread_decl_inside lock_wait_time proc_no row_locks);
   map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge
         txn_sees_lt query_status ip query_text lock_wait_status user);
   $stuff{'hostname'} ||= $stuff{'ip'};

   return \%stuff;
}

sub parse_tx_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return unless $section && $section->{'fulltext'};
   my $fulltext = $section->{'fulltext'};
   $section->{'transactions'} = [];

   # Handle the individual transactions
   my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs;
   foreach my $txn ( @transactions ) {
      my $stuff = parse_tx_text( $txn, $complete, $debug, $full );
      delete $stuff->{'fulltext'} unless $debug;
      push @{$section->{'transactions'}}, $stuff;
   }

   # Handle the general info
   @{$section}{ 'trx_id_counter' }
      = $fulltext =~ m/^Trx id counter $t$/m;
   @{$section}{ 'purge_done_for', 'purge_undo_for' }
      = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m;
   @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions
      = $fulltext =~ m/^History list length $d$/m;
   @{$section}{ 'num_lock_structs' }
      = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m;
   @{$section}{ 'is_truncated' }
      = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0;

   # Fill in things that might not be present
   foreach ( qw(history_list_len) ) {
      $section->{$_} ||= 0;
   }

   delete $section->{'fulltext'} unless $debug;
   return 1;
}

# I've read the source for this section.
sub parse_ro_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return unless $section && $section->{'fulltext'};
   my $fulltext = $section->{'fulltext'};

   # Grab the info
   @{$section}{ 'queries_inside', 'queries_in_queue' }
      = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m;
   ( $section->{ 'read_views_open' } )
      = $fulltext =~ m/^$d read views open inside InnoDB$/m;
   ( $section->{ 'n_reserved_extents' } )
      = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m;
   @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' }
      = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m;
   @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' }
      = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m;
   @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' }
      = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m;
   $section->{'main_thread_proc_no'} ||= 0;

   map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents);
   delete $section->{'fulltext'} unless $debug;
   return 1;
}

sub parse_lg_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return unless $section;
   my $fulltext = $section->{'fulltext'};

   # Grab the info
   ( $section->{ 'log_seq_no' } )
      = $fulltext =~ m/Log sequence number \s*(\d.*)$/m;
   ( $section->{ 'log_flushed_to' } )
      = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m;
   ( $section->{ 'last_chkp' } )
      = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m;
   @{$section}{ 'pending_log_writes', 'pending_chkp_writes' }
      = $fulltext =~ m/$d pending log writes, $d pending chkp writes/;
   @{$section}{ 'log_ios_done', 'log_ios_s' }
      = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#;

   delete $section->{'fulltext'} unless $debug;
   return 1;
}

sub parse_ib_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return unless $section && $section->{'fulltext'};
   my $fulltext = $section->{'fulltext'};

   # Some servers will output ibuf information for tablespace 0, as though there
   # might be many tablespaces with insert buffers.  (In practice I believe
   # the source code shows there will only ever be one).  I have to parse both
   # cases here, but I assume there will only be one.
   @{$section}{ 'size', 'free_list_len', 'seg_size' }
      = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m;
   @{$section}{ 'inserts', 'merged_recs', 'merges' }
      = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m;

   @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' }
      = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m;
   @{$section}{ 'hash_searches_s', 'non_hash_searches_s' }
      = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m;

   delete $section->{'fulltext'} unless $debug;
   return 1;
}

sub parse_wait_array {
   my ( $text, $complete, $debug, $full ) = @_;
   my %result;

   @result{ qw(thread waited_at_filename waited_at_line waited_secs) }
      = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m;

   # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED,
   # there will be different text output
   if ( $text =~ m/^Mutex at/m ) {
      $result{'request_type'} = 'M';
      @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) }
         = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m;
      @result{ qw( waiters_flag )}
         = $text =~ m/^waiters flag $d$/m;
   }
   else {
      @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) }
         = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m;
      @result{ qw( writer_thread writer_lock_mode ) }
         = $text =~ m/^a writer \(thread id $d\) has reserved it in mode  (.*)$/m;
      @result{ qw( num_readers waiters_flag )}
         = $text =~ m/^number of readers $d, waiters flag $d$/m;
      @result{ qw(last_s_file_name last_s_line ) }
         = $text =~ m/Last time read locked in file $fl$/m;
      @result{ qw(last_x_file_name last_x_line ) }
         = $text =~ m/Last time write locked in file $fl$/m;
   }

   $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1;
   $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0;

   # Because there are two code paths, some things won't get set.
   map { $result{$_} ||= '' }
      qw(last_s_file_name last_x_file_name writer_lock_mode);
   map { $result{$_} ||= 0 }
      qw(num_readers lock_var last_s_line last_x_line writer_thread);

   return \%result;
}

sub parse_sm_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return 0 unless $section && $section->{'fulltext'};
   my $fulltext = $section->{'fulltext'};

   # Grab the info
   @{$section}{ 'reservation_count', 'signal_count' }
      = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m;
   @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' }
      = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m;
   @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' }
      = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m;

   # Look for info on waits.
   my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms;
   $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ];
   $section->{'wait_array_size'} = scalar(@waits);

   delete $section->{'fulltext'} unless $debug;
   return 1;
}

# I've read the source for this section.
sub parse_bp_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return unless $section && $section->{'fulltext'};
   my $fulltext = $section->{'fulltext'};

   # Grab the info
   @{$section}{ 'total_mem_alloc', 'add_pool_alloc' }
      = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m;
   @{$section}{'dict_mem_alloc'}     = $fulltext =~ m/Dictionary memory allocated $d/;
   @{$section}{'awe_mem_alloc'}      = $fulltext =~ m/$d MB of AWE memory/;
   @{$section}{'buf_pool_size'}      = $fulltext =~ m/^Buffer pool size\s*$d$/m;
   @{$section}{'buf_free'}           = $fulltext =~ m/^Free buffers\s*$d$/m;
   @{$section}{'pages_total'}        = $fulltext =~ m/^Database pages\s*$d$/m;
   @{$section}{'pages_modified'}     = $fulltext =~ m/^Modified db pages\s*$d$/m;
   @{$section}{'pages_read', 'pages_created', 'pages_written'}
      = $fulltext =~ m/^Pages read $d, created $d, written $d$/m;
   @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'}
      = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m;
   @{$section}{'buf_pool_hits', 'buf_pool_reads'}
      = $fulltext =~ m{Buffer pool hit rate $d / $d$}m;
   if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) {
      @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0);
      @{$section}{'buf_pool_hit_rate'} = '--';
   }
   else {
      @{$section}{'buf_pool_hit_rate'}
         = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m;
   }
   @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m;
   @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' }
      = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m;

   map { $section->{$_} ||= 0 }
      qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page
      awe_mem_alloc dict_mem_alloc);
   @{$section}{'writes_pending'} = List::Util::sum(
      @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) });

   delete $section->{'fulltext'} unless $debug;
   return 1;
}

# I've read the source for this.
sub parse_io_section {
   my ( $section, $complete, $debug, $full ) = @_;
   return unless $section && $section->{'fulltext'};
   my $fulltext = $section->{'fulltext'};
   $section->{'threads'} = {};

   # Grab the I/O thread info
   my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm;
   foreach my $thread (@threads) {
      my ( $tid, $state, $purpose, $event_set )
         = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m;
      if ( defined $tid ) {
         $section->{'threads'}->{$tid} = {
            thread    => $tid,
            state     => $state,
            purpose   => $purpose,
            event_set => $event_set ? 1 : 0,
         };
      }
   }

   # Grab the reads/writes/flushes info
   @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' }
      = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m;
   @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' }
      = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m;
   @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' }
      = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m;
   @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' }
      = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m;
   @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' }
      = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m;
   @{$section}{ 'pending_preads', 'pending_pwrites' }
      = $fulltext =~ m/$d pending preads, $d pending pwrites$/m;
   @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0)
      unless defined($section->{'pending_preads'});

   delete $section->{'fulltext'} unless $debug;
   return 1;
}

sub _debug {
   my ( $debug, $msg ) = @_;
   if ( $debug ) {
      die $msg;
   }
   else {
      warn $msg;
   }
   return 1;
}

1;

# end_of_package InnoDBParser

package main;

use sigtrap qw(handler finish untrapped normal-signals);

use Data::Dumper;
use DBI;
use English qw(-no_match_vars);
use File::Basename qw(dirname);
use File::Temp;
use Getopt::Long;
use List::Util qw(max min maxstr sum);
use POSIX qw(ceil);
use Time::HiRes qw(time sleep);
use Term::ReadKey qw(ReadMode ReadKey);

# License and warranty information. {{{1
# ###########################################################################

my $innotop_license = <<"LICENSE";

This is innotop version $VERSION, a MySQL and InnoDB monitor.

This program is copyright (c) 2006 Baron Schwartz.
Feedback and improvements are welcome.

THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, version 2; OR the Perl Artistic License.  On UNIX and similar
systems, you can issue `man perlgpl' or `man perlartistic' to read these
licenses.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA  02111-1307  USA.
LICENSE

# Configuration information and global setup {{{1
# ###########################################################################

# Really, really, super-global variables.
my @config_versions = (
   "000-000-000", "001-003-000", # config file was one big name-value hash.
   "001-003-000", "001-004-002", # config file contained non-user-defined stuff.
);

my $clear_screen_sub;
my $dsn_parser = new DSNParser();

# This defines expected properties and defaults for the column definitions that
# eventually end up in tbl_meta.
my %col_props = (
   hdr     => '',
   just    => '-',
   dec     => 0,     # Whether to align the column on the decimal point
   num     => 0,
   label   => '',
   user    => 0,
   src     => '',
   tbl     => '',    # Helps when writing/reading custom columns in config files
   minw    => 0,
   maxw    => 0,
   trans   => [],
   agg     => 'first',  # Aggregate function
   aggonly => 0,        # Whether to show only when tbl_meta->{aggregate} is true
);

# Actual DBI connections to MySQL servers.
my %dbhs;

# Command-line parameters {{{2
# ###########################################################################

my @opt_spec = (
   { s => 'help',       d => 'Show this help message' },
   { s => 'color|C!',   d => 'Use terminal coloring (default)',   c => 'color' },
   { s => 'config|c=s', d => 'Config file to read' },
   { s => 'nonint|n',   d => 'Non-interactive, output tab-separated fields' },
   { s => 'count=i',    d => 'Number of updates before exiting' },
   { s => 'delay|d=f',  d => 'Delay between updates in seconds',  c => 'interval' },
   { s => 'mode|m=s',   d => 'Operating mode to start in',        c => 'mode' },
   { s => 'inc|i!',     d => 'Measure incremental differences',   c => 'status_inc' },
   { s => 'write|w',    d => 'Write running configuration into home directory if no config files were loaded' },
   { s => 'skipcentral|s',     d => 'Skip reading the central configuration file' },
   { s => 'version',    d => 'Output version information and exit' },
   { s => 'user|u=s',   d => 'User for login if not current user' },
   { s => 'password|p=s',   d => 'Password to use for connection' },
   { s => 'host|h=s',   d => 'Connect to host' },
   { s => 'port|P=i',   d => 'Port number to use for connection' },
);

# This is the container for the command-line options' values to be stored in
# after processing.  Initial values are defaults.
my %opts = (
   n => !( -t STDIN && -t STDOUT ), # If in/out aren't to terminals, we're interactive
);
# Post-process...
my %opt_seen;
foreach my $spec ( @opt_spec ) {
   my ( $long, $short ) = $spec->{s} =~ m/^(\w+)(?:\|([^!+=]*))?/;
   $spec->{k} = $short || $long;
   $spec->{l} = $long;
   $spec->{t} = $short;
   $spec->{n} = $spec->{s} =~ m/!/;
   $opts{$spec->{k}} = undef unless defined $opts{$spec->{k}};
   die "Duplicate option $spec->{k}" if $opt_seen{$spec->{k}}++;
}

Getopt::Long::Configure('no_ignore_case', 'bundling');
GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1;

if ( $opts{version} ) {
   print "innotop  Ver $VERSION\n";
   exit(0);
}

if ( $opts{c} and ! -f $opts{c} ) {
   print $opts{c} . " doesn't exist.  Exiting.\n";
   exit(1);
}
if ( $opts{'help'} ) {
   print "Usage: innotop <options> <innodb-status-file>\n\n";
   my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec);
   foreach my $spec ( sort { $a->{l} cmp $b->{l} } @opt_spec ) {
      my $long  = $spec->{n} ? "[no]$spec->{l}" : $spec->{l};
      my $short = $spec->{t} ? "-$spec->{t}" : '';
      printf("  --%-${maxw}s %-4s %s\n", $long, $short, $spec->{d});
   }
   print <<USAGE;

innotop is a MySQL and InnoDB transaction/status monitor, like 'top' for
MySQL.  It displays queries, InnoDB transactions, lock waits, deadlocks,
foreign key errors, open tables, replication status, buffer information,
row operations, logs, I/O operations, load graph, and more.  You can
monitor many servers at once with innotop. 

USAGE
   exit(1);
}

# Meta-data (table definitions etc) {{{2
# ###########################################################################

# Expressions {{{3
# Convenience so I can copy/paste these in several places...
# ###########################################################################
my %exprs = (
   Host              => q{my $host = host || hostname || ''; ($host) = $host =~ m/^((?:[\d.]+(?=:))|(?:[a-zA-Z]\w+))/; return $host || ''},
   Port              => q{my ($p) = host =~ m/:(.*)$/; return $p || 0},
   OldVersions       => q{dulint_to_int(IB_tx_trx_id_counter) - dulint_to_int(IB_tx_purge_done_for)},
   MaxTxnTime        => q/max(map{ $_->{active_secs} } @{ IB_tx_transactions }) || 0/,
   NumTxns           => q{scalar @{ IB_tx_transactions } },
   DirtyBufs         => q{ $cur->{IB_bp_pages_modified} / ($cur->{IB_bp_buf_pool_size} || 1) },
   BufPoolFill       => q{ $cur->{IB_bp_pages_total} / ($cur->{IB_bp_buf_pool_size} || 1) },
   ServerLoad        => q{ $cur->{Threads_connected}/(Questions||1)/Uptime_hires },
   TxnTimeRemain     => q{ defined undo_log_entries && defined $pre->{undo_log_entries} && undo_log_entries < $pre->{undo_log_entries} ? undo_log_entries / (($pre->{undo_log_entries} - undo_log_entries)/((active_secs-$pre->{active_secs})||1))||1 : 0},
   SlaveCatchupRate  => ' defined $cur->{seconds_behind_master} && defined $pre->{seconds_behind_master} && $cur->{seconds_behind_master} < $pre->{seconds_behind_master} ? ($pre->{seconds_behind_master}-$cur->{seconds_behind_master})/($cur->{Uptime_hires}-$pre->{Uptime_hires}) : 0',
   QcacheHitRatio    => q{(Qcache_hits||0)/(((Com_select||0)+(Qcache_hits||0))||1)},
);

# ###########################################################################
# Column definitions {{{3
# Defines every column in every table. A named column has the following
# properties:
#    * hdr    Column header/title
#    * label  Documentation for humans.
#    * num    Whether it's numeric (for sorting).
#    * just   Alignment; generated from num, user-overridable in tbl_meta
#    * minw, maxw Auto-generated, user-overridable.
# Values from this hash are just copied to tbl_meta, which is where everything
# else in the program should read from.
# ###########################################################################

my %columns = (
   active_secs                 => { hdr => 'SecsActive',          num => 1, label => 'Seconds transaction has been active', },
   add_pool_alloc              => { hdr => 'Add\'l Pool',         num => 1, label => 'Additonal pool allocated' },
   attempted_op                => { hdr => 'Action',              num => 0, label => 'The action that caused the error' },
   awe_mem_alloc               => { hdr => 'AWE Memory',          num => 1, label => '[Windows] AWE memory allocated' },
   binlog_cache_overflow       => { hdr => 'Binlog Cache',        num => 1, label => 'Transactions too big for binlog cache that went to disk' },
   binlog_do_db                => { hdr => 'Binlog Do DB',        num => 0, label => 'binlog-do-db setting' },
   binlog_ignore_db            => { hdr => 'Binlog Ignore DB',    num => 0, label => 'binlog-ignore-db setting' },
   bps_in                      => { hdr => 'BpsIn',               num => 1, label => 'Bytes per second received by the server', },
   bps_out                     => { hdr => 'BpsOut',              num => 1, label => 'Bytes per second sent by the server', },
   buf_free                    => { hdr => 'Free Bufs',           num => 1, label => 'Buffers free in the buffer pool' },
   buf_pool_hit_rate           => { hdr => 'Hit Rate',            num => 0, label => 'Buffer pool hit rate' },
   buf_pool_hits               => { hdr => 'Hits',                num => 1, label => 'Buffer pool hits' },
   buf_pool_reads              => { hdr => 'Reads',               num => 1, label => 'Buffer pool reads' },
   buf_pool_size               => { hdr => 'Size',                num => 1, label => 'Buffer pool size' },
   bufs_in_node_heap           => { hdr => 'Node Heap Bufs',      num => 1, label => 'Buffers in buffer pool node heap' },
   bytes_behind_master         => { hdr => 'ByteLag',             num => 1, label => 'Bytes the slave lags the master in binlog' },
   cell_event_set              => { hdr => 'Ending?',             num => 1, label => 'Whether the cell event is set' },
   cell_waiting                => { hdr => 'Waiting?',            num => 1, label => 'Whether the cell is waiting' },
   child_db                    => { hdr => 'Child DB',            num => 0, label => 'The database of the child table' },
   child_index                 => { hdr => 'Child Index',         num => 0, label => 'The index in the child table' },
   child_table                 => { hdr => 'Child Table',         num => 0, label => 'The child table' },
   cmd                         => { hdr => 'Cmd',                 num => 0, label => 'Type of command being executed', },
   cnt                         => { hdr => 'Cnt',                 num => 0, label => 'Count', agg => 'count', aggonly => 1 },
   connect_retry               => { hdr => 'Connect Retry',       num => 1, label => 'Slave connect-retry timeout' },
   cxn                         => { hdr => 'CXN',                 num => 0, label => 'Connection from which the data came', },
   db                          => { hdr => 'DB',                  num => 0, label => 'Current database', },
   dict_mem_alloc              => { hdr => 'Dict Mem',            num => 1, label => 'Dictionary memory allocated' },
   dirty_bufs                  => { hdr => 'Dirty Buf',           num => 1, label => 'Dirty buffer pool pages' },
   dl_txn_num                  => { hdr => 'Num',                 num => 0, label => 'Deadlocked transaction number', },
   event_set                   => { hdr => 'Evt Set?',            num => 1, label => '[Win32] if a wait event is set', },
   exec_master_log_pos         => { hdr => 'Exec Master Log Pos', num => 1, label => 'Exec Master Log Position' },
   fk_name                     => { hdr => 'Constraint',          num => 0, label => 'The name of the FK constraint' },
   free_list_len               => { hdr => 'Free List Len',       num => 1, label => 'Length of the free list' },
   has_read_view               => { hdr => 'Rd View',             num => 1, label => 'Whether the transaction has a read view' },
   hash_searches_s             => { hdr => 'Hash/Sec',            num => 1, label => 'Number of hash searches/sec' },
   hash_table_size             => { hdr => 'Size',                num => 1, label => 'Number of non-hash searches/sec' },
   heap_no                     => { hdr => 'Heap',                num => 1, label => 'Heap number' },
   heap_size                   => { hdr => 'Heap',                num => 1, label => 'Heap size' },
   history_list_len            => { hdr => 'History',             num => 1, label => 'History list length' },
   host_and_domain             => { hdr => 'Host',                num => 0, label => 'Hostname/IP and domain' },
   host_and_port               => { hdr => 'Host/IP',             num => 0, label => 'Hostname or IP address, and port number', },
   hostname                    => { hdr => 'Host',                num => 0, label => 'Hostname' },
   index                       => { hdr => 'Index',               num => 0, label => 'The index involved' },
   index_ref                   => { hdr => 'Index Ref',           num => 0, label => 'Index referenced' },
   info                        => { hdr => 'Query',               num => 0, label => 'Info or the current query', },
   insert_intention            => { hdr => 'Ins Intent',          num => 1, label => 'Whether the thread was trying to insert' },
   inserts                     => { hdr => 'Inserts',             num => 1, label => 'Inserts' },
   io_bytes_s                  => { hdr => 'Bytes/Sec',           num => 1, label => 'Average I/O bytes/sec' },
   io_flush_type               => { hdr => 'Flush Type',          num => 0, label => 'I/O Flush Type' },
   io_fsyncs_s                 => { hdr => 'fsyncs/sec',          num => 1, label => 'I/O fsyncs/sec' },
   io_reads_s                  => { hdr => 'Reads/Sec',           num => 1, label => 'Average I/O reads/sec' },
   io_writes_s                 => { hdr => 'Writes/Sec',          num => 1, label => 'Average I/O writes/sec' },
   ip                          => { hdr => 'IP',                  num => 0, label => 'IP address' },
   is_name_locked              => { hdr => 'Locked',              num => 1, label => 'Whether table is name locked', },
   key_buffer_hit              => { hdr => 'KCacheHit',           num => 1, label => 'Key cache hit ratio', },
   key_len                     => { hdr => 'Key Length',          num => 1, label => 'Number of bytes used in the key' },
   last_chkp                   => { hdr => 'Last Checkpoint',     num => 0, label => 'Last log checkpoint' },
   last_errno                  => { hdr => 'Last Errno',          num => 1, label => 'Last error number' },
   last_error                  => { hdr => 'Last Error',          num => 0, label => 'Last error' },
   last_s_file_name            => { hdr => 'S-File',              num => 0, label => 'Filename where last read locked' },
   last_s_line                 => { hdr => 'S-Line',              num => 1, label => 'Line where last read locked' },
   last_x_file_name            => { hdr => 'X-File',              num => 0, label => 'Filename where last write locked' },
   last_x_line                 => { hdr => 'X-Line',              num => 1, label => 'Line where last write locked' },
   last_pct                    => { hdr => 'Pct',                 num => 1, label => 'Last Percentage' },
   last_total                  => { hdr => 'Last Total',          num => 1, label => 'Last Total' },
   last_value                  => { hdr => 'Last Incr',           num => 1, label => 'Last Value' },
   load                        => { hdr => 'Load',                num => 1, label => 'Server load' },
   lock_cfile_name             => { hdr => 'Crtd File',           num => 0, label => 'Filename where lock created' },
   lock_cline                  => { hdr => 'Crtd Line',           num => 1, label => 'Line where lock created' },
   lock_mem_addr               => { hdr => 'Addr',                num => 0, label => 'The lock memory address' },
   lock_mode                   => { hdr => 'Mode',                num => 0, label => 'The lock mode' },
   lock_structs                => { hdr => 'LStrcts',             num => 1, label => 'Number of lock structs' },
   lock_type                   => { hdr => 'Type',                num => 0, label => 'The lock type' },
   lock_var                    => { hdr => 'Lck Var',             num => 1, label => 'The lock variable' },
   lock_wait_time              => { hdr => 'Wait',                num => 1, label => 'How long txn has waited for a lock' },
   log_flushed_to              => { hdr => 'Flushed To',          num => 0, label => 'Log position flushed to' },
   log_ios_done                => { hdr => 'IO Done',             num => 1, label => 'Log I/Os done' },
   log_ios_s                   => { hdr => 'IO/Sec',              num => 1, label => 'Average log I/Os per sec' },
   log_seq_no                  => { hdr => 'Sequence No.',        num => 0, label => 'Log sequence number' },
   main_thread_id              => { hdr => 'Main Thread ID',      num => 1, label => 'Main thread ID' },
   main_thread_proc_no         => { hdr => 'Main Thread Proc',    num => 1, label => 'Main thread process number' },
   main_thread_state           => { hdr => 'Main Thread State',   num => 0, label => 'Main thread state' },
   master_file                 => { hdr => 'File',                num => 0, label => 'Master file' },
   master_host                 => { hdr => 'Master',              num => 0, label => 'Master server hostname' },
   master_log_file             => { hdr => 'Master Log File',     num => 0, label => 'Master log file' },
   master_port                 => { hdr => 'Master Port',         num => 1, label => 'Master port' },
   master_pos                  => { hdr => 'Position',            num => 1, label => 'Master position' },
   master_ssl_allowed          => { hdr => 'Master SSL Allowed',  num => 0, label => 'Master SSL Allowed' },
   master_ssl_ca_file          => { hdr => 'Master SSL CA File',  num => 0, label => 'Master SSL Cert Auth File' },
   master_ssl_ca_path          => { hdr => 'Master SSL CA Path',  num => 0, label => 'Master SSL Cert Auth Path' },
   master_ssl_cert             => { hdr => 'Master SSL Cert',     num => 0, label => 'Master SSL Cert' },
   master_ssl_cipher           => { hdr => 'Master SSL Cipher',   num => 0, label => 'Master SSL Cipher' },
   master_ssl_key              => { hdr => 'Master SSL Key',      num => 0, label => 'Master SSL Key' },
   master_user                 => { hdr => 'Master User',         num => 0, label => 'Master username' },
   max_txn                     => { hdr => 'MaxTxnTime',          num => 1, label => 'MaxTxn' },
   merged_recs                 => { hdr => 'Merged Recs',         num => 1, label => 'Merged records' },
   merges                      => { hdr => 'Merges',              num => 1, label => 'Merges' },
   mutex_os_waits              => { hdr => 'Waits',               num => 1, label => 'Mutex OS Waits' },
   mutex_spin_rounds           => { hdr => 'Rounds',              num => 1, label => 'Mutex Spin Rounds' },
   mutex_spin_waits            => { hdr => 'Spins',               num => 1, label => 'Mutex Spin Waits' },
   mysql_thread_id             => { hdr => 'ID',                  num => 1, label => 'MySQL connection (thread) ID', },
   name                        => { hdr => 'Name',                num => 0, label => 'Variable Name' },
   n_bits                      => { hdr => '# Bits',              num => 1, label => 'Number of bits' },
   non_hash_searches_s         => { hdr => 'Non-Hash/Sec',        num => 1, label => 'Non-hash searches/sec' },
   num_deletes                 => { hdr => 'Del',                 num => 1, label => 'Number of deletes' },
   num_deletes_sec             => { hdr => 'Del/Sec',             num => 1, label => 'Number of deletes' },
   num_inserts                 => { hdr => 'Ins',                 num => 1, label => 'Number of inserts' },
   num_inserts_sec             => { hdr => 'Ins/Sec',             num => 1, label => 'Number of inserts' },
   num_readers                 => { hdr => 'Readers',             num => 1, label => 'Number of readers' },
   num_reads                   => { hdr => 'Read',                num => 1, label => 'Number of reads' },
   num_reads_sec               => { hdr => 'Read/Sec',            num => 1, label => 'Number of reads' },
   num_res_ext                 => { hdr => 'BTree Extents',       num => 1, label => 'Number of extents reserved for B-Tree' },
   num_rows                    => { hdr => 'Row Count',           num => 1, label => 'Number of rows estimated to examine' },
   num_times_open              => { hdr => 'In Use',              num => 1, label => '# times table is opened', },
   num_txns                    => { hdr => 'Txns',                num => 1, label => 'Number of transactions' },
   num_updates                 => { hdr => 'Upd',                 num => 1, label => 'Number of updates' },
   num_updates_sec             => { hdr => 'Upd/Sec',             num => 1, label => 'Number of updates' },
   os_file_reads               => { hdr => 'OS Reads',            num => 1, label => 'OS file reads' },
   os_file_writes              => { hdr => 'OS Writes',           num => 1, label => 'OS file writes' },
   os_fsyncs                   => { hdr => 'OS fsyncs',           num => 1, label => 'OS fsyncs' },
   os_thread_id                => { hdr => 'OS Thread',           num => 1, label => 'The operating system thread ID' },
   p_aio_writes                => { hdr => 'Async Wrt',           num => 1, label => 'Pending asynchronous I/O writes' },
   p_buf_pool_flushes          => { hdr => 'Buffer Pool Flushes', num => 1, label => 'Pending buffer pool flushes' },
   p_ibuf_aio_reads            => { hdr => 'IBuf Async Rds',      num => 1, label => 'Pending insert buffer asynch I/O reads' },
   p_log_flushes               => { hdr => 'Log Flushes',         num => 1, label => 'Pending log flushes' },
   p_log_ios                   => { hdr => 'Log I/Os',            num => 1, label => 'Pending log I/O operations' },
   p_normal_aio_reads          => { hdr => 'Async Rds',           num => 1, label => 'Pending asynchronous I/O reads' },
   p_preads                    => { hdr => 'preads',              num => 1, label => 'Pending p-reads' },
   p_pwrites                   => { hdr => 'pwrites',             num => 1, label => 'Pending p-writes' },
   p_sync_ios                  => { hdr => 'Sync I/Os',           num => 1, label => 'Pending synchronous I/O operations' },
   page_creates_sec            => { hdr => 'Creates/Sec',         num => 1, label => 'Page creates/sec' },
   page_no                     => { hdr => 'Page',                num => 1, label => 'Page number' },
   page_reads_sec              => { hdr => 'Reads/Sec',           num => 1, label => 'Page reads per second' },
   page_writes_sec             => { hdr => 'Writes/Sec',          num => 1, label => 'Page writes per second' },
   pages_created               => { hdr => 'Created',             num => 1, label => 'Pages created' },
   pages_modified              => { hdr => 'Dirty Pages',         num => 1, label => 'Pages modified (dirty)' },
   pages_read                  => { hdr => 'Reads',               num => 1, label => 'Pages read' },
   pages_total                 => { hdr => 'Pages',               num => 1, label => 'Pages total' },
   pages_written               => { hdr => 'Writes',              num => 1, label => 'Pages written' },
   parent_col                  => { hdr => 'Parent Column',       num => 0, label => 'The referred column in the parent table', },
   parent_db                   => { hdr => 'Parent DB',           num => 0, label => 'The database of the parent table' },
   parent_index                => { hdr => 'Parent Index',        num => 0, label => 'The referred index in the parent table' },
   parent_table                => { hdr => 'Parent Table',        num => 0, label => 'The parent table' },
   part_id                     => { hdr => 'Part ID',             num => 1, label => 'Sub-part ID of the query' },
   partitions                  => { hdr => 'Partitions',          num => 0, label => 'Query partitions used' },
   pct                         => { hdr => 'Pct',                 num => 1, label => 'Percentage' },
   pending_chkp_writes         => { hdr => 'Chkpt Writes',        num => 1, label => 'Pending log checkpoint writes' },
   pending_log_writes          => { hdr => 'Log Writes',          num => 1, label => 'Pending log writes' },
   port                        => { hdr => 'Port',                num => 1, label => 'Client port number', },
   possible_keys               => { hdr => 'Poss. Keys',          num => 0, label => 'Possible keys' },
   proc_no                     => { hdr => 'Proc',                num => 1, label => 'Process number' },
   q_cache_hit                 => { hdr => 'QCacheHit',           num => 1, label => 'Query cache hit ratio', },
   qps                         => { hdr => 'QPS',                 num => 1, label => 'How many queries/sec', },
   queries_in_queue            => { hdr => 'Queries Queued',      num => 1, label => 'Queries in queue' },
   queries_inside              => { hdr => 'Queries Inside',      num => 1, label => 'Queries inside InnoDB' },
   query_id                    => { hdr => 'Query ID',            num => 1, label => 'Query ID' },
   query_status                => { hdr => 'Query Status',        num => 0, label => 'The query status' },
   query_text                  => { hdr => 'Query Text',          num => 0, label => 'The query text' },
   questions                   => { hdr => 'Questions',           num => 1, label => 'How many queries the server has gotten', },
   read_master_log_pos         => { hdr => 'Read Master Pos',     num => 1, label => 'Read master log position' },
   read_views_open             => { hdr => 'Rd Views',            num => 1, label => 'Number of read views open' },
   reads_pending               => { hdr => 'Pending Reads',       num => 1, label => 'Reads pending' },
   relay_log_file              => { hdr => 'Relay File',          num => 0, label => 'Relay log file' },
   relay_log_pos               => { hdr => 'Relay Pos',           num => 1, label => 'Relay log position' },
   relay_log_size              => { hdr => 'Relay Size',          num => 1, label => 'Relay log size' },
   relay_master_log_file       => { hdr => 'Relay Master File',   num => 0, label => 'Relay master log file' },
   replicate_do_db             => { hdr => 'Do DB',               num => 0, label => 'Replicate-do-db setting' },
   replicate_do_table          => { hdr => 'Do Table',            num => 0, label => 'Replicate-do-table setting' },
   replicate_ignore_db         => { hdr => 'Ignore DB',           num => 0, label => 'Replicate-ignore-db setting' },
   replicate_ignore_table      => { hdr => 'Ignore Table',        num => 0, label => 'Replicate-do-table setting' },
   replicate_wild_do_table     => { hdr => 'Wild Do Table',       num => 0, label => 'Replicate-wild-do-table setting' },
   replicate_wild_ignore_table => { hdr => 'Wild Ignore Table',   num => 0, label => 'Replicate-wild-ignore-table setting' },
   request_type                => { hdr => 'Type',                num => 0, label => 'Type of lock the thread waits for' },
   reservation_count           => { hdr => 'ResCnt',              num => 1, label => 'Reservation Count' },
   row_locks                   => { hdr => 'RLocks',              num => 1, label => 'Number of row locks' },
   rw_excl_os_waits            => { hdr => 'RW Waits',            num => 1, label => 'R/W Excl. OS Waits' },
   rw_excl_spins               => { hdr => 'RW Spins',            num => 1, label => 'R/W Excl. Spins' },
   rw_shared_os_waits          => { hdr => 'Sh Waits',            num => 1, label => 'R/W Shared OS Waits' },
   rw_shared_spins             => { hdr => 'Sh Spins',            num => 1, label => 'R/W Shared Spins' },
   scan_type                   => { hdr => 'Type',                num => 0, label => 'Scan type in chosen' },
   seg_size                    => { hdr => 'Seg. Size',           num => 1, label => 'Segment size' },
   select_type                 => { hdr => 'Select Type',         num => 0, label => 'Type of select used' },
   signal_count                => { hdr => 'Signals',             num => 1, label => 'Signal Count' },
   size                        => { hdr => 'Size',                num => 1, label => 'Size of the tablespace' },
   skip_counter                => { hdr => 'Skip Counter',        num => 1, label => 'Skip counter' },
   slave_catchup_rate          => { hdr => 'Catchup',             num => 1, label => 'How fast the slave is catching up in the binlog' },
   slave_io_running            => { hdr => 'Slave-IO',            num => 0, label => 'Whether the slave I/O thread is running' },
   slave_io_state              => { hdr => 'Slave IO State',      num => 0, label => 'Slave I/O thread state' },
   slave_open_temp_tables      => { hdr => 'Temp',                num => 1, label => 'Slave open temp tables' },
   slave_sql_running           => { hdr => 'Slave-SQL',           num => 0, label => 'Whether the slave SQL thread is running' },
   slow                        => { hdr => 'Slow',                num => 1, label => 'How many slow queries', },
   space_id                    => { hdr => 'Space',               num => 1, label => 'Tablespace ID' },
   special                     => { hdr => 'Special',             num => 0, label => 'Special/Other info' },
   state                       => { hdr => 'State',               num => 0, label => 'Connection state', maxw => 18, },
   tables_in_use               => { hdr => 'Tbl Used',            num => 1, label => 'Number of tables in use' },
   tables_locked               => { hdr => 'Tbl Lck',             num => 1, label => 'Number of tables locked' },
   tbl                         => { hdr => 'Table',               num => 0, label => 'Table', },
   thread                      => { hdr => 'Thread',              num => 1, label => 'Thread number' },
   thread_decl_inside          => { hdr => 'Thread Inside',       num => 0, label => 'What the thread is declared inside' },
   thread_purpose              => { hdr => 'Purpose',             num => 0, label => "The thread's purpose" },
   thread_status               => { hdr => 'Thread Status',       num => 0, label => 'The thread status' },
   time                        => { hdr => 'Time',                num => 1, label => 'Time since the last event', },
   time_behind_master          => { hdr => 'TimeLag',             num => 1, label => 'Time slave lags master' },
   timestring                  => { hdr => 'Timestring',          num => 0, label => 'Time the event occurred' },
   total                       => { hdr => 'Total',               num => 1, label => 'Total' },
   total_mem_alloc             => { hdr => 'Memory',              num => 1, label => 'Total memory allocated' },
   truncates                   => { hdr => 'Trunc',               num => 0, label => 'Whether the deadlock is truncating InnoDB status' },
   txn_doesnt_see_ge           => { hdr => "Txn Won't See",       num => 0, label => 'Where txn read view is limited' },
   txn_id                      => { hdr => 'ID',                  num => 0, label => 'Transaction ID' },
   txn_sees_lt                 => { hdr => 'Txn Sees',            num => 1, label => 'Where txn read view is limited' },
   txn_status                  => { hdr => 'Txn Status',          num => 0, label => 'Transaction status' },
   txn_time_remain             => { hdr => 'Remaining',           num => 1, label => 'Time until txn rollback/commit completes' },
   undo_log_entries            => { hdr => 'Undo',                num => 1, label => 'Number of undo log entries' },
   undo_for                    => { hdr => 'Undo',                num => 0, label => 'Undo for' },
   until_condition             => { hdr => 'Until Condition',     num => 0, label => 'Slave until condition' },
   until_log_file              => { hdr => 'Until Log File',      num => 0, label => 'Slave until log file' },
   until_log_pos               => { hdr => 'Until Log Pos',       num => 1, label => 'Slave until log position' },
   used_cells                  => { hdr => 'Cells Used',          num => 1, label => 'Number of cells used' },
   used_bufs                   => { hdr => 'Used Bufs',           num => 1, label => 'Number of buffer pool pages used' },
   user                        => { hdr => 'User',                num => 0, label => 'Database username', },
   value                       => { hdr => 'Value',               num => 1, label => 'Value' },
   versions                    => { hdr => 'Versions',            num => 1, label => 'Number of InnoDB MVCC versions unpurged' },
   victim                      => { hdr => 'Victim',              num => 0, label => 'Whether this txn was the deadlock victim' },
   wait_array_size             => { hdr => 'Wait Array Size',     num => 1, label => 'Wait Array Size' },
   wait_status                 => { hdr => 'Lock Status',         num => 0, label => 'Status of txn locks' },
   waited_at_filename          => { hdr => 'File',                num => 0, label => 'Filename at which thread waits' },
   waited_at_line              => { hdr => 'Line',                num => 1, label => 'Line at which thread waits' },
   waiters_flag                => { hdr => 'Waiters',             num => 1, label => 'Waiters Flag' },
   waiting                     => { hdr => 'Waiting',             num => 1, label => 'Whether lock is being waited for' },
   when                        => { hdr => 'When',                num => 0, label => 'Time scale' },
   writer_lock_mode            => { hdr => 'Wrtr Lck Mode',       num => 0, label => 'Writer lock mode' },
   writer_thread               => { hdr => 'Wrtr Thread',         num => 1, label => 'Writer thread ID' },
   writes_pending              => { hdr => 'Writes',              num => 1, label => 'Number of writes pending' },
   writes_pending_flush_list   => { hdr => 'Flush List Writes',   num => 1, label => 'Number of flush list writes pending' },
   writes_pending_lru          => { hdr => 'LRU Writes',          num => 1, label => 'Number of LRU writes pending' },
   writes_pending_single_page  => { hdr => '1-Page Writes',       num => 1, label => 'Number of 1-page writes pending' },
);

# Apply a default property or three.  By default, columns are not width-constrained,
# aligned left, and sorted alphabetically, not numerically.
foreach my $col ( values %columns ) {
   map { $col->{$_} ||= 0 } qw(num minw maxw);
   $col->{just} = $col->{num} ? '' : '-';
}

# Filters {{{3
# This hash defines every filter that can be applied to a table.  These
# become part of tbl_meta as well.  Each filter is just an expression that
# returns true or false.
# Properties of each entry:
#  * func:   the subroutine
#  * name:   the name, repeated
#  * user:   whether it's a user-defined filter (saved in config)
#  * text:   text of the subroutine
#  * note:   explanation
my %filters = ();

# These are pre-processed to live in %filters above, by compiling them.
my %builtin_filters = (
   hide_self => {
      text => <<'      END',
         return ( !$set->{info} || $set->{info} ne 'SHOW FULL PROCESSLIST' )
             && ( !$set->{query_text}    || $set->{query_text} !~ m/INNODB STATUS$/ );
      END
      note => 'Removes the innotop processes from the list',
      tbls => [qw(innodb_transactions processlist)],
   },
   hide_inactive => {
      text => <<'      END',
         return ( !defined($set->{txn_status}) || $set->{txn_status} ne 'not started' )
             && ( !defined($set->{cmd})        || $set->{cmd} !~ m/Sleep|Binlog Dump/ )
             && ( !defined($set->{info})       || $set->{info} =~ m/\S/               );
      END
      note => 'Removes processes which are not doing anything',
      tbls => [qw(innodb_transactions processlist)],
   },
   hide_slave_io => {
      text => <<'      END',
         return !$set->{state} || $set->{state} !~ m/^(?:Waiting for master|Has read all relay)/;
      END
      note => 'Removes slave I/O threads from the list',
      tbls => [qw(processlist slave_io_status)],
   },
   table_is_open => {
      text => <<'      END',
         return $set->{num_times_open} + $set->{is_name_locked};
      END
      note => 'Removes tables that are not in use or locked',
      tbls => [qw(open_tables)],
   },
   cxn_is_master => {
      text => <<'      END',
         return $set->{master_file} ? 1 : 0;
      END
      note => 'Removes servers that are not masters',
      tbls => [qw(master_status)],
   },
   cxn_is_slave => {
      text => <<'      END',
         return $set->{master_host} ? 1 : 0;
      END
      note => 'Removes servers that are not slaves',
      tbls => [qw(slave_io_status slave_sql_status)],
   },
   thd_is_not_waiting => {
      text => <<'      END',
         return $set->{thread_status} !~ m#waiting for i/o request#;
      END
      note => 'Removes idle I/O threads',
      tbls => [qw(io_threads)],
   },
);
foreach my $key ( keys %builtin_filters ) {
   my ( $sub, $err ) = compile_filter($builtin_filters{$key}->{text});
   $filters{$key} = {
      func => $sub,
      text => $builtin_filters{$key}->{text},
      user => 0,
      name => $key, # useful for later
      note => $builtin_filters{$key}->{note},
      tbls => $builtin_filters{$key}->{tbls},
   }
}

# Variable sets {{{3
# Sets (arrayrefs) of variables that are used in S mode.  They are read/written to
# the config file.
my %var_sets = (
   general => {
      text => join(
         ', ',
         'set_precision(Questions/Uptime_hires) as QPS',
         'set_precision(Com_commit/Uptime_hires) as Commit_PS',
         'set_precision((Com_rollback||0)/(Com_commit||1)) as Rollback_Commit',
         'set_precision(('
            . join('+', map { "($_||0)" }
               qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
                  Com_replace_select Com_select Com_update Com_update_multi))
            . ')/(Com_commit||1)) as Write_Commit',
         'set_precision((Com_select+(Qcache_hits||0))/(('
            . join('+', map { "($_||0)" }
               qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace
                  Com_replace_select Com_select Com_update Com_update_multi))
            . ')||1)) as R_W_Ratio',
         'set_precision(Opened_tables/Uptime_hires) as Opens_PS',
         'percent($cur->{Open_tables}/($cur->{table_cache})) as Table_Cache_Used',
         'set_precision(Threads_created/Uptime_hires) as Threads_PS',
         'percent($cur->{Threads_cached}/($cur->{thread_cache_size}||1)) as Thread_Cache_Used',
         'percent($cur->{Max_used_connections}/($cur->{max_connections}||1)) as CXN_Used_Ever',
         'percent($cur->{Threads_connected}/($cur->{max_connections}||1)) as CXN_Used_Now',
      ),
   },
   commands => {
      text => join(
         ', ',
         qw(Uptime Questions Com_delete Com_delete_multi Com_insert
         Com_insert_select Com_replace Com_replace_select Com_select Com_update
         Com_update_multi)
      ),
   },
   query_status => {
      text => join(
         ',',
         qw( Uptime Select_full_join Select_full_range_join Select_range
         Select_range_check Select_scan Slow_queries Sort_merge_passes
         Sort_range Sort_rows Sort_scan)
      ),
   },
   innodb => {
      text => join(
         ',',
         qw( Uptime Innodb_row_lock_current_waits Innodb_row_lock_time
         Innodb_row_lock_time_avg Innodb_row_lock_time_max Innodb_row_lock_waits
         Innodb_rows_deleted Innodb_rows_inserted Innodb_rows_read
         Innodb_rows_updated)
      ),
   },
   txn => {
      text => join(
         ',',
         qw( Uptime Com_begin Com_commit Com_rollback Com_savepoint
         Com_xa_commit Com_xa_end Com_xa_prepare Com_xa_recover Com_xa_rollback
         Com_xa_start)
      ),
   },
   key_cache => {
      text => join(
         ',',
         qw( Uptime Key_blocks_not_flushed Key_blocks_unused Key_blocks_used
         Key_read_requests Key_reads Key_write_requests Key_writes )
      ),
   },
   query_cache => {
      text => join(
         ',',
         "percent($exprs{QcacheHitRatio}) as Hit_Pct",
         'set_precision((Qcache_hits||0)/(Qcache_inserts||1)) as Hit_Ins',
         'set_precision((Qcache_lowmem_prunes||0)/Uptime_hires) as Lowmem_Prunes_sec',
         'percent(1-((Qcache_free_blocks||0)/(Qcache_total_blocks||1))) as Blocks_used',
         qw( Qcache_free_blocks Qcache_free_memory Qcache_not_cached Qcache_queries_in_cache)
      ),
   },
   handler => {
      text => join(
         ',',
         qw( Uptime Handler_read_key Handler_read_first Handler_read_next
         Handler_read_prev Handler_read_rnd Handler_read_rnd_next Handler_delete
         Handler_update Handler_write)
      ),
   },
   cxns_files_threads => {
      text => join(
         ',',
         qw( Uptime Aborted_clients Aborted_connects Bytes_received Bytes_sent
         Compression Connections Created_tmp_disk_tables Created_tmp_files
         Created_tmp_tables Max_used_connections Open_files Open_streams
         Open_tables Opened_tables Table_locks_immediate Table_locks_waited
         Threads_cached Threads_connected Threads_created Threads_running)
      ),
   },
   prep_stmt => {
      text => join(
         ',',
         qw( Uptime Com_dealloc_sql Com_execute_sql Com_prepare_sql Com_reset
         Com_stmt_close Com_stmt_execute Com_stmt_fetch Com_stmt_prepare
         Com_stmt_reset Com_stmt_send_long_data )
      ),
   },
   innodb_health => {
      text => join(
         ',',
         "$exprs{OldVersions} as OldVersions",
         qw(IB_sm_mutex_spin_waits IB_sm_mutex_spin_rounds IB_sm_mutex_os_waits),
         "$exprs{NumTxns} as NumTxns",
         "$exprs{MaxTxnTime} as MaxTxnTime",
         qw(IB_ro_queries_inside IB_ro_queries_in_queue),
         "set_precision($exprs{DirtyBufs} * 100) as dirty_bufs",
         "set_precision($exprs{BufPoolFill} * 100) as buf_fill",
         qw(IB_bp_pages_total IB_bp_pages_read IB_bp_pages_written IB_bp_pages_created)
      ),
   },
   innodb_health2 => {
      text => join(
         ', ',
         'percent(1-((Innodb_buffer_pool_pages_free||0)/($cur->{Innodb_buffer_pool_pages_total}||1))) as BP_page_cache_usage',
         'percent(1-((Innodb_buffer_pool_reads||0)/(Innodb_buffer_pool_read_requests||1))) as BP_cache_hit_ratio',
         'Innodb_buffer_pool_wait_free',
         'Innodb_log_waits',
      ),
   },
   slow_queries => {
      text => join(
         ', ',
         'set_precision(Slow_queries/Uptime_hires) as Slow_PS',
         'set_precision(Select_full_join/Uptime_hires) as Full_Join_PS',
         'percent(Select_full_join/(Com_select||1)) as Full_Join_Ratio',
      ),
   },
);

# Server sets {{{3
# Defines sets of servers between which the user can quickly switch.
my %server_groups;

# Connections {{{3
# This hash defines server connections.  Each connection is a string that can be passed to
# the DBI connection.  These are saved in the connections section in the config file.
my %connections;
# Defines the parts of connections.
my @conn_parts = qw(user have_user pass have_pass dsn savepass dl_table);

# Graph widths {{{3
# This hash defines the max values seen for various status/variable values, for graphing.
# These are stored in their own section in the config file.  These are just initial values:
my %mvs = (
   Com_select   => 50,
   Com_insert   => 50,
   Com_update   => 50,
   Com_delete   => 50,
   Questions    => 100,
);

# ###########################################################################
# Valid Term::ANSIColor color strings.
# ###########################################################################
my %ansicolors = map { $_ => 1 }
   qw( black blink blue bold clear concealed cyan dark green magenta on_black
       on_blue on_cyan on_green on_magenta on_red on_white on_yellow red reset
       reverse underline underscore white yellow);

# ###########################################################################
# Valid comparison operators for color rules
# ###########################################################################
my %comp_ops = (
   '==' => 'Numeric equality',
   '>'  => 'Numeric greater-than',
   '<'  => 'Numeric less-than',
   '>=' => 'Numeric greater-than/equal',
   '<=' => 'Numeric less-than/equal',
   '!=' => 'Numeric not-equal',
   'eq' => 'String equality',
   'gt' => 'String greater-than',
   'lt' => 'String less-than',
   'ge' => 'String greater-than/equal',
   'le' => 'String less-than/equal',
   'ne' => 'String not-equal',
   '=~' => 'Pattern match',
   '!~' => 'Negated pattern match',
);

# ###########################################################################
# Valid aggregate functions.
# ###########################################################################
my %agg_funcs = (
   first => sub {
      return $_[0]
   },
   count => sub {
      return 0 + @_;
   },
   avg   => sub {
      my @args = grep { defined $_ } @_;
      return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1);
   },
   sum   => sub {
      my @args = grep { defined $_ } @_;
      return sum(@args);
   }
);

# ###########################################################################
# Valid functions for transformations.
# ###########################################################################
my %trans_funcs = (
   shorten      => \&shorten,
   secs_to_time => \&secs_to_time,
   no_ctrl_char => \&no_ctrl_char,
   percent      => \&percent,
   commify      => \&commify,
   dulint_to_int => \&dulint_to_int,
   set_precision => \&set_precision,
);

# Table definitions {{{3
# This hash defines every table that can get displayed in every mode.  Each
# table specifies columns and column data sources.  The column is
# defined by the %columns hash.
#
# Example: foo => { src => 'bar' } means the foo column (look at
# $columns{foo} for its definition) gets its data from the 'bar' element of
# the current data set, whatever that is.
#
# These columns are post-processed after being defined, because they get stuff
# from %columns.  After all the config is loaded for columns, there's more
# post-processing too; the subroutines compiled from src get added to
# the hash elements for extract_values to use.
# ###########################################################################

my %tbl_meta = (
   adaptive_hash_index => {
      capt => 'Adaptive Hash Index',
      cust => {},
      cols => {
         cxn                 => { src => 'cxn' },
         hash_table_size     => { src => 'IB_ib_hash_table_size', trans => [qw(shorten)], },
         used_cells          => { src => 'IB_ib_used_cells' },
         bufs_in_node_heap   => { src => 'IB_ib_bufs_in_node_heap' },
         hash_searches_s     => { src => 'IB_ib_hash_searches_s' },
         non_hash_searches_s => { src => 'IB_ib_non_hash_searches_s' },
      },
      visible => [ qw(cxn hash_table_size used_cells bufs_in_node_heap hash_searches_s non_hash_searches_s) ],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'ib',
      group_by => [],
      aggregate => 0,
   },
   buffer_pool => {
      capt => 'Buffer Pool',
      cust => {},
      cols => {
         cxn                        => { src => 'cxn' },
         total_mem_alloc            => { src => 'IB_bp_total_mem_alloc', trans => [qw(shorten)], },
         awe_mem_alloc              => { src => 'IB_bp_awe_mem_alloc', trans => [qw(shorten)], },
         add_pool_alloc             => { src => 'IB_bp_add_pool_alloc', trans => [qw(shorten)], },
         buf_pool_size              => { src => 'IB_bp_buf_pool_size', trans => [qw(shorten)], },
         buf_free                   => { src => 'IB_bp_buf_free' },
         buf_pool_hit_rate          => { src => 'IB_bp_buf_pool_hit_rate' },
         buf_pool_reads             => { src => 'IB_bp_buf_pool_reads' },
         buf_pool_hits              => { src => 'IB_bp_buf_pool_hits' },
         dict_mem_alloc             => { src => 'IB_bp_dict_mem_alloc' },
         pages_total                => { src => 'IB_bp_pages_total' },
         pages_modified             => { src => 'IB_bp_pages_modified' },
         reads_pending              => { src => 'IB_bp_reads_pending' },
         writes_pending             => { src => 'IB_bp_writes_pending' },
         writes_pending_lru         => { src => 'IB_bp_writes_pending_lru' },
         writes_pending_flush_list  => { src => 'IB_bp_writes_pending_flush_list' },
         writes_pending_single_page => { src => 'IB_bp_writes_pending_single_page' },
         page_creates_sec           => { src => 'IB_bp_page_creates_sec' },
         page_reads_sec             => { src => 'IB_bp_page_reads_sec' },
         page_writes_sec            => { src => 'IB_bp_page_writes_sec' },
         pages_created              => { src => 'IB_bp_pages_created' },
         pages_read                 => { src => 'IB_bp_pages_read' },
         pages_written              => { src => 'IB_bp_pages_written' },
      },
      visible => [ qw(cxn buf_pool_size buf_free pages_total pages_modified buf_pool_hit_rate total_mem_alloc add_pool_alloc)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'bp',
      group_by => [],
      aggregate => 0,
   },
   # TODO: a new step in set_to_tbl: join result to itself, grouped?
   # TODO: this would also enable pulling Q and T data together.
   # TODO: using a SQL-ish language would also allow pivots to be easier -- treat the pivoted data as a view and SELECT from it.
   cmd_summary => {
      capt => 'Command Summary',
      cust => {},
      cols => {
         name       => { src => 'name' },
         total      => { src => 'total' },
         value      => { src => 'value',                     agg   => 'sum'},
         pct        => { src => 'value/total',               trans => [qw(percent)] },
         last_total => { src => 'last_total' },
         last_value => { src => 'last_value',                agg   => 'sum'},
         last_pct   => { src => 'last_value/last_total',     trans => [qw(percent)] },
      },
      visible   => [qw(name value pct last_value last_pct)],
      filters   => [qw()],
      sort_cols => '-value',
      sort_dir  => '1',
      innodb    => '',
      group_by  => [qw(name)],
      aggregate => 1,
   },
   deadlock_locks => {
      capt => 'Deadlock Locks',
      cust => {},
      cols => {
         cxn              => { src => 'cxn' },
         mysql_thread_id  => { src => 'mysql_thread_id' },
         dl_txn_num       => { src => 'dl_txn_num' },
         lock_type        => { src => 'lock_type' },
         space_id         => { src => 'space_id' },
         page_no          => { src => 'page_no' },
         heap_no          => { src => 'heap_no' },
         n_bits           => { src => 'n_bits' },
         index            => { src => 'index' },
         db               => { src => 'db' },
         tbl              => { src => 'table' },
         lock_mode        => { src => 'lock_mode' },
         special          => { src => 'special' },
         insert_intention => { src => 'insert_intention' },
         waiting          => { src => 'waiting' },
      },
      visible => [ qw(cxn mysql_thread_id waiting lock_mode db tbl index special insert_intention)],
      filters => [],
      sort_cols => 'cxn mysql_thread_id',
      sort_dir => '1',
      innodb   => 'dl',
      group_by => [],
      aggregate => 0,
   },
   deadlock_transactions => {
      capt => 'Deadlock Transactions',
      cust => {},
      cols => {
         cxn                => { src => 'cxn' },
         active_secs        => { src => 'active_secs' },
         dl_txn_num         => { src => 'dl_txn_num' },
         has_read_view      => { src => 'has_read_view' },
         heap_size          => { src => 'heap_size' },
         host_and_domain    => { src => 'hostname' },
         hostname           => { src => $exprs{Host} },
         ip                 => { src => 'ip' },
         lock_structs       => { src => 'lock_structs' },
         lock_wait_time     => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
         mysql_thread_id    => { src => 'mysql_thread_id' },
         os_thread_id       => { src => 'os_thread_id' },
         proc_no            => { src => 'proc_no' },
         query_id           => { src => 'query_id' },
         query_status       => { src => 'query_status' },
         query_text         => { src => 'query_text', trans => [ qw(no_ctrl_char) ] },
         row_locks          => { src => 'row_locks' },
         tables_in_use      => { src => 'tables_in_use' },
         tables_locked      => { src => 'tables_locked' },
         thread_decl_inside => { src => 'thread_decl_inside' },
         thread_status      => { src => 'thread_status' },
         'time'             => { src => 'active_secs', trans => [ qw(secs_to_time) ] },
         timestring         => { src => 'timestring' },
         txn_doesnt_see_ge  => { src => 'txn_doesnt_see_ge' },
         txn_id             => { src => 'txn_id' },
         txn_sees_lt        => { src => 'txn_sees_lt' },
         txn_status         => { src => 'txn_status' },
         truncates          => { src => 'truncates' },
         undo_log_entries   => { src => 'undo_log_entries' },
         user               => { src => 'user' },
         victim             => { src => 'victim' },
         wait_status        => { src => 'lock_wait_status' },
      },
      visible => [ qw(cxn mysql_thread_id timestring user hostname victim time undo_log_entries lock_structs query_text)],
      filters => [],
      sort_cols => 'cxn mysql_thread_id',
      sort_dir => '1',
      innodb   => 'dl',
      group_by => [],
      aggregate => 0,
   },
   explain => {
      capt => 'EXPLAIN Results',
      cust => {},
      cols => {
         part_id       => { src => 'id' },
         select_type   => { src => 'select_type' },
         tbl           => { src => 'table' },
         partitions    => { src => 'partitions' },
         scan_type     => { src => 'type' },
         possible_keys => { src => 'possible_keys' },
         index         => { src => 'key' },
         key_len       => { src => 'key_len' },
         index_ref     => { src => 'ref' },
         num_rows      => { src => 'rows' },
         special       => { src => 'extra' },
      },
      visible => [ qw(select_type tbl partitions scan_type possible_keys index key_len index_ref num_rows special)],
      filters => [],
      sort_cols => '',
      sort_dir => '1',
      innodb   => '',
      group_by => [],
      aggregate => 0,
   },
   file_io_misc => {
      capt => 'File I/O Misc',
      cust => {},
      cols => {
         cxn            => { src => 'cxn' },
         io_bytes_s     => { src => 'IB_io_avg_bytes_s' },
         io_flush_type  => { src => 'IB_io_flush_type' },
         io_fsyncs_s    => { src => 'IB_io_fsyncs_s' },
         io_reads_s     => { src => 'IB_io_reads_s' },
         io_writes_s    => { src => 'IB_io_writes_s' },
         os_file_reads  => { src => 'IB_io_os_file_reads' },
         os_file_writes => { src => 'IB_io_os_file_writes' },
         os_fsyncs      => { src => 'IB_io_os_fsyncs' },
      },
      visible => [ qw(cxn os_file_reads os_file_writes os_fsyncs io_reads_s io_writes_s io_bytes_s)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'io',
      group_by => [],
      aggregate => 0,
   },
   fk_error => {
      capt => 'Foreign Key Error Info',
      cust => {},
      cols => {
         timestring   => { src => 'IB_fk_timestring' },
         child_db     => { src => 'IB_fk_child_db' },
         child_table  => { src => 'IB_fk_child_table' },
         child_index  => { src => 'IB_fk_child_index' },
         fk_name      => { src => 'IB_fk_fk_name' },
         parent_db    => { src => 'IB_fk_parent_db' },
         parent_table => { src => 'IB_fk_parent_table' },
         parent_col   => { src => 'IB_fk_parent_col' },
         parent_index => { src => 'IB_fk_parent_index' },
         attempted_op => { src => 'IB_fk_attempted_op' },
      },
      visible => [ qw(timestring child_db child_table child_index parent_db parent_table parent_col parent_index fk_name attempted_op)],
      filters => [],
      sort_cols => '',
      sort_dir => '1',
      innodb   => 'fk',
      group_by => [],
      aggregate => 0,
   },
   insert_buffers => {
      capt => 'Insert Buffers',
      cust => {},
      cols => {
         cxn           => { src => 'cxn' },
         inserts       => { src => 'IB_ib_inserts' },
         merged_recs   => { src => 'IB_ib_merged_recs' },
         merges        => { src => 'IB_ib_merges' },
         size          => { src => 'IB_ib_size' },
         free_list_len => { src => 'IB_ib_free_list_len' },
         seg_size      => { src => 'IB_ib_seg_size' },
      },
      visible => [ qw(cxn inserts merged_recs merges size free_list_len seg_size)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'ib',
      group_by => [],
      aggregate => 0,
   },
   innodb_locks  => {
      capt => 'InnoDB Locks',
      cust => {},
      cols => {
         cxn              => { src => 'cxn' },
         db               => { src => 'db' },
         index            => { src => 'index' },
         insert_intention => { src => 'insert_intention' },
         lock_mode        => { src => 'lock_mode' },
         lock_type        => { src => 'lock_type' },
         lock_wait_time   => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] },
         mysql_thread_id  => { src => 'mysql_thread_id' },
         n_bits           => { src => 'n_bits' },
         page_no          => { src => 'page_no' },
         space_id         => { src => 'space_id' },
         special          => { src => 'special' },
         tbl              => { src => 'table' },
         'time'           => { src => 'active_secs', hdr => 'Active', trans => [ qw(secs_to_time) ] },
         txn_id           => { src => 'txn_id' },
         waiting          => { src => 'waiting' },
      },
      visible => [ qw(cxn mysql_thread_id lock_type waiting lock_wait_time time lock_mode db tbl index insert_intention special)],
      filters => [],
      sort_cols => 'cxn -lock_wait_time',
      sort_dir => '1',
      innodb   => 'tx',
      colors   => [
         { col => 'lock_wait_time', op => '>',  arg => 60, color => 'red' },
         { col => 'lock_wait_time', op => '>',  arg => 30, color => 'yellow' },
         { col => 'lock_wait_time', op => '>',  arg => 10, color => 'green' },
      ],
      group_by => [],
      aggregate => 0,
   },
   innodb_transactions => {
      capt => 'InnoDB Transactions',
      cust => {},
      cols => {
         cxn                => { src => 'cxn' },
         active_secs        => { src => 'active_secs' },
         has_read_view      => { src => 'has_read_view' },
         heap_size          => { src => 'heap_size' },
         hostname           => { src => $exprs{Host} },
         ip                 => { src => 'ip' },
         wait_status        => { src => 'lock_wait_status' },
         lock_wait_time     => { src => 'lock_wait_time',      trans => [ qw(secs_to_time) ] },
         lock_structs       => { src => 'lock_structs' },
         mysql_thread_id    => { src => 'mysql_thread_id' },
         os_thread_id       => { src => 'os_thread_id' },
         proc_no            => { src => 'proc_no' },
         query_id           => { src => 'query_id' },
         query_status       => { src => 'query_status' },
         query_text         => { src => 'query_text',          trans => [ qw(no_ctrl_char) ] },
         txn_time_remain    => { src => $exprs{TxnTimeRemain}, trans => [ qw(secs_to_time) ] },
         row_locks          => { src => 'row_locks' },
         tables_in_use      => { src => 'tables_in_use' },
         tables_locked      => { src => 'tables_locked' },
         thread_decl_inside => { src => 'thread_decl_inside' },
         thread_status      => { src => 'thread_status' },
         'time'             => { src => 'active_secs',         trans => [ qw(secs_to_time) ], agg => 'sum' },
         txn_doesnt_see_ge  => { src => 'txn_doesnt_see_ge' },
         txn_id             => { src => 'txn_id' },
         txn_sees_lt        => { src => 'txn_sees_lt' },
         txn_status         => { src => 'txn_status',          minw => 10, maxw => 10 },
         undo_log_entries   => { src => 'undo_log_entries' },
         user               => { src => 'user',                maxw => 10 },
         cnt                => { src => 'mysql_thread_id',     minw => 0 },
      },
      visible => [ qw(cxn cnt mysql_thread_id user hostname txn_status time undo_log_entries query_text)],
      filters => [ qw( hide_self hide_inactive ) ],
      sort_cols => '-active_secs txn_status cxn mysql_thread_id',
      sort_dir => '1',
      innodb   => 'tx',
      hide_caption => 1,
      colors   => [
         { col => 'wait_status', op => 'eq', arg => 'LOCK WAIT',   color => 'black on_red' },
         { col => 'time',        op => '>',  arg => 600,           color => 'red' },
         { col => 'time',        op => '>',  arg => 300,           color => 'yellow' },
         { col => 'time',        op => '>',  arg => 60,            color => 'green' },
         { col => 'time',        op => '>',  arg => 30,            color => 'cyan' },
         { col => 'txn_status',  op => 'eq', arg => 'not started', color => 'white' },
      ],
      group_by => [ qw(cxn txn_status) ],
      aggregate => 0,
   },
   io_threads => {
      capt => 'I/O Threads',
      cust => {},
      cols => {
         cxn            => { src => 'cxn' },
         thread         => { src => 'thread' },
         thread_purpose => { src => 'purpose' },
         event_set      => { src => 'event_set' },
         thread_status  => { src => 'state' },
      },
      visible => [ qw(cxn thread thread_purpose thread_status)],
      filters => [ qw() ],
      sort_cols => 'cxn thread',
      sort_dir => '1',
      innodb   => 'io',
      group_by => [],
      aggregate => 0,
   },
   log_statistics => {
      capt => 'Log Statistics',
      cust => {},
      cols => {
         cxn                 => { src => 'cxn' },
         last_chkp           => { src => 'IB_lg_last_chkp' },
         log_flushed_to      => { src => 'IB_lg_log_flushed_to' },
         log_ios_done        => { src => 'IB_lg_log_ios_done' },
         log_ios_s           => { src => 'IB_lg_log_ios_s' },
         log_seq_no          => { src => 'IB_lg_log_seq_no' },
         pending_chkp_writes => { src => 'IB_lg_pending_chkp_writes' },
         pending_log_writes  => { src => 'IB_lg_pending_log_writes' },
      },
      visible => [ qw(cxn log_seq_no log_flushed_to last_chkp log_ios_done log_ios_s)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'lg',
      group_by => [],
      aggregate => 0,
   },
   master_status => {
      capt => 'Master Status',
      cust => {},
      cols => {
         cxn                         => { src => 'cxn' },
         binlog_do_db                => { src => 'binlog_do_db' },
         binlog_ignore_db            => { src => 'binlog_ignore_db' },
         master_file                 => { src => 'file' },
         master_pos                  => { src => 'position' },
         binlog_cache_overflow       => { src => '(Binlog_cache_disk_use||0)/(Binlog_cache_use||1)', trans => [ qw(percent) ] },
      },
      visible => [ qw(cxn master_file master_pos binlog_cache_overflow)],
      filters => [ qw(cxn_is_master) ],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => '',
      group_by => [],
      aggregate => 0,
   },
   pending_io => {
      capt => 'Pending I/O',
      cust => {},
      cols => {
         cxn                => { src => 'cxn' },
         p_normal_aio_reads => { src => 'IB_io_pending_normal_aio_reads' },
         p_aio_writes       => { src => 'IB_io_pending_aio_writes' },
         p_ibuf_aio_reads   => { src => 'IB_io_pending_ibuf_aio_reads' },
         p_sync_ios         => { src => 'IB_io_pending_sync_ios' },
         p_buf_pool_flushes => { src => 'IB_io_pending_buffer_pool_flushes' },
         p_log_flushes      => { src => 'IB_io_pending_log_flushes' },
         p_log_ios          => { src => 'IB_io_pending_log_ios' },
         p_preads           => { src => 'IB_io_pending_preads' },
         p_pwrites          => { src => 'IB_io_pending_pwrites' },
      },
      visible => [ qw(cxn p_normal_aio_reads p_aio_writes p_ibuf_aio_reads p_sync_ios p_log_flushes p_log_ios)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'io',
      group_by => [],
      aggregate => 0,
   },
   open_tables => {
      capt => 'Open Tables',
      cust => {},
      cols => {
         cxn            => { src => 'cxn' },
         db             => { src => 'database' },
         tbl            => { src => 'table' },
         num_times_open => { src => 'in_use' },
         is_name_locked => { src => 'name_locked' },
      },
      visible => [ qw(cxn db tbl num_times_open is_name_locked)],
      filters => [ qw(table_is_open) ],
      sort_cols => '-num_times_open cxn db tbl',
      sort_dir => '1',
      innodb   => '',
      group_by => [],
      aggregate => 0,
   },
   page_statistics => {
      capt => 'Page Statistics',
      cust => {},
      cols => {
         cxn              => { src => 'cxn' },
         pages_read       => { src => 'IB_bp_pages_read' },
         pages_written    => { src => 'IB_bp_pages_written' },
         pages_created    => { src => 'IB_bp_pages_created' },
         page_reads_sec   => { src => 'IB_bp_page_reads_sec' },
         page_writes_sec  => { src => 'IB_bp_page_writes_sec' },
         page_creates_sec => { src => 'IB_bp_page_creates_sec' },
      },
      visible => [ qw(cxn pages_read pages_written pages_created page_reads_sec page_writes_sec page_creates_sec)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'bp',
      group_by => [],
      aggregate => 0,
   },
   processlist => {
      capt => 'MySQL Process List',
      cust => {},
      cols => {
         cxn             => { src => 'cxn',        minw => 6,  maxw => 10 },
         mysql_thread_id => { src => 'id',         minw => 6,  maxw => 0 },
         user            => { src => 'user',       minw => 5,  maxw => 8 },
         hostname        => { src => $exprs{Host}, minw => 13, maxw => 8, },
         port            => { src => $exprs{Port}, minw => 0,  maxw => 0, },
         host_and_port   => { src => 'host',       minw => 0,  maxw => 0 },
         db              => { src => 'db',         minw => 6,  maxw => 12 },
         cmd             => { src => 'command',    minw => 5,  maxw => 0 },
         time            => { src => 'time',       minw => 5,  maxw => 0, trans => [ qw(secs_to_time) ], agg => 'sum' },
         state           => { src => 'state',      minw => 0,  maxw => 0 },
         info            => { src => 'info',       minw => 0,  maxw => 0, trans => [ qw(no_ctrl_char) ] },
         cnt             => { src => 'id',         minw => 0,  maxw => 0 },
      },
      visible => [ qw(cxn cmd cnt mysql_thread_id state user hostname db time info)],
      filters => [ qw(hide_self hide_inactive hide_slave_io) ],
      sort_cols => '-time cxn hostname mysql_thread_id',
      sort_dir => '1',
      innodb   => '',
      hide_caption => 1,
      colors   => [
         { col => 'state',       op => 'eq', arg => 'Locked',      color => 'black on_red' },
         { col => 'cmd',         op => 'eq', arg => 'Sleep',       color => 'white' },
         { col => 'user',        op => 'eq', arg => 'system user', color => 'white' },
         { col => 'cmd',         op => 'eq', arg => 'Connect',     color => 'white' },
         { col => 'cmd',         op => 'eq', arg => 'Binlog Dump', color => 'white' },
         { col => 'time',        op => '>',  arg => 600,           color => 'red' },
         { col => 'time',        op => '>',  arg => 120,           color => 'yellow' },
         { col => 'time',        op => '>',  arg => 60,            color => 'green' },
         { col => 'time',        op => '>',  arg => 30,            color => 'cyan' },
      ],
      group_by => [qw(cxn cmd)],
      aggregate => 0,
   },

   # TODO: some more columns:
   # kb_used=hdr='BufUsed' minw='0' num='0' src='percent(1 - ((Key_blocks_unused * key_cache_block_size) / (key_buffer_size||1)))' dec='0' trans='' tbl='q_header' just='-' user='1' maxw='0' label='User-defined'
   # retries=hdr='Retries' minw='0' num='0' src='Slave_retried_transactions' dec='0' trans='' tbl='slave_sql_status' just='-' user='1' maxw='0' label='User-defined'
   # thd=hdr='Thd' minw='0' num='0' src='Threads_connected' dec='0' trans='' tbl='slave_sql_status' just='-' user='1' maxw='0' label='User-defined'

   q_header => {
      capt => 'Q-mode Header',
      cust => {},
      cols => {
         cxn            => { src => 'cxn' },
         questions      => { src => 'Questions' },
         qps            => { src => 'Questions/Uptime_hires',               dec => 1, trans => [qw(shorten)] },
         load           => { src => $exprs{ServerLoad},                     dec => 1, trans => [qw(shorten)] },
         slow           => { src => 'Slow_queries',                         dec => 1, trans => [qw(shorten)] },
         q_cache_hit    => { src => $exprs{QcacheHitRatio},                 dec => 1, trans => [qw(percent)] },
         key_buffer_hit => { src => '1-(Key_reads/(Key_read_requests||1))', dec => 1, trans => [qw(percent)] },
         bps_in         => { src => 'Bytes_received/Uptime_hires',          dec => 1, trans => [qw(shorten)] },
         bps_out        => { src => 'Bytes_sent/Uptime_hires',              dec => 1, trans => [qw(shorten)] },
         when           => { src => 'when' },
      },
      visible => [ qw(cxn when load qps slow q_cache_hit key_buffer_hit bps_in bps_out)],
      filters => [],
      sort_cols => 'when cxn',
      sort_dir => '1',
      innodb   => '',
      hide_caption => 1,
      group_by => [],
      aggregate => 0,
   },
   row_operations => {
      capt => 'InnoDB Row Operations',
      cust => {},
      cols => {
         cxn         => { src => 'cxn' },
         num_inserts => { src => 'IB_ro_num_rows_ins' },
         num_updates => { src => 'IB_ro_num_rows_upd' },
         num_reads   => { src => 'IB_ro_num_rows_read' },
         num_deletes => { src => 'IB_ro_num_rows_del' },
         num_inserts_sec => { src => 'IB_ro_ins_sec' },
         num_updates_sec => { src => 'IB_ro_upd_sec' },
         num_reads_sec   => { src => 'IB_ro_read_sec' },
         num_deletes_sec => { src => 'IB_ro_del_sec' },
      },
      visible => [ qw(cxn num_inserts num_updates num_reads num_deletes num_inserts_sec
                       num_updates_sec num_reads_sec num_deletes_sec)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'ro',
      group_by => [],
      aggregate => 0,
   },
   row_operation_misc => {
      capt => 'Row Operation Misc',
      cust => {},
      cols => {
         cxn                 => { src => 'cxn' },
         queries_in_queue    => { src => 'IB_ro_queries_in_queue' },
         queries_inside      => { src => 'IB_ro_queries_inside' },
         read_views_open     => { src => 'IB_ro_read_views_open' },
         main_thread_id      => { src => 'IB_ro_main_thread_id' },
         main_thread_proc_no => { src => 'IB_ro_main_thread_proc_no' },
         main_thread_state   => { src => 'IB_ro_main_thread_state' },
         num_res_ext         => { src => 'IB_ro_n_reserved_extents' },
      },
      visible => [ qw(cxn queries_in_queue queries_inside read_views_open main_thread_state)],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'ro',
      group_by => [],
      aggregate => 0,
   },
   semaphores => {
      capt => 'InnoDB Semaphores',
      cust => {},
      cols => {
         cxn                => { src => 'cxn' },
         mutex_os_waits     => { src => 'IB_sm_mutex_os_waits' },
         mutex_spin_rounds  => { src => 'IB_sm_mutex_spin_rounds' },
         mutex_spin_waits   => { src => 'IB_sm_mutex_spin_waits' },
         reservation_count  => { src => 'IB_sm_reservation_count' },
         rw_excl_os_waits   => { src => 'IB_sm_rw_excl_os_waits' },
         rw_excl_spins      => { src => 'IB_sm_rw_excl_spins' },
         rw_shared_os_waits => { src => 'IB_sm_rw_shared_os_waits' },
         rw_shared_spins    => { src => 'IB_sm_rw_shared_spins' },
         signal_count       => { src => 'IB_sm_signal_count' },
         wait_array_size    => { src => 'IB_sm_wait_array_size' },
      },
      visible => [ qw(cxn mutex_os_waits mutex_spin_waits mutex_spin_rounds
         rw_excl_os_waits rw_excl_spins rw_shared_os_waits rw_shared_spins
         signal_count reservation_count )],
      filters => [],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => 'sm',
      group_by => [],
      aggregate => 0,
   },
   slave_io_status => {
      capt => 'Slave I/O Status',
      cust => {},
      cols => {
         cxn                         => { src => 'cxn' },
         connect_retry               => { src => 'connect_retry' },
         master_host                 => { src => 'master_host', hdr => 'Master'},
         master_log_file             => { src => 'master_log_file', hdr => 'File' },
         master_port                 => { src => 'master_port' },
         master_ssl_allowed          => { src => 'master_ssl_allowed' },
         master_ssl_ca_file          => { src => 'master_ssl_ca_file' },
         master_ssl_ca_path          => { src => 'master_ssl_ca_path' },
         master_ssl_cert             => { src => 'master_ssl_cert' },
         master_ssl_cipher           => { src => 'master_ssl_cipher' },
         master_ssl_key              => { src => 'master_ssl_key' },
         master_user                 => { src => 'master_user' },
         read_master_log_pos         => { src => 'read_master_log_pos', hdr => 'Pos' },
         relay_log_size              => { src => 'relay_log_space', trans => [qw(shorten)] },
         slave_io_running            => { src => 'slave_io_running', hdr => 'On?' },
         slave_io_state              => { src => 'slave_io_state', hdr => 'State' },
      },
      visible => [ qw(cxn master_host slave_io_running master_log_file relay_log_size read_master_log_pos slave_io_state)],
      filters => [ qw( cxn_is_slave ) ],
      sort_cols => 'slave_io_running cxn',
      colors   => [
         { col => 'slave_io_running',  op => 'ne', arg => 'Yes', color => 'black on_red' },
      ],
      sort_dir => '1',
      innodb   => '',
      group_by => [],
      aggregate => 0,
   },
   slave_sql_status => {
      capt => 'Slave SQL Status',
      cust => {},
      cols => {
         cxn                         => { src => 'cxn' },
         exec_master_log_pos         => { src => 'exec_master_log_pos', hdr => 'Master Pos' },
         last_errno                  => { src => 'last_errno' },
         last_error                  => { src => 'last_error' },
         master_host                 => { src => 'master_host', hdr => 'Master' },
         relay_log_file              => { src => 'relay_log_file' },
         relay_log_pos               => { src => 'relay_log_pos' },
         relay_log_size              => { src => 'relay_log_space', trans => [qw(shorten)] },
         relay_master_log_file       => { src => 'relay_master_log_file', hdr => 'Master File' },
         replicate_do_db             => { src => 'replicate_do_db' },
         replicate_do_table          => { src => 'replicate_do_table' },
         replicate_ignore_db         => { src => 'replicate_ignore_db' },
         replicate_ignore_table      => { src => 'replicate_ignore_table' },
         replicate_wild_do_table     => { src => 'replicate_wild_do_table' },
         replicate_wild_ignore_table => { src => 'replicate_wild_ignore_table' },
         skip_counter                => { src => 'skip_counter' },
         slave_sql_running           => { src => 'slave_sql_running', hdr => 'On?' },
         until_condition             => { src => 'until_condition' },
         until_log_file              => { src => 'until_log_file' },
         until_log_pos               => { src => 'until_log_pos' },
         time_behind_master          => { src => 'seconds_behind_master', trans => [ qw(secs_to_time) ] },
         bytes_behind_master         => { src => 'master_log_file && master_log_file eq relay_master_log_file ? read_master_log_pos - exec_master_log_pos : 0', trans => [qw(shorten)] },
         slave_catchup_rate          => { src => $exprs{SlaveCatchupRate}, trans => [ qw(set_precision) ] },
         slave_open_temp_tables      => { src => 'Slave_open_temp_tables' },
      },
      visible => [ qw(cxn master_host slave_sql_running time_behind_master slave_catchup_rate slave_open_temp_tables relay_log_pos last_error)],
      filters => [ qw( cxn_is_slave ) ],
      sort_cols => 'slave_sql_running cxn',
      sort_dir => '1',
      innodb   => '',
      colors   => [
         { col => 'slave_sql_running',  op => 'ne', arg => 'Yes', color => 'black on_red' },
         { col => 'time_behind_master', op => '>',  arg => 600,   color => 'red' },
         { col => 'time_behind_master', op => '>',  arg => 60,    color => 'yellow' },
         { col => 'time_behind_master', op => '==', arg => 0,     color => 'white' },
      ],
      group_by => [],
      aggregate => 0,
   },
   t_header => {
      capt => 'T-Mode Header',
      cust => {},
      cols => {
         cxn                         => { src => 'cxn' },
         dirty_bufs                  => { src => $exprs{DirtyBufs},           trans => [qw(percent)] },
         history_list_len            => { src => 'IB_tx_history_list_len' },
         lock_structs                => { src => 'IB_tx_num_lock_structs' },
         num_txns                    => { src => $exprs{NumTxns} },
         max_txn                     => { src => $exprs{MaxTxnTime},          trans => [qw(secs_to_time)] },
         undo_for                    => { src => 'IB_tx_purge_undo_for' },
         used_bufs                   => { src => $exprs{BufPoolFill},         trans => [qw(percent)]},
         versions                    => { src => $exprs{OldVersions} },
      },
      visible => [ qw(cxn history_list_len versions undo_for dirty_bufs used_bufs num_txns max_txn lock_structs)],
      filters => [ ],
      sort_cols => 'cxn',
      sort_dir => '1',
      innodb   => '',
      colors   => [],
      hide_caption => 1,
      group_by => [],
      aggregate => 0,
   },
   var_status => {
      capt      => 'Variables & Status',
      cust      => {},
      cols      => {}, # Generated from current varset
      visible   => [], # Generated from current varset
      filters   => [],
      sort_cols => '',
      sort_dir  => 1,
      innodb    => '',
      temp      => 1, # Do not persist to config file.
      hide_caption  => 1,
      pivot     => 0,
      group_by => [],
      aggregate => 0,
   },
   wait_array => {
      capt => 'InnoDB Wait Array',
      cust => {},
      cols => {
         cxn                => { src => 'cxn' },
         thread             => { src => 'thread' },
         waited_at_filename => { src => 'waited_at_filename' },
         waited_at_line     => { src => 'waited_at_line' },
         'time'             => { src => 'waited_secs', trans => [ qw(secs_to_time) ] },
         request_type       => { src => 'request_type' },
         lock_mem_addr      => { src => 'lock_mem_addr' },
         lock_cfile_name    => { src => 'lock_cfile_name' },
         lock_cline         => { src => 'lock_cline' },
         writer_thread      => { src => 'writer_thread' },
         writer_lock_mode   => { src => 'writer_lock_mode' },
         num_readers        => { src => 'num_readers' },
         lock_var           => { src => 'lock_var' },
         waiters_flag       => { src => 'waiters_flag' },
         last_s_file_name   => { src => 'last_s_file_name' },
         last_s_line        => { src => 'last_s_line' },
         last_x_file_name   => { src => 'last_x_file_name' },
         last_x_line        => { src => 'last_x_line' },
         cell_waiting       => { src => 'cell_waiting' },
         cell_event_set     => { src => 'cell_event_set' },
      },
      visible => [ qw(cxn thread time waited_at_filename waited_at_line request_type num_readers lock_var waiters_flag cell_waiting cell_event_set)],
      filters => [],
      sort_cols => 'cxn -time',
      sort_dir => '1',
      innodb   => 'sm',
      group_by => [],
      aggregate => 0,
   },
);

# Initialize %tbl_meta from %columns and do some checks.
foreach my $table_name ( keys %tbl_meta ) {
   my $table = $tbl_meta{$table_name};
   my $cols  = $table->{cols};

   foreach my $col_name ( keys %$cols ) {
      my $col_def = $table->{cols}->{$col_name};
      die "I can't find a column named '$col_name' for '$table_name'" unless $columns{$col_name};
      $columns{$col_name}->{referenced} = 1;

      foreach my $prop ( keys %col_props ) {
         # Each column gets non-existing values set from %columns or defaults from %col_props.
         if ( !$col_def->{$prop} ) {
            $col_def->{$prop}
               = defined($columns{$col_name}->{$prop})
               ? $columns{$col_name}->{$prop}
               : $col_props{$prop};
         }
      }

      # Ensure transformations and aggregate functions are valid
      die "Unknown aggregate function '$col_def->{agg}' "
         . "for column '$col_name' in table '$table_name'"
         unless exists $agg_funcs{$col_def->{agg}};
      foreach my $trans ( @{$col_def->{trans}} ) {
         die "Unknown transformation '$trans' "
            . "for column '$col_name' in table '$table_name'"
            unless exists $trans_funcs{$trans};
      }
   }

   # Ensure each column in visible and group_by exists in cols
   foreach my $place ( qw(visible group_by) ) {
      foreach my $col_name ( @{$table->{$place}} ) {
         if ( !exists $cols->{$col_name} ) {
            die "Column '$col_name' is listed in '$place' for '$table_name', but doesn't exist";
         }
      }
   }

   # Compile sort and color subroutines
   $table->{sort_func}  = make_sort_func($table);
   $table->{color_func} = make_color_func($table);
}

# This is for code cleanup:
{
   my @unused_cols = grep { !$columns{$_}->{referenced} } sort keys %columns;
   if ( @unused_cols ) {
      die "The following columns are not used: "
         . join(' ', @unused_cols);
   }
}

# ###########################################################################
# Operating modes {{{3
# ###########################################################################
my %modes = (
   B => {
      hdr               => 'InnoDB Buffers',
      cust              => {},
      note              => 'Shows buffer info from InnoDB',
      action_for        => {
         i => {
            action => sub { toggle_config('status_inc') },
            label  => 'Toggle incremental status display',                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                