Commit 618959cb authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 364780: The keyword cache cannot be fixed with editkeywords privs only -…

Bug 364780: The keyword cache cannot be fixed with editkeywords privs only - Patch by Fré©ric Buclin <LpSolit@gmail.com> r/a=justdave
parent f9f63fd6
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
# Matthew Tuck <matty@chariot.net.au> # Matthew Tuck <matty@chariot.net.au>
# Max Kanat-Alexander <mkanat@bugzilla.org> # Max Kanat-Alexander <mkanat@bugzilla.org>
# Marc Schumann <wurblzap@gmail.com> # Marc Schumann <wurblzap@gmail.com>
# Frédéric Buclin <LpSolit@gmail.com>
use strict; use strict;
...@@ -78,11 +79,13 @@ Bugzilla->login(LOGIN_REQUIRED); ...@@ -78,11 +79,13 @@ Bugzilla->login(LOGIN_REQUIRED);
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $template = Bugzilla->template; my $template = Bugzilla->template;
my $user = Bugzilla->user;
# Make sure the user is authorized to access sanitycheck.cgi. # Make sure the user is authorized to access sanitycheck.cgi.
# As this script can now alter the group_control_map table, we no longer # As this script can now alter the group_control_map table, we no longer
# let users with editbugs privs run it anymore. # let users with editbugs privs run it anymore.
Bugzilla->user->in_group("editcomponents") $user->in_group("editcomponents")
|| ($user->in_group('editkeywords') && defined $cgi->param('rebuildkeywordcache'))
|| ThrowUserError("auth_failure", {group => "editcomponents", || ThrowUserError("auth_failure", {group => "editcomponents",
action => "run", action => "run",
object => "sanity_check"}); object => "sanity_check"});
...@@ -94,6 +97,16 @@ my @row; ...@@ -94,6 +97,16 @@ my @row;
$template->put_header("Sanity Check"); $template->put_header("Sanity Check");
########################################################################### ###########################################################################
# Users with 'editkeywords' privs only can only check keywords.
###########################################################################
unless ($user->in_group('editcomponents')) {
check_votes_or_keywords('keywords');
Status("Sanity check completed.");
$template->put_footer();
exit;
}
###########################################################################
# Fix vote cache # Fix vote cache
########################################################################### ###########################################################################
...@@ -602,164 +615,180 @@ sub AlertBadVoteCache { ...@@ -602,164 +615,180 @@ sub AlertBadVoteCache {
Alert("Bad vote cache for bug " . BugLink($id)); Alert("Bad vote cache for bug " . BugLink($id));
} }
$sth = $dbh->prepare(q{SELECT bug_id, votes, keywords check_votes_or_keywords();
FROM bugs
WHERE votes != 0
OR keywords != ''});
$sth->execute;
my %votes;
my %bugid;
my %keyword;
while (my ($id, $v, $k) = $sth->fetchrow_array) { sub check_votes_or_keywords {
if ($v != 0) { my $check = shift || 'all';
$votes{$id} = $v;
}
if ($k) {
$keyword{$id} = $k;
}
}
Status("Checking cached vote counts"); my $dbh = Bugzilla->dbh;
$sth = $dbh->prepare(q{SELECT bug_id, SUM(vote_count) my $sth = $dbh->prepare(q{SELECT bug_id, votes, keywords
FROM votes }. FROM bugs
$dbh->sql_group_by('bug_id')); WHERE votes != 0 OR keywords != ''});
$sth->execute; $sth->execute;
my $offer_votecache_rebuild = 0; my %votes;
my %keyword;
while (my ($id, $v) = $sth->fetchrow_array) { while (my ($id, $v, $k) = $sth->fetchrow_array) {
if ($v <= 0) { if ($v != 0) {
Alert("Bad vote sum for bug $id"); $votes{$id} = $v;
} else { }
if (!defined $votes{$id} || $votes{$id} != $v) { if ($k) {
AlertBadVoteCache($id); $keyword{$id} = $k;
$offer_votecache_rebuild = 1;
} }
delete $votes{$id};
} }
}
foreach my $id (keys %votes) {
AlertBadVoteCache($id);
$offer_votecache_rebuild = 1;
}
if ($offer_votecache_rebuild) { # If we only want to check keywords, skip checks about votes.
print qq{<a href="sanitycheck.cgi?rebuildvotecache=1">Click here to rebuild the vote cache</a><p>\n}; _check_votes(\%votes) unless ($check eq 'keywords');
# If we only want to check votes, skip checks about keywords.
_check_keywords(\%keyword) unless ($check eq 'votes');
} }
sub _check_votes {
my $votes = shift;
Status("Checking keywords table"); Status("Checking cached vote counts");
my $dbh = Bugzilla->dbh;
my %keywordids; my $sth = $dbh->prepare(q{SELECT bug_id, SUM(vote_count)
FROM votes }.
$dbh->sql_group_by('bug_id'));
$sth->execute;
my $keywords = $dbh->selectall_arrayref(q{SELECT id, name my $offer_votecache_rebuild = 0;
FROM keyworddefs});
foreach my $keyword (@$keywords) { while (my ($id, $v) = $sth->fetchrow_array) {
my ($id, $name) = @$keyword; if ($v <= 0) {
if ($keywordids{$id}) { Alert("Bad vote sum for bug $id");
Alert("Duplicate entry in keyworddefs for id $id"); } else {
if (!defined $votes->{$id} || $votes->{$id} != $v) {
AlertBadVoteCache($id);
$offer_votecache_rebuild = 1;
}
delete $votes->{$id};
}
} }
$keywordids{$id} = 1; foreach my $id (keys %$votes) {
if ($name =~ /[\s,]/) { AlertBadVoteCache($id);
Alert("Bogus name in keyworddefs for id $id"); $offer_votecache_rebuild = 1;
} }
}
$sth = $dbh->prepare(q{SELECT bug_id, keywordid if ($offer_votecache_rebuild) {
FROM keywords print qq{<a href="sanitycheck.cgi?rebuildvotecache=1">Click here to rebuild the vote cache</a><p>\n};
ORDER BY bug_id, keywordid});
$sth->execute;
my $lastid;
my $lastk;
while (my ($id, $k) = $sth->fetchrow_array) {
if (!$keywordids{$k}) {
Alert("Bogus keywordids $k found in keywords table");
} }
if (defined $lastid && $id eq $lastid && $k eq $lastk) {
Alert("Duplicate keyword ids found in bug " . BugLink($id));
}
$lastid = $id;
$lastk = $k;
} }
Status("Checking cached keywords"); sub _check_keywords {
my $keyword = shift;
my %realk;
if (defined $cgi->param('rebuildkeywordcache')) { Status("Checking keywords table");
$dbh->bz_lock_tables('bugs write', 'keywords read', my $dbh = Bugzilla->dbh;
'keyworddefs read'); my $cgi = Bugzilla->cgi;
}
my $query = q{SELECT keywords.bug_id, keyworddefs.name my %keywordids;
FROM keywords my $keywords = $dbh->selectall_arrayref(q{SELECT id, name
INNER JOIN keyworddefs FROM keyworddefs});
ON keyworddefs.id = keywords.keywordid
INNER JOIN bugs
ON keywords.bug_id = bugs.bug_id
ORDER BY keywords.bug_id, keyworddefs.name};
$sth = $dbh->prepare($query); foreach (@$keywords) {
$sth->execute; my ($id, $name) = @$_;
if ($keywordids{$id}) {
Alert("Duplicate entry in keyworddefs for id $id");
}
$keywordids{$id} = 1;
if ($name =~ /[\s,]/) {
Alert("Bogus name in keyworddefs for id $id");
}
}
my $lastb = 0; my $sth = $dbh->prepare(q{SELECT bug_id, keywordid
my @list; FROM keywords
while (1) { ORDER BY bug_id, keywordid});
my ($b, $k) = $sth->fetchrow_array; $sth->execute;
if (!defined $b || $b != $lastb) { my $lastid;
if (@list) { my $lastk;
$realk{$lastb} = join(', ', @list); while (my ($id, $k) = $sth->fetchrow_array) {
if (!$keywordids{$k}) {
Alert("Bogus keywordids $k found in keywords table");
} }
if (!$b) { if (defined $lastid && $id eq $lastid && $k eq $lastk) {
last; Alert("Duplicate keyword ids found in bug " . BugLink($id));
} }
$lastb = $b; $lastid = $id;
@list = (); $lastk = $k;
} }
push(@list, $k);
}
my @badbugs = (); Status("Checking cached keywords");
foreach my $b (keys(%keyword)) { if (defined $cgi->param('rebuildkeywordcache')) {
if (!exists $realk{$b} || $realk{$b} ne $keyword{$b}) { $dbh->bz_lock_tables('bugs write', 'keywords read', 'keyworddefs read');
push(@badbugs, $b);
} }
}
foreach my $b (keys(%realk)) { my $query = q{SELECT keywords.bug_id, keyworddefs.name
if (!exists $keyword{$b}) { FROM keywords
push(@badbugs, $b); INNER JOIN keyworddefs
ON keyworddefs.id = keywords.keywordid
INNER JOIN bugs
ON keywords.bug_id = bugs.bug_id
ORDER BY keywords.bug_id, keyworddefs.name};
$sth = $dbh->prepare($query);
$sth->execute;
my $lastb = 0;
my @list;
my %realk;
while (1) {
my ($b, $k) = $sth->fetchrow_array;
if (!defined $b || $b != $lastb) {
if (@list) {
$realk{$lastb} = join(', ', @list);
}
last unless $b;
$lastb = $b;
@list = ();
}
push(@list, $k);
} }
}
if (@badbugs) { my @badbugs = ();
@badbugs = sort {$a <=> $b} @badbugs;
Alert(scalar(@badbugs) . " bug(s) found with incorrect keyword cache: " . foreach my $b (keys(%$keyword)) {
BugListLinks(@badbugs)); if (!exists $realk{$b} || $realk{$b} ne $keyword->{$b}) {
push(@badbugs, $b);
my $sth_update = $dbh->prepare(q{UPDATE bugs }
SET keywords = ? }
WHERE bug_id = ?}); foreach my $b (keys(%realk)) {
if (!exists $keyword->{$b}) {
if (defined $cgi->param('rebuildkeywordcache')) { push(@badbugs, $b);
Status("OK, now fixing keyword cache."); }
foreach my $b (@badbugs) { }
my $k = ''; if (@badbugs) {
if (exists($realk{$b})) { @badbugs = sort {$a <=> $b} @badbugs;
$k = $realk{$b}; Alert(scalar(@badbugs) . " bug(s) found with incorrect keyword cache: " .
BugListLinks(@badbugs));
my $sth_update = $dbh->prepare(q{UPDATE bugs
SET keywords = ?
WHERE bug_id = ?});
if (defined $cgi->param('rebuildkeywordcache')) {
Status("OK, now fixing keyword cache.");
foreach my $b (@badbugs) {
my $k = '';
if (exists($realk{$b})) {
$k = $realk{$b};
}
$sth_update->execute($k, $b);
} }
$sth_update->execute($k, $b); Status("Keyword cache fixed.");
} else {
print qq{<a href="sanitycheck.cgi?rebuildkeywordcache=1">Click here to rebuild the keyword cache</a><p>\n};
} }
Status("Keyword cache fixed.");
} else {
print qq{<a href="sanitycheck.cgi?rebuildkeywordcache=1">Click here to rebuild the keyword cache</a><p>\n};
} }
}
if (defined $cgi->param('rebuildkeywordcache')) { if (defined $cgi->param('rebuildkeywordcache')) {
$dbh->bz_unlock_tables(); $dbh->bz_unlock_tables();
}
} }
########################################################################### ###########################################################################
...@@ -888,7 +917,7 @@ BugCheck("bugs INNER JOIN products ON bugs.product_id = products.id " . ...@@ -888,7 +917,7 @@ BugCheck("bugs INNER JOIN products ON bugs.product_id = products.id " .
Status("Checking for bad values in group_control_map"); Status("Checking for bad values in group_control_map");
my $groups = join(", ", (CONTROLMAPNA, CONTROLMAPSHOWN, CONTROLMAPDEFAULT, my $groups = join(", ", (CONTROLMAPNA, CONTROLMAPSHOWN, CONTROLMAPDEFAULT,
CONTROLMAPMANDATORY)); CONTROLMAPMANDATORY));
$query = qq{ my $query = qq{
SELECT COUNT(product_id) SELECT COUNT(product_id)
FROM group_control_map FROM group_control_map
WHERE membercontrol NOT IN( $groups ) WHERE membercontrol NOT IN( $groups )
......
...@@ -122,6 +122,6 @@ ...@@ -122,6 +122,6 @@
[% '<link rel="Administration" title="Whining" [% '<link rel="Administration" title="Whining"
href="editwhines.cgi">' IF user.groups.bz_canusewhines %] href="editwhines.cgi">' IF user.groups.bz_canusewhines %]
[% '<link rel="Administration" title="Sanity Check" [% '<link rel="Administration" title="Sanity Check"
href="sanitycheck.cgi">' IF user.groups.tweakparams %] href="sanitycheck.cgi">' IF user.groups.editcomponents %]
[% END %] [% END %]
[% END %] [% END %]
...@@ -48,6 +48,8 @@ ...@@ -48,6 +48,8 @@
<li><span class="separator">[% sep %]</span><a href="editparams.cgi">Parameters</a></li> <li><span class="separator">[% sep %]</span><a href="editparams.cgi">Parameters</a></li>
[% sep = "| " %] [% sep = "| " %]
<li><span class="separator">[% sep %]</span><a href="editsettings.cgi">User Preferences</a></li> <li><span class="separator">[% sep %]</span><a href="editsettings.cgi">User Preferences</a></li>
[% END %]
[% IF user.groups.editcomponents %]
<li><span class="separator">[% sep %]</span><a href="sanitycheck.cgi">Sanity Check</a></li> <li><span class="separator">[% sep %]</span><a href="sanitycheck.cgi">Sanity Check</a></li>
[% END %] [% END %]
[% IF user.groups.editusers || user.can_bless %] [% IF user.groups.editusers || user.can_bless %]
......
...@@ -94,7 +94,7 @@ function normal_keypress_handler( aEvent ) { ...@@ -94,7 +94,7 @@ function normal_keypress_handler( aEvent ) {
[%- IF user.groups.bz_canusewhines %] [%- IF user.groups.bz_canusewhines %]
<text class="text-link" onclick="load_relative_url('editwhines.cgi')" value="edit whining"/> <text class="text-link" onclick="load_relative_url('editwhines.cgi')" value="edit whining"/>
[%- END %] [%- END %]
[%- IF user.groups.tweakparams %] [%- IF user.groups.editcomponents %]
<text class="text-link" onclick="load_relative_url('sanitycheck.cgi')" value="sanity check"/> <text class="text-link" onclick="load_relative_url('sanitycheck.cgi')" value="sanity check"/>
[%- END %] [%- END %]
[%- IF user.authorizer.can_logout %] [%- IF user.authorizer.can_logout %]
......
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