/[wait]/trunk/lib/WAIT/Database.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/WAIT/Database.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 108 - (show annotations)
Tue Jul 13 17:41:12 2004 UTC (19 years, 9 months ago) by dpavlin
File size: 12402 byte(s)
beginning of version 2.0 using BerkeleyDB (non-functional for now)

1 # -*- Mode: cperl -*-
2 # $Basename: Database.pm $
3 # $Revision: 1.14 $
4 # Author : Ulrich Pfeifer
5 # Created On : Thu Aug 8 09:44:13 1996
6 # Last Modified By: Ulrich Pfeifer
7 # Last Modified On: Sat Apr 15 16:15:29 2000
8 # Language : CPerl
9 #
10 # (C) Copyright 1996-2000, Ulrich Pfeifer
11 #
12
13 =head1 NAME
14
15 WAIT::Database - Module fo maintaining WAIT databases
16
17 =head1 SYNOPSIS
18
19 require WAIT::Database;
20
21 =head1 DESCRIPTION
22
23 The modules handles creating, opening, and deleting of databases and
24 tables.
25
26 =cut
27
28 package WAIT::Database;
29
30 use strict;
31 use FileHandle ();
32 use File::Path qw(rmtree);
33 use WAIT::Table ();
34 use BerkeleyDB;
35 use Fcntl;
36 use Carp; # will use autouse later
37 use Storable qw(nfreeze thaw);
38 use vars qw($VERSION);
39 use Data::Dumper;
40
41 $VERSION = "2.000";
42
43 #$WAIT::Database::Pagesize = 1*1024;
44 #$WAIT::Database::Cachesize = 4*1024*1024;
45
46 # use autouse Carp => qw( croak($) );
47
48 =head2 Constructor create
49
50 $db = WAIT::Database->create(
51 directory => '/dir/to/database/'
52 name => 'name',
53 );
54
55 Create a new database.
56
57 =over 10
58
59 =item B<name> I<name>
60
61 Mandatory name of database
62
63 =item B<directory> I<directory>
64
65 Directory which should contain the database (defaults to the current
66 directory).
67
68 =item B<uniqueatt> I<true>
69
70 If given, the database will require unique attributes over all tables.
71
72 The method will croak on failure.
73
74 =back
75
76 =cut
77
78 sub create {
79 my $type = shift;
80 my %parm = @_;
81 my $self = {};
82 bless $self => ref($type) || $type;
83 my $dir = $parm{directory} || '.';
84 my $name = $parm{name};
85
86 unless ($name) {
87 croak("No name specified");
88 }
89
90 unless (-d $dir){
91 croak("Directory '$dir' does not exits: $!");
92 }
93
94 if (-d "$dir/$name") {
95 warn "Warning: Directory '$dir/$name' already exists";
96 } else {
97 unless (mkdir "$dir/$name", 0775) {
98 croak("Could not mkdir '$dir/$name': $!");
99 }
100 }
101
102 $self->{dir} = $dir;
103 $self->{name} = $name;
104
105 use Data::Dumper;
106 print Dumper($self);
107
108 print STDERR "## dir: $dir name: $name path: ",$self->file,"\n";
109
110 my $env= BerkeleyDB::Env->new(
111 -Home => $self->path,
112 -Flags => DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB|DB_PRIVATE,
113 # Cachesize => 1024*1024*8,
114 # Pagesize => 8*1024, # environment doesn't understand Pagesize parameter!
115 -Verbose => 1,
116 -ErrFile => $self->path."/error.log",
117 );
118 unless ($env) {
119 confess("Could not create environment: $BerkeleyDB::Error");
120 }
121
122 $self->{env} = $env;
123
124 # apperently (! learned from trial and error) while the Env doesn't
125 # understand Pagesize, the very first table needs to set it up if we
126 # want to deviate from the default. And all tables need to follow
127 # this lead. I'm doing so explicitly, it looks prettier to me
128 $self->{_attr} = BerkeleyDB::Btree->new(
129 -Filename => $self->maindbfile,
130 -Subname => "_attr",
131 -Flags => DB_CREATE,
132 -Mode => 0664,
133 -Env => $env,
134 $WAIT::Database::Cachesize?(Cachesize => $WAIT::Database::Cachesize):(),
135 $WAIT::Database::Pagesize?(Pagesize => $WAIT::Database::Pagesize):(),
136 );
137
138 print STDERR "### created ",$self->maindbfile,"\n";
139
140 unless (defined($self->{_attr})) {
141 die("Cannot open [".$self->maindbfile."] _attr: $BerkeleyDB::Error");
142 }
143
144
145 # Use of BerkeleyDB::Env->new here could maybe some day be a way to
146 # introduce a smart locking mechanism? Whatever... it is currently
147 # kein Thema: remember, that the database has a $self->path which
148 # is a *directory* and there are no berkeley tables in this
149 # directory, but there is one subdirectory in this directory for
150 # *each* *table* object.
151
152 $self->{uniqueatt} = $parm{uniqueatt};
153 $self->{mode} = O_RDWR;
154 $self;
155 }
156
157
158 =head2 Constructor open
159
160 $db = WAIT::Database->open(
161 name => "foo",
162 directory => "bar"
163 );
164
165 Open an existing database I<foo> in directory I<bar>.
166
167 =cut
168
169 sub open {
170 my $type = shift;
171 my %parm = @_;
172 my $dir = $parm{directory} || '.';
173 my $name = $parm{name} or croak "No name specified";
174 my $self = bless {}, ref($type) || $type;
175
176 my $mode = (exists $parm{mode})?$parm{mode}:(O_CREAT | O_RDWR);
177 $self->{dir} = $dir; # will be overwritten by the thaw below, but we need it now
178 $self->{name} = $name;
179
180 my $env;
181
182 if ($mode & O_RDWR) {
183 my $flags = DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB;
184 warn "setting flags for envorinment 'writing'";
185 $env = BerkeleyDB::Env->new(
186 -Home => $self->path,
187 -Flags => $flags,
188 );
189 unless ($env) {
190 confess("Could not create environment in [".$self->path."]: $BerkeleyDB::Error");
191 }
192 } else {
193 # in der Datei neo-lieferung.pl habe ich in Punkt 2.6 darueber
194 # lamentiert, dass der Readonly-User kein Environment bekommt.
195 # Es muesste ein Klacks sein, dafuer einen Schalter
196 # bereitzustellen. Kostet mich aber zu viel Denkzeit.
197 # warn "DEBUG: setting env to NIL";
198 $env = "";
199 }
200 # warn "DEBUG: trying to open the database for _attr";
201 my $maindbfile = $self->maindbfile;
202 my $attr = BerkeleyDB::Btree->new(
203 -Filename => $maindbfile,
204 -Subname => "_attr",
205 $env ? (-Env => $env) : (-Flags => DB_RDONLY, -Home => $self->path),
206 );
207 unless (defined($attr)) {
208 use Data::Dumper;
209 print Dumper($attr);
210 croak "Cannot open [$maindbfile] _attr: $BerkeleyDB::Error; mdbf[$maindbfile] env[$env]";
211 }
212 # warn "DEBUG: opened the database for _attr";
213 $attr->db_get(0, my $dat);
214 # warn sprintf "DEBUG: got _attr record. time[%.6f] length(dat)[%d]", Time::HiRes::time(), length $dat;
215 $self = thaw $dat;
216 # warn sprintf "DEBUG: thawed _attr rec. time[%.6f]", Time::HiRes::time();
217 $self->{_attr} = $attr;
218
219 return unless defined $self;
220
221 $self->{mode} = $mode;
222 $self->{env} = $env;
223 $self->{dir} = $dir; # yes, again
224 $self->{name} = $name;
225 $self->walkncomplete;
226
227 $self;
228 }
229
230 sub walkncomplete {
231 my $self = shift;
232 $self->maindbfile;
233 $self->path;
234 for my $t (values %{$self->{tables} || {}}) {
235 $t->{file} ||= $self->{file};
236 $t->{maindbfile} ||= $self->{maindbfile};
237 $t->{mode} = $self->{mode};
238 for my $ind (values %{$t->{indexes}}) {
239 $ind->{file} ||= $self->{file};
240 $ind->{maindbfile} ||= $self->{maindbfile};
241 $ind->{mode} = $self->{mode};
242 }
243 for my $inv (values %{$t->{inverted}}) {
244 for my $ind (@$inv) {
245 $ind->{file} ||= $self->{file};
246 $ind->{maindbfile} ||= $self->{maindbfile};
247 $ind->{mode} = $self->{mode};
248 }
249 }
250 }
251 }
252
253 =head2 dispose
254
255 Dispose a database. Remove all associated files. This may fail if the
256 database or one of its tables is still open. Failure will be indicated
257 by a false return value.
258
259 $db->dispose;
260
261 WAIT::Database->dispose(name=>'name', directory=>'/dir/to/db/');
262
263 =cut
264
265 sub dispose {
266 my $path;
267
268 if (ref $_[0]) { # called with instance
269 croak "Database readonly" unless $_[0]->{mode} & (O_CREAT | O_RDWR);
270 $path = $_[0]->path;
271 $_[0]->close;
272 } else {
273 my $type = shift;
274 my %parm = @_;
275 my $base = $parm{directory} || '.';
276 my $name = $parm{name} || croak "No name specified";
277 $path = "$base/$name";
278 }
279 croak "No such database '$path'" unless -e "$path";
280
281 my $ret = rmtree($path, 0, 1);
282
283 $ret;
284 }
285
286
287 =head2 C<$db-E<gt>close;>
288
289 Close a database saving all meta data after closing all associated tables.
290
291 =cut
292
293 sub close {
294 my $self = $_[0];
295
296 for my $table (values %{$self->{tables}}) {
297 $table->close if ref($table);
298 }
299 return 1 unless $self->{mode} & (O_RDWR | O_CREAT);
300
301 for my $att (qw(file maindbfile name env)) {
302 delete $self->{$att} || confess "can't delete '$att'";
303 }
304
305 my $db = $self->{_attr};
306 delete $self->{_attr} || confess "can't delete _attr";
307
308 my $dat = nfreeze $self;
309 $db->db_put(0, $dat);
310
311 #warn "DEBUG: Removing env[$env] before closing database";
312 undef $env;
313 #warn "DEBUG: Removed it.";
314
315 undef $_[0];
316 return 1;
317 }
318
319
320 =head2 C<$db-E<gt>create_table(name =E<gt>> I<tname>, ... C<);>
321
322 Create a new table with name I<tname>. All parameters are passed to
323 C<WAIT::Table-E<gt>new> together with a filename to use. See
324 L<WAIT::Table> for which attributes are required. The method returns a
325 table handle (C<WAIT::Table::Handle>).
326
327 =cut
328
329 sub create_table {
330 my $self = shift;
331 my %parm = @_;
332 my $name = $parm{name} or croak "create_table: No name specified";
333 my $attr = $parm{attr} or croak "create_table: No attributes specified";
334 my $path = $self->path;
335
336 croak "Database readonly" unless $self->{mode} & (O_CREAT | O_RDWR);
337
338 if (defined $self->{tables}->{$name}) {
339 die "Table '$name' already exists";
340 }
341
342 if ($self->{uniqueatt}) {
343 for (@$attr) { # attribute names must be uniqe
344 if ($self->{attr}->{$_}) {
345 croak("Attribute '$_' is not unique")
346 }
347 }
348 }
349 $self->{tables}->{$name} = WAIT::Table->new(file => "$path/$name",
350 database => $self,
351 env => $self->{env},
352 maindbfile => $self->maindbfile,
353 tablename => $name,
354 %parm);
355 unless (defined $self->{tables}->{$name}) {# fail gracefully
356 delete $self->{tables}->{$name};
357 return undef;
358 }
359
360 if ($self->{uniqueatt}) {
361 # remember table name for each attribute
362 map ($self->{attr}->{$_} = $name, @$attr);
363 }
364 WAIT::Table::Handle->new($self, $name);
365 }
366
367 =head2 maindbfile
368
369 Name of BerekelyDB database (without path)
370
371 my $bdb = $db->maindbfile;
372
373 =cut
374
375 sub maindbfile {
376 my($self,$path) = @_;
377 return $self->{maindbfile} if $self->{maindbfile};
378 $path ||= $self->path;
379 confess "ALERT: no file argument, no file attribute???" unless $path;
380 $self->{maindbfile} = "etat";
381 }
382
383 =head2 path
384
385 Path to database
386
387 my $db_path = $db->path;
388
389 =cut
390
391 sub path {
392 my $self = shift;
393 return $self->{path} if $self->{path};
394 confess("no attribut dir?") unless $self->{dir};
395 confess("no attribut name?") unless $self->{name};
396 $self->{path} = "$self->{dir}/$self->{name}";
397 }
398
399 =head2 C<$db-E<gt>table(name =E<gt>> I<tname>C<);>
400
401 Open a new table with name I<tname>. The method
402 returns a table handle (C<WAIT::Table::Handle>).
403
404 =cut
405
406 sub sync {
407 my $self = shift;
408
409 for (values %{$self->{tables}}) {
410 $_->sync;
411 }
412 }
413
414 sub table {
415 my $self = shift;
416 my %parm = @_;
417 my $name = $parm{name} or croak "No name specified";
418
419 if (defined $self->{tables}->{$name}) {
420 if (exists $parm{mode}) {
421 $self->{tables}->{$name}->{mode} = $parm{mode};
422 } else {
423 $self->{tables}->{$name}->{mode} = $self->{mode};
424 }
425 WAIT::Table::Handle->new($self,$name);
426 } else {
427 croak "No such table '$name'";
428 }
429 }
430
431
432 =head2 C<$db-E<gt>drop(name =E<gt>> I<tname>C<);>
433
434 Drop the table named I<tname>. The table should be closed before
435 calling B<drop>.
436
437 =cut
438
439 sub drop_table {
440 my $self = shift;
441 my %parm = @_;
442 my $name = $parm{name} or croak "No name specified";
443
444 croak "Database readonly" unless $self->{mode} & (O_CREAT | O_RDWR);
445 if (!defined $self->{tables}->{$name}) {
446 croak "Table '$name' does not exist";
447 }
448 $self->{tables}->{$name}->drop;
449
450 if ($self->{uniqueatt}) {
451 # recycle attribute names
452 for (keys %{$self->{attr}}) {
453 delete $self->{attr}->{$_} if $self->{attr}->{$_} eq $name;
454 }
455 }
456 undef $self->{tables}->{$name}; # Call WAIT::Table::DESTROY here;
457 1;
458 }
459
460
461 1;
462
463
464 =head1 AUTHOR
465
466 Ulrich Pfeifer E<lt>F<pfeifer@ls6.informatik.uni-dortmund.de>E<gt>
467
468 =cut
469
470

Properties

Name Value
cvs2svn:cvs-rev 1.2

  ViewVC Help
Powered by ViewVC 1.1.26