Work on Dbm stuff coninues.. Not done yet though..
[onis.git] / lib / Onis / Data / Persistent / Dbm.pm
1 package Onis::Data::Persistent::Dbm;
2
3 use strict;
4 use warnings;
5
6 BEGIN
7 {
8         @AnyDBM_File::ISA = (qw(GDBM_File DB_File GDBM_File SDBM_File NDBM_File ODBM_File));
9 }
10
11 use Carp qw(carp confess);
12 use Fcntl (qw(O_RDWR O_CREAT));
13 use AnyDBM_File;
14
15 use Onis::Config (qw(get_config));
16
17 =head1 NAME
18
19 Onis::Data::Persistent::Dbm - Storage backend using AnyDBM_File.
20
21 =head1 DESCRIPTION
22
23 Storage backend that uses DBM files for storing data permanently.
24
25 =head1 CONFIGURATION OPTIONS
26
27 =over 4
28
29 =item B<dbm_directory>: I<E<lt>dirE<gt>>
30
31 Directory in which the DBM-files are kept.
32
33 =back
34
35 =cut
36
37 our $DBMDirectory = get_config ('dbm_directory') || 'var';
38 $DBMDirectory =~ s#/$##g;
39
40 if (!$DBMDirectory or !-d $DBMDirectory)
41 {
42         print STDERR <<ERROR;
43 The directory ``$DBMDirectory'' does not exist or is not useable. Please
44 create it before running onis.
45 ERROR
46         exit (1);
47 }
48
49 our $Alarm = chr (7);
50 our %Objects = ();
51
52 if ($::DEBUG & 0x0200)
53 {
54         require Data::Dumper;
55 }
56
57 return (1);
58
59 sub new
60 {
61         my $pkg    = shift;
62         my $name   = shift;
63         my $key    = shift;
64         my @fields = @_;
65         my $caller = caller ();
66         my $obj    = {};
67         my %hash;
68         my $i = 0;
69         my $filename;
70         
71         my $id = $caller . ':' . $name;
72         $id =~ s#/##g;
73
74         $filename = "$DBMDirectory/$id.dbm";
75         
76         if (exists ($Objects{$id}))
77         {
78                 print STDERR $/, __FILE__, ": Name $name has been used in context $caller before.";
79                 return (undef);
80         }
81
82         no strict (qw(subs));
83         tie (%hash, 'AnyDBM_File', $filename, O_RDWR | O_CREAT, 0666) or die ("tie: $!");
84
85         $obj->{'data'} = tied %hash;
86         $obj->{'key'} = $key;
87         $obj->{'fields'} = [@fields];
88         $obj->{'num_fields'} = scalar (@fields);
89         $obj->{'field_index'} = {map { $_ => $i++ } (@fields)};
90         $obj->{'id'} = $id;
91         $obj->{'cache'} = {};
92
93         if ($::DEBUG & 0x0200)
94         {
95                 my $prefix = __FILE__ . ': ';
96                 my $dbg = Data::Dumper->Dump ([$obj], ['obj']);
97                 $dbg =~ s/^/$prefix/mg; chomp ($dbg);
98                 print STDOUT $/, $dbg;
99         }
100         
101         $Objects{$id} = bless ($obj, $pkg);
102         return ($Objects{$id});
103 }
104
105 sub put
106 {
107         my $obj    = shift;
108         my $key    = shift;
109         my @fields = @_;
110         my $db = $obj->{'data'};
111
112         if ($obj->{'num_fields'} != scalar (@fields))
113         {
114                 my $id = $obj->{'id'};
115                 carp ("Number of fields do not match ($id).");
116                 return;
117         }
118
119         if ($::DEBUG & 0x0200)
120         {
121                 print STDOUT $/, __FILE__, ': PUT(', $obj->{'id'}, ', ', $key, ') = (' . join (', ', @fields) . ')';
122         }
123
124         $obj->{'cache'}{$key} = [@fields];
125 }
126
127 sub get
128 {
129         my $obj = shift;
130         my $key = shift;
131         my $val;
132         my @ret;
133         my $db = $obj->{'data'};
134
135         if (!exists ($obj->{'cache'}{$key}))
136         {
137                 $val = $db->FETCH ($key);
138                 if (!defined ($val))
139                 {
140                         $obj->{'cache'}{$key} = undef;
141                 }
142                 else
143                 {
144                         $obj->{'cache'}{$key} = [split ($Alarm, $val)];
145                 }
146         }
147
148         if (!defined ($obj->{'cache'}{$key}))
149         {
150                 return (qw());
151         }
152         else
153         {
154                 @ret = @{$obj->{'cache'}{$key}};
155         }
156
157         if ($::DEBUG & 0x0200)
158         {
159                 print STDOUT $/, __FILE__, ': GET(', $obj->{'id'}, ', ', $key, ') = (' . join (', ', @ret) . ')';
160         }
161
162         return (@ret);
163 }
164
165 sub keys
166 {
167         my $obj = shift;
168         my @fields = @_;
169         my @field_indizes = ();
170         my $db = $obj->{'data'};
171         my $key;
172         my $val;
173
174         no strict (qw(subs));
175         for ($db->seq ($key, $val, R_FIRST); $db->seq ($key, $val, R_NEXT) == 0;)
176         {
177                 next if (defined ($obj->{'cache'}{$key}));
178                 $obj->{'cache'}{$key} = [split ($Alarm, $val)];
179         }
180
181         if (!@fields)
182         {
183                 return (keys %{$obj->{'cache'}});
184         }
185
186         for (@fields)
187         {
188                 my $field = $_;
189                 if (!defined ($obj->{'field_index'}{$field}))
190                 {
191                         my $id = $obj->{'id'};
192                         print STDERR $/, __FILE__, ": $field is not a valid field ($id).";
193                         next;
194                 }
195                 push (@field_indizes, $obj->{'field_index'}{$field});
196         }
197
198         return (sort
199         {
200                 for (@field_indizes)
201                 {
202                         my $d = $obj->{'cache'}{$a}[$_] cmp $obj->{'cache'}{$b}[$_];
203                         return ($d) if ($d);
204                 }
205         } (keys %{$obj->{'cache'}}));
206 }
207
208 sub del
209 {
210         my $obj = shift;
211         my $key = shift;
212         my $db = $obj->{'data'};
213
214         if (exists ($obj->{'cache'}{$key}))
215         {
216                 if (defined ($obj->{'cache'}{$key}))
217                 {
218                         $db->del ($key);
219                         $obj->{'cache'}{$key} = undef;
220                 }
221                 # It's known that the key doesn't exist..
222         }
223         else
224         {
225                 $db->del ($key);
226                 $obj->{'cache'}{$key} = undef;
227         }
228 }
229
230 sub sync
231 {
232         my $obj = shift;
233         my $db = $obj->{'data'};
234
235         for (CORE::keys %{$obj->{'cache'}})
236         {
237                 my $key = $_;
238                 next unless (defined ($obj->{'cache'}{$key}));
239
240                 my $val = join ($Alarm, @{$obj->{'cache'}{$key}});
241
242                 $db->put ($key, $val);
243                 delete ($obj->{'cache'}{$key});
244         }
245
246         $db->sync ();
247 }
248
249 END
250 {
251         for (CORE::keys (%Objects))
252         {
253                 my $key = $_;
254                 my $obj = $Objects{$key};
255                 $obj->sync ();
256         }
257 }
258
259 =head1 AUTHOR
260
261 Florian octo Forster, L<octo at verplant.org>
262
263 =cut