]> git.donarmstrong.com Git - debbugs.git/blobdiff - Debbugs/Bugs.pm
merge changes from dla source tree
[debbugs.git] / Debbugs / Bugs.pm
index e2293261f7d22c3f1c8912f27d2bef517a3df338..e63f2240b88f171a927137e97365de89ece10ce4 100644 (file)
@@ -47,8 +47,11 @@ BEGIN{
 use Debbugs::Config qw(:config);
 use Params::Validate qw(validate_with :types);
 use IO::File;
-use Debbugs::Status;
+use Debbugs::Status qw(splitpackages);
 use Debbugs::Packages qw(getsrcpkgs);
+use Debbugs::Common qw(getparsedaddrs getmaintainers getmaintainers_reverse);
+use Fcntl qw(O_RDONLY);
+use MLDBM qw(DB_File Storable);
 
 =head2 get_bugs
 
@@ -59,7 +62,7 @@ use Debbugs::Packages qw(getsrcpkgs);
 The following parameters can either be a single scalar or a reference
 to an array. The parameters are ANDed together, and the elements of
 arrayrefs are a parameter are ORed. Future versions of this may allow
-for limited regular expressions.
+for limited regular expressions, and/or more complex expressions.
 
 =over
 
@@ -69,8 +72,6 @@ for limited regular expressions.
 
 =item maint -- address of the maintainer
 
-=item maintenc -- encoded address of the maintainer
-
 =item submitter -- address of the submitter
 
 =item severity -- severity of the bug
@@ -85,6 +86,8 @@ for limited regular expressions.
 
 =item bugs -- list of bugs to search within
 
+=item function -- see description below
+
 =back
 
 =head3 Special options
@@ -114,6 +117,26 @@ trivial.]
 This function will then immediately move on to the next subroutine,
 giving it the same arguments.
 
