File Coverage

blib/lib/DBIx/ProfileManager.pm
Criterion Covered Total %
statement 112 118 94.9
branch 18 30 60.0
condition 3 7 42.8
subroutine 18 20 90.0
pod 5 5 100.0
total 156 180 86.6


line stmt bran cond sub pod time code
1             package DBIx::ProfileManager;
2              
3 2     2   592 use strict;
  2         3  
  2         82  
4 2     2   10 use warnings;
  2         4  
  2         93  
5              
6             our $VERSION = '0.03';
7              
8 2     2   7804 use DBI;
  2         30146  
  2         108  
9 2     2   1134 use DBI::Profile;
  2         3076  
  2         172  
10 2     2   12 use Scalar::Util qw(weaken);
  2         4  
  2         308  
11              
12             our %ORIGINAL_METHODS;
13              
14             sub new {
15 2     2 1 25 my ( $class, %args ) = @_;
16 2   50     21 bless +{
17             config => $args{config} || '!Statement',
18             data => +{},
19             path => [],
20             is_started => 0,
21             } => $class;
22             }
23              
24             {
25 2     2   9 no strict 'refs';
  2         3  
  2         482  
26             for my $attr ( qw/config data path is_started/ ) {
27             *{$attr} = sub {
28 56 100   56   131 if ( @_ == 2 ) {
29 8         32 $_[0]->{$attr} = $_[1];
30             }
31             else {
32 48         183 return $_[0]->{$attr};
33             }
34             };
35             }
36             }
37              
38             sub profile_start {
39 2     2 1 13 my ( $self, @db_handles ) = @_;
40              
41 2         9 my $config = $self->config;
42              
43 2 50       7 unless ( @db_handles > 0 ) {
44 2         9 @db_handles = $self->_active_db_handles;
45 2         14 $ENV{DBI_PROFILE} = $config;
46             }
47            
48 2 50       8 if ( @db_handles > 0 ) {
49 2         3 for my $dbh (@db_handles) {
50 3         280 $dbh->{Profile} = $config;
51             }
52 2 50       86 if ( $db_handles[0] ) {
53 2         21 $self->path($db_handles[0]->{Profile}{Path});
54             }
55             }
56              
57 2         9 $self->data(+{});
58 2 50       4 $self->path( [ split(':', $config) ] ) if ( @{$self->path} == 0 );
  2         6  
59              
60             {
61 2     2   15 no strict 'refs';
  2         4  
  2         71  
  2         4  
62 2     2   10 no warnings 'redefine';
  2         3  
  2         529  
63              
64 2         3 my $pfm = $self;
65 2         11 weaken( $pfm );
66              
67             my $cb = sub {
68 0     0   0 my $dbh = shift;
69 0         0 $pfm->_fetch_profile_data($dbh);
70 2         17 };
71              
72 2 50       10 unless ( exists $DBI::db::{DESTROY} ) {
73 0         0 *DBI::db::DESTROY = $cb;
74             }
75              
76 2         8 $ORIGINAL_METHODS{disconnect} = \&DBI::db::disconnect;
77             *DBI::db::disconnect = sub {
78 0     0   0 my $dbh = shift;
79 0         0 $cb->($dbh);
80 0         0 $ORIGINAL_METHODS{disconnect}->($dbh);
81 2         15 };
82             };
83            
84 2         7 $self->is_started(1);
85             }
86              
87             sub profile_stop {
88 2     2 1 24 my $self = shift;
89 2 50       10 return unless ($self->is_started);
90 2         11 my @db_handles = $self->_active_db_handles;
91              
92 2         17 delete $ENV{DBI_PROFILE};
93 2         10 delete $DBI::db::{DESTROY};
94            
95 2         6 for my $dbh (@db_handles) {
96 3         10 $self->_fetch_profile_data( $dbh );
97             }
98              
99             {
100 2     2   10 no warnings 'redefine';
  2         4  
  2         1680  
  2         7  
101 2         28 *DBI::db::disconnect = $ORIGINAL_METHODS{disconnect};
102             };
103            
104 2         8 $self->is_started(0);
105             }
106              
107             sub data_formatted {
108 2     2 1 54 my ($self, $format, @results) = @_;
109 2   50     9 $format ||= '%{statement} : %{total}s / %{count} = %{avg}s avg (first %{first}s, min %{min}s, max %{max}s)';
110 2 100       12 @results = $self->data_structured unless ( @results > 0 );
111 2         4 my @formatted;
112              
113 2         5 for my $result ( @results ) {
114 7         12 my $log = $format;
115 7 50       46 $log =~ s/%\{?([\w_]+)\}?/(exists $result->{$1})?$result->{$1}:sprintf('%%{%s}',$1)/gex;
  7         45  
116 7         21 push(@formatted, $log);
117             }
118              
119 2 50       21 return wantarray ? @formatted : join("\n", @formatted);
120             }
121              
122             sub data_structured {
123 2     2 1 9 my $self = shift;
124 2         29 my $data = $self->data;
125 2         5 my @results;
126 2         9 for my $dsn ( keys %$data ) {
127              
128 3         4 my $depth = 0;
129 3         18 my $profile_data = $self->_data_structured_recursive(
130             +{ dsn => $dsn }, $data->{$dsn}, \@results, $depth,
131             );
132             }
133              
134 2 50       16 return wantarray ? @results : \@results;
135             }
136              
137             sub _fetch_profile_data {
138 3     3   7 my ( $self, $dbh ) = @_;
139              
140 3 50 33     40 return unless ( exists $dbh->{Profile} && defined $dbh->{Profile}{Data} );
141 3         115 my $dsn = sprintf( 'dbi:%s:%s', $dbh->{Driver}{Name}, $dbh->{Name} );
142 3 50       14 return if ( exists $self->data->{$dsn} );
143            
144 7         67 $self->data->{$dsn}
145             = +{
146 10         16 map { $_ => $dbh->{Profile}{Data}{$_} }
147 3         7 grep { length $_ } keys %{ $dbh->{Profile}{Data} }
  3         21  
148             };
149 3         28 $dbh->{Profile}{Data} = undef;
150             }
151              
152             sub _active_db_handles {
153 4     4   29 my %drhs = DBI->installed_drivers;
154 4         33 my @handles;
155 4         11 for my $drh ( values %drhs ) {
156 4         6 for my $dbh ( grep { $_->{Active} } @{ $drh->{ChildHandles} } ) {
  8         87  
  4         44  
157 6         95 push( @handles, $dbh );
158             }
159             }
160 4 50       22 wantarray ? @handles : \@handles;
161             }
162              
163             sub _data_structured_recursive {
164 22     22   37 my ($self, $default, $data, $results, $depth) = @_;
165              
166 22 100       27 if ( @{$self->path} == $depth ) {
  22         42  
167 14         51 my %profile_data = %$default;
168 14         97 @profile_data{qw/count total first min max start end/} = @$data;
169 14         45 $profile_data{avg} = $profile_data{total} / $profile_data{count};
170            
171 14         27 push( @$results, \%profile_data );
172 14         60 return;
173             }
174              
175 8         34 my $sp_const = lcfirst(substr($self->path->[$depth], 1));
176 8         32 $sp_const =~ s/([A-Z])/'_'.lc($1)/gex;
  5         23  
177 8         13 $sp_const =~ s/\~/_/g;
178            
179 8         25 for my $key ( keys %$data ) {
180 19         34 $default->{$sp_const} = $key;
181 19         55 $self->_data_structured_recursive( $default, $data->{$key}, $results, $depth + 1 );
182             }
183             }
184              
185             1;
186             __END__