File Coverage

blib/lib/Paranoid/BerkeleyDB/Db.pm
Criterion Covered Total %
statement 31 33 93.9
branch n/a
condition n/a
subroutine 11 11 100.0
pod n/a
total 42 44 95.4


line stmt bran cond sub pod time code
1             # Paranoid::BerkeleyDB::Db -- BerkeleyDB Db Wrapper
2             #
3             # (c) 2005 - 2017, Arthur Corliss
4             #
5             # $Id: lib/Paranoid/BerkeleyDB/Db.pm, 2.03 2017/02/06 02:49:24 acorliss Exp $
6             #
7             # This software is licensed under the same terms as Perl, itself.
8             # Please see http://dev.perl.org/licenses/ for more information.
9             #
10             #####################################################################
11              
12             #####################################################################
13             #
14             # Dbironment definitions
15             #
16             #####################################################################
17              
18             package Paranoid::BerkeleyDB::Db;
19              
20 2     2   30895 use strict;
  2         3  
  2         43  
21 2     2   6 use warnings;
  2         2  
  2         40  
22 2     2   7 use vars qw($VERSION);
  2         3  
  2         57  
23 2     2   6 use Paranoid;
  2         2  
  2         61  
24 2     2   5 use Paranoid::Debug qw(:all);
  2         2  
  2         225  
25 2     2   774 use Paranoid::Input qw(detaint);
  2         3809  
  2         92  
26 2     2   880 use Paranoid::IO;
  2         15596  
  2         136  
27 2     2   771 use Paranoid::IO::Lockfile;
  2         1617  
  2         92  
28 2     2   906 use Paranoid::Filesystem;
  2         31889  
  2         155  
29 2     2   1111 use Class::EHierarchy qw(:all);
  2         11670  
  2         230  
30 2     2   1532 use BerkeleyDB;
  0            
  0            
