line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
2
|
|
|
|
|
|
|
package FlatFile::DataStore::DBM; |
3
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
FlatFile::DataStore::DBM - Perl module that implements a flatfile |
8
|
|
|
|
|
|
|
datastore with a DBM file key access. |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 SYNOPSYS |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
use Fctnl; |
13
|
|
|
|
|
|
|
use FlatFile::DataStore::DBM; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
$FlatFile::DataStore::DBM::dbm_package = "SDBM_File"; # the defaults |
16
|
|
|
|
|
|
|
$FlatFile::DataStore::DBM::dbm_parms = [ O_CREAT|O_RDWR, 0666 ]; |
17
|
|
|
|
|
|
|
$FlatFile::DataStore::DBM::dbm_lock_ext = ".dir"; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# new object |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $obj = tie my %dshash, 'FlatFile::DataStore::DBM', { |
22
|
|
|
|
|
|
|
name => "dsname", |
23
|
|
|
|
|
|
|
dir => "/my/datastore/directory", |
24
|
|
|
|
|
|
|
}; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# create a record and retrieve it |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my $id = "testrec1"; |
29
|
|
|
|
|
|
|
my $record = $dshash{ $id } = { data => "Test record", user => "Test user data" }; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# update it |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
$record->data( "Updating the test record." ); |
34
|
|
|
|
|
|
|
$dshash{ $id } = $record; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# delete it |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
delete $dshash{ $id }; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# get its history |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
my @records = $obj->history( $id ); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 DESCRIPTION |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
FlatFile::DataStore::DBM implements a tied hash interface to a |
47
|
|
|
|
|
|
|
flatfile datastore. The hash keys are strings that you provide. |
48
|
|
|
|
|
|
|
These keys do not necessarily have to exist as data in the record. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
In the case of delete, you're limited in the tied interface -- you |
51
|
|
|
|
|
|
|
can't supply a "delete record" (one that has information about the |
52
|
|
|
|
|
|
|
delete operation). Instead, it will simply retrieve the existing |
53
|
|
|
|
|
|
|
record and store that as the delete record. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Record data may be created or updated (i.e., STORE'd) three ways: |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
As a data string (or scalar reference), e.g., |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
$record = $dshash{ $id } = $record_data; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
As a hash reference, e.g. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
$record = $dshash{ $id } = { data => $record_data, user => $user_data }; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
As a record object (record data and user data gotten from object), |
66
|
|
|
|
|
|
|
e.g., |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
$record->data( $record_data ); |
69
|
|
|
|
|
|
|
$record->user( $user_data ); |
70
|
|
|
|
|
|
|
$record = $dshash{ $id } = $record; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
In the last line above, the object fetched is not the same as |
73
|
|
|
|
|
|
|
the one given to be stored (it has a different preamble). |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
FWIW, this module is not a subclass of FlatFile::DataStore. Instead, |
76
|
|
|
|
|
|
|
it is a wrapper, so it's a "has a" relationship rather than an "is a" |
77
|
|
|
|
|
|
|
one. But many of the public flatfile methods are available via the |
78
|
|
|
|
|
|
|
tied object, as illustrated by the history() call in the synopsis. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
These methods include |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
name |
83
|
|
|
|
|
|
|
dir |
84
|
|
|
|
|
|
|
retrieve |
85
|
|
|
|
|
|
|
retrieve_preamble |
86
|
|
|
|
|
|
|
locate_record_data |
87
|
|
|
|
|
|
|
history |
88
|
|
|
|
|
|
|
userdata |
89
|
|
|
|
|
|
|
howmany |
90
|
|
|
|
|
|
|
lastkeynum |
91
|
|
|
|
|
|
|
nextkeynum |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Note that create(), update(), and delete() are not included in this |
94
|
|
|
|
|
|
|
list. If a datastore is set up using this module, all updates to its |
95
|
|
|
|
|
|
|
data should use this module. This will keep the keys in sync with |
96
|
|
|
|
|
|
|
the data. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head1 VERSION |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
FlatFile::DataStore::DBM version 1.03 |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=cut |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
our $VERSION = '1.03'; |
105
|
|
|
|
|
|
|
|
106
|
2
|
|
|
2
|
|
109727
|
use 5.008003; |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
114
|
|
107
|
2
|
|
|
2
|
|
12
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
89
|
|
108
|
2
|
|
|
2
|
|
12
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
107
|
|
109
|
|
|
|
|
|
|
|
110
|
2
|
|
|
2
|
|
12
|
use Fcntl qw(:DEFAULT :flock); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
1186
|
|
111
|
2
|
|
|
2
|
|
14
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
127
|
|
112
|
|
|
|
|
|
|
|
113
|
2
|
|
|
2
|
|
2259
|
use FlatFile::DataStore; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
6952
|
|
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head1 DESCRIPTION |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 Tieing the hash |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Accepts hash ref giving values for C and C. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
tie my %dshash, 'FlatFile::DataStore::DBM', { |
124
|
|
|
|
|
|
|
name => $name, |
125
|
|
|
|
|
|
|
dir => $dir, |
126
|
|
|
|
|
|
|
}; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
To initialize a new datastore, pass the URI as the value of the |
129
|
|
|
|
|
|
|
C parameter, e.g., |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
tie my %dshash, 'FlatFile::DataStore::DBM', { |
132
|
|
|
|
|
|
|
dir => $dir, |
133
|
|
|
|
|
|
|
name => $name, |
134
|
|
|
|
|
|
|
uri => join( ";" => |
135
|
|
|
|
|
|
|
"http://example.com?name=$name", |
136
|
|
|
|
|
|
|
"desc=My%20Data%20Store", |
137
|
|
|
|
|
|
|
"defaults=medium", |
138
|
|
|
|
|
|
|
"user=8-%20-%7E", |
139
|
|
|
|
|
|
|
"recsep=%0A", |
140
|
|
|
|
|
|
|
), |
141
|
|
|
|
|
|
|
}; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
(See URI Configuration in FlatFile::DataStore.) |
144
|
|
|
|
|
|
|
Also accepts a C parameter, which sets the default user |
145
|
|
|
|
|
|
|
data for this instance. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Returns a reference to the FlatFile::DataStore::DBM object. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head2 Object Methods |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head3 get_key( $keynum ); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Gets the key associated with a record sequence number (keynum). |
156
|
|
|
|
|
|
|
This could be handy if you have a record, but don't have its key |
157
|
|
|
|
|
|
|
in the DBM file, e.g., |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# have a record to update, but forgot its key |
160
|
|
|
|
|
|
|
# (the key isn't necessarily in the record) |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
my $id = tied(%dshash)->get_key( $record->keynum ); |
163
|
|
|
|
|
|
|
$dshash{ $id } = $record; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=cut |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub get_key { |
168
|
2
|
|
|
2
|
1
|
1274
|
my( $self, $keynum ) = @_; |
169
|
|
|
|
|
|
|
|
170
|
2
|
100
|
66
|
|
|
213
|
croak qq/Not a keynum: $keynum/ |
171
|
|
|
|
|
|
|
unless defined $keynum and $keynum =~ /^[0-9]+$/; |
172
|
|
|
|
|
|
|
|
173
|
1
|
|
|
|
|
5
|
my $ds = $self->datastore; |
174
|
1
|
|
|
|
|
5
|
my $dir = $ds->dir; |
175
|
1
|
|
|
|
|
6
|
my $name = $ds->name; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# lock the dbm file and read the key |
178
|
1
|
|
|
|
|
4
|
$self->readlock; |
179
|
1
|
50
|
|
|
|
4
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
1
|
|
|
|
|
8
|
|
180
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
181
|
|
|
|
|
|
|
|
182
|
1
|
|
|
|
|
18
|
my $key = $dbm_hash{ "_$keynum" }; |
183
|
|
|
|
|
|
|
|
184
|
1
|
|
|
|
|
21
|
untie %dbm_hash; |
185
|
1
|
|
|
|
|
4
|
$self->unlock; |
186
|
|
|
|
|
|
|
|
187
|
1
|
|
|
|
|
4
|
$key; # returned |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head3 get_keynum( $key ); |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Gets the record sequence number (keynum) associated with a key. Don't |
195
|
|
|
|
|
|
|
have a good use case yet -- included this method as a complement to |
196
|
|
|
|
|
|
|
get_key(). |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub get_keynum { |
201
|
2
|
|
|
2
|
1
|
18
|
my( $self, $key ) = @_; |
202
|
|
|
|
|
|
|
|
203
|
2
|
100
|
|
|
|
187
|
croak qq/Unsupported key format: $key/ if $key =~ /^_[0-9]+$/; |
204
|
|
|
|
|
|
|
|
205
|
1
|
|
|
|
|
3
|
my $ds = $self->datastore; |
206
|
1
|
|
|
|
|
4
|
my $dir = $ds->dir; |
207
|
1
|
|
|
|
|
3
|
my $name = $ds->name; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# lock the dbm file and read the keynum |
210
|
1
|
|
|
|
|
4
|
$self->readlock; |
211
|
1
|
50
|
|
|
|
4
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
1
|
|
|
|
|
3
|
|
212
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
213
|
|
|
|
|
|
|
|
214
|
1
|
|
|
|
|
13
|
my $keynum = $dbm_hash{ $key }; |
215
|
|
|
|
|
|
|
|
216
|
1
|
|
|
|
|
16
|
untie %dbm_hash; |
217
|
1
|
|
|
|
|
3
|
$self->unlock; |
218
|
|
|
|
|
|
|
|
219
|
1
|
|
|
|
|
4
|
$keynum; # returned |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
223
|
|
|
|
|
|
|
# accessors |
224
|
|
|
|
|
|
|
# the following are required attributes, so simple accessors are okay |
225
|
|
|
|
|
|
|
# |
226
|
|
|
|
|
|
|
# Private methods. |
227
|
|
|
|
|
|
|
|
228
|
49
|
50
|
|
49
|
0
|
148
|
sub datastore {for($_[0]->{datastore }){$_=$_[1]if@_>1;return$_}} |
|
49
|
|
|
|
|
129
|
|
|
49
|
|
|
|
|
140
|
|
229
|
74
|
100
|
|
74
|
0
|
185
|
sub locked {for($_[0]->{locked }){$_=$_[1]if@_>1;return$_}} |
|
74
|
|
|
|
|
195
|
|
|
74
|
|
|
|
|
178
|
|
230
|
74
|
50
|
|
74
|
0
|
220
|
sub dbm_lock_file {for($_[0]->{dbm_lock_file}){$_=$_[1]if@_>1;return$_}} |
|
74
|
|
|
|
|
160
|
|
|
74
|
|
|
|
|
154
|
|
231
|
37
|
50
|
|
37
|
0
|
88
|
sub dbm_package {for($_[0]->{dbm_package }){$_=$_[1]if@_>1;return$_}} |
|
37
|
|
|
|
|
98
|
|
|
37
|
|
|
|
|
128
|
|
232
|
37
|
50
|
|
37
|
0
|
94
|
sub dbm_parms {for($_[0]->{dbm_parms }){$_=$_[1]if@_>1;return$_}} |
|
37
|
|
|
|
|
85
|
|
|
37
|
|
|
|
|
1954
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
235
|
|
|
|
|
|
|
# globals |
236
|
|
|
|
|
|
|
# |
237
|
|
|
|
|
|
|
# These are read in TIEHASH(). They may be changed prior to calling |
238
|
|
|
|
|
|
|
# tie(), e.g., |
239
|
|
|
|
|
|
|
# |
240
|
|
|
|
|
|
|
# my $ds_parms = { name => $ds_name, dir => $ds_dir }; |
241
|
|
|
|
|
|
|
# $FlatFile::DataStore::DBM::dbm_parms = [ O_RDONLY, 0666 ]; |
242
|
|
|
|
|
|
|
# |
243
|
|
|
|
|
|
|
# tie my %hash, "FlatFile::DataStore::DBM", $ds_parms; |
244
|
|
|
|
|
|
|
# |
245
|
|
|
|
|
|
|
# ... or different values may be passed to tie() using a hash |
246
|
|
|
|
|
|
|
# reference as the second parameter, e.g., |
247
|
|
|
|
|
|
|
# |
248
|
|
|
|
|
|
|
# my $ds_parms = { name => $ds_name, dir => $ds_dir }; |
249
|
|
|
|
|
|
|
# my $dbm_specs = { dbm_parms => [ O_RDONLY, 0666 ] } |
250
|
|
|
|
|
|
|
# |
251
|
|
|
|
|
|
|
# tie my %hash, "FlatFile::DataStore::DBM", $ds_parms, $dbm_specs; |
252
|
|
|
|
|
|
|
# |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
our $dbm_package = "SDBM_File"; |
255
|
|
|
|
|
|
|
our $dbm_parms = [ O_CREAT|O_RDWR, 0666 ]; |
256
|
|
|
|
|
|
|
our $dbm_lock_ext = ".dir"; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
259
|
|
|
|
|
|
|
# TIEHASH() supports tied hash access |
260
|
|
|
|
|
|
|
# |
261
|
|
|
|
|
|
|
# Coding note: in TIEHASH(), the object attributes are set directly in |
262
|
|
|
|
|
|
|
# the hash. In all the other subs the above accessors are used. |
263
|
|
|
|
|
|
|
# |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub TIEHASH { |
266
|
9
|
|
|
9
|
|
10868
|
my( $class, $ds_parms, $dbm_specs ) = @_; |
267
|
|
|
|
|
|
|
|
268
|
9
|
|
|
|
|
75
|
my $ds = FlatFile::DataStore->new( $ds_parms ); |
269
|
8
|
|
|
|
|
26
|
my $dir = $ds->dir; |
270
|
8
|
|
|
|
|
28
|
my $name = $ds->name; |
271
|
|
|
|
|
|
|
|
272
|
8
|
|
|
|
|
61
|
my $self = { |
273
|
|
|
|
|
|
|
datastore => $ds, |
274
|
|
|
|
|
|
|
dbm_package => $dbm_package, # may be changed by dbm_specs |
275
|
|
|
|
|
|
|
dbm_parms => $dbm_parms, # " |
276
|
|
|
|
|
|
|
dbm_lock_ext => $dbm_lock_ext, # " |
277
|
|
|
|
|
|
|
}; |
278
|
8
|
50
|
|
|
|
29
|
if( $dbm_specs ) { |
279
|
0
|
|
|
|
|
0
|
$self->{ $_ } = $dbm_specs->{ $_ } for keys %$dbm_specs; |
280
|
|
|
|
|
|
|
} |
281
|
8
|
|
|
|
|
40
|
$self->{'dbm_lock_file'} = "$dir/$name$self->{'dbm_lock_ext'}"; |
282
|
|
|
|
|
|
|
|
283
|
8
|
50
|
|
|
|
832
|
eval qq{require $self->{'dbm_package'}; 1} |
284
|
|
|
|
|
|
|
or croak qq/Can't use $self->{'dbm_package'}: $@/; |
285
|
|
|
|
|
|
|
|
286
|
8
|
|
|
|
|
81
|
bless $self, $class; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
290
|
|
|
|
|
|
|
# FETCH() supports tied hash access |
291
|
|
|
|
|
|
|
# Returns a FlatFile::DataStore::Record object. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub FETCH { |
294
|
17
|
|
|
17
|
|
108
|
my( $self, $key ) = @_; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# block efforts to fetch a "_keynum" entry |
297
|
17
|
100
|
|
|
|
262
|
croak qq/Unsupported key format: $key/ if $key =~ /^_[0-9]+$/; |
298
|
|
|
|
|
|
|
|
299
|
16
|
|
|
|
|
51
|
my $ds = $self->datastore; |
300
|
16
|
|
|
|
|
57
|
my $dir = $ds->dir; |
301
|
16
|
|
|
|
|
53
|
my $name = $ds->name; |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# lock the dbm file and read the keynum |
304
|
16
|
|
|
|
|
55
|
$self->readlock; |
305
|
16
|
50
|
|
|
|
54
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
16
|
|
|
|
|
48
|
|
306
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
307
|
|
|
|
|
|
|
|
308
|
16
|
|
|
|
|
235
|
my $keynum = $dbm_hash{ $key }; |
309
|
|
|
|
|
|
|
|
310
|
16
|
|
|
|
|
288
|
untie %dbm_hash; |
311
|
16
|
|
|
|
|
45
|
$self->unlock; |
312
|
|
|
|
|
|
|
|
313
|
16
|
50
|
|
|
|
40
|
return unless defined $keynum; |
314
|
16
|
|
|
|
|
74
|
$ds->retrieve( $keynum ); # retrieve and return record |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
318
|
|
|
|
|
|
|
# STORE() supports tied hash access |
319
|
|
|
|
|
|
|
# Returns a FlatFile::DataStore::Record object. |
320
|
|
|
|
|
|
|
# |
321
|
|
|
|
|
|
|
# to help with FIRSTKEY/NEXTKEY, we're keeping two entries |
322
|
|
|
|
|
|
|
# in the dbm file for every record: |
323
|
|
|
|
|
|
|
# 1. record id => key sequence number |
324
|
|
|
|
|
|
|
# 2. key sequence number => record id |
325
|
|
|
|
|
|
|
# |
326
|
|
|
|
|
|
|
# to avoid collisions with numeric keys, the key of the second |
327
|
|
|
|
|
|
|
# entry has an underscore pasted on to the front, e.g., a record |
328
|
|
|
|
|
|
|
# whose id is "able_baker_charlie" and whose keynum is 257 would |
329
|
|
|
|
|
|
|
# have these entries: |
330
|
|
|
|
|
|
|
# 1. able_baker_charlie => 257 |
331
|
|
|
|
|
|
|
# 2. _257 => able_baker_charlie |
332
|
|
|
|
|
|
|
# |
333
|
|
|
|
|
|
|
# Note: the $error variable is intended to avoid having a croak |
334
|
|
|
|
|
|
|
# between writelock() and unlock(). On linux systems that don't |
335
|
|
|
|
|
|
|
# allow a process to have multiple locks on the same file, if you |
336
|
|
|
|
|
|
|
# trap those croaks in an eval{} (like for testing), the program |
337
|
|
|
|
|
|
|
# will hang waiting for a lock. |
338
|
|
|
|
|
|
|
# |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub STORE { |
341
|
18
|
|
|
18
|
|
3167
|
my( $self, $key, $parms ) = @_; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# block efforts to store to "_keynum" entries |
344
|
18
|
100
|
|
|
|
230
|
croak qq/Unsupported key format: $key/ if $key =~ /^_[0-9]+$/; |
345
|
|
|
|
|
|
|
|
346
|
17
|
|
|
|
|
61
|
my $ds = $self->datastore; |
347
|
17
|
|
|
|
|
60
|
my $dir = $ds->dir; |
348
|
17
|
|
|
|
|
59
|
my $name = $ds->name; |
349
|
|
|
|
|
|
|
|
350
|
17
|
|
|
|
|
29
|
my $error; |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# lock the dbm file and read the keynum |
353
|
17
|
|
|
|
|
59
|
$self->writelock; |
354
|
17
|
50
|
|
|
|
55
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
17
|
|
|
|
|
50
|
|
355
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
356
|
|
|
|
|
|
|
|
357
|
17
|
|
|
|
|
294
|
my $keynum = $dbm_hash{ $key }; |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
# $parms may be record, href, sref, or string |
360
|
17
|
|
|
|
|
53
|
my $reftype = ref $parms; |
361
|
|
|
|
|
|
|
|
362
|
17
|
|
|
|
|
33
|
my $record; # to be returned |
363
|
|
|
|
|
|
|
|
364
|
17
|
100
|
|
|
|
49
|
if( defined $keynum ) { # update |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# record data string |
367
|
4
|
50
|
33
|
|
|
46
|
if( !$reftype or $reftype eq "SCALAR" ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
368
|
0
|
|
|
|
|
0
|
$record = $ds->retrieve( $keynum ); # read it |
369
|
0
|
|
|
|
|
0
|
$record->data( $parms ); # update it |
370
|
0
|
|
|
|
|
0
|
$record = $ds->update( $record ); # write it |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# record object |
374
|
|
|
|
|
|
|
elsif( $reftype =~ /Record/ ) { |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# trying to update a record using the wrong key? |
377
|
2
|
100
|
|
|
|
11
|
if( $keynum != $parms->keynum ) { |
378
|
1
|
|
|
|
|
3
|
$error = qq/Record key number doesn't match key/; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
else { |
381
|
1
|
|
|
|
|
6
|
$record = $ds->update( $parms ); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# hash, e.g., {data=>'record data',user=>'user data'} |
386
|
|
|
|
|
|
|
elsif( $reftype eq 'HASH' ) { |
387
|
1
|
50
|
|
|
|
9
|
$parms->{'record'} = $ds->retrieve( $keynum ) unless $parms->{'record'}; |
388
|
1
|
|
|
|
|
7
|
$record = $ds->update( $parms ); |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
else { |
392
|
1
|
|
|
|
|
4
|
$error = qq/Unsupported ref type: $reftype/; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
else { # create |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# record data string |
400
|
13
|
100
|
66
|
|
|
137
|
if( !$reftype or $reftype eq "SCALAR" ) { |
|
|
100
|
100
|
|
|
|
|
401
|
1
|
|
|
|
|
7
|
$record = $ds->create({ data => $parms }); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# record object or hash, e.g., |
405
|
|
|
|
|
|
|
# { data => 'record data', user => 'user data' } |
406
|
|
|
|
|
|
|
elsif( $reftype =~ /Record/ or |
407
|
|
|
|
|
|
|
$reftype eq 'HASH' ) { |
408
|
11
|
|
|
|
|
61
|
$record = $ds->create( $parms ); |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
else { |
412
|
1
|
|
|
|
|
4
|
$error = qq/Unsupported ref type: $reftype/; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# create succeeded, let's store the key |
416
|
13
|
100
|
|
|
|
40
|
unless( $error ) { |
417
|
12
|
|
|
|
|
54
|
for( $record->keynum ) { |
418
|
12
|
|
|
|
|
383
|
$dbm_hash{ $key } = $_; |
419
|
12
|
|
|
|
|
208
|
$dbm_hash{ "_$_" } = $key; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
17
|
|
|
|
|
354
|
untie %dbm_hash; |
425
|
17
|
|
|
|
|
67
|
$self->unlock; |
426
|
|
|
|
|
|
|
|
427
|
17
|
100
|
|
|
|
451
|
croak $error if $error; |
428
|
|
|
|
|
|
|
|
429
|
14
|
|
|
|
|
149
|
$record; # returned |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
434
|
|
|
|
|
|
|
# DELETE() supports tied hash access |
435
|
|
|
|
|
|
|
# Returns a FlatFile::DataStore::Record object. |
436
|
|
|
|
|
|
|
# |
437
|
|
|
|
|
|
|
# Otherwise, we must have a record to delete one, so we retrieve |
438
|
|
|
|
|
|
|
# it first. |
439
|
|
|
|
|
|
|
# |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
sub DELETE { |
442
|
1
|
|
|
1
|
|
3
|
my( $self, $key ) = @_; |
443
|
|
|
|
|
|
|
|
444
|
1
|
|
|
|
|
6
|
my $ds = $self->datastore; |
445
|
1
|
|
|
|
|
6
|
my $dir = $ds->dir; |
446
|
1
|
|
|
|
|
4
|
my $name = $ds->name; |
447
|
|
|
|
|
|
|
|
448
|
1
|
|
|
|
|
5
|
$self->writelock; |
449
|
1
|
50
|
|
|
|
5
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
1
|
|
|
|
|
5
|
|
450
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
451
|
|
|
|
|
|
|
|
452
|
1
|
|
|
|
|
3
|
my $exists; |
453
|
|
|
|
|
|
|
my $record; |
454
|
|
|
|
|
|
|
|
455
|
1
|
50
|
|
|
|
20
|
if( $exists = exists $dbm_hash{ $key } ) { |
456
|
|
|
|
|
|
|
|
457
|
1
|
|
|
|
|
7
|
my $keynum = $dbm_hash{ $key }; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# must have a record to delete it |
460
|
1
|
|
|
|
|
8
|
$record = $ds->retrieve( $keynum ); |
461
|
1
|
|
|
|
|
8
|
$record = $ds->delete( $record ); |
462
|
|
|
|
|
|
|
|
463
|
1
|
|
|
|
|
31
|
delete $dbm_hash{ $key }; |
464
|
1
|
|
|
|
|
16
|
delete $dbm_hash{ "_$keynum" }; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
1
|
|
|
|
|
23
|
untie %dbm_hash; |
468
|
1
|
|
|
|
|
6
|
$self->unlock; |
469
|
|
|
|
|
|
|
|
470
|
1
|
50
|
|
|
|
5
|
return unless $exists; |
471
|
1
|
|
|
|
|
8
|
$record; # return the "delete record" |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
475
|
|
|
|
|
|
|
# CLEAR() supports tied hash access |
476
|
|
|
|
|
|
|
# except we don't support CLEAR, because it would be very |
477
|
|
|
|
|
|
|
# destructive and it would be a pain to recover from an |
478
|
|
|
|
|
|
|
# accidental %h = (); |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
sub CLEAR { |
481
|
1
|
|
|
1
|
|
199
|
croak qq/Clearing the entire datastore is not supported/; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
485
|
|
|
|
|
|
|
# FIRSTKEY() supports tied hash access |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub FIRSTKEY { |
488
|
0
|
|
|
0
|
|
0
|
my( $self ) = @_; |
489
|
|
|
|
|
|
|
|
490
|
0
|
|
|
|
|
0
|
my $ds = $self->datastore; |
491
|
0
|
|
|
|
|
0
|
my $dir = $ds->dir; |
492
|
0
|
|
|
|
|
0
|
my $name = $ds->name; |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# lock the dbm file and read the first key (stored as '_0') |
495
|
0
|
|
|
|
|
0
|
$self->readlock; |
496
|
0
|
0
|
|
|
|
0
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
0
|
|
|
|
|
0
|
|
497
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
498
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
0
|
my $firstkey = $dbm_hash{ '_0' }; |
500
|
|
|
|
|
|
|
|
501
|
0
|
|
|
|
|
0
|
untie %dbm_hash; |
502
|
0
|
|
|
|
|
0
|
$self->unlock; |
503
|
|
|
|
|
|
|
|
504
|
0
|
|
|
|
|
0
|
$firstkey; # returned, might be undef |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
508
|
|
|
|
|
|
|
# NEXTKEY() supports tied hash access |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
sub NEXTKEY { |
511
|
0
|
|
|
0
|
|
0
|
my( $self, $prevkey ) = @_; |
512
|
|
|
|
|
|
|
|
513
|
0
|
|
|
|
|
0
|
my $ds = $self->datastore; |
514
|
0
|
|
|
|
|
0
|
my $dir = $ds->dir; |
515
|
0
|
|
|
|
|
0
|
my $name = $ds->name; |
516
|
|
|
|
|
|
|
|
517
|
0
|
|
|
|
|
0
|
my $nextkey; |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# lock the dbm file and get the prev key's keynum |
520
|
0
|
|
|
|
|
0
|
$self->readlock; |
521
|
0
|
0
|
|
|
|
0
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
0
|
|
|
|
|
0
|
|
522
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
523
|
|
|
|
|
|
|
|
524
|
0
|
|
|
|
|
0
|
my $keynum = $dbm_hash{ $prevkey }; |
525
|
|
|
|
|
|
|
|
526
|
0
|
0
|
|
|
|
0
|
if( $keynum++ < $ds->lastkeynum ) { |
527
|
0
|
|
|
|
|
0
|
$nextkey = $dbm_hash{ "_$keynum" }; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
0
|
|
|
|
|
0
|
untie %dbm_hash; |
531
|
0
|
|
|
|
|
0
|
$self->unlock; |
532
|
|
|
|
|
|
|
|
533
|
0
|
|
|
|
|
0
|
$nextkey; # returned, might be undef |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
537
|
|
|
|
|
|
|
# SCALAR() supports tied hash access |
538
|
|
|
|
|
|
|
# Here we're bypassing the dbm file altogether and simply getting |
539
|
|
|
|
|
|
|
# the number of non-deleted records in the datastore. This |
540
|
|
|
|
|
|
|
# should be the same as the number of (logical) entries in the |
541
|
|
|
|
|
|
|
# dbm hash. |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
sub SCALAR { |
544
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
545
|
0
|
|
|
|
|
0
|
$self->datastore->howmany; # create|update (not deletes) |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
549
|
|
|
|
|
|
|
# EXISTS() supports tied hash access |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub EXISTS { |
552
|
2
|
|
|
2
|
|
24
|
my( $self, $key ) = @_; |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# block efforts to look at a "_keynum" entry |
555
|
2
|
100
|
|
|
|
191
|
croak qq/Unsupported key format: $key/ if $key =~ /^_[0-9]+$/; |
556
|
|
|
|
|
|
|
|
557
|
1
|
|
|
|
|
4
|
my $ds = $self->datastore; |
558
|
1
|
50
|
|
|
|
5
|
return unless $ds->exists; |
559
|
|
|
|
|
|
|
|
560
|
1
|
|
|
|
|
5
|
my $dir = $ds->dir; |
561
|
1
|
|
|
|
|
5
|
my $name = $ds->name; |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# lock the dbm file and call exists on dbm hash |
564
|
1
|
|
|
|
|
5
|
$self->readlock; |
565
|
1
|
50
|
|
|
|
4
|
tie my %dbm_hash, $self->dbm_package, "$dir/$name", @{$self->dbm_parms} |
|
1
|
|
|
|
|
4
|
|
566
|
|
|
|
|
|
|
or die "Can't tie dbm hash: $!"; |
567
|
|
|
|
|
|
|
|
568
|
1
|
|
|
|
|
13
|
my $exists = exists $dbm_hash{ $key }; |
569
|
|
|
|
|
|
|
|
570
|
1
|
|
|
|
|
19
|
untie %dbm_hash; |
571
|
1
|
|
|
|
|
4
|
$self->unlock; |
572
|
|
|
|
|
|
|
|
573
|
1
|
50
|
|
|
|
11
|
return unless $exists; |
574
|
0
|
|
|
|
|
0
|
$exists; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
578
|
|
|
|
|
|
|
# UNTIE() supports tied hash access |
579
|
|
|
|
|
|
|
# (see perldoc perltie, The "untie" Gotcha) |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
sub UNTIE { |
582
|
0
|
|
|
0
|
|
0
|
my( $self, $count ) = @_; |
583
|
0
|
0
|
|
|
|
0
|
carp "untie attempted while $count inner references still exist" if $count; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
0
|
|
|
0
|
|
0
|
sub DESTROY {} # to keep from calling AUTOLOAD |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
589
|
|
|
|
|
|
|
# readlock() |
590
|
|
|
|
|
|
|
# Takes a file name, opens it for input, locks it, and stores the |
591
|
|
|
|
|
|
|
# open file handle in the object. This file handle isn't really |
592
|
|
|
|
|
|
|
# used except for locking, so it's bit of a "lock token" |
593
|
|
|
|
|
|
|
# |
594
|
|
|
|
|
|
|
# Private method. |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
sub readlock { |
597
|
19
|
|
|
19
|
0
|
33
|
my( $self ) = @_; |
598
|
|
|
|
|
|
|
|
599
|
19
|
|
|
|
|
47
|
my $file = $self->dbm_lock_file; |
600
|
19
|
|
|
|
|
30
|
my $fh; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# open $fh, '<', $file or croak qq/Can't open for read $file: $!/; |
603
|
19
|
50
|
|
|
|
707
|
sysopen( $fh, $file, O_RDONLY|O_CREAT ) or croak qq/Can't open for read $file: $!/; |
604
|
19
|
50
|
|
|
|
139
|
flock $fh, LOCK_SH or croak qq/Can't lock shared $file: $!/; |
605
|
19
|
|
|
|
|
40
|
binmode $fh; |
606
|
|
|
|
|
|
|
|
607
|
19
|
|
|
|
|
49
|
$self->locked( $fh ); |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
611
|
|
|
|
|
|
|
# writelock() |
612
|
|
|
|
|
|
|
# Takes a file name, opens it for read/write, locks it, and |
613
|
|
|
|
|
|
|
# stores the open file handle in the object. |
614
|
|
|
|
|
|
|
# |
615
|
|
|
|
|
|
|
# Private method. |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub writelock { |
618
|
18
|
|
|
18
|
0
|
30
|
my( $self ) = @_; |
619
|
|
|
|
|
|
|
|
620
|
18
|
|
|
|
|
72
|
my $file = $self->dbm_lock_file; |
621
|
18
|
|
|
|
|
28
|
my $fh; |
622
|
|
|
|
|
|
|
|
623
|
18
|
50
|
|
|
|
1092
|
sysopen( $fh, $file, O_RDWR|O_CREAT ) or croak qq/Can't open for read-write $file: $!/; |
624
|
18
|
|
|
|
|
82
|
my $ofh = select( $fh ); $| = 1; select ( $ofh ); # flush buffers |
|
18
|
|
|
|
|
45
|
|
|
18
|
|
|
|
|
48
|
|
625
|
18
|
50
|
|
|
|
173
|
flock $fh, LOCK_EX or croak qq/Can't lock exclusive $file: $!/; |
626
|
18
|
|
|
|
|
38
|
binmode $fh; |
627
|
|
|
|
|
|
|
|
628
|
18
|
|
|
|
|
67
|
$self->locked( $fh ); |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
632
|
|
|
|
|
|
|
# unlock() |
633
|
|
|
|
|
|
|
# closes the file handle -- the "lock token" in the object |
634
|
|
|
|
|
|
|
# |
635
|
|
|
|
|
|
|
# Private method. |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
sub unlock { |
638
|
37
|
|
|
37
|
0
|
59
|
my( $self ) = @_; |
639
|
|
|
|
|
|
|
|
640
|
37
|
|
|
|
|
98
|
my $file = $self->dbm_lock_file; |
641
|
37
|
|
|
|
|
108
|
my $fh = $self->locked; |
642
|
|
|
|
|
|
|
|
643
|
37
|
50
|
|
|
|
456
|
close $fh or croak qq/Problem closing $file: $!/; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
647
|
|
|
|
|
|
|
our $AUTOLOAD; |
648
|
|
|
|
|
|
|
sub AUTOLOAD { |
649
|
|
|
|
|
|
|
|
650
|
13
|
|
|
13
|
|
5663
|
my $method = $AUTOLOAD; |
651
|
13
|
|
|
|
|
70
|
$method =~ s/.*:://; |
652
|
13
|
|
|
|
|
26
|
for( $method ) { |
653
|
13
|
100
|
|
|
|
255
|
croak qq/Unsupported method: $_/ unless /^ |
654
|
|
|
|
|
|
|
name |
655
|
|
|
|
|
|
|
|dir |
656
|
|
|
|
|
|
|
|retrieve |
657
|
|
|
|
|
|
|
|retrieve_preamble |
658
|
|
|
|
|
|
|
|locate_record_data |
659
|
|
|
|
|
|
|
|history |
660
|
|
|
|
|
|
|
|userdata |
661
|
|
|
|
|
|
|
|howmany |
662
|
|
|
|
|
|
|
|lastkeynum |
663
|
|
|
|
|
|
|
|nextkeynum |
664
|
|
|
|
|
|
|
$/x; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
12
|
|
|
|
|
21
|
my $self = shift; |
668
|
12
|
|
|
|
|
33
|
$self->datastore->$method( @_ ); |
669
|
|
|
|
|
|
|
} |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
1; # returned |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
__END__ |