diff --git a/bin/pt-query-digest b/bin/pt-query-digest index 836f5427..027842f8 100755 --- a/bin/pt-query-digest +++ b/bin/pt-query-digest @@ -3531,20 +3531,6 @@ $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Quotekeys = 0; -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - parse_error_packet - parse_ok_packet - parse_ok_prepared_statement_packet - parse_server_handshake_packet - parse_client_handshake_packet - parse_com_packet - parse_flags -); - use constant { COM_SLEEP => '00', COM_QUIT => '01', @@ -12377,7 +12363,7 @@ sub main { my $dp = $o->DSNParser(); $dp->prop('set-vars', $o->get('set-vars')); - + # Frequently used options. my $review_dsn = $o->get('review'); my @groupby = @{$o->get('group-by')}; diff --git a/lib/MySQLProtocolParser.pm b/lib/MySQLProtocolParser.pm index 8b896467..764d426f 100644 --- a/lib/MySQLProtocolParser.pm +++ b/lib/MySQLProtocolParser.pm @@ -51,19 +51,7 @@ $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Quotekeys = 0; -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - parse_error_packet - parse_ok_packet - parse_ok_prepared_statement_packet - parse_server_handshake_packet - parse_client_handshake_packet - parse_com_packet - parse_flags -); +use base 'ProtocolParser'; use constant { COM_SLEEP => '00', @@ -1526,46 +1514,6 @@ sub remove_mysql_header { return; } -sub _get_errors_fh { - my ( $self ) = @_; - my $errors_fh = $self->{errors_fh}; - return $errors_fh if $errors_fh; - - # Errors file isn't open yet; try to open it. - my $o = $self->{o}; - if ( $o && $o->has('tcpdump-errors') && $o->got('tcpdump-errors') ) { - my $errors_file = $o->get('tcpdump-errors'); - PTDEBUG && _d('tcpdump-errors file:', $errors_file); - open $errors_fh, '>>', $errors_file - or die "Cannot open tcpdump-errors file $errors_file: $OS_ERROR"; - } - - $self->{errors_fh} = $errors_fh; - return $errors_fh; -} - -sub fail_session { - my ( $self, $session, $reason ) = @_; - PTDEBUG && _d('Client', $session->{client}, 'failed because', $reason); - my $errors_fh = $self->_get_errors_fh(); - if ( $errors_fh ) { - my $raw_packets = $session->{raw_packets}; - delete $session->{raw_packets}; # Don't dump, it's printed below. - $session->{reason_for_failure} = $reason; - my $session_dump = '# ' . Dumper($session); - chomp $session_dump; - $session_dump =~ s/\n/\n# /g; - print $errors_fh "$session_dump\n"; - { - local $LIST_SEPARATOR = "\n"; - print $errors_fh "@$raw_packets"; - print $errors_fh "\n"; - } - } - delete $self->{sessions}->{$session->{client}}; - return; -} - # Delete anything we added to the session related to # buffering a large query received in multiple packets. sub _delete_buff { diff --git a/lib/PerconaTest.pm b/lib/PerconaTest.pm index 5da730b4..3285d16f 100644 --- a/lib/PerconaTest.pm +++ b/lib/PerconaTest.pm @@ -448,6 +448,7 @@ sub test_protocol_parser { keys %args; my $file = "$trunk/$args{file}"; + my ($base_file_name) = $args{file} =~ m/([^\/]+)$/; my @e; eval { open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR"; @@ -463,11 +464,10 @@ sub test_protocol_parser { close $fh; }; - my ($base_file_name) = $args{file} =~ m/([^\/]+)$/; is( $EVAL_ERROR, '', - "$base_file_name: no errors" + "$base_file_name: no perl errors" ); if ( defined $args{result} ) { diff --git a/lib/ProtocolParser.pm b/lib/ProtocolParser.pm index a733e673..15678f99 100644 --- a/lib/ProtocolParser.pm +++ b/lib/ProtocolParser.pm @@ -27,6 +27,9 @@ use warnings FATAL => 'all'; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG} || 0; +use File::Basename qw(basename); +use File::Temp qw(tempfile); + eval { require IO::Uncompress::Inflate; # yum: perl-IO-Compress-Zlib IO::Uncompress::Inflate->import(qw(inflate $InflateError)); @@ -245,39 +248,34 @@ sub make_event { sub _get_errors_fh { my ( $self ) = @_; - my $errors_fh = $self->{errors_fh}; - return $errors_fh if $errors_fh; + return $self->{errors_fh} if $self->{errors_fh}; + my $exec = basename($0); # Errors file isn't open yet; try to open it. - my $o = $self->{o}; - if ( $o && $o->has('tcpdump-errors') && $o->got('tcpdump-errors') ) { - my $errors_file = $o->get('tcpdump-errors'); - PTDEBUG && _d('tcpdump-errors file:', $errors_file); - open $errors_fh, '>>', $errors_file - or die "Cannot open tcpdump-errors file $errors_file: $OS_ERROR"; - } + my ($errors_fh, $filename) = tempfile("/tmp/$exec-errors.XXXXXXX", UNLINK => 0); - $self->{errors_fh} = $errors_fh; + $self->{errors_file} = $filename; + $self->{errors_fh} = $errors_fh; return $errors_fh; } sub fail_session { my ( $self, $session, $reason ) = @_; - my $errors_fh = $self->_get_errors_fh(); - if ( $errors_fh ) { - $session->{reason_for_failure} = $reason; - my $session_dump = '# ' . Dumper($session); - chomp $session_dump; - $session_dump =~ s/\n/\n# /g; - print $errors_fh "$session_dump\n"; - { - local $LIST_SEPARATOR = "\n"; - print $errors_fh "@{$session->{raw_packets}}"; - print $errors_fh "\n"; - } - } PTDEBUG && _d('Failed session', $session->{client}, 'because', $reason); delete $self->{sessions}->{$session->{client}}; + + return if $self->{_no_save_error}; + + my $errors_fh = $self->_get_errors_fh(); + + print "Session $session->{client} had errors, will save them in $self->{errors_file}\n"; + + my $raw_packets = delete $session->{raw_packets}; + $session->{reason_for_failure} = $reason; + my $session_dump = '# ' . Dumper($session); + chomp $session_dump; + $session_dump =~ s/\n/\n# /g; + print $errors_fh join("\n", $session_dump, @$raw_packets), "\n"; return; } diff --git a/t/lib/MySQLProtocolParser.t b/t/lib/MySQLProtocolParser.t index e63d2647..ebddcec5 100644 --- a/t/lib/MySQLProtocolParser.t +++ b/t/lib/MySQLProtocolParser.t @@ -285,16 +285,9 @@ test_protocol_parser( # ############################################################################# # Check the individual packet parsing subs. # ############################################################################# -MySQLProtocolParser->import(qw( - parse_error_packet - parse_ok_packet - parse_server_handshake_packet - parse_client_handshake_packet - parse_com_packet -)); is_deeply( - parse_error_packet(load_data("t/lib/samples/mysql_proto_001.txt")), + MySQLProtocolParser::parse_error_packet(load_data("t/lib/samples/mysql_proto_001.txt")), { errno => '1046', sqlstate => '#3D000', @@ -304,7 +297,7 @@ is_deeply( ); is_deeply( - parse_ok_packet('010002000100'), + MySQLProtocolParser::parse_ok_packet('010002000100'), { affected_rows => 1, insert_id => 0, @@ -316,7 +309,7 @@ is_deeply( ); is_deeply( - parse_server_handshake_packet(load_data("t/lib/samples/mysql_proto_002.txt")), + MySQLProtocolParser::parse_server_handshake_packet(load_data("t/lib/samples/mysql_proto_002.txt")), { thread_id => '9', server_version => '5.0.67-0ubuntu6-log', @@ -345,7 +338,7 @@ is_deeply( ); is_deeply( - parse_client_handshake_packet(load_data("t/lib/samples/mysql_proto_003.txt")), + MySQLProtocolParser::parse_client_handshake_packet(load_data("t/lib/samples/mysql_proto_003.txt")), { db => 'mysql', user => 'msandbox', @@ -374,7 +367,7 @@ is_deeply( ); is_deeply( - parse_com_packet('0373686f77207761726e696e67738d2dacbc', 14), + MySQLProtocolParser::parse_com_packet('0373686f77207761726e696e67738d2dacbc', 14), { code => '03', com => 'COM_QUERY', @@ -1274,6 +1267,7 @@ test_protocol_parser( # Issue 761: mk-query-digest --tcpdump does not handle incomplete packets # ############################################################################# $protocol = new MySQLProtocolParser(server=>'127.0.0.1',port=>'3306'); +$protocol->{_no_save_error} = 1; test_protocol_parser( parser => $tcpdump, protocol => $protocol, @@ -1449,6 +1443,7 @@ test_protocol_parser( ); $protocol = new MySQLProtocolParser(); +$protocol->{_no_save_error} = 1; test_protocol_parser( parser => $tcpdump, protocol => $protocol, @@ -1528,6 +1523,7 @@ test_protocol_parser( ); $protocol = new MySQLProtocolParser(); +$protocol->{_no_save_error} = 1; test_protocol_parser( parser => $tcpdump, protocol => $protocol, @@ -1576,6 +1572,7 @@ test_protocol_parser( # client query # ############################################################################# $protocol = new MySQLProtocolParser(server => '127.0.0.1',port=>'12345'); +$protocol->{_no_save_error} = 1; $e = test_protocol_parser( parser => $tcpdump, protocol => $protocol, @@ -1595,6 +1592,7 @@ $protocol = new MySQLProtocolParser( server => '127.0.0.1', port => '3306', ); +$protocol->{_no_save_error} = 1; test_protocol_parser( parser => $tcpdump, protocol => $protocol, @@ -1745,6 +1743,37 @@ test_protocol_parser( ], ); +# ############################################################################# +# Save errors by default +# ############################################################################# +$protocol = new MySQLProtocolParser(server=>'127.0.0.1',port=>'3306'); + +my $out = output(sub { + open my $fh, "<", "$sample/tcpdump032.txt" or die "Cannot open tcpdump032.txt: $OS_ERROR"; + my %parser_args = ( + next_event => sub { return <$fh>; }, + tell => sub { return tell($fh); }, + ); + while ( my $p = $tcpdump->parse_event(%parser_args) ) { + $protocol->parse_event(%parser_args, event => $p); + } + close $fh; +}); + +like( + $out, + qr/had errors, will save them in /, + "Saves errors by default" +); + +close $protocol->{errors_fh}; # flush the handle + +like( + slurp_file($protocol->{errors_file}), + qr/got server response before full buffer/, + "The right error is saved" +); + # ############################################################################# # Done. # #############################################################################