diff --git a/Changelog b/Changelog index 1fc2a2be..5ff66a01 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,41 @@ Changelog for Percona Toolkit +v2.1.8 released 2012-12-21 + + * Beta support for MySQL 5.6 + * Beta support for Percona XtraDB Cluster + * pt-online-schema-change: If ran on Percona XtraDB Cluster, requires PXC 5.5.28 or newer + * pt-table-checksum: If ran on Percona XtraDB Cluster, requires PXC 5.5.28 or newer + * pt-upgrade: Added --[no]disable-query-cache + * Fixed bug 927955: Bad pod2rst transformation + * Fixed bug 898665: Bad online docs formatting for --[no]vars + * Fixed bug 1022622: pt-config-diff is case-sensitive + * Fixed bug 1007938: pt-config-diff doesn't handle end-of-line comments + * Fixed bug 917770: pt-config-diff Use of uninitialized value in substitution (s///) at line 1996 + * Fixed bug 1082104: pt-deadlock-logger doesn't handle usernames with dashes + * Fixed bug 886059: pt-heartbeat handles timezones inconsistently + * Fixed bug 1086259: pt-kill --log-dsn timestamp is wrong + * Fixed bug 1015590: pt-mysql-summary doesn't handle renamed variables in Percona Server 5.5 + * Fixed bug 1079341: pt-online-schema-change checks for foreign keys on MyISAM tables + * Fixed bug 823431: pt-query-advisor hangs on big queries + * Fixed bug 996069: pt-query-advisor RES.001 is incorrect + * Fixed bug 933465: pt-query-advisor false positive on RES.001 + * Fixed bug 937234: pt-query-advisor issues wrong RES.001 + * Fixed bug 1082599: pt-query-digest fails to parse timestamp with no query + * Fixed bug 1078838: pt-query-digest doesn't parse general log with "Connect user as user" + * Fixed bug 957442: pt-query-digest with custom --group-by throws error + * Fixed bug 887638: pt-query-digest prints negative byte offset + * Fixed bug 831525: pt-query-digest help output mangled + * Fixed bug 932614: pt-slave-restart CHANGE MASTER query causes error + * Fixed bug 1046440: pt-stalk purge_samples slows down checks + * Fixed bug 986847: pt-stalk does not report NFS iostat + * Fixed bug 1074179: pt-table-checksum doesn't ignore tables for --replicate-check-only + * Fixed bug 911385: pt-table-checksum v2 fails when --resume + --ignore-database is used + * Fixed bug 1041391: pt-table-checksum debug statement for "Chosen hash func" prints undef + * Fixed bug 1075638: pt-table-checksum Illegal division by zero at line 7950 + * Fixed bug 1052475: pt-table-checksum uninitialized value in numeric lt (<) at line 8611 + * Fixed bug 1078887: Tools let --set-vars clobber the required SQL mode + v2.1.7 released 2012-11-19 * Fixed bug 1080384: pt-table-checksum 2.1.6 crashes using PTDEBUG diff --git a/Makefile.PL b/Makefile.PL index 0d74b097..d4c4c4d4 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -2,7 +2,7 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'percona-toolkit', - VERSION => '2.1.7', + VERSION => '2.1.8', EXE_FILES => [ ], MAN1PODS => { 'docs/percona-toolkit.pod' => 'blib/man1/percona-toolkit.1p', diff --git a/bin/pt-align b/bin/pt-align index d170249d..dd497446 100755 --- a/bin/pt-align +++ b/bin/pt-align @@ -199,8 +199,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -218,6 +218,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-align 2.1.7 +pt-align 2.1.8 =cut diff --git a/bin/pt-archiver b/bin/pt-archiver index 774f54b4..54aa0158 100755 --- a/bin/pt-archiver +++ b/bin/pt-archiver @@ -39,7 +39,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -7555,8 +7555,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -7574,6 +7574,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-archiver 2.1.7 +pt-archiver 2.1.8 =cut diff --git a/bin/pt-config-diff b/bin/pt-config-diff index d8995b7f..6a2bfc5f 100755 --- a/bin/pt-config-diff +++ b/bin/pt-config-diff @@ -14,6 +14,7 @@ use warnings FATAL => 'all'; BEGIN { $INC{$_} = __FILE__ for map { (my $pkg = "$_.pm") =~ s!::!/!g; $pkg } (qw( Percona::Toolkit + Mo OptionParser DSNParser Cxn @@ -38,7 +39,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -46,6 +47,470 @@ our $VERSION = '2.1.7'; # End Percona::Toolkit package # ########################################################################### +# ########################################################################### +# Mo package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/Mo.pm +# t/lib/Mo.t +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### +{ +BEGIN { +$INC{"Mo.pm"} = __FILE__; +package Mo; +our $VERSION = '0.30_Percona'; # Forked from 0.30 of Mo. + +{ + no strict 'refs'; + sub _glob_for { + return \*{shift()} + } + + sub _stash_for { + return \%{ shift() . "::" }; + } +} + +use strict; +use warnings qw( FATAL all ); + +use Carp (); +use Scalar::Util qw(looks_like_number blessed); + + +our %TYPES = ( + Bool => sub { !$_[0] || (defined $_[0] && looks_like_number($_[0]) && $_[0] == 1) }, + Num => sub { defined $_[0] && looks_like_number($_[0]) }, + Int => sub { defined $_[0] && looks_like_number($_[0]) && $_[0] == int($_[0]) }, + Str => sub { defined $_[0] }, + Object => sub { defined $_[0] && blessed($_[0]) }, + FileHandle => sub { local $@; require IO::Handle; fileno($_[0]) && $_[0]->opened }, + + map { + my $type = /R/ ? $_ : uc $_; + $_ . "Ref" => sub { ref $_[0] eq $type } + } qw(Array Code Hash Regexp Glob Scalar) +); + +our %metadata_for; +{ + package Mo::Object; + + sub new { + my $class = shift; + my $args = $class->BUILDARGS(@_); + + my @args_to_delete; + while ( my ($attr, $meta) = each %{$metadata_for{$class}} ) { + next unless exists $meta->{init_arg}; + my $init_arg = $meta->{init_arg}; + + if ( defined $init_arg ) { + $args->{$attr} = delete $args->{$init_arg}; + } + else { + push @args_to_delete, $attr; + } + } + + delete $args->{$_} for @args_to_delete; + + for my $attribute ( keys %$args ) { + if ( my $coerce = $metadata_for{$class}{$attribute}{coerce} ) { + $args->{$attribute} = $coerce->($args->{$attribute}); + } + if ( my $I = $metadata_for{$class}{$attribute}{isa} ) { + ( (my $I_name), $I ) = @{$I}; + Mo::_check_type_constaints($attribute, $I, $I_name, $args->{$attribute}); + } + } + + while ( my ($attribute, $meta) = each %{$metadata_for{$class}} ) { + next unless $meta->{required}; + Carp::confess("Attribute ($attribute) is required for $class") + if ! exists $args->{$attribute} + } + + @_ = %$args; + my $self = bless $args, $class; + + my @build_subs; + my $linearized_isa = mro::get_linear_isa($class); + + for my $isa_class ( @$linearized_isa ) { + unshift @build_subs, *{ Mo::_glob_for "${isa_class}::BUILD" }{CODE}; + } + exists &$_ && $_->( $self, @_ ) for grep { defined } @build_subs; + return $self; + } + + sub BUILDARGS { + shift; + my $ref; + if ( @_ == 1 && ref($_[0]) ) { + Carp::confess("Single parameters to new() must be a HASH ref") + unless ref($_[0]) eq ref({}); + $ref = {%{$_[0]}} # We want a new reference, always + } + else { + $ref = { @_ }; + } + return $ref; + } +} + +my %export_for; +%Mo::Internal::Keyword = map { $_ => 1 } qw(has extends override); + +sub Mo::import { + warnings->import(qw(FATAL all)); + strict->import(); + + my $caller = scalar caller(); # Caller's package + my $caller_pkg = $caller . "::"; # Caller's package with :: at the end + my (%exports, %options); + + my (undef, @features) = @_; + my %ignore = ( map { $_ => 1 } qw( is isa init_arg builder buildargs clearer predicate build handles default required ) ); + for my $feature (grep { !$ignore{$_} } @features) { + { local $@; require "Mo/$feature.pm"; } + { + no strict 'refs'; + &{"Mo::${feature}::e"}( + $caller_pkg, + \%exports, + \%options, + \@_ + ); + } + } + + return if $exports{M}; + + %exports = ( + extends => sub { + for my $class ( map { "$_" } @_ ) { + $class =~ s{::|'}{/}g; + { local $@; eval { require "$class.pm" } } # or warn $@; + } + _set_package_isa($caller, @_); + _set_inherited_metadata($caller); + }, + override => \&override, + has => sub { + my $names = shift; + for my $attribute ( ref $names ? @$names : $names ) { + my %args = @_; + my $method = ($args{is} || '') eq 'ro' + ? sub { + Carp::confess("Cannot assign a value to a read-only accessor at reader ${caller_pkg}${attribute}") + if $#_; + return $_[0]{$attribute}; + } + : sub { + return $#_ + ? $_[0]{$attribute} = $_[1] + : $_[0]{$attribute}; + }; + + $metadata_for{$caller}{$attribute} = (); + + if ( my $I = $args{isa} ) { + my $orig_I = $I; + my $type; + if ( $I =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { + $I = _nested_constraints($attribute, $1, $2); + } + $metadata_for{$caller}{$attribute}{isa} = [$orig_I, $I]; + my $orig_method = $method; + $method = sub { + if ( $#_ ) { + Mo::_check_type_constaints($attribute, $I, $orig_I, $_[1]); + } + goto &$orig_method; + }; + } + + if ( my $builder = $args{builder} ) { + my $original_method = $method; + $method = sub { + $#_ + ? goto &$original_method + : ! exists $_[0]{$attribute} + ? $_[0]{$attribute} = $_[0]->$builder + : goto &$original_method + }; + } + + if ( my $code = $args{default} ) { + Carp::confess("${caller}::${attribute}'s default is $code, but should be a coderef") + unless ref($code) eq 'CODE'; + my $original_method = $method; + $method = sub { + $#_ + ? goto &$original_method + : ! exists $_[0]{$attribute} + ? $_[0]{$attribute} = $_[0]->$code + : goto &$original_method + }; + } + + if ( my $role = $args{does} ) { + my $original_method = $method; + $method = sub { + if ( $#_ ) { + Carp::confess(qq) + unless Scalar::Util::blessed($_[1]) && eval { $_[1]->does($role) } + } + goto &$original_method + }; + } + + if ( my $coercion = $args{coerce} ) { + $metadata_for{$caller}{$attribute}{coerce} = $coercion; + my $original_method = $method; + $method = sub { + if ( $#_ ) { + return $original_method->($_[0], $coercion->($_[1])) + } + goto &$original_method; + } + } + + $method = $options{$_}->($method, $attribute, @_) + for sort keys %options; + + *{ _glob_for "${caller}::$attribute" } = $method; + + if ( $args{required} ) { + $metadata_for{$caller}{$attribute}{required} = 1; + } + + if ($args{clearer}) { + *{ _glob_for "${caller}::$args{clearer}" } + = sub { delete shift->{$attribute} } + } + + if ($args{predicate}) { + *{ _glob_for "${caller}::$args{predicate}" } + = sub { exists shift->{$attribute} } + } + + if ($args{handles}) { + _has_handles($caller, $attribute, \%args); + } + + if (exists $args{init_arg}) { + $metadata_for{$caller}{$attribute}{init_arg} = $args{init_arg}; + } + } + }, + %exports, + ); + + $export_for{$caller} = [ keys %exports ]; + + for my $keyword ( keys %exports ) { + *{ _glob_for "${caller}::$keyword" } = $exports{$keyword} + } + *{ _glob_for "${caller}::extends" }{CODE}->( "Mo::Object" ) + unless @{ *{ _glob_for "${caller}::ISA" }{ARRAY} || [] }; +}; + +sub _check_type_constaints { + my ($attribute, $I, $I_name, $val) = @_; + ( ref($I) eq 'CODE' + ? $I->($val) + : (ref $val eq $I + || ($val && $val eq $I) + || (exists $TYPES{$I} && $TYPES{$I}->($val))) + ) + || Carp::confess( + qq + . qq + . (defined $val ? Mo::Dumper($val) : 'undef') ) +} + +sub _has_handles { + my ($caller, $attribute, $args) = @_; + my $handles = $args->{handles}; + + my $ref = ref $handles; + my $kv; + if ( $ref eq ref [] ) { + $kv = { map { $_,$_ } @{$handles} }; + } + elsif ( $ref eq ref {} ) { + $kv = $handles; + } + elsif ( $ref eq ref qr// ) { + Carp::confess("Cannot delegate methods based on a Regexp without a type constraint (isa)") + unless $args->{isa}; + my $target_class = $args->{isa}; + $kv = { + map { $_, $_ } + grep { $_ =~ $handles } + grep { !exists $Mo::Object::{$_} && $target_class->can($_) } + grep { !$Mo::Internal::Keyword{$_} } + keys %{ _stash_for $target_class } + }; + } + else { + Carp::confess("handles for $ref not yet implemented"); + } + + while ( my ($method, $target) = each %{$kv} ) { + my $name = _glob_for "${caller}::$method"; + Carp::confess("You cannot overwrite a locally defined method ($method) with a delegation") + if defined &$name; + + my ($target, @curried_args) = ref($target) ? @$target : $target; + *$name = sub { + my $self = shift; + my $delegate_to = $self->$attribute(); + my $error = "Cannot delegate $method to $target because the value of $attribute"; + Carp::confess("$error is not defined") unless $delegate_to; + Carp::confess("$error is not an object (got '$delegate_to')") + unless Scalar::Util::blessed($delegate_to) || (!ref($delegate_to) && $delegate_to->can($target)); + return $delegate_to->$target(@curried_args, @_); + } + } +} + +sub _nested_constraints { + my ($attribute, $aggregate_type, $type) = @_; + + my $inner_types; + if ( $type =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { + $inner_types = _nested_constraints($1, $2); + } + else { + $inner_types = $TYPES{$type}; + } + + if ( $aggregate_type eq 'ArrayRef' ) { + return sub { + my ($val) = @_; + return unless ref($val) eq ref([]); + + if ($inner_types) { + for my $value ( @{$val} ) { + return unless $inner_types->($value) + } + } + else { + for my $value ( @{$val} ) { + return unless $value && ($value eq $type + || (Scalar::Util::blessed($value) && $value->isa($type))); + } + } + return 1; + }; + } + elsif ( $aggregate_type eq 'Maybe' ) { + return sub { + my ($value) = @_; + return 1 if ! defined($value); + if ($inner_types) { + return unless $inner_types->($value) + } + else { + return unless $value eq $type + || (Scalar::Util::blessed($value) && $value->isa($type)); + } + return 1; + } + } + else { + Carp::confess("Nested aggregate types are only implemented for ArrayRefs and Maybe"); + } +} + +sub _set_package_isa { + my ($package, @new_isa) = @_; + + *{ _glob_for "${package}::ISA" } = [@new_isa]; +} + +sub _set_inherited_metadata { + my $class = shift; + my $linearized_isa = mro::get_linear_isa($class); + my %new_metadata; + + for my $isa_class (reverse @$linearized_isa) { + %new_metadata = ( + %new_metadata, + %{ $metadata_for{$isa_class} || {} }, + ); + } + $metadata_for{$class} = \%new_metadata; +} + +sub unimport { + my $caller = scalar caller(); + my $stash = _stash_for( $caller ); + + delete $stash->{$_} for @{$export_for{$caller}}; +} + +sub Dumper { + require Data::Dumper; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Sortkeys = 0; + local $Data::Dumper::Quotekeys = 0; + local $Data::Dumper::Terse = 1; + + Data::Dumper::Dumper(@_) +} + +BEGIN { + if ($] >= 5.010) { + { local $@; require mro; } + } + else { + local $@; + eval { + require MRO::Compat; + } or do { + *mro::get_linear_isa = *mro::get_linear_isa_dfs = sub { + no strict 'refs'; + + my $classname = shift; + + my @lin = ($classname); + my %stored; + foreach my $parent (@{"$classname\::ISA"}) { + my $plin = mro::get_linear_isa_dfs($parent); + foreach (@$plin) { + next if exists $stored{$_}; + push(@lin, $_); + $stored{$_} = 1; + } + } + return \@lin; + }; + } + } +} + +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + +} +1; +} +# ########################################################################### +# End Mo package +# ########################################################################### + # ########################################################################### # OptionParser package # This package is a copy without comments from the original. The original @@ -2641,8 +3106,7 @@ sub _d { { package ReportFormatter; -use strict; -use warnings FATAL => 'all'; +use Mo; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG} || 0; @@ -2652,40 +3116,102 @@ use POSIX qw(ceil); eval { require Term::ReadKey }; my $have_term = $EVAL_ERROR ? 0 : 1; -sub new { - my ( $class, %args ) = @_; - my @required_args = qw(); - foreach my $arg ( @required_args ) { - die "I need a $arg argument" unless $args{$arg}; - } - my $self = { - underline_header => 1, - line_prefix => '# ', - line_width => 78, - column_spacing => ' ', - extend_right => 0, - truncate_line_mark => '...', - column_errors => 'warn', - truncate_header_side => 'left', - strip_whitespace => 1, - %args, # args above can be overriden, args below cannot - n_cols => 0, - }; - if ( ($self->{line_width} || '') eq 'auto' ) { +has underline_header => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has line_prefix => ( + is => 'ro', + isa => 'Str', + default => sub { '# ' }, +); +has line_width => ( + is => 'ro', + isa => 'Int', + default => sub { 78 }, +); +has column_spacing => ( + is => 'ro', + isa => 'Str', + default => sub { ' ' }, +); +has extend_right => ( + is => 'ro', + isa => 'Bool', + default => sub { '' }, +); +has truncate_line_mark => ( + is => 'ro', + isa => 'Str', + default => sub { '...' }, +); +has column_errors => ( + is => 'ro', + isa => 'Str', + default => sub { 'warn' }, +); +has truncate_header_side => ( + is => 'ro', + isa => 'Str', + default => sub { 'left' }, +); +has strip_whitespace => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has title => ( + is => 'rw', + isa => 'Str', + predicate => 'has_title', +); + + +has n_cols => ( + is => 'rw', + isa => 'Int', + default => sub { 0 }, + init_arg => undef, +); + +has cols => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_cols', +); + +has lines => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_lines', +); + +has truncate_headers => ( + is => 'rw', + isa => 'Bool', + default => sub { undef }, + init_arg => undef, + clearer => 'clear_truncate_headers', +); + +sub BUILDARGS { + my $class = shift; + my $args = $class->SUPER::BUILDARGS(@_); + + if ( ($args->{line_width} || '') eq 'auto' ) { die "Cannot auto-detect line width because the Term::ReadKey module " . "is not installed" unless $have_term; - ($self->{line_width}) = GetTerminalSize(); + ($args->{line_width}) = GetTerminalSize(); + PTDEBUG && _d('Line width:', $args->{line_width}); } - PTDEBUG && _d('Line width:', $self->{line_width}); - return bless $self, $class; -} - -sub set_title { - my ( $self, $title ) = @_; - $self->{title} = $title; - return; + return $args; } sub set_columns { @@ -2701,7 +3227,7 @@ sub set_columns { die "Column does not have a name" unless defined $col_name; if ( $col->{width} ) { - $col->{width_pct} = ceil(($col->{width} * 100) / $self->{line_width}); + $col->{width_pct} = ceil(($col->{width} * 100) / $self->line_width()); PTDEBUG && _d('col:', $col_name, 'width:', $col->{width}, 'chars =', $col->{width_pct}, '%'); } @@ -2728,10 +3254,10 @@ sub set_columns { $col->{right_most} = 1 if $i == $#cols; - push @{$self->{cols}}, $col; + push @{$self->cols}, $col; } - $self->{n_cols} = scalar @cols; + $self->n_cols( scalar @cols ); if ( ($used_width || 0) > 100 ) { die "Total width_pct for all columns is >100%"; @@ -2741,15 +3267,15 @@ sub set_columns { my $wid_per_col = int((100 - $used_width) / scalar @auto_width_cols); PTDEBUG && _d('Line width left:', (100-$used_width), '%;', 'each auto width col:', $wid_per_col, '%'); - map { $self->{cols}->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; + map { $self->cols->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; } - $min_hdr_wid += ($self->{n_cols} - 1) * length $self->{column_spacing}; + $min_hdr_wid += ($self->n_cols() - 1) * length $self->column_spacing(); PTDEBUG && _d('min header width:', $min_hdr_wid); - if ( $min_hdr_wid > $self->{line_width} ) { + if ( $min_hdr_wid > $self->line_width() ) { PTDEBUG && _d('Will truncate headers because min header width', - $min_hdr_wid, '> line width', $self->{line_width}); - $self->{truncate_headers} = 1; + $min_hdr_wid, '> line width', $self->line_width()); + $self->truncate_headers(1); } return; @@ -2758,14 +3284,14 @@ sub set_columns { sub add_line { my ( $self, @vals ) = @_; my $n_vals = scalar @vals; - if ( $n_vals != $self->{n_cols} ) { + if ( $n_vals != $self->n_cols() ) { $self->_column_error("Number of values $n_vals does not match " - . "number of columns $self->{n_cols}"); + . "number of columns " . $self->n_cols()); } for my $i ( 0..($n_vals-1) ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals[$i] ? $vals[$i] : $col->{undef_value}; - if ( $self->{strip_whitespace} ) { + if ( $self->strip_whitespace() ) { $val =~ s/^\s+//g; $val =~ s/\s+$//; $vals[$i] = $val; @@ -2774,7 +3300,7 @@ sub add_line { $col->{min_val} = min($width, ($col->{min_val} || $width)); $col->{max_val} = max($width, ($col->{max_val} || $width)); } - push @{$self->{lines}}, \@vals; + push @{$self->lines}, \@vals; return; } @@ -2782,26 +3308,28 @@ sub get_report { my ( $self, %args ) = @_; $self->_calculate_column_widths(); - $self->_truncate_headers() if $self->{truncate_headers}; + if ( $self->truncate_headers() ) { + $self->_truncate_headers(); + } $self->_truncate_line_values(%args); my @col_fmts = $self->_make_column_formats(); - my $fmt = ($self->{line_prefix} || '') - . join($self->{column_spacing}, @col_fmts); + my $fmt = $self->line_prefix() + . join($self->column_spacing(), @col_fmts); PTDEBUG && _d('Format:', $fmt); (my $hdr_fmt = $fmt) =~ s/%([^-])/%-$1/g; my @lines; - push @lines, sprintf "$self->{line_prefix}$self->{title}" if $self->{title}; + push @lines, $self->line_prefix() . $self->title() if $self->has_title(); push @lines, $self->_truncate_line( - sprintf($hdr_fmt, map { $_->{name} } @{$self->{cols}}), + sprintf($hdr_fmt, map { $_->{name} } @{$self->cols}), strip => 1, mark => '', ); - if ( $self->{underline_header} ) { - my @underlines = map { '=' x $_->{print_width} } @{$self->{cols}}; + if ( $self->underline_header() ) { + my @underlines = map { '=' x $_->{print_width} } @{$self->cols}; push @lines, $self->_truncate_line( sprintf($fmt, map { $_ || '' } @underlines), mark => '', @@ -2812,19 +3340,23 @@ sub get_report { my $vals = $_; my $i = 0; my @vals = map { - my $val = defined $_ ? $_ : $self->{cols}->[$i++]->{undef_value}; + my $val = defined $_ ? $_ : $self->cols->[$i++]->{undef_value}; $val = '' if !defined $val; $val =~ s/\n/ /g; $val; } @$vals; my $line = sprintf($fmt, @vals); - if ( $self->{extend_right} ) { + if ( $self->extend_right() ) { $line; } else { $self->_truncate_line($line); } - } @{$self->{lines}}; + } @{$self->lines}; + + $self->clear_cols(); + $self->clear_lines(); + $self->clear_truncate_headers(); return join("\n", @lines) . "\n"; } @@ -2832,7 +3364,7 @@ sub get_report { sub truncate_value { my ( $self, $col, $val, $width, $side ) = @_; return $val if length $val <= $width; - return $val if $col->{right_most} && $self->{extend_right}; + return $val if $col->{right_most} && $self->extend_right(); $side ||= $col->{truncate_side}; my $mark = $col->{truncate_mark}; if ( $side eq 'right' ) { @@ -2852,8 +3384,8 @@ sub _calculate_column_widths { my ( $self ) = @_; my $extra_space = 0; - foreach my $col ( @{$self->{cols}} ) { - my $print_width = int($self->{line_width} * ($col->{width_pct} / 100)); + foreach my $col ( @{$self->cols} ) { + my $print_width = int($self->line_width() * ($col->{width_pct} / 100)); PTDEBUG && _d('col:', $col->{name}, 'width pct:', $col->{width_pct}, 'char width:', $print_width, @@ -2877,7 +3409,7 @@ sub _calculate_column_widths { PTDEBUG && _d('Extra space:', $extra_space); while ( $extra_space-- ) { - foreach my $col ( @{$self->{cols}} ) { + foreach my $col ( @{$self->cols} ) { if ( $col->{auto_width} && ( $col->{print_width} < $col->{max_val} || $col->{print_width} < $col->{header_width}) @@ -2892,8 +3424,8 @@ sub _calculate_column_widths { sub _truncate_headers { my ( $self, $col ) = @_; - my $side = $self->{truncate_header_side}; - foreach my $col ( @{$self->{cols}} ) { + my $side = $self->truncate_header_side(); + foreach my $col ( @{$self->cols} ) { my $col_name = $col->{name}; my $print_width = $col->{print_width}; next if length $col_name <= $print_width; @@ -2906,10 +3438,10 @@ sub _truncate_headers { sub _truncate_line_values { my ( $self, %args ) = @_; - my $n_vals = $self->{n_cols} - 1; - foreach my $vals ( @{$self->{lines}} ) { + my $n_vals = $self->n_cols() - 1; + foreach my $vals ( @{$self->lines} ) { for my $i ( 0..$n_vals ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals->[$i] ? $vals->[$i] : $col->{undef_value}; my $width = length $val; @@ -2935,9 +3467,9 @@ sub _truncate_line_values { sub _make_column_formats { my ( $self ) = @_; my @col_fmts; - my $n_cols = $self->{n_cols} - 1; + my $n_cols = $self->n_cols() - 1; for my $i ( 0..$n_cols ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $width = $col->{right_most} && !$col->{right_justify} ? '' : $col->{print_width}; @@ -2950,12 +3482,12 @@ sub _make_column_formats { sub _truncate_line { my ( $self, $line, %args ) = @_; - my $mark = defined $args{mark} ? $args{mark} : $self->{truncate_line_mark}; + my $mark = defined $args{mark} ? $args{mark} : $self->truncate_line_mark(); if ( $line ) { $line =~ s/\s+$// if $args{strip}; my $len = length($line); - if ( $len > $self->{line_width} ) { - $line = substr($line, 0, $self->{line_width} - length $mark); + if ( $len > $self->line_width() ) { + $line = substr($line, 0, $self->line_width() - length $mark); $line .= $mark if $mark; } } @@ -2965,7 +3497,7 @@ sub _truncate_line { sub _column_error { my ( $self, $err ) = @_; my $msg = "Column error: $err"; - $self->{column_errors} eq 'die' ? die $msg : warn $msg; + $self->column_errors() eq 'die' ? die $msg : warn $msg; return; } @@ -4443,7 +4975,7 @@ sub main { foreach my $var ( sort keys %$diffs ) { $report->add_line($var, @{$diffs->{$var}}); } - $report->set_title( + $report->title( "$n_diffs config difference" . ($n_diffs > 1 ? 's' : '')); print $report->get_report(); } @@ -4853,8 +5385,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -4872,6 +5403,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-config-diff 2.1.7 +pt-config-diff 2.1.8 =cut diff --git a/bin/pt-deadlock-logger b/bin/pt-deadlock-logger index 9e351a0f..601fdba2 100755 --- a/bin/pt-deadlock-logger +++ b/bin/pt-deadlock-logger @@ -36,7 +36,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -4766,8 +4766,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -4785,6 +4785,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-deadlock-logger 2.1.7 +pt-deadlock-logger 2.1.8 =cut diff --git a/bin/pt-diskstats b/bin/pt-diskstats index 4e7193df..dc5dc822 100755 --- a/bin/pt-diskstats +++ b/bin/pt-diskstats @@ -38,7 +38,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -1240,6 +1240,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -5548,8 +5551,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -5567,6 +5570,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-diskstats 2.1.7 +pt-diskstats 2.1.8 =cut diff --git a/bin/pt-duplicate-key-checker b/bin/pt-duplicate-key-checker index 7c382bc6..fbc359ef 100755 --- a/bin/pt-duplicate-key-checker +++ b/bin/pt-duplicate-key-checker @@ -39,7 +39,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -5416,8 +5416,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -5435,6 +5435,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-duplicate-key-checker 2.1.7 +pt-duplicate-key-checker 2.1.8 =cut diff --git a/bin/pt-fifo-split b/bin/pt-fifo-split index 37f8f693..06303be6 100755 --- a/bin/pt-fifo-split +++ b/bin/pt-fifo-split @@ -1549,8 +1549,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -1568,6 +1568,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-fifo-split 2.1.7 +pt-fifo-split 2.1.8 =cut diff --git a/bin/pt-find b/bin/pt-find index 89c4a6b7..70a54a2b 100755 --- a/bin/pt-find +++ b/bin/pt-find @@ -35,7 +35,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -4879,8 +4879,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -4898,6 +4898,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-find 2.1.7 +pt-find 2.1.8 =cut diff --git a/bin/pt-fingerprint b/bin/pt-fingerprint index 4595bc02..973b4f4c 100755 --- a/bin/pt-fingerprint +++ b/bin/pt-fingerprint @@ -2129,8 +2129,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2012 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -2148,6 +2147,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-fingerprint 2.1.7 +pt-fingerprint 2.1.8 =cut diff --git a/bin/pt-fk-error-logger b/bin/pt-fk-error-logger index e09cf965..d2a2eb90 100755 --- a/bin/pt-fk-error-logger +++ b/bin/pt-fk-error-logger @@ -35,7 +35,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -4011,8 +4011,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2012 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -4030,6 +4029,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-fk-error-logger 2.1.7 +pt-fk-error-logger 2.1.8 =cut diff --git a/bin/pt-heartbeat b/bin/pt-heartbeat index 7cf8b44b..35ab1432 100755 --- a/bin/pt-heartbeat +++ b/bin/pt-heartbeat @@ -38,7 +38,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -3168,6 +3168,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -6023,9 +6026,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2006 Proven Scaling LLC and Six Apart Ltd, -2007-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2007-2013 Percona Ireland Ltd, +2006 Proven Scaling LLC and Six Apart Ltd. Feedback and improvements are welcome. @@ -6045,6 +6047,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-heartbeat 2.1.7 +pt-heartbeat 2.1.8 =cut diff --git a/bin/pt-index-usage b/bin/pt-index-usage index 185c56ee..7049e3fa 100755 --- a/bin/pt-index-usage +++ b/bin/pt-index-usage @@ -45,7 +45,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -2713,14 +2713,25 @@ sub parse_event { if ( !$found_arg && $pos == $len ) { PTDEBUG && _d("Did not find arg, looking for special cases"); - local $INPUT_RECORD_SEPARATOR = ";\n"; + local $INPUT_RECORD_SEPARATOR = ";\n"; # get next line if ( defined(my $l = $next_event->()) ) { - chomp $l; - $l =~ s/^\s+//; - PTDEBUG && _d("Found admin statement", $l); - push @properties, 'cmd', 'Admin', 'arg', $l; - push @properties, 'bytes', length($properties[-1]); - $found_arg++; + if ( $l =~ /^\s*[A-Z][a-z_]+: / ) { + PTDEBUG && _d("Found NULL query before", $l); + local $INPUT_RECORD_SEPARATOR = ";\n#"; + my $rest_of_event = $next_event->(); + push @{$self->{pending}}, $l . $rest_of_event; + push @properties, 'cmd', 'Query', 'arg', '/* No query */'; + push @properties, 'bytes', 0; + $found_arg++; + } + else { + chomp $l; + $l =~ s/^\s+//; + PTDEBUG && _d("Found admin statement", $l); + push @properties, 'cmd', 'Admin', 'arg', $l; + push @properties, 'bytes', length($properties[-1]); + $found_arg++; + } } else { PTDEBUG && _d("I can't figure out what to do with this line"); @@ -3349,6 +3360,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -7450,8 +7464,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -7469,6 +7483,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-index-usage 2.1.7 +pt-index-usage 2.1.8 =cut diff --git a/bin/pt-ioprofile b/bin/pt-ioprofile index d97f6040..5f4a0c57 100755 --- a/bin/pt-ioprofile +++ b/bin/pt-ioprofile @@ -69,7 +69,7 @@ PO_DIR="" # Directory with program option spec files usage() { local file="$1" - local usage=$(grep '^Usage: ' "$file") + local usage="$(grep '^Usage: ' "$file")" echo $usage echo echo "For more information, 'man $TOOL' or 'perldoc $file'." @@ -1084,8 +1084,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -1103,7 +1103,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-ioprofile 2.1.7 +pt-ioprofile 2.1.8 =cut diff --git a/bin/pt-kill b/bin/pt-kill index 600705db..d6fa3869 100755 --- a/bin/pt-kill +++ b/bin/pt-kill @@ -43,7 +43,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -2275,6 +2275,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -7767,8 +7770,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2009-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2009-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -7786,6 +7789,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-kill 2.1.7 +pt-kill 2.1.8 =cut diff --git a/bin/pt-mext b/bin/pt-mext index 9a1d1779..34222b78 100755 --- a/bin/pt-mext +++ b/bin/pt-mext @@ -263,8 +263,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -282,7 +282,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-mext 2.1.7 +pt-mext 2.1.8 =cut diff --git a/bin/pt-mysql-summary b/bin/pt-mysql-summary index 3809408d..f5a40676 100755 --- a/bin/pt-mysql-summary +++ b/bin/pt-mysql-summary @@ -2983,8 +2983,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -3002,7 +3002,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-mysql-summary 2.1.7 +pt-mysql-summary 2.1.8 =cut diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index d3120b26..d65a8ee7 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -51,7 +51,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -1232,6 +1232,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1528,6 +1529,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -2319,8 +2330,7 @@ sub _d { { package ReportFormatter; -use strict; -use warnings FATAL => 'all'; +use Mo; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG} || 0; @@ -2330,40 +2340,102 @@ use POSIX qw(ceil); eval { require Term::ReadKey }; my $have_term = $EVAL_ERROR ? 0 : 1; -sub new { - my ( $class, %args ) = @_; - my @required_args = qw(); - foreach my $arg ( @required_args ) { - die "I need a $arg argument" unless $args{$arg}; - } - my $self = { - underline_header => 1, - line_prefix => '# ', - line_width => 78, - column_spacing => ' ', - extend_right => 0, - truncate_line_mark => '...', - column_errors => 'warn', - truncate_header_side => 'left', - strip_whitespace => 1, - %args, # args above can be overriden, args below cannot - n_cols => 0, - }; - if ( ($self->{line_width} || '') eq 'auto' ) { +has underline_header => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has line_prefix => ( + is => 'ro', + isa => 'Str', + default => sub { '# ' }, +); +has line_width => ( + is => 'ro', + isa => 'Int', + default => sub { 78 }, +); +has column_spacing => ( + is => 'ro', + isa => 'Str', + default => sub { ' ' }, +); +has extend_right => ( + is => 'ro', + isa => 'Bool', + default => sub { '' }, +); +has truncate_line_mark => ( + is => 'ro', + isa => 'Str', + default => sub { '...' }, +); +has column_errors => ( + is => 'ro', + isa => 'Str', + default => sub { 'warn' }, +); +has truncate_header_side => ( + is => 'ro', + isa => 'Str', + default => sub { 'left' }, +); +has strip_whitespace => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has title => ( + is => 'rw', + isa => 'Str', + predicate => 'has_title', +); + + +has n_cols => ( + is => 'rw', + isa => 'Int', + default => sub { 0 }, + init_arg => undef, +); + +has cols => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_cols', +); + +has lines => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_lines', +); + +has truncate_headers => ( + is => 'rw', + isa => 'Bool', + default => sub { undef }, + init_arg => undef, + clearer => 'clear_truncate_headers', +); + +sub BUILDARGS { + my $class = shift; + my $args = $class->SUPER::BUILDARGS(@_); + + if ( ($args->{line_width} || '') eq 'auto' ) { die "Cannot auto-detect line width because the Term::ReadKey module " . "is not installed" unless $have_term; - ($self->{line_width}) = GetTerminalSize(); + ($args->{line_width}) = GetTerminalSize(); + PTDEBUG && _d('Line width:', $args->{line_width}); } - PTDEBUG && _d('Line width:', $self->{line_width}); - return bless $self, $class; -} - -sub set_title { - my ( $self, $title ) = @_; - $self->{title} = $title; - return; + return $args; } sub set_columns { @@ -2379,7 +2451,7 @@ sub set_columns { die "Column does not have a name" unless defined $col_name; if ( $col->{width} ) { - $col->{width_pct} = ceil(($col->{width} * 100) / $self->{line_width}); + $col->{width_pct} = ceil(($col->{width} * 100) / $self->line_width()); PTDEBUG && _d('col:', $col_name, 'width:', $col->{width}, 'chars =', $col->{width_pct}, '%'); } @@ -2406,10 +2478,10 @@ sub set_columns { $col->{right_most} = 1 if $i == $#cols; - push @{$self->{cols}}, $col; + push @{$self->cols}, $col; } - $self->{n_cols} = scalar @cols; + $self->n_cols( scalar @cols ); if ( ($used_width || 0) > 100 ) { die "Total width_pct for all columns is >100%"; @@ -2419,15 +2491,15 @@ sub set_columns { my $wid_per_col = int((100 - $used_width) / scalar @auto_width_cols); PTDEBUG && _d('Line width left:', (100-$used_width), '%;', 'each auto width col:', $wid_per_col, '%'); - map { $self->{cols}->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; + map { $self->cols->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; } - $min_hdr_wid += ($self->{n_cols} - 1) * length $self->{column_spacing}; + $min_hdr_wid += ($self->n_cols() - 1) * length $self->column_spacing(); PTDEBUG && _d('min header width:', $min_hdr_wid); - if ( $min_hdr_wid > $self->{line_width} ) { + if ( $min_hdr_wid > $self->line_width() ) { PTDEBUG && _d('Will truncate headers because min header width', - $min_hdr_wid, '> line width', $self->{line_width}); - $self->{truncate_headers} = 1; + $min_hdr_wid, '> line width', $self->line_width()); + $self->truncate_headers(1); } return; @@ -2436,14 +2508,14 @@ sub set_columns { sub add_line { my ( $self, @vals ) = @_; my $n_vals = scalar @vals; - if ( $n_vals != $self->{n_cols} ) { + if ( $n_vals != $self->n_cols() ) { $self->_column_error("Number of values $n_vals does not match " - . "number of columns $self->{n_cols}"); + . "number of columns " . $self->n_cols()); } for my $i ( 0..($n_vals-1) ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals[$i] ? $vals[$i] : $col->{undef_value}; - if ( $self->{strip_whitespace} ) { + if ( $self->strip_whitespace() ) { $val =~ s/^\s+//g; $val =~ s/\s+$//; $vals[$i] = $val; @@ -2452,7 +2524,7 @@ sub add_line { $col->{min_val} = min($width, ($col->{min_val} || $width)); $col->{max_val} = max($width, ($col->{max_val} || $width)); } - push @{$self->{lines}}, \@vals; + push @{$self->lines}, \@vals; return; } @@ -2460,26 +2532,28 @@ sub get_report { my ( $self, %args ) = @_; $self->_calculate_column_widths(); - $self->_truncate_headers() if $self->{truncate_headers}; + if ( $self->truncate_headers() ) { + $self->_truncate_headers(); + } $self->_truncate_line_values(%args); my @col_fmts = $self->_make_column_formats(); - my $fmt = ($self->{line_prefix} || '') - . join($self->{column_spacing}, @col_fmts); + my $fmt = $self->line_prefix() + . join($self->column_spacing(), @col_fmts); PTDEBUG && _d('Format:', $fmt); (my $hdr_fmt = $fmt) =~ s/%([^-])/%-$1/g; my @lines; - push @lines, sprintf "$self->{line_prefix}$self->{title}" if $self->{title}; + push @lines, $self->line_prefix() . $self->title() if $self->has_title(); push @lines, $self->_truncate_line( - sprintf($hdr_fmt, map { $_->{name} } @{$self->{cols}}), + sprintf($hdr_fmt, map { $_->{name} } @{$self->cols}), strip => 1, mark => '', ); - if ( $self->{underline_header} ) { - my @underlines = map { '=' x $_->{print_width} } @{$self->{cols}}; + if ( $self->underline_header() ) { + my @underlines = map { '=' x $_->{print_width} } @{$self->cols}; push @lines, $self->_truncate_line( sprintf($fmt, map { $_ || '' } @underlines), mark => '', @@ -2490,19 +2564,23 @@ sub get_report { my $vals = $_; my $i = 0; my @vals = map { - my $val = defined $_ ? $_ : $self->{cols}->[$i++]->{undef_value}; + my $val = defined $_ ? $_ : $self->cols->[$i++]->{undef_value}; $val = '' if !defined $val; $val =~ s/\n/ /g; $val; } @$vals; my $line = sprintf($fmt, @vals); - if ( $self->{extend_right} ) { + if ( $self->extend_right() ) { $line; } else { $self->_truncate_line($line); } - } @{$self->{lines}}; + } @{$self->lines}; + + $self->clear_cols(); + $self->clear_lines(); + $self->clear_truncate_headers(); return join("\n", @lines) . "\n"; } @@ -2510,7 +2588,7 @@ sub get_report { sub truncate_value { my ( $self, $col, $val, $width, $side ) = @_; return $val if length $val <= $width; - return $val if $col->{right_most} && $self->{extend_right}; + return $val if $col->{right_most} && $self->extend_right(); $side ||= $col->{truncate_side}; my $mark = $col->{truncate_mark}; if ( $side eq 'right' ) { @@ -2530,8 +2608,8 @@ sub _calculate_column_widths { my ( $self ) = @_; my $extra_space = 0; - foreach my $col ( @{$self->{cols}} ) { - my $print_width = int($self->{line_width} * ($col->{width_pct} / 100)); + foreach my $col ( @{$self->cols} ) { + my $print_width = int($self->line_width() * ($col->{width_pct} / 100)); PTDEBUG && _d('col:', $col->{name}, 'width pct:', $col->{width_pct}, 'char width:', $print_width, @@ -2555,7 +2633,7 @@ sub _calculate_column_widths { PTDEBUG && _d('Extra space:', $extra_space); while ( $extra_space-- ) { - foreach my $col ( @{$self->{cols}} ) { + foreach my $col ( @{$self->cols} ) { if ( $col->{auto_width} && ( $col->{print_width} < $col->{max_val} || $col->{print_width} < $col->{header_width}) @@ -2570,8 +2648,8 @@ sub _calculate_column_widths { sub _truncate_headers { my ( $self, $col ) = @_; - my $side = $self->{truncate_header_side}; - foreach my $col ( @{$self->{cols}} ) { + my $side = $self->truncate_header_side(); + foreach my $col ( @{$self->cols} ) { my $col_name = $col->{name}; my $print_width = $col->{print_width}; next if length $col_name <= $print_width; @@ -2584,10 +2662,10 @@ sub _truncate_headers { sub _truncate_line_values { my ( $self, %args ) = @_; - my $n_vals = $self->{n_cols} - 1; - foreach my $vals ( @{$self->{lines}} ) { + my $n_vals = $self->n_cols() - 1; + foreach my $vals ( @{$self->lines} ) { for my $i ( 0..$n_vals ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals->[$i] ? $vals->[$i] : $col->{undef_value}; my $width = length $val; @@ -2613,9 +2691,9 @@ sub _truncate_line_values { sub _make_column_formats { my ( $self ) = @_; my @col_fmts; - my $n_cols = $self->{n_cols} - 1; + my $n_cols = $self->n_cols() - 1; for my $i ( 0..$n_cols ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $width = $col->{right_most} && !$col->{right_justify} ? '' : $col->{print_width}; @@ -2628,12 +2706,12 @@ sub _make_column_formats { sub _truncate_line { my ( $self, $line, %args ) = @_; - my $mark = defined $args{mark} ? $args{mark} : $self->{truncate_line_mark}; + my $mark = defined $args{mark} ? $args{mark} : $self->truncate_line_mark(); if ( $line ) { $line =~ s/\s+$// if $args{strip}; my $len = length($line); - if ( $len > $self->{line_width} ) { - $line = substr($line, 0, $self->{line_width} - length $mark); + if ( $len > $self->line_width() ) { + $line = substr($line, 0, $self->line_width() - length $mark); $line .= $mark if $mark; } } @@ -2643,7 +2721,7 @@ sub _truncate_line { sub _column_error { my ( $self, $err ) = @_; my $msg = "Column error: $err"; - $self->{column_errors} eq 'die' ? die $msg : warn $msg; + $self->column_errors() eq 'die' ? die $msg : warn $msg; return; } @@ -5686,24 +5764,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -5835,6 +5915,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -10830,8 +10913,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -10849,6 +10931,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-online-schema-change 2.1.7 +pt-online-schema-change 2.1.8 =cut diff --git a/bin/pt-pmp b/bin/pt-pmp index 405efd7c..eb545746 100755 --- a/bin/pt-pmp +++ b/bin/pt-pmp @@ -377,8 +377,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -396,7 +396,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-pmp 2.1.7 +pt-pmp 2.1.8 =cut diff --git a/bin/pt-query-advisor b/bin/pt-query-advisor index a76b0b5a..9daace80 100755 --- a/bin/pt-query-advisor +++ b/bin/pt-query-advisor @@ -14,6 +14,7 @@ use warnings FATAL => 'all'; BEGIN { $INC{$_} = __FILE__ for map { (my $pkg = "$_.pm") =~ s!::!/!g; $pkg } (qw( Percona::Toolkit + Mo DSNParser OptionParser Quoter @@ -46,7 +47,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -54,6 +55,470 @@ our $VERSION = '2.1.7'; # End Percona::Toolkit package # ########################################################################### +# ########################################################################### +# Mo package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/Mo.pm +# t/lib/Mo.t +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### +{ +BEGIN { +$INC{"Mo.pm"} = __FILE__; +package Mo; +our $VERSION = '0.30_Percona'; # Forked from 0.30 of Mo. + +{ + no strict 'refs'; + sub _glob_for { + return \*{shift()} + } + + sub _stash_for { + return \%{ shift() . "::" }; + } +} + +use strict; +use warnings qw( FATAL all ); + +use Carp (); +use Scalar::Util qw(looks_like_number blessed); + + +our %TYPES = ( + Bool => sub { !$_[0] || (defined $_[0] && looks_like_number($_[0]) && $_[0] == 1) }, + Num => sub { defined $_[0] && looks_like_number($_[0]) }, + Int => sub { defined $_[0] && looks_like_number($_[0]) && $_[0] == int($_[0]) }, + Str => sub { defined $_[0] }, + Object => sub { defined $_[0] && blessed($_[0]) }, + FileHandle => sub { local $@; require IO::Handle; fileno($_[0]) && $_[0]->opened }, + + map { + my $type = /R/ ? $_ : uc $_; + $_ . "Ref" => sub { ref $_[0] eq $type } + } qw(Array Code Hash Regexp Glob Scalar) +); + +our %metadata_for; +{ + package Mo::Object; + + sub new { + my $class = shift; + my $args = $class->BUILDARGS(@_); + + my @args_to_delete; + while ( my ($attr, $meta) = each %{$metadata_for{$class}} ) { + next unless exists $meta->{init_arg}; + my $init_arg = $meta->{init_arg}; + + if ( defined $init_arg ) { + $args->{$attr} = delete $args->{$init_arg}; + } + else { + push @args_to_delete, $attr; + } + } + + delete $args->{$_} for @args_to_delete; + + for my $attribute ( keys %$args ) { + if ( my $coerce = $metadata_for{$class}{$attribute}{coerce} ) { + $args->{$attribute} = $coerce->($args->{$attribute}); + } + if ( my $I = $metadata_for{$class}{$attribute}{isa} ) { + ( (my $I_name), $I ) = @{$I}; + Mo::_check_type_constaints($attribute, $I, $I_name, $args->{$attribute}); + } + } + + while ( my ($attribute, $meta) = each %{$metadata_for{$class}} ) { + next unless $meta->{required}; + Carp::confess("Attribute ($attribute) is required for $class") + if ! exists $args->{$attribute} + } + + @_ = %$args; + my $self = bless $args, $class; + + my @build_subs; + my $linearized_isa = mro::get_linear_isa($class); + + for my $isa_class ( @$linearized_isa ) { + unshift @build_subs, *{ Mo::_glob_for "${isa_class}::BUILD" }{CODE}; + } + exists &$_ && $_->( $self, @_ ) for grep { defined } @build_subs; + return $self; + } + + sub BUILDARGS { + shift; + my $ref; + if ( @_ == 1 && ref($_[0]) ) { + Carp::confess("Single parameters to new() must be a HASH ref") + unless ref($_[0]) eq ref({}); + $ref = {%{$_[0]}} # We want a new reference, always + } + else { + $ref = { @_ }; + } + return $ref; + } +} + +my %export_for; +%Mo::Internal::Keyword = map { $_ => 1 } qw(has extends override); + +sub Mo::import { + warnings->import(qw(FATAL all)); + strict->import(); + + my $caller = scalar caller(); # Caller's package + my $caller_pkg = $caller . "::"; # Caller's package with :: at the end + my (%exports, %options); + + my (undef, @features) = @_; + my %ignore = ( map { $_ => 1 } qw( is isa init_arg builder buildargs clearer predicate build handles default required ) ); + for my $feature (grep { !$ignore{$_} } @features) { + { local $@; require "Mo/$feature.pm"; } + { + no strict 'refs'; + &{"Mo::${feature}::e"}( + $caller_pkg, + \%exports, + \%options, + \@_ + ); + } + } + + return if $exports{M}; + + %exports = ( + extends => sub { + for my $class ( map { "$_" } @_ ) { + $class =~ s{::|'}{/}g; + { local $@; eval { require "$class.pm" } } # or warn $@; + } + _set_package_isa($caller, @_); + _set_inherited_metadata($caller); + }, + override => \&override, + has => sub { + my $names = shift; + for my $attribute ( ref $names ? @$names : $names ) { + my %args = @_; + my $method = ($args{is} || '') eq 'ro' + ? sub { + Carp::confess("Cannot assign a value to a read-only accessor at reader ${caller_pkg}${attribute}") + if $#_; + return $_[0]{$attribute}; + } + : sub { + return $#_ + ? $_[0]{$attribute} = $_[1] + : $_[0]{$attribute}; + }; + + $metadata_for{$caller}{$attribute} = (); + + if ( my $I = $args{isa} ) { + my $orig_I = $I; + my $type; + if ( $I =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { + $I = _nested_constraints($attribute, $1, $2); + } + $metadata_for{$caller}{$attribute}{isa} = [$orig_I, $I]; + my $orig_method = $method; + $method = sub { + if ( $#_ ) { + Mo::_check_type_constaints($attribute, $I, $orig_I, $_[1]); + } + goto &$orig_method; + }; + } + + if ( my $builder = $args{builder} ) { + my $original_method = $method; + $method = sub { + $#_ + ? goto &$original_method + : ! exists $_[0]{$attribute} + ? $_[0]{$attribute} = $_[0]->$builder + : goto &$original_method + }; + } + + if ( my $code = $args{default} ) { + Carp::confess("${caller}::${attribute}'s default is $code, but should be a coderef") + unless ref($code) eq 'CODE'; + my $original_method = $method; + $method = sub { + $#_ + ? goto &$original_method + : ! exists $_[0]{$attribute} + ? $_[0]{$attribute} = $_[0]->$code + : goto &$original_method + }; + } + + if ( my $role = $args{does} ) { + my $original_method = $method; + $method = sub { + if ( $#_ ) { + Carp::confess(qq) + unless Scalar::Util::blessed($_[1]) && eval { $_[1]->does($role) } + } + goto &$original_method + }; + } + + if ( my $coercion = $args{coerce} ) { + $metadata_for{$caller}{$attribute}{coerce} = $coercion; + my $original_method = $method; + $method = sub { + if ( $#_ ) { + return $original_method->($_[0], $coercion->($_[1])) + } + goto &$original_method; + } + } + + $method = $options{$_}->($method, $attribute, @_) + for sort keys %options; + + *{ _glob_for "${caller}::$attribute" } = $method; + + if ( $args{required} ) { + $metadata_for{$caller}{$attribute}{required} = 1; + } + + if ($args{clearer}) { + *{ _glob_for "${caller}::$args{clearer}" } + = sub { delete shift->{$attribute} } + } + + if ($args{predicate}) { + *{ _glob_for "${caller}::$args{predicate}" } + = sub { exists shift->{$attribute} } + } + + if ($args{handles}) { + _has_handles($caller, $attribute, \%args); + } + + if (exists $args{init_arg}) { + $metadata_for{$caller}{$attribute}{init_arg} = $args{init_arg}; + } + } + }, + %exports, + ); + + $export_for{$caller} = [ keys %exports ]; + + for my $keyword ( keys %exports ) { + *{ _glob_for "${caller}::$keyword" } = $exports{$keyword} + } + *{ _glob_for "${caller}::extends" }{CODE}->( "Mo::Object" ) + unless @{ *{ _glob_for "${caller}::ISA" }{ARRAY} || [] }; +}; + +sub _check_type_constaints { + my ($attribute, $I, $I_name, $val) = @_; + ( ref($I) eq 'CODE' + ? $I->($val) + : (ref $val eq $I + || ($val && $val eq $I) + || (exists $TYPES{$I} && $TYPES{$I}->($val))) + ) + || Carp::confess( + qq + . qq + . (defined $val ? Mo::Dumper($val) : 'undef') ) +} + +sub _has_handles { + my ($caller, $attribute, $args) = @_; + my $handles = $args->{handles}; + + my $ref = ref $handles; + my $kv; + if ( $ref eq ref [] ) { + $kv = { map { $_,$_ } @{$handles} }; + } + elsif ( $ref eq ref {} ) { + $kv = $handles; + } + elsif ( $ref eq ref qr// ) { + Carp::confess("Cannot delegate methods based on a Regexp without a type constraint (isa)") + unless $args->{isa}; + my $target_class = $args->{isa}; + $kv = { + map { $_, $_ } + grep { $_ =~ $handles } + grep { !exists $Mo::Object::{$_} && $target_class->can($_) } + grep { !$Mo::Internal::Keyword{$_} } + keys %{ _stash_for $target_class } + }; + } + else { + Carp::confess("handles for $ref not yet implemented"); + } + + while ( my ($method, $target) = each %{$kv} ) { + my $name = _glob_for "${caller}::$method"; + Carp::confess("You cannot overwrite a locally defined method ($method) with a delegation") + if defined &$name; + + my ($target, @curried_args) = ref($target) ? @$target : $target; + *$name = sub { + my $self = shift; + my $delegate_to = $self->$attribute(); + my $error = "Cannot delegate $method to $target because the value of $attribute"; + Carp::confess("$error is not defined") unless $delegate_to; + Carp::confess("$error is not an object (got '$delegate_to')") + unless Scalar::Util::blessed($delegate_to) || (!ref($delegate_to) && $delegate_to->can($target)); + return $delegate_to->$target(@curried_args, @_); + } + } +} + +sub _nested_constraints { + my ($attribute, $aggregate_type, $type) = @_; + + my $inner_types; + if ( $type =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { + $inner_types = _nested_constraints($1, $2); + } + else { + $inner_types = $TYPES{$type}; + } + + if ( $aggregate_type eq 'ArrayRef' ) { + return sub { + my ($val) = @_; + return unless ref($val) eq ref([]); + + if ($inner_types) { + for my $value ( @{$val} ) { + return unless $inner_types->($value) + } + } + else { + for my $value ( @{$val} ) { + return unless $value && ($value eq $type + || (Scalar::Util::blessed($value) && $value->isa($type))); + } + } + return 1; + }; + } + elsif ( $aggregate_type eq 'Maybe' ) { + return sub { + my ($value) = @_; + return 1 if ! defined($value); + if ($inner_types) { + return unless $inner_types->($value) + } + else { + return unless $value eq $type + || (Scalar::Util::blessed($value) && $value->isa($type)); + } + return 1; + } + } + else { + Carp::confess("Nested aggregate types are only implemented for ArrayRefs and Maybe"); + } +} + +sub _set_package_isa { + my ($package, @new_isa) = @_; + + *{ _glob_for "${package}::ISA" } = [@new_isa]; +} + +sub _set_inherited_metadata { + my $class = shift; + my $linearized_isa = mro::get_linear_isa($class); + my %new_metadata; + + for my $isa_class (reverse @$linearized_isa) { + %new_metadata = ( + %new_metadata, + %{ $metadata_for{$isa_class} || {} }, + ); + } + $metadata_for{$class} = \%new_metadata; +} + +sub unimport { + my $caller = scalar caller(); + my $stash = _stash_for( $caller ); + + delete $stash->{$_} for @{$export_for{$caller}}; +} + +sub Dumper { + require Data::Dumper; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Sortkeys = 0; + local $Data::Dumper::Quotekeys = 0; + local $Data::Dumper::Terse = 1; + + Data::Dumper::Dumper(@_) +} + +BEGIN { + if ($] >= 5.010) { + { local $@; require mro; } + } + else { + local $@; + eval { + require MRO::Compat; + } or do { + *mro::get_linear_isa = *mro::get_linear_isa_dfs = sub { + no strict 'refs'; + + my $classname = shift; + + my @lin = ($classname); + my %stored; + foreach my $parent (@{"$classname\::ISA"}) { + my $plin = mro::get_linear_isa_dfs($parent); + foreach (@$plin) { + next if exists $stored{$_}; + push(@lin, $_); + $stored{$_} = 1; + } + } + return \@lin; + }; + } + } +} + +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + +} +1; +} +# ########################################################################### +# End Mo package +# ########################################################################### + # ########################################################################### # DSNParser package # This package is a copy without comments from the original. The original @@ -1727,14 +2192,25 @@ sub parse_event { if ( !$found_arg && $pos == $len ) { PTDEBUG && _d("Did not find arg, looking for special cases"); - local $INPUT_RECORD_SEPARATOR = ";\n"; + local $INPUT_RECORD_SEPARATOR = ";\n"; # get next line if ( defined(my $l = $next_event->()) ) { - chomp $l; - $l =~ s/^\s+//; - PTDEBUG && _d("Found admin statement", $l); - push @properties, 'cmd', 'Admin', 'arg', $l; - push @properties, 'bytes', length($properties[-1]); - $found_arg++; + if ( $l =~ /^\s*[A-Z][a-z_]+: / ) { + PTDEBUG && _d("Found NULL query before", $l); + local $INPUT_RECORD_SEPARATOR = ";\n#"; + my $rest_of_event = $next_event->(); + push @{$self->{pending}}, $l . $rest_of_event; + push @properties, 'cmd', 'Query', 'arg', '/* No query */'; + push @properties, 'bytes', 0; + $found_arg++; + } + else { + chomp $l; + $l =~ s/^\s+//; + PTDEBUG && _d("Found admin statement", $l); + push @properties, 'cmd', 'Admin', 'arg', $l; + push @properties, 'bytes', length($properties[-1]); + $found_arg++; + } } else { PTDEBUG && _d("I can't figure out what to do with this line"); @@ -1890,7 +2366,8 @@ sub parse_event { $cmd = $arg; } else { - my ($user, undef, $db) = $arg =~ /(\S+)/g; + my ($user) = $arg =~ m/(\S+)/; + my ($db) = $arg =~ m/on (\S+)/; my $host; ($user, $host) = split(/@/, $user); PTDEBUG && _d('Connect', $user, '@', $host, 'on', $db); @@ -2758,24 +3235,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -2907,6 +3386,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -5706,8 +6188,7 @@ sub _d { { package ReportFormatter; -use strict; -use warnings FATAL => 'all'; +use Mo; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG} || 0; @@ -5717,40 +6198,102 @@ use POSIX qw(ceil); eval { require Term::ReadKey }; my $have_term = $EVAL_ERROR ? 0 : 1; -sub new { - my ( $class, %args ) = @_; - my @required_args = qw(); - foreach my $arg ( @required_args ) { - die "I need a $arg argument" unless $args{$arg}; - } - my $self = { - underline_header => 1, - line_prefix => '# ', - line_width => 78, - column_spacing => ' ', - extend_right => 0, - truncate_line_mark => '...', - column_errors => 'warn', - truncate_header_side => 'left', - strip_whitespace => 1, - %args, # args above can be overriden, args below cannot - n_cols => 0, - }; - if ( ($self->{line_width} || '') eq 'auto' ) { +has underline_header => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has line_prefix => ( + is => 'ro', + isa => 'Str', + default => sub { '# ' }, +); +has line_width => ( + is => 'ro', + isa => 'Int', + default => sub { 78 }, +); +has column_spacing => ( + is => 'ro', + isa => 'Str', + default => sub { ' ' }, +); +has extend_right => ( + is => 'ro', + isa => 'Bool', + default => sub { '' }, +); +has truncate_line_mark => ( + is => 'ro', + isa => 'Str', + default => sub { '...' }, +); +has column_errors => ( + is => 'ro', + isa => 'Str', + default => sub { 'warn' }, +); +has truncate_header_side => ( + is => 'ro', + isa => 'Str', + default => sub { 'left' }, +); +has strip_whitespace => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has title => ( + is => 'rw', + isa => 'Str', + predicate => 'has_title', +); + + +has n_cols => ( + is => 'rw', + isa => 'Int', + default => sub { 0 }, + init_arg => undef, +); + +has cols => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_cols', +); + +has lines => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_lines', +); + +has truncate_headers => ( + is => 'rw', + isa => 'Bool', + default => sub { undef }, + init_arg => undef, + clearer => 'clear_truncate_headers', +); + +sub BUILDARGS { + my $class = shift; + my $args = $class->SUPER::BUILDARGS(@_); + + if ( ($args->{line_width} || '') eq 'auto' ) { die "Cannot auto-detect line width because the Term::ReadKey module " . "is not installed" unless $have_term; - ($self->{line_width}) = GetTerminalSize(); + ($args->{line_width}) = GetTerminalSize(); + PTDEBUG && _d('Line width:', $args->{line_width}); } - PTDEBUG && _d('Line width:', $self->{line_width}); - return bless $self, $class; -} - -sub set_title { - my ( $self, $title ) = @_; - $self->{title} = $title; - return; + return $args; } sub set_columns { @@ -5766,7 +6309,7 @@ sub set_columns { die "Column does not have a name" unless defined $col_name; if ( $col->{width} ) { - $col->{width_pct} = ceil(($col->{width} * 100) / $self->{line_width}); + $col->{width_pct} = ceil(($col->{width} * 100) / $self->line_width()); PTDEBUG && _d('col:', $col_name, 'width:', $col->{width}, 'chars =', $col->{width_pct}, '%'); } @@ -5793,10 +6336,10 @@ sub set_columns { $col->{right_most} = 1 if $i == $#cols; - push @{$self->{cols}}, $col; + push @{$self->cols}, $col; } - $self->{n_cols} = scalar @cols; + $self->n_cols( scalar @cols ); if ( ($used_width || 0) > 100 ) { die "Total width_pct for all columns is >100%"; @@ -5806,15 +6349,15 @@ sub set_columns { my $wid_per_col = int((100 - $used_width) / scalar @auto_width_cols); PTDEBUG && _d('Line width left:', (100-$used_width), '%;', 'each auto width col:', $wid_per_col, '%'); - map { $self->{cols}->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; + map { $self->cols->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; } - $min_hdr_wid += ($self->{n_cols} - 1) * length $self->{column_spacing}; + $min_hdr_wid += ($self->n_cols() - 1) * length $self->column_spacing(); PTDEBUG && _d('min header width:', $min_hdr_wid); - if ( $min_hdr_wid > $self->{line_width} ) { + if ( $min_hdr_wid > $self->line_width() ) { PTDEBUG && _d('Will truncate headers because min header width', - $min_hdr_wid, '> line width', $self->{line_width}); - $self->{truncate_headers} = 1; + $min_hdr_wid, '> line width', $self->line_width()); + $self->truncate_headers(1); } return; @@ -5823,14 +6366,14 @@ sub set_columns { sub add_line { my ( $self, @vals ) = @_; my $n_vals = scalar @vals; - if ( $n_vals != $self->{n_cols} ) { + if ( $n_vals != $self->n_cols() ) { $self->_column_error("Number of values $n_vals does not match " - . "number of columns $self->{n_cols}"); + . "number of columns " . $self->n_cols()); } for my $i ( 0..($n_vals-1) ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals[$i] ? $vals[$i] : $col->{undef_value}; - if ( $self->{strip_whitespace} ) { + if ( $self->strip_whitespace() ) { $val =~ s/^\s+//g; $val =~ s/\s+$//; $vals[$i] = $val; @@ -5839,7 +6382,7 @@ sub add_line { $col->{min_val} = min($width, ($col->{min_val} || $width)); $col->{max_val} = max($width, ($col->{max_val} || $width)); } - push @{$self->{lines}}, \@vals; + push @{$self->lines}, \@vals; return; } @@ -5847,26 +6390,28 @@ sub get_report { my ( $self, %args ) = @_; $self->_calculate_column_widths(); - $self->_truncate_headers() if $self->{truncate_headers}; + if ( $self->truncate_headers() ) { + $self->_truncate_headers(); + } $self->_truncate_line_values(%args); my @col_fmts = $self->_make_column_formats(); - my $fmt = ($self->{line_prefix} || '') - . join($self->{column_spacing}, @col_fmts); + my $fmt = $self->line_prefix() + . join($self->column_spacing(), @col_fmts); PTDEBUG && _d('Format:', $fmt); (my $hdr_fmt = $fmt) =~ s/%([^-])/%-$1/g; my @lines; - push @lines, sprintf "$self->{line_prefix}$self->{title}" if $self->{title}; + push @lines, $self->line_prefix() . $self->title() if $self->has_title(); push @lines, $self->_truncate_line( - sprintf($hdr_fmt, map { $_->{name} } @{$self->{cols}}), + sprintf($hdr_fmt, map { $_->{name} } @{$self->cols}), strip => 1, mark => '', ); - if ( $self->{underline_header} ) { - my @underlines = map { '=' x $_->{print_width} } @{$self->{cols}}; + if ( $self->underline_header() ) { + my @underlines = map { '=' x $_->{print_width} } @{$self->cols}; push @lines, $self->_truncate_line( sprintf($fmt, map { $_ || '' } @underlines), mark => '', @@ -5877,19 +6422,23 @@ sub get_report { my $vals = $_; my $i = 0; my @vals = map { - my $val = defined $_ ? $_ : $self->{cols}->[$i++]->{undef_value}; + my $val = defined $_ ? $_ : $self->cols->[$i++]->{undef_value}; $val = '' if !defined $val; $val =~ s/\n/ /g; $val; } @$vals; my $line = sprintf($fmt, @vals); - if ( $self->{extend_right} ) { + if ( $self->extend_right() ) { $line; } else { $self->_truncate_line($line); } - } @{$self->{lines}}; + } @{$self->lines}; + + $self->clear_cols(); + $self->clear_lines(); + $self->clear_truncate_headers(); return join("\n", @lines) . "\n"; } @@ -5897,7 +6446,7 @@ sub get_report { sub truncate_value { my ( $self, $col, $val, $width, $side ) = @_; return $val if length $val <= $width; - return $val if $col->{right_most} && $self->{extend_right}; + return $val if $col->{right_most} && $self->extend_right(); $side ||= $col->{truncate_side}; my $mark = $col->{truncate_mark}; if ( $side eq 'right' ) { @@ -5917,8 +6466,8 @@ sub _calculate_column_widths { my ( $self ) = @_; my $extra_space = 0; - foreach my $col ( @{$self->{cols}} ) { - my $print_width = int($self->{line_width} * ($col->{width_pct} / 100)); + foreach my $col ( @{$self->cols} ) { + my $print_width = int($self->line_width() * ($col->{width_pct} / 100)); PTDEBUG && _d('col:', $col->{name}, 'width pct:', $col->{width_pct}, 'char width:', $print_width, @@ -5942,7 +6491,7 @@ sub _calculate_column_widths { PTDEBUG && _d('Extra space:', $extra_space); while ( $extra_space-- ) { - foreach my $col ( @{$self->{cols}} ) { + foreach my $col ( @{$self->cols} ) { if ( $col->{auto_width} && ( $col->{print_width} < $col->{max_val} || $col->{print_width} < $col->{header_width}) @@ -5957,8 +6506,8 @@ sub _calculate_column_widths { sub _truncate_headers { my ( $self, $col ) = @_; - my $side = $self->{truncate_header_side}; - foreach my $col ( @{$self->{cols}} ) { + my $side = $self->truncate_header_side(); + foreach my $col ( @{$self->cols} ) { my $col_name = $col->{name}; my $print_width = $col->{print_width}; next if length $col_name <= $print_width; @@ -5971,10 +6520,10 @@ sub _truncate_headers { sub _truncate_line_values { my ( $self, %args ) = @_; - my $n_vals = $self->{n_cols} - 1; - foreach my $vals ( @{$self->{lines}} ) { + my $n_vals = $self->n_cols() - 1; + foreach my $vals ( @{$self->lines} ) { for my $i ( 0..$n_vals ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals->[$i] ? $vals->[$i] : $col->{undef_value}; my $width = length $val; @@ -6000,9 +6549,9 @@ sub _truncate_line_values { sub _make_column_formats { my ( $self ) = @_; my @col_fmts; - my $n_cols = $self->{n_cols} - 1; + my $n_cols = $self->n_cols() - 1; for my $i ( 0..$n_cols ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $width = $col->{right_most} && !$col->{right_justify} ? '' : $col->{print_width}; @@ -6015,12 +6564,12 @@ sub _make_column_formats { sub _truncate_line { my ( $self, $line, %args ) = @_; - my $mark = defined $args{mark} ? $args{mark} : $self->{truncate_line_mark}; + my $mark = defined $args{mark} ? $args{mark} : $self->truncate_line_mark(); if ( $line ) { $line =~ s/\s+$// if $args{strip}; my $len = length($line); - if ( $len > $self->{line_width} ) { - $line = substr($line, 0, $self->{line_width} - length $mark); + if ( $len > $self->line_width() ) { + $line = substr($line, 0, $self->line_width() - length $mark); $line .= $mark if $mark; } } @@ -6030,7 +6579,7 @@ sub _truncate_line { sub _column_error { my ( $self, $err ) = @_; my $msg = "Column error: $err"; - $self->{column_errors} eq 'die' ? die $msg : warn $msg; + $self->column_errors() eq 'die' ? die $msg : warn $msg; return; } @@ -7835,7 +8384,7 @@ sub main { long_last_column => 1, extend_right => 1, ); - $profile->set_title("Profile"); + $profile->title("Profile"); $profile->set_columns( { name => 'Query ID', }, { name => 'NOTE', right_justify => 1, }, @@ -8694,8 +9243,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2010-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -8713,6 +9261,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-query-advisor 2.1.7 +pt-query-advisor 2.1.8 =cut diff --git a/bin/pt-query-digest b/bin/pt-query-digest index c38332f7..695c5056 100755 --- a/bin/pt-query-digest +++ b/bin/pt-query-digest @@ -64,7 +64,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -7977,14 +7977,14 @@ sub _d { { package JSONReportFormatter; use Mo; -use JSON (); - -use List::Util qw(sum); +use List::Util qw(sum); use Transformers qw(make_checksum parse_timestamp); use constant PTDEBUG => $ENV{PTDEBUG} || 0; +my $have_json = eval { require JSON }; + our $pretty_json = undef; our $sorted_json = undef; @@ -7994,11 +7994,23 @@ has _json => ( is => 'ro', init_arg => undef, builder => '_build_json', - handles => { encode_json => 'encode' }, ); sub _build_json { - return JSON->new->utf8->pretty($pretty_json)->canonical($sorted_json); + return unless $have_json; + return JSON->new->utf8 + ->pretty($pretty_json) + ->canonical($sorted_json); +} + +sub encode_json { + my ($self, $encode) = @_; + if ( my $json = $self->_json ) { + return $json->encode($encode); + } + else { + return Transformers::encode_json($encode); + } } override [qw(rusage date hostname files header profile prepared)] => sub { @@ -8076,7 +8088,9 @@ override query_report => sub { }; } - return $self->encode_json(\@queries) . "\n"; + my $json = $self->encode_json(\@queries); + $json .= "\n" if $json !~ /\n\Z/; + return $json . "\n"; }; 1; @@ -15284,7 +15298,8 @@ Analyze, aggregate, and report on a slow query log: Review a slow log, saving results to the test.query_review table in a MySQL server running on host1. See L<"--review"> for more on reviewing queries: - pt-query-digest --review h=host1,D=test,t=query_review /path/to/slow.log + pt-query-digest --review h=host1 --review-table test.query_review + --history-table test.query_history /path/to/slow.log Print the structure of events so you can construct a complex L<"--filter">: @@ -15314,7 +15329,7 @@ is safe to run even on production systems, but you might want to monitor it until you are satisfied that the input you give it does not cause undue load. Various options will cause pt-query-digest to insert data into tables, execute -SQL queries, and so on. These include the L<"--review"> options. +SQL queries, and so on. These include the L<"--review"> option. At the time of this release, we know of no bugs that could cause serious harm to users. @@ -15795,7 +15810,9 @@ Continue parsing even if there is an error. The tool will not continue forever: it stops once any process causes 100 errors, in which case there is probably a bug in the tool or the input is invalid. -=item --create-review-tables +=item --[no]create-review-tables + +default: yes Create the L<"--review"> tables if they do not exist. @@ -16305,10 +16322,15 @@ type: DSN Store a sample of each class of query in this DSN, plus historical values for review trend analysis -The argument specifies a table to store all unique query fingerprints in. The -table must have at least the following columns. You can add more columns for -your own special purposes, but they won't be used by pt-query-digest. The -following CREATE TABLE definition is also used for L<"--create-review-table">. +The argument specifies a host to store all unique query fingerprints in; the +databases and tables were this data is stored can be specified with the +L<"--review-table"> and L<"--history-table"> options. +By default, if the table doesn't exist the tool mtries creating it; This +behavior can bhe controlled with the L<"--[no]create-review-tables"> option. +If the table was created manually, it must have at least the following columns. +You can add more columns for your own special purposes, but they won't be used +by pt-query-digest. The following CREATE TABLE definition is also used by +L<"--no-create-review-tables">. =for comment ignore-pt-internal-value MAGIC_create_review: @@ -16346,7 +16368,7 @@ fingerprint. This option depends on C<--group-by fingerprint> (which is the default). It will not work otherwise. -Additionally, pt-query-digest will save information into a review table, +Additionally, pt-query-digest will save historical information into a review table, so you can see how classes of queries have changed over time. You can change the destination table with the L<"--history-table"> @@ -16379,7 +16401,7 @@ you could also just add a ts_min column and make it a DATE type, so you'd get one row per class of queries per day. The default table structure follows. The following table definition is used -for L<"--create-review-tables">: +for L<"--[no]create-review-tables">: =for comment ignore-pt-internal-value MAGIC_create_review_history @@ -17297,8 +17319,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2008-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2008-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -17316,6 +17337,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-query-digest 2.1.7 +pt-query-digest 2.1.8 =cut diff --git a/bin/pt-show-grants b/bin/pt-show-grants index 1854f2d4..3f318ecd 100755 --- a/bin/pt-show-grants +++ b/bin/pt-show-grants @@ -2285,8 +2285,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -2304,6 +2304,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-show-grants 2.1.7 +pt-show-grants 2.1.8 =cut diff --git a/bin/pt-sift b/bin/pt-sift index 539af395..5be9e9b6 100755 --- a/bin/pt-sift +++ b/bin/pt-sift @@ -762,8 +762,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -781,7 +781,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-sift 2.1.7 +pt-sift 2.1.8 =cut diff --git a/bin/pt-slave-delay b/bin/pt-slave-delay index e3de0762..2c702d12 100755 --- a/bin/pt-slave-delay +++ b/bin/pt-slave-delay @@ -36,7 +36,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -2268,6 +2268,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -4578,9 +4581,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Sergey Zhuravle and Baron Schwartz, -2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Sergey Zhuravle and Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -4598,6 +4600,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-slave-delay 2.1.7 +pt-slave-delay 2.1.8 =cut diff --git a/bin/pt-slave-find b/bin/pt-slave-find index d1b5c6f1..32fa41a6 100755 --- a/bin/pt-slave-find +++ b/bin/pt-slave-find @@ -3175,6 +3175,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -4003,8 +4006,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -4022,6 +4025,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-slave-find 2.1.7 +pt-slave-find 2.1.8 =cut diff --git a/bin/pt-slave-restart b/bin/pt-slave-restart index 5ea55372..bd04d04a 100755 --- a/bin/pt-slave-restart +++ b/bin/pt-slave-restart @@ -37,7 +37,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -5500,8 +5500,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -5519,6 +5519,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-slave-restart 2.1.7 +pt-slave-restart 2.1.8 =cut diff --git a/bin/pt-stalk b/bin/pt-stalk index 754ba671..81a7df90 100755 --- a/bin/pt-stalk +++ b/bin/pt-stalk @@ -71,7 +71,7 @@ PO_DIR="" # Directory with program option spec files usage() { local file="$1" - local usage=$(grep '^Usage: ' "$file") + local usage="$(grep '^Usage: ' "$file")" echo $usage echo echo "For more information, 'man $TOOL' or 'perldoc $file'." @@ -1965,8 +1965,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -1984,7 +1984,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-stalk 2.1.7 +pt-stalk 2.1.8 =cut diff --git a/bin/pt-summary b/bin/pt-summary index 9bdd2f79..4c4ac007 100755 --- a/bin/pt-summary +++ b/bin/pt-summary @@ -78,7 +78,7 @@ PO_DIR="" # Directory with program option spec files usage() { local file="$1" - local usage=$(grep '^Usage: ' "$file") + local usage="$(grep '^Usage: ' "$file")" echo $usage echo echo "For more information, 'man $TOOL' or 'perldoc $file'." @@ -2654,8 +2654,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2010-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -2673,7 +2673,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-summary 2.1.7 +pt-summary 2.1.8 =cut diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 71d4b853..574a03a4 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -53,7 +53,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -7360,6 +7360,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -8741,6 +8744,15 @@ sub main { } @$slaves; } PTDEBUG && _d(scalar @$slaves, 'slaves found'); + if ( !@$slaves && $o->get('recursion-method')->[0] ne 'none' ) { + $exit_status |= 1; + if ( $o->get('quiet') < 2 ) { + warn "Diffs cannot be detected because no slaves were found. " + . "Please read the --recursion-method documentation for " + . "information.\n"; + } + } + # https://bugs.launchpad.net/percona-toolkit/+bug/938068 if ( $o->get('check-binlog-format') ) { @@ -11490,6 +11502,13 @@ type: array; default: processlist,hosts Preferred recursion method for discovering replicas. pt-table-checksum performs several L<"REPLICA CHECKS"> before and while running. + +Although replicas are not required to run pt-table-checksum, the tool +cannot detect diffs on slaves that it cannot discover. Therefore, +a warning is printed and the L<"EXIT STATUS"> is non-zero if no replicas +are found and the method is not C. If this happens, try a different +recursion method, or use the C method to specify the replicas to check. + Possible methods are: METHOD USES @@ -11935,8 +11954,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -11954,6 +11973,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-table-checksum 2.1.7 +pt-table-checksum 2.1.8 =cut diff --git a/bin/pt-table-sync b/bin/pt-table-sync index b2957ac3..e27d5713 100755 --- a/bin/pt-table-sync +++ b/bin/pt-table-sync @@ -52,7 +52,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -8036,6 +8036,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -12614,8 +12617,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -12633,6 +12636,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-table-sync 2.1.7 +pt-table-sync 2.1.8 =cut diff --git a/bin/pt-table-usage b/bin/pt-table-usage index f972cbd9..3a9767a3 100755 --- a/bin/pt-table-usage +++ b/bin/pt-table-usage @@ -7364,8 +7364,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2012-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -7383,6 +7382,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-table-usage 2.1.7 +pt-table-usage 2.1.8 =cut diff --git a/bin/pt-upgrade b/bin/pt-upgrade index 47d4e910..ffe73898 100755 --- a/bin/pt-upgrade +++ b/bin/pt-upgrade @@ -14,6 +14,7 @@ use warnings FATAL => 'all'; BEGIN { $INC{$_} = __FILE__ for map { (my $pkg = "$_.pm") =~ s!::!/!g; $pkg } (qw( Percona::Toolkit + Mo DSNParser TableParser Quoter @@ -58,7 +59,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -66,6 +67,470 @@ our $VERSION = '2.1.7'; # End Percona::Toolkit package # ########################################################################### +# ########################################################################### +# Mo package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/Mo.pm +# t/lib/Mo.t +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### +{ +BEGIN { +$INC{"Mo.pm"} = __FILE__; +package Mo; +our $VERSION = '0.30_Percona'; # Forked from 0.30 of Mo. + +{ + no strict 'refs'; + sub _glob_for { + return \*{shift()} + } + + sub _stash_for { + return \%{ shift() . "::" }; + } +} + +use strict; +use warnings qw( FATAL all ); + +use Carp (); +use Scalar::Util qw(looks_like_number blessed); + + +our %TYPES = ( + Bool => sub { !$_[0] || (defined $_[0] && looks_like_number($_[0]) && $_[0] == 1) }, + Num => sub { defined $_[0] && looks_like_number($_[0]) }, + Int => sub { defined $_[0] && looks_like_number($_[0]) && $_[0] == int($_[0]) }, + Str => sub { defined $_[0] }, + Object => sub { defined $_[0] && blessed($_[0]) }, + FileHandle => sub { local $@; require IO::Handle; fileno($_[0]) && $_[0]->opened }, + + map { + my $type = /R/ ? $_ : uc $_; + $_ . "Ref" => sub { ref $_[0] eq $type } + } qw(Array Code Hash Regexp Glob Scalar) +); + +our %metadata_for; +{ + package Mo::Object; + + sub new { + my $class = shift; + my $args = $class->BUILDARGS(@_); + + my @args_to_delete; + while ( my ($attr, $meta) = each %{$metadata_for{$class}} ) { + next unless exists $meta->{init_arg}; + my $init_arg = $meta->{init_arg}; + + if ( defined $init_arg ) { + $args->{$attr} = delete $args->{$init_arg}; + } + else { + push @args_to_delete, $attr; + } + } + + delete $args->{$_} for @args_to_delete; + + for my $attribute ( keys %$args ) { + if ( my $coerce = $metadata_for{$class}{$attribute}{coerce} ) { + $args->{$attribute} = $coerce->($args->{$attribute}); + } + if ( my $I = $metadata_for{$class}{$attribute}{isa} ) { + ( (my $I_name), $I ) = @{$I}; + Mo::_check_type_constaints($attribute, $I, $I_name, $args->{$attribute}); + } + } + + while ( my ($attribute, $meta) = each %{$metadata_for{$class}} ) { + next unless $meta->{required}; + Carp::confess("Attribute ($attribute) is required for $class") + if ! exists $args->{$attribute} + } + + @_ = %$args; + my $self = bless $args, $class; + + my @build_subs; + my $linearized_isa = mro::get_linear_isa($class); + + for my $isa_class ( @$linearized_isa ) { + unshift @build_subs, *{ Mo::_glob_for "${isa_class}::BUILD" }{CODE}; + } + exists &$_ && $_->( $self, @_ ) for grep { defined } @build_subs; + return $self; + } + + sub BUILDARGS { + shift; + my $ref; + if ( @_ == 1 && ref($_[0]) ) { + Carp::confess("Single parameters to new() must be a HASH ref") + unless ref($_[0]) eq ref({}); + $ref = {%{$_[0]}} # We want a new reference, always + } + else { + $ref = { @_ }; + } + return $ref; + } +} + +my %export_for; +%Mo::Internal::Keyword = map { $_ => 1 } qw(has extends override); + +sub Mo::import { + warnings->import(qw(FATAL all)); + strict->import(); + + my $caller = scalar caller(); # Caller's package + my $caller_pkg = $caller . "::"; # Caller's package with :: at the end + my (%exports, %options); + + my (undef, @features) = @_; + my %ignore = ( map { $_ => 1 } qw( is isa init_arg builder buildargs clearer predicate build handles default required ) ); + for my $feature (grep { !$ignore{$_} } @features) { + { local $@; require "Mo/$feature.pm"; } + { + no strict 'refs'; + &{"Mo::${feature}::e"}( + $caller_pkg, + \%exports, + \%options, + \@_ + ); + } + } + + return if $exports{M}; + + %exports = ( + extends => sub { + for my $class ( map { "$_" } @_ ) { + $class =~ s{::|'}{/}g; + { local $@; eval { require "$class.pm" } } # or warn $@; + } + _set_package_isa($caller, @_); + _set_inherited_metadata($caller); + }, + override => \&override, + has => sub { + my $names = shift; + for my $attribute ( ref $names ? @$names : $names ) { + my %args = @_; + my $method = ($args{is} || '') eq 'ro' + ? sub { + Carp::confess("Cannot assign a value to a read-only accessor at reader ${caller_pkg}${attribute}") + if $#_; + return $_[0]{$attribute}; + } + : sub { + return $#_ + ? $_[0]{$attribute} = $_[1] + : $_[0]{$attribute}; + }; + + $metadata_for{$caller}{$attribute} = (); + + if ( my $I = $args{isa} ) { + my $orig_I = $I; + my $type; + if ( $I =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { + $I = _nested_constraints($attribute, $1, $2); + } + $metadata_for{$caller}{$attribute}{isa} = [$orig_I, $I]; + my $orig_method = $method; + $method = sub { + if ( $#_ ) { + Mo::_check_type_constaints($attribute, $I, $orig_I, $_[1]); + } + goto &$orig_method; + }; + } + + if ( my $builder = $args{builder} ) { + my $original_method = $method; + $method = sub { + $#_ + ? goto &$original_method + : ! exists $_[0]{$attribute} + ? $_[0]{$attribute} = $_[0]->$builder + : goto &$original_method + }; + } + + if ( my $code = $args{default} ) { + Carp::confess("${caller}::${attribute}'s default is $code, but should be a coderef") + unless ref($code) eq 'CODE'; + my $original_method = $method; + $method = sub { + $#_ + ? goto &$original_method + : ! exists $_[0]{$attribute} + ? $_[0]{$attribute} = $_[0]->$code + : goto &$original_method + }; + } + + if ( my $role = $args{does} ) { + my $original_method = $method; + $method = sub { + if ( $#_ ) { + Carp::confess(qq) + unless Scalar::Util::blessed($_[1]) && eval { $_[1]->does($role) } + } + goto &$original_method + }; + } + + if ( my $coercion = $args{coerce} ) { + $metadata_for{$caller}{$attribute}{coerce} = $coercion; + my $original_method = $method; + $method = sub { + if ( $#_ ) { + return $original_method->($_[0], $coercion->($_[1])) + } + goto &$original_method; + } + } + + $method = $options{$_}->($method, $attribute, @_) + for sort keys %options; + + *{ _glob_for "${caller}::$attribute" } = $method; + + if ( $args{required} ) { + $metadata_for{$caller}{$attribute}{required} = 1; + } + + if ($args{clearer}) { + *{ _glob_for "${caller}::$args{clearer}" } + = sub { delete shift->{$attribute} } + } + + if ($args{predicate}) { + *{ _glob_for "${caller}::$args{predicate}" } + = sub { exists shift->{$attribute} } + } + + if ($args{handles}) { + _has_handles($caller, $attribute, \%args); + } + + if (exists $args{init_arg}) { + $metadata_for{$caller}{$attribute}{init_arg} = $args{init_arg}; + } + } + }, + %exports, + ); + + $export_for{$caller} = [ keys %exports ]; + + for my $keyword ( keys %exports ) { + *{ _glob_for "${caller}::$keyword" } = $exports{$keyword} + } + *{ _glob_for "${caller}::extends" }{CODE}->( "Mo::Object" ) + unless @{ *{ _glob_for "${caller}::ISA" }{ARRAY} || [] }; +}; + +sub _check_type_constaints { + my ($attribute, $I, $I_name, $val) = @_; + ( ref($I) eq 'CODE' + ? $I->($val) + : (ref $val eq $I + || ($val && $val eq $I) + || (exists $TYPES{$I} && $TYPES{$I}->($val))) + ) + || Carp::confess( + qq + . qq + . (defined $val ? Mo::Dumper($val) : 'undef') ) +} + +sub _has_handles { + my ($caller, $attribute, $args) = @_; + my $handles = $args->{handles}; + + my $ref = ref $handles; + my $kv; + if ( $ref eq ref [] ) { + $kv = { map { $_,$_ } @{$handles} }; + } + elsif ( $ref eq ref {} ) { + $kv = $handles; + } + elsif ( $ref eq ref qr// ) { + Carp::confess("Cannot delegate methods based on a Regexp without a type constraint (isa)") + unless $args->{isa}; + my $target_class = $args->{isa}; + $kv = { + map { $_, $_ } + grep { $_ =~ $handles } + grep { !exists $Mo::Object::{$_} && $target_class->can($_) } + grep { !$Mo::Internal::Keyword{$_} } + keys %{ _stash_for $target_class } + }; + } + else { + Carp::confess("handles for $ref not yet implemented"); + } + + while ( my ($method, $target) = each %{$kv} ) { + my $name = _glob_for "${caller}::$method"; + Carp::confess("You cannot overwrite a locally defined method ($method) with a delegation") + if defined &$name; + + my ($target, @curried_args) = ref($target) ? @$target : $target; + *$name = sub { + my $self = shift; + my $delegate_to = $self->$attribute(); + my $error = "Cannot delegate $method to $target because the value of $attribute"; + Carp::confess("$error is not defined") unless $delegate_to; + Carp::confess("$error is not an object (got '$delegate_to')") + unless Scalar::Util::blessed($delegate_to) || (!ref($delegate_to) && $delegate_to->can($target)); + return $delegate_to->$target(@curried_args, @_); + } + } +} + +sub _nested_constraints { + my ($attribute, $aggregate_type, $type) = @_; + + my $inner_types; + if ( $type =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { + $inner_types = _nested_constraints($1, $2); + } + else { + $inner_types = $TYPES{$type}; + } + + if ( $aggregate_type eq 'ArrayRef' ) { + return sub { + my ($val) = @_; + return unless ref($val) eq ref([]); + + if ($inner_types) { + for my $value ( @{$val} ) { + return unless $inner_types->($value) + } + } + else { + for my $value ( @{$val} ) { + return unless $value && ($value eq $type + || (Scalar::Util::blessed($value) && $value->isa($type))); + } + } + return 1; + }; + } + elsif ( $aggregate_type eq 'Maybe' ) { + return sub { + my ($value) = @_; + return 1 if ! defined($value); + if ($inner_types) { + return unless $inner_types->($value) + } + else { + return unless $value eq $type + || (Scalar::Util::blessed($value) && $value->isa($type)); + } + return 1; + } + } + else { + Carp::confess("Nested aggregate types are only implemented for ArrayRefs and Maybe"); + } +} + +sub _set_package_isa { + my ($package, @new_isa) = @_; + + *{ _glob_for "${package}::ISA" } = [@new_isa]; +} + +sub _set_inherited_metadata { + my $class = shift; + my $linearized_isa = mro::get_linear_isa($class); + my %new_metadata; + + for my $isa_class (reverse @$linearized_isa) { + %new_metadata = ( + %new_metadata, + %{ $metadata_for{$isa_class} || {} }, + ); + } + $metadata_for{$class} = \%new_metadata; +} + +sub unimport { + my $caller = scalar caller(); + my $stash = _stash_for( $caller ); + + delete $stash->{$_} for @{$export_for{$caller}}; +} + +sub Dumper { + require Data::Dumper; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Sortkeys = 0; + local $Data::Dumper::Quotekeys = 0; + local $Data::Dumper::Terse = 1; + + Data::Dumper::Dumper(@_) +} + +BEGIN { + if ($] >= 5.010) { + { local $@; require mro; } + } + else { + local $@; + eval { + require MRO::Compat; + } or do { + *mro::get_linear_isa = *mro::get_linear_isa_dfs = sub { + no strict 'refs'; + + my $classname = shift; + + my @lin = ($classname); + my %stored; + foreach my $parent (@{"$classname\::ISA"}) { + my $plin = mro::get_linear_isa_dfs($parent); + foreach (@$plin) { + next if exists $stored{$_}; + push(@lin, $_); + $stored{$_} = 1; + } + } + return \@lin; + }; + } + } +} + +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + +} +1; +} +# ########################################################################### +# End Mo package +# ########################################################################### + # ########################################################################### # DSNParser package # This package is a copy without comments from the original. The original @@ -2025,24 +2490,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -2174,6 +2641,9 @@ sub parse_timestamp { . (defined $f ? '%09.6f' : '%02d'), $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); } + elsif ( $val =~ m/^$proper_ts$/ ) { + return $val; + } return $val; } @@ -2498,14 +2968,25 @@ sub parse_event { if ( !$found_arg && $pos == $len ) { PTDEBUG && _d("Did not find arg, looking for special cases"); - local $INPUT_RECORD_SEPARATOR = ";\n"; + local $INPUT_RECORD_SEPARATOR = ";\n"; # get next line if ( defined(my $l = $next_event->()) ) { - chomp $l; - $l =~ s/^\s+//; - PTDEBUG && _d("Found admin statement", $l); - push @properties, 'cmd', 'Admin', 'arg', $l; - push @properties, 'bytes', length($properties[-1]); - $found_arg++; + if ( $l =~ /^\s*[A-Z][a-z_]+: / ) { + PTDEBUG && _d("Found NULL query before", $l); + local $INPUT_RECORD_SEPARATOR = ";\n#"; + my $rest_of_event = $next_event->(); + push @{$self->{pending}}, $l . $rest_of_event; + push @properties, 'cmd', 'Query', 'arg', '/* No query */'; + push @properties, 'bytes', 0; + $found_arg++; + } + else { + chomp $l; + $l =~ s/^\s+//; + PTDEBUG && _d("Found admin statement", $l); + push @properties, 'cmd', 'Admin', 'arg', $l; + push @properties, 'bytes', length($properties[-1]); + $found_arg++; + } } else { PTDEBUG && _d("I can't figure out what to do with this line"); @@ -2970,15 +3451,6 @@ sub calculate_statistical_metrics { $classes->{$class}->{$attrib}->{all}, $classes->{$class}->{$attrib} ); - - if ( $args{apdex_t} && $attrib eq 'Query_time' ) { - $class_metrics->{$class}->{$attrib}->{apdex_t} = $args{apdex_t}; - $class_metrics->{$class}->{$attrib}->{apdex} - = $self->calculate_apdex( - t => $args{apdex_t}, - samples => $classes->{$class}->{$attrib}->{all}, - ); - } } } } @@ -3103,9 +3575,6 @@ sub metrics { median => $metrics->{classes}->{$where}->{$attrib}->{median} || 0, pct_95 => $metrics->{classes}->{$where}->{$attrib}->{pct_95} || 0, stddev => $metrics->{classes}->{$where}->{$attrib}->{stddev} || 0, - - apdex_t => $metrics->{classes}->{$where}->{$attrib}->{apdex_t}, - apdex => $metrics->{classes}->{$where}->{$attrib}->{apdex}, }; } @@ -3421,51 +3890,6 @@ sub _deep_copy_attrib_vals { return $copy; } -sub calculate_apdex { - my ( $self, %args ) = @_; - my @required_args = qw(t samples); - foreach my $arg ( @required_args ) { - die "I need a $arg argument" unless $args{$arg}; - } - my ($t, $samples) = @args{@required_args}; - - if ( $t <= 0 ) { - die "Invalid target threshold (T): $t. T must be greater than zero"; - } - - my $f = 4 * $t; - PTDEBUG && _d("Apdex T =", $t, "F =", $f); - - my $satisfied = 0; - my $tolerating = 0; - my $frustrated = 0; # just for debug output - my $n_samples = 0; - BUCKET: - for my $bucket ( keys %$samples ) { - my $n_responses = $samples->{$bucket}; - my $response_time = $buck_vals[$bucket]; - - if ( $response_time <= $t ) { - $satisfied += $n_responses; - } - elsif ( $response_time <= $f ) { - $tolerating += $n_responses; - } - else { - $frustrated += $n_responses; - } - - $n_samples += $n_responses; - } - - my $apdex = sprintf('%.2f', ($satisfied + ($tolerating / 2)) / $n_samples); - PTDEBUG && _d($n_samples, "samples,", $satisfied, "satisfied,", - $tolerating, "tolerating,", $frustrated, "frustrated, Apdex score:", - $apdex); - - return $apdex; -} - sub _get_value { my ( $self, %args ) = @_; my ($event, $attrib, $alts) = @args{qw(event attribute alternates)}; @@ -8165,8 +8589,7 @@ sub _d { { package ReportFormatter; -use strict; -use warnings FATAL => 'all'; +use Mo; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG} || 0; @@ -8176,40 +8599,102 @@ use POSIX qw(ceil); eval { require Term::ReadKey }; my $have_term = $EVAL_ERROR ? 0 : 1; -sub new { - my ( $class, %args ) = @_; - my @required_args = qw(); - foreach my $arg ( @required_args ) { - die "I need a $arg argument" unless $args{$arg}; - } - my $self = { - underline_header => 1, - line_prefix => '# ', - line_width => 78, - column_spacing => ' ', - extend_right => 0, - truncate_line_mark => '...', - column_errors => 'warn', - truncate_header_side => 'left', - strip_whitespace => 1, - %args, # args above can be overriden, args below cannot - n_cols => 0, - }; - if ( ($self->{line_width} || '') eq 'auto' ) { +has underline_header => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has line_prefix => ( + is => 'ro', + isa => 'Str', + default => sub { '# ' }, +); +has line_width => ( + is => 'ro', + isa => 'Int', + default => sub { 78 }, +); +has column_spacing => ( + is => 'ro', + isa => 'Str', + default => sub { ' ' }, +); +has extend_right => ( + is => 'ro', + isa => 'Bool', + default => sub { '' }, +); +has truncate_line_mark => ( + is => 'ro', + isa => 'Str', + default => sub { '...' }, +); +has column_errors => ( + is => 'ro', + isa => 'Str', + default => sub { 'warn' }, +); +has truncate_header_side => ( + is => 'ro', + isa => 'Str', + default => sub { 'left' }, +); +has strip_whitespace => ( + is => 'ro', + isa => 'Bool', + default => sub { 1 }, +); +has title => ( + is => 'rw', + isa => 'Str', + predicate => 'has_title', +); + + +has n_cols => ( + is => 'rw', + isa => 'Int', + default => sub { 0 }, + init_arg => undef, +); + +has cols => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_cols', +); + +has lines => ( + is => 'ro', + isa => 'ArrayRef', + init_arg => undef, + default => sub { [] }, + clearer => 'clear_lines', +); + +has truncate_headers => ( + is => 'rw', + isa => 'Bool', + default => sub { undef }, + init_arg => undef, + clearer => 'clear_truncate_headers', +); + +sub BUILDARGS { + my $class = shift; + my $args = $class->SUPER::BUILDARGS(@_); + + if ( ($args->{line_width} || '') eq 'auto' ) { die "Cannot auto-detect line width because the Term::ReadKey module " . "is not installed" unless $have_term; - ($self->{line_width}) = GetTerminalSize(); + ($args->{line_width}) = GetTerminalSize(); + PTDEBUG && _d('Line width:', $args->{line_width}); } - PTDEBUG && _d('Line width:', $self->{line_width}); - return bless $self, $class; -} - -sub set_title { - my ( $self, $title ) = @_; - $self->{title} = $title; - return; + return $args; } sub set_columns { @@ -8225,7 +8710,7 @@ sub set_columns { die "Column does not have a name" unless defined $col_name; if ( $col->{width} ) { - $col->{width_pct} = ceil(($col->{width} * 100) / $self->{line_width}); + $col->{width_pct} = ceil(($col->{width} * 100) / $self->line_width()); PTDEBUG && _d('col:', $col_name, 'width:', $col->{width}, 'chars =', $col->{width_pct}, '%'); } @@ -8252,10 +8737,10 @@ sub set_columns { $col->{right_most} = 1 if $i == $#cols; - push @{$self->{cols}}, $col; + push @{$self->cols}, $col; } - $self->{n_cols} = scalar @cols; + $self->n_cols( scalar @cols ); if ( ($used_width || 0) > 100 ) { die "Total width_pct for all columns is >100%"; @@ -8265,15 +8750,15 @@ sub set_columns { my $wid_per_col = int((100 - $used_width) / scalar @auto_width_cols); PTDEBUG && _d('Line width left:', (100-$used_width), '%;', 'each auto width col:', $wid_per_col, '%'); - map { $self->{cols}->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; + map { $self->cols->[$_]->{width_pct} = $wid_per_col } @auto_width_cols; } - $min_hdr_wid += ($self->{n_cols} - 1) * length $self->{column_spacing}; + $min_hdr_wid += ($self->n_cols() - 1) * length $self->column_spacing(); PTDEBUG && _d('min header width:', $min_hdr_wid); - if ( $min_hdr_wid > $self->{line_width} ) { + if ( $min_hdr_wid > $self->line_width() ) { PTDEBUG && _d('Will truncate headers because min header width', - $min_hdr_wid, '> line width', $self->{line_width}); - $self->{truncate_headers} = 1; + $min_hdr_wid, '> line width', $self->line_width()); + $self->truncate_headers(1); } return; @@ -8282,14 +8767,14 @@ sub set_columns { sub add_line { my ( $self, @vals ) = @_; my $n_vals = scalar @vals; - if ( $n_vals != $self->{n_cols} ) { + if ( $n_vals != $self->n_cols() ) { $self->_column_error("Number of values $n_vals does not match " - . "number of columns $self->{n_cols}"); + . "number of columns " . $self->n_cols()); } for my $i ( 0..($n_vals-1) ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals[$i] ? $vals[$i] : $col->{undef_value}; - if ( $self->{strip_whitespace} ) { + if ( $self->strip_whitespace() ) { $val =~ s/^\s+//g; $val =~ s/\s+$//; $vals[$i] = $val; @@ -8298,7 +8783,7 @@ sub add_line { $col->{min_val} = min($width, ($col->{min_val} || $width)); $col->{max_val} = max($width, ($col->{max_val} || $width)); } - push @{$self->{lines}}, \@vals; + push @{$self->lines}, \@vals; return; } @@ -8306,26 +8791,28 @@ sub get_report { my ( $self, %args ) = @_; $self->_calculate_column_widths(); - $self->_truncate_headers() if $self->{truncate_headers}; + if ( $self->truncate_headers() ) { + $self->_truncate_headers(); + } $self->_truncate_line_values(%args); my @col_fmts = $self->_make_column_formats(); - my $fmt = ($self->{line_prefix} || '') - . join($self->{column_spacing}, @col_fmts); + my $fmt = $self->line_prefix() + . join($self->column_spacing(), @col_fmts); PTDEBUG && _d('Format:', $fmt); (my $hdr_fmt = $fmt) =~ s/%([^-])/%-$1/g; my @lines; - push @lines, sprintf "$self->{line_prefix}$self->{title}" if $self->{title}; + push @lines, $self->line_prefix() . $self->title() if $self->has_title(); push @lines, $self->_truncate_line( - sprintf($hdr_fmt, map { $_->{name} } @{$self->{cols}}), + sprintf($hdr_fmt, map { $_->{name} } @{$self->cols}), strip => 1, mark => '', ); - if ( $self->{underline_header} ) { - my @underlines = map { '=' x $_->{print_width} } @{$self->{cols}}; + if ( $self->underline_header() ) { + my @underlines = map { '=' x $_->{print_width} } @{$self->cols}; push @lines, $self->_truncate_line( sprintf($fmt, map { $_ || '' } @underlines), mark => '', @@ -8336,19 +8823,23 @@ sub get_report { my $vals = $_; my $i = 0; my @vals = map { - my $val = defined $_ ? $_ : $self->{cols}->[$i++]->{undef_value}; + my $val = defined $_ ? $_ : $self->cols->[$i++]->{undef_value}; $val = '' if !defined $val; $val =~ s/\n/ /g; $val; } @$vals; my $line = sprintf($fmt, @vals); - if ( $self->{extend_right} ) { + if ( $self->extend_right() ) { $line; } else { $self->_truncate_line($line); } - } @{$self->{lines}}; + } @{$self->lines}; + + $self->clear_cols(); + $self->clear_lines(); + $self->clear_truncate_headers(); return join("\n", @lines) . "\n"; } @@ -8356,7 +8847,7 @@ sub get_report { sub truncate_value { my ( $self, $col, $val, $width, $side ) = @_; return $val if length $val <= $width; - return $val if $col->{right_most} && $self->{extend_right}; + return $val if $col->{right_most} && $self->extend_right(); $side ||= $col->{truncate_side}; my $mark = $col->{truncate_mark}; if ( $side eq 'right' ) { @@ -8376,8 +8867,8 @@ sub _calculate_column_widths { my ( $self ) = @_; my $extra_space = 0; - foreach my $col ( @{$self->{cols}} ) { - my $print_width = int($self->{line_width} * ($col->{width_pct} / 100)); + foreach my $col ( @{$self->cols} ) { + my $print_width = int($self->line_width() * ($col->{width_pct} / 100)); PTDEBUG && _d('col:', $col->{name}, 'width pct:', $col->{width_pct}, 'char width:', $print_width, @@ -8401,7 +8892,7 @@ sub _calculate_column_widths { PTDEBUG && _d('Extra space:', $extra_space); while ( $extra_space-- ) { - foreach my $col ( @{$self->{cols}} ) { + foreach my $col ( @{$self->cols} ) { if ( $col->{auto_width} && ( $col->{print_width} < $col->{max_val} || $col->{print_width} < $col->{header_width}) @@ -8416,8 +8907,8 @@ sub _calculate_column_widths { sub _truncate_headers { my ( $self, $col ) = @_; - my $side = $self->{truncate_header_side}; - foreach my $col ( @{$self->{cols}} ) { + my $side = $self->truncate_header_side(); + foreach my $col ( @{$self->cols} ) { my $col_name = $col->{name}; my $print_width = $col->{print_width}; next if length $col_name <= $print_width; @@ -8430,10 +8921,10 @@ sub _truncate_headers { sub _truncate_line_values { my ( $self, %args ) = @_; - my $n_vals = $self->{n_cols} - 1; - foreach my $vals ( @{$self->{lines}} ) { + my $n_vals = $self->n_cols() - 1; + foreach my $vals ( @{$self->lines} ) { for my $i ( 0..$n_vals ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $val = defined $vals->[$i] ? $vals->[$i] : $col->{undef_value}; my $width = length $val; @@ -8459,9 +8950,9 @@ sub _truncate_line_values { sub _make_column_formats { my ( $self ) = @_; my @col_fmts; - my $n_cols = $self->{n_cols} - 1; + my $n_cols = $self->n_cols() - 1; for my $i ( 0..$n_cols ) { - my $col = $self->{cols}->[$i]; + my $col = $self->cols->[$i]; my $width = $col->{right_most} && !$col->{right_justify} ? '' : $col->{print_width}; @@ -8474,12 +8965,12 @@ sub _make_column_formats { sub _truncate_line { my ( $self, $line, %args ) = @_; - my $mark = defined $args{mark} ? $args{mark} : $self->{truncate_line_mark}; + my $mark = defined $args{mark} ? $args{mark} : $self->truncate_line_mark(); if ( $line ) { $line =~ s/\s+$// if $args{strip}; my $len = length($line); - if ( $len > $self->{line_width} ) { - $line = substr($line, 0, $self->{line_width} - length $mark); + if ( $len > $self->line_width() ) { + $line = substr($line, 0, $self->line_width() - length $mark); $line .= $mark if $mark; } } @@ -8489,7 +8980,7 @@ sub _truncate_line { sub _column_error { my ( $self, $err ) = @_; my $msg = "Column error: $err"; - $self->{column_errors} eq 'die' ? die $msg : warn $msg; + $self->column_errors() eq 'die' ? die $msg : warn $msg; return; } @@ -9429,7 +9920,7 @@ sub _report_diff_checksums { return unless keys %{$self->{diffs}->{checksums}}; my $report = new ReportFormatter(); - $report->set_title('Checksum differences'); + $report->title('Checksum differences'); $report->set_columns( $args{query_id_col}, @{$args{host_cols}}, @@ -9460,7 +9951,7 @@ sub _report_diff_col_vals { return unless keys %{$self->{diffs}->{col_vals}}; my $report = new ReportFormatter(); - $report->set_title('Column value differences'); + $report->title('Column value differences'); $report->set_columns( $args{query_id_col}, { @@ -9495,7 +9986,7 @@ sub _report_diff_row_counts { return unless keys %{$self->{diffs}->{row_counts}}; my $report = new ReportFormatter(); - $report->set_title('Row count differences'); + $report->title('Row count differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -9771,7 +10262,7 @@ sub _report_diff_big { return unless keys %{$self->{diffs}->{big}}; my $report = new ReportFormatter(); - $report->set_title('Big query time differences'); + $report->title('Big query time differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -9808,7 +10299,7 @@ sub _report_diff_in_bucket { return unless keys %{$self->{diffs}->{in_bucket}}; my $report = new ReportFormatter(); - $report->set_title('Significant query time differences'); + $report->title('Significant query time differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -10127,7 +10618,7 @@ sub _report_diff_warnings { return unless keys %{$self->{diffs}->{warnings}}; my $report = new ReportFormatter(extend_right => 1); - $report->set_title('New warnings'); + $report->title('New warnings'); $report->set_columns( $args{query_id_col}, { name => 'Host', }, @@ -10161,7 +10652,7 @@ sub _report_diff_levels { return unless keys %{$self->{diffs}->{levels}}; my $report = new ReportFormatter(extend_right => 1); - $report->set_title('Warning level differences'); + $report->title('Warning level differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -10199,7 +10690,7 @@ sub _report_diff_warning_counts { return unless keys %{$self->{diffs}->{warning_counts}}; my $report = new ReportFormatter(); - $report->set_title('Warning count differences'); + $report->title('Warning count differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -12361,7 +12852,7 @@ sub report_errors { return unless keys %$errors; my $rf = new ReportFormatter(extend_right=>1); - $rf->set_title('Errors'); + $rf->title('Errors'); $rf->set_columns( { name => 'Query ID' }, { name => 'Host', }, @@ -13139,8 +13630,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2009-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2009-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -13158,6 +13648,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-upgrade 2.1.7 +pt-upgrade 2.1.8 =cut diff --git a/bin/pt-variable-advisor b/bin/pt-variable-advisor index bf6ce2e2..0732fa2c 100755 --- a/bin/pt-variable-advisor +++ b/bin/pt-variable-advisor @@ -40,7 +40,7 @@ BEGIN { # ########################################################################### { package Percona::Toolkit; -our $VERSION = '2.1.7'; +our $VERSION = '2.1.8'; 1; } @@ -5851,8 +5851,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2010-2013 Percona Ireland Ltd. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -5870,6 +5869,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-variable-advisor 2.1.7 +pt-variable-advisor 2.1.8 =cut diff --git a/bin/pt-visual-explain b/bin/pt-visual-explain index 55bd1a33..eb43aa38 100755 --- a/bin/pt-visual-explain +++ b/bin/pt-visual-explain @@ -3124,8 +3124,8 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. -Feedback and improvements are welcome. +This program is copyright 2011-2013 Percona Ireland Ltd, +2007-2011 Baron Schwartz. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF @@ -3143,6 +3143,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-visual-explain 2.1.7 +pt-visual-explain 2.1.8 =cut diff --git a/config/deb/changelog b/config/deb/changelog index 0440710a..189c9e3e 100644 --- a/config/deb/changelog +++ b/config/deb/changelog @@ -1,3 +1,41 @@ +percona-toolkit (2.1.8) unstable; urgency=low + + * Beta support for MySQL 5.6 + * Beta support for Percona XtraDB Cluster + * pt-online-schema-change: If ran on Percona XtraDB Cluster, requires PXC 5.5.28 or newer + * pt-table-checksum: If ran on Percona XtraDB Cluster, requires PXC 5.5.28 or newer + * pt-upgrade: Added --[no]disable-query-cache + * Fixed bug 927955: Bad pod2rst transformation + * Fixed bug 898665: Bad online docs formatting for --[no]vars + * Fixed bug 1022622: pt-config-diff is case-sensitive + * Fixed bug 1007938: pt-config-diff doesn't handle end-of-line comments + * Fixed bug 917770: pt-config-diff Use of uninitialized value in substitution (s///) at line 1996 + * Fixed bug 1082104: pt-deadlock-logger doesn't handle usernames with dashes + * Fixed bug 886059: pt-heartbeat handles timezones inconsistently + * Fixed bug 1086259: pt-kill --log-dsn timestamp is wrong + * Fixed bug 1015590: pt-mysql-summary doesn't handle renamed variables in Percona Server 5.5 + * Fixed bug 1079341: pt-online-schema-change checks for foreign keys on MyISAM tables + * Fixed bug 823431: pt-query-advisor hangs on big queries + * Fixed bug 996069: pt-query-advisor RES.001 is incorrect + * Fixed bug 933465: pt-query-advisor false positive on RES.001 + * Fixed bug 937234: pt-query-advisor issues wrong RES.001 + * Fixed bug 1082599: pt-query-digest fails to parse timestamp with no query + * Fixed bug 1078838: pt-query-digest doesn't parse general log with "Connect user as user" + * Fixed bug 957442: pt-query-digest with custom --group-by throws error + * Fixed bug 887638: pt-query-digest prints negative byte offset + * Fixed bug 831525: pt-query-digest help output mangled + * Fixed bug 932614: pt-slave-restart CHANGE MASTER query causes error + * Fixed bug 1046440: pt-stalk purge_samples slows down checks + * Fixed bug 986847: pt-stalk does not report NFS iostat + * Fixed bug 1074179: pt-table-checksum doesn't ignore tables for --replicate-check-only + * Fixed bug 911385: pt-table-checksum v2 fails when --resume + --ignore-database is used + * Fixed bug 1041391: pt-table-checksum debug statement for "Chosen hash func" prints undef + * Fixed bug 1075638: pt-table-checksum Illegal division by zero at line 7950 + * Fixed bug 1052475: pt-table-checksum uninitialized value in numeric lt (<) at line 8611 + * Fixed bug 1078887: Tools let --set-vars clobber the required SQL mode + + -- Percona Toolkit Developers Fri, 21 Dec 2012 17:31:09 +0000 + percona-toolkit (2.1.7) unstable; urgency=low * Fixed bug 1080384: pt-table-checksum 2.1.6 crashes using PTDEBUG diff --git a/config/sphinx-build/conf.py b/config/sphinx-build/conf.py index 47902245..76a304e2 100644 --- a/config/sphinx-build/conf.py +++ b/config/sphinx-build/conf.py @@ -50,7 +50,7 @@ copyright = u'2011, Percona Inc' # The short X.Y version. version = '2.1' # The full version, including alpha/beta/rc tags. -release = '2.1.7' +release = '2.1.8' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/percona-toolkit.pod b/docs/percona-toolkit.pod index 961dd38d..0dbd6f03 100644 --- a/docs/percona-toolkit.pod +++ b/docs/percona-toolkit.pod @@ -17,7 +17,7 @@ no libraries are installed. Percona Toolkit is derived from Maatkit and Aspersa, two of the best-known toolkits for MySQL server administration. It is developed and supported by -Percona Inc. For more information and other free, open-source software +Percona. For more information and other free, open-source software developed by Percona, visit L. =head1 TOOLS @@ -485,13 +485,33 @@ see L<"ENVIRONMENT">. =head1 AUTHORS -Percona Toolkit is primarily developed by Baron Schwartz and Daniel Nichter, -both of whom are employed by Percona Inc. See each program's documentation -for details. +=over + +=item Baron Schwartz + +Baron created Maatkit, from which Percona Toolkit was forked. Many of +the tools and modules were originally written by Baron. + +=item Daniel Nichter + +Daniel began helping Baron with Maatkit and, later, Percona Toolkit in +June, 2008. He is the project's lead developer, employed by Percona. + +=item Brian Fraser + +Brian started with Percona in December, 2011. He works on Percona Toolkit +full-time, employed by Percona. + +=item Others + +Many people have contributed code over the years. See each tool's +"AUTHORS" section for details. + +=back =head1 COPYRIGHT, LICENSE, AND WARRANTY -Percona Toolkit is copyright 2011-2012 Percona Inc. and others. +Percona Toolkit is copyright 2011-2013 Percona Ireland Ltd and others. See each program's documentation for complete copyright notices. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED @@ -510,6 +530,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -Percona Toolkit v2.1.7 released 2012-11-19 +Percona Toolkit v2.1.8 released 2012-12-21 =cut diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 8a3d1a08..9785cd1b 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,6 +1,89 @@ Release Notes ************* +v2.1.8 released 2012-12-21 +========================== + +Percona Toolkit 2.1.8 has been released. This release includes 28 bug fixes, beta support for MySQL 5.6, and extensive support for Percona XtraDB Cluster (PXC). Users intending on running the tools on Percona XtraDB Cluster or MySQL 5.6 should upgrade. The following tools have been verified to work on PXC versions 5.5.28 and newer: + +* pt-table-chcecksum +* pt-online-schema-change +* pt-archive +* pt-mysql-summary +* pt-heartbeat +* pt-variable-advisor +* pt-config-diff +* pt-deadlock-logger + +However, there are limitations when running these tools on PXC; see the Percona XtraDB Cluster section in each tool's documentation for further details. All other tools, with the exception of pt-slave-find, pt-slave-delay and pt-slave-restart, should also work correctly, but in some cases they have not been modified to take advantage of PXC features, so they may behave differently in future releases. + +The bug fixes are widely assorted. The following highlights some of the more interesting and "hot" bugs: + +* Fixed bug 1082599: pt-query-digest fails to parse timestamp with no query + +Slow logs which include timestamps but no query--which can happen if using slow_query_log_timestamp_always in Percona Server--were misparsed, resulting in an erroneous report. Now such no-query events show up in reports as ``/* No query */``. + +* Fixed bug 1078838: pt-query-digest doesn't parse general log with "Connect user as user" + +The "as" was misparsed and the following word would end up reported as the database; pt-query-digest now handles this correctly. + +* Fixed bug 1015590: pt-mysql-summary doesn't handle renamed variables in Percona Server 5.5 + +Some renamed variables had caused the Percona Server section to work unreliably. + +* Fixed bug 1074179: pt-table-checksum doesn't ignore tables for --replicate-check-only + +When using --replicate-check-only, filter options like --databases and --tables were not applied. + +* Fixed bug 886059: pt-heartbeat handles timezones inconsistently + +Previously, pt-heartbeat respected the MySQL time zone, but this caused false readings (e.g. very high lag) with slaves running in different time zones. Now pt-heartbeat uses UTC regardless of the server or MySQL time zone. + +* Fixed bug 1079341: pt-online-schema-change checks for foreign keys on MyISAM tables + +Since MyISAM tables can't have foreign keys, and the tool uses the information_schema to find child tables, this could cause unnecessary load on the server. + +2.1.8 continues the trend of solid bug fix releases, and all 2.1 users are encouraged to upgrade. + +Percona Toolkit packages can be downloaded from http://www.percona.com/downloads/percona-toolkit/ or the Percona Software Repositories (http://www.percona.com/software/repositories/). + +Changelog +--------- + +* Beta support for MySQL 5.6 +* Beta support for Percona XtraDB Cluster +* pt-online-schema-change: If ran on Percona XtraDB Cluster, requires PXC 5.5.28 or newer +* pt-table-checksum: If ran on Percona XtraDB Cluster, requires PXC 5.5.28 or newer +* pt-upgrade: Added --[no]disable-query-cache +* Fixed bug 927955: Bad pod2rst transformation +* Fixed bug 898665: Bad online docs formatting for --[no]vars +* Fixed bug 1022622: pt-config-diff is case-sensitive +* Fixed bug 1007938: pt-config-diff doesn't handle end-of-line comments +* Fixed bug 917770: pt-config-diff Use of uninitialized value in substitution (s///) at line 1996 +* Fixed bug 1082104: pt-deadlock-logger doesn't handle usernames with dashes +* Fixed bug 886059: pt-heartbeat handles timezones inconsistently +* Fixed bug 1086259: pt-kill --log-dsn timestamp is wrong +* Fixed bug 1015590: pt-mysql-summary doesn't handle renamed variables in Percona Server 5.5 +* Fixed bug 1079341: pt-online-schema-change checks for foreign keys on MyISAM tables +* Fixed bug 823431: pt-query-advisor hangs on big queries +* Fixed bug 996069: pt-query-advisor RES.001 is incorrect +* Fixed bug 933465: pt-query-advisor false positive on RES.001 +* Fixed bug 937234: pt-query-advisor issues wrong RES.001 +* Fixed bug 1082599: pt-query-digest fails to parse timestamp with no query +* Fixed bug 1078838: pt-query-digest doesn't parse general log with "Connect user as user" +* Fixed bug 957442: pt-query-digest with custom --group-by throws error +* Fixed bug 887638: pt-query-digest prints negative byte offset +* Fixed bug 831525: pt-query-digest help output mangled +* Fixed bug 932614: pt-slave-restart CHANGE MASTER query causes error +* Fixed bug 1046440: pt-stalk purge_samples slows down checks +* Fixed bug 986847: pt-stalk does not report NFS iostat +* Fixed bug 1074179: pt-table-checksum doesn't ignore tables for --replicate-check-only +* Fixed bug 911385: pt-table-checksum v2 fails when --resume + --ignore-database is used +* Fixed bug 1041391: pt-table-checksum debug statement for "Chosen hash func" prints undef +* Fixed bug 1075638: pt-table-checksum Illegal division by zero at line 7950 +* Fixed bug 1052475: pt-table-checksum uninitialized value in numeric lt (<) at line 8611 +* Fixed bug 1078887: Tools let --set-vars clobber the required SQL mode + v2.1.7 released 2012-11-19 ========================== diff --git a/lib/Advisor.pm b/lib/Advisor.pm index e22cd9cf..77aa224a 100644 --- a/lib/Advisor.pm +++ b/lib/Advisor.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/AdvisorRules.pm b/lib/AdvisorRules.pm index f815914e..e8c5dd99 100644 --- a/lib/AdvisorRules.pm +++ b/lib/AdvisorRules.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/BinaryLogParser.pm b/lib/BinaryLogParser.pm index b6804893..0caa3fb4 100644 --- a/lib/BinaryLogParser.pm +++ b/lib/BinaryLogParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ChangeHandler.pm b/lib/ChangeHandler.pm index 1507b6ed..c8ac3977 100644 --- a/lib/ChangeHandler.pm +++ b/lib/ChangeHandler.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/CleanupTask.pm b/lib/CleanupTask.pm index 5401360b..f3f9ddac 100644 --- a/lib/CleanupTask.pm +++ b/lib/CleanupTask.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/CompareQueryTimes.pm b/lib/CompareQueryTimes.pm index 71c091a5..3faa6cb7 100644 --- a/lib/CompareQueryTimes.pm +++ b/lib/CompareQueryTimes.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED @@ -298,7 +298,7 @@ sub _report_diff_big { return unless keys %{$self->{diffs}->{big}}; my $report = new ReportFormatter(); - $report->set_title('Big query time differences'); + $report->title('Big query time differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -347,7 +347,7 @@ sub _report_diff_in_bucket { return unless keys %{$self->{diffs}->{in_bucket}}; my $report = new ReportFormatter(); - $report->set_title('Significant query time differences'); + $report->title('Significant query time differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, diff --git a/lib/CompareResults.pm b/lib/CompareResults.pm index e623601f..f760a917 100644 --- a/lib/CompareResults.pm +++ b/lib/CompareResults.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED @@ -874,7 +874,7 @@ sub _report_diff_checksums { return unless keys %{$self->{diffs}->{checksums}}; my $report = new ReportFormatter(); - $report->set_title('Checksum differences'); + $report->title('Checksum differences'); $report->set_columns( $args{query_id_col}, @{$args{host_cols}}, @@ -905,7 +905,7 @@ sub _report_diff_col_vals { return unless keys %{$self->{diffs}->{col_vals}}; my $report = new ReportFormatter(); - $report->set_title('Column value differences'); + $report->title('Column value differences'); $report->set_columns( $args{query_id_col}, { @@ -940,7 +940,7 @@ sub _report_diff_row_counts { return unless keys %{$self->{diffs}->{row_counts}}; my $report = new ReportFormatter(); - $report->set_title('Row count differences'); + $report->title('Row count differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, diff --git a/lib/CompareWarnings.pm b/lib/CompareWarnings.pm index 7cd30142..9c79a4c6 100644 --- a/lib/CompareWarnings.pm +++ b/lib/CompareWarnings.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED @@ -324,7 +324,7 @@ sub _report_diff_warnings { return unless keys %{$self->{diffs}->{warnings}}; my $report = new ReportFormatter(extend_right => 1); - $report->set_title('New warnings'); + $report->title('New warnings'); $report->set_columns( $args{query_id_col}, { name => 'Host', }, @@ -358,7 +358,7 @@ sub _report_diff_levels { return unless keys %{$self->{diffs}->{levels}}; my $report = new ReportFormatter(extend_right => 1); - $report->set_title('Warning level differences'); + $report->title('Warning level differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, @@ -396,7 +396,7 @@ sub _report_diff_warning_counts { return unless keys %{$self->{diffs}->{warning_counts}}; my $report = new ReportFormatter(); - $report->set_title('Warning count differences'); + $report->title('Warning count differences'); my $hostno = 0; $report->set_columns( $args{query_id_col}, diff --git a/lib/CopyRowsInsertSelect.pm b/lib/CopyRowsInsertSelect.pm index b210d902..74a704f7 100644 --- a/lib/CopyRowsInsertSelect.pm +++ b/lib/CopyRowsInsertSelect.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Cxn.pm b/lib/Cxn.pm index df7f6ce4..864abb3f 100644 --- a/lib/Cxn.pm +++ b/lib/Cxn.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/DSNParser.pm b/lib/DSNParser.pm index b4816724..f48939ab 100644 --- a/lib/DSNParser.pm +++ b/lib/DSNParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Daemon.pm b/lib/Daemon.pm index 75acaf78..2e53cccd 100644 --- a/lib/Daemon.pm +++ b/lib/Daemon.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Diskstats.pm b/lib/Diskstats.pm index f12499a3..0365b8fd 100644 --- a/lib/Diskstats.pm +++ b/lib/Diskstats.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/DiskstatsGroupByAll.pm b/lib/DiskstatsGroupByAll.pm index 8627496d..75e8c417 100644 --- a/lib/DiskstatsGroupByAll.pm +++ b/lib/DiskstatsGroupByAll.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/DiskstatsGroupByDisk.pm b/lib/DiskstatsGroupByDisk.pm index 94fbefc6..e877e4c2 100644 --- a/lib/DiskstatsGroupByDisk.pm +++ b/lib/DiskstatsGroupByDisk.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/DiskstatsGroupBySample.pm b/lib/DiskstatsGroupBySample.pm index c8761a6e..3c4eca61 100644 --- a/lib/DiskstatsGroupBySample.pm +++ b/lib/DiskstatsGroupBySample.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/DiskstatsMenu.pm b/lib/DiskstatsMenu.pm index 86516001..0b054eba 100644 --- a/lib/DiskstatsMenu.pm +++ b/lib/DiskstatsMenu.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/DuplicateKeyFinder.pm b/lib/DuplicateKeyFinder.pm index b36b72a2..bef971ca 100644 --- a/lib/DuplicateKeyFinder.pm +++ b/lib/DuplicateKeyFinder.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/EventAggregator.pm b/lib/EventAggregator.pm index 8c06c12d..9c922b15 100644 --- a/lib/EventAggregator.pm +++ b/lib/EventAggregator.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/EventTimeline.pm b/lib/EventTimeline.pm index d60ef8c8..8f8f6a9f 100644 --- a/lib/EventTimeline.pm +++ b/lib/EventTimeline.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ExecutionThrottler.pm b/lib/ExecutionThrottler.pm index d04aae1b..cf8b238b 100644 --- a/lib/ExecutionThrottler.pm +++ b/lib/ExecutionThrottler.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ExplainAnalyzer.pm b/lib/ExplainAnalyzer.pm index 9dc32bd9..b685ca9e 100644 --- a/lib/ExplainAnalyzer.pm +++ b/lib/ExplainAnalyzer.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/FileIterator.pm b/lib/FileIterator.pm index 0552481f..c966453e 100644 --- a/lib/FileIterator.pm +++ b/lib/FileIterator.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/GeneralLogParser.pm b/lib/GeneralLogParser.pm index 14f47945..5c46f8c6 100644 --- a/lib/GeneralLogParser.pm +++ b/lib/GeneralLogParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/HTTP/Micro.pm b/lib/HTTP/Micro.pm index f5dc472b..127894c4 100644 --- a/lib/HTTP/Micro.pm +++ b/lib/HTTP/Micro.pm @@ -1,4 +1,4 @@ -# This program is copyright 2012-2013 Percona Inc. +# This program is copyright 2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/HTTPProtocolParser.pm b/lib/HTTPProtocolParser.pm index 1974b1ce..aaa8e522 100644 --- a/lib/HTTPProtocolParser.pm +++ b/lib/HTTPProtocolParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/IndexLength.pm b/lib/IndexLength.pm index 15e65164..32bbfc58 100644 --- a/lib/IndexLength.pm +++ b/lib/IndexLength.pm @@ -1,4 +1,4 @@ -# This program is copyright 2012 Percona Inc. +# This program is copyright 2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/IndexUsage.pm b/lib/IndexUsage.pm index 06e169c8..d645f29a 100644 --- a/lib/IndexUsage.pm +++ b/lib/IndexUsage.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/JSONReportFormatter.pm b/lib/JSONReportFormatter.pm index 27118a6b..be0e534f 100644 --- a/lib/JSONReportFormatter.pm +++ b/lib/JSONReportFormatter.pm @@ -1,14 +1,14 @@ { package JSONReportFormatter; use Mo; -use JSON (); - -use List::Util qw(sum); +use List::Util qw(sum); use Transformers qw(make_checksum parse_timestamp); use constant PTDEBUG => $ENV{PTDEBUG} || 0; +my $have_json = eval { require JSON }; + our $pretty_json = undef; our $sorted_json = undef; @@ -18,11 +18,23 @@ has _json => ( is => 'ro', init_arg => undef, builder => '_build_json', - handles => { encode_json => 'encode' }, ); sub _build_json { - return JSON->new->utf8->pretty($pretty_json)->canonical($sorted_json); + return unless $have_json; + return JSON->new->utf8 + ->pretty($pretty_json) + ->canonical($sorted_json); +} + +sub encode_json { + my ($self, $encode) = @_; + if ( my $json = $self->_json ) { + return $json->encode($encode); + } + else { + return Transformers::encode_json($encode); + } } override [qw(rusage date hostname files header profile prepared)] => sub { @@ -103,7 +115,9 @@ override query_report => sub { }; } - return $self->encode_json(\@queries) . "\n"; + my $json = $self->encode_json(\@queries); + $json .= "\n" if $json !~ /\n\Z/; + return $json . "\n"; }; 1; diff --git a/lib/KeySize.pm b/lib/KeySize.pm index 8093cb11..6f52d8d6 100644 --- a/lib/KeySize.pm +++ b/lib/KeySize.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MasterSlave.pm b/lib/MasterSlave.pm index a60f63c4..cf3c2aa9 100644 --- a/lib/MasterSlave.pm +++ b/lib/MasterSlave.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MemcachedEvent.pm b/lib/MemcachedEvent.pm index e5d89dcb..ca2f637e 100644 --- a/lib/MemcachedEvent.pm +++ b/lib/MemcachedEvent.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MemcachedProtocolParser.pm b/lib/MemcachedProtocolParser.pm index e2f573a6..2da3cf88 100644 --- a/lib/MemcachedProtocolParser.pm +++ b/lib/MemcachedProtocolParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Percona Inc. +# This program is copyright 2007-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MySQLConfig.pm b/lib/MySQLConfig.pm index 20fe8ec7..c7064734 100644 --- a/lib/MySQLConfig.pm +++ b/lib/MySQLConfig.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MySQLConfigComparer.pm b/lib/MySQLConfigComparer.pm index febd023f..c0e500a4 100644 --- a/lib/MySQLConfigComparer.pm +++ b/lib/MySQLConfigComparer.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MySQLProtocolParser.pm b/lib/MySQLProtocolParser.pm index 64867bce..b7b7f636 100644 --- a/lib/MySQLProtocolParser.pm +++ b/lib/MySQLProtocolParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Percona Inc. +# This program is copyright 2007-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/MySQLStatusWaiter.pm b/lib/MySQLStatusWaiter.pm index f87a9841..ab5bfa55 100644 --- a/lib/MySQLStatusWaiter.pm +++ b/lib/MySQLStatusWaiter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/NibbleIterator.pm b/lib/NibbleIterator.pm index c8c03405..7edabdc3 100644 --- a/lib/NibbleIterator.pm +++ b/lib/NibbleIterator.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/OobNibbleIterator.pm b/lib/OobNibbleIterator.pm index a98b5cc8..c1204023 100644 --- a/lib/OobNibbleIterator.pm +++ b/lib/OobNibbleIterator.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/OptionParser.pm b/lib/OptionParser.pm index 58496ff4..938aaff0 100644 --- a/lib/OptionParser.pm +++ b/lib/OptionParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Outfile.pm b/lib/Outfile.pm index b940da6e..6583c0a4 100644 --- a/lib/Outfile.pm +++ b/lib/Outfile.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Percona/Toolkit.pm b/lib/Percona/Toolkit.pm index 7b00acc0..f699c297 100644 --- a/lib/Percona/Toolkit.pm +++ b/lib/Percona/Toolkit.pm @@ -1,4 +1,4 @@ -# This program is copyright 2012 Percona Inc. +# This program is copyright 2012-2013 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/PerconaTest.pm b/lib/PerconaTest.pm index 5a0a2af4..41620364 100644 --- a/lib/PerconaTest.pm +++ b/lib/PerconaTest.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/PgLogParser.pm b/lib/PgLogParser.pm index c1b4d733..8ac78e1d 100644 --- a/lib/PgLogParser.pm +++ b/lib/PgLogParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2010-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Pipeline.pm b/lib/Pipeline.pm index fcb47226..1f50afbf 100644 --- a/lib/Pipeline.pm +++ b/lib/Pipeline.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/PodParser.pm b/lib/PodParser.pm index c6475eb1..4da7df2f 100644 --- a/lib/PodParser.pm +++ b/lib/PodParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Processlist.pm b/lib/Processlist.pm index 015b8508..e53eca3b 100644 --- a/lib/Processlist.pm +++ b/lib/Processlist.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2008-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Progress.pm b/lib/Progress.pm index 54b47b12..07ad4f4c 100644 --- a/lib/Progress.pm +++ b/lib/Progress.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ProtocolParser.pm b/lib/ProtocolParser.pm index d8aaa544..a733e673 100644 --- a/lib/ProtocolParser.pm +++ b/lib/ProtocolParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/QueryAdvisorRules.pm b/lib/QueryAdvisorRules.pm index 3d4cddc1..ad315ef2 100644 --- a/lib/QueryAdvisorRules.pm +++ b/lib/QueryAdvisorRules.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/QueryParser.pm b/lib/QueryParser.pm index 90c6e7d0..b6f56f20 100644 --- a/lib/QueryParser.pm +++ b/lib/QueryParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/QueryReportFormatter.pm b/lib/QueryReportFormatter.pm index e679fdaa..ac45a51c 100644 --- a/lib/QueryReportFormatter.pm +++ b/lib/QueryReportFormatter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/QueryReview.pm b/lib/QueryReview.pm index 699aae6c..f2ecc062 100644 --- a/lib/QueryReview.pm +++ b/lib/QueryReview.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/QueryRewriter.pm b/lib/QueryRewriter.pm index 43d426e9..fddee79f 100644 --- a/lib/QueryRewriter.pm +++ b/lib/QueryRewriter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Quoter.pm b/lib/Quoter.pm index ea05d00d..21359bd8 100644 --- a/lib/Quoter.pm +++ b/lib/Quoter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/RawLogParser.pm b/lib/RawLogParser.pm index 1bd6426a..6052e98a 100644 --- a/lib/RawLogParser.pm +++ b/lib/RawLogParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2012 Percona Inc. +# This program is copyright 2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ReadKeyMini.pm b/lib/ReadKeyMini.pm index 6bc620ed..601ad419 100644 --- a/lib/ReadKeyMini.pm +++ b/lib/ReadKeyMini.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2012 Percona Inc. +# This program is copyright 2010-2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ReplicaLagWaiter.pm b/lib/ReplicaLagWaiter.pm index abe442f2..194406e0 100644 --- a/lib/ReplicaLagWaiter.pm +++ b/lib/ReplicaLagWaiter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/ReportFormatter.pm b/lib/ReportFormatter.pm index 06c7ba46..936f2919 100644 --- a/lib/ReportFormatter.pm +++ b/lib/ReportFormatter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Retry.pm b/lib/Retry.pm index ca8d9366..43a8f840 100644 --- a/lib/Retry.pm +++ b/lib/Retry.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/RowChecksum.pm b/lib/RowChecksum.pm index 8c58b6fb..c567c19b 100644 --- a/lib/RowChecksum.pm +++ b/lib/RowChecksum.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/RowDiff.pm b/lib/RowDiff.pm index 7f254ad6..80e33b99 100644 --- a/lib/RowDiff.pm +++ b/lib/RowDiff.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Runtime.pm b/lib/Runtime.pm index 6ba0bef9..18e266a8 100644 --- a/lib/Runtime.pm +++ b/lib/Runtime.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/SQLParser.pm b/lib/SQLParser.pm index d3980806..ba9b69e3 100644 --- a/lib/SQLParser.pm +++ b/lib/SQLParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2012 Percona Inc. +# This program is copyright 2010-2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Sandbox.pm b/lib/Sandbox.pm index e42ad3ce..0a574648 100644 --- a/lib/Sandbox.pm +++ b/lib/Sandbox.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2012 Percona Inc. +# This program is copyright 2008-2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Schema.pm b/lib/Schema.pm index 77f8c980..5d8b7ff0 100644 --- a/lib/Schema.pm +++ b/lib/Schema.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/SchemaIterator.pm b/lib/SchemaIterator.pm index 56d46cd6..901ce76f 100644 --- a/lib/SchemaIterator.pm +++ b/lib/SchemaIterator.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2011 Percona Inc. +# This program is copyright 2009-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/SimpleTCPDumpParser.pm b/lib/SimpleTCPDumpParser.pm index ca68848c..b462b9a5 100644 --- a/lib/SimpleTCPDumpParser.pm +++ b/lib/SimpleTCPDumpParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/SlowLogParser.pm b/lib/SlowLogParser.pm index 9840539c..d1fc1b16 100644 --- a/lib/SlowLogParser.pm +++ b/lib/SlowLogParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/SlowLogWriter.pm b/lib/SlowLogWriter.pm index fb209fa2..84ed0867 100644 --- a/lib/SlowLogWriter.pm +++ b/lib/SlowLogWriter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/SysLogParser.pm b/lib/SysLogParser.pm index e094705b..ae36f39c 100644 --- a/lib/SysLogParser.pm +++ b/lib/SysLogParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2010-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TCPRequestAggregator.pm b/lib/TCPRequestAggregator.pm index 1be73f94..0655f02f 100644 --- a/lib/TCPRequestAggregator.pm +++ b/lib/TCPRequestAggregator.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableChecksum.pm b/lib/TableChecksum.pm index 2b7206c0..59c8a489 100644 --- a/lib/TableChecksum.pm +++ b/lib/TableChecksum.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableChunker.pm b/lib/TableChunker.pm index 03235b45..1130a61b 100644 --- a/lib/TableChunker.pm +++ b/lib/TableChunker.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableNibbler.pm b/lib/TableNibbler.pm index 788598ca..0eed5177 100644 --- a/lib/TableNibbler.pm +++ b/lib/TableNibbler.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableParser.pm b/lib/TableParser.pm index 91a71840..6f5dcc4c 100644 --- a/lib/TableParser.pm +++ b/lib/TableParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableSyncChunk.pm b/lib/TableSyncChunk.pm index 65aea35e..610ccba9 100644 --- a/lib/TableSyncChunk.pm +++ b/lib/TableSyncChunk.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableSyncGroupBy.pm b/lib/TableSyncGroupBy.pm index 7115d632..2e2d2088 100644 --- a/lib/TableSyncGroupBy.pm +++ b/lib/TableSyncGroupBy.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableSyncNibble.pm b/lib/TableSyncNibble.pm index 8fe07af9..62b637a1 100644 --- a/lib/TableSyncNibble.pm +++ b/lib/TableSyncNibble.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableSyncStream.pm b/lib/TableSyncStream.pm index 541f2bac..0d30c57e 100644 --- a/lib/TableSyncStream.pm +++ b/lib/TableSyncStream.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableSyncer.pm b/lib/TableSyncer.pm index f096103e..9901397d 100644 --- a/lib/TableSyncer.pm +++ b/lib/TableSyncer.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TableUsage.pm b/lib/TableUsage.pm index acc349bb..bf6ea442 100644 --- a/lib/TableUsage.pm +++ b/lib/TableUsage.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011-2012 Percona Inc. +# This program is copyright 2011-2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TcpdumpParser.pm b/lib/TcpdumpParser.pm index 79379925..930e2077 100644 --- a/lib/TcpdumpParser.pm +++ b/lib/TcpdumpParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/TextResultSetParser.pm b/lib/TextResultSetParser.pm index fafd618a..0de3b2f9 100644 --- a/lib/TextResultSetParser.pm +++ b/lib/TextResultSetParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/Transformers.pm b/lib/Transformers.pm index 561111fd..f378c474 100644 --- a/lib/Transformers.pm +++ b/lib/Transformers.pm @@ -1,4 +1,4 @@ -# This program is copyright 2008-2011 Percona Inc. +# This program is copyright 2008-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/UpgradeReportFormatter.pm b/lib/UpgradeReportFormatter.pm index 46dd80e7..7338bdad 100644 --- a/lib/UpgradeReportFormatter.pm +++ b/lib/UpgradeReportFormatter.pm @@ -1,4 +1,4 @@ -# This program is copyright 2009-2012 Percona Inc. +# This program is copyright 2009-2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/VariableAdvisorRules.pm b/lib/VariableAdvisorRules.pm index 16a1af05..7a9d3091 100644 --- a/lib/VariableAdvisorRules.pm +++ b/lib/VariableAdvisorRules.pm @@ -1,4 +1,4 @@ -# This program is copyright 2010-2011 Percona Inc. +# This program is copyright 2010-2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/VersionCheck.pm b/lib/VersionCheck.pm index bb761d8e..a765aa1a 100644 --- a/lib/VersionCheck.pm +++ b/lib/VersionCheck.pm @@ -1,4 +1,4 @@ -# This program is copyright 2012-2013 Percona Inc. +# This program is copyright 2012 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED @@ -26,41 +26,17 @@ use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); -use File::Basename qw(); -use Data::Dumper qw(); -use Data::Dumper qw(); -use Digest::MD5 qw(md5_hex); -use Sys::Hostname qw(hostname); -use Fcntl qw(:DEFAULT); -use File::Basename qw(); -use File::Spec; - use constant PTDEBUG => $ENV{PTDEBUG} || 0; -local $EVAL_ERROR; -eval { - require Percona::Toolkit; - require HTTP::Micro; -}; +use File::Basename (); +use Data::Dumper (); -my $dir = File::Spec->tmpdir(); -my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check'); -my $check_time_limit = 60 * 60 * 24; # one day +sub Dumper { + local $Data::Dumper::Indent = 1; + local $Data::Dumper::Sortkeys = 1; + local $Data::Dumper::Quotekeys = 0; -sub validate_options { - my ($o) = @_; - - # No need to validate anything if we didn't get an explicit v-c - return if !$o->got('version-check'); - - my $value = $o->get('version-check'); - my @values = split /, /, - $o->read_para_after(__FILE__, qr/MAGIC_version_check/); - chomp(@values); - - return if grep { $value eq $_ } @values; - $o->save_error("--version-check invalid value $value. Accepted values are " - . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); + Data::Dumper::Dumper(@_); } sub new { @@ -78,360 +54,6 @@ sub new { return bless $self, $class; } -sub version_check { - my %args = @_; - my @instances = $args{instances} ? @{ $args{instances} } : (); - # If this blows up, oh well, don't bother the user about it. - # This feature is a "best effort" only; we don't want it to - # get in the way of the tool's real work. - - if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) { - warn '--version-check is disabled by the PERCONA_VERSION_CHECK ', - "environment variable.\n\n"; - return; - } - - # we got here if the protocol wasn't "off", and the values - # were validated earlier, so just handle auto - # This line is mostly here for the test suite: - $args{protocol} ||= 'https'; - my @protocols = $args{protocol} eq 'auto' - ? qw(https http) - : $args{protocol}; - - my $instances_to_check = []; - my $time = int(time()); - eval { - # Name and ID the instances. The name is for debugging; the ID is - # what the code uses. - foreach my $instance ( @instances ) { - my ($name, $id) = _generate_identifier($instance); - $instance->{name} = $name; - $instance->{id} = $id; - } - - my $time_to_check; - ($time_to_check, $instances_to_check) - = time_to_check($check_time_file, \@instances, $time); - if ( !$time_to_check ) { - warn 'It is not time to --version-check again; ', - "only 1 check per day.\n\n"; - return; - } - - my $advice; - my $e; - for my $protocol ( @protocols ) { - $advice = eval { pingback( - url => $ENV{PERCONA_VERSION_CHECK_URL} || "$protocol://v.percona.com", - instances => $instances_to_check, - protocol => $protocol, - ) }; - # No advice, and no error, so no reason to keep trying. - last if !$advice && !$EVAL_ERROR; - $e ||= $EVAL_ERROR; - } - if ( $advice ) { - print "# Percona suggests these upgrades:\n"; - print join("\n", map { "# * $_" } @$advice), "\n\n"; - } - else { - die $e if $e; - print "# No suggestions at this time.\n\n"; - ($ENV{PTVCDEBUG} || PTDEBUG ) - && _d('--version-check worked, but there were no suggestions'); - } - }; - if ( $EVAL_ERROR ) { - warn "Error doing --version-check: $EVAL_ERROR"; - } - else { - update_checks_file($check_time_file, $instances_to_check, $time); - } - - return; -} - -sub pingback { - my (%args) = @_; - my @required_args = qw(url); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($url) = @args{@required_args}; - - # Optional args - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; - - $ua ||= HTTP::Micro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); - - # GET https://upgrade.percona.com, the server will return - # a plaintext list of items/programs it wants the tool - # to get, one item per line with the format ITEM;TYPE[;VARS] - # ITEM is the pretty name of the item/program; TYPE is - # the type of ITEM that helps the tool determine how to - # get the item's version; and VARS is optional for certain - # items/types that need extra hints. - my $response = $ua->request('GET', $url); - ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); - die "No response from GET $url" - if !$response; - die("GET on $url returned HTTP status $response->{status}; expected 200\n", - ($response->{content} || '')) if $response->{status} != 200; - die("GET on $url did not return any programs to check") - if !$response->{content}; - - # Parse the plaintext server response into a hashref keyed on - # the items like: - # "MySQL" => { - # item => "MySQL", - # type => "mysql_variables", - # vars => ["version", "version_comment"], - # } - my $items = $vc->parse_server_response( - response => $response->{content} - ); - die "Failed to parse server requested programs: $response->{content}" - if !scalar keys %$items; - - # Get the versions for those items in another hashref also keyed on - # the items like: - # "MySQL" => "MySQL Community Server 5.1.49-log", - my $versions = $vc->get_versions( - items => $items, - instances => $instances, - ); - die "Failed to get any program versions; should have at least gotten Perl" - if !scalar keys %$versions; - - # Join the items and whatever versions are available and re-encode - # them in same simple plaintext item-per-line protocol, and send - # it back to Percona. - my $client_content = encode_client_response( - items => $items, - versions => $versions, - general_id => md5_hex( hostname() ), - ); - - my $client_response = { - headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) }, - content => $client_content, - }; - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Client response:', Dumper($client_response)); - } - - $response = $ua->request('POST', $url, $client_response); - PTDEBUG && _d('Server suggestions:', Dumper($response)); - die "No response from POST $url $client_response" - if !$response; - die "POST $url returned HTTP status $response->{status}; expected 200" - if $response->{status} != 200; - - # If the server does not have any suggestions, - # there will not be any content. - return unless $response->{content}; - - # If the server has suggestions for items, it sends them back in - # the same format: ITEM:TYPE:SUGGESTION\n. ITEM:TYPE is mostly for - # debugging; the tool just repports the suggestions. - $items = $vc->parse_server_response( - response => $response->{content}, - split_vars => 0, - ); - die "Failed to parse server suggestions: $response->{content}" - if !scalar keys %$items; - my @suggestions = map { $_->{vars} } - sort { $a->{item} cmp $b->{item} } - values %$items; - - return \@suggestions; -} - -sub time_to_check { - my ($file, $instances, $time) = @_; - die "I need a file argument" unless $file; - $time ||= int(time()); # current time - - # If we have MySQL instances, check only the ones that haven't been - # seen/checked before or were check > 24 hours ago. - if ( @$instances ) { - my $instances_to_check = instances_to_check($file, $instances, $time); - return scalar @$instances_to_check, $instances_to_check; - } - - return 1 if !-f $file; - - # No MySQL instances (happens with tools like pt-diskstats), so just - # check the file's mtime and check if it was updated > 24 hours ago. - my $mtime = (stat $file)[9]; - if ( !defined $mtime ) { - PTDEBUG && _d('Error getting modified time of', $file); - return 1; - } - PTDEBUG && _d('time=', $time, 'mtime=', $mtime); - if ( ($time - $mtime) > $check_time_limit ) { - return 1; - } - - # File was updated less than a day ago; don't check yet. - return 0; -} - -sub instances_to_check { - my ($file, $instances, $time, %args) = @_; - - # The time limit file contains "ID,time" lines for each MySQL instance - # that the last tool connected to. The last tool may have seen fewer - # or more MySQL instances than the current tool, but we'll read them - # all and check only the MySQL instances for the current tool. - my $file_contents = ''; - if (open my $fh, '<', $file) { - chomp($file_contents = do { local $/ = undef; <$fh> }); - close $fh; - } - my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg; - - # Check the MySQL instances that have either 1) never been checked - # (or seen) before, or 2) were check > 24 hours ago. - my @instances_to_check; - foreach my $instance ( @$instances ) { - my $mtime = $cached_instances{ $instance->{id} }; - if ( !$mtime || (($time - $mtime) > $check_time_limit) ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Time to check MySQL instance', $instance->{name}); - } - push @instances_to_check, $instance; - $cached_instances{ $instance->{id} } = $time; - } - } - - if ( $args{update_file} ) { - # Overwrite the time limit file with the check times for instances - # we're going to check or with the original check time for instances - # that we're still waiting on. - open my $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR"; - while ( my ($id, $time) = each %cached_instances ) { - print { $fh } "$id,$time\n"; - } - close $fh or die "Cannot close $file: $OS_ERROR"; - } - - return \@instances_to_check; -} - -sub update_checks_file { - my ($file, $instances, $time) = @_; - - # If there's no time limit file, then create it, but - # don't return yet, let _time_to_check_by_instances() write any MySQL - # instances to the file, then return. - if ( !-f $file ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Creating time limit file', $file); - } - _touch($file); - } - - if ( $instances && @$instances ) { - instances_to_check($file, $instances, $time, update_file => 1); - return; - } - - my $mtime = (stat $file)[9]; - if ( !defined $mtime ) { - _touch($file); - return; - } - PTDEBUG && _d('time=', $time, 'mtime=', $mtime); - if ( ($time - $mtime) > $check_time_limit ) { - _touch($file); - return; - } - - return; -} - -sub _touch { - my ($file) = @_; - sysopen my $fh, $file, O_WRONLY|O_CREAT - or die "Cannot create $file : $!"; - close $fh or die "Cannot close $file : $!"; - utime(undef, undef, $file); -} - -sub _generate_identifier { - my $instance = shift; - my $dbh = $instance->{dbh}; - my $dsn = $instance->{dsn}; - - # MySQL 5.1+ has @@hostname and @@port - # MySQL 5.0 has @@hostname but port only in SHOW VARS - # MySQL 4.x has nothing, so we use the dsn - my $sql = q{SELECT CONCAT(@@hostname, @@port)}; - PTDEBUG && _d($sql); - my ($name) = eval { $dbh->selectrow_array($sql) }; - if ( $EVAL_ERROR ) { - # MySQL 4.x or 5.0 - PTDEBUG && _d($EVAL_ERROR); - $sql = q{SELECT @@hostname}; - PTDEBUG && _d($sql); - ($name) = eval { $dbh->selectrow_array($sql) }; - if ( $EVAL_ERROR ) { - # MySQL 4.x - PTDEBUG && _d($EVAL_ERROR); - $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306); - } - else { - # MySQL 5.0 - $sql = q{SHOW VARIABLES LIKE 'port'}; - PTDEBUG && _d($sql); - my (undef, $port) = eval { $dbh->selectrow_array($sql) }; - PTDEBUG && _d('port:', $port); - $name .= $port || ''; - } - } - my $id = md5_hex($name); - - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('MySQL instance', $name, 'is', $id); - } - - return $name, $id; -} - -sub encode_client_response { - my (%args) = @_; - my @required_args = qw(items versions general_id); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items, $versions, $general_id) = @args{@required_args}; - - # There may not be a version for each item. For example, the server - # may have requested the "MySQL" (version) item, but if the tool - # didn't connect to MySQL, there won't be a $versions->{MySQL}. - # That's ok; just use what we've got. - # NOTE: the sort is only need to make testing deterministic. - my @lines; - foreach my $item ( sort keys %$items ) { - next unless exists $versions->{$item}; - if ( ref($versions->{$item}) eq 'HASH' ) { - my $mysql_versions = $versions->{$item}; - for my $id ( sort keys %$mysql_versions ) { - push @lines, join(';', $id, $item, $mysql_versions->{$id}); - } - } - else { - push @lines, join(';', $general_id, $item, $versions->{$item}); - } - } - - my $client_response = join("\n", @lines) . "\n"; - return $client_response; -} - sub parse_server_response { my ($self, %args) = @_; my @required_args = qw(response); @@ -675,6 +297,14 @@ sub get_bin_version { return $version; } +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"; +} + 1; } # ########################################################################### diff --git a/lib/VersionParser.pm b/lib/VersionParser.pm index 1a8833d6..0a6dd496 100644 --- a/lib/VersionParser.pm +++ b/lib/VersionParser.pm @@ -1,4 +1,4 @@ -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. +# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/lib/WeightedAvgRate.pm b/lib/WeightedAvgRate.pm index 9ced889b..0c46fcef 100644 --- a/lib/WeightedAvgRate.pm +++ b/lib/WeightedAvgRate.pm @@ -1,4 +1,4 @@ -# This program is copyright 2011 Percona Inc. +# This program is copyright 2011 Percona Ireland Ltd. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED diff --git a/sandbox/jenkins-test b/sandbox/jenkins-test index fcd49277..086c2d4b 100755 --- a/sandbox/jenkins-test +++ b/sandbox/jenkins-test @@ -21,6 +21,9 @@ util/check-dev-env ######################### # Check the system env. # ######################### +env +df -h +w whoami id if [ "$(id -u)" = "0" ]; then diff --git a/t/lib/TimeSeriesTrender.t b/t/lib/TimeSeriesTrender.t deleted file mode 100644 index 605c9380..00000000 --- a/t/lib/TimeSeriesTrender.t +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/perl - -BEGIN { - die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" - unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; - unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; -}; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); -use Test::More tests => 1; - -use TimeSeriesTrender; -use PerconaTest; - -my $result; -my $tst = new TimeSeriesTrender( - callback => sub { $result = $_[0]; }, -); - -$tst->set_time('5'); -map { $tst->add_number($_) } - qw(1 2 1 2 12 23 2 2 3 3 21 3 3 1 1 2 3 1 2 12 2 - 3 1 3 2 22 2 2 2 2 3 1 1); -$tst->set_time('6'); - -is_deeply($result, - { - ts => 5, - stdev => 6.09038140334414, - avg => 4.42424242424242, - min => 1, - max => 23, - cnt => 33, - sum => 146, - }, - 'Simple stats test'); - -# ############################################################################# -# Done. -# ############################################################################# diff --git a/t/pt-archiver/file.t b/t/pt-archiver/file.t index 928e4d2d..121ca890 100644 --- a/t/pt-archiver/file.t +++ b/t/pt-archiver/file.t @@ -118,6 +118,7 @@ like( # ############################################################################# # Done. # ############################################################################# +diag(`rm -f /tmp/*.table_1`); $sb->wipe_clean($dbh); ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); diff --git a/t/pt-table-checksum/basics.t b/t/pt-table-checksum/basics.t index d0aa786b..4f2516a3 100644 --- a/t/pt-table-checksum/basics.t +++ b/t/pt-table-checksum/basics.t @@ -361,10 +361,11 @@ $output = output( stderr => 1, ); +# Before 2.2 the exit status was 0, but bug 1087804 changed this to 1. is( $exit_status, - 0, - "No host in DSN, zero exit status" + 1, + "No host in DSN, non-zero exit status" ); is( @@ -490,6 +491,55 @@ is( "Bug 821675 (dot): 0 errors" ); +# ############################################################################# +# Bug 1087804: pt-table-checksum doesn't warn if no slaves are found +# ############################################################################# +$sb->load_file('master', "$sample/dsn-table.sql"); +$master_dbh->do('TRUNCATE TABLE dsns.dsns'); +$sb->wait_for_slaves; + +my $slave1_dsn = $sb->dsn_for('slave1'); + +$output = output( + sub { $exit_status = pt_table_checksum::main(@args, + qw(-t sakila.country), + "--recursion-method", "dsn=$slave1_dsn,t=dsns.dsns") + }, + stderr => 1, +); + +like( + $output, + qr/no slaves were found/, + "Warns if no slaves are found" +); + +is( + $exit_status, + 1, + '...exit status 1' +); + +$output = output( + sub { $exit_status = pt_table_checksum::main(@args, + qw(-t sakila.country), + "--recursion-method", "none") + }, + stderr => 1, +); + +unlike( + $output, + qr/no slaves were found/, + "No warning if no slaves and --recursion-method=none" +); + +is( + $exit_status, + 0, + '...exit status 0' +); + # ############################################################################# # Done. # ############################################################################# diff --git a/util/build-packages b/util/build-packages index b219d668..33c35360 100755 --- a/util/build-packages +++ b/util/build-packages @@ -181,7 +181,7 @@ update_copyright_year() { echo -n "Updating copyright year in tools... " cd $BRANCH/bin for tool_file in *; do - local copyright="$(grep "[0-9] Percona Inc." $tool_file)" + local copyright="$(grep "[0-9] Percona Ireland Ltd" $tool_file)" local new_copyright="$(../util/new-copyright-year "$YEAR" "$copyright")" if [ $? -ne 0 ]; then die "Error parsing copyright year in $tool_file" @@ -196,7 +196,7 @@ update_copyright_year() { echo -n "Updating copyright year in percona-toolkit.pod... " local pod=$DOCS_DIR/percona-toolkit.pod - local copyright="$(grep "[0-9] Percona Inc." $pod)" + local copyright="$(grep "[0-9] Percona Ireland Ltd" $pod)" local new_copyright="$(../util/new-copyright-year "$YEAR" "$copyright")" if [ $? -ne 0 ]; then die "Error parsing copyright year in percona-toolkit.pod" diff --git a/util/update-modules b/util/update-modules index f17fa695..954729cb 100755 --- a/util/update-modules +++ b/util/update-modules @@ -190,13 +190,13 @@ if [ $pkgs_updated -gt 0 ]; then fi if [ $EXIT_STATUS -eq 0 ]; then cp $tmp_tool_file $tool_file + EXIT_STATUS=$((EXIT_STATUS | $?)) if [ $? -ne 0 ]; then warn "Failed to copy $tmp_tool_file to $tool_file" - else - rm $tmp_tool_file > /dev/null - EXIT_STATUS=$((EXIT_STATUS | $?)) fi fi fi +[ -f "$tmp_tool_file" ] && rm $tmp_tool_file + exit $EXIT_STATUS