]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1143871: Correctly preload bug data when viewing a bug
authorFrédéric Buclin <LpSolit@gmail.com>
Sun, 5 Apr 2015 19:38:15 +0000 (21:38 +0200)
committerFrédéric Buclin <LpSolit@gmail.com>
Sun, 5 Apr 2015 19:38:15 +0000 (21:38 +0200)
r=dkl a=sgreen

Bugzilla/Bug.pm
Bugzilla/Markdown.pm
Bugzilla/Template.pm
process_bug.cgi

index ab9cf4e66310ec6e157d32ddb236b26592a11d12..2f34d55e749f92b21f50a42f8e2da40e0581ac3d 100644 (file)
@@ -505,59 +505,68 @@ sub preload {
     # to the more complex method.
     my @all_dep_ids;
     foreach my $bug (@$bugs) {
-        push(@all_dep_ids, @{ $bug->blocked }, @{ $bug->dependson });
-        push(@all_dep_ids, @{ $bug->duplicate_ids });
+        push @all_dep_ids, @{ $bug->blocked }, @{ $bug->dependson };
+        push @all_dep_ids, @{ $bug->duplicate_ids };
+        push @all_dep_ids, @{ $bug->_preload_referenced_bugs };
     }
     @all_dep_ids = uniq @all_dep_ids;
     # If we don't do this, can_see_bug will do one call per bug in
     # the dependency and duplicate lists, in Bugzilla::Template::get_bug_link.
     $user->visible_bugs(\@all_dep_ids);
-
-    foreach my $bug (@$bugs) {
-        $bug->_preload_referenced_bugs();
-    }
 }
 
 # Helps load up bugs referenced in comments by retrieving them with a single
 # query from the database and injecting bug objects into the object-cache.
 sub _preload_referenced_bugs {
     my $self = shift;
-    my @referenced_bug_ids;
 
     # inject current duplicates into the object-cache first
     foreach my $bug (@{ $self->duplicates }) {
-        $bug->object_cache_set()
-            unless Bugzilla::Bug->object_cache_get($bug->id);
+        $bug->object_cache_set() unless Bugzilla::Bug->object_cache_get($bug->id);
     }
 
     # preload bugs from comments
-    require Bugzilla::Template;
-    foreach my $comment (@{ $self->comments }) {
-        if ($comment->type == CMT_HAS_DUPE || $comment->type == CMT_DUPE_OF) {
-            # duplicate bugs that aren't currently in $self->duplicates
-            push @referenced_bug_ids, $comment->extra_data
-                unless Bugzilla::Bug->object_cache_get($comment->extra_data);
-        }
-        else {
-            # bugs referenced in comments
-            Bugzilla::Template::quoteUrls($comment->body, undef, undef, undef,
-                sub {
-                    my $bug_id = $_[0];
-                    push @referenced_bug_ids, $bug_id
-                        unless Bugzilla::Bug->object_cache_get($bug_id);
-                });
-        }
-    }
+    my $referenced_bug_ids = _extract_bug_ids($self->comments);
+    my @ref_bug_ids = grep { !Bugzilla::Bug->object_cache_get($_) } @$referenced_bug_ids;
 
     # inject into object-cache
-    my $referenced_bugs = Bugzilla::Bug->new_from_list(
-        [ uniq @referenced_bug_ids ]);
-    foreach my $bug (@$referenced_bugs) {
-        $bug->object_cache_set();
-    }
+    my $referenced_bugs = Bugzilla::Bug->new_from_list(\@ref_bug_ids);
+    $_->object_cache_set() foreach @$referenced_bugs;
 
-    # preload bug visibility
-    Bugzilla->user->visible_bugs(\@referenced_bug_ids);
+    return $referenced_bug_ids
+}
+
+# Extract bug IDs mentioned in comments. This is much faster than calling quoteUrls().
+sub _extract_bug_ids {
+    my $comments = shift;
+    my @bug_ids;
+
+    my $params = Bugzilla->params;
+    my @urlbases = ($params->{'urlbase'});
+    push(@urlbases, $params->{'sslbase'}) if $params->{'sslbase'};
+    my $urlbase_re = '(?:' . join('|', map { qr/$_/ } @urlbases) . ')';
+    my $bug_word = template_var('terms')->{bug};
+    my $bugs_word = template_var('terms')->{bugs};
+
+    foreach my $comment (@$comments) {
+        if ($comment->type == CMT_HAS_DUPE || $comment->type == CMT_DUPE_OF) {
+            push @bug_ids, $comment->extra_data;
+            next;
+        }
+        my $s = $comment->already_wrapped ? qr/\s/ : qr/\h/;
+        my $text = $comment->body;
+        # Full bug links
+        push @bug_ids, $text =~ /\b$urlbase_re\Qshow_bug.cgi?id=\E(\d+)(?:\#c\d+)?/g;
+        # bug X
+        my $bug_re = qr/\Q$bug_word\E$s*\#?$s*(\d+)/i;
+        push @bug_ids, $text =~ /\b$bug_re/g;
+        # bugs X, Y, Z
+        my $bugs_re = qr/\Q$bugs_word\E$s*\#?$s*(\d+)(?:$s*,$s*\#?$s*(\d+))+/i;
+        push @bug_ids, $text =~ /\b$bugs_re/g;
+        # Old duplicate markers
+        push @bug_ids, $text =~ /(?<=^\*\*\*\ This\ bug\ has\ been\ marked\ as\ a\ duplicate\ of\ )(\d+)(?=\ \*\*\*\Z)/;
+    }
+    return [uniq @bug_ids];
 }
 
 sub possible_duplicates {
@@ -3381,13 +3390,8 @@ sub alias {
 
     my $dbh = Bugzilla->dbh;
     $self->{'alias'} = $dbh->selectcol_arrayref(
-        q{SELECT alias
-            FROM bugs_aliases
-           WHERE bug_id = ?
-        ORDER BY alias},
-      undef, $self->bug_id);
-
-    $self->{'alias'} = [] if !scalar(@{$self->{'alias'}});
+        q{SELECT alias FROM bugs_aliases WHERE bug_id = ? ORDER BY alias},
+        undef, $self->bug_id);
 
     return $self->{'alias'};
 }
@@ -3415,6 +3419,7 @@ sub attachments {
 
     $self->{'attachments'} =
         Bugzilla::Attachment->get_attachments_by_bug($self, {preload => 1});
+    $_->object_cache_set() foreach @{ $self->{'attachments'} };
     return $self->{'attachments'};
 }
 
@@ -4046,7 +4051,11 @@ sub EmitDependList {
 # Creates a lot of bug objects in the same order as the input array.
 sub _bugs_in_order {
     my ($self, $bug_ids) = @_;
+    return [] unless @$bug_ids;
+
     my %bug_map;
+    my $dbh = Bugzilla->dbh;
+
     # there's no need to load bugs from the database if they are already in the
     # object-cache
     my @missing_ids;
@@ -4058,10 +4067,25 @@ sub _bugs_in_order {
             push @missing_ids, $bug_id;
         }
     }
-    my $bugs = $self->new_from_list(\@missing_ids);
-    foreach my $bug (@$bugs) {
-        $bug_map{$bug->id} = $bug;
+    if (@missing_ids) {
+        my $bugs = Bugzilla::Bug->new_from_list(\@missing_ids);
+        $bug_map{$_->id} = $_ foreach @$bugs;
+    }
+
+    # Dependencies are often displayed using their aliases instead of their
+    # bug ID. Load them all at once.
+    my $rows = $dbh->selectall_arrayref(
+        'SELECT bug_id, alias FROM bugs_aliases WHERE ' .
+        $dbh->sql_in('bug_id', $bug_ids) . ' ORDER BY alias');
+
+    foreach my $row (@$rows) {
+        my ($bug_id, $alias) = @$row;
+        $bug_map{$bug_id}->{alias} ||= [];
+        push @{ $bug_map{$bug_id}->{alias} }, $alias;
     }
+    # Make sure all bugs have their alias attribute set.
+    $bug_map{$_}->{alias} ||= [] foreach @$bug_ids;
+
     return [ map { $bug_map{$_} } @$bug_ids ];
 }
 
@@ -4510,6 +4534,10 @@ sub ValidateDependencies {
     my $dbh = Bugzilla->dbh;
     my %deps;
     my %deptree;
+    my %sth;
+    $sth{dependson} = $dbh->prepare('SELECT dependson FROM dependencies WHERE blocked   = ?');
+    $sth{blocked}   = $dbh->prepare('SELECT blocked   FROM dependencies WHERE dependson = ?');
+
     foreach my $pair (["blocked", "dependson"], ["dependson", "blocked"]) {
         my ($me, $target) = @{$pair};
         $deptree{$target} = [];
@@ -4534,10 +4562,7 @@ sub ValidateDependencies {
         my @stack = @{$deps{$target}};
         while (@stack) {
             my $i = shift @stack;
-            my $dep_list =
-                $dbh->selectcol_arrayref("SELECT $target
-                                          FROM dependencies
-                                          WHERE $me = ?", undef, $i);
+            my $dep_list = $dbh->selectcol_arrayref($sth{$target}, undef, $i);
             foreach my $t (@$dep_list) {
                 # ignore any _current_ dependencies involving this bug,
                 # as they will be overwritten with data from the form.
index bbb5b0f270171b37c346578ddd9d8d72d42aff0b..5ee4768762558f21f39a59ab0bcef723d149e2fd 100644 (file)
@@ -75,7 +75,7 @@ sub _Markdown {
     my $self = shift;
     my $text = shift;
 
-    $text = Bugzilla::Template::quoteUrls($text, undef, undef, undef, undef, 1);
+    $text = Bugzilla::Template::quoteUrls($text, undef, undef, undef, 1);
 
     return $self->SUPER::_Markdown($text, @_);
 }
index 653a8c51f80b09fddd822c6015551c55df84d883..de72cd71a04c280cac17fa40efb8bcd16f5ae63d 100644 (file)
@@ -148,10 +148,9 @@ sub get_format {
 # If you want to modify this routine, read the comments carefully
 
 sub quoteUrls {
-    my ($text, $bug, $comment, $user, $bug_link_func, $for_markdown) = @_;
+    my ($text, $bug, $comment, $user, $for_markdown) = @_;
     return $text unless $text;
     $user ||= Bugzilla->user;
-    $bug_link_func ||= \&get_bug_link;
     $for_markdown ||= 0;
 
     # We use /g for speed, but uris can have other things inside them
@@ -206,7 +205,7 @@ sub quoteUrls {
         map { qr/$_/ } grep($_, Bugzilla->params->{'urlbase'}, 
                             Bugzilla->params->{'sslbase'})) . ')';
     $text =~ s~\b(${urlbase_re}\Qshow_bug.cgi?id=\E([0-9]+)(\#c([0-9]+))?)\b
-              ~($things[$count++] = $bug_link_func->($3, $1, { comment_num => $5, user => $user })) &&
+              ~($things[$count++] = get_bug_link($3, $1, { comment_num => $5, user => $user })) &&
                ("\x{FDD2}" . ($count-1) . "\x{FDD3}")
               ~egox;
 
@@ -254,7 +253,7 @@ sub quoteUrls {
     $text =~ s~\b($bug_re(?:$s*,?$s*$comment_re)?|$comment_re)
               ~ # We have several choices. $1 here is the link, and $2-4 are set
                 # depending on which part matched
-               (defined($2) ? $bug_link_func->($2, $1, { comment_num => $3, user => $user }) :
+               (defined($2) ? get_bug_link($2, $1, { comment_num => $3, user => $user }) :
                               "<a href=\"$current_bugurl#c$4\">$1</a>")
               ~egx;
 
@@ -268,7 +267,7 @@ sub quoteUrls {
 
     $text =~ s{($bugs_re)}{
         my $match = $1;
-        $match =~ s/((?:#$s*)?(\d+))/$bug_link_func->($2, $1);/eg;
+        $match =~ s/((?:#$s*)?(\d+))/get_bug_link($2, $1);/eg;
         $match;
     }eg;
 
@@ -288,7 +287,7 @@ sub quoteUrls {
     $text =~ s~(?<=^\*\*\*\ This\ bug\ has\ been\ marked\ as\ a\ duplicate\ of\ )
                (\d+)
                (?=\ \*\*\*\Z)
-              ~$bug_link_func->($1, $1, { user => $user })
+              ~get_bug_link($1, $1, { user => $user })
               ~egmx;
 
     # Now remove the encoding hacks in reverse order
@@ -302,7 +301,6 @@ sub quoteUrls {
 # Creates a link to an attachment, including its title.
 sub get_attachment_link {
     my ($attachid, $link_text, $user) = @_;
-    my $dbh = Bugzilla->dbh;
     $user ||= Bugzilla->user;
 
     my $attachment = new Bugzilla::Attachment({ id => $attachid, cache => 1 });
@@ -353,7 +351,6 @@ sub get_bug_link {
     my ($bug, $link_text, $options) = @_;
     $options ||= {};
     $options->{user} ||= Bugzilla->user;
-    my $dbh = Bugzilla->dbh;
 
     if (defined $bug && $bug ne '') {
         if (!blessed($bug)) {
index 448b42c408bebae12804a103fe1d1a45fbbe4105..a39f0cc45e937521f57c0f0525efd2171f23ad75 100755 (executable)
@@ -412,6 +412,10 @@ elsif ($action eq 'next_bug' or $action eq 'same_bug') {
         if ($action eq 'next_bug') {
             $vars->{'nextbug'} = $bug->id;
         }
+        # For performance reasons, preload visibility of dependencies
+        # and duplicates related to this bug.
+        Bugzilla::Bug->preload([$bug]);
+
         $template->process("bug/show.html.tmpl", $vars)
           || ThrowTemplateError($template->error());
         exit;