Commit 3bc6ea42 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 347439: Implement support for referential integrity in Bugzilla::DB::Schema…

Bug 347439: Implement support for referential integrity in Bugzilla::DB::Schema and implement it on profiles_activity Patch By Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat
parent 3a943fe0
...@@ -515,10 +515,11 @@ sub bz_alter_column_raw { ...@@ -515,10 +515,11 @@ sub bz_alter_column_raw {
my @statements = $self->_bz_real_schema->get_alter_column_ddl( my @statements = $self->_bz_real_schema->get_alter_column_ddl(
$table, $name, $new_def, $table, $name, $new_def,
defined $set_nulls_to ? $self->quote($set_nulls_to) : undef); defined $set_nulls_to ? $self->quote($set_nulls_to) : undef);
my $new_ddl = $self->_bz_schema->get_type_ddl($new_def); my $new_ddl = $self->_bz_schema->get_display_ddl($table, $name, $new_def);
print "Updating column $name in table $table ...\n"; print "Updating column $name in table $table ...\n";
if (defined $current_def) { if (defined $current_def) {
my $old_ddl = $self->_bz_schema->get_type_ddl($current_def); my $old_ddl = $self->_bz_schema->get_display_ddl($table, $name,
$current_def);
print "Old: $old_ddl\n"; print "Old: $old_ddl\n";
} }
print "New: $new_ddl\n"; print "New: $new_ddl\n";
......
...@@ -176,9 +176,17 @@ sub get_alter_column_ddl { ...@@ -176,9 +176,17 @@ sub get_alter_column_ddl {
# keys are not allowed. # keys are not allowed.
delete $new_def_copy{PRIMARYKEY}; delete $new_def_copy{PRIMARYKEY};
} }
# CHANGE COLUMN doesn't support REFERENCES
delete $new_def_copy{REFERENCES};
my $new_ddl = $self->get_type_ddl(\%new_def_copy); my $new_ddl = $self->get_type_ddl(\%new_def_copy);
my @statements; my @statements;
# Drop the FK if the new definition doesn't have one.
if ($old_def->{REFERENCES} && !$new_def->{REFERENCES}) {
push(@statements, $self->_get_drop_fk_sql($table, $column, $old_def));
}
push(@statements, "UPDATE $table SET $column = $set_nulls_to push(@statements, "UPDATE $table SET $column = $set_nulls_to
WHERE $column IS NULL") if defined $set_nulls_to; WHERE $column IS NULL") if defined $set_nulls_to;
push(@statements, "ALTER TABLE $table CHANGE COLUMN push(@statements, "ALTER TABLE $table CHANGE COLUMN
...@@ -187,9 +195,30 @@ sub get_alter_column_ddl { ...@@ -187,9 +195,30 @@ sub get_alter_column_ddl {
# Dropping a PRIMARY KEY needs an explicit DROP PRIMARY KEY # Dropping a PRIMARY KEY needs an explicit DROP PRIMARY KEY
push(@statements, "ALTER TABLE $table DROP PRIMARY KEY"); push(@statements, "ALTER TABLE $table DROP PRIMARY KEY");
} }
# Add the FK if the new definition has one and the old definition doesn't.
if ($new_def->{REFERENCES} && !$old_def->{REFERENCES}) {
push(@statements, $self->_get_add_fk_sql($table, $column, $new_def));
}
return @statements; return @statements;
} }
sub _get_drop_fk_sql {
my ($self, $table, $column, $old_def) = @_;
my $fk_name = $self->_get_fk_name($table, $column, $old_def->{REFERENCES});
my @sql = ("ALTER TABLE $table DROP FOREIGN KEY $fk_name");
my $dbh = Bugzilla->dbh;
# MySQL requires, and will create, an index on any column with
# an FK. It will name it after the fk, which we never do.
# So if there's an index named after the fk, we also have to delete it.
if ($dbh->bz_index_info_real($table, $fk_name)) {
push(@sql, $self->get_drop_index_ddl($table, $fk_name));
}
return @sql;
}
sub get_drop_index_ddl { sub get_drop_index_ddl {
my ($self, $table, $name) = @_; my ($self, $table, $name) = @_;
return ("DROP INDEX \`$name\` ON $table"); return ("DROP INDEX \`$name\` ON $table");
......
...@@ -49,6 +49,39 @@ sub indicate_progress { ...@@ -49,6 +49,39 @@ sub indicate_progress {
} }
} }
# This is used before adding a foreign key to a column, to make sure
# that the database won't fail adding the key.
sub check_references {
my ($table, $column, $foreign_table, $foreign_column) = @_;
my $dbh = Bugzilla->dbh;
my $bad_values = $dbh->selectcol_arrayref(
"SELECT DISTINCT $table.$column
FROM $table LEFT JOIN $foreign_table
ON $table.$column = $foreign_table.$foreign_column
WHERE $foreign_table.$foreign_column IS NULL");
if (@$bad_values) {
my $values = join(', ', @$bad_values);
print <<EOT;
ERROR: There are invalid values for the $column column in the $table
table. (These values do not exist in the $foreign_table table, in the
$foreign_column column.)
Before continuing with checksetup, you will need to fix these values,
either by deleting these rows from the database, or changing the values
of $column in $table to point to valid values in $foreign_table.$foreign_column.
The bad values from the $table.$column column are:
$values
EOT
# I just picked a number above 2, to be considered "abnormal exit."
exit 3;
}
}
# NOTE: This is NOT the function for general table updates. See # NOTE: This is NOT the function for general table updates. See
# update_table_definitions for that. This is only for the fielddefs table. # update_table_definitions for that. This is only for the fielddefs table.
sub update_fielddefs_definition { sub update_fielddefs_definition {
...@@ -519,6 +552,20 @@ sub update_table_definitions { ...@@ -519,6 +552,20 @@ sub update_table_definitions {
$dbh->bz_add_column('milestones', 'id', $dbh->bz_add_column('milestones', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1}); {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
# Referential Integrity begins here
check_references('profiles_activity', 'userid', 'profiles', 'userid');
$dbh->bz_alter_column('profiles_activity', 'userid',
{TYPE => 'INT3', NOTNULL => 1, REFERENCES =>
{TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}});
check_references('profiles_activity', 'who', 'profiles', 'userid');
$dbh->bz_alter_column('profiles_activity', 'who',
{TYPE => 'INT3', NOTNULL => 1, REFERENCES =>
{TABLE => 'profiles', COLUMN => 'userid'}});
check_references('profiles_activity', 'fieldid', 'fielddefs', 'id');
$dbh->bz_alter_column('profiles_activity', 'fieldid',
{TYPE => 'INT3', NOTNULL => 1, REFERENCES =>
{TABLE => 'fielddefs', COLUMN => 'id'}});
################################################################ ################################################################
# New --TABLE-- changes should go *** A B O V E *** this point # # New --TABLE-- changes should go *** A B O V E *** this point #
################################################################ ################################################################
......
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