File Coverage

blib/lib/DBIx/AssertIndex.pm
Criterion Covered Total %
statement 30 68 44.1
branch 0 16 0.0
condition 0 6 0.0
subroutine 9 16 56.2
pod n/a
total 39 106 36.7


line stmt bran cond sub pod time code
1             package DBIx::AssertIndex;
2              
3 1     1   5 use strict;
  1         1  
  1         31  
4 1     1   4 use warnings;
  1         2  
  1         23  
5 1     1   21 use 5.008_009;
  1         3  
  1         43  
6              
7             our $VERSION = '0.02';
8              
9 1     1   2244 use DBI;
  1         23399  
  1         95  
10              
11             our $OUTPUT = *STDERR;
12              
13             sub import {
14 1     1   2 my ($class) = @_;
15              
16 1     1   12 no warnings qw(redefine prototype);
  1         2  
  1         96  
17 1         4 my $original_st_execute = \&DBI::st::execute;
18 1         5 *DBI::st::execute = __explain_and_st_execute($original_st_execute);
19              
20 1         3 foreach my $db_method (qw/do selectall_arrayref selectrow_array selectrow_arrayref /){
21 1     1   4 no strict qw(refs);
  1         2  
  1         699  
22 4         5 my $original = \&{"DBI::db::$db_method"};
  4         16  
23 4         10 *{"DBI::db::$db_method"} = __explain_and_db_XXX($original, $original_st_execute);
  4         55  
24             }
25             };
26              
27             sub __explain_and_db_XXX {
28 4     4   6 my($original_db_XXX, $original_st_execute) = @_;
29              
30             return sub {
31 0     0   0 my ($dbh, $statement, @rest ) = @_;
32              
33 0         0 __expain($original_st_execute, $dbh, $statement, @rest);
34 0         0 return $original_db_XXX->(@_);
35 4         15 };
36             }
37              
38             sub __explain_and_st_execute {
39 1     1   2 my $original_st_execute = shift;
40             return sub {
41 0     0     my ($sth, @rest ) = @_;
42              
43 0           my $dbh = $sth->{Database};
44 0           my $statement = $sth->{Statement};
45 0           __expain($original_st_execute, $dbh, $statement, @rest);
46 0           return $original_st_execute->(@_);
47 1         8 };
48             }
49              
50             sub __expain {
51 0     0     my($original_st_execute, $dbh, $statement, @rest) = @_;
52              
53 0 0         return unless($dbh->{Driver}{Name} eq 'mysql');
54 0 0         return unless $statement =~ m/^\s*SELECT/i;
55 0 0         return unless $statement =~ m/FROM/mi;
56              
57 0           my $explain_sth = $dbh->prepare( 'explain ' . $statement );
58 0           $original_st_execute->($explain_sth, @rest);
59 0           __assert_explain($explain_sth->fetchall_arrayref( +{} ), $statement);
60             }
61              
62             sub __assert_explain {
63 0     0     my($explains, $statement) = @_;
64 0           my $clean_statement = __clean_statement($statement);
65 0           my @using_no_key = grep {__should_alert($_) } @$explains;
  0            
66 0 0         return unless @using_no_key;
67              
68 0           __warn->('[explain alert] ', $clean_statement);
69             }
70              
71             sub __should_alert {
72 0     0     my ( $explain_by_table ) = @_;
73 0           my $extra = $explain_by_table->{Extra};
74 0           my $type = $explain_by_table->{type};
75 0           my $possible_key = $explain_by_table->{possible_keys};
76             # search uniq/primary key but not found any rows
77 0 0 0       return 0 if $extra and $extra =~ m/^Impossible/;
78             # not using any index
79 0 0         return 0 if defined $possible_key;
80 0 0 0       return 1 if $type and $type eq 'ALL';
81 0           return 0;
82             }
83              
84             sub __clean_statement {
85 0     0     my $statement = shift;
86 0           $statement =~ s/\n/ /g;
87 0           $statement =~ s/\s+/ /g;
88 0           return $statement;
89             }
90              
91             sub __warn {
92 0     0     my($message, $statement, $using_no_key) = @_;
93              
94 0 0         if (ref $OUTPUT eq 'CODE') {
95 0           $OUTPUT->(
96             message => $message,
97             statement => $statement,
98             using_no_key => $using_no_key,
99             );
100             } else {
101 0           print {$OUTPUT} $message, ' statement:', $statement;
  0            
102             }
103             }
104              
105             1;
106             __END__