+=head3 function
+
+This option allows you to provide an arbitrary function which will be
+given the information in the index.db file. This will be super, super
+slow, so only do this if there's no other way to write the search.
+
+You'll be given a list (which you can turn into a hash) like the
+following:
+
+ (pkg => ['a','b'], # may be a scalar (most common)
+  bug => 1234,
+  status => 'pending',
+  submitter => 'boo@baz.com',
+  severity => 'serious',
+  tags => ['a','b','c'], # may be an empty arrayref
+ )
+
+The function should return 1 if the bug should be included; 0 if the
+bug should not.
+
 =cut
 
 sub get_bugs{
@@ -127,9 +150,6 @@ sub get_bugs{
                                          maint     => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
-                                         maintenc  => {type => SCALAR|ARRAYREF,
-                                                       optional => 1,
-                                                      },
                                          submitter => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
@@ -148,6 +168,9 @@ sub get_bugs{
                                          dist      => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
+                                         function  => {type => CODEREF,
+                                                       optional => 1,
+                                                      },
                                          bugs      => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
@@ -164,7 +187,7 @@ sub get_bugs{
      my %options = %param;
      my @bugs;
      # A configuration option will set an array that we'll use here instead.
-     for my $routine (qw(Debbugs::Bugs::get_bugs_flatfile)) {
+     for my $routine (qw(Debbugs::Bugs::get_bugs_by_idx Debbugs::Bugs::get_bugs_flatfile)) {
          my ($package) = $routine =~ m/^(.+)\:\:/;
          eval "use $package;";
          if ($@) {
@@ -191,9 +214,32 @@ sub get_bugs{
      return @bugs;
 }
 
-sub get_bugs_flatfile{
+=head2 get_bugs_by_idx
+
+This routine uses the by-$index.idx indicies to try to speed up
+searches.
+
+
+=cut
+
+sub get_bugs_by_idx{
      my %param = validate_with(params => \@_,
                               spec   => {package   => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         submitter => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         severity  => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         tag       => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         archive   => {type => BOOLEAN,
+                                                       default => 0,
+                                                      },
+                                         owner     => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
                                          src       => {type => SCALAR|ARRAYREF,
@@ -202,7 +248,62 @@ sub get_bugs_flatfile{
                                          maint     => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
-                                         maintenc  => {type => SCALAR|ARRAYREF,
+                                        },
+                             );
+     my %bugs = ();
+
+     # We handle src packages, maint and maintenc by mapping to the
+     # appropriate binary packages, then removing all packages which
+     # don't match all queries
+     my @packages = __handle_pkg_src_and_maint(map {exists $param{$_}?($_,$param{$_}):()}
+                                              qw(package src maint)
+                                             );
+     if (exists $param{package} or
+        exists $param{src} or
+        exists $param{maint}) {
+         delete @param{qw(maint src)};
+         $param{package} = [@packages];
+     }
+     my $keys = keys(%param) - 1;
+     die "Need at least 1 key to search by" unless $keys;
+     my $arc = $param{archive} ? '-arc':'';
+     my %idx;
+     for my $key (grep {$_ ne 'archive'} keys %param) {
+         my $index = $key;
+         $index = 'submitter-email' if $key eq 'submitter';
+         $index = "$config{spool_dir}/by-${index}${arc}.idx";
+         tie(%idx, MLDBM => $index, O_RDONLY)
+              or die "Unable to open $index: $!";
+         for my $search (__make_list($param{$key})) {
+              next unless defined $idx{$search};
+              for my $bug (keys %{$idx{$search}}) {
+                   # increment the number of searches that this bug matched
+                   $bugs{$bug}++;
+              }
+         }
+         untie %idx or die 'Unable to untie %idx';
+     }
+     # Throw out results that do not match all of the search specifications
+     return map {$keys <= $bugs{$_}?($_):()} keys %bugs;
+}
+
+
+=head2 get_bugs_flatfile
+
+This is the fallback search routine. It should be able to complete all
+searches. [Or at least, that's the idea.]
+
+=cut
+
+sub get_bugs_flatfile{
+     my %param = validate_with(params => \@_,
+                              spec   => {package   => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         src       => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         maint     => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
                                          submitter => {type => SCALAR|ARRAYREF,
@@ -230,6 +331,9 @@ sub get_bugs_flatfile{
                                          usertags  => {type => HASHREF,
                                                        optional => 1,
                                                       },
+                                         function  => {type => CODEREF,
+                                                       optional => 1,
+                                                      },
                                         },
                              );
      my $flatfile;
@@ -250,6 +354,18 @@ sub get_bugs_flatfile{
                             @{$param{usertags}}{__make_list($param{tag})}
                        } = (1) x @{$param{usertags}}{__make_list($param{tag})}
      }
+     # We handle src packages, maint and maintenc by mapping to the
+     # appropriate binary packages, then removing all packages which
+     # don't match all queries
+     my @packages = __handle_pkg_src_and_maint(map {exists $param{$_}?($_,$param{$_}):()}
+                                              qw(package src maint)
+                                             );
+     if (exists $param{package} or
+        exists $param{src} or
+        exists $param{maint}) {
+         delete @param{qw(maint src)};
+         $param{package} = [@packages];
+     }
      my @bugs;
      while (<$flatfile>) {
          next unless m/^(\S+)\s+(\d+)\s+(\d+)\s+(\S+)\s+\[\s*([^]]*)\s*\]\s+(\w+)\s+(.*)$/;
@@ -269,8 +385,8 @@ sub get_bugs_flatfile{
                             } @src_packages;
          }
          if (exists $param{submitter}) {
-              my @p_addrs = map {$_->address}
-                   map {lc(getparsedaddrs($_))}
+              my @p_addrs = map {lc($_->address)}
+                   map {getparsedaddrs($_)}
                         __make_list($param{submitter});
               my @f_addrs = map {$_->address}
                    getparsedaddrs($submitter||'');
@@ -290,17 +406,73 @@ sub get_bugs_flatfile{
                               } @bug_tags;
               next unless $bug_ok;
          }
+         # We do this last, because a function may be slow...
+         if (exists $param{function}) {
+              my @bug_tags = split ' ', $tags;
+              my @packages = splitpackages($pkg);
+              my $package = (@packages > 1)?\@packages:$packages[0];
+              next unless
+                   $param{function}->(pkg       => $package,
+                                      bug       => $bug,
+                                      status    => $status,
+                                      submitter => $submitter,
+                                      severity  => $severity,
+                                      tags      => \@bug_tags,
+                                     );
+         }
          push @bugs, $bug;
      }
      return @bugs;
 }
 
+sub __handle_pkg_src_and_maint{
+     my %param = validate_with(params => \@_,
+                              spec   => {package   => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         src       => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         maint     => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                        },
+                              allow_extra => 1,
+                             );
+
+     my @packages = __make_list($param{package});
+     my $package_keys = @packages?1:0;
+     my %packages;
+     @packages{@packages} = (1) x @packages;
+     if (exists $param{src}) {
+         # We only want to increment the number of keys if there is
+         # something to match
+         my $key_inc = 0;
+         for my $package ((map { getsrcpkgs($_)} __make_list($param{src})),__make_list($param{src})) {
+              $packages{$package}++;
+              $key_inc=1;
+         }
+         $package_keys += $key_inc;
+     }
+     if (exists $param{maint}) {
+         my $key_inc = 0;
+         my $maint_rev = getmaintainers_reverse();
+         for my $package (map { exists $maint_rev->{$_}?@{$maint_rev->{$_}}:()}
+                          __make_list($param{maint})) {
+              $packages{$package}++;
+              $key_inc = 1;
+         }
+         $package_keys += $key_inc;
+     }
+     return grep {$packages{$_} >= $package_keys} keys %packages;
+}
+
 
-# This private subroutine takes a scalar and turns it
-# into a list; transforming arrayrefs into their contents
-# along the way.
+# This private subroutine takes a scalar and turns it into a list;
+# transforming arrayrefs into their contents along the way. It also
+# turns undef into the empty list.
 sub __make_list{
-     return map {ref($_) eq 'ARRAY'?@{$_}:$_} @_;
+     return map {defined $_?(ref($_) eq 'ARRAY'?@{$_}:$_):()} @_;
 }
 
 1;