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