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   779067 use 5.008001; use strict; use warnings;
  13     13   140  
  13     13   79  
  13         24  
  13         254  
  13         59  
  13         35  
  13         663  
2              
3             package DBIx::Connector;
4              
5 13     13   20627 use DBI '1.605';
  13         231961  
  13         2741  
6 13     13   6335 use DBIx::Connector::Driver;
  13         38  
  13         26493  
7              
8             our $VERSION = '0.57';
9              
10             sub new {
11 30     30 1 4122 my $class = shift;
12 30         232 my @args = @_;
13             bless {
14 63     63   197 _args => sub { @args },
15 30         384 _svp_depth => 0,
16             _mode => 'no_ping',
17             _dond => 1,
18             } => $class;
19             }
20              
21 26 100   26   5599 sub DESTROY { $_[0]->disconnect if $_[0]->{_dond} }
22              
23             sub _connect {
24 61     61   3048 my $self = shift;
25 61         153 my @args = $self->{_args}->();
26 61 50       102 my $dbh = do {
27 61 100 66     234 if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
28 1         3 local $DBI::connect_via = 'connect'; # Disable Apache::DBI.
29 1         5 DBI->connect( @args );
30             } else {
31 60         309 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     72816 );
      66        
39              
40             $dbh->STORE(RaiseError => 1) if @args < 4 || (
41             !exists $args[3]->{RaiseError} && !exists $args[3]->{HandleError}
42 60 100 100     1061 );
      100        
43              
44             # Where are we?
45 60         660 $self->{_pid} = $$;
46 60 100       193 $self->{_tid} = threads->tid if $INC{'threads.pm'};
47 60         141 $self->{_dbh} = $dbh;
48              
49 60   66     463 $self->{driver_name} ||= $dbh->{Driver}{Name};
50              
51             # Set up the driver and go!
52 60         551 return $self->driver->_connect($dbh, @args);
53             }
54              
55 2     2 1 5 sub dsn { ( $_[0]{_args}->() )[0] }
56              
57             sub driver_name {
58 2     2 1 3 my $self = shift;
59 2   33     11 $self->{driver_name} ||= ( DBI->parse_dsn( $self->dsn ) )[1];
60             }
61              
62             sub driver {
63 229     229 1 562 my $self = shift;
64 229   66     1000 $self->{driver} ||= DBIx::Connector::Driver->new( $self->{driver_name} || $self->driver_name );
      66        
65             }
66              
67             sub connect {
68 4     4 1 1059 my $self = shift->new(@_);
69 3         7 $self->{_dond} = 0;
70 3         16 $self->dbh;
71             }
72              
73             sub dbh {
74 99     99 1 32784 my $self = shift;
75 99 100       262 my $dbh = $self->_seems_connected or return $self->_connect;
76 64 100       811 return $dbh if $self->{_in_run};
77 39 100       105 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   1686 my $self = shift;
83 95 100       197 $self->_seems_connected || $self->_connect;
84             }
85              
86             sub connected {
87 65     65 1 15960 my $self = shift;
88 65 100       144 return unless $self->_seems_connected;
89 60 50       665 my $dbh = $self->{_dbh} or return;
90 60         145 return $self->driver->ping($dbh);
91             }
92              
93             sub mode {
94 42     42 1 2664 my $self = shift;
95 42 100       178 return $self->{_mode} unless @_;
96 18 100 33     271 require Carp && Carp::croak(qq{Invalid mode: "$_[0]"})
97             unless $_[0] =~ /^(?:fixup|(?:no_)?ping)$/;
98 17         66 $self->{_mode} = shift;
99             }
100              
101             sub disconnect_on_destroy {
102 6     6 1 959 my $self = shift;
103 6 100       28 return $self->{_dond} unless @_;
104 3         17 $self->{_dond} = !!shift;
105             }
106              
107             sub in_txn {
108 71 100   71 1 36580 my $dbh = shift->{_dbh} or return;
109 70         358 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   715 my $self = shift;
116 261 100       828 my $dbh = $self->{_dbh} or return;
117 232 100 100     987 if ( defined $self->{_tid} && $self->{_tid} != threads->tid ) {
    100          
118 2         16 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         20 $dbh->STORE(InactiveDestroy => 1);
125 3         51 return;
126             }
127             # Use FETCH() to avoid death when called from during global destruction.
128 227 100       991 return $dbh->FETCH('Active') ? $dbh : undef;
129             }
130              
131             sub disconnect {
132 25     25 1 1969 my $self = shift;
133 25 100       99 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         78 $dbh->disconnect;
141 16         117 $self->{_dbh} = undef;
142             }
143 25         331 return $self;
144             }
145              
146             sub run {
147 44     44 1 20017 my $self = shift;
148 44 100       137 my $mode = ref $_[0] eq 'CODE' ? $self->{_mode} : shift;
149 44         109 local $self->{_mode} = $mode;
150 44 100       125 return $self->_fixup_run(@_) if $mode eq 'fixup';
151 29         64 return $self->_run(@_);
152             }
153              
154             sub _run {
155 29     29   54 my ($self, $code) = @_;
156 29 100       81 my $dbh = $self->{_mode} eq 'ping' ? $self->dbh : $self->_dbh;
157 29         336 local $self->{_in_run} = 1;
158 29         68 return _exec( $dbh, $code, wantarray );
159             }
160              
161             sub _fixup_run {
162 15     15   60 my ($self, $code) = @_;
163 15         32 my $dbh = $self->_dbh;
164              
165 15         128 my $wantarray = wantarray;
166             return _exec( $dbh, $code, $wantarray )
167 15 100 66     68 if $self->{_in_run} || !$dbh->FETCH('AutoCommit');
168              
169 13         122 local $self->{_in_run} = 1;
170 13         20 my ($err, @ret);
171             TRY: {
172 13         20 local $@;
  13         17  
173 13         22 @ret = eval { _exec( $dbh, $code, $wantarray ) };
  13         26  
174 13         4276 $err = $@;
175             }
176              
177 13 100       34 if ($err) {
178 2 100       6 die $err if $self->connected;
179             # Not connected. Try again.
180 1         13 return _exec( $self->_connect, $code, $wantarray, @_ );
181             }
182              
183 11 100       75 return $wantarray ? @ret : $ret[0];
184             }
185              
186             sub txn {
187 79     79 1 11364 my $self = shift;
188 79 100       266 my $mode = ref $_[0] eq 'CODE' ? $self->{_mode} : shift;
189 79         193 local $self->{_mode} = $mode;
190 79 100       243 return $self->_txn_fixup_run(@_) if $mode eq 'fixup';
191 53         151 return $self->_txn_run(@_);
192             }
193              
194             sub _txn_run {
195 53     53   121 my ($self, $code) = @_;
196 53         115 my $driver = $self->driver;
197 53         103 my $wantarray = wantarray;
198 53 100       177 my $dbh = $self->{_mode} eq 'ping' ? $self->dbh : $self->_dbh;
199              
200 53 100       656 unless ($dbh->FETCH('AutoCommit')) {
201 6         50 local $self->{_in_run} = 1;
202 6         20 return _exec( $dbh, $code, $wantarray );
203             }
204              
205 47         372 my ($err, @ret);
206             TRY: {
207 47         76 local $@;
  47         70  
208 47         81 eval {
209 47         113 local $self->{_in_run} = 1;
210 47         191 $driver->begin_work($dbh);
211 47         1402 @ret = _exec( $dbh, $code, $wantarray );
212 40         10856 $driver->commit($dbh);
213             };
214 47         3400 $err = $@;
215             }
216              
217 47 100       131 if ($err) {
218 7         29 $err = $driver->_rollback($dbh, $err);
219 7         33 die $err;
220             }
221              
222 40 100       255 return $wantarray ? @ret : $ret[0];
223             }
224              
225             sub _txn_fixup_run {
226 26     26   61 my ($self, $code) = @_;
227 26         61 my $dbh = $self->_dbh;
228 26         267 my $driver = $self->driver;
229              
230 26         48 my $wantarray = wantarray;
231 26         62 local $self->{_in_run} = 1;
232              
233 26 100       87 return _exec( $dbh, $code, $wantarray ) unless $dbh->FETCH('AutoCommit');
234              
235 22         179 my ($err, @ret);
236             TRY: {
237 22         42 local $@;
  22         31  
238 22         37 eval {
239 22         77 $driver->begin_work($dbh);
240 22         631 @ret = _exec( $dbh, $code, $wantarray );
241 17         4592 $driver->commit($dbh);
242             };
243 22         3196 $err = $@;
244             }
245              
246 22 100       67 if ($err) {
247 5 100       17 if ($self->connected) {
248 1         8 $err = $driver->_rollback($dbh, $err);
249 1         6 die $err;
250             }
251              
252             # Not connected. Try again.
253 4         59 $dbh = $self->_connect;
254             TRY: {
255 4         120 local $@;
  4         9  
256 4         9 eval {
257 4         15 $driver->begin_work($dbh);
258 4         83 @ret = _exec( $dbh, $code, $wantarray );
259 1         854 $driver->commit($dbh);
260             };
261 4         411 $err = $@;
262             }
263 4 100       20 if ($err) {
264 3         13 $err = $driver->_rollback($dbh, $err);
265 3         17 die $err;
266             }
267             }
268              
269 18 100       138 return $wantarray ? @ret : $ret[0];
270             }
271              
272             sub svp {
273 39     39 1 7906 my $self = shift;
274 39         79 my $dbh = $self->{_dbh};
275              
276             # Gotta have a transaction.
277 39 100 66     243 return $self->txn( @_ ) if !$dbh || $dbh->FETCH('AutoCommit');
278              
279 24 100       307 my $mode = ref $_[0] eq 'CODE' ? $self->{_mode} : shift;
280 24         55 local $self->{_mode} = $mode;
281 24         43 my $code = shift;
282              
283 24         35 my ($err, @ret);
284 24         44 my $wantarray = wantarray;
285 24         58 my $driver = $self->driver;
286 24         81 my $name = "savepoint_$self->{_svp_depth}";
287 24         41 ++$self->{_svp_depth};
288              
289             TRY: {
290 24         36 local $@;
  24         37  
291 24         39 eval {
292 24         74 $driver->savepoint($dbh, $name);
293 24         93 @ret = _exec( $dbh, $code, $wantarray );
294 20         6549 $driver->release($dbh, $name);
295             };
296 24         113 $err = $@;
297             }
298 24         41 --$self->{_svp_depth};
299              
300 24 100       65 if ($err) {
301             # If we died, there is nothing to be done.
302 4 50       12 if ($self->connected) {
303 4         52 $err = $driver->_rollback_and_release($dbh, $name, $err);
304             }
305 4         21 die $err;
306             }
307              
308 20 100       124 return $wantarray ? @ret : $ret[0];
309             }
310              
311             sub _exec {
312 152     152   341 my ($dbh, $code, $wantarray) = @_;
313 152 50       335 local $_ = $dbh or return;
314             # Block prevents exiting via next or last, otherwise no commit/rollback.
315             NOEXIT: {
316 152 100       227 return $wantarray ? $code->($dbh) : scalar $code->($dbh)
  152 100       477  
317             if defined $wantarray;
318 70         188 return $code->($dbh);
319             }
320 18         81 return;
321             }
322              
323             1;
324              
325             __END__