diff options
author | mkanat%bugzilla.org <> | 2007-03-11 01:21:19 +0100 |
---|---|---|
committer | mkanat%bugzilla.org <> | 2007-03-11 01:21:19 +0100 |
commit | 40a199771b751ebf7378efe32a68584ad7570ee7 (patch) | |
tree | fd2dbdbdbb782cd3cb5fa3bd4e09dd5be9fa3c09 /Bugzilla | |
parent | 2884cd8606668e3174617cecf159e9e2bd128edd (diff) | |
download | bugzilla-40a199771b751ebf7378efe32a68584ad7570ee7.tar.gz bugzilla-40a199771b751ebf7378efe32a68584ad7570ee7.tar.xz |
Bug 373442: Add referential integrity against the profiles table in some more simple places
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat
Diffstat (limited to 'Bugzilla')
-rw-r--r-- | Bugzilla/DB.pm | 92 | ||||
-rw-r--r-- | Bugzilla/DB/Schema.pm | 208 | ||||
-rw-r--r-- | Bugzilla/DB/Schema/Mysql.pm | 11 | ||||
-rw-r--r-- | Bugzilla/Install/DB.pm | 53 |
4 files changed, 192 insertions, 172 deletions
diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index e4e30a8d3..22c6bbafa 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -43,6 +43,7 @@ use Bugzilla::Error; use Bugzilla::DB::Schema; use List::Util qw(max); +use Storable qw(dclone); ##################################################################### # Constants @@ -428,6 +429,23 @@ sub bz_populate_enum_tables { } } +sub bz_setup_foreign_keys { + my ($self) = @_; + + # We use _bz_schema because bz_add_table has removed all REFERENCES + # items from _bz_real_schema. + my @tables = $self->_bz_schema->get_table_list(); + foreach my $table (@tables) { + my @columns = $self->_bz_schema->get_table_columns($table); + foreach my $column (@columns) { + my $def = $self->_bz_schema->get_column_abstract($table, $column); + if ($def->{REFERENCES}) { + $self->bz_add_fk($table, $column, $def->{REFERENCES}); + } + } + } +} + ##################################################################### # Schema Modification Methods ##################################################################### @@ -463,6 +481,24 @@ sub bz_add_column { } } +sub bz_add_fk { + my ($self, $table, $column, $def) = @_; + + my $col_def = $self->bz_column_info($table, $column); + if (!$col_def->{REFERENCES}) { + $self->_check_references($table, $column, $def->{TABLE}, + $def->{COLUMN}); + 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; + $col_def->{REFERENCES} = $def; + $self->_bz_real_schema->set_column($table, $column, $col_def); + $self->_bz_store_real_schema; + } +} + sub bz_alter_column { my ($self, $table, $name, $new_def, $set_nulls_to) = @_; @@ -515,11 +551,10 @@ sub bz_alter_column_raw { my @statements = $self->_bz_real_schema->get_alter_column_ddl( $table, $name, $new_def, defined $set_nulls_to ? $self->quote($set_nulls_to) : undef); - my $new_ddl = $self->_bz_schema->get_display_ddl($table, $name, $new_def); + my $new_ddl = $self->_bz_schema->get_type_ddl($new_def); print "Updating column $name in table $table ...\n"; if (defined $current_def) { - my $old_ddl = $self->_bz_schema->get_display_ddl($table, $name, - $current_def); + my $old_ddl = $self->_bz_schema->get_type_ddl($current_def); print "Old: $old_ddl\n"; } print "New: $new_ddl\n"; @@ -569,8 +604,17 @@ sub bz_add_table { if (!$table_exists) { $self->_bz_add_table_raw($name); - $self->_bz_real_schema->add_table($name, - $self->_bz_schema->get_table_abstract($name)); + my $table_def = dclone($self->_bz_schema->get_table_abstract($name)); + + my %fields = @{$table_def->{FIELDS}}; + foreach my $col (keys %fields) { + # Foreign Key references have to be added by Install::DB after + # initial table creation, because column names have changed + # over history and it's impossible to keep track of that info + # in ABSTRACT_SCHEMA. + delete $fields{$col}->{REFERENCES}; + } + $self->_bz_real_schema->add_table($name, $table_def); $self->_bz_store_real_schema; } } @@ -751,7 +795,10 @@ sub _bz_get_initial_schema { sub bz_column_info { my ($self, $table, $column) = @_; - return $self->_bz_real_schema->get_column_abstract($table, $column); + my $def = $self->_bz_real_schema->get_column_abstract($table, $column); + # We dclone it so callers can't modify the Schema. + $def = dclone($def) if defined $def; + return $def; } sub bz_index_info { @@ -1050,6 +1097,39 @@ sub _bz_populate_enum_table { } } +# 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 ($self, $table, $column, $foreign_table, $foreign_column) = @_; + + my $bad_values = $self->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 + AND $table.$column IS NOT 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; + } +} + 1; __END__ diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 6b596b161..15d7dd8b2 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -40,6 +40,7 @@ use Bugzilla::Hook; use Bugzilla::Util; use Bugzilla::Constants; +use Carp qw(confess); use Hash::Util qw(lock_value unlock_hash lock_keys unlock_keys); use Safe; # Historical, needed for SCHEMA_VERSION = '1.00' @@ -98,20 +99,6 @@ more about how this integrates into the rest of Bugzilla. =over -=item C<TABLES_FIRST> - -Because of L</"Referential Integrity">, certain tables must be created -before other tables. L</ABSTRACT_SCHEMA> can't specify this, because -it's a hash. So we specify it here. When calling C<get_table_list()>, -these tables will be returned before the other tables. - -=cut - -use constant TABLES_FIRST => qw( - fielddefs - profiles -); - =item C<SCHEMA_VERSION> The 'version' of the internal schema structure. This version number @@ -194,7 +181,7 @@ The column pointed at in that table. =item C<DELETE> What to do if the row in the parent table is deleted. Choices are -C<RESTRICT> or C<CASCADE>. +C<RESTRICT>, C<CASCADE>, or C<SET NULL>. C<RESTRICT> means the deletion of the row in the parent table will be forbidden by the database if there is a row in I<this> table that @@ -203,17 +190,19 @@ C<DELETE>. C<CASCADE> means that this row will be deleted along with that row. +C<SET NULL> means that the column will be set to C<NULL> when the parent +row is deleted. Note that this is only valid if the column can actually +be set to C<NULL>. (That is, the column isn't C<NOT NULL>.) + =item C<UPDATE> -What to do if the value in the parent table is updated. It has the -same choices as L</DELETE>, except it defaults to C<CASCADE>, which means -"also update this column in this table." +What to do if the value in the parent table is updated. You can set this +to C<CASCADE> or C<RESTRICT>, which mean the same thing as they do for +L</DELETE>. This variable defaults to C<CASCADE>, which means "also +update this column in this table." =back -Note that not all our supported databases actually enforce C<RESTRICT> -and C<CASCADE>, so don't depend on them. - =cut use constant SCHEMA_VERSION => '2.00'; @@ -293,7 +282,9 @@ use constant ABSTRACT_SCHEMA => { FIELDS => [ bug_id => {TYPE => 'INT3', NOTNULL => 1}, attach_id => {TYPE => 'INT3'}, - who => {TYPE => 'INT3', NOTNULL => 1}, + who => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid'}}, bug_when => {TYPE => 'DATETIME', NOTNULL => 1}, fieldid => {TYPE => 'INT3', NOTNULL => 1}, added => {TYPE => 'TINYTEXT'}, @@ -310,7 +301,10 @@ use constant ABSTRACT_SCHEMA => { cc => { FIELDS => [ bug_id => {TYPE => 'INT3', NOTNULL => 1}, - who => {TYPE => 'INT3', NOTNULL => 1}, + who => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, ], INDEXES => [ cc_bug_id_idx => {FIELDS => [qw(bug_id who)], @@ -359,7 +353,10 @@ use constant ABSTRACT_SCHEMA => { votes => { FIELDS => [ - who => {TYPE => 'INT3', NOTNULL => 1}, + who => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, bug_id => {TYPE => 'INT3', NOTNULL => 1}, vote_count => {TYPE => 'INT2', NOTNULL => 1}, ], @@ -379,7 +376,9 @@ use constant ABSTRACT_SCHEMA => { mimetype => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, ispatch => {TYPE => 'BOOLEAN'}, filename => {TYPE => 'varchar(100)', NOTNULL => 1}, - submitter_id => {TYPE => 'INT3', NOTNULL => 1}, + submitter_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid'}}, isobsolete => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}, isprivate => {TYPE => 'BOOLEAN', NOTNULL => 1, @@ -725,7 +724,10 @@ use constant ABSTRACT_SCHEMA => { email_setting => { FIELDS => [ - user_id => {TYPE => 'INT3', NOTNULL => 1}, + user_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, relationship => {TYPE => 'INT1', NOTNULL => 1}, event => {TYPE => 'INT1', NOTNULL => 1}, ], @@ -738,8 +740,14 @@ use constant ABSTRACT_SCHEMA => { watch => { FIELDS => [ - watcher => {TYPE => 'INT3', NOTNULL => 1}, - watched => {TYPE => 'INT3', NOTNULL => 1}, + watcher => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, + watched => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, ], INDEXES => [ watch_watcher_idx => {FIELDS => [qw(watcher watched)], @@ -752,7 +760,10 @@ use constant ABSTRACT_SCHEMA => { FIELDS => [ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1}, - userid => {TYPE => 'INT3', NOTNULL => 1}, + userid => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, name => {TYPE => 'varchar(64)', NOTNULL => 1}, query => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, query_type => {TYPE => 'BOOLEAN', NOTNULL => 1}, @@ -765,8 +776,14 @@ use constant ABSTRACT_SCHEMA => { namedqueries_link_in_footer => { FIELDS => [ - namedquery_id => {TYPE => 'INT3', NOTNULL => 1}, - user_id => {TYPE => 'INT3', NOTNULL => 1}, + namedquery_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'namedqueries', + COLUMN => 'id', + DELETE => 'CASCADE'}}, + user_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, ], INDEXES => [ namedqueries_link_in_footer_id_idx => {FIELDS => [qw(namedquery_id user_id)], @@ -778,7 +795,10 @@ use constant ABSTRACT_SCHEMA => { component_cc => { FIELDS => [ - user_id => {TYPE => 'INT3', NOTNULL => 1}, + user_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, component_id => {TYPE => 'INT2', NOTNULL => 1}, ], INDEXES => [ @@ -794,7 +814,10 @@ use constant ABSTRACT_SCHEMA => { FIELDS => [ cookie => {TYPE => 'varchar(16)', NOTNULL => 1, PRIMARYKEY => 1}, - userid => {TYPE => 'INT3', NOTNULL => 1}, + userid => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, ipaddr => {TYPE => 'varchar(40)', NOTNULL => 1}, lastused => {TYPE => 'DATETIME', NOTNULL => 1}, ], @@ -808,7 +831,9 @@ use constant ABSTRACT_SCHEMA => { # for these changes. tokens => { FIELDS => [ - userid => {TYPE => 'INT3'}, + userid => {TYPE => 'INT3', REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, issuedate => {TYPE => 'DATETIME', NOTNULL => 1} , token => {TYPE => 'varchar(16)', NOTNULL => 1, PRIMARYKEY => 1}, @@ -996,8 +1021,13 @@ use constant ABSTRACT_SCHEMA => { PRIMARYKEY => 1}, name => {TYPE => 'varchar(64)', NOTNULL => 1}, product_id => {TYPE => 'INT2', NOTNULL => 1}, - initialowner => {TYPE => 'INT3', NOTNULL => 1}, - initialqacontact => {TYPE => 'INT3'}, + initialowner => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid'}}, + initialqacontact => {TYPE => 'INT3', + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'SET NULL'}}, description => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, ], INDEXES => [ @@ -1063,7 +1093,10 @@ use constant ABSTRACT_SCHEMA => { FIELDS => [ id => {TYPE => 'MEDIUMSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, - eventid => {TYPE => 'INT3', NOTNULL => 1}, + eventid => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'whine_events', + COLUMN => 'id', + DELETE => 'CASCADE'}}, query_name => {TYPE => 'varchar(64)', NOTNULL => 1, DEFAULT => "''"}, sortkey => {TYPE => 'INT2', NOTNULL => 1, @@ -1082,7 +1115,10 @@ use constant ABSTRACT_SCHEMA => { FIELDS => [ id => {TYPE => 'MEDIUMSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, - eventid => {TYPE => 'INT3', NOTNULL => 1}, + eventid => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'whine_events', + COLUMN => 'id', + DELETE => 'CASCADE'}}, run_day => {TYPE => 'varchar(32)'}, run_time => {TYPE => 'varchar(32)'}, run_next => {TYPE => 'DATETIME'}, @@ -1099,7 +1135,10 @@ use constant ABSTRACT_SCHEMA => { FIELDS => [ id => {TYPE => 'MEDIUMSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, - owner_userid => {TYPE => 'INT3', NOTNULL => 1}, + owner_userid => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, subject => {TYPE => 'varchar(128)'}, body => {TYPE => 'MEDIUMTEXT'}, ], @@ -1159,7 +1198,10 @@ use constant ABSTRACT_SCHEMA => { profile_setting => { FIELDS => [ - user_id => {TYPE => 'INT3', NOTNULL => 1}, + user_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, setting_name => {TYPE => 'varchar(32)', NOTNULL => 1}, setting_value => {TYPE => 'varchar(32)', NOTNULL => 1}, ], @@ -1376,7 +1418,8 @@ C<ALTER TABLE> SQL statement my $self = shift; my $finfo = (@_ == 1 && ref($_[0]) eq 'HASH') ? $_[0] : { @_ }; my $type = $finfo->{TYPE}; - die "A valid TYPE was not specified for this column." unless ($type); + confess "A valid TYPE was not specified for this column (got " + . Dumper($finfo) . ")" unless ($type); my $default = $finfo->{DEFAULT}; # Replace any abstract default value (such as 'TRUE' or 'FALSE') @@ -1397,17 +1440,6 @@ C<ALTER TABLE> SQL statement } #eosub--get_type_ddl -# Used if you want to display the DDL to the user, as opposed to actually -# use it in the database. -sub get_display_ddl { - my ($self, $table, $column, $def) = @_; - my $ddl = $self->get_type_ddl($def); - if ($def->{REFERENCES}) { - $ddl .= $self->get_fk_ddl($table, $column, $def->{REFERENCES}); - } - return $ddl; -} - sub get_fk_ddl { =item C<_get_fk_ddl> @@ -1441,8 +1473,8 @@ is undefined. my $update = $references->{UPDATE} || 'CASCADE'; my $delete = $references->{DELETE} || 'RESTRICT'; - my $to_table = $references->{TABLE} || die "No table in reference"; - my $to_column = $references->{COLUMN} || die "No column in reference"; + my $to_table = $references->{TABLE} || confess "No table in reference"; + my $to_column = $references->{COLUMN} || confess "No column in reference"; my $fk_name = $self->_get_fk_name($table, $column, $references); return "\n CONSTRAINT $fk_name FOREIGN KEY ($column)\n" @@ -1460,10 +1492,10 @@ sub _get_fk_name { return "fk_${table}_${column}_${to_table}_${to_column}"; } -sub _get_add_fk_sql { - my ($self, $table, $column, $new_def) = @_; +sub get_add_fk_sql { + my ($self, $table, $column, $def) = @_; - my $fk_string = $self->get_fk_ddl($table, $column, $new_def->{REFERENCES}); + my $fk_string = $self->get_fk_ddl($table, $column, $def); return ("ALTER TABLE $table ADD $fk_string"); } @@ -1519,22 +1551,12 @@ sub get_table_list { Parameters: none - Returns: An array of table names. The tables specified - in L</TABLES_FIRST> will come first, in order. The - rest of the tables will be in random order. + Returns: An array of table names, in alphabetical order. =cut my $self = shift; - - my %schema = %{$self->{schema}}; - my @tables; - foreach my $table (TABLES_FIRST) { - push(@tables, $table); - delete $schema{$table}; - } - push(@tables, keys %schema); - return @tables; + return sort keys %{$self->{schema}}; } sub get_table_columns { @@ -1603,14 +1625,6 @@ sub get_table_ddl { push(@ddl, $index_sql) if $index_sql; } - my %fields = @{$self->{schema}{$table}{FIELDS}}; - foreach my $col (keys %fields) { - my $def = $fields{$col}; - if ($def->{REFERENCES}) { - push(@ddl, $self->_get_add_fk_sql($table, $col, $def)); - } - } - push(@ddl, @{ $self->{schema}{$table}{DB_EXTRAS} }) if (ref($self->{schema}{$table}{DB_EXTRAS})); @@ -1704,6 +1718,11 @@ sub get_add_column_ddl { (push(@statements, "UPDATE $table SET $column = $init_value")) if defined $init_value; + if (defined $definition->{REFERENCES}) { + push(@statements, $self->get_add_fk_sql($table, $column, + $definition->{REFERENCES})); + } + return (@statements); } @@ -1824,15 +1843,6 @@ sub get_alter_column_ddl { push(@statements, "ALTER TABLE $table DROP PRIMARY KEY"); } - # If we went from not having an FK to having an FK - # XXX For right now, you can't change an FK's actual definition. - if (!$old_def->{REFERENCES} && $new_def->{REFERENCES}) { - push(@statements, $self->_get_add_fk_sql($table, $column, $new_def)); - } - elsif ($old_def->{REFERENCES} && !$new_def->{REFERENCES}) { - push(@statements, $self->_get_drop_fk_sql($table, $column, $old_def)); - } - return @statements; } @@ -2220,31 +2230,17 @@ sub columns_equal { $col_one->{TYPE} = uc($col_one->{TYPE}); $col_two->{TYPE} = uc($col_two->{TYPE}); - # It doesn't work to compare the two REFERENCES items, because - # they look like 'HASH(0xaf3c434)' to diff_arrays--they'll never - # be equal. - my %col_one_def = %$col_one; - delete $col_one_def{REFERENCES}; - my %col_two_def = %$col_two; - delete $col_two_def{REFERENCES}; + # We don't care about foreign keys when comparing column definitions. + delete $col_one->{REFERENCES}; + delete $col_two->{REFERENCES}; - my @col_one_array = %col_one_def; - my @col_two_array = %col_two_def; + my @col_one_array = %$col_one; + my @col_two_array = %$col_two; my ($removed, $added) = diff_arrays(\@col_one_array, \@col_two_array); # If there are no differences between the arrays, then they are equal. - my $defs_identical = !scalar(@$removed) && !scalar(@$added); - - # If the basic definitions are identical, we still want to check - # if the two foreign key definitions are different. - my @fk_array_one = %{$col_one->{REFERENCES} || {}}; - my @fk_array_two = %{$col_two->{REFERENCES} || {}}; - my ($fk_removed, $fk_added) = - diff_arrays(\@fk_array_one, \@fk_array_two); - my $fk_identical = !scalar(@$fk_removed) && !scalar(@$fk_added); - - return $defs_identical && $fk_identical ? 1 : 0; + return !scalar(@$removed) && !scalar(@$added) ? 1 : 0; } diff --git a/Bugzilla/DB/Schema/Mysql.pm b/Bugzilla/DB/Schema/Mysql.pm index eb7cd3c87..c867dc0fc 100644 --- a/Bugzilla/DB/Schema/Mysql.pm +++ b/Bugzilla/DB/Schema/Mysql.pm @@ -176,17 +176,10 @@ sub get_alter_column_ddl { # keys are not allowed. 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 @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 WHERE $column IS NULL") if defined $set_nulls_to; push(@statements, "ALTER TABLE $table CHANGE COLUMN @@ -196,10 +189,6 @@ sub get_alter_column_ddl { 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; } diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index 5e6401828..6ddca06bd 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -49,39 +49,6 @@ 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 # update_table_definitions for that. This is only for the fielddefs table. sub update_fielddefs_definition { @@ -413,9 +380,9 @@ sub update_table_definitions { if ($dbh->bz_column_info('components', 'initialqacontact')->{NOTNULL}) { $dbh->bz_alter_column('components', 'initialqacontact', {TYPE => 'INT3'}); - $dbh->do("UPDATE components SET initialqacontact = NULL " . - "WHERE initialqacontact = 0"); } + $dbh->do("UPDATE components SET initialqacontact = NULL " . + "WHERE initialqacontact = 0"); _migrate_email_prefs_to_new_table(); _initialize_dependency_tree_changes_email_pref(); @@ -552,25 +519,13 @@ sub update_table_definitions { $dbh->bz_add_column('milestones', 'id', {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 # ################################################################ Bugzilla::Hook::process('install-update_db'); + + $dbh->bz_setup_foreign_keys(); } # Subroutines should be ordered in the order that they are called. |