]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 553266: config.cgi?ctype=rdf spends most of its time loading flagtypes from the...
authorFrédéric Buclin <LpSolit@gmail.com>
Wed, 20 Oct 2010 00:30:20 +0000 (02:30 +0200)
committerFrédéric Buclin <LpSolit@gmail.com>
Wed, 20 Oct 2010 00:30:20 +0000 (02:30 +0200)
a=LpSolit (module owner)

Bugzilla/Component.pm
Bugzilla/FlagType.pm
Bugzilla/Product.pm
config.cgi

index e5eb78a2d941ebf6be519623dee336548cbd2d0a..c2b36ebeb5d2c1656b8113436c89d899e1c59996 100644 (file)
@@ -370,16 +370,14 @@ sub flag_types {
     my $self = shift;
 
     if (!defined $self->{'flag_types'}) {
+        my $flagtypes = Bugzilla::FlagType::match({ product_id   => $self->product_id,
+                                                    component_id => $self->id });
+
         $self->{'flag_types'} = {};
         $self->{'flag_types'}->{'bug'} =
-          Bugzilla::FlagType::match({ 'target_type'  => 'bug',
-                                      'product_id'   => $self->product_id,
-                                      'component_id' => $self->id });
-
+          [grep { $_->target_type eq 'bug' } @$flagtypes];
         $self->{'flag_types'}->{'attachment'} =
-          Bugzilla::FlagType::match({ 'target_type'  => 'attachment',
-                                      'product_id'   => $self->product_id,
-                                      'component_id' => $self->id });
+          [grep { $_->target_type eq 'attachment' } @$flagtypes];
     }
     return $self->{'flag_types'};
 }
index 2892a8392cb8df664502c57689c6f1df90f10b83..0cc392ed239f16624e589158b52fff14c923114a 100644 (file)
@@ -48,7 +48,6 @@ whose names start with _ or are specifically noted as being private.
 
 =cut
 
-use Bugzilla::User;
 use Bugzilla::Error;
 use Bugzilla::Util;
 use Bugzilla::Group;
