Fix foreign key DELETE and UPDATE actions, fix user removal

hinted-selects
Vitaliy Filippov 2014-08-01 18:44:49 +04:00
parent 924dff1b2f
commit 02050d63c6
8 changed files with 53 additions and 132 deletions

View File

@ -233,13 +233,9 @@ sub DEPENDENCIES
# Product may have classification value_field, but it's not
# stored in bugs table so we shouldn't check it
next if $deps->{$field->name} || $field->name eq 'product';
if ($field->visibility_field)
for (qw(visibility_field value_field null_field))
{
$deps->{$field->name}->{$field->visibility_field->name} = 1;
}
if ($field->value_field)
{
$deps->{$field->name}->{$field->value_field->name} = 1;
$deps->{$field->name}->{$field->$_->name} = 1 if $field->$_;
}
}
@ -3544,8 +3540,7 @@ sub GetBugActivity
}
my $query =
"SELECT fielddefs.name, a.attach_id, " .
$dbh->sql_date_format('a.bug_when', '%Y.%m.%d %H:%i:%s') .
"SELECT fielddefs.name, a.attach_id, " . $dbh->sql_date_format('a.bug_when') .
" bug_when, a.removed, a.added, profiles.login_name, null AS comment_id, null AS comment_count" .
" FROM bugs_activity a $suppjoins".
" LEFT JOIN fielddefs ON a.fieldid = fielddefs.id".

View File

