File Coverage

blib/lib/DBIx/Connector.pm
Criterion Covered Total %
statement 181 181 100.0
branch 90 94 95.7
condition 28 39 71.7
subroutine 29 29 100.0
pod 14 14 100.0
total 342 357 95.8


line stmt bran cond sub pod time code
1 13     13   1889570 use 5.008001; use strict; use warnings;
  13     13   54  
  13     13   84  
  13         81  
  13         449  
  13         84  
  13         23  
  13         1029  
2              
3             package DBIx::Connector;
4              
5 13     13   24662 use DBI '1.605';
  13         341572  
  13         3764  
6 13     13   10035 use DBIx::Connector::Driver;
  13         45  
  13         36998  
7              
8             our $VERSION = '0.60';
9              
10             sub new {
11 30     30 1 2186156 my $class = shift;
12 30         158 my @args = @_;
13             bless {
14 63     63   228 _args => sub { @args },
15 30         444 _svp_depth => 0,
16             _mode => 'no_ping',
17             _dond => 1,
18             } => $class;
19             }
20              
21 26 100   26   15586 sub DESTROY { $_[0]->disconnect if $_[0]->{_dond} }
22              
23             sub _connect {
24 61     61   3652 my $self = shift;
25 61         210 my @args = $self->{_args}->();
26 61 50       157 my $dbh = do {
27 61 100 66     340 if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
28 1         4 local $DBI::connect_via = 'connect'; # Disable Apache::DBI.
29 1         6 DBI->connect( @args );
30             } else {
31 60         397 DBI->connect( @args );
32             }
33             } or return undef;
34              
35             # Modify default values.
36             $dbh->STORE(AutoInactiveDestroy => 1) if DBI->VERSION > 1.613 && (
37             @args < 4 || !exists $args[3]->{AutoInactiveDestroy}
38 60 100 100     115936 );
      66        
39              
40             $dbh->STORE(RaiseError => 1) if @args < 4 || (
41             !exists $args[3]->{RaiseError} && !exists $args[3]->{HandleError}
42 60 100 100     1066 );
      100        
