File Coverage

blib/lib/MySQL/SlowLog/Filter.pm
Criterion Covered Total %
statement 45 121 37.1
branch 11 62 17.7
condition 3 33 9.0
subroutine 9 12 75.0
pod 4 5 80.0
total 72 233 30.9


line stmt bran cond sub pod time code
1             package MySQL::SlowLog::Filter;
2            
3 2     2   61827 use warnings;
  2         6  
  2         66  
4 2     2   12 use strict;
  2         5  
  2         122  
5            
6             our $VERSION = '0.05';
7             our $AUTHORITY = 'cpan:FAYLAND';
8            
9 2     2   11 use base 'Exporter';
  2         9  
  2         178  
10 2     2   10 use vars qw/@EXPORT_OK/;
  2         5  
  2         122  
11             @EXPORT_OK = qw/ run parse_date_range parse_time /;
12            
13 2     2   15 use Carp qw/croak/;
  2         3  
  2         92  
14 2     2   2313 use Time::Local;
  2         2241  
  2         184  
15             #use DBI;
16 2     2   2250 use File::Slurp;
  2         45208  
  2         3459  
17             #use Data::Dumper;
18            
19             our @default_sorting = (
20             4, 'sum-query-time',
21             2, 'avg-query-time',
22             3, 'max-query-time',
23             7, 'sum-lock-time',
24             5, 'avg-lock-time',
25             6, 'max-lock-time',
26             13, 'sum-rows-examined',
27             11, 'avg-rows-examined',
28             12, 'max-rows-examined',
29             1, 'execution-count',
30             10, 'sum-rows-sent',
31             8, 'avg-rows-sent',
32             9, 'max-rows-sent'
33             );
34            
35             sub run {
36 0     0 1 0 my $file = shift;
37 0 0       0 my ( $params ) = ( scalar @_ == 1 ) ? shift : { @_ };
38            
39             # check date range
40 0         0 my $date = $params->{date};
41 0         0 my ( $date_first, $date_last ) = parse_date_range( $date );
42            
43             # check settings
44 0   0     0 my $include_hosts = $params->{'include-host'} || $params->{ih} || [];
45 0   0     0 my $exclude_hosts = $params->{'exclude-host'} || $params->{eh} || [];
46 0   0     0 my $include_users = $params->{'include-user'} || $params->{iu} || [];
47 0   0     0 my $exclude_users = $params->{'exclude-user'} || $params->{eu} || [];
48            
49 0   0     0 my $no_duplicates = $params->{no_duplicates} || 0;
50 0   0     0 my $no_output = $params->{no_output} || 0;
51 0   0     0 my $incremental = $params->{incremental} || 0;
52            
53 0   0     0 my $min_query_time = $params->{min_query_time} || $params->{T} || -1;
54 0   0     0 my $min_rows_examined = $params->{min_rows_examined} || $params->{R} || -1;
55            
56 0         0 my @lines = read_file( $file );
57            
58 0 0       0 if ( $incremental ) {
59             # TODO
60             }
61            
62 0         0 my ( $query, $timestamp, $user, $host, $in_query );
63 0         0 my @query_time;
64 0         0 my %queries;
65 0         0 foreach my $line ( @lines ) {
66 0 0       0 next unless $line;
67            
68 0 0       0 if ( $line =~ /^\# / ) {
    0          
69 0 0       0 if ( $query ) {
70 0 0       0 if ( $in_query ) {
71 0         0 process_query( \%queries, $query, $no_duplicates, $user, $host,
72             $timestamp, \@query_time);
73             }
74 0         0 $query = '';
75 0         0 $in_query = 0;
76             }
77            
78 0 0 0     0 if ( $line =~ /^\# T/ ) { # # Time: 070119 12:29:58
    0 0        
    0          
79 0         0 ( $timestamp ) = ( $line =~ /(\d+(.*?))$/ );
80 0         0 my $t = get_log_timestamp($timestamp);
81 0 0 0     0 if ( $t < $date_first or $t > $date_last ) {
82 0         0 $timestamp = 0;
83             }
84             } elsif ( $timestamp and $line =~ /^\# U/ ) { # # User@Host: root[root] @ localhost []
85 0         0 chomp($line);
86 0         0 my $text = substr( $line, 13, length($line) - 13 );
87 0         0 ( $user, $host ) = split(' @ ', $text, 2);
88            
89 0 0       0 if (not scalar @$include_hosts) {
90 0         0 $in_query = 1;
91 0         0 foreach my $eh ( @$exclude_hosts ) {
92 0 0       0 if ( $host =~ /$eh/ ) {
93 0         0 $in_query = 0;
94 0         0 last;
95             }
96             }
97             } else {
98 0         0 $in_query = 0;
99 0         0 foreach my $ih ( @$include_hosts ) {
100 0 0       0 if ( $host =~ /$ih/ ) {
101 0         0 $in_query = 1;
102 0         0 last;
103             }
104             }
105             }
106 0 0       0 next if ( not $in_query );
107            
108 0 0       0 if (not scalar @$include_users) {
109 0         0 $in_query = 1;
110 0         0 foreach my $eu ( @$exclude_users ) {
111 0 0       0 if ( $user =~ /$eu/ ) {
112 0         0 $in_query = 0;
113 0         0 last;
114             }
115             }
116             } else {
117 0         0 $in_query = 0;
118 0         0 foreach my $iu ( @$include_users ) {
119 0 0       0 if ( $user =~ /$iu/ ) {
120 0         0 $in_query = 1;
121 0         0 last;
122             }
123             }
124             }
125             }
126             # # Query_time: 0 Lock_time: 0 Rows_sent: 0 Rows_examined: 156
127             elsif ( $in_query and $line =~ /^\# Q/ ) {
128 0         0 my $text = substr( $line, 13, length($line) - 13 );
129 0         0 my @numbers = split(':', $text);
130 0         0 @query_time = ();
131 0         0 foreach (@numbers) {
132 0 0       0 push @query_time, $1 if (/(\d+)/);
133             }
134 0 0 0     0 $in_query = ( $query_time[0] >= $min_query_time or ($min_rows_examined and $query_time[3] >= $min_rows_examined) ) ? 1 : 0;
135             }
136             } elsif ( $in_query ) {
137 0         0 $query .= $line;
138             }
139             }
140            
141 0 0       0 if ( $query ) {
142 0         0 process_query(\%queries, $query, $no_duplicates, $user, $host,
143             $timestamp, \@query_time);
144             }
145            
146             }
147            
148             sub process_query {
149 0     0 0 0 my ( $queries, $query, $no_duplicates, $user, $host, $timestamp, $query_time ) = @_;
150            
151 0         0 my $user_host = $user . ' @ ' . $host;
152 0 0       0 if ( $no_duplicates ) {
153             # TODO
154             } else {
155 0 0       0 my $ls = ( $^O eq 'MSWin32' ) ? "\r\n" :
    0          
156             ( $^O eq 'darwin' ) ? "\r" : "\n";
157 0         0 print sprintf("# Time: %s%s# User\@Host: %s%s# Query_time: %d Lock_time: %d Rows_sent: %d Rows_examined: %d%s%s", $timestamp, $ls, $user_host, $ls, $query_time->[0], $query_time->[1], $query_time->[2], $query_time->[3], $ls, $query);
158             }
159             }
160            
161             sub parse_date_range {
162 9     9 1 9635 my $date = shift;
163            
164 9         20 my ( $start, $end ) = ( 0, 9999999999 );
165 9 100       30 return ( $start, $end ) unless ( $date );
166            
167 8         61 my @parts = ( $date =~ /
168             ( # first date (don't match beginning of string)
169             (?:\d{4}|\d{1,2}) # first part can be 1-2 or 4 digits long (DD, MM, YYYY)
170             (?:[\.\-\/]?\d{1,2}[\.\-\/]?)? # middle part (1-2 digits), optionally separated
171             (?:\d{4}|\d{1,2})? # end part (1-2, 4 digits), optionally separated
172             ) # end of first date
173             (?:-( # optional second date, separated by "-"
174             (?:\d{4}|\d{1,2}) # first part can be 1-2 or 4 digits long (DD, MM, YYYY)
175             (?:[\.\-\/]?\d{1,2})? # middle part (1-2 digits), optionally separated
176             (?:[\.\-\/]?(?:\d{4}|\d{1,2}))? # end part (1-2, 4 digits), optionally separated
177             ))? # end of optional second date
178             /x );
179            
180 8         17 @parts = grep { defined $_ } @parts;
  16         47  
181 8 50       27 return ( $start, $end ) unless ( scalar @parts );
182            
183             # for >13.11.2006 <13.11.2006 -13.11.2006
184 8 100       38 if ( $date =~ /^([\>\<\-])/ ) {
    100          
185 3 100 100     24 if ( $1 eq '<' or $1 eq '-' ) {
186 2         7 $end = parse_time( $parts[0] );
187             } else {
188 1         4 $start = parse_time( $parts[0] );
189             }
190             } elsif ( scalar @parts > 1 ) {
191 3         9 $start = parse_time( $parts[0] );
192             # for '13/11/2006-'
193 3 50       15 $end = parse_time( $parts[1] ) if ( $parts[1] ne '-' );
194             } else {
195 2         6 $start = parse_time( $parts[0] );
196             }
197            
198 8         30 return ( $start, $end );
199             }
200            
201             sub parse_time {
202             # Return a unix timestamp from the given date.
203 14     14 1 1728 my $date = shift;
204            
205             # for those '13.11.2006' '11/13/2006' '15-11-2006'
206 14         210 my @parts = ( $date =~ /(\d+)/g );
207 14         31 $parts[2] -= 1900;
208 14         19 $parts[1] -= 1;
209            
210 14         16 my $r;
211 14         23 eval {
212 14         45 $r = timelocal(0, 0, 0, @parts);
213             };
214 14 50       881 croak "$date is not accepted\n" if ($@);
215 14         44 return $r;
216             }
217            
218             sub get_log_timestamp {
219 0     0 1   my $date = shift;
220            
221             # 070119 12:29:58
222 0           my ( $year, $month, $day, $hour, $min, $secs ) = (
223             $date =~ /(\d\d)(\d\d)(\d\d)\s+(\d{1,2})\:(\d{1,2})\:(\d{1,2})/ );
224            
225 0           $year += 100;
226 0           $month -= 1;
227 0           return timelocal($secs, $min, $hour, $day, $month, $year);
228             }
229            
230             1;
231             __END__