/[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 116 - (show annotations)
Wed Jul 14 09:48:26 2004 UTC (19 years, 9 months ago) by dpavlin
File size: 12214 byte(s)
more fixes, more debug

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 croak("No name specified") unless ($name);
87
88 croak("Directory '$dir' does not exits: $!") unless (-d $dir);
89
90 if (-d "$dir/$name") {
91 carp "Warning: directory '$dir/$name' already exists\n";
92 } else {
93 unless (mkdir "$dir/$name", 0775) {
94 croak("Could not mkdir '$dir/$name': $!");
95 }
96 }
97
98 $self->{dir} = $dir;
99 $self->{name} = $name;
100
101 my $env = BerkeleyDB::Env->new(
102 -Home => $self->path,
103 -Flags => DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB|DB_PRIVATE,
104 # Cachesize => 1024*1024*8,
105 # Pagesize => 8*1024, # environment doesn't understand Pagesize parameter!
106 -Verbose => 1,
107 -ErrFile => $self->path."/error.log",
108 );
109 unless ($env) {
110 confess("Could not create environment: $BerkeleyDB::Error");
111 }
112
113 $self->{env} = $env;
114
115 # apperently (! learned from trial and error) while the Env doesn't
116 # understand Pagesize, the very first table needs to set it up if we
117 # want to deviate from the default. And all tables need to follow
118 # this lead. I'm doing so explicitly, it looks prettier to me
119 $self->{_attr} = BerkeleyDB::Btree->new(
120 -Filename => $self->maindbfile,
121 -Subname => "_attr",
122 -Flags => DB_CREATE,
123 -Mode => 0664,
124 -Env => $env,
125 $WAIT::Database::Cachesize?(Cachesize => $WAIT::Database::Cachesize):(),
126 $WAIT::Database::Pagesize?(Pagesize => $WAIT::Database::Pagesize):(),
127 );
128
129 unless (defined($self->{_attr})) {
130 die("Cannot open [".$self->maindbfile."] _attr: $BerkeleyDB::Error");
131 }
132
133
134 # Use of BerkeleyDB::Env->new here could maybe some day be a way to
135 # introduce a smart locking mechanism? Whatever... it is currently
136 # kein Thema: remember, that the database has a $self->path which
137 # is a *directory* and there are no berkeley tables in this
138 # directory, but there is one subdirectory in this directory for
139 # *each* *table* object.
140
141 $self->{uniqueatt} = $parm{uniqueatt};
142 $self->{mode} = O_RDWR;
143 $self;
144 }
145
146
147 =head2 Constructor open
148
149 $db = WAIT::Database->open(
150 name => "foo",
151 directory => "bar"
152 );
153
154 Open an existing database I<foo> in directory I<bar>.
155
156 =cut
157
158 sub open {
159 my $type = shift;
160 my %parm = @_;
161 my $dir = $parm{directory} || '.';
162 my $name = $parm{name} or croak "No name specified";
163 my $self = bless {}, ref($type) || $type;
164
165 croak("Directory '$dir' does not exits: $!") unless (-d $dir);
166 if (!-d "$dir/$name") {
167 carp "Warning: database '$dir/$name' doesn't exist\n";
168 return;
169 }
170 my $mode = (exists $parm{mode})?$parm{mode}:(O_CREAT | O_RDWR);
171 $self->{dir} = $dir; # will be overwritten by the thaw below, but we need it now
172 $self->{name} = $name;
173
174 my $env;
175
176 return if (! -d $self->path);
177
178 my $flags = DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB;
179
180 $env = BerkeleyDB::Env->new(
181 -Home => $self->path,
182 -Flags => $flags,
183 );
184 unless ($env) {
185 return if ($parm{mode} & O_RDWR);
186 confess("Could not create environment in [".$self->path."]: $BerkeleyDB::Error");
187 }
188
189 #warn "DEBUG: trying to open the database for _attr";
190 my $maindbfile = $self->maindbfile;
191 my $attr = BerkeleyDB::Btree->new(
192 -Filename => $maindbfile,
193 -Subname => "_attr",
194 $env ? (-Env => $env) : (-Flags => DB_RDONLY),
195 );
196 unless (defined($attr)) {
197 return if ($parm{mode} & O_RDWR);
198 croak "Cannot open [$maindbfile] _attr: $BerkeleyDB::Error; mdbf[$maindbfile] env[$env]";
199 }
200
201 #warn "DEBUG: opened the database for _attr";
202 $attr->db_get(0, my $dat);
203 #warn sprintf "DEBUG: got _attr record. time[%.6f] length(dat)[%d]", Time::HiRes::time(), length $dat;
204 $self = thaw $dat;
205 #warn sprintf "DEBUG: thawed _attr rec. time[%.6f]", Time::HiRes::time();
206 $self->{_attr} = $attr;
207
208 return unless defined $self;
209
210 $self->{mode} = $mode;
211 $self->{env} = $env;
212 $self->{dir} = $dir; # yes, again
213 $self->{name} = $name;
214 $self->walkncomplete;
215
216 $self;
217 }
218
219 sub walkncomplete {
220 my $self = shift;
221 $self->maindbfile;
222 $self->path;
223 for my $t (values %{$self->{tables} || {}}) {
224 $t->{path} ||= $self->{path};
225 $t->{maindbfile} ||= $self->{maindbfile};
226 $t->{mode} = $self->{mode};
227 for my $ind (values %{$t->{indexes}}) {
228 $ind->{path} ||= $self->{path};
229 $ind->{maindbfile} ||= $self->{maindbfile};
230 $ind->{mode} = $self->{mode};
231 }
232 for my $inv (values %{$t->{inverted}}) {
233 for my $ind (@$inv) {
234 $ind->{path} ||= $self->{path};
235 $ind->{maindbfile} ||= $self->{maindbfile};
236 $ind->{mode} = $self->{mode};
237 }
238 }
239 }
240 }
241
242
243 =head2 close
244
245 Close a database saving all meta data after closing all associated tables.
246
247 $db->close;
248
249 =cut
250
251 sub close {
252 # my $self = shift would increase refcount!
253 my $self = $_[0];
254
255 for my $table (values %{$self->{tables}}) {
256 $table->close if ref($table);
257 }
258 return 1 unless $self->{mode} & (O_RDWR | O_CREAT);
259
260 my $env = $self->{env};
261
262 for my $att (qw(path maindbfile name env)) {
263 delete $self->{$att} || confess "can't delete '$att'";
264 }
265
266 my $db = $self->{_attr};
267 delete $self->{_attr} || confess "can't delete _attr";
268
269 my $dat = nfreeze $self;
270 $db->db_put(0, $dat);
271
272 undef $db;
273
274 #warn "DEBUG: Removing env[$env] before closing database";
275 undef $env;
276 #warn "DEBUG: Removed it.";
277
278 undef $_[0];
279 return 1;
280 }
281
282
283 =head2 dispose
284
285 Dispose a database. Remove all associated files. This may fail if the
286 database or one of its tables is still open. Failure will be indicated
287 by a false return value.
288
289 $db->dispose;
290
291 WAIT::Database->dispose(name=>'name', directory=>'/dir/to/db/');
292
293 =cut
294
295 sub dispose {
296 # my $self = shift would increase refcount!
297
298 my $path;
299
300 if (ref $_[0]) { # called with instance
301 croak "no mode" unless defined($_[0]->{mode});
302 croak "Database readonly" unless $_[0]->{mode} & (O_CREAT | O_RDWR);
303 $path = $_[0]->path;
304 $_[0]->close;
305 } else {
306 shift;
307 my %parm = @_;
308 my $base = $parm{directory} || '.';
309 my $name = $parm{name} || croak "No name specified";
310 $path = "$base/$name";
311 }
312 croak "No such database '$path'" unless -e "$path";
313
314 #warn "DEBUG: removing $path";
315 my $ret = rmtree($path, 0, 1);
316
317 return $ret;
318 }
319
320
321 =head2 C<$db-E<gt>create_table(name =E<gt>> I<tname>, ... C<);>
322
323 Create a new table with name I<tname>. All parameters are passed to
324 C<WAIT::Table-E<gt>new> together with a filename to use. See
325 L<WAIT::Table> for which attributes are required. The method returns a
326 table handle (C<WAIT::Table::Handle>).
327
328 =cut
329
330 sub create_table {
331 my $self = shift;
332 my %parm = @_;
333 my $name = $parm{name} or croak "create_table: No name specified";
334 my $attr = $parm{attr} or croak "create_table: No attributes specified";
335 my $path = $self->path;
336
337 croak "Database readonly" unless $self->{mode} & (O_CREAT | O_RDWR);
338
339 if (defined $self->{tables}->{$name}) {
340 die "Table '$name' already exists";
341 }
342
343 if ($self->{uniqueatt}) {
344 for (@$attr) { # attribute names must be uniqe
345 if ($self->{attr}->{$_}) {
346 croak("Attribute '$_' is not unique")
347 }
348 }
349 }
350 $self->{tables}->{$name} = WAIT::Table->new(path => "$path/$name",
351 database => $self,
352 env => $self->{env},
353 maindbfile => $self->maindbfile,
354 tablename => $name,
355 %parm);
356 unless (defined $self->{tables}->{$name}) {# fail gracefully
357 delete $self->{tables}->{$name};
358 return undef;
359 }
360
361 if ($self->{uniqueatt}) {
362 # remember table name for each attribute
363 map ($self->{attr}->{$_} = $name, @$attr);
364 }
365 WAIT::Table::Handle->new($self, $name);
366 }
367
368 =head2 maindbfile
369
370 Name of BerekelyDB database (without path)
371
372 my $bdb = $db->maindbfile;
373
374 =cut
375
376 sub maindbfile {
377 my($self,$path) = @_;
378 return $self->{maindbfile} if $self->{maindbfile};
379 $path ||= $self->path;
380 confess "no path argument or attribute" unless $path;
381 $self->{maindbfile} = "etat";
382 }
383
384 =head2 path
385
386 Path to database
387
388 my $db_path = $db->path;
389
390 =cut
391
392 sub path {
393 my $self = shift;
394 return $self->{path} if $self->{path};
395 confess("no attribut dir?") unless $self->{dir};
396 confess("no attribut name?") unless $self->{name};
397 $self->{path} = "$self->{dir}/$self->{name}";
398 }
399
400 =head2 C<$db-E<gt>table(name =E<gt>> I<tname>C<);>
401
402 Open a new table with name I<tname>. The method
403 returns a table handle (C<WAIT::Table::Handle>).
404
405 =cut
406
407 sub sync {
408 my $self = shift;
409
410 for (values %{$self->{tables}}) {
411 $_->sync;
412 }
413 }
414
415 sub table {
416 my $self = shift;
417 my %parm = @_;
418 my $name = $parm{name} or croak "No name specified";
419
420 if (defined $self->{tables}->{$name}) {
421 if (exists $parm{mode}) {
422 $self->{tables}->{$name}->{mode} = $parm{mode};
423 } else {
424 $self->{tables}->{$name}->{mode} = $self->{mode};
425 }
426 WAIT::Table::Handle->new($self,$name);
427 } else {
428 croak "No such table '$name'";
429 }
430 }
431
432
433 =head2 drop
434
435 Drop the table named I<tname>. The table should be closed before
436 calling B<drop>.
437
438 $db->drop(name => 'tname');
439
440 =cut
441
442 sub drop_table {
443 my $self = shift;
444 my %parm = @_;
445 my $name = $parm{name} or croak "No name specified";
446
447 croak "Database readonly" unless $self->{mode} & (O_CREAT | O_RDWR);
448 if (!defined $self->{tables}->{$name}) {
449 croak "Table '$name' does not exist";
450 }
451 $self->{tables}->{$name}->drop;
452
453 if ($self->{uniqueatt}) {
454 # recycle attribute names
455 for (keys %{$self->{attr}}) {
456 delete $self->{attr}->{$_} if $self->{attr}->{$_} eq $name;
457 }
458 }
459 undef $self->{tables}->{$name}; # Call WAIT::Table::DESTROY here;
460 1;
461 }
462
463
464 1;
465
466
467 =head1 AUTHOR
468
469 Ulrich Pfeifer E<lt>F<pfeifer@ls6.informatik.uni-dortmund.de>E<gt>
470
471 =cut
472
473

Properties

Name Value
cvs2svn:cvs-rev 1.2

  ViewVC Help
Powered by ViewVC 1.1.26