File Coverage

blib/lib/DBR/Config/Instance.pm
Criterion Covered Total %
statement 105 115 91.3
branch 32 52 61.5
condition 32 59 54.2
subroutine 17 21 80.9
pod 0 15 0.0
total 186 262 70.9


line stmt bran cond sub pod time code
1             # the contents of this file are Copyright (c) 2009 Daniel Norman
2             # This program is free software; you can redistribute it and/or
3             # modify it under the terms of the GNU General Public License as
4             # published by the Free Software Foundation.
5              
6             package DBR::Config::Instance;
7              
8 18     18   127 use strict;
  18         39  
  18         595  
9 18     18   72552 use DBI;
  18         552174  
  18         1803  
10 18     18   240 use base 'DBR::Common';
  18         43  
  18         1866  
11 18     18   12349 use DBR::Config::Schema;
  18         63  
  18         904  
12 18     18   166 use Carp;
  18         44  
  18         59810  
13              
14             my $GUID = 1;
15              
16             #here is a list of the currently supported databases and their connect string formats
17             my %connectstrings = (
18             Mysql => 'dbi:mysql:database=-database-;host=-hostname-',
19             SQLite => 'dbi:SQLite:dbname=-dbfile-',
20             Pg => 'dbi:Pg:dbname=-database-;host=-hostname-',
21             );
22              
23             my %CONCACHE;
24             my %INSTANCE_MAP;
25             my %INSTANCES_BY_GUID;
26              
27              
28             sub flush_all_handles {
29             # can be run with or without an object
30 34     34 0 97 my $cache = \%CONCACHE;
31              
32 34         172 foreach my $guid (keys %INSTANCES_BY_GUID){
33 68         164 my $conn = $cache->{ $guid };
34 68 100       232 if($conn){
35 51         389 $conn->disconnect();
36 51         2893 delete $cache->{ $guid };
37             }
38             }
39              
40 34         569 return 1;
41             }
42              
43             sub lookup{
44 133     133 0 337 my $package = shift;
45 133         863 my %params = @_;
46              
47 133         658 my $self = {
48             session => $params{session}
49             };
50 133         569 bless( $self, $package );
51              
52 133 50       658 return $self->_error('session is required') unless $self->{session};
53              
54 133 100       904 if( $params{guid} ){
55 83         345 $self->{guid} = $params{guid};
56             }else{
57 50   50     447 my $handle = $params{handle} || return $self->_error('handle is required');
58 50   50     456 my $class = $params{class} || 'master';
59              
60 50 50 33     795 $self->{guid} = $INSTANCE_MAP{$handle}->{$class} || $INSTANCE_MAP{$handle}->{'*'} or # handle aliases if there's no exact match
61             return $self->_error("No DB instance found for '$handle','$class'");
62              
63             }
64              
65 133 50       1251 $INSTANCES_BY_GUID{ $self->{guid} } or return $self->_error('no such guid');
66              
67 133         1354 return $self;
68              
69             }
70              
71             sub load_from_db{
72              
73 17     17 0 54 my( $package ) = shift;
74 17         79 my %params = @_;
75              
76 17         75 my $self = {
77             session => $params{session},
78             };
79 17         61 bless( $self, $package ); # Dummy object
80              
81 17   50     93 my $parent = $params{parent_inst} || return $self->_error('parent_inst is required');
82 17   50     94 my $dbh = $parent->connect || return $self->_error("Failed to connect to (@{[$parent->handle]} @{[$parent->class]})");
83              
84 17 50       156 return $self->_error('Failed to select instances') unless
85             my $instrows = $dbh->select(
86             -table => 'dbr_instances',
87             -fields => 'instance_id schema_id class dbname username password host dbfile module handle readonly'
88             );
89              
90 17         66 my @instances;
91 17         70 foreach my $instrow (@$instrows){
92              
93 17 50 33     127 my $instance = $self->register(
94             session => $self->{session},
95             spec => $instrow
96             ) || $self->_error("failed to load instance from database (@{[$parent->handle]} @{[$parent->class]})") or next;
97 17         83 push @instances, $instance;
98             }
99              
100 17         177 return \@instances;
101             }
102              
103             sub register { # basically the same as a new
104 34     34 0 106 my( $package ) = shift;
105 34   66     211 $package = ref( $package ) || $package;
106 34         191 my %params = @_;
107              
108              
109 34         211 my $self = {
110             session => $params{session}
111             };
112 34         8698 bless( $self, $package );
113              
114 34 50       282 return $self->_error( 'session is required' ) unless $self->{session};
115              
116              
117 34 50       245 my $spec = $params{spec} or return $self->_error( 'spec ref is required' );
118              
119 34   66     2138 my $config = {
      66        
      33        
      33        
      33        
      50        
      100        
      100        
      50        
      50        
120             handle => $spec->{handle} || $spec->{name},
121             module => $spec->{module} || $spec->{type},
122             database => $spec->{dbname} || $spec->{database},
123             hostname => $spec->{hostname} || $spec->{host},
124             user => $spec->{username} || $spec->{user},
125             dbfile => $spec->{dbfile},
126             password => $spec->{password},
127             class => $spec->{class} || 'master', # default to master
128             instance_id => $spec->{instance_id} || '',
129             schema_id => $spec->{schema_id} || '',
130             allowquery => $spec->{allowquery} || 0,
131             readonly => $spec->{readonly} || 0,
132             };
133              
134 34 50       151 return $self->_error( 'module/type parameter is required' ) unless $config->{module};
135 34 50       126 return $self->_error( 'handle/name parameter is required' ) unless $config->{handle};
136              
137 34   50     542 $config->{connectstring} = $connectstrings{$config->{module}} || return $self->_error("module '$config->{module}' is not a supported database type");
138              
139 34         128 my $connclass = 'DBR::Misc::Connection::' . $config->{module};
140 34 50       3939 return $self->_error("Failed to Load $connclass ($@)") unless eval "require $connclass";
141              
142 34         187 $config->{connclass} = $connclass;
143              
144 34 50       261 my $reqfields = $connclass->required_config_fields or return $self->_error('Failed to determine required config fields');
145              
146 34         119 foreach my $name (@$reqfields){
147 34 50       207 return $self->_error( $name . ' parameter is required' ) unless $config->{$name};
148             }
149              
150 34 100       204 $config->{dbr_bootstrap} = $spec->{dbr_bootstrap}? 1:0;
151              
152 34         72 foreach my $key (keys %{$config}) {
  34         238  
153 510         6862 $config->{connectstring} =~ s/-$key-/$config->{$key}/;
154             }
155              
156             #Reuse the guid if we are being reloaded
157 34   33     4674 my $guid = $INSTANCE_MAP{ $config->{handle} }->{ $config->{class} } || $GUID++;
158              
159             # Register this instance in the global repository
160 34   33     785 $INSTANCE_MAP{ $config->{handle} }->{ $config->{class} } ||= $guid;
161              
162 34         116 $INSTANCES_BY_GUID{ $guid } = $config;
163 34         203 $self->{guid} = $config->{guid} = $guid;
164             # Now we are cool to start calling accessors
165              
166 34 50       145 if ($spec->{alias}) {
167 0         0 $INSTANCE_MAP{ $spec->{alias} }->{'*'} = $guid;
168             }
169              
170 34 100       187 if ($config->{schema_id}){
171 17 50       259 DBR::Config::Schema->_register_instance(
172             schema_id => $config->{schema_id},
173             class => $config->{class},
174             guid => $guid,
175             ) or return $self->_error('failed to register table');
176             }
177              
178 34         1969 return( $self );
179             }
180              
181              
182             #######################################################################
183             ############################ #
184             ############################ All subs below here require an object #
185             ############################ #
186             #######################################################################
187              
188              
189             sub connect{
190 2428     2428 0 10716 my $self = shift;
191 2428   100     12631 my $flag = shift || '';
192              
193 2428 100       12904 if (lc($flag) eq 'dbh') {
    100          
194 75         289 return $self->getconn->dbh;
195             }elsif (lc($flag) eq 'conn') {
196 1717         4609 return $self->getconn;
197             } else {
198 636 0       2303 return DBR::Handle->new(
199             conn => $self->getconn,
200             session => $self->{session},
201             instance => $self,
202             ) or confess 'Failed to create Handle object';
203             }
204             }
205              
206             sub getconn{
207 3015     3015 0 5751 my $self = shift;
208              
209 3015         7603 my $conn = $CONCACHE{ $self->{guid} };
210              
211             # conn-ping-zoom!!
212 3015 100 66     24040 return $conn if $conn && $conn->ping; # Most of the time, we are done right here
213              
214 51 50       157 if ($conn) {
215 0         0 $conn->disconnect();
216 0         0 $conn = $CONCACHE{ $self->{guid} } = undef;
217 0         0 $self->_logDebug('Handle went stale');
218             }
219              
220             # if we are here, that means either the connection failed, or we never had one
221              
222 51         337 $self->_logDebug2('getting a new connection');
223 51 50       235 $conn = $self->_new_connection() or confess "Failed to connect to ${\$self->handle}, ${\$self->class}";
  0         0  
  0         0  
224              
225 51         230 $self->_logDebug2('Connected');
226              
227 51         964 return $CONCACHE{ $self->{guid} } = $conn;
228             }
229              
230             sub _new_connection{
231 51     51   105 my $self = shift;
232              
233 51         202 my $config = $INSTANCES_BY_GUID{ $self->{guid} };
234 51         267 my @params = ($config->{connectstring}, $config->{user}, $config->{password});
235              
236 51 50       448 my $dbh = DBI->connect(@params) or
237             return $self->_error("Error: Failed to connect to db $config->{handle},$config->{class}");
238              
239 51         40824 my $connclass = $config->{connclass};
240              
241 51 50       956 return $self->_error("Failed to create $connclass object") unless
242             my $conn = $connclass->new(
243             session => $self->{session},
244             dbh => $dbh
245             );
246              
247 51         616 return $conn;
248             }
249              
250 76     76 0 715 sub is_readonly { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{readonly} }
251 0     0 0 0 sub handle { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{handle} }
252 0     0 0 0 sub class { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{class} }
253 102     102 0 741 sub guid { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{guid} }
254 0     0 0 0 sub module { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{module} }
255 17     17 0 119 sub dbr_bootstrap { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{dbr_bootstrap} }
256 671     671 0 7760 sub schema_id { $INSTANCES_BY_GUID{ $_[0]->{guid} }->{schema_id} }
257 0     0 0 0 sub name { return $_[0]->handle . ' ' . $_[0]->class }
258              
259             #shortcut to fetch the schema object that corresponds to this instance
260             sub schema{
261 637     637 0 1725 my $self = shift;
262 637         1573 my %params = @_;
263              
264 637   100     2899 my $schema_id = $self->schema_id || return ''; # No schemas here
265              
266 82   50     600 my $schema = DBR::Config::Schema->new(
267             session => $self->{session},
268             schema_id => $schema_id,
269             ) || return $self->_error("failed to fetch schema object for schema_id $schema_id");
270              
271 82         390 return $schema;
272             }
273              
274             1;