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