]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 373442: Add referential integrity against the profiles table in some more simple...
authormkanat%bugzilla.org <>
Sun, 11 Mar 2007 00:21:19 +0000 (00:21 +0000)
committermkanat%bugzilla.org <>
Sun, 11 Mar 2007 00:21:19 +0000 (00:21 +0000)
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat

Bugzilla/DB.pm
Bugzilla/DB/Schema.pm
Bugzilla/DB/Schema/Mysql.pm
Bugzilla/Install/DB.pm
template/en/default/global/messages.html.tmpl

index e4e30a8d394a981a34f7d19ac1f735d698d7120e..22c6bbafacc3d1c457ba87bf25986336255c0b15 100644 (file)
@@ -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__
index 6b596b161937fa8b1aad2b5add54f8d275841f77..15d7dd8b2b87e37dfbc1ef65a4a13011f3786477 100644 (file)
@@ -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;
 }
 
 
index eb7cd3c87a888a28451f65ea702e0f8fa9ad36d4..c867dc0fc3e0f0491725f4bca1747952ec6c129b 100644 (file)
@@ -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;
 }
 
index 5e6401828ba4f769cb67306ce4aa714f55ca1569..6ddca06bd3b16596ec585013264520aa175beff1 100644 (file)
@@ -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.
index bebed5579e79019b0fa99f347c5093b41e6ca8e8..560ef2b33ea5f32aeadb6337284b4c19af7b2e2c 100644 (file)
   [% ELSIF message_tag == "install_file_perms_fix" %]
     Fixing file permissions...
 
+  [% ELSIF message_tag == "install_fk_add" %]
+    Adding foreign key: [% table FILTER html %].[% column FILTER html %] -&gt; [% fk.TABLE FILTER html %].[% fk.COLUMN FILTER html %]...
+
   [% ELSIF message_tag == "install_group_create" %]
     Creating group [% name FILTER html %]...