File Coverage

blib/lib/BerkeleyDB/Easy.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package BerkeleyDB::Easy;
2            
3 2     2   55511 use strict;
  2         4  
  2         67  
4 2     2   11 use warnings;
  2         4  
  2         50  
5 2     2   3111 use BerkeleyDB ();
  0            
  0            
6            
7             our $VERSION = '0.06';
8            
9             sub import {
10             my @args = @_;
11            
12             # TODO: process options and set compile-time globals before
13             # loading any other files
14            
15             require BerkeleyDB;
16             BerkeleyDB->export_to_level(1, @args);
17            
18             require BerkeleyDB::Easy::Error;
19             BerkeleyDB::Easy::Error->export_to_level(1, @args);
20             }
21            
22             # This is the frontend module that you `use` in your code
23             # There are two ways to construct a BTREE handle:
24             # 1 BerkeleyDB::Easy->new(Type => BerkeleyDB::DB_BTREE);
25             # 2 BerkeleyDB::Easy::Btree->new();
26             # This is following the interface of BerkeleyDB.pm. To allow the second
27             # type of constructor, we provide those subclasses here.
28            
29             sub new {
30             shift;
31             require BerkeleyDB::Easy::Handle;
32             BerkeleyDB::Easy::Handle->new(@_);
33             }
34            
35             # --------------------------------------------------------
36            
37             package BerkeleyDB::Easy::Btree;
38             our @ISA = qw(BerkeleyDB::Easy::Handle);
39            
40             sub new {
41             require BerkeleyDB;
42             require BerkeleyDB::Easy::Handle;
43             shift->SUPER::_new(@_, Type => BerkeleyDB::DB_BTREE());
44             }
45            
46             # --------------------------------------------------------
47            
48             package BerkeleyDB::Easy::Hash;
49             our @ISA = qw(BerkeleyDB::Easy::Handle);
50            
51             sub new {
52             require BerkeleyDB;
53             require BerkeleyDB::Easy::Handle;
54             shift->SUPER::_new(@_, Type => BerkeleyDB::DB_HASH());
55             }
56            
57             # --------------------------------------------------------
58            
59             package BerkeleyDB::Easy::Recno;
60             our @ISA = qw(BerkeleyDB::Easy::Handle);
61            
62             sub new {
63             require BerkeleyDB;
64             require BerkeleyDB::Easy::Handle;
65             shift->SUPER::_new(@_, Type => BerkeleyDB::DB_RECNO());
66             }
67            
68             # --------------------------------------------------------
69            
70             package BerkeleyDB::Easy::Queue;
71             our @ISA = qw(BerkeleyDB::Easy::Handle);
72            
73             sub new {
74             require BerkeleyDB;
75             require BerkeleyDB::Easy::Handle;
76             shift->SUPER::_new(@_, Type => BerkeleyDB::DB_QUEUE());
77             }
78            
79             # --------------------------------------------------------
80            
81             package BerkeleyDB::Easy::Heap;
82             our @ISA = qw(BerkeleyDB::Easy::Handle);
83            
84             sub new {
85             require BerkeleyDB;
86             require BerkeleyDB::Easy::Handle;
87             shift->SUPER::_new(@_, Type => BerkeleyDB::DB_HEAP());
88             }
89            
90             # --------------------------------------------------------
91            
92             package BerkeleyDB::Easy::Unknown;
93             our @ISA = qw(BerkeleyDB::Easy::Handle);
94            
95             sub new {
96             require BerkeleyDB;
97             require BerkeleyDB::Easy::Handle;
98             shift->SUPER::_new(@_, Type => BerkeleyDB::DB_UNKNOWN());
99             }
100            
101             # --------------------------------------------------------
102            
103             1;
104            
105             =encoding utf8
106            
107             =head1 NAME
108            
109             BerkeleyDB::Easy - BerkeleyDB wrapper with Perlish interface and error handling
110            
111             =head1 SYNOPSIS
112            
113             my $db = BerkeleyDB::Easy::Btree->new(
114             -Filename => 'test.db',
115             -Flags => DB_CREATE,
116             );
117            
118             $db->put('foo', 'bar');
119            
120             my $foo = $db->get('foo');
121            
122             my $cur = $db->cursor;
123            
124             while (my ($key, $val) = $cur->next) {
125             $db->del($key);
126             }
127            
128             $db->sync;
129            
130             =head1 DESCRIPTION
131            
132             BerkeleyDB::Easy is a convenience wrapper around BerkeleyDB.pm. It will
133             reduce the amount of boilerplate you have to write, with special focus
134             on comprehensive and customizable error handling and logging, with minimal
135             overhead.
136            
137             =head1 ERRORS
138            
139             When using BerkeleyDB, errors can be generated at many levels. The OS,
140             the Perl interpreter, the BDB C library, and the BerkeleyDB.pm module.
141             Each of these need to be handled via different mechanisms, which can be
142             quite a headache. This module attempts to consolidate and automate error
143             handling at all these levels, so you don't have to think about it.
144            
145             Errors are thrown as a versatile structured exception object. It is overloaded
146             to stringify as a concise message, numberify into an error code, and has various
147             methods for detailed handling.
148            
149             use BerkeleyDB::Easy;
150            
151             my $db = BerkeleyDB::Easy::Btree->new();
152             my $err;
153            
154             use Try::Tiny;
155             try { $db->get('asdf', 666) } catch { $err = $_ };
156            
157             use feature 'say';
158             say $err;
159            
160             # [BerkeleyDB::Easy::Handle::get] EINVAL (22): Invalid argument.
161             # DB_READ_COMMITTED, DB_READ_UNCOMMITTED and DB_RMW require locking
162             # at error.pl line 16.
163            
164             say 0 + $err;
165            
166             # 22
167            
168             use Data::Dump;
169             dd $err;
170            
171             # bless({
172             # code => 22,
173             # desc => "Invalid argument",
174             # detail => "DB_READ_COMMITTED, DB_READ_UNCOMMITTED and DB_RMW require locking",
175             # file => "error.pl",
176             # level => "BDB_ERROR",
177             # line => 16,
178             # message => "Invalid argument. DB_READ_COMMITTED, DB_READ_UNCOMMITTED and "
179             # . "DB_RMW require locking",
180             # name => "EINVAL",
181             # package => "main",
182             # string => "[BerkeleyDB::Easy::Handle::get] EINVAL (22): Invalid argument. "
183             # . "DB_READ_COMMITTED, DB_READ_UNCOMMITTED and DB_RMW require locking "
184             # . "at error.pl line 16.",
185             # sub => "BerkeleyDB::Easy::Handle::get",
186             # time => 1409926665.1101,
187             # trace => "at error.pl line 16.",
188             # }, "BerkeleyDB::Easy::Error")
189            
190             =head1 IMPLEMENTATION
191            
192             Wrapper methods are dynamically generated according to a declarative specification
193             combined with user-configurable options. This way, dozens of (very similar)
194             methods can be created without copy and paste coding, and features can be
195             compiled in or out based on your criteria. By tailoring each wrapper to the
196             underlying BerkeleyDB function and offering an optimization parameter,
197             each wrapper uses the minimum number of ops to provide as little overhead as
198             possible.
199            
200             For example, here is the specification for BerkeleyDB::Easy::Handle::put()
201            
202             ['db_put',[K,V,F],[K,V,F],[V],[],0,0]
203            
204             The following fields are defined:
205            
206             0 FUNC : the underlying BerkeleyDB.pm function we are wrapping
207             1 RECV : parameters to our wrapper, passed by the end user
208             2 SEND : arguments we call FUNC with, often carried thru from RECV
209             3 SUCC : what to return on success
210             4 FAIL : what to return on failure
211             5 OPTI : integer specifying optimization level
212             6 FLAG : default flag to FUNC
213            
214             As well as these single-letter aliases:
215            
216             K $key | R $return | X $x
217             V $value | S $status | Y $y
218             F $flags | T 1 ('True') | Z $z
219             A @_ ('All') | N '' ('Nope') | U undef
220            
221             And so our wrapper delcaration expands to the following code:
222            
223             sub put {
224             my @err;
225             local ($!, $^E);
226             local $SIG{__DIE__} = sub { @err = (BDB_FATAL, $_) };
227             local $SIG{__WARN__} = sub { @err = (BDB_WARN, $_) };
228             undef $BerkeleyDB::Error;
229             my ($self, $key, $value, $flags) = @_;
230             my $status = BerkeleyDB::Common::db_put($self, $key, $value, $flags);
231             $self->_log(@err) if @err;
232             if ($status) {
233             $self->_throw($status);
234             return();
235             }
236             return($value);
237             }
238            
239             In BerkeleyDB version < 4.6, there is no C, so we fake it:
240            
241             ['db_get',[K,F],[K,V,F],[T],[N],1,0]
242            
243             Here, the optimization flag has been set to true. This results in:
244            
245             sub exists {
246             undef $BerkeleyDB::Error;
247             my ($self, $key, $flags) = @_;
248             my ($value);
249             my $status = BerkeleyDB::Common::db_get($self, $key, $value, $flags);
250             if ($status) {
251             $self->_throw($status, undef, 1);
252             return('');
253             }
254             return(1);
255             }
256            
257             You can see that some (not all) of the error-checking has been compiled out.
258             Namely, we are no longer catching warnings and exceptions from BerkeleyDB
259             and only checking the status of its return value. This is normally enough
260             to catch any errors from the module, as it will usually only die in special
261             circumstances, so it would be reasonable to compile out these (expensive)
262             extra checks if performance were important.
263            
264             You can also see the difference between how the two methods operate. C
265             takes a key and value, and returns the value upon success and C on failure.
266             C takes only a key and returns C<1> on success and an empty string on
267             failure. Currently over 30 methods are defined this way, using a single line
268             of code each. See the documentation for C and
269             C for a full listing.
270            
271             =head1 BUGS
272            
273             This module is functional but unfinished.
274            
275             =head1 AUTHOR
276            
277             Rob Schaber, C<< >>
278            
279             =head1 LICENSE
280            
281             Copyright 2013 Rob Schaber.
282            
283             This program is free software; you can redistribute it and/or modify it
284             under the terms of either: the GNU General Public License as published
285             by the Free Software Foundation; or the Artistic License.
286            
287             See L for more information.