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