File Coverage

blib/lib/DBIx/Tracer.pm
Criterion Covered Total %
statement 27 122 22.1
branch 0 48 0.0
condition 0 8 0.0
subroutine 9 20 45.0
pod 1 1 100.0
total 37 199 18.5


line stmt bran cond sub pod time code
1             package DBIx::Tracer;
2 1     1   43470 use strict;
  1         4  
  1         62  
3 1     1   7 use warnings;
  1         3  
  1         42  
4 1     1   52 use 5.008008;
  1         14  
  1         126  
5             our $VERSION = '0.02';
6              
7 1     1   12675 use DBI;
  1         36586  
  1         93  
8 1     1   13728 use Time::HiRes qw(gettimeofday tv_interval);
  1         3028  
  1         6  
9 1     1   210 use Carp;
  1         2  
  1         562  
10              
11             my $org_execute = \&DBI::st::execute;
12             my $org_bind_param = \&DBI::st::bind_param;
13             my $org_db_do = \&DBI::db::do;
14             my $org_db_selectall_arrayref = \&DBI::db::selectall_arrayref;
15             my $org_db_selectrow_arrayref = \&DBI::db::selectrow_arrayref;
16             my $org_db_selectrow_array = \&DBI::db::selectrow_array;
17              
18             my $has_mysql = eval { require DBD::mysql; 1 } ? 1 : 0;
19             my $pp_mode = $INC{'DBI/PurePerl.pm'} ? 1 : 0;
20              
21             my $st_execute;
22             my $st_bind_param;
23             my $db_do;
24             my $selectall_arrayref;
25             my $selectrow_arrayref;
26             my $selectrow_array;
27              
28             our $OUTPUT;
29              
30             sub new {
31 0     0 1   my $class = shift;
32              
33             # argument processing
34 0           my %args;
35 0 0         if (@_==1) {
36 0 0         if (ref $_[0] eq 'CODE') {
37 0           $args{code} = $_[0];
38             } else {
39 0           %args = %{$_[0]};
  0            
40             }
41             } else {
42 0           %args = @_;
43             }
44 0           for (qw(code)) {
45 0 0         unless ($args{$_}) {
46 0           croak "Missing mandatory parameter $_ for DBIx::Tracer->new";
47             }
48             }
49              
50 0           my $logger = $args{code};
51              
52             # create object
53 0           my $self = bless \%args, $class;
54              
55             # wrap methods
56 0           my $st_execute = $class->_st_execute($org_execute, $logger);
57 0           $st_bind_param = $class->_st_bind_param($org_bind_param, $logger);
58 0 0         $db_do = $class->_db_do($org_db_do, $logger) if $has_mysql;
59 0 0         unless ($pp_mode) {
60 0           $selectall_arrayref = $class->_select_array($org_db_selectall_arrayref, 0, $logger);
61 0           $selectrow_arrayref = $class->_select_array($org_db_selectrow_arrayref, 0, $logger);
62 0           $selectrow_array = $class->_select_array($org_db_selectrow_array, 1, $logger);
63             }
64              
65 1     1   8 no warnings qw(redefine prototype);
  1         2  
  1         220  
66 0           *DBI::st::execute = $st_execute;
67 0           *DBI::st::bind_param = $st_bind_param;
68 0 0         *DBI::db::do = $db_do if $has_mysql;
69 0 0         unless ($pp_mode) {
70 0           *DBI::db::selectall_arrayref = $selectall_arrayref;
71 0           *DBI::db::selectrow_arrayref = $selectrow_arrayref;
72 0           *DBI::db::selectrow_array = $selectrow_array;
73             }
74              
75 0           return $self;
76             }
77              
78             sub DESTROY {
79 0     0     my $self = shift;
80              
81 1     1   6 no warnings qw(redefine prototype);
  1         3  
  1         8033  
82 0           *DBI::st::execute = $org_execute;
83 0           *DBI::st::bind_param = $org_bind_param;
84 0 0         *DBI::db::do = $org_db_do if $has_mysql;
85 0 0         unless ($pp_mode) {
86 0           *DBI::db::selectall_arrayref = $org_db_selectall_arrayref;
87 0           *DBI::db::selectrow_arrayref = $org_db_selectrow_arrayref;
88 0           *DBI::db::selectrow_array = $org_db_selectrow_array;
89             }
90             }
91              
92             # -------------------------------------------------------------------------
93             # wrapper methods.
94              
95             sub _st_execute {
96 0     0     my ($class, $org, $logger) = @_;
97              
98             return sub {
99 0     0     my $sth = shift;
100 0           my @params = @_;
101 0           my @types;
102              
103 0           my $dbh = $sth->{Database};
104 0           my $ret = $sth->{Statement};
105 0 0         if (my $attrs = $sth->{private_DBIx_Tracer_attrs}) {
106 0           my $bind_params = $sth->{private_DBIx_Tracer_params};
107 0           for my $i (1..@$attrs) {
108 0           push @types, $attrs->[$i - 1]{TYPE};
109 0 0         push @params, $bind_params->[$i - 1] if $bind_params;
110             }
111             }
112 0           $sth->{private_DBIx_Tracer_params} = undef;
113              
114 0           my $begin = [gettimeofday];
115 0 0         my $wantarray = wantarray ? 1 : 0;
116 0 0         my $res = $wantarray ? [$org->($sth, @_)] : scalar $org->($sth, @_);
117 0           my $time = tv_interval($begin, [gettimeofday]);
118              
119 0           $class->_logging($logger, $dbh, $ret, $time, \@params);
120              
121 0 0         return $wantarray ? @$res : $res;
122 0           };
123             }
124              
125             sub _st_bind_param {
126 0     0     my ($class, $org) = @_;
127              
128             return sub {
129 0     0     my ($sth, $p_num, $value, $attr) = @_;
130 0   0       $sth->{private_DBIx_Tracer_params} ||= [];
131 0   0       $sth->{private_DBIx_Tracer_attrs } ||= [];
132 0 0 0       $attr = +{ TYPE => $attr || 0 } unless ref $attr eq 'HASH';
133 0           $sth->{private_DBIx_Tracer_params}[$p_num - 1] = $value;
134 0           $sth->{private_DBIx_Tracer_attrs }[$p_num - 1] = $attr;
135 0           $org->(@_);
136 0           };
137             }
138              
139             sub _select_array {
140 0     0     my ($class, $org, $is_selectrow_array, $logger) = @_;
141              
142             return sub {
143 0     0     my $wantarray = wantarray;
144 0           my ($dbh, $stmt, $attr, @bind) = @_;
145              
146 1     1   11 no warnings qw(redefine prototype);
  1         3  
  1         1253  
147 0           local *DBI::st::execute = $org_execute; # suppress duplicate logging
148              
149 0 0         my $ret = ref $stmt ? $stmt->{Statement} : $stmt;
150              
151 0           my $begin = [gettimeofday];
152 0           my $res;
153 0 0         if ($is_selectrow_array) {
154 0 0         $res = $wantarray ? [$org->($dbh, $stmt, $attr, @bind)] : $org->($dbh, $stmt, $attr, @bind);
155             }
156             else {
157 0           $res = $org->($dbh, $stmt, $attr, @bind);
158             }
159 0           my $time = tv_interval($begin, [gettimeofday]);
160              
161 0           $class->_logging($logger, $dbh, $ret, $time, \@bind);
162              
163 0 0         if ($is_selectrow_array) {
164 0 0         return $wantarray ? @$res : $res;
165             }
166 0           return $res;
167 0           };
168             }
169              
170             sub _db_do {
171 0     0     my ($class, $org, $logger) = @_;
172              
173             return sub {
174 0 0   0     my $wantarray = wantarray ? 1 : 0;
175 0           my ($dbh, $stmt, $attr, @bind) = @_;
176              
177 0 0         if ($dbh->{Driver}{Name} ne 'mysql') {
178 0           return $org->($dbh, $stmt, $attr, @bind);
179             }
180              
181 0           my $ret = $stmt;
182              
183 0           my $begin = [gettimeofday];
184 0 0         my $res = $wantarray ? [$org->($dbh, $stmt, $attr, @bind)] : scalar $org->($dbh, $stmt, $attr, @bind);
185 0           my $time = tv_interval($begin, [gettimeofday]);
186              
187 0           $class->_logging($logger, $dbh, $ret, $time, \@bind);
188              
189 0 0         return $wantarray ? @$res : $res;
190 0           };
191             }
192              
193             sub _logging {
194 0     0     my ($class, $logger, $dbh, $sql, $time, $bind_params) = @_;
195 0   0       $bind_params ||= [];
196              
197 0           $logger->(
198             dbh => $dbh,
199             time => $time,
200             sql => $sql,
201             bind_params => $bind_params,
202             );
203             }
204              
205             1;
206             __END__