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.
|