/[wait]/trunk/lib/WAIT/Table.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

Diff of /trunk/lib/WAIT/Table.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

cvs-head/lib/WAIT/Table.pm revision 24 by ulpfr, Sat Nov 11 17:21:28 2000 UTC trunk/lib/WAIT/Table.pm revision 88 by dpavlin, Mon May 24 13:44:01 2004 UTC
# Line 4  Line 4 
4  # Author          : Ulrich Pfeifer  # Author          : Ulrich Pfeifer
5  # Created On      : Thu Aug  8 13:05:10 1996  # Created On      : Thu Aug  8 13:05:10 1996
6  # Last Modified By: Ulrich Pfeifer  # Last Modified By: Ulrich Pfeifer
7  # Last Modified On: Fri May 19 14:51:14 2000  # Last Modified On: Sat Apr 27 17:20:31 2002
8  # Language        : CPerl  # Language        : CPerl
9  # Update Count    : 133  # Update Count    : 172
10  # Status          : Unknown, Use with caution!  # Status          : Unknown, Use with caution!
11  #  #
12  # Copyright (c) 1996-1997, Ulrich Pfeifer  # Copyright (c) 1996-1997, Ulrich Pfeifer
# Line 32  require WAIT::Parse::Base; Line 32  require WAIT::Parse::Base;
32  use strict;  use strict;
33  use Carp;  use Carp;
34  # use autouse Carp => qw( croak($) );  # use autouse Carp => qw( croak($) );
35  use DB_File;  use BerkeleyDB;
36  use Fcntl;  use Fcntl;
37  use LockFile::Simple ();  use LockFile::Simple ();
38    
# Line 159  sub new { Line 159  sub new {
159    }    }
160    
161    $self->{file}     = $parm{file}     or croak "No file specified";    $self->{file}     = $parm{file}     or croak "No file specified";
162    if (-d  $self->{file}){    if (-e  $self->{file}){
163      warn "Warning: Directory '$self->{file}' already exists\n";      warn "Warning: file '$self->{file}' already exists\n";
   } elsif (!mkdir($self->{file}, 0775)) {  
     croak "Could not 'mkdir $self->{file}': $!\n";  
164    }    }
165    
   my $lockmgr = LockFile::Simple->make(-autoclean => 1);  
   # aquire a write lock  
   $self->{write_lock} = $lockmgr->lock($self->{file} . '/write')  
     or die "Can't lock '$self->{file}/write'";  
   
166    $self->{djk}      = $parm{djk}      if defined $parm{djk};    $self->{djk}      = $parm{djk}      if defined $parm{djk};
167    $self->{layout}   = $parm{layout} || new WAIT::Parse::Base;    $self->{layout}   = $parm{layout} || new WAIT::Parse::Base;
168    $self->{access}   = $parm{access} if defined $parm{access};    $self->{access}   = $parm{access} if defined $parm{access};
# Line 178  sub new { Line 171  sub new {
171    $self->{indexes}  = {};    $self->{indexes}  = {};
172    
173    bless $self, $type;    bless $self, $type;
174    
175      # Checking for readers is not necessary, but let's go with the
176      # generic method.
177      $self->getlock(O_RDWR|O_CREAT); # dies when failing
178      
179    # Call create_index() and create_index() for compatibility    # Call create_index() and create_index() for compatibility
180    for (@{$self->{keyset}||[]}) {    for (@{$self->{keyset}||[]}) {
181      #carp "Specification of indexes at table create time is deprecated";      #carp "Specification of indexes at table create time is deprecated";
# Line 223  sub create_index { Line 221  sub create_index {
221    
222    my $name = join '-', @_;    my $name = join '-', @_;
223    $self->{indexes}->{$name} =    $self->{indexes}->{$name} =
224      new WAIT::Index file => $self->{file}.'/'.$name, attr => $_;      new WAIT::Index file => $self->{file}, name => $name, attr => $_;
225  }  }
226    
227  =head2 Creating an inverted index  =head2 Creating an inverted index
# Line 323  Must be called via C<WAIT::Database::dro Line 321  Must be called via C<WAIT::Database::dro
321    
322  sub drop {  sub drop {
323    my $self = shift;    my $self = shift;
324    
325      unless ($self->{write_lock}){
326        warn "Cannot drop table without write lock. Nothing done";
327        return;
328      }
329      
330    if ((caller)[0] eq 'WAIT::Database') { # database knows about this    if ((caller)[0] eq 'WAIT::Database') { # database knows about this
331      $self->close;               # just make sure      $self->close;               # just make sure
332    
333      my $file = $self->{file};      my $file = $self->{file};
334    
335      for (values %{$self->{indexes}}) {      for (values %{$self->{indexes}}) {
336        $_->drop;        $_->drop;
337      }      }
338      unlink "$file/records";      rmdir "$file.read" or warn "Could not rmdir '$file/read'";
339      # $self->unlock;      unlink "$file";
340      ! (!-e $file or rmdir $file);      
341    } else {    } else {
342      croak ref($self)."::drop called directly";      croak ref($self)."::drop called directly";
343    }    }
# Line 372  sub open { Line 377  sub open {
377      }      }
378      require WAIT::InvertedIndex;      require WAIT::InvertedIndex;
379    }    }
380    
381      $self->getlock($self->{mode});
382    
383      my $dbmode = ($self->{mode} & O_CREAT) ? DB_CREATE : 0;
384    unless (defined $self->{dbh}) {    unless (defined $self->{dbh}) {
385      if ($USE_RECNO) {      if ($USE_RECNO) {
386        $self->{dbh} = tie(@{$self->{db}}, 'DB_File', $file,        tie(%{$self->{db}}, 'BerkeleyDB::Recno',
387                           $self->{mode}, 0664, $DB_RECNO);            -Filename => $self->{file},
388              -Subname  => 'records',
389              -Flags    => $dbmode);
390      } else {      } else {
391        $self->{dbh} =        $self->{dbh} =
392          tie(%{$self->{db}}, 'DB_File', $file,          tie(%{$self->{db}}, 'BerkeleyDB::Btree',
393                           $self->{mode}, 0664, $DB_BTREE);              -Filename => $self->{file},
394                -Subname  => 'records',
395                -Mode     => 0664,
396                -Flags    => $dbmode);
397      }      }
398    }    }
399      
400    # Locking    
   #  
   # We allow multiple readers to coexists.  But write access excludes  
   # all read access vice versa.  In practice read access on tables  
   # open for writing will mostly work ;-)  
   
   my $lockmgr = LockFile::Simple->make(-autoclean => 1);  
   
   # aquire a write lock. We might hold one acquired in create() already  
   $self->{write_lock} ||= $lockmgr->lock($self->{file} . '/write')  
     or die "Can't lock '$self->{file}/write'";  
   
   my $lockdir = $self->{file} . '/read';  
   unless (-d $lockdir) {  
     mkdir $lockdir, 0755 or die "Could not mkdir $lockdir: $!";  
   }  
   
   if ($self->{mode} & O_RDWR) {  
     # this is a hack.  We do not check for reopening ...  
     return $self if $self->{write_lock};  
       
     # If we actually want to write we must check if there are any readers  
     opendir DIR, $lockdir or  
       die "Could not opendir '$lockdir': $!";  
     for my $lockfile (grep { -f "$lockdir/$_" } readdir DIR) {  
       # check if the locks are still valid.  
       # Since we are protected by a write lock, we could use a pline file.  
       # But we want to use the stale testing from LockFile::Simple.  
       if (my $lck = $lockmgr->trylock("$lockdir/$lockfile")) {  
         warn "Removing stale lockfile '$lockdir/$lockfile'";  
         $lck->release;  
       } else {  
         $self->{write_lock}->release;  
         die "Cannot write table '$file' while it's in use";  
       }  
     }  
   } else {  
     # this is a hack.  We do not check for reopening ...  
     return $self if $self->{read_lock};  
       
     # We are a reader. So we release the write lock  
     my $id = time;  
     while (-f "$lockdir/$id.lock") { # here assume ".lock" format!  
       $id++;  
     }  
     $self->{read_lock} = $lockmgr->lock("$lockdir/$id");  
     $self->{write_lock}->release;  
     delete $self->{write_lock};  
   }  
   
401    $self;    $self;
402  }  }
403    
# Line 609  sub delete { Line 574  sub delete {
574  }  }
575    
576  sub unpack {  sub unpack {
577    my $self = shift;    my($self, $tuple) = @_;
578    my $tuple = shift;  
579    return unless defined $tuple;    unless (defined $tuple){
580        # require Carp; # unfortunately gives us "bizarre copy...." :-(((((
581        warn("Debug: somebody called unpack without argument tuple!");
582        return;
583      }
584    
585    my $att;    my $att;
586    my @result;    my @result;
# Line 626  sub unpack { Line 595  sub unpack {
595  sub set {  sub set {
596    my ($self, $iattr, $value) = @_;    my ($self, $iattr, $value) = @_;
597        
598    unless ($self->{write_lock}) {    unless ($self->{write_lock}){
599      die "Cannot set attribute $iattr without having a write lock. Nothing done";      warn "Cannot set iattr[$iattr] without write lock. Nothing done";
600        return;
601    }    }
602    
603      # in the rare case that they haven't written a single record yet, we
604      # make sure, the inverted inherits our $self->{mode}:
605      defined $self->{db} or $self->open;
606    
607    for my $att (keys %{$self->{inverted}}) {    for my $att (keys %{$self->{inverted}}) {
608      if ($] > 5.003) {         # avoid bug in perl up to 5.003_05      require WAIT::InvertedIndex;
609        if ($^V gt v5.003) {         # avoid bug in perl up to 5.003_05
610        my $idx;        my $idx;
611        for $idx (@{$self->{inverted}->{$att}}) {        for $idx (@{$self->{inverted}->{$att}}) {
612          $idx->set($iattr, $value);          $idx->set($iattr, $value);
# Line 649  sub close { Line 625  sub close {
625    if (exists $self->{'access'}) {    if (exists $self->{'access'}) {
626      eval {$self->{'access'}->close}; # dont bother if not opened      eval {$self->{'access'}->close}; # dont bother if not opened
627    }    }
628    for (values %{$self->{indexes}}) {    if ($WAIT::Index::VERSION) {
629      require WAIT::Index;      for (values %{$self->{indexes}}) {
630      $_->close();        $_->close();
631        }
632    }    }
633    if (defined $self->{inverted}) {    if (defined $self->{inverted} && $WAIT::InvertedIndex::VERSION) {
634      require WAIT::InvertedIndex;      # require WAIT::InvertedIndex; Uli: we can avoid closing indexes:
635        # if WAIT::InvertedIndex has not been loaded, they cannot have
636        # been altered so far
637      my $att;      my $att;
638      for $att (keys %{$self->{inverted}}) {      for $att (keys %{$self->{inverted}}) {
639        if ($] > 5.003) {         # avoid bug in perl up to 5.003_05        if ($] > 5.003) {         # avoid bug in perl up to 5.003_05
# Line 683  sub close { Line 662  sub close {
662    1;    1;
663  }  }
664    
665    # Locking
666    #
667    # We allow multiple readers to coexists.  But write access excludes
668    # all read access and vice versa.  In practice read access on tables
669    # open for writing will mostly work ;-)
670    
671    # If a "write" lock is requested, an existing "read" lock will be
672    # released.  If a "read" lock ist requested, an existing "write" lock
673    # will be released.  Requiring a lock already hold has no effect.
674    
675    sub getlock {
676      my ($self, $mode) = @_;
677    
678      # autoclean cleans on DESTROY, stale sends SIGZERO to the owner
679      #
680      my $lockmgr = LockFile::Simple->make(-autoclean => 1, -stale => 1);
681      my $file    = $self->{file};
682      my $lockdir = $self->{file} . '.read';
683    
684      unless (-d $lockdir) {
685        mkdir $lockdir, 0755 or die "Could not mkdir $lockdir: $!";
686      }
687      
688      if ($mode & O_RDWR) {         # Get a write lock.  Release it again
689                                    # and die if there is any valid
690                                    # readers.
691        
692        # Have a write lock already
693        return $self if $self->{write_lock};
694    
695        if ($self->{read_lock}) {   # We are a becoming a writer now. So
696                                    # we release the read lock to avoid
697                                    # blocking ourselves.
698          $self->{read_lock}->release;
699          delete $self->{read_lock};
700        }
701    
702        # Get the preliminary write lock
703        $self->{write_lock} = $lockmgr->lock($self->{file} . '.write')
704          or die "Can't lock '$self->{file}.write'";
705        
706        # If we actually want to write we must check if there are any
707        # readers.  The write lock is confirmed if wen cannot find any
708        # valid readers.
709        
710        local *DIR;
711        opendir DIR, $lockdir or
712          die "Could not opendir '$lockdir': $!";
713        for my $lockfile (grep { -f "$lockdir/$_" } readdir DIR) {
714          # Check if the locks are still valid.  Since we are protected by
715          # a write lock, we could use a plain file.  But we want to use
716          # the stale testing from LockFile::Simple.
717          if (my $lck = $lockmgr->trylock("$lockdir/$lockfile")) {
718            warn "Removing stale lockfile '$lockdir/$lockfile'";
719            $lck->release;
720          } else {                  # Found an active reader, rats!
721            $self->{write_lock}->release;
722            die "Cannot write table '$file' while it's in use";
723          }
724        }
725        closedir DIR;
726      } else {
727        # Have a read lock already
728        return $self if $self->{read_lock};
729    
730        # Get the preliminary write lock to protect the directory
731        # operations.
732    
733        my $write_lock = $lockmgr->lock($self->{file} . '.read/write')
734          or die "Can't lock '$self->{file}.read/write'";
735    
736        # Find a new read slot.  Maybe the plain file would be better?
737        my $id = time;
738        while (-f "$lockdir/$id.lock") { # here assume ".lock" format!
739          $id++;
740        }
741    
742        $self->{read_lock} = $lockmgr->lock("$lockdir/$id")
743          or die "Can't lock '$lockdir/$id'";
744    
745        # We are a reader now. So we release the write lock
746        $write_lock->release;
747      }
748      return $self;
749    }
750    
751  sub unlock {  sub unlock {
752    my $self = shift;    my $self = shift;
753    
# Line 704  sub unlock { Line 769  sub unlock {
769  sub DESTROY {  sub DESTROY {
770    my $self = shift;    my $self = shift;
771    
772    warn "Table handle destroyed without closing it first"    if ($self->{write_lock} || $self->{read_lock}) {
773      if $self->{write_lock} || $self->{read_lock};      warn "Table handle destroyed without closing it first";
774        $self->unlock;
775      }
776  }  }
777    
778  sub open_scan {  sub open_scan {

Legend:
Removed from v.24  
changed lines
  Added in v.88

  ViewVC Help
Powered by ViewVC 1.1.26