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

branches/CPAN/lib/WAIT/Table.pm revision 11 by unknown, Fri Apr 28 15:41:10 2000 UTC trunk/lib/WAIT/Table.pm revision 89 by dpavlin, Mon May 24 20:57:08 2004 UTC
# Line 1  Line 1 
1  #                              -*- Mode: Perl -*-  #                              -*- Mode: Cperl -*-
2  # Table.pm --  # Table.pm --
3  # ITIID           : $ITI$ $Header $__Header$  # ITIID           : $ITI$ $Header $__Header$
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: Sun Nov 22 18:44:37 1998  # Last Modified On: Wed Jan 23 14:15:15 2002
8  # Language        : CPerl  # Language        : CPerl
9  # Update Count    : 51  # Update Count    : 152
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
13  #  #
14    
15  =head1 NAME  =head1 NAME
16    
# Line 25  WAIT::Table -- Module for maintaining Ta Line 25  WAIT::Table -- Module for maintaining Ta
25  =cut  =cut
26    
27  package WAIT::Table;  package WAIT::Table;
28    
29    use WAIT::Table::Handle ();
30  require WAIT::Parse::Base;  require WAIT::Parse::Base;
31    
32  use strict;  use strict;
33  use Carp;  use Carp;
34    # use autouse Carp => qw( croak($) );
35  use DB_File;  use DB_File;
36  use Fcntl;  use Fcntl;
37    use LockFile::Simple ();
38    
39  my $USE_RECNO = 0;  my $USE_RECNO = 0;
40    
41  =head2 Creating a Table.  =head2 Creating a Table.
42    
43  The constructor WAIT::Table-<gt>new is normally called via the  The constructor WAIT::Table-E<gt>new is normally called via the
44  create_table method of a database handle. This is not enforced, but  create_table method of a database handle. This is not enforced, but
45  creating a table doesn not make any sense unless the table is  creating a table does not make any sense unless the table is
46  registered by the database because the latter implements persistence  registered by the database because the latter implements persistence
47  of the meta data. Registering is done automatically by letting the  of the meta data. Registering is done automatically by letting the
48  database handle create a table.  database handle the creation of a table.
49    
50    my $db = create WAIT::Database name => 'sample';    my $db = WAIT::Database->create(name => 'sample');
51    my $tb = $db->create_table (name     => 'test',    my $tb = $db->create_table(name     => 'test',
52                                attr     => ['docid', 'headline'],                               access   => $access,
53                                layout   => $layout,                               layout   => $layout,
54                                access   => $access,                               attr     => ['docid', 'headline'],
55                               );                              );
56    
57  The constructor returns a handle for the table. This handle is hidden by the  The constructor returns a handle for the table. This handle is hidden by the
58  table module, to prevent direct access if called via Table.  table module, to prevent direct access if called via Table.
59    
60  =over 10  =over 10
61    
62  =item C<access> => I<accesobj>  =item C<access> => I<accessobj>
63    
64  A reference to a acces object for the external parts (attributes) of  A reference to an access object for the external parts (attributes) of
65  tuples. As you may remember, the WAIT System does not enforce that  tuples. As you may remember, the WAIT System does not enforce that
66  objects are completely stored inside the system to avoid duplication.  objects are completely stored inside the system to avoid duplication.
67  There is no (strong) point in storing all you HTML-Documents inside  There is no (strong) point in storing all your HTML documents inside
68  the system when indexing your WWW-Server.  the system when indexing your WWW-Server.
69    
70    The access object is designed to work like as a tied hash. You pass
71    the refernce to the object, not the tied hash though. An example
72    implementation of an access class that works for manpages is
73    WAIT::Document::Nroff.
74    
75    The implementation needs to take into account that WAIT will keep this
76    object in a Data::Dumper or Storable database and re-use it when sman
77    is run. So it is not good enough if we can produce the index with it
78    now, when we create or actively access the table, WAIT also must be
79    able to retrieve documents on its own, when we are in a different
80    context. This happens specifically in a retrieval. To get this working
81    seemlessly, the access-defining class must implement a close method.
82    This method will be called before the Data::Dumper dump takes place.
83    In that moment the access-defining class must get rid of all data
84    structures that cannot be reconstructed via the Data::Dumper dump,
85    such as database handles or C pointers.
86    
87  =item C<file> => I<fname>  =item C<file> => I<fname>
88    
89  The filename of the records file. Files for indexes will have I<fname>  The filename of the records file. Files for indexes will have I<fname>
90  as prefix. I<Mandatory>  as prefix. I<Mandatory>, but usually taken care of by the
91    WAIT::Database handle when the constructor is called via
92    WAIT::Database::create_table().
93    
94  =item C<name> => I<name>  =item C<name> => I<name>
95    
# Line 73  The name of this table. I<Mandatory> Line 97  The name of this table. I<Mandatory>
97    
98  =item C<attr> => [ I<attr> ... ]  =item C<attr> => [ I<attr> ... ]
99    
100  A reference to an array of attribute names. I<Mandatory>  A reference to an array of attribute names. WAIT will keep the
101    contents of these attributes in its table. I<Mandatory>
102    
103  =item C<djk> => [ I<attr> ... ]  =item C<djk> => [ I<attr> ... ]
104    
105  A reference to an array of attribute names which make up the  A reference to an array of attribute names which make up the
106  I<disjointness key>. Don't think about it - i's of no use yet;  I<disjointness key>. Don't think about it - it's of no use yet;
107    
108  =item C<layout> => I<layoutobj>  =item C<layout> => I<layoutobj>
109    
110  A reference to an external parser object. Defaults to anew instance of  A reference to an external parser object. Defaults to a new instance
111  C<WAIT::Parse::Base>  of C<WAIT::Parse::Base>. For an example implementation see
112    WAIT::Parse::Nroff. A layout class can be implemented as a singleton
113    class if you so like.
114    
115    =item C<keyset> => I<keyset>
116    
117  =item C<access> => I<accesobj>  The set of attributes needed to identify a record. Defaults to all
118    attributes.
119    
120  A reference to a acces object for the external parts of tuples.  =item C<invindex> => I<inverted index>
121    
122    A reference to an anon array defining attributes of each record that
123    need to be indexed. See the source of smakewhatis for how to set this
124    up.
125    
126  =back  =back
127    
# Line 98  sub new { Line 132  sub new {
132    my %parm = @_;    my %parm = @_;
133    my $self = {};    my $self = {};
134    
135      # Check for mandatory attrs early
136      $self->{name}     = $parm{name}     or croak "No name specified";
137      $self->{attr}     = $parm{attr}     or croak "No attributes specified";
138    
139    # Do that before we eventually add '_weight' to attributes.    # Do that before we eventually add '_weight' to attributes.
140    $self->{keyset}   = $parm{keyset}   || [[@{$parm{attr}}]];    $self->{keyset}   = $parm{keyset}   || [[@{$parm{attr}}]];
141    
142    $self->{mode}     = O_CREAT | O_RDWR;    $self->{mode}     = O_CREAT | O_RDWR;
143    
144    # Determine and set up subclass    # Determine and set up subclass
145    $type = ref($type) || $type;    $type = ref($type) || $type;
146    if (defined $parm{djk}) {    if (defined $parm{djk}) {
# Line 119  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} or !mkdir($self->{file}, 0775)) {    if (-d  $self->{file}){
163        warn "Warning: Directory '$self->{file}' already exists\n";
164      } elsif (!mkdir($self->{file}, 0775)) {
165      croak "Could not 'mkdir $self->{file}': $!\n";      croak "Could not 'mkdir $self->{file}': $!\n";
166    }    }
167    $self->{name}     = $parm{name}     or croak "No name specified";  
   $self->{attr}     = $parm{attr}     or croak "No attributes specified";  
