Commit 429534ee authored by Frédéric Buclin's avatar Frédéric Buclin

Bug 683644: Foreign keys aren't renamed correctly when DB tables are renamed

r=wicked a=LpSolit
parent e647ec07
...@@ -548,7 +548,7 @@ sub bz_setup_foreign_keys { ...@@ -548,7 +548,7 @@ sub bz_setup_foreign_keys {
# prior to 4.2, and also to handle problems caused # prior to 4.2, and also to handle problems caused
# by enabling an extension pre-4.2, disabling it for # by enabling an extension pre-4.2, disabling it for
# the 4.2 upgrade, and then re-enabling it later. # the 4.2 upgrade, and then re-enabling it later.
if (!$fk) { unless ($fk && $fk->{created}) {
my $standard_def = my $standard_def =
$self->_bz_schema->get_column_abstract($table, $column); $self->_bz_schema->get_column_abstract($table, $column);
if (exists $standard_def->{REFERENCES}) { if (exists $standard_def->{REFERENCES}) {
...@@ -1058,6 +1058,18 @@ sub bz_rename_table { ...@@ -1058,6 +1058,18 @@ sub bz_rename_table {
my $new = $self->bz_table_info($new_name); my $new = $self->bz_table_info($new_name);
ThrowCodeError('db_rename_conflict', { old => $old_name, ThrowCodeError('db_rename_conflict', { old => $old_name,
new => $new_name }) if $new; new => $new_name }) if $new;
# FKs will all have the wrong names unless we drop and then let them
# be re-created later. Under normal circumstances, checksetup.pl will
# automatically re-create these dropped FKs at the end of its DB upgrade
# run, so we don't need to re-create them in this method.
my @columns = $self->bz_table_columns($old_name);
foreach my $column (@columns) {
# these just return silently if there's no FK to drop
$self->bz_drop_fk($old_name, $column);
$self->bz_drop_related_fks($old_name, $column);
}
my @sql = $self->_bz_real_schema->get_rename_table_sql($old_name, $new_name); my @sql = $self->_bz_real_schema->get_rename_table_sql($old_name, $new_name);
print get_text('install_table_rename', print get_text('install_table_rename',
{ old => $old_name, new => $new_name }) . "\n" { old => $old_name, new => $new_name }) . "\n"
......
...@@ -638,6 +638,10 @@ sub bz_setup_database { ...@@ -638,6 +638,10 @@ sub bz_setup_database {
my $fk_name = $self->_bz_schema->_get_fk_name($table, my $fk_name = $self->_bz_schema->_get_fk_name($table,
$column, $column,
$references); $references);
# bz_rename_table didn't rename the trigger correctly.
if ($table eq 'bug_tag' && $to_table eq 'tags') {
$to_table = 'tag';
}
if ( $update =~ /CASCADE/i ){ if ( $update =~ /CASCADE/i ){
my $trigger_name = uc($fk_name . "_UC"); my $trigger_name = uc($fk_name . "_UC");
my $exist_trigger = $self->selectcol_arrayref( my $exist_trigger = $self->selectcol_arrayref(
......
...@@ -282,14 +282,18 @@ END ...@@ -282,14 +282,18 @@ END
$self->bz_add_index('products', 'products_name_lower_idx', $self->bz_add_index('products', 'products_name_lower_idx',
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'}); {FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
# bz_rename_column didn't correctly rename the sequence. # bz_rename_column and bz_rename_table didn't correctly rename
if ($self->bz_column_info('fielddefs', 'id') # the sequence.
&& $self->bz_sequence_exists('fielddefs_fieldid_seq')) $self->_fix_bad_sequence('fielddefs', 'id', 'fielddefs_fieldid_seq', 'fielddefs_id_seq');
{ # If the 'tags' table still exists, then bz_rename_table()
print "Fixing fielddefs_fieldid_seq sequence...\n"; # will fix the sequence for us.
$self->do("ALTER TABLE fielddefs_fieldid_seq RENAME TO fielddefs_id_seq"); if (!$self->bz_table_info('tags')) {
$self->do("ALTER TABLE fielddefs ALTER COLUMN id my $res = $self->_fix_bad_sequence('tag', 'id', 'tags_id_seq', 'tag_id_seq');
SET DEFAULT NEXTVAL('fielddefs_id_seq')"); # If $res is true, then the sequence has been renamed, meaning that
# the primary key must be renamed too.
if ($res) {
$self->do('ALTER INDEX tags_pkey RENAME TO tag_pkey');
}
} }
# Certain sequences got upgraded before we required Pg 8.3, and # Certain sequences got upgraded before we required Pg 8.3, and
...@@ -320,6 +324,20 @@ END ...@@ -320,6 +324,20 @@ END
} }
} }
sub _fix_bad_sequence {
my ($self, $table, $column, $old_seq, $new_seq) = @_;
if ($self->bz_column_info($table, $column)
&& $self->bz_sequence_exists($old_seq))
{
print "Fixing $old_seq sequence...\n";
$self->do("ALTER SEQUENCE $old_seq RENAME TO $new_seq");
$self->do("ALTER TABLE $table ALTER COLUMN $column
SET DEFAULT NEXTVAL('$new_seq')");
return 1;
}
return 0;
}
# Renames things that differ only in case. # Renames things that differ only in case.
sub _fix_case_differences { sub _fix_case_differences {
my ($table, $field) = @_; my ($table, $field) = @_;
......
...@@ -351,17 +351,10 @@ sub get_rename_column_ddl { ...@@ -351,17 +351,10 @@ sub get_rename_column_ddl {
my $def = $self->get_column_abstract($table, $old_name); my $def = $self->get_column_abstract($table, $old_name);
if ($def->{TYPE} =~ /SERIAL/i) { if ($def->{TYPE} =~ /SERIAL/i) {
# We have to rename the series also, and fix the default of the series. # We have to rename the series also, and fix the default of the series.
push(@sql, "RENAME ${table}_${old_name}_SEQ TO my $old_seq = "${table}_${old_name}_SEQ";
${table}_${new_name}_seq"); my $new_seq = "${table}_${new_name}_SEQ";
my $serial_sql = push(@sql, "RENAME $old_seq TO $new_seq");
"CREATE OR REPLACE TRIGGER ${table}_${new_name}_TR " push(@sql, $self->_get_create_trigger_ddl($table, $new_name, $new_seq));
. " BEFORE INSERT ON ${table} "
. " FOR EACH ROW "
. " BEGIN "
. " SELECT ${table}_${new_name}_SEQ.NEXTVAL "
. " INTO :NEW.${new_name} FROM DUAL; "
. " END;";
push(@sql, $serial_sql);
push(@sql, "DROP TRIGGER ${table}_${old_name}_TR"); push(@sql, "DROP TRIGGER ${table}_${old_name}_TR");
} }
if ($def->{TYPE} =~ /varchar|text/i && $def->{NOTNULL} ) { if ($def->{TYPE} =~ /varchar|text/i && $def->{NOTNULL} ) {
...@@ -371,6 +364,35 @@ sub get_rename_column_ddl { ...@@ -371,6 +364,35 @@ sub get_rename_column_ddl {
return @sql; return @sql;
} }
sub get_rename_table_sql {
my ($self, $old_name, $new_name) = @_;
if (lc($old_name) eq lc($new_name)) {
# if the only change is a case change, return an empty list.
return ();
}
my @sql = ("ALTER TABLE $old_name RENAME TO $new_name");
my @columns = $self->get_table_columns($old_name);
foreach my $column (@columns) {
my $def = $self->get_column_abstract($old_name, $column);
if ($def->{TYPE} =~ /SERIAL/i) {
# If there's a SERIAL column on this table, we also need
# to rename the sequence.
my $old_seq = "${old_name}_${column}_SEQ";
my $new_seq = "${new_name}_${column}_SEQ";
push(@sql, "RENAME $old_seq TO $new_seq");
push(@sql, $self->_get_create_trigger_ddl($new_name, $column, $new_seq));
push(@sql, "DROP TRIGGER ${old_name}_${column}_TR");
}
if ($def->{TYPE} =~ /varchar|text/i && $def->{NOTNULL}) {
push(@sql, _get_notnull_trigger_ddl($new_name, $column));
push(@sql, "DROP TRIGGER ${$old_name}_${column}");
}
}
return @sql;
}
sub _get_notnull_trigger_ddl { sub _get_notnull_trigger_ddl {
my ($table, $column) = @_; my ($table, $column) = @_;
...@@ -398,17 +420,22 @@ sub _get_create_seq_ddl { ...@@ -398,17 +420,22 @@ sub _get_create_seq_ddl {
. " NOMAXVALUE " . " NOMAXVALUE "
. " NOCYCLE " . " NOCYCLE "
. " NOCACHE"; . " NOCACHE";
push (@ddl, $seq_sql);
push(@ddl, $self->_get_create_trigger_ddl($table, $column, $seq_name));
return @ddl;
}
sub _get_create_trigger_ddl {
my ($self, $table, $column, $seq_name) = @_;
my $serial_sql = "CREATE OR REPLACE TRIGGER ${table}_${column}_TR " my $serial_sql = "CREATE OR REPLACE TRIGGER ${table}_${column}_TR "
. " BEFORE INSERT ON ${table} " . " BEFORE INSERT ON $table "
. " FOR EACH ROW " . " FOR EACH ROW "
. " BEGIN " . " BEGIN "
. " SELECT ${seq_name}.NEXTVAL " . " SELECT ${seq_name}.NEXTVAL "
. " INTO :NEW.${column} FROM DUAL; " . " INTO :NEW.$column FROM DUAL; "
. " END;"; . " END;";
push (@ddl, $seq_sql); return $serial_sql;
push (@ddl, $serial_sql);
return @ddl;
} }
sub get_set_serial_sql { sub get_set_serial_sql {
......
...@@ -114,7 +114,30 @@ sub get_rename_table_sql { ...@@ -114,7 +114,30 @@ sub get_rename_table_sql {
# is case-insensitive and will return an error about a duplicate name # is case-insensitive and will return an error about a duplicate name
return (); return ();
} }
return ("ALTER TABLE $old_name RENAME TO $new_name");
my @sql = ("ALTER TABLE $old_name RENAME TO $new_name");
# If there's a SERIAL column on this table, we also need to rename the
# sequence.
# If there is a PRIMARY KEY, we need to rename it too.
my @columns = $self->get_table_columns($old_name);
foreach my $column (@columns) {
my $def = $self->get_column_abstract($old_name, $column);
if ($def->{TYPE} =~ /SERIAL/i) {
my $old_seq = "${old_name}_${column}_seq";
my $new_seq = "${new_name}_${column}_seq";
push(@sql, "ALTER SEQUENCE $old_seq RENAME TO $new_seq");
push(@sql, "ALTER TABLE $new_name ALTER COLUMN $column
SET DEFAULT NEXTVAL('$new_seq')");
}
if ($def->{PRIMARYKEY}) {
my $old_pk = "${old_name}_pkey";
my $new_pk = "${new_name}_pkey";
push(@sql, "ALTER INDEX $old_pk RENAME to $new_pk");
}
}
return @sql;
} }
sub get_set_serial_sql { sub get_set_serial_sql {
......
...@@ -3611,6 +3611,13 @@ sub _rename_tags_to_tag { ...@@ -3611,6 +3611,13 @@ sub _rename_tags_to_tag {
$dbh->bz_add_index('tag', 'tag_user_id_idx', $dbh->bz_add_index('tag', 'tag_user_id_idx',
{FIELDS => [qw(user_id name)], TYPE => 'UNIQUE'}); {FIELDS => [qw(user_id name)], TYPE => 'UNIQUE'});
} }
if (my $bug_tag_fk = $dbh->bz_fk_info('bug_tag', 'tag_id')) {
# bz_rename_table() didn't handle FKs correctly.
if ($bug_tag_fk->{TABLE} eq 'tags') {
$bug_tag_fk->{TABLE} = 'tag';
$dbh->bz_alter_fk('bug_tag', 'tag_id', $bug_tag_fk);
}
}
} }
sub _on_delete_set_null_for_audit_log_userid { sub _on_delete_set_null_for_audit_log_userid {
......
...@@ -433,7 +433,7 @@ the database, and that file has been renamed to ##data##/comments.bak ...@@ -433,7 +433,7 @@ the database, and that file has been renamed to ##data##/comments.bak
You may delete the renamed file once you have confirmed that all your You may delete the renamed file once you have confirmed that all your
quips were moved successfully. quips were moved successfully.
END END
update_queries_to_tags => "Populating the new tags table:", update_queries_to_tags => "Populating the new 'tag' table:",
webdot_bad_htaccess => <<END, webdot_bad_htaccess => <<END,
WARNING: Dependency graph images are not accessible. WARNING: Dependency graph images are not accessible.
Delete ##dir##/.htaccess and re-run checksetup.pl. Delete ##dir##/.htaccess and re-run checksetup.pl.
......
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