mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-02 02:34:19 +00:00
Add lib/, t/lib/, and sandbox/. All modules are updated and passing on MySQL 5.1.
This commit is contained in:
338
lib/MySQLConfigComparer.pm
Normal file
338
lib/MySQLConfigComparer.pm
Normal file
@@ -0,0 +1,338 @@
|
||||
# This program is copyright 2010-2011 Percona Inc.
|
||||
# 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.
|
||||
# ###########################################################################
|
||||
# MySQLConfigComparer package $Revision: 7403 $
|
||||
# ###########################################################################
|
||||
|
||||
# Package: MySQLConfigComparer
|
||||
# MySQLConfigComparer compares and diffs C<MySQLConfig> objects.
|
||||
package MySQLConfigComparer;
|
||||
|
||||
{ # package scope
|
||||
use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Data::Dumper;
|
||||
$Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
$Data::Dumper::Quotekeys = 0;
|
||||
|
||||
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
|
||||
|
||||
# Alternate values because a config file can have var=ON and then be shown
|
||||
# in SHOW VARS as var=TRUE. I.e. there's several synonyms for basic
|
||||
# true (1) and false (0), so we normalize them to make comparisons easier.
|
||||
my %alt_val_for = (
|
||||
ON => 1,
|
||||
YES => 1,
|
||||
TRUE => 1,
|
||||
OFF => 0,
|
||||
NO => 0,
|
||||
FALSE => 0,
|
||||
);
|
||||
|
||||
# Sub: new
|
||||
#
|
||||
# Parameters:
|
||||
# %args - Arguments
|
||||
#
|
||||
# Optional Arguments:
|
||||
# ignore_variables - Arrayref of variables to ignore
|
||||
# numeric_variables - Arrayref of variables to compare numerically
|
||||
# optional_value_variables - Arrayref of vars whose val is optional
|
||||
# any_value_is_true_variables - Arrayref of vars... see below
|
||||
# base_path - Hashref of variable=>base_path
|
||||
#
|
||||
# Returns:
|
||||
# MySQLConfigComparer object
|
||||
sub new {
|
||||
my ( $class, %args ) = @_;
|
||||
|
||||
# These vars don't interest us so we ignore them.
|
||||
my %ignore_vars = (
|
||||
date_format => 1,
|
||||
datetime_format => 1,
|
||||
ft_stopword_file => 1,
|
||||
timestamp => 1,
|
||||
time_format => 1,
|
||||
($args{ignore_variables}
|
||||
? map { $_ => 1 } @{$args{ignore_variables}}
|
||||
: ()),
|
||||
);
|
||||
|
||||
# The vars should be compared with == instead of eq so that
|
||||
# 0 equals 0.0, etc.
|
||||
my %is_numeric = (
|
||||
long_query_time => 1,
|
||||
($args{numeric_variables}
|
||||
? map { $_ => 1 } @{$args{numeric_variables}}
|
||||
: ()),
|
||||
);
|
||||
|
||||
# These vars can be specified like --log-error or --log-error=file in config
|
||||
# files. If specified without a value, then they're "equal" to whatever
|
||||
# default value SHOW VARIABLES lists.
|
||||
my %value_is_optional = (
|
||||
log_error => 1,
|
||||
log_isam => 1,
|
||||
($args{optional_value_variables}
|
||||
? map { $_ => 1 } @{$args{optional_value_variables}}
|
||||
: ()),
|
||||
);
|
||||
|
||||
# Like value_is_optional but SHOW VARIABlES does not list a default value,
|
||||
# it only lists ON if the variable was given in a config file without or
|
||||
# without a value (e.g. --log or --log=file). So any value from the config
|
||||
# file that's true (i.e. not a blank string) equals ON from SHOW VARIABLES.
|
||||
my %any_value_is_true = (
|
||||
log => 1,
|
||||
log_bin => 1,
|
||||
log_slow_queries => 1,
|
||||
($args{any_value_is_true_variables}
|
||||
? map { $_ => 1 } @{$args{any_value_is_true_variables}}
|
||||
: ()),
|
||||
);
|
||||
|
||||
# The value of these vars are relative to some base path. In config files
|
||||
# just a filename can be given, but in SHOW VARS the full /base/path/filename
|
||||
# is shown. So we have to qualify the config value with the correct
|
||||
# base path.
|
||||
my %base_path = (
|
||||
character_sets_dir => 'basedir',
|
||||
datadir => 'basedir',
|
||||
general_log_file => 'datadir',
|
||||
language => 'basedir',
|
||||
log_error => 'datadir',
|
||||
pid_file => 'datadir',
|
||||
plugin_dir => 'basedir',
|
||||
slow_query_log_file => 'datadir',
|
||||
socket => 'datadir',
|
||||
($args{base_paths}
|
||||
? map { $_ => 1 } @{$args{base_paths}}
|
||||
: ()),
|
||||
);
|
||||
|
||||
my $self = {
|
||||
ignore_vars => \%ignore_vars,
|
||||
is_numeric => \%is_numeric,
|
||||
value_is_optional => \%value_is_optional,
|
||||
any_value_is_true => \%any_value_is_true,
|
||||
base_path => \%base_path,
|
||||
};
|
||||
|
||||
return bless $self, $class;
|
||||
}
|
||||
|
||||
# Sub: diff
|
||||
# Diff the variable values of <MySQLConfig> objects. Only the common
|
||||
# set of variables (i.e. the vars that all configs have) are compared.
|
||||
#
|
||||
# Parameters:
|
||||
# %args - Arguments
|
||||
#
|
||||
# Required Arguments:
|
||||
# configs - Arrayref of <MySQLConfig> objects
|
||||
#
|
||||
# Returns:
|
||||
# Hashref of variables that have different values, like
|
||||
# (start code)
|
||||
# {
|
||||
# max_connections => [ 100, 50 ]
|
||||
# }
|
||||
# (end code)
|
||||
# The arrayref vals correspond to the C<MySQLConfig> objects, so
|
||||
# $diff->{var}->[N] is $configs->[N]->value_of(var).
|
||||
sub diff {
|
||||
my ( $self, %args ) = @_;
|
||||
my @required_args = qw(configs);
|
||||
foreach my $arg( @required_args ) {
|
||||
die "I need a $arg argument" unless $args{$arg};
|
||||
}
|
||||
my ($configs) = @args{@required_args};
|
||||
|
||||
if ( @$configs < 2 ) {
|
||||
MKDEBUG && _d("Less than two MySQLConfig objects; nothing to compare");
|
||||
return;
|
||||
}
|
||||
|
||||
my $base_path = $self->{base_path};
|
||||
my $is_numeric = $self->{is_numeric};
|
||||
my $any_value_is_true = $self->{any_value_is_true};
|
||||
my $value_is_optional = $self->{value_is_optional};
|
||||
|
||||
# Get the vars that exist in all configs minus the ones we want to ignore.
|
||||
my $config0 = $configs->[0];
|
||||
my $last_config = @$configs - 1;
|
||||
my $vars = $self->_get_shared_vars(%args);
|
||||
|
||||
# Compare variables from first config (config0) to other configs (configN).
|
||||
my $diffs;
|
||||
VARIABLE:
|
||||
foreach my $var ( @$vars ) {
|
||||
my $is_dir = $var =~ m/dir$/ || $var eq 'language';
|
||||
my $val0 = $self->_normalize_value( # config0 value
|
||||
value => $config0->value_of($var),
|
||||
is_directory => $is_dir,
|
||||
base_path => $config0->value_of($base_path->{$var}) || "",
|
||||
);
|
||||
|
||||
eval {
|
||||
CONFIG:
|
||||
foreach my $configN ( @$configs[1..$last_config] ) {
|
||||
my $valN = $self->_normalize_value( # configN value
|
||||
value => $configN->value_of($var),
|
||||
is_directory => $is_dir,
|
||||
base_path => $configN->value_of($base_path->{$var}) || "",
|
||||
);
|
||||
|
||||
if ( $is_numeric->{$var} ) {
|
||||
next CONFIG if $val0 == $valN;
|
||||
}
|
||||
else {
|
||||
next CONFIG if $val0 eq $valN;
|
||||
|
||||
# Special rules apply when comparing different inputs/formats,
|
||||
# e.g. when comparing an option file to SHOW VARIABLES. This
|
||||
# is because certain difference are actually equal in different
|
||||
# formats.
|
||||
if ( $config0->format() ne $configN->format() ) {
|
||||
if ( $any_value_is_true->{$var} ) {
|
||||
next CONFIG if $val0 && $valN;
|
||||
}
|
||||
if ( $value_is_optional->{$var} ) {
|
||||
next CONFIG if (!$val0 && $valN) || ($val0 && !$valN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# We reach here if no comparison above was true and skipped
|
||||
# to the next CONFIG. So reaching here means the values are
|
||||
# different. We save the real, not-normalized values.
|
||||
MKDEBUG && _d("Different", $var, "values:", $val0, $valN);
|
||||
$diffs->{$var} = [ map { $_->value_of($var) } @$configs ];
|
||||
last CONFIG;
|
||||
} # CONFIG
|
||||
};
|
||||
if ( $EVAL_ERROR ) {
|
||||
my $vals = join(', ',
|
||||
map {
|
||||
my $val = $_->value_of($var);
|
||||
defined $val ? $val : 'undef'
|
||||
} @$configs);
|
||||
warn "Comparing $var values ($vals) caused an error: $EVAL_ERROR";
|
||||
}
|
||||
} # VARIABLE
|
||||
|
||||
return $diffs;
|
||||
}
|
||||
|
||||
# Sub: missing
|
||||
# Return variables that aren't in all the given <MySQLConfig> objects.
|
||||
#
|
||||
# Parameters:
|
||||
# %args - Arguments
|
||||
#
|
||||
# Required Arguments:
|
||||
# configs - Arrayref of C<MySQLConfig> objects
|
||||
#
|
||||
# Returns:
|
||||
# Hashref of missing variables like,
|
||||
# (start code)
|
||||
# {
|
||||
# query_cache_size => [0, 1]
|
||||
# }
|
||||
# (end code)
|
||||
# The arrayref vals correspond to the C<MySQLConfig> objects, so
|
||||
# $missing->{var}->[N] is $configs->[N]; the values are boolean:
|
||||
# 1 means the C<MySQLConfig> obj has the variable, 0 means it doesn't.
|
||||
sub missing {
|
||||
my ( $self, %args ) = @_;
|
||||
my @required_args = qw(configs);
|
||||
foreach my $arg( @required_args ) {
|
||||
die "I need a $arg argument" unless $args{$arg};
|
||||
}
|
||||
my ($configs) = @args{@required_args};
|
||||
|
||||
if ( @$configs < 2 ) {
|
||||
MKDEBUG && _d("Less than two MySQLConfig objects; nothing to compare");
|
||||
return;
|
||||
}
|
||||
|
||||
# Get a unique list of all vars from all configs.
|
||||
my %vars = map { $_ => 1 } map { keys %{$_->variables()} } @$configs;
|
||||
my $missing;
|
||||
foreach my $var ( keys %vars ) {
|
||||
# If the number of configs having the var is less than the number of
|
||||
# configs, then one of the configs must be missing the variable.
|
||||
my $n_configs_having_var = grep { $_->has($var) } @$configs;
|
||||
if ( $n_configs_having_var < @$configs ) {
|
||||
$missing->{$var} = [ map { $_->has($var) ? 1 : 0 } @$configs ];
|
||||
}
|
||||
}
|
||||
|
||||
return $missing;
|
||||
}
|
||||
|
||||
sub _normalize_value {
|
||||
my ( $self, %args ) = @_;
|
||||
my ($val, $is_dir, $base_path) = @args{qw(value is_directory base_path)};
|
||||
|
||||
$val = defined $val ? $val : '';
|
||||
$val = $alt_val_for{$val} if exists $alt_val_for{$val};
|
||||
|
||||
if ( $val ) {
|
||||
if ( $is_dir ) {
|
||||
$val .= '/' unless $val =~ m/\/$/;
|
||||
}
|
||||
if ( $base_path && $val !~ m/^\// ) {
|
||||
$val =~ s/^\.?(.+)/$base_path\/$1/; # prepend base path
|
||||
$val =~ s/\/{2,}/\//g; # make redundant // single /
|
||||
}
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
sub _get_shared_vars {
|
||||
my ( $self, %args ) = @_;
|
||||
my ($configs) = @args{qw(configs)};
|
||||
my $ignore_vars = $self->{ignore_vars};
|
||||
my $config0 = $configs->[0];
|
||||
my $last_config = @$configs - 1;
|
||||
my @vars
|
||||
= grep { !$ignore_vars->{$_} }
|
||||
map {
|
||||
my $config = $_;
|
||||
my $vars = $config->variables();
|
||||
grep { $config0->has($_); } keys %$vars;
|
||||
} @$configs[1..$last_config];
|
||||
return \@vars;
|
||||
}
|
||||
|
||||
sub _d {
|
||||
my ($package, undef, $line) = caller 0;
|
||||
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
||||
map { defined $_ ? $_ : 'undef' }
|
||||
@_;
|
||||
print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
|
||||
}
|
||||
|
||||
} # package scope
|
||||
1;
|
||||
|
||||
# ###########################################################################
|
||||
# End MySQLConfigComparer package
|
||||
# ###########################################################################
|
Reference in New Issue
Block a user