File Coverage

blib/lib/DBIx/Tracer.pm
Criterion Covered Total %
statement 117 121 96.6
branch 25 42 59.5
condition 6 8 75.0
subroutine 20 20 100.0
pod 1 1 100.0
total 169 192 88.0


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