31             use Cwd qw(getcwd realpath);
32             use Carp;
33              
34             ($VERSION) = ( q$Revision: 2.03 $ =~ /(\d+(?:\.\d+)+)/sm );
35              
36             use vars qw(@ISA @_properties @_methods);
37              
38             @_properties = ( [ CEH_PRIV | CEH_SCALAR, 'filename' ], );
39              
40             @ISA = qw(Class::EHierarchy);
41              
42             #####################################################################
43             #
44             # module code follows
45             #
46             #####################################################################
47              
48             {
49              
50             my %dbh; # Object handles
51             my %dbp; # Parameters used for handles
52             my %dbc; # Reference count
53             my %pid; # Object handle create PID
54              
55             sub _openDb {
56              
57             # Purpose: Opens a new database or returns a cached reference
58             # Returns: Reference to BerkeleyDB::Db object
59             # Usage: $env = _openDb(%params);
60              
61             my $obj = shift;
62             my %params = @_;
63             my ( $db, $fn, $fnp, $env, $rv );
64              
65             pdebug( 'entering w/%s', PDLEVEL2, %params );
66             pIn();
67              
68             # Validate filename
69             $rv = defined $params{'-Filename'};
70             if ( $rv and $rv = detaint( $params{'-Filename'}, 'filename', $fn ) )
71             {
72              
73             # Create the path
74             ($fnp) = ( $fn =~ m#^(.*/)#s );
75             if ( defined $fnp and length $fnp and pmkdir($fnp) ) {
76             if ( pmkdir($fnp) ) {
77              
78             # Make canonical and save
79             $params{'-Filename'} = $fn = realpath($fn);
80             pdebug( 'canonical filename: %s', PDLEVEL3, $fn );
81              
82             } else {
83             carp pdebug(
84             'failed to create/access the requisite directory',
85             PDLEVEL1 );
86             $rv = 0;
87             }
88             }
89              
90             } else {
91             carp pdebug( 'invalid filename specified: %s',
92             PDLEVEL1, $params{'-Filename'} );
93             $rv = 0;
94             }
95              
96             if ($rv) {
97             if ( exists $dbh{$fn} ) {
98             pdebug( 'database already exists', PDLEVEL3 );
99              
100             if ( $pid{$fn} == $$ ) {
101             pdebug( 'using cached reference', PDLEVEL3 );
102              
103             # Increment reference count
104             $dbc{$fn}++;
105             $db = $dbh{$fn};
106              
107             } else {
108              
109             # Expect bad things from here, particularly since
110             # BerkeleyDB will close the shared file handles for
111             # the parent process as well...
112             croak pdebug(
113             'cached ref created under different pid (%s)',
114             PDLEVEL1, $pid{$fn} );
115              
116             }
117              
118             } else {
119              
120             pdebug( 'opening a new database', PDLEVEL3 );
121              
122             # Add default flags if they're omitted
123             $params{'-Mode'} = 0666 & ~umask;
124             $params{'-Flags'} = DB_CREATE
125             unless exists $params{'-Flags'};
126              
127             # Validate/set the Env
128             unless (exists $params{'-Env'}
129             and defined $params{'-Env'}
130             and $params{'-Env'}->isa('Paranoid::BerkeleyDB::Env') ) {
131              
132             # It doesn't appear we were called with a valid Env, so
133             # we'll delete any references to it and try to muscle
134             # ahead
135             delete $params{'-Env'};
136              
137             }
138              
139             pdebug( 'final parameters: %s', PDLEVEL4, %params );
140              
141             # Create the database
142             if ( pexclock( "$fn.lock", $params{'-Mode'} ) ) {
143             $db = BerkeleyDB::Btree->new(
144             %params, (
145             exists $params{'-Env'}
146             ? ( '-Env' => $params{'-Env'}->env )
147             : () ) );
148             punlock("$fn.lock");
149             }
150              
151             if ( defined $db ) {
152              
153             # Remove the Env from %params to avoid circular references
154             delete $params{'-Env'};
155              
156             # Store the metadata
157             $dbp{$fn} = {%params};
158             $dbh{$fn} = $db;
159             $pid{$fn} = $$;
160             $dbc{$fn} = 1;
161              
162             } else {
163             Paranoid::ERROR =
164             pdebug( 'failed to open database: %s %s',
165             PDLEVEL1, $!, $BerkeleyDB::Error );
166             }
167             }
168             }
169              
170             $obj->set( 'filename', $fn ) if defined $db;
171              
172             pOut();
173             pdebug( 'leaving w/rv: %s', PDLEVEL2, $db );
174              
175             return $db;
176             }
177              
178             sub _closeDb {
179              
180             # Purpose: Closes db or decrements counter
181             # Returns: Boolean
182             # Usage: $rv = _closeDb($filename);
183              
184             my $fn = shift;
185              
186             pdebug( 'entering w/%s', PDLEVEL2, $fn );
187             pIn();
188              
189             if ( defined $fn and exists $dbh{$fn} ) {
190             if ( $dbc{$fn} == 1 ) {
191             pdebug( 'closing out database %s', PDLEVEL4, $dbh{$fn} );
192             {
193             no warnings;
194             $dbh{$fn}->db_sync;
195             $dbh{$fn}->db_close;
196             }
197             delete $dbh{$fn};
198             delete $dbp{$fn};
199             delete $dbc{$fn};
200             delete $pid{$fn};
201             } else {
202             pdebug( 'decrementing ref count for %s', PDLEVEL4,
203             $dbh{$fn} );
204             $dbc{$fn}--;
205             }
206             }
207              
208             pOut();
209             pdebug( 'leaving w/rv: 1', PDLEVEL2 );
210              
211             return 1;
212             }
213              
214             sub filename {
215              
216             # Purpose: Returns canonical filename
217             # Returns: String
218             # Usage: $fn = $obj->filename;
219              
220             my $obj = shift;
221             my $rv;
222              
223             pdebug( 'entering', PDLEVEL1 );
224             pIn();
225              
226             if ( !$obj->isStale ) {
227              
228             $rv = $obj->get('filename');
229              
230             croak pdebug( 'object opened under a different pid (%s)',
231             PDLEVEL1, $pid{$rv} )
232             if defined $rv and $pid{$rv} != $$;
233              
234             } else {
235             carp pdebug( 'filename method called on stale object', PDLEVEL1 );
236             }
237              
238             pOut();
239             pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv );
240              
241             return $rv;
242             }
243              
244             sub params {
245              
246             # Purpose: Returns parameter hash
247             # Returns: Hash
248             # Usage: %params = $obj->params;
249              
250             my $obj = shift;
251             my ( $fn, %rv );
252              
253             pdebug( 'entering', PDLEVEL1 );
254             pIn();
255              
256             if ( !$obj->isStale ) {
257             $fn = $obj->filename;
258             %rv = %{ $dbp{$fn} };
259              
260             croak pdebug( 'object opened under a different pid (%s)',
261             PDLEVEL1, $pid{$fn} )
262             if $pid{$fn} != $$;
263              
264             } else {
265             carp pdebug( 'params method called on stale object', PDLEVEL1 );
266             }
267              
268             pOut();
269             pdebug( 'leaving w/rv: %s', PDLEVEL1, %rv );
270              
271             return %rv;
272             }
273              
274             sub refc {
275              
276             # Purpose: Returns reference count for underlying database
277             # Returns: Integer
278             # Usage: $ount = $obj->refc;
279              
280             my $obj = shift;
281             my ( $fn, $rv );
282              
283             pdebug( 'entering', PDLEVEL1 );
284             pIn();
285              
286             if ( !$obj->isStale ) {
287             $fn = $obj->filename;
288             $rv = $dbc{$fn};
289              
290             croak pdebug( 'object opened under a different pid (%s)',
291             PDLEVEL1, $pid{$fn} )
292             if $pid{$fn} != $$;
293              
294             } else {
295             carp pdebug( 'refc method called on stale object', PDLEVEL1 );
296             }
297              
298             pOut();
299             pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv );
300              
301             return $rv;
302             }
303              
304             sub dbh {
305              
306             # Purpose: Returns a reference to db
307             # Returns: Ref
308             # Usage: $dbh = $obj->dbh;
309              
310             my $obj = shift;
311             my ( $fn, $rv );
312              
313             pdebug( 'entering', PDLEVEL1 );
314             pIn();
315              
316             if ( !$obj->isStale ) {
317             $fn = $obj->filename;
318             $rv = $dbh{$fn};
319              
320             croak pdebug( 'object opened under a different pid (%s)',
321             PDLEVEL1, $pid{$fn} )
322             if $pid{$fn} != $$;
323              
324             } else {
325             pdebug( 'dbh method called on stale object', PDLEVEL1 );
326             }
327              
328             pOut();
329             pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv );
330              
331             return $rv;
332             }
333             }
334              
335             sub _initialize {
336             my $obj = shift;
337             my %params = @_;
338             my ( $db, $env, $rv );
339              
340             # Make sure minimal parameters are preset
341             pdebug( 'entering', PDLEVEL1 );
342             pIn();
343              
344             if ( exists $params{'-Filename'} ) {
345             $db = _openDb( $obj, %params );
346              
347             if ( defined $db ) {
348              
349             # Adopt the database
350             $env = $params{'-Env'};
351             $env->adopt($obj)
352             if defined $env
353             and $env->isa('Paranoid::BerkeleyDB::Env');
354             delete $params{'-Env'};
355              
356             $rv = 1;
357             }
358              
359             } else {
360             Paranoid::ERROR =
361             pdebug( 'caller didn\'t specify -Filename', PDLEVEL1 );
362             }
363              
364             pOut();
365             pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv );
366              
367             return $rv;
368             }
369              
370             sub _deconstruct {
371             my $obj = shift;
372             my ( $env, $db, $rv );
373              
374             pdebug( 'entering', PDLEVEL1 );
375             pIn();
376              
377             # Close database
378             $rv = _closeDb( $obj->filename ) if !$obj->isStale;
379              
380             pOut();
381             pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv );
382              
383             return $rv;
384             }
385              
386             1;
387              
388             __END__