line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
=head1 NAME |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
XBase::SDBM - SDBM index support for dbf |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 DESCRIPTION |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
When developing the XBase.pm/DBD::XBase module, I was trying to |
9
|
|
|
|
|
|
|
support as many existing variants of file formats as possible. The |
10
|
|
|
|
|
|
|
module thus accepts wide range of dbf files and their versions from |
11
|
|
|
|
|
|
|
various producers. But with index files, the task is much, much |
12
|
|
|
|
|
|
|
harder. First, there is little or no documentation of index files |
13
|
|
|
|
|
|
|
formats, so the development is based on reverse engineering. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
None if the index formats support is finalized. That made it hard to |
16
|
|
|
|
|
|
|
integrate them into one consistent API. That is why I decided to write |
17
|
|
|
|
|
|
|
my own index support, and as I wanted to avoid inventing yet another |
18
|
|
|
|
|
|
|
way of storing records in pages and similar things, I used SDBM. It |
19
|
|
|
|
|
|
|
comes with Perl, so you already have it, and it's proven and it |
20
|
|
|
|
|
|
|
works. |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Now, SDBM is a module that aims at other task than to do supporting |
23
|
|
|
|
|
|
|
indexes for a dbf. But equality tests are fast with it and I have |
24
|
|
|
|
|
|
|
creted a structure in each index file to enable "walk" though the |
25
|
|
|
|
|
|
|
index file. |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 VERSION |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
1.02 |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 AVAILABLE FROM |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
http://www.adelton.com/perl/DBD-XBase/ |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 AUTHOR |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
(c) 2001--2011 Jan Pazdziora. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
All rights reserved. This package is free software; you can |
40
|
|
|
|
|
|
|
redistribute it and/or modify it under the same terms as Perl itself. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=cut |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
package XBase::SDBM; |
45
|
1
|
|
|
1
|
|
1606
|
use SDBM_File; |
|
1
|
|
|
|
|
4489
|
|
|
1
|
|
|
|
|
50
|
|
46
|
1
|
|
|
1
|
|
9
|
use Fcntl; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
3681
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub fetch { |
49
|
85
|
|
|
85
|
0
|
151
|
my $self = shift; |
50
|
85
|
|
|
|
|
384
|
my $current = $self->{'current'}; # current pointer |
51
|
85
|
100
|
|
|
|
186
|
return unless defined $current; |
52
|
82
|
|
|
|
|
390
|
my $hash = $self->{'sdbmhash'}; |
53
|
82
|
|
|
|
|
1082
|
my $value = $hash->{$current}; |
54
|
|
|
|
|
|
|
|
55
|
82
|
50
|
|
|
|
227
|
if (not defined $value) { |
56
|
0
|
|
|
|
|
0
|
delete $self->{'current'}; |
57
|
0
|
|
|
|
|
0
|
return; |
58
|
|
|
|
|
|
|
} |
59
|
82
|
|
|
|
|
2164
|
my ($key, $num) = ($current =~ /^(.*):(\d+)$/s); |
60
|
82
|
|
|
|
|
115
|
$num++; |
61
|
82
|
50
|
|
|
|
972
|
if (defined $hash->{"$key:$num"}) { # next record for the same key |
62
|
0
|
|
|
|
|
0
|
$self->{'current'} = "$key:$num"; |
63
|
|
|
|
|
|
|
} else { |
64
|
82
|
|
|
|
|
860
|
my $newkey = $hash->{"$key:next"}; # next key |
65
|
82
|
100
|
|
|
|
196
|
if (defined $newkey) { |
66
|
79
|
|
|
|
|
191
|
$self->{'current'} = "$newkey:1"; |
67
|
|
|
|
|
|
|
} else { |
68
|
3
|
|
|
|
|
13
|
delete $self->{'current'}; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
82
|
|
|
|
|
468
|
return ($key, $value); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
sub fetch_current { |
74
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
75
|
0
|
|
|
|
|
0
|
my $current = $self->{'current'}; |
76
|
0
|
0
|
|
|
|
0
|
return unless defined $current; |
77
|
0
|
|
|
|
|
0
|
my $value = $self->{'sdbmhash'}{$current}; |
78
|
0
|
0
|
|
|
|
0
|
return unless defined $value; |
79
|
0
|
|
|
|
|
0
|
my ($key) = ($current =~ /^(.*):\d+$/s); |
80
|
0
|
|
|
|
|
0
|
return ($key, $value); |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
sub tags { |
83
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
84
|
0
|
0
|
|
|
|
0
|
return map { if (s/:file$//) { ( $_ ) } else { () } } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
85
|
0
|
|
|
|
|
0
|
keys %{$self->{'definition'}}; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub prepare_select { |
89
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
90
|
1
|
|
|
|
|
8
|
$self->{'current'} = $self->{'sdbmhash'}{':first'}; |
91
|
1
|
50
|
|
|
|
6
|
$self->{'current'} .= ':1' if defined $self->{'current'}; |
92
|
1
|
|
|
|
|
5
|
1; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
sub prepare_select_eq { |
95
|
2
|
|
|
2
|
0
|
6
|
my ($self, $eq, $recno) = @_; |
96
|
2
|
|
|
|
|
5
|
delete $self->{'current'}; |
97
|
2
|
|
|
|
|
14
|
my $hash = $self->{'sdbmhash'}; |
98
|
2
|
|
|
|
|
4
|
my $start = $eq; |
99
|
2
|
|
|
|
|
29
|
my $value = $hash->{"$start:1"}; |
100
|
|
|
|
|
|
|
|
101
|
2
|
100
|
|
|
|
10
|
if (not defined $value) { |
102
|
|
|
|
|
|
|
# not exact match |
103
|
1
|
|
|
|
|
12
|
$start = $hash->{':first'}; |
104
|
1
|
50
|
|
|
|
6
|
if (not defined $start) { |
105
|
|
|
|
|
|
|
# no records, jsut return |
106
|
0
|
|
|
|
|
0
|
return 1; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
# move throught the chain |
109
|
1
|
|
66
|
|
|
9
|
while (defined $start and $start lt $eq) { |
110
|
22
|
|
|
|
|
271
|
$start = $hash->{"$start:next"}; |
111
|
|
|
|
|
|
|
} |
112
|
1
|
50
|
|
|
|
4
|
if (not defined $start) { |
113
|
0
|
|
|
|
|
0
|
return 1; |
114
|
|
|
|
|
|
|
} |
115
|
1
|
50
|
|
|
|
5
|
if ($start gt $eq) { |
116
|
1
|
|
|
|
|
16
|
$self->{'current'} = "$start:1"; |
117
|
1
|
|
|
|
|
5
|
return 1; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
# we shouldn't have never got here, but nevermind |
120
|
0
|
|
|
|
|
0
|
$value = $hash->{"$start:1"}; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# here we've found exact match of the key |
124
|
1
|
50
|
|
|
|
5
|
if (not defined $recno) { |
125
|
|
|
|
|
|
|
# if not requested exact match of the recno, return |
126
|
1
|
|
|
|
|
4
|
$self->{'current'} = "$start:1"; |
127
|
1
|
|
|
|
|
4
|
return 1; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
0
|
my $num = 1; |
131
|
0
|
|
0
|
|
|
0
|
while (defined $value and $value != $recno) { |
132
|
0
|
|
|
|
|
0
|
$num++; |
133
|
0
|
|
|
|
|
0
|
$value = $hash->{"$start:$num"}; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
0
|
0
|
|
|
|
0
|
if (defined $value) { |
137
|
0
|
|
|
|
|
0
|
$self->{'current'} = "$start:$num"; |
138
|
|
|
|
|
|
|
} else { |
139
|
0
|
|
|
|
|
0
|
$start = $hash->{"$start:next"}; |
140
|
0
|
0
|
|
|
|
0
|
$self->{'current'} = "$start:1" if defined $start; |
141
|
|
|
|
|
|
|
} |
142
|
0
|
|
|
|
|
0
|
1; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# method new (open) will open the named SDBM index for given dbf |
147
|
|
|
|
|
|
|
sub new { |
148
|
1
|
|
|
1
|
0
|
5
|
my ($class, $filename, %opts) = @_; |
149
|
1
|
|
|
|
|
2
|
my $dbf = $opts{'dbf'}; |
150
|
1
|
|
|
|
|
2
|
my $tag = $opts{'tag'}; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# return immediatelly if the index file was already opened |
153
|
1
|
50
|
33
|
|
|
21
|
return $dbf->{'sdbm_definition'}{'tags'}{$tag} |
154
|
|
|
|
|
|
|
if defined $dbf->{'sdbm_definition'} |
155
|
|
|
|
|
|
|
and defined $dbf->{'sdbm_definition'}{'tags'}{$tag}; |
156
|
|
|
|
|
|
|
|
157
|
0
|
|
|
|
|
0
|
my $dbffile = $dbf->{'filename'}; |
158
|
0
|
|
|
|
|
0
|
my $file = $dbffile; |
159
|
0
|
|
|
|
|
0
|
$file =~ s/\.dbf$/.sdbmd/i; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# some of the SDBM indexes were already touched |
162
|
|
|
|
|
|
|
# the definitionhash is a SDBM that lists the content of the |
163
|
|
|
|
|
|
|
# actual SDBM index files |
164
|
0
|
|
|
|
|
0
|
my $definitionhash = {}; |
165
|
0
|
0
|
|
|
|
0
|
if (defined $dbf->{'sdbm_definition'}) { |
166
|
0
|
|
|
|
|
0
|
$definitionhash = $dbf->{'sdbm_definition'}{'definitionhash'}; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
else { |
169
|
|
|
|
|
|
|
# if it wasn't opened yet, open the definition file |
170
|
0
|
0
|
|
|
|
0
|
if (not tie(%$definitionhash, 'SDBM_File', |
171
|
|
|
|
|
|
|
$file, O_RDWR, 0666)) { |
172
|
0
|
|
|
|
|
0
|
die "SDBM index definition file `$file' not found for `$dbffile': $!."; |
173
|
|
|
|
|
|
|
} |
174
|
0
|
|
|
|
|
0
|
$dbf->{'sdbm_definition'} = { 'filename' => $file, |
175
|
|
|
|
|
|
|
'definitionhash' => $definitionhash }; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# check the definition file for tag requested |
179
|
0
|
|
|
|
|
0
|
my $sdbmfile = $definitionhash->{"$tag:file"}; |
180
|
0
|
0
|
|
|
|
0
|
if (not defined $sdbmfile) { |
181
|
|
|
|
|
|
|
# no such SDBM index exists, the definition SDBM says |
182
|
0
|
|
|
|
|
0
|
die "SDBM index `$tag' not known for `$dbffile'."; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# open the SDBM index file |
186
|
0
|
|
|
|
|
0
|
my $sdbmhash = {}; |
187
|
0
|
0
|
|
|
|
0
|
unless (tie(%$sdbmhash, 'SDBM_File', $sdbmfile, O_RDWR, 0666)) { |
188
|
0
|
|
|
|
|
0
|
die "SDBM index file `$sdbmfile' not found for `$dbffile': $!."; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
0
|
|
|
|
|
0
|
my $self = bless { 'dbf' => $dbf, |
192
|
|
|
|
|
|
|
'tag' => $tag, 'sdbmhash' => $sdbmhash, |
193
|
|
|
|
|
|
|
'definition' => $definitionhash }, $class; |
194
|
0
|
|
|
|
|
0
|
$dbf->{'sdbm_definition'}{'tags'}{$tag} = $self; |
195
|
0
|
|
|
|
|
0
|
return $self; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
*open = \&new; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# method create will create SDBM index with given name and expression |
200
|
|
|
|
|
|
|
# for the dbf table |
201
|
|
|
|
|
|
|
sub create { |
202
|
1
|
|
|
1
|
0
|
271
|
my ($class, $dbf, $tag, $expression) = @_; |
203
|
1
|
|
|
|
|
3
|
my $dbffile = $dbf->{'filename'}; |
204
|
1
|
|
|
|
|
2
|
my $file; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
my $definitionhash; |
207
|
1
|
50
|
|
|
|
3
|
if (defined $dbf->{'sdbm_definition'}) { |
208
|
|
|
|
|
|
|
# the definition SDBM was already opened |
209
|
0
|
|
|
|
|
0
|
$definitionhash = $dbf->{'sdbm_definition'}{'definitionhash'}; |
210
|
|
|
|
|
|
|
} else { |
211
|
1
|
|
|
|
|
2
|
$file = $dbffile; |
212
|
1
|
|
|
|
|
7
|
$file =~ s/\.dbf$/.sdbmd/i; |
213
|
|
|
|
|
|
|
|
214
|
1
|
|
|
|
|
2
|
$definitionhash = {}; |
215
|
|
|
|
|
|
|
# open or create the definition SDBM file |
216
|
1
|
50
|
|
|
|
155
|
if (not tie(%$definitionhash, 'SDBM_File', |
217
|
|
|
|
|
|
|
$file, O_RDWR|O_CREAT, 0666)) { |
218
|
0
|
|
|
|
|
0
|
die "SDBM index definition file `$file' not found/created for `$dbffile': $!."; |
219
|
|
|
|
|
|
|
} |
220
|
1
|
|
|
|
|
12
|
$dbf->{'sdbm_definition'} = { 'filename' => $file, |
221
|
|
|
|
|
|
|
'definitionhash' => $definitionhash }; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
1
|
50
|
|
|
|
28
|
if (defined $definitionhash->{"$tag:file"}) { |
225
|
0
|
|
|
|
|
0
|
die "SDBM index `$tag' already exists for `$dbfffile'."; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
1
|
|
|
|
|
54
|
my $maxindexnumber = ++$definitionhash->{'tagnumber'}; |
229
|
|
|
|
|
|
|
|
230
|
1
|
|
|
|
|
3
|
my $sdbmfile = $dbffile; |
231
|
1
|
|
|
|
|
8
|
$sdbmfile =~ s/\.dbf$/.sdbm$maxindexnumber/i; |
232
|
|
|
|
|
|
|
|
233
|
1
|
|
|
|
|
2
|
my $sdbmhash = {}; |
234
|
1
|
50
|
|
|
|
105
|
if (not tie(%$sdbmhash, 'SDBM_File', $sdbmfile, O_CREAT|O_EXCL|O_RDWR, 0666)) { |
235
|
0
|
|
|
|
|
0
|
die "SDBM index file `$sdbmfile' couldn't be created for `$dbffile': $!." |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
1
|
|
|
|
|
15
|
my $self = bless { 'dbf' => $dbf, 'tag' => $tag, |
239
|
|
|
|
|
|
|
'sdbmhash' => $sdbmhash, |
240
|
|
|
|
|
|
|
'definition' => $definitionhash}, $class; |
241
|
1
|
|
|
|
|
4
|
$dbf->{'sdbm_definition'}{'tags'}{$tag} = $self; |
242
|
1
|
|
|
|
|
16
|
$definitionhash->{"$tag:file"} = $sdbmfile; |
243
|
|
|
|
|
|
|
|
244
|
1
|
50
|
|
|
|
8
|
if (defined $dbf->field_type(uc $expression)) { |
245
|
1
|
|
|
|
|
2
|
$expression = uc $expression; |
246
|
|
|
|
|
|
|
} |
247
|
1
|
50
|
|
|
|
5
|
if (not defined $dbf->field_type($expression)) { |
248
|
0
|
|
|
|
|
0
|
$self->drop; |
249
|
0
|
|
|
|
|
0
|
die "SDBM index `$expression' couldn't be created for `$dbffile': no such column name."; |
250
|
|
|
|
|
|
|
} |
251
|
1
|
|
|
|
|
16
|
$definitionhash->{"$tag:expression"} = $expression; |
252
|
|
|
|
|
|
|
|
253
|
1
|
|
|
|
|
4
|
my $i = 0; |
254
|
1
|
|
|
|
|
5
|
while ($i <= $dbf->last_record) { |
255
|
42
|
|
|
|
|
114
|
my ($deleted, $value) = $dbf->get_record($i); |
256
|
42
|
50
|
|
|
|
87
|
if (not $deleted) { |
257
|
42
|
|
|
|
|
93
|
$self->insert($value, $i + 1); |
258
|
|
|
|
|
|
|
} |
259
|
42
|
|
|
|
|
132
|
$i++; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
1
|
|
|
|
|
7
|
return $self; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# method drop will drop the SDBM index |
266
|
|
|
|
|
|
|
sub drop { |
267
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_; |
268
|
0
|
|
|
|
|
0
|
my $tag = $self->{'tag'}; |
269
|
0
|
|
|
|
|
0
|
my $definitionhash = $self->{'definition'}; |
270
|
0
|
|
|
|
|
0
|
my $sdbmfile = $definitionhash->{"$tag:file"}; |
271
|
0
|
|
|
|
|
0
|
delete $definitionhash->{"$tag:file"}; |
272
|
0
|
|
|
|
|
0
|
delete $definitionhash->{"$tag:definition"}; |
273
|
0
|
|
|
|
|
0
|
delete $self->{'dbf'}{'sdbm_definition'}{'tags'}{$tag}; |
274
|
0
|
|
|
|
|
0
|
unlink "$sdbmfile.pag", "$sdbmfile.dir"; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub insert { |
278
|
42
|
|
|
42
|
0
|
63
|
my ($self, $key, $value) = @_; |
279
|
|
|
|
|
|
|
### print "Adding $key $value\n"; |
280
|
42
|
|
|
|
|
58
|
my $hash = $self->{'sdbmhash'}; |
281
|
42
|
|
|
|
|
288
|
my $key_maxid = $hash->{"$key:0"}; |
282
|
42
|
|
|
|
|
75
|
$key_maxid++; |
283
|
|
|
|
|
|
|
|
284
|
42
|
|
|
|
|
850
|
$hash->{"$key:$key_maxid"} = $value; |
285
|
42
|
|
|
|
|
652
|
$hash->{"$key:0"} = $key_maxid; |
286
|
42
|
50
|
|
|
|
105
|
return 1 if $key_maxid > 1; # no need to change the chain |
287
|
|
|
|
|
|
|
|
288
|
42
|
|
|
|
|
48
|
my $prev = undef; |
289
|
42
|
|
|
|
|
46
|
my $prev_next = ':first'; |
290
|
42
|
|
|
|
|
37
|
my $next; |
291
|
42
|
|
100
|
|
|
442
|
while (defined($next = $hash->{$prev_next}) and $key gt $next) { |
292
|
591
|
|
|
|
|
943
|
$prev = $next; |
293
|
591
|
|
|
|
|
632
|
$prev_next = "$prev:next"; |
294
|
591
|
|
|
|
|
6284
|
$next = undef; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
42
|
100
|
|
|
|
96
|
if (not defined $next) { |
298
|
10
|
|
|
|
|
129
|
$hash->{':last'} = $key; # we reached the last record |
299
|
|
|
|
|
|
|
} else { |
300
|
32
|
|
|
|
|
537
|
$hash->{"$key:next"} = $next; |
301
|
32
|
|
|
|
|
481
|
$hash->{"$next:prev"} = $key; |
302
|
|
|
|
|
|
|
} |
303
|
42
|
100
|
|
|
|
89
|
if (not defined $prev) { |
304
|
2
|
|
|
|
|
26
|
$hash->{':first'} = $key; |
305
|
|
|
|
|
|
|
} else { |
306
|
40
|
|
|
|
|
562
|
$hash->{"$prev:next"} = $key; |
307
|
40
|
|
|
|
|
596
|
$hash->{"$key:prev"} = $prev; |
308
|
|
|
|
|
|
|
} |
309
|
42
|
|
|
|
|
110
|
return 1; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub delete { |
313
|
0
|
|
|
0
|
0
|
|
my ($self, $key, $value) = @_; |
314
|
|
|
|
|
|
|
### print "Deleting $key $value\n"; |
315
|
0
|
|
|
|
|
|
my $hash = $self->{'sdbmhash'}; |
316
|
0
|
|
|
|
|
|
my $key_maxid = $hash->{"$key:0"}; |
317
|
|
|
|
|
|
|
|
318
|
0
|
|
|
|
|
|
my $number = 1; |
319
|
0
|
|
|
|
|
|
while ($number <= $key_maxid) { |
320
|
0
|
0
|
|
|
|
|
if ($hash->{"$key:$number"} == $value) { |
321
|
0
|
|
|
|
|
|
last; |
322
|
|
|
|
|
|
|
} |
323
|
0
|
|
|
|
|
|
$number++; |
324
|
|
|
|
|
|
|
} |
325
|
0
|
0
|
|
|
|
|
if ($number > $key_maxid) { |
326
|
|
|
|
|
|
|
# such a record was not found |
327
|
0
|
|
|
|
|
|
return 0; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
0
|
0
|
|
|
|
|
if ($key_maxid > 1) { |
331
|
0
|
0
|
|
|
|
|
$hash->{"$key:$number"} = $hash->{"$key:$key_maxid"} |
332
|
|
|
|
|
|
|
if $number != $key_maxid; |
333
|
0
|
|
|
|
|
|
delete $hash->{"$key:$key_maxid"}; |
334
|
0
|
|
|
|
|
|
$hash->{"$key:0"} = $key_maxid - 1; |
335
|
|
|
|
|
|
|
} else { |
336
|
0
|
|
|
|
|
|
my $next = $hash->{"$key:next"}; |
337
|
0
|
|
|
|
|
|
my $prev = $hash->{"$key:prev"}; |
338
|
0
|
0
|
|
|
|
|
if (defined $next) { |
339
|
0
|
0
|
|
|
|
|
if (not defined $prev) { |
340
|
0
|
|
|
|
|
|
$hash->{':first'} = $next; |
341
|
0
|
|
|
|
|
|
delete $hash->{"$next:prev"}; |
342
|
|
|
|
|
|
|
} else { |
343
|
0
|
|
|
|
|
|
$hash->{"$prev:next"} = $next; |
344
|
0
|
|
|
|
|
|
$hash->{"$next:prev"} = $prev; |
345
|
0
|
|
|
|
|
|
delete $hash->{"$key:prev"}; |
346
|
|
|
|
|
|
|
} |
347
|
0
|
|
|
|
|
|
delete $hash->{"$key:next"}; |
348
|
|
|
|
|
|
|
} else { |
349
|
0
|
0
|
|
|
|
|
if (not defined $prev) { |
350
|
0
|
|
|
|
|
|
delete $hash->{':first'}; |
351
|
0
|
|
|
|
|
|
delete $hash->{':last'}; |
352
|
|
|
|
|
|
|
} else { |
353
|
0
|
|
|
|
|
|
$hash->{':last'} = $prev; |
354
|
0
|
|
|
|
|
|
delete $hash->{"$prev:next"}; |
355
|
0
|
|
|
|
|
|
delete $hash->{"$key:prev"}; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
} |
358
|
0
|
|
|
|
|
|
delete $hash->{"$key:0"}; |
359
|
0
|
|
|
|
|
|
delete $hash->{"$key:1"}; |
360
|
|
|
|
|
|
|
} |
361
|
0
|
|
|
|
|
|
return 1; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
sub delete_current { |
364
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
365
|
0
|
|
|
|
|
|
my ($key, $value) = $self->fetch_current; |
366
|
0
|
0
|
|
|
|
|
if (defined $value) { |
367
|
0
|
|
|
|
|
|
$self->delete($key, $value); |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
sub insert_before_current { |
371
|
0
|
|
|
0
|
0
|
|
die "SDBM index doesn't support backward rolling yet.\n"; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub dump { |
375
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
376
|
0
|
|
|
|
|
|
my $hash = $self->{'sdbmhash'}; |
377
|
|
|
|
|
|
|
|
378
|
0
|
|
|
|
|
|
for (sort keys %$hash) { |
379
|
0
|
|
|
|
|
|
print "$_ $hash->{$_}\n"; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
1; |
384
|
|
|
|
|
|
|
|