@ -601,14 +601,26 @@ sub bz_add_column {
}
}
sub bz_add_fk {
sub bz_add_fk
{
my ($self, $table, $column, $def) = @_;
my $check = 1;
my $col_def = $self->bz_column_info($table, $column);
if (!$col_def->{REFERENCES}) {
$self->_check_references($table, $column, $def);
print get_text('install_fk_add',
{ table => $table, column => $column, fk => $def })
if ($col_def->{REFERENCES} && (lc($col_def->{REFERENCES}->{DELETE} || 'RESTRICT') ne lc($def->{DELETE} || 'RESTRICT')
|| lc($col_def->{REFERENCES}->{UPDATE} || 'CASCADE') ne lc($def->{UPDATE} || 'CASCADE')))
{
# Fix UPDATE and DELETE actions
$check = 0;
delete $col_def->{REFERENCES};
print get_text('install_fk_drop', { table => $table, column => $column, fk => $def })
. "\n" if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
$self->do($_) foreach $self->_bz_real_schema->get_drop_fk_sql($table, $column, $def);
}
if (!$col_def->{REFERENCES})
{
$self->_check_references($table, $column, $def) if $check;
print get_text('install_fk_add', { table => $table, column => $column, fk => $def })
. "\n" if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
my @sql = $self->_bz_real_schema->get_add_fk_sql($table, $column, $def);
$self->do($_) foreach @sql;
@ -1371,24 +1383,21 @@ sub _bz_real_schema {
=cut
sub _bz_store_real_schema {
sub _bz_store_real_schema
{
my ($self) = @_;
# Make sure that there's a schema to update
my $table_size = $self->selectrow_array("SELECT COUNT(*) FROM bz_schema");
die "Attempted to update the bz_schema table but there's nothing "
. "there to update. Run checksetup." unless $table_size;
$self->_bz_real_schema || die "Attempted to update the bz_schema table but there's nothing there to update. Run checksetup.";
# We want to store the current object, not one
# that we read from the database. So we use the actual hash
# member instead of the subroutine call. If the hash
# member is not defined, we will (and should) fail.
my $update_schema = $self->{private_real_schema};
my $update_schema = $self->_bz_real_schema;
my $store_me = $update_schema->serialize_abstract();
my $schema_version = $update_schema->SCHEMA_VERSION;
my $sth = $self->prepare("UPDATE bz_schema
SET schema_data = ?, version = ?");
my $sth = $self->prepare("UPDATE bz_schema SET schema_data = ?, version = ?");
$sth->bind_param(1, $store_me, $self->BLOB_TYPE);
$sth->bind_param(2, $schema_version);
$sth->execute();

View File

@ -239,7 +239,7 @@ sub sql_to_days {
sub sql_date_format {
my ($self, $date, $format) = @_;
$format = "%Y.%m.%d %H:%i:%s" if !$format;
$format = "%Y-%m-%d %H:%i:%s" if !$format;
return "DATE_FORMAT($date, " . $self->quote($format) . ")";
}

View File

@ -310,7 +310,7 @@ use constant ABSTRACT_SCHEMA => {
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
bug_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}},
attach_id => {TYPE => 'INT4', REFERENCES => {TABLE => 'attachments', COLUMN => 'attach_id', DELETE => 'CASCADE'}},
who => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}},
who => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
bug_when => {TYPE => 'DATETIME', NOTNULL => 1},
fieldid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'fielddefs', COLUMN => 'id', DELETE => 'CASCADE'}},
added => {TYPE => 'LONGTEXT'},
@ -341,7 +341,7 @@ use constant ABSTRACT_SCHEMA => {
FIELDS => [
comment_id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
bug_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}},
who => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}},
who => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
bug_when => {TYPE => 'DATETIME', NOTNULL => 1},
work_time => {TYPE => 'decimal(7,2)', NOTNULL => 1, DEFAULT => '0'},
thetext => {TYPE => 'LONGTEXT', NOTNULL => 1},
@ -362,8 +362,8 @@ use constant ABSTRACT_SCHEMA => {
# Editable comments (CustIS Bug 134368)
longdescs_history => {
FIELDS => [
bug_id => { TYPE => 'INT4', NOTNULL => 1, REFERENCES => { TABLE => 'bugs', COLUMN => 'bug_id' } },
who => { TYPE => 'INT4', NOTNULL => 1, REFERENCES => { TABLE => 'profiles', COLUMN => 'userid' } },
bug_id => { TYPE => 'INT4', NOTNULL => 1, REFERENCES => { TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE' } },
who => { TYPE => 'INT4', NOTNULL => 1, REFERENCES => { TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE' } },
bug_when => { TYPE => 'DATETIME', NOTNULL => 1 },
oldthetext => { TYPE => 'LONGTEXT', NOTNULL => 1 },
thetext => { TYPE => 'LONGTEXT', NOTNULL => 1 },
@ -733,7 +733,7 @@ use constant ABSTRACT_SCHEMA => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
userid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
who => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}},
who => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
profiles_when => {TYPE => 'DATETIME', NOTNULL => 1},
fieldid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'fielddefs', COLUMN => 'id'}},
oldvalue => {TYPE => 'TINYTEXT'},
@ -773,7 +773,7 @@ use constant ABSTRACT_SCHEMA => {
emailin_aliases => {
FIELDS => [
address => {TYPE => 'varchar(255)', NOTNULL => 1},
userid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}},
userid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
fromldap => {TYPE => 'BOOLEAN'},
isprimary => {TYPE => 'BOOLEAN'},
],
@ -796,7 +796,7 @@ use constant ABSTRACT_SCHEMA => {
namedqueries => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
userid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
userid => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
name => {TYPE => 'varchar(255)', NOTNULL => 1},
query => {TYPE => 'LONGTEXT', NOTNULL => 1},
],
@ -810,7 +810,7 @@ use constant ABSTRACT_SCHEMA => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
query_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'namedqueries', COLUMN => 'id'}},
user_id => {TYPE => 'INT4', REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}},
user_id => {TYPE => 'INT4', REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'SET NULL'}},
flags => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0},
message => {TYPE => 'LONGTEXT', NOTNULL => 1},
sql_code => {TYPE => 'LONGTEXT'},
@ -849,7 +849,7 @@ use constant ABSTRACT_SCHEMA => {
reports => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
user_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
user_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
name => {TYPE => 'varchar(255)', NOTNULL => 1},
query => {TYPE => 'LONGTEXT', NOTNULL => 1},
],
@ -891,9 +891,9 @@ use constant ABSTRACT_SCHEMA => {
ip_addr => {TYPE => 'varchar(40)', NOTNULL => 1},
],
INDEXES => [
# We do lookups by every item in the table simultaneously, but
# We do lookups by every item in the table simultaneously, but
# having an index with all three items would be the same size as
# the table. So instead we have an index on just the smallest item,
# the table. So instead we have an index on just the smallest item,
# to speed lookups.
login_failure_user_id_idx => ['user_id'],
],
@ -905,7 +905,7 @@ use constant ABSTRACT_SCHEMA => {
# for these changes.
tokens => {
FIELDS => [
userid => {TYPE => 'INT4', REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
userid => {TYPE => 'INT4', REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
issuedate => {TYPE => 'DATETIME', NOTNULL => 1},
token => {TYPE => 'varchar(16)', NOTNULL => 1, PRIMARYKEY => 1},
tokentype => {TYPE => 'varchar(8)', NOTNULL => 1},
@ -961,7 +961,6 @@ use constant ABSTRACT_SCHEMA => {
# if GRANT_REGEXP - record was created by evaluating a regexp
user_group_map => {
FIELDS => [
user_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}},
group_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'groups', COLUMN => 'id', DELETE => 'CASCADE'}},
isbless => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
grant_type => {TYPE => 'INT1', NOTNULL => 1, DEFAULT => GRANT_DIRECT},
@ -1006,18 +1005,11 @@ use constant ABSTRACT_SCHEMA => {
# in order to see a named query somebody else shares.
namedquery_group_map => {
FIELDS => [
namedquery_id => {TYPE => 'INT4', NOTNULL => 1,
REFERENCES => {TABLE => 'namedqueries',
COLUMN => 'id',
DELETE => 'CASCADE'}},
group_id => {TYPE => 'INT4', NOTNULL => 1,
REFERENCES => {TABLE => 'groups',
COLUMN => 'id',
DELETE => 'CASCADE'}},
namedquery_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'namedqueries', COLUMN => 'id', DELETE => 'CASCADE'}},
group_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'groups', COLUMN => 'id', DELETE => 'CASCADE'}},
],
INDEXES => [
namedquery_group_map_namedquery_id_idx =>
{FIELDS => [qw(namedquery_id)], TYPE => 'UNIQUE'},
namedquery_group_map_namedquery_id_idx => {FIELDS => [qw(namedquery_id)], TYPE => 'UNIQUE'},
namedquery_group_map_group_id_idx => ['group_id'],
],
},

View File

@ -1337,7 +1337,7 @@ sub update_control_lists
$controlling_value_id = Bugzilla->get_field($controlling_field_id)->value_type->new($controlling_value_id);
$controlling_value_id = $controlling_value_id ? $controlling_value_id->id : return undef;
# Save all visible, nullable and clone flags at once
my $mod = {};
my $mod = { del => [], add => [] };
for my $f (Bugzilla->get_fields({ obsolete => 0, visibility_field_id => $controlling_field_id }))
{
push @{$mod->{$params->{'is_visible_'.$f->name} ? 'add' : 'del'}}, [ $f->id, FLAG_VISIBLE ];
@ -1367,7 +1367,7 @@ sub update_control_lists
}
# Save all dependent defaults at once
my $touched = { map { $_->[0] => 1 } (@{$mod->{add}}, @{$mod->{del}}) };
$mod = {};
$mod = { del => [], add => [] };
for my $f (Bugzilla->get_fields({ obsolete => 0, default_field_id => $controlling_field_id }))
{
next if $f eq 'version' || $f eq 'target_milestone'; # FIXME: default version is hardcoded to depend on component, default milestone is hardcoded to depend on product

View File

@ -821,15 +821,6 @@ WHERE description LIKE\'%[CC:%\'');
);
}
# Fix bugs_activity.fieldid foreign key
{
my $fk = $dbh->bz_fk_info('bugs_activity', 'fieldid');
if ($fk && (!$fk->{DELETE} || $fk->{DELETE} ne 'CASCADE'))
{
$dbh->bz_drop_fk('bugs_activity', 'fieldid');
}
}
# Varchar is VARIABLE, it's generally pointless to set a size limit less than 255 chars for it
_set_varchar_255();
@ -3458,22 +3449,14 @@ sub _convert_multiselects
}
}
sub _add_foreign_keys_to_multiselects {
sub _add_foreign_keys_to_multiselects
{
my $dbh = Bugzilla->dbh;
my $names = $dbh->selectcol_arrayref(
'SELECT name
FROM fielddefs
WHERE type = ' . FIELD_TYPE_MULTI_SELECT);
foreach my $name (@$names) {
$dbh->bz_add_fk("bug_$name", "bug_id", {TABLE => 'bugs',
COLUMN => 'bug_id',
DELETE => 'CASCADE',});
$dbh->bz_add_fk("bug_$name", "value_id", {TABLE => $name,
COLUMN => 'id',
DELETE => 'RESTRICT',});
my $names = $dbh->selectcol_arrayref('SELECT name FROM fielddefs WHERE type=' . FIELD_TYPE_MULTI_SELECT);
foreach my $name (@$names)
{
$dbh->bz_add_fk("bug_$name", "bug_id", {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'});
$dbh->bz_add_fk("bug_$name", "value_id", {TABLE => $name, COLUMN => 'id', DELETE => 'CASCADE'});
}
}

View File

@ -521,35 +521,11 @@ if ($action eq 'search') {
$updatedbugs{$bug_id} = 1;
}
# Simple deletions in referred tables.
$dbh->do('DELETE FROM email_setting WHERE user_id = ?', undef,
$otherUserID);
$dbh->do('DELETE FROM logincookies WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM namedqueries WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM namedqueries_link_in_footer WHERE user_id = ?', undef,
$otherUserID);
if ($namedqueries_as_string) {
$dbh->do('DELETE FROM namedquery_group_map WHERE namedquery_id IN ' .
"($namedqueries_as_string)");
}
$dbh->do('DELETE FROM profile_setting WHERE user_id = ?', undef,
$otherUserID);
$dbh->do('DELETE FROM profiles_activity WHERE userid = ? OR who = ?', undef,
($otherUserID, $otherUserID));
$dbh->do('DELETE FROM tokens WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM user_group_map WHERE user_id = ?', undef,
$otherUserID);
$dbh->do('DELETE FROM votes WHERE who = ?', undef, $otherUserID);
$dbh->do('DELETE FROM watch WHERE watcher = ? OR watched = ?', undef,
($otherUserID, $otherUserID));
# Deletions in referred tables which need LogActivityEntry.
my $buglist = $dbh->selectcol_arrayref('SELECT bug_id FROM cc WHERE who = ?',
undef, $otherUserID);
my $buglist = $dbh->selectcol_arrayref('SELECT bug_id FROM cc WHERE who = ?', undef, $otherUserID);
$dbh->do('DELETE FROM cc WHERE who = ?', undef, $otherUserID);
foreach my $bug_id (@$buglist) {
LogActivityEntry($bug_id, 'cc', $otherUser->login, '', $userid,
$timestamp);
LogActivityEntry($bug_id, 'cc', $otherUser->login, '', $userid, $timestamp);
$sth_set_bug_timestamp->execute($timestamp, $bug_id);
$updatedbugs{$bug_id} = 1;
}
@ -557,40 +533,6 @@ if ($action eq 'search') {
# Even more complex deletions in referred tables.
my $id;
# 1) Series
my $sth_seriesid = $dbh->prepare(
'SELECT series_id FROM series WHERE creator = ?');
my $sth_deleteSeries = $dbh->prepare(
'DELETE FROM series WHERE series_id = ?');
my $sth_deleteSeriesData = $dbh->prepare(
'DELETE FROM series_data WHERE series_id = ?');
$sth_seriesid->execute($otherUserID);
while ($id = $sth_seriesid->fetchrow_array()) {
$sth_deleteSeriesData->execute($id);
$sth_deleteSeries->execute($id);
}
# 2) Whines
my $sth_whineidFromEvents = $dbh->prepare(
'SELECT id FROM whine_events WHERE owner_userid = ?');
my $sth_deleteWhineEvent = $dbh->prepare(
'DELETE FROM whine_events WHERE id = ?');
my $sth_deleteWhineQuery = $dbh->prepare(
'DELETE FROM whine_queries WHERE eventid = ?');
my $sth_deleteWhineSchedule = $dbh->prepare(
'DELETE FROM whine_schedules WHERE eventid = ?');
$dbh->do('DELETE FROM whine_schedules WHERE mailto = ? AND mailto_type = ?',
undef, ($otherUserID, MAILTO_USER));
$sth_whineidFromEvents->execute($otherUserID);
while ($id = $sth_whineidFromEvents->fetchrow_array()) {
$sth_deleteWhineQuery->execute($id);
$sth_deleteWhineSchedule->execute($id);
$sth_deleteWhineEvent->execute($id);
}
# 3) Bugs
# 3.1) fall back to the default assignee
$buglist = $dbh->selectall_arrayref(

View File

@ -68,7 +68,7 @@
<th>Group set:</th>
<td>
[% IF otheruser.groups.size %]
<ul>
<ul style="margin: 0">
[% FOREACH group = otheruser.groups %]
<li>[% group.name FILTER html %]</li>
[% END %]