168    $self->{djk}      = $parm{djk}      if defined $parm{djk};    $self->{djk}      = $parm{djk}      if defined $parm{djk};
169    $self->{layout}   = $parm{layout} || new WAIT::Parse::Base;    $self->{layout}   = $parm{layout} || new WAIT::Parse::Base;
170    $self->{access}   = $parm{access} if defined $parm{access};    $self->{access}   = $parm{access} if defined $parm{access};
# Line 132  sub new { Line 173  sub new {
173    $self->{indexes}  = {};    $self->{indexes}  = {};
174    
175    bless $self, $type;    bless $self, $type;
176    
177      # Checking for readers is not necessary, but let's go with the
178      # generic method.
179      $self->getlock(O_RDWR|O_CREAT); # dies when failing
180      
181    # Call create_index() and create_index() for compatibility    # Call create_index() and create_index() for compatibility
182    for (@{$self->{keyset}||[]}) {    for (@{$self->{keyset}||[]}) {
183      #carp "Specification of indexes at table create time is deprecated";      #carp "Specification of indexes at table create time is deprecated";
# Line 142  sub new { Line 188  sub new {
188      my $att  = shift @{$parm{invindex}};      my $att  = shift @{$parm{invindex}};
189      my @spec = @{shift @{$parm{invindex}}};      my @spec = @{shift @{$parm{invindex}}};
190      my @opt;      my @opt;
191        
192      if (ref($spec[0])) {      if (ref($spec[0])) {
193        carp "Secondary pipelines are deprecated\n";        carp "Secondary pipelines are deprecated\n";
194        @opt = %{shift @spec};        @opt = %{shift @spec};
195      }      }
196      $self->create_inverted_index(attribute => $att, pipeline  => \@spec, @opt);      $self->create_inverted_index(attribute => $att, pipeline  => \@spec, @opt);
197    }    }
198    
199    $self;    $self;
200    # end of backwarn compatibility stuff    # end of backwarn compatibility stuff
201  }  }
# Line 168  table! Line 215  table!
215    
216  sub create_index {  sub create_index {
217    my $self= shift;    my $self= shift;
218      
219    croak "Cannot create index for table aready populated"    croak "Cannot create index for table aready populated"
220      if $self->{nextk} > 1;      if $self->{nextk} > 1;
221      
222    require WAIT::Index;    require WAIT::Index;
223      
224    my $name = join '-', @_;    my $name = join '-', @_;
225    $self->{indexes}->{$name} =    $self->{indexes}->{$name} =
226      new WAIT::Index file => $self->{file}.'/'.$name, attr => $_;      new WAIT::Index file => $self->{file}.'/'.$name, attr => $_;
# Line 196  set attributes specified when the table Line 243  set attributes specified when the table
243    
244  =item C<pipeline>  =item C<pipeline>
245    
246  A piplines specification is a reference to and array of method names  A piplines specification is a reference to an array of method names
247  (from package C<WAIT::Filter>) which are to applied in sequence to the  (from package C<WAIT::Filter>) which are to be applied in sequence to
248  contents of the named attribute. The attribute name may not be in the  the contents of the named attribute. The attribute name may not be in
249  attribute list.  the attribute list.
250    
251  =item C<predicate>  =item C<predicate>
252    
253  An indication which predicate the index implements. This may be  An indication which predicate the index implements. This may be
254  e.g. 'plain', 'stemming' or 'soundex'. The indicator will be used for  e.g. 'plain', 'stemming' or 'soundex'. The indicator will be used for
255  query processing. Currently there is no standard set of predicate  query processing. Currently there is no standard set of predicate
256  names. The predicate defaults to the last member of the ppline if  names. The predicate defaults to the last member of the pipeline if
257  omitted.  omitted.
258    
259  =back  =back
# Line 224  sub create_inverted_index { Line 271  sub create_inverted_index {
271    croak "No pipeline specified"  unless $parm{pipeline};    croak "No pipeline specified"  unless $parm{pipeline};
272    
273    $parm{predicate} ||= $parm{pipeline}->[-1];    $parm{predicate} ||= $parm{pipeline}->[-1];
274      
275    croak "Cannot create index for table aready populated"    croak "Cannot create index for table aready populated"
276      if $self->{nextk} > 1;      if $self->{nextk} > 1;
277      
278    require WAIT::InvertedIndex;    require WAIT::InvertedIndex;
279    
280    # backward compatibility stuff    # backward compatibility stuff
# Line 235  sub create_inverted_index { Line 282  sub create_inverted_index {
282    for (qw(attribute pipeline predicate)) {    for (qw(attribute pipeline predicate)) {
283      delete $opt{$_};      delete $opt{$_};
284    }    }
285      
286    my $name = join '_', ($parm{attribute}, @{$parm{pipeline}});    my $name = join '_', ($parm{attribute}, @{$parm{pipeline}});
287    my $idx = new WAIT::InvertedIndex(file   => $self->{file}.'/'.$name,    my $idx = new WAIT::InvertedIndex(file   => $self->{file}.'/'.$name,
288                                      filter => [@{$parm{pipeline}}], # clone                                      filter => [@{$parm{pipeline}}], # clone
# Line 276  Must be called via C<WAIT::Database::dro Line 323  Must be called via C<WAIT::Database::dro
323    
324  sub drop {  sub drop {
325    my $self = shift;    my $self = shift;
326    
327      unless ($self->{write_lock}){
328        warn "Cannot drop table without write lock. Nothing done";
329        return;
330      }
331      
332    if ((caller)[0] eq 'WAIT::Database') { # database knows about this    if ((caller)[0] eq 'WAIT::Database') { # database knows about this
333      $self->close;               # just make sure      $self->close;               # just make sure
334    
335      my $file = $self->{file};      my $file = $self->{file};
336    
337      for (values %{$self->{indexes}}) {      for (values %{$self->{indexes}}) {
338        $_->drop;        $_->drop;
339      }      }
340      unlink "$file/records";      unlink "$file/records";
341        rmdir "$file/read" or warn "Could not rmdir '$file/read'";
342    
343        # $self->unlock;
344      ! (!-e $file or rmdir $file);      ! (!-e $file or rmdir $file);
345    } else {    } else {
346      croak ref($self)."::drop called directly";      croak ref($self)."::drop called directly";
# Line 324  sub open { Line 381  sub open {
381      }      }
382      require WAIT::InvertedIndex;      require WAIT::InvertedIndex;
383    }    }
384    
385      $self->getlock($self->{mode});
386    
387    unless (defined $self->{dbh}) {    unless (defined $self->{dbh}) {
388      if ($USE_RECNO) {      if ($USE_RECNO) {
389        $self->{dbh} = tie(@{$self->{db}}, 'DB_File', $file,        $self->{dbh} = tie(@{$self->{db}}, 'DB_File', $file,
# Line 334  sub open { Line 394  sub open {
394                           $self->{mode}, 0664, $DB_BTREE);                           $self->{mode}, 0664, $DB_BTREE);
395      }      }
396    }    }
397      
398      
399    $self;    $self;
400  }  }
401    
402  sub fetch_extern {  sub fetch_extern {
403    my $self  = shift;    my $self  = shift;
404    
405    print "#@_", $self->{'access'}->{Mode}, "\n";    # print "#@_", $self->{'access'}->{Mode}, "\n"; # DEBUGGING?
406    if (exists $self->{'access'}) {    if (exists $self->{'access'}) {
407      mrequire ref($self->{'access'});      mrequire ref($self->{'access'});
408      $self->{'access'}->FETCH(@_);      $self->{'access'}->FETCH(@_);
# Line 358  sub _find_index { Line 420  sub _find_index {
420    my (@att) = @_;    my (@att) = @_;
421    my %att;    my %att;
422    my $name;    my $name;
423      
424    @att{@att} = @att;    @att{@att} = @att;
425    
426    KEY: for $name (keys %{$self->{indexes}}) {    KEY: for $name (keys %{$self->{indexes}}) {
# Line 375  sub have { Line 437  sub have {
437    my $self  = shift;    my $self  = shift;
438    my %parm  = @_;    my %parm  = @_;
439    
440    my $index = $self->_find_index(keys %parm);    my $index = $self->_find_index(keys %parm) or return; # no index-no have
441    croak "No index found" unless $index;  
442    defined $self->{db} or $self->open;    defined $self->{db} or $self->open;
443    return $index->have(@_);    return $index->have(@_);
444  }  }
# Line 387  sub insert { Line 449  sub insert {
449    
450    defined $self->{db} or $self->open;    defined $self->{db} or $self->open;
451    
452      # We should move all writing methods to a subclass to check only once
453      $self->{mode} & O_RDWR or croak "Cannot insert into table opened in RD_ONLY mode";
454    
455    my $tuple = join($;, map($parm{$_} || '', @{$self->{attr}}));    my $tuple = join($;, map($parm{$_} || '', @{$self->{attr}}));
456    my $key;    my $key;
457    my @deleted = keys %{$self->{deleted}};    my @deleted = keys %{$self->{deleted}};
458      my $gotkey = 0;
459    
460    if (@deleted) {    if (@deleted) {
461      $key = pop @deleted;      $key = pop @deleted;
462      delete $self->{deleted}->{$key};      delete $self->{deleted}->{$key};
463        # Sanity check
464        if ($key && $key>0) {
465          $gotkey=1;
466    } else {    } else {
467          warn(sprintf("WAIT database inconsistency during insert ".
468                       "key[%s]: Please rebuild index\n",
469                       $key
470                      ));
471        }
472      }
473      unless ($gotkey) {
474      $key = $self->{nextk}++;      $key = $self->{nextk}++;
475    }    }
476    if ($USE_RECNO) {    if ($USE_RECNO) {
# Line 408  sub insert { Line 484  sub insert {
484        if ($key == $self->{nextk}-1) {        if ($key == $self->{nextk}-1) {
485          $self->{nextk}--;          $self->{nextk}--;
486        } else {        } else {
487            # warn "setting key[$key] deleted during insert";
488          $self->{deleted}->{$key}=1;          $self->{deleted}->{$key}=1;
489        }        }
490        my $idx;        my $idx;
# Line 416  sub insert { Line 493  sub insert {
493          $idx->remove($key, %parm);          $idx->remove($key, %parm);
494        }        }
495        return undef;        return undef;
496      }      }
497    }    }
498    if (defined $self->{inverted}) {    if (defined $self->{inverted}) {
499      my $att;      my $att;
# Line 432  sub insert { Line 509  sub insert {
509    
510  sub sync {  sub sync {
511    my $self  = shift;    my $self  = shift;
512      
513    for (values %{$self->{indexes}}) {    for (values %{$self->{indexes}}) {
514      map $_->sync, $_;      map $_->sync, $_;
515    }    }
# Line 449  sub fetch { Line 526  sub fetch {
526    my $key   = shift;    my $key   = shift;
527    
528    return () if exists $self->{deleted}->{$key};    return () if exists $self->{deleted}->{$key};
529      
530    defined $self->{db} or $self->open;    defined $self->{db} or $self->open;
531    if ($USE_RECNO) {    if ($USE_RECNO) {
532      $self->unpack($self->{db}->[$key]);      $self->unpack($self->{db}->[$key]);
# Line 462  sub delete_by_key { Line 539  sub delete_by_key {
539    my $self  = shift;    my $self  = shift;
540    my $key   = shift;    my $key   = shift;
541    
542      unless ($key) {
543        Carp::cluck "Warning: delete_by_key called without key. Looks like a bug in WAIT?";
544        return;
545      }
546    
547    return $self->{deleted}->{$key} if defined $self->{deleted}->{$key};    return $self->{deleted}->{$key} if defined $self->{deleted}->{$key};
548    my %tuple = $self->fetch($key);    my %tuple = $self->fetch($key);
549    for (values %{$self->{indexes}}) {    for (values %{$self->{indexes}}) {
# Line 478  sub delete_by_key { Line 560  sub delete_by_key {
560        }        }
561      }      }
562    }    }
563      # warn "setting key[$key] deleted during delete_by_key";
564    ++$self->{deleted}->{$key};    ++$self->{deleted}->{$key};
565  }  }
566    
567  sub delete {  sub delete {
568    my $self  = shift;    my $self  = shift;
569    my $tkey = $self->have(@_);    my $tkey = $self->have(@_);
570      # warn "tkey[$tkey]\@_[@_]";
571    defined $tkey && $self->delete_by_key($tkey, @_);    defined $tkey && $self->delete_by_key($tkey, @_);
572  }  }
573    
574  sub unpack {  sub unpack {
575    my $self = shift;    my($self, $tuple) = @_;
576    my $tuple = shift;  
577      unless (defined $tuple){
578        # require Carp; # unfortunately gives us "bizarre copy...." :-(((((
579        warn("Debug: somebody called unpack without argument tuple!");
580        return;
581      }
582    
583    my $att;    my $att;
584    my @result;    my @result;
# Line 502  sub unpack { Line 590  sub unpack {
590    @result;    @result;
591  }  }
592    
593    sub set {
594      my ($self, $iattr, $value) = @_;
595      
596      unless ($self->{write_lock}){
597        warn "Cannot set iattr[$iattr] without write lock. Nothing done";
598        return;
599      }
600    
601      # in the rare case that they haven't written a single record yet, we
602      # make sure, the inverted inherits our $self->{mode}:
603      defined $self->{db} or $self->open;
604    
605      for my $att (keys %{$self->{inverted}}) {
606        require WAIT::InvertedIndex;
607        if ($^V gt v5.003) {         # avoid bug in perl up to 5.003_05
608          my $idx;
609          for $idx (@{$self->{inverted}->{$att}}) {
610            $idx->set($iattr, $value);
611          }
612        } else {
613          map $_->set($iattr, $value), @{$self->{inverted}->{$att}};
614        }
615      }
616    
617      1;
618    }
619    
620  sub close {  sub close {
621    my $self = shift;    my $self = shift;
622    
623    if (exists $self->{'access'}) {    if (exists $self->{'access'}) {
624      eval {$self->{'access'}->close}; # dont bother if not opened      eval {$self->{'access'}->close}; # dont bother if not opened
625    }    }
626    for (values %{$self->{indexes}}) {    if ($WAIT::Index::VERSION) {
627      $_->close();      for (values %{$self->{indexes}}) {
628          $_->close();
629        }
630    }    }
631    if (defined $self->{inverted}) {    if (defined $self->{inverted} && $WAIT::InvertedIndex::VERSION) {
632        # require WAIT::InvertedIndex; Uli: we can avoid closing indexes:
633        # if WAIT::InvertedIndex has not been loaded, they cannot have
634        # been altered so far
635      my $att;      my $att;
636      for $att (keys %{$self->{inverted}}) {      for $att (keys %{$self->{inverted}}) {
637        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 535  sub close { Line 655  sub close {
655      delete $self->{db};      delete $self->{db};
656    }    }
657    
658      $self->unlock;
659      
660    1;    1;
661  }  }
662    
663    # Locking
664    #
665    # We allow multiple readers to coexists.  But write access excludes
666    # all read access and vice versa.  In practice read access on tables
667    # open for writing will mostly work ;-)
668    
669    # If a "write" lock is requested, an existing "read" lock will be
670    # released.  If a "read" lock ist requested, an existing "write" lock
671    # will be released.  Requiring a lock already hold has no effect.
672    
673    sub getlock {
674      my ($self, $mode) = @_;
675    
676      # autoclean cleans on DESTROY, stale sends SIGZERO to the owner
677      #
678      my $lockmgr = LockFile::Simple->make(-autoclean => 1, -stale => 1);
679      my $file    = $self->{file} . '/records';
680      my $lockdir = $self->{file} . '/read';
681    
682      unless (-d $lockdir) {
683        mkdir $lockdir, 0755 or die "Could not mkdir $lockdir: $!";
684      }
685      
686      if ($mode & O_RDWR) {         # Get a write lock.  Release it again
687                                    # and die if there is any valid
688                                    # readers.
689        
690        # Have a write lock already
691        return $self if $self->{write_lock};
692    
693        if ($self->{read_lock}) {   # We are a becoming a writer now. So
694                                    # we release the read lock to avoid
695                                    # blocking ourselves.
696          $self->{read_lock}->release;
697          delete $self->{read_lock};
698        }
699    
700        # Get the preliminary write lock
701        $self->{write_lock} = $lockmgr->lock($self->{file} . '/write')
702          or die "Can't lock '$self->{file}/write'";
703        
704        # If we actually want to write we must check if there are any
705        # readers.  The write lock is confirmed if wen cannot find any
706        # valid readers.
707        
708        local *DIR;
709        opendir DIR, $lockdir or
710          die "Could not opendir '$lockdir': $!";
711        for my $lockfile (grep { -f "$lockdir/$_" } readdir DIR) {
712          # Check if the locks are still valid.  Since we are protected by
713          # a write lock, we could use a plain file.  But we want to use
714          # the stale testing from LockFile::Simple.
715          if (my $lck = $lockmgr->trylock("$lockdir/$lockfile")) {
716            warn "Removing stale lockfile '$lockdir/$lockfile'";
717            $lck->release;
718          } else {                  # Found an active reader, rats!
719            $self->{write_lock}->release;
720            die "Cannot write table '$file' while it's in use";
721          }
722        }
723        closedir DIR;
724      } else {
725        # Have a read lock already
726        return $self if $self->{read_lock};
727    
728        # Get the preliminary write lock to protect the directory
729        # operations.
730    
731        my $write_lock = $lockmgr->lock($self->{file} . '/read/write')
732          or die "Can't lock '$self->{file}/read/write'";
733    
734        # Find a new read slot.  Maybe the plain file would be better?
735        my $id = time;
736        while (-f "$lockdir/$id.lock") { # here assume ".lock" format!
737          $id++;
738        }
739    
740        $self->{read_lock} = $lockmgr->lock("$lockdir/$id")
741          or die "Can't lock '$lockdir/$id'";
742    
743        # We are a reader now. So we release the write lock
744        $write_lock->release;
745      }
746      return $self;
747    }
748    
749    sub unlock {
750      my $self = shift;
751    
752      # Either we have a read or a write lock (or we close the table already)
753      # unless ($self->{read_lock} || $self->{write_lock}) {
754      #   warn "WAIT::Table::unlock: Table aparently hold's no lock"
755      # }
756      if ($self->{write_lock}) {
757        $self->{write_lock}->release();
758        delete $self->{write_lock};
759      }
760      if ($self->{read_lock}) {
761        $self->{read_lock}->release();
762        delete $self->{read_lock};
763      }
764    
765    }
766    
767    sub DESTROY {
768      my $self = shift;
769    
770      if ($self->{write_lock} || $self->{read_lock}) {
771        warn "Table handle destroyed without closing it first";
772        $self->unlock;
773      }
774    }
775    
776  sub open_scan {  sub open_scan {
777    my $self = shift;    my $self = shift;
778    my $code = shift;    my $code = shift;
# Line 593  sub intervall { Line 828  sub intervall {
828  }  }
829    
830  sub search {  sub search {
831    my $self = shift;    my $self  = shift;
832    my $attr = shift;    my ($query, $attr, $cont, $raw);
833    my $cont = shift;    if (ref $_[0]) {
834    my $raw  = shift;      $query = shift;
835      
836        $attr = $query->{attr};
837        $cont = $query->{cont};
838        $raw  = $query->{raw};
839      } else {
840        require Carp;
841        Carp::cluck("Using three argument search interface is deprecated, use hashref interface instead");
842        $attr = shift;
843        $cont = shift;
844        $raw  = shift;
845        $query = {
846                  attr => $attr,
847                  cont => $cont,
848                  raw  => $raw,
849                 };
850      }
851    
852    my %result;    my %result;
853    
854    defined $self->{db} or $self->open; # require layout    defined $self->{db} or $self->open; # require layout
# Line 606  sub search { Line 858  sub search {
858        my $name = $_->name;        my $name = $_->name;
859        if (exists $raw->{$name} and @{$raw->{$name}}) {        if (exists $raw->{$name} and @{$raw->{$name}}) {
860          my $scale = 1/scalar(@{$raw->{$name}});          my $scale = 1/scalar(@{$raw->{$name}});
861          my %r = $_->search_raw(@{$raw->{$name}});          my %r = $_->search_raw($query, @{$raw->{$name}});
862          my ($key, $val);          my ($key, $val);
863          while (($key, $val) = each %r) {          while (($key, $val) = each %r) {
864            if (exists $result{$key}) {            if (exists $result{$key}) {
# Line 620  sub search { Line 872  sub search {
872    }    }
873    if (defined $cont and $cont ne '') {    if (defined $cont and $cont ne '') {
874      for (@{$self->{inverted}->{$attr}}) {      for (@{$self->{inverted}->{$attr}}) {
875        my %r = $_->search($cont);        my %r = $_->search($query, $cont);
876        my ($key, $val);        my ($key, $val);
877        while (($key, $val) = each %r) {        while (($key, $val) = each %r) {
878          if (exists $result{$key}) {          if (exists $result{$key}) {
# Line 644  sub hilight_positions { Line 896  sub hilight_positions {
896    my %pos;    my %pos;
897    
898    if (defined $raw) {    if (defined $raw) {
899      for (@{$self->{inverted}->{$attr}}) {      for (@{$self->{inverted}->{$attr}}) { # objects of type
900                                              # WAIT::InvertedIndex for
901                                              # this index field $attr
902        my $name = $_->name;        my $name = $_->name;
903        if (exists $raw->{$name}) {        if (exists $raw->{$name}) {
904          my %qt;          my %qt;
# Line 678  sub hilight_positions { Line 932  sub hilight_positions {
932  }  }
933    
934  sub hilight {  sub hilight {
935    my ($tb, $text, $query, $raw) = @_;    my ($tb, $buf, $qplain, $qraw) = @_;
936    my $type = $tb->layout();    my $layout = $tb->layout();
937    
938    my @result;    my @result;
939    
940    $query ||= {};    $qplain ||= {};
941    $raw   ||= {};    $qraw   ||= {};
942    my @ttxt = $type->tag($text);    my @ttxt = $layout->tag($buf);
943    while (@ttxt) {    while (@ttxt) {
944      no strict 'refs';      no strict 'refs';
945      my %tag = %{shift @ttxt};      my %tag = %{shift @ttxt};
# Line 692  sub hilight { Line 947  sub hilight {
947      my $fld;      my $fld;
948    
949      my %hl;      my %hl;
950      for $fld (grep defined $tag{$_}, keys %$query, keys %$raw) {      for $fld (grep defined $tag{$_}, keys %$qplain, keys %$qraw) {
951        my $hp = $tb->hilight_positions($fld, $txt,        my $hp = $tb->hilight_positions($fld, $txt,
952                                        $query->{$fld}, $raw->{$fld});                                        $qplain->{$fld}, $qraw->{$fld});
953        for (keys %$hp) {        for (keys %$hp) {
954          if (exists $hl{$_}) {   # -w ;-(          if (exists $hl{$_}) {   # -w ;-(
955            $hl{$_} = max($hl{$_}, $hp->{$_});            $hl{$_} = max($hl{$_}, $hp->{$_});
# Line 720  sub hilight { Line 975  sub hilight {
975  }  }
976    
977  1;  1;
   

Legend:
Removed from v.11  
changed lines
  Added in v.89

  ViewVC Help
Powered by ViewVC 1.1.26