43              
44             # Where are we?
45 60         867 $self->{_pid} = $$;
46 60 100       245 $self->{_tid} = threads->tid if $INC{'threads.pm'};
47 60         177 $self->{_dbh} = $dbh;
48              
49 60   66     586 $self->{driver_name} ||= $dbh->{Driver}{Name};
50              
51             # Set up the driver and go!
52 60         675 return $self->driver->_connect($dbh, @args);
53             }
54              
55 2     2 1 4 sub dsn { ( $_[0]{_args}->() )[0] }
56              
57             sub driver_name {
58 2     2 1 3 my $self = shift;
59 2   33     10 $self->{driver_name} ||= ( DBI->parse_dsn( $self->dsn ) )[1];
60             }
61              
62             sub driver {
63 229     229 1 706 my $self = shift;
64 229   66     1262 $self->{driver} ||= DBIx::Connector::Driver->new( $self->{driver_name} || $self->driver_name );
      66        
65             }
66              
67             sub connect {
68 4     4 1 4915 my $self = shift->new(@_);
69 3         9 $self->{_dond} = 0;
70 3         9 $self->dbh;
71             }
72              
73             sub dbh {
74 99     99 1 54928 my $self = shift;
75 99 100       336 my $dbh = $self->_seems_connected or return $self->_connect;
76 64 100       962 return $dbh if $self->{_in_run};
77 39 100       110 return $self->connected ? $dbh : $self->_connect;
78             }
79              
80             # Just like dbh(), except it doesn't ping the server.
81             sub _dbh {
82 95     95   5127 my $self = shift;
83 95 100       250 $self->_seems_connected || $self->_connect;
84             }
85              
86             sub connected {
87 65     65 1 30651 my $self = shift;
88 65 100       188 return unless $self->_seems_connected;
89 60 50       671 my $dbh = $self->{_dbh} or return;
90 60         194 return $self->driver->ping($dbh);
91             }
92              
93             sub mode {
94 42     42 1 3565 my $self = shift;
95 42 100       240 return $self->{_mode} unless @_;
96 18 100 33     333 require Carp && Carp::croak(qq{Invalid mode: "$_[0]"})
97             unless $_[0] =~ /^(?:fixup|(?:no_)?ping)$/;
98 17         91 $self->{_mode} = shift;
99             }
100              
101             sub disconnect_on_destroy {
102 6     6 1 1591 my $self = shift;
103 6 100       34 return $self->{_dond} unless @_;
104 3         17 $self->{_dond} = !!shift;
105             }
106              
107             sub in_txn {
108 71 100   71 1 56885 my $dbh = shift->{_dbh} or return;
109 70         502 return !$dbh->FETCH('AutoCommit');
110             }
111              
112             # returns true if there is a database handle and the PID and TID have not
113             # changed and the handle's Active attribute is true.
114             sub _seems_connected {
115 261     261   881 my $self = shift;
116 261 100       2843 my $dbh = $self->{_dbh} or return;
117 232 100 100     2182 if ( defined $self->{_tid} && $self->{_tid} != threads->tid ) {
    100          
118 2         20 return;
119             } elsif ( $self->{_pid} != $$ ) {
120             # We've forked, so prevent the parent process handle from touching the
121             # DB on DESTROY. Here in the child process, that could really screw
122             # things up. This is superfluous when AutoInactiveDestroy is set, but
123             # harmless. It's better to be proactive anyway.
124 3         26 $dbh->STORE(InactiveDestroy => 1);
125 3         59 return;
126             }
127             # Use FETCH() to avoid death when called from during global destruction.
128 227 100       1399 return $dbh->FETCH('Active') ? $dbh : undef;
129             }
130              
131             sub disconnect {
132 25     25 1 3385 my $self = shift;
133 25 100       1260 if (my $dbh = $self->{_dbh}) {
134             # Some databases need this to stop spewing warnings, according to
135             # DBIx::Class::Storage::DBI. Probably Sybase, as the code was added
136             # when Sybase ASA and SQLAnywhere support were added to DBIx::Class.
137             # If that ever becomes an issue for us, add a _disconnect to the
138             # Driver class that does it, don't do it here.
139             # $dbh->STORE(CachedKids => {});
140 16         89 $dbh->disconnect;
141 16         111 $self->{_dbh} = undef;
142             }
143 25         392 return $self;
144             }
145              
146             sub run {
147 44     44 1 25688 my $self = shift;
148 44 100       172 my $mode = ref $_[0] eq 'CODE' ? $self->{_mode} : shift;
149 44         170 local $self->{_mode} = $mode;
150 44 100       202 return $self->_fixup_run(@_) if $mode eq 'fixup';
151 29         69 return $self->_run(@_);
152             }
153              
154             sub _run {
155 29     29   60 my ($self, $code) = @_;
156 29 100       94 my $dbh = $self->{_mode} eq 'ping' ? $self->dbh : $self->_dbh;
157 29         439 local $self->{_in_run} = 1;
158 29         71 return _exec( $dbh, $code, wantarray );
159             }
160              
161             sub _fixup_run {
162 15     15   35 my ($self, $code) = @_;
163 15         45 my $dbh = $self->_dbh;
164              
165 15         188 my $wantarray = wantarray;
166             return _exec( $dbh, $code, $wantarray )
167 15 100 66     87 if $self->{_in_run} || !$dbh->FETCH('AutoCommit');
168              
169 13         153 local $self->{_in_run} = 1;
170 13         26 my ($err, @ret);
171             TRY: {
172 13         26 local $@;
  13         24  
173 13         58 @ret = eval { _exec( $dbh, $code, $wantarray ) };
  13         38  
174 13         6946 $err = $@;
175             }
176              
177 13 100       48 if ($err) {
178 2 100       10 die $err if $self->connected;
179             # Not connected. Try again.
180 1         20 return _exec( $self->_connect, $code, $wantarray, @_ );
181             }
182              
183 11 100       114 return $wantarray ? @ret : $ret[0];
184             }
185              
186             sub txn {
187 79     79 1 17557 my $self = shift;
188 79 100       363 my $mode = ref $_[0] eq 'CODE' ? $self->{_mode} : shift;
189 79         243 local $self->{_mode} = $mode;
190 79 100       291 return $self->_txn_fixup_run(@_) if $mode eq 'fixup';
191 53         197 return $self->_txn_run(@_);
192             }
193              
194             sub _txn_run {
195 53     53   150 my ($self, $code) = @_;
196 53         220 my $driver = $self->driver;
197 53         112 my $wantarray = wantarray;
198 53 100       200 my $dbh = $self->{_mode} eq 'ping' ? $self->dbh : $self->_dbh;
199              
200 53 100       814 unless ($dbh->FETCH('AutoCommit')) {
201 6         52 local $self->{_in_run} = 1;
202 6         27 return _exec( $dbh, $code, $wantarray );
203             }
204              
205 47         430 my ($err, @ret);
206             TRY: {
207 47         75 local $@;
  47         110  
208 47         104 eval {
209 47         117 local $self->{_in_run} = 1;
210 47         186 $driver->begin_work($dbh);
211 47         1472 @ret = _exec( $dbh, $code, $wantarray );
212 40         15893 $driver->commit($dbh);
213             };
214 47         4661 $err = $@;
215             }
216              
217 47 100       146 if ($err) {
218 7         80 $err = $driver->_rollback($dbh, $err);
219 7         90 die $err;
220             }
221              
222 40 100       326 return $wantarray ? @ret : $ret[0];
223             }
224              
225             sub _txn_fixup_run {
226 26     26   68 my ($self, $code) = @_;
227 26         73 my $dbh = $self->_dbh;
228 26         348 my $driver = $self->driver;
229              
230 26         53 my $wantarray = wantarray;
231 26         162 local $self->{_in_run} = 1;
232              
233 26 100       101 return _exec( $dbh, $code, $wantarray ) unless $dbh->FETCH('AutoCommit');
234              
235 22         207 my ($err, @ret);
236             TRY: {
237 22         40 local $@;
  22         37  
238 22         79 eval {
239 22         97 $driver->begin_work($dbh);
240 22         756 @ret = _exec( $dbh, $code, $wantarray );
241 17         6946 $driver->commit($dbh);
242             };
243 22         5120 $err = $@;
244             }
245              
246 22 100       77 if ($err) {
247 5 100       20 if ($self->connected) {
248 1         10 $err = $driver->_rollback($dbh, $err);
249 1         12 die $err;
250             }
251              
252             # Not connected. Try again.
253 4         51 $dbh = $self->_connect;
254             TRY: {
255 4         119 local $@;
  4         10  
256 4         11 eval {
257 4         16 $driver->begin_work($dbh);
258 4         97 @ret = _exec( $dbh, $code, $wantarray );
259 1         1475 $driver->commit($dbh);
260             };
261 4         566 $err = $@;
262             }
263 4 100       16 if ($err) {
264 3         15 $err = $driver->_rollback($dbh, $err);
265 3         31 die $err;
266             }
267             }
268              
269 18 100       180 return $wantarray ? @ret : $ret[0];
270             }
271              
272             sub svp {
273 39     39 1 10659 my $self = shift;
274 39         88 my $dbh = $self->{_dbh};
275              
276             # Gotta have a transaction.
277 39 100 66     306 return $self->txn( @_ ) if !$dbh || $dbh->FETCH('AutoCommit');
278              
279 24 100       301 my $mode = ref $_[0] eq 'CODE' ? $self->{_mode} : shift;
280 24         62 local $self->{_mode} = $mode;
281 24         43 my $code = shift;
282              
283 24         71 my ($err, @ret);
284 24         47 my $wantarray = wantarray;
285 24         80 my $driver = $self->driver;
286 24         60 my $name = "savepoint_$self->{_svp_depth}";
287 24         47 ++$self->{_svp_depth};
288              
289             TRY: {
290 24         35 local $@;
  24         52  
291 24         47 eval {
292 24         159 $driver->savepoint($dbh, $name);
293 24         90 @ret = _exec( $dbh, $code, $wantarray );
294 20         11310 $driver->release($dbh, $name);
295             };
296 24         110 $err = $@;
297             }
298 24         64 --$self->{_svp_depth};
299              
300 24 100       66 if ($err) {
301             # If we died, there is nothing to be done.
302 4 50       13 if ($self->connected) {
303 4         54 $err = $driver->_rollback_and_release($dbh, $name, $err);
304             }
305 4         31 die $err;
306             }
307              
308 20 100       143 return $wantarray ? @ret : $ret[0];
309             }
310              
311             sub _exec {
312 152     152   383 my ($dbh, $code, $wantarray) = @_;
313 152 50       483 local $_ = $dbh or return;
314             # Block prevents exiting via next or last, otherwise no commit/rollback.
315             NOEXIT: {
316 152 100       232 return $wantarray ? $code->($dbh) : scalar $code->($dbh)
  152 100       537  
317             if defined $wantarray;
318 70         268 return $code->($dbh);
319             }
320 18         137 return;
321             }
322              
323             1;
324              
325             __END__