@@ -223,6 +222,7 @@ explicitly excluded from the flagtype.
 
 sub grant_list {
     my $self = shift;
+    require Bugzilla::User;
     my @custusers;
     my @allusers = @{Bugzilla->user->get_userlist};
     foreach my $user (@allusers) {
@@ -264,15 +264,33 @@ sub flag_count {
 sub inclusions {
     my $self = shift;
 
-    $self->{'inclusions'} ||= get_clusions($self->id, 'in');
-    return $self->{'inclusions'};
+    if (!defined $self->{inclusions}) {
+        ($self->{inclusions}, $self->{inclusions_as_hash}) = get_clusions($self->id, 'in');
+    }
+    return $self->{inclusions};
+}
+
+sub inclusions_as_hash {
+    my $self = shift;
+
+    $self->inclusions unless defined $self->{inclusions_as_hash};
+    return $self->{inclusions_as_hash};
 }
 
 sub exclusions {
     my $self = shift;
 
-    $self->{'exclusions'} ||= get_clusions($self->id, 'ex');
-    return $self->{'exclusions'};
+    if (!defined $self->{exclusions}) {
+        ($self->{exclusions}, $self->{exclusions_as_hash}) = get_clusions($self->id, 'ex');
+    }
+    return $self->{exclusions};
+}
+
+sub exclusions_as_hash {
+    my $self = shift;
+
+    $self->exclusions unless defined $self->{exclusions_as_hash};
+    return $self->{exclusions_as_hash};
 }
 
 ######################################################################
@@ -310,7 +328,7 @@ sub get_clusions {
                                  "WHERE flagtypes.id = ? " .
                                  " AND flag${type}clusions.type_id = flagtypes.id",
                                  undef, $id);
-    my %clusions;
+    my (%clusions, %clusions_as_hash);
     foreach my $data (@$list) {
         my ($product_id, $product_name, $component_id, $component_name) = @$data;
         $product_id ||= 0;
@@ -318,8 +336,9 @@ sub get_clusions {
         $component_id ||= 0;
         $component_name ||= "__Any__";
         $clusions{"$product_name:$component_name"} = "$product_id:$component_id";
+        $clusions_as_hash{$product_id}->{$component_id} = 1;
     }
-    return \%clusions;
+    return (\%clusions, \%clusions_as_hash);
 }
 
 =pod
@@ -423,15 +442,13 @@ sub sqlify_criteria {
         my $is_active = $criteria->{is_active} ? "1" : "0";
         push(@criteria, "flagtypes.is_active = $is_active");
     }
-    if ($criteria->{product_id} && $criteria->{'component_id'}) {
+    if ($criteria->{product_id}) {
         my $product_id = $criteria->{product_id};
-        my $component_id = $criteria->{component_id};
         
         # Add inclusions to the query, which simply involves joining the table
         # by flag type ID and target product/component.
         push(@$tables, "INNER JOIN flaginclusions AS i ON flagtypes.id = i.type_id");
         push(@criteria, "(i.product_id = $product_id OR i.product_id IS NULL)");
-        push(@criteria, "(i.component_id = $component_id OR i.component_id IS NULL)");
         
         # Add exclusions to the query, which is more complicated.  First of all,
         # we do a LEFT JOIN so we don't miss flag types with no exclusions.
@@ -439,9 +456,19 @@ sub sqlify_criteria {
         # component.  However, since we want flag types that *aren't* on the
         # exclusions list, we add a WHERE criteria to use only records with
         # NULL exclusion type, i.e. without any exclusions.
-        my $join_clause = "flagtypes.id = e.type_id " .
-                          "AND (e.product_id = $product_id OR e.product_id IS NULL) " .
-                          "AND (e.component_id = $component_id OR e.component_id IS NULL)";
+        my $join_clause = "flagtypes.id = e.type_id ";
+
+        my $addl_join_clause = "";
+        if ($criteria->{component_id}) {
+            my $component_id = $criteria->{component_id};
+            push(@criteria, "(i.component_id = $component_id OR i.component_id IS NULL)");
+            $join_clause .= "AND (e.component_id = $component_id OR e.component_id IS NULL) ";
+        }
+        else {
+            $addl_join_clause = "AND e.component_id IS NULL OR (i.component_id != e.component_id) ";
+        }
+        $join_clause .= "AND ((e.product_id = $product_id $addl_join_clause) OR e.product_id IS NULL)";
+
         push(@$tables, "LEFT JOIN flagexclusions AS e ON ($join_clause)");
         push(@criteria, "e.type_id IS NULL");
     }
index e61b3b5778c9c1209cbeb50966a9412d26a27e9c..b9443e9e6c111b250d84b7cfd4e3ba52c0174f58 100644 (file)
@@ -31,6 +31,7 @@ use Bugzilla::Install::Requirements;
 use Bugzilla::Mailer;
 use Bugzilla::Series;
 use Bugzilla::Hook;
+use Bugzilla::FlagType;
 
 use Scalar::Util qw(blessed);
 
@@ -113,7 +114,7 @@ sub create {
 # for each product in the list, particularly with hundreds or thousands
 # of products.
 sub preload {
-    my ($products) = @_;
+    my ($products, $preload_flagtypes) = @_;
     my %prods = map { $_->id => $_ } @$products;
     my @prod_ids = keys %prods;
     return unless @prod_ids;
@@ -130,6 +131,9 @@ sub preload {
             push(@{$prods{$product_id}->{"${field}s"}}, $obj);
         }
     }
+    if ($preload_flagtypes) {
+        $_->flag_types foreach @$products;
+    }
 }
 
 sub update {
@@ -775,17 +779,34 @@ sub user_has_access {
 sub flag_types {
     my $self = shift;
 
-    if (!defined $self->{'flag_types'}) {
-        $self->{'flag_types'} = {};
-        foreach my $type ('bug', 'attachment') {
-            my %flagtypes;
-            foreach my $component (@{$self->components}) {
-                foreach my $flagtype (@{$component->flag_types->{$type}}) {
-                    $flagtypes{$flagtype->{'id'}} ||= $flagtype;
-                }
+    return $self->{'flag_types'} if defined $self->{'flag_types'};
+
+    # We cache flag types to avoid useless calls to get_clusions().
+    my $cache = Bugzilla->request_cache->{flag_types_per_product} ||= {};
+    $self->{flag_types} = {};
+    my $prod_id = $self->id;
+    my $flagtypes = Bugzilla::FlagType::match({ product_id => $prod_id });
+
+    foreach my $type ('bug', 'attachment') {
+        my @flags = grep { $_->target_type eq $type } @$flagtypes;
+        $self->{flag_types}->{$type} = \@flags;
+
+        # Also populate component flag types, while we are here.
+        foreach my $comp (@{$self->components}) {
+            $comp->{flag_types} ||= {};
+            my $comp_id = $comp->id;
+
+            foreach my $flag (@flags) {
+                my $flag_id = $flag->id;
+                $cache->{$flag_id} ||= $flag;
+                my $i = $cache->{$flag_id}->inclusions_as_hash;
+                my $e = $cache->{$flag_id}->exclusions_as_hash;
+                my $included = $i->{0}->{0} || $i->{0}->{$comp_id}
+                               || $i->{$prod_id}->{0} || $i->{$prod_id}->{$comp_id};
+                my $excluded = $e->{0}->{0} || $e->{0}->{$comp_id}
+                               || $e->{$prod_id}->{0} || $e->{$prod_id}->{$comp_id};
+                push(@{$comp->{flag_types}->{$type}}, $flag) if ($included && !$excluded);
             }
-            $self->{'flag_types'}->{$type} = [sort { $a->{'sortkey'} <=> $b->{'sortkey'}
-                                                    || $a->{'name'} cmp $b->{'name'} } values %flagtypes];
         }
     }
     return $self->{'flag_types'};
@@ -1040,6 +1061,9 @@ When passed an arrayref of C<Bugzilla::Product> objects, preloads their
 L</milestones>, L</components>, and L</versions>, which is much faster
 than calling those accessors on every item in the array individually.
 
+If the 2nd argument passed to C<preload> is true, flag types for these
+products and their components are also preloaded.
+
 This function is not exported, so must be called like 
 C<Bugzilla::Product::preload($products)>.
 
index d52c6b6c1b146da1998dcd44c9b8a61209081e1d..2c82fdc59a6023491c88fa01967e70e398df0442 100755 (executable)
@@ -81,7 +81,8 @@ if ($cgi->param('product')) {
     $vars->{'products'} = $user->get_selectable_products;
 }
 
-Bugzilla::Product::preload($vars->{'products'});
+# We set the 2nd argument to 1 to also preload flag types.
+Bugzilla::Product::preload($vars->{'products'}, 1);
 
 # Allow consumers to specify whether or not they want flag data.
 if (defined $cgi->param('flags')) {