File Coverage

blib/lib/Scope/Container/DBI.pm
Criterion Covered Total %
statement 113 126 89.6
branch 44 60 73.3
condition 17 40 42.5
subroutine 18 18 100.0
pod 1 2 50.0
total 193 246 78.4


line stmt bran cond sub pod time code
1             package Scope::Container::DBI;
2              
3 6     6   346150 use strict;
  6         12  
  6         194  
4 6     6   32 use warnings;
  6         15  
  6         161  
5 6     6   1765 use Scope::Container;
  6         1102  
  6         356  
6 6     6   5373 use Log::Minimal;
  6         155808  
  6         37  
7 6     6   813 use List::Util qw/shuffle/;
  6         13  
  6         567  
8 6     6   31 use Data::Dumper;
  6         9  
  6         230  
9 6     6   5124 use Try::Tiny;
  6         8922  
  6         343  
10 6     6   11625 use Time::HiRes qw//;
  6         11180  
  6         176  
11 6     6   6090 use Module::Load qw/load/;
  6         7026  
  6         51  
12 6     6   485 use Carp;
  6         12  
  6         486  
13 6     6   15522 use DBI 1.615;
  6         107453  
  6         7345  
14              
15             our $VERSION = '0.09';
16             our $DBI_CLASS = 'DBI';
17              
18             sub connect {
19 104     104 1 1081048 my $class = shift;
20              
21 104 100 100     1319 if ( @_ && (ref $_[0] || '' eq 'ARRAY') ) {
      33        
22 36         93 my @dsn = @_;
23 36         54 my $dbi;
24 36         129 my $dsn_key = _build_dsn_key(@dsn);
25 36         120 my $dbh = _lookup_cache($dsn_key);
26 36 100       366 return $dbh if $dbh;
27              
28 33         201 for my $s_dsn ( shuffle(@dsn) ) {
29 45         72 eval {
30 45         183 ($dbh, $dbi) = $class->connect(@$s_dsn);
31             };
32 45 100       849 infof("Connection failed: " . $@) if $@;
33 45 100       1923 last if ( $dbh );
34             }
35              
36 33 50       108 if ( $dbh ) {
37 33         69 _save_cache($dsn_key, $dbi);
38 33 50       288 return wantarray ? ( $dbh, $dbi) : $dbh;
39             }
40            
41 0         0 croak("couldn't connect all DB, " .
42 0         0 join(",", map { $_->[0] } @dsn));
43             }
44              
45 68         463 my @dsn = @_;
46 68         254 my $dsn_key = _build_dsn_key(\@dsn);
47 68         208 my $cached_dbh = _lookup_cache($dsn_key);
48 68 100       611 return $cached_dbh if $cached_dbh;
49              
50 62         145 my ($dsn, $user, $pass, $attr) = @dsn;
51 62   100     333 $attr ||= {};
52 62         170 $attr->{AutoInactiveDestroy} = 1;
53 62 100       308 my $retry = exists $attr->{ScopeContainerConnectRetry} ? delete $attr->{ScopeContainerConnectRetry} : 1;
54 62         116 my $sleep = delete $attr->{ScopeContainerConnectRetrySleep};
55 62 100       633 $sleep = $sleep / 1000 if $sleep;
56              
57 62 50       396 if ( ! is_class_loaded($DBI_CLASS) ) {
58 0         0 load $DBI_CLASS;
59             }
60 62         207 my $lasterrstr;
61 62         78 my $dbh = do {
62 62         82 my $connect;
63 62         177 for ( 1..$retry ) {
64             try {
65 62 50 33 62   2947 if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
66 0         0 local $DBI::connect_via = 'connect'; # Disable Apache::DBI.
67 0         0 $connect = $DBI_CLASS->connect( $dsn, $user, $pass, $attr );
68             } else {
69 62         523 $connect = $DBI_CLASS->connect( $dsn, $user, $pass, $attr );
70             }
71 60 100       87803 die $DBI::errstr."\n" unless $connect;
72             }
73             catch {
74 14     14   2700 $lasterrstr = $_;
75 62         1084 };
76 62 100       1480 last if $connect;
77 14 50 33     68 Time::HiRes::sleep($sleep) if $sleep && $retry != $_;
78             }
79 62         266 $connect;
80             };
81 62 100       4124 croak($lasterrstr) if !$dbh;
82              
83 48         285 my $dbi = {
84             dbh => $dbh,
85             pid => $$,
86             };
87 48 50       190 $dbi->{tid} = threads->tid if $INC{'threads.pm'};
88              
89 48         170 _save_cache($dsn_key, $dbi);
90 48 100       555 return wantarray ? ( $dbh, $dbi ) : $dbh;
91             }
92              
93              
94             sub _build_dsn_key {
95 104     104   195 my @dsn = @_;
96 104         248 local $Data::Dumper::Terse = 1;
97 104         289 local $Data::Dumper::Indent = 0;
98 104         182 local $Data::Dumper::Sortkeys = 1;
99 104         563 my $key = Data::Dumper::Dumper(\@dsn);
100 104         11687 "sc:dbix:".$key;
101             }
102              
103             sub _lookup_cache {
104 104     104   175 my $key = shift;
105 104 100       378 return unless in_scope_container();
106 31         240 my $dbi = scope_container($key);
107 31 100       364 return if !$dbi;
108              
109 10         28 my $dbh = $dbi->{dbh};
110 10 50 33     45 if ( defined $dbi->{tid} && $dbi->{tid} != threads->tid ) {
111 0         0 return;
112             }
113 10 100       265 if ( $dbi->{pid} != $$ ) {
114 1         187 $dbh->STORE(InactiveDestroy => 1);
115 1         13 return;
116             }
117 9 50 33     207 return $dbh if $dbh->FETCH('Active') && $dbh->ping;
118 0         0 return;
119             }
120              
121             sub _save_cache {
122 81     81   120 my $key = shift;
123 81 100       224 return unless in_scope_container();
124 20         127 scope_container($key, shift);
125             }
126              
127             # stolen from Mouse::PurePerl
128             sub is_class_loaded {
129 62     62 0 146 my $class = shift;
130              
131 62 50 33     677 return 0 if ref($class) || !defined($class) || !length($class);
      33        
132              
133             # walk the symbol table tree to avoid autovififying
134             # \*{${main::}{"Foo::"}{"Bar::"}} == \*main::Foo::Bar::
135              
136 62         143 my $pack = \%::;
137              
138 62         547 foreach my $part (split('::', $class)) {
139 62         105 $part .= '::';
140 62 50       183 return 0 if !exists $pack->{$part};
141              
142 62         108 my $entry = \$pack->{$part};
143 62 50       189 return 0 if ref($entry) ne 'GLOB';
144 62         77 $pack = *{$entry}{HASH};
  62         246  
145             }
146              
147 62 50       85 return 0 if !%{$pack};
  62         461  
148              
149             # check for $VERSION or @ISA
150 61         635 return 1 if exists $pack->{VERSION}
151 62 50 66     366 && defined *{$pack->{VERSION}}{SCALAR} && defined ${ $pack->{VERSION} };
  61   66     479  
152 1         10 return 1 if exists $pack->{ISA}
153 1 50 33     7 && defined *{$pack->{ISA}}{ARRAY} && @{ $pack->{ISA} } != 0;
  1   33     19  
154              
155             # check for any method
156 0           foreach my $name( keys %{$pack} ) {
  0            
157 0           my $entry = \$pack->{$name};
158 0 0 0       return 1 if ref($entry) ne 'GLOB' || defined *{$entry}{CODE};
  0            
159             }
160              
161             # fail
162 0           return 0;
163             }
164              
165              
166             1;
167             __END__