Commit fb85e2ee authored by Frédéric Buclin's avatar Frédéric Buclin

Bug 553266: config.cgi?ctype=rdf spends most of its time loading flagtypes from the database

a=LpSolit (module owner)
parent 956e5086
...@@ -374,16 +374,14 @@ sub flag_types { ...@@ -374,16 +374,14 @@ sub flag_types {
my $self = shift; my $self = shift;
if (!defined $self->{'flag_types'}) { 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'} = {};
$self->{'flag_types'}->{'bug'} = $self->{'flag_types'}->{'bug'} =
Bugzilla::FlagType::match({ 'target_type' => 'bug', [grep { $_->target_type eq 'bug' } @$flagtypes];
'product_id' => $self->product_id,
'component_id' => $self->id });
$self->{'flag_types'}->{'attachment'} = $self->{'flag_types'}->{'attachment'} =
Bugzilla::FlagType::match({ 'target_type' => 'attachment', [grep { $_->target_type eq 'attachment' } @$flagtypes];
'product_id' => $self->product_id,
'component_id' => $self->id });
} }
return $self->{'flag_types'}; return $self->{'flag_types'};
} }
......
...@@ -48,7 +48,6 @@ whose names start with _ or are specifically noted as being private. ...@@ -48,7 +48,6 @@ whose names start with _ or are specifically noted as being private.
=cut =cut
use Bugzilla::User;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Group; use Bugzilla::Group;
...@@ -223,6 +222,7 @@ explicitly excluded from the flagtype. ...@@ -223,6 +222,7 @@ explicitly excluded from the flagtype.
sub grant_list { sub grant_list {
my $self = shift; my $self = shift;
require Bugzilla::User;
my @custusers; my @custusers;
my @allusers = @{Bugzilla->user->get_userlist}; my @allusers = @{Bugzilla->user->get_userlist};
foreach my $user (@allusers) { foreach my $user (@allusers) {
...@@ -264,15 +264,33 @@ sub flag_count { ...@@ -264,15 +264,33 @@ sub flag_count {
sub inclusions { sub inclusions {
my $self = shift; my $self = shift;
$self->{'inclusions'} ||= get_clusions($self->id, 'in'); if (!defined $self->{inclusions}) {
return $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 { sub exclusions {
my $self = shift; my $self = shift;
$self->{'exclusions'} ||= get_clusions($self->id, 'ex'); if (!defined $self->{exclusions}) {
return $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 { ...@@ -310,7 +328,7 @@ sub get_clusions {
"WHERE flagtypes.id = ? " . "WHERE flagtypes.id = ? " .
" AND flag${type}clusions.type_id = flagtypes.id", " AND flag${type}clusions.type_id = flagtypes.id",
undef, $id); undef, $id);
my %clusions; my (%clusions, %clusions_as_hash);
foreach my $data (@$list) { foreach my $data (@$list) {
my ($product_id, $product_name, $component_id, $component_name) = @$data; my ($product_id, $product_name, $component_id, $component_name) = @$data;
$product_id ||= 0; $product_id ||= 0;
...@@ -318,8 +336,9 @@ sub get_clusions { ...@@ -318,8 +336,9 @@ sub get_clusions {
$component_id ||= 0; $component_id ||= 0;
$component_name ||= "__Any__"; $component_name ||= "__Any__";
$clusions{"$product_name:$component_name"} = "$product_id:$component_id"; $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 =pod
...@@ -423,15 +442,13 @@ sub sqlify_criteria { ...@@ -423,15 +442,13 @@ sub sqlify_criteria {
my $is_active = $criteria->{is_active} ? "1" : "0"; my $is_active = $criteria->{is_active} ? "1" : "0";
push(@criteria, "flagtypes.is_active = $is_active"); 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 $product_id = $criteria->{product_id};
my $component_id = $criteria->{component_id};
# Add inclusions to the query, which simply involves joining the table # Add inclusions to the query, which simply involves joining the table
# by flag type ID and target product/component. # by flag type ID and target product/component.
push(@$tables, "INNER JOIN flaginclusions AS i ON flagtypes.id = i.type_id"); 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.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, # 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. # we do a LEFT JOIN so we don't miss flag types with no exclusions.
...@@ -439,9 +456,19 @@ sub sqlify_criteria { ...@@ -439,9 +456,19 @@ sub sqlify_criteria {
# component. However, since we want flag types that *aren't* on the # component. However, since we want flag types that *aren't* on the
# exclusions list, we add a WHERE criteria to use only records with # exclusions list, we add a WHERE criteria to use only records with
# NULL exclusion type, i.e. without any exclusions. # NULL exclusion type, i.e. without any exclusions.
my $join_clause = "flagtypes.id = e.type_id " . 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 $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(@$tables, "LEFT JOIN flagexclusions AS e ON ($join_clause)");
push(@criteria, "e.type_id IS NULL"); push(@criteria, "e.type_id IS NULL");
} }
......
...@@ -31,6 +31,7 @@ use Bugzilla::Install::Requirements; ...@@ -31,6 +31,7 @@ use Bugzilla::Install::Requirements;
use Bugzilla::Mailer; use Bugzilla::Mailer;
use Bugzilla::Series; use Bugzilla::Series;
use Bugzilla::Hook; use Bugzilla::Hook;
use Bugzilla::FlagType;
use Scalar::Util qw(blessed); use Scalar::Util qw(blessed);
...@@ -113,7 +114,7 @@ sub create { ...@@ -113,7 +114,7 @@ sub create {
# for each product in the list, particularly with hundreds or thousands # for each product in the list, particularly with hundreds or thousands
# of products. # of products.
sub preload { sub preload {
my ($products) = @_; my ($products, $preload_flagtypes) = @_;
my %prods = map { $_->id => $_ } @$products; my %prods = map { $_->id => $_ } @$products;
my @prod_ids = keys %prods; my @prod_ids = keys %prods;
return unless @prod_ids; return unless @prod_ids;
...@@ -130,6 +131,9 @@ sub preload { ...@@ -130,6 +131,9 @@ sub preload {
push(@{$prods{$product_id}->{"${field}s"}}, $obj); push(@{$prods{$product_id}->{"${field}s"}}, $obj);
} }
} }
if ($preload_flagtypes) {
$_->flag_types foreach @$products;
}
} }
sub update { sub update {
...@@ -775,17 +779,34 @@ sub user_has_access { ...@@ -775,17 +779,34 @@ sub user_has_access {
sub flag_types { sub flag_types {
my $self = shift; my $self = shift;
if (!defined $self->{'flag_types'}) { return $self->{'flag_types'} if defined $self->{'flag_types'};
$self->{'flag_types'} = {};
foreach my $type ('bug', 'attachment') { # We cache flag types to avoid useless calls to get_clusions().
my %flagtypes; my $cache = Bugzilla->request_cache->{flag_types_per_product} ||= {};
foreach my $component (@{$self->components}) { $self->{flag_types} = {};
foreach my $flagtype (@{$component->flag_types->{$type}}) { my $prod_id = $self->id;
$flagtypes{$flagtype->{'id'}} ||= $flagtype; 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'}; return $self->{'flag_types'};
...@@ -1040,6 +1061,9 @@ When passed an arrayref of C<Bugzilla::Product> objects, preloads their ...@@ -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 L</milestones>, L</components>, and L</versions>, which is much faster
than calling those accessors on every item in the array individually. 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 This function is not exported, so must be called like
C<Bugzilla::Product::preload($products)>. C<Bugzilla::Product::preload($products)>.
......
...@@ -81,7 +81,8 @@ if ($cgi->param('product')) { ...@@ -81,7 +81,8 @@ if ($cgi->param('product')) {
$vars->{'products'} = $user->get_selectable_products; $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. # Allow consumers to specify whether or not they want flag data.
if (defined $cgi->param('flags')) { if (defined $cgi->param('